SPI 总线协议和驱动
协议介绍
SPI(Serial Peripheral Interface),即串行外设接口,是一种串口通信接口协议。SPI遵循主从模式框架设计架构,但只能有一个主设备。SPI总线有4条信号线,CS(片选信号线),SCLK(时钟信号线),MOSI(主出从入信号线)和MISO(主入从出信号线),因此SPI是全双工总线。SPI的工作原理比较简单,主设备控制片选和时钟输出,输出数据到从设备及读取从设备输入的数据。
SPI协议有四种时钟工作模式(Mode0,Mode1,Mode2和Mode3),要求主从设备工作模式一致。从设备在出厂之后,时钟工作模式已经固定,一般通过修改主设备的时钟工作模式来匹配从设备时钟模式。SPI的四种工作模式取决于时钟极性(CPOL)和时钟相位(CPHA)的组合,时钟极性CPOL用来配置SCLK的电平处于哪种状态时是空闲态或者工作态,而时钟相位CPHA用来配置数据采样在哪个时钟沿:
(1)CPOL=0,表示SCLK低电平时是空闲态,也就是SCLK高电平时是工作态
(2)CPOL=1,表示SCLK高电平时是工作态,也就是SCLK低电平时是工作态
(3)CPHA=0,表示数据采样在第一个时钟沿,数据发送在第二个时钟沿
(4)CPHA=1,表示数据采样在第二个时钟沿,数据发送在第一个时钟沿
协议工作时序图如下(图片来源于网络):
SPI总线具有传输速率快,全双工和硬件电路简单等有点,但是有个缺点:没有类似于I2C总线的应答机制。
内核框架
内核中SPI总线协议框架类似于I2C的总线框架。核心层提供spi_master,spi_device,spi_driver,spi_message和spi_transfer核心数据结构和API接口,供上层系统注册主设备驱动,从设备以及从设备驱动。
1,数据结构:
(1)struct spi_master:主设备和驱动。spi_master结构包含主设备速率,工作模式,统计相关信息。采用了kernel workqueue的工作模式,提供了数据传输相关的回调接口:transfer()接口用于将spi_message加入消息队列,workqueue从消息队列中取出spi_message,通过transfer_one_message()接口发送一个spi_message,每个spi_message包含多个spi_transfer,最终通过transfer_one()接口发送一个spi_transfer。
struct spi_master {
struct device dev;
struct list_head list;
/* other than negative (== assign one dynamically), bus_num is fully
* board-specific. usually that simplifies to being SOC-specific.
* example: one SOC has three SPI controllers, numbered 0..2,
* and one board's schematics might show it using SPI-2. software
* would normally use bus_num=2 for that controller.
*/
s16 bus_num; // 总线编号
/* chipselects will be integral to many controllers; some others
* might use board-specific GPIOs.
*/
u16 num_chipselect; // 片选个数
/* some SPI controllers pose alignment requirements on DMAable
* buffers; let protocol drivers know about these requirements.
*/
u16 dma_alignment;
/* spi_device.mode flags understood by this controller driver */
u16 mode_bits;
/* bitmask of supported bits_per_word for transfers */
u32 bits_per_word_mask; // 一个transfer的bit数掩码
#define SPI_BPW_MASK(bits) BIT((bits) - 1)
#define SPI_BIT_MASK(bits) (((bits) == 32) ? ~0U : (BIT(bits) - 1))
#define SPI_BPW_RANGE_MASK(min, max) (SPI_BIT_MASK(max) - SPI_BIT_MASK(min - 1))
/* limits on transfer speed */
u32 min_speed_hz; // 支持的最小传输速率
u32 max_speed_hz; // 支持的最大传输速率
/* other constraints relevant to this driver */
u16 flags;
#define SPI_MASTER_HALF_DUPLEX BIT(0) /* can't do full duplex */
#define SPI_MASTER_NO_RX BIT(1) /* can't do buffer read */
#define SPI_MASTER_NO_TX BIT(2) /* can't do buffer write */
#define SPI_MASTER_MUST_RX BIT(3) /* requires rx */
#define SPI_MASTER_MUST_TX BIT(4) /* requires tx */
/* lock and mutex for SPI bus locking */
spinlock_t bus_lock_spinlock;
struct mutex bus_lock_mutex;
/* flag indicating that the SPI bus is locked for exclusive use */
bool bus_lock_flag;
/* Setup mode and clock, etc (spi driver may call many times).
*
* IMPORTANT: this may be called when transfers to another
* device are active. DO NOT UPDATE SHARED REGISTERS in ways
* which could break those transfers.
*/
int (*setup)(struct spi_device *spi); // 每个transfer传输之前由 spi_driver调用,用于配置传输参数(不同从设备在速率和时钟模式上都可能不同,因此每个transfer都需要重新配置)
/* bidirectional bulk transfers
*
* + The transfer() method may not sleep; its main role is
* just to add the message to the queue.
* + For now there's no remove-from-queue operation, or
* any other request management
* + To a given spi_device, message queueing is pure fifo
*
* + The master's main job is to process its message queue,
* selecting a chip then transferring data
* + If there are multiple spi_device children, the i/o queue
* arbitration algorithm is unspecified (round robin, fifo,
* priority, reservations, preemption, etc)
*
* + Chipselect stays active during the entire message
* (unless modified by spi_transfer.cs_change != 0).
* + The message transfers use clock and SPI mode parameters
* previously established by setup() for this device
*/
int (*transfer)(struct spi_device *spi,
struct spi_message *mesg); // spi_async() / spi_sync() 接口调用该方法将spi_message消息加入队列
/* called on release() to free memory provided by spi_master */
void (*cleanup)(struct spi_device *spi);
/*
* Used to enable core support for DMA handling, if can_dma()
* exists and returns true then the transfer will be mapped
* prior to transfer_one() being called. The driver should
* not modify or store xfer and dma_tx and dma_rx must be set
* while the device is prepared.
*/
bool (*can_dma)(struct spi_master *master,
struct spi_device *spi,
struct spi_transfer *xfer);
/*
* These hooks are for drivers that want to use the generic
* master transfer queueing mechanism. If these are used, the
* transfer() function above must NOT be specified by the driver.
* Over time we expect SPI drivers to be phased over to this API.
*/
bool queued;
struct kthread_worker kworker;
struct task_struct *kworker_task;
struct kthread_work pump_messages; // work queue,用于处理队列中的spi_message
spinlock_t queue_lock;
struct list_head queue;
struct spi_message *cur_msg;
bool idling;
bool busy;
bool running;
bool rt;
bool auto_runtime_pm;
bool cur_msg_prepared;
bool cur_msg_mapped;
struct completion xfer_completion;
size_t max_dma_len;
int (*prepare_transfer_hardware)(struct spi_master *master); // 准备好硬件,用于实际数据传输
int (*transfer_one_message)(struct spi_master *master,
struct spi_message *mesg); // 发送一个spi_message
int (*unprepare_transfer_hardware)(struct spi_master *master);
int (*prepare_message)(struct spi_master *master,
struct spi_message *message);
int (*unprepare_message)(struct spi_master *master,
struct spi_message *message);
/*
* These hooks are for drivers that use a generic implementation
* of transfer_one_message() provied by the core.
*/
void (*set_cs)(struct spi_device *spi, bool enable); // 每次传输前,设置片选信号
int (*transfer_one)(struct spi_master *master, struct spi_device *spi,
struct spi_transfer *transfer); // 发送一个 spi_transfer
void (*handle_err)(struct spi_master *master,
struct spi_message *message);
/* gpio chip select */
int *cs_gpios;
/* statistics */
struct spi_statistics statistics;
/* DMA channels for use with core dmaengine helpers */
struct dma_chan *dma_tx;
struct dma_chan *dma_rx;
/* dummy data for full duplex devices */
void *dummy_rx;
void *dummy_tx;
};
(2)struct spi_device:spi_device用于描述一个spi从设备,一般在主设备注册时,通过平台相关代码配置或者设备树配置自动创建并注册。
struct spi_device {
struct device dev;
struct spi_master *master;
u32 max_speed_hz; // 支持的最大传输速率
u8 chip_select; // 片选
u8 bits_per_word; // 单位传输bit数
u16 mode; // 工作模式
#define SPI_CPHA 0x01 /* clock phase */
#define SPI_CPOL 0x02 /* clock polarity */
#define SPI_MODE_0 (0|0) /* (original MicroWire) */
#define SPI_MODE_1 (0|SPI_CPHA)
#define SPI_MODE_2 (SPI_CPOL|0)
#define SPI_MODE_3 (SPI_CPOL|SPI_CPHA)
#define SPI_CS_HIGH 0x04 /* chipselect active high? */
#define SPI_LSB_FIRST 0x08 /* per-word bits-on-wire */
#define SPI_3WIRE 0x10 /* SI/SO signals shared */
#define SPI_LOOP 0x20 /* loopback mode */
#define SPI_NO_CS 0x40 /* 1 dev/bus, no chipselect */
#define SPI_READY 0x80 /* slave pulls low to pause */
#define SPI_TX_DUAL 0x100 /* transmit with 2 wires */
#define SPI_TX_QUAD 0x200 /* transmit with 4 wires */
#define SPI_RX_DUAL 0x400 /* receive with 2 wires */
#define SPI_RX_QUAD 0x800 /* receive with 4 wires */
int irq;
void *controller_state;
void *controller_data;
char modalias[SPI_NAME_SIZE];
int cs_gpio; /* chip select gpio */
/* the statistics */
struct spi_statistics statistics;
/*
* likely need more hooks for more protocol options affecting how
* the controller talks to each chip, like:
* - memory packing (12 bit samples into low bits, others zeroed)
* - priority
* - drop chipselect after each word
* - chipselect delays
* - ...
*/
};
(3)struct spi_driver:从设备驱动
struct spi_driver {
const struct spi_device_id *id_table;
int (*probe)(struct spi_device *spi);
int (*remove)(struct spi_device *spi);
void (*shutdown)(struct spi_device *spi);
struct device_driver driver;
};
(4)struct spi_message:spi消息结构
struct spi_message {
struct list_head transfers; // 包含的spi_transfer列表
struct spi_device *spi;
unsigned is_dma_mapped:1;
/* REVISIT: we might want a flag affecting the behavior of the
* last transfer ... allowing things like "read 16 bit length L"
* immediately followed by "read L bytes". Basically imposing
* a specific message scheduling algorithm.
*
* Some controller drivers (message-at-a-time queue processing)
* could provide that as their default scheduling algorithm. But
* others (with multi-message pipelines) could need a flag to
* tell them about such special cases.
*/
/* completion is reported through a callback */
void (*complete)(void *context);
void *context;
unsigned frame_length;
unsigned actual_length;
int status;
/* for optional use by whatever driver currently owns the
* spi_message ... between calls to spi_async and then later
* complete(), that's the spi_master controller driver.
*/
struct list_head queue;
void *state;
};
(5)spi_transfer:包含在spi_message中的spi_transfer
struct spi_transfer {
/* it's ok if tx_buf == rx_buf (right?)
* for MicroWire, one buffer must be null
* buffers must work with dma_*map_single() calls, unless
* spi_message.is_dma_mapped reports a pre-existing mapping
*/
const void *tx_buf; // 发送buffer
void *rx_buf; // 接收buffer
unsigned len; // buffer长度
dma_addr_t tx_dma;
dma_addr_t rx_dma;
struct sg_table tx_sg;
struct sg_table rx_sg;
unsigned cs_change:1;
unsigned tx_nbits:3;
unsigned rx_nbits:3;
#define SPI_NBITS_SINGLE 0x01 /* 1bit transfer */
#define SPI_NBITS_DUAL 0x02 /* 2bits transfer */
#define SPI_NBITS_QUAD 0x04 /* 4bits transfer */
u8 bits_per_word;
u16 delay_usecs;
u32 speed_hz;
struct list_head transfer_list;
};
2,框架接口:
(1)spi_master注册接口:int spi_register_master(struct spi_master *master);
int spi_register_master(struct spi_master *master)
{
static atomic_t dyn_bus_id = ATOMIC_INIT((1<<15) - 1);
struct device *dev = master->dev.parent;
struct boardinfo *bi;
int status = -ENODEV;
int dynamic = 0;
if (!dev)
return -ENODEV;
status = of_spi_register_master(master); // 从设备树配置中获取片选个数,和片选gpio编号
if (status)
return status;
/* even if it's just one always-selected device, there must
* be at least one chipselect
*/
if (master->num_chipselect == 0)
return -EINVAL;
if ((master->bus_num < 0) && master->dev.of_node)
master->bus_num = of_alias_get_id(master->dev.of_node, "spi");
/* convention: dynamically assigned bus IDs count down from the max */
if (master->bus_num < 0) {
/* FIXME switch to an IDR based scheme, something like
* I2C now uses, so we can't run out of "dynamic" IDs
*/
master->bus_num = atomic_dec_return(&dyn_bus_id);
dynamic = 1;
}
INIT_LIST_HEAD(&master->queue);
spin_lock_init(&master->queue_lock);
spin_lock_init(&master->bus_lock_spinlock);
mutex_init(&master->bus_lock_mutex);
master->bus_lock_flag = 0;
init_completion(&master->xfer_completion);
if (!master->max_dma_len)
master->max_dma_len = INT_MAX;
/* register the device, then userspace will see it.
* registration fails if the bus ID is in use.
*/
dev_set_name(&master->dev, "spi%u", master->bus_num);
status = device_add(&master->dev); // 注册master到kernel设备驱动框架
if (status < 0)
goto done;
dev_dbg(dev, "registered master %s%s\n", dev_name(&master->dev),
dynamic ? " (dynamic)" : "");
/* If we're using a queued driver, start the queue */
if (master->transfer)
dev_info(dev, "master is unqueued, this is deprecated\n");
else {
status = spi_master_initialize_queue(master); // 初始化主设备消息队列处理机制,workqueue
if (status) {
device_del(&master->dev);
goto done;
}
}
/* add statistics */
spin_lock_init(&master->statistics.lock);
mutex_lock(&board_lock);
list_add_tail(&master->list, &spi_master_list); // 将master加入 spi_master_list 列表中
list_for_each_entry(bi, &board_list, list)
spi_match_master_to_boardinfo(master, &bi->board_info); // 从预配置的board_list中创建匹配的spi_device
mutex_unlock(&board_lock);
/* Register devices from the device tree and ACPI */
of_register_spi_devices(master); // 从设备树配置中创建并注册所有的spi_device
acpi_register_spi_devices(master);
done:
return status;
}
static int spi_master_initialize_queue(struct spi_master *master)
{
int ret;
master->transfer = spi_queued_transfer; // 设置transfer方法为 spi_queued_transfer(),该方法将spi_message加入master的message queue
if (!master->transfer_one_message)
master->transfer_one_message = spi_transfer_one_message; // spi_transfer_one_message用来轮询message中的所有spi_transfer,调 用 master中的transfer_one来一个个传输spi_transfer
/* Initialize and start queue */
ret = spi_init_queue(master); // 初始化workqueue线程
if (ret) {
dev_err(&master->dev, "problem initializing queue\n");
goto err_init_queue;
}
master->queued = true;
ret = spi_start_queue(master); // 启动workqueue线程,执行spi_pump_messages()方法从消息队列获取spi_message进行传输
if (ret) {
dev_err(&master->dev, "problem starting queue\n");
goto err_start_queue;
}
return 0;
err_start_queue:
spi_destroy_queue(master);
err_init_queue:
return ret;
}
static void of_register_spi_devices(struct spi_master *master)
{
struct spi_device *spi;
struct device_node *nc;
if (!master->dev.of_node)
return;
for_each_available_child_of_node(master->dev.of_node, nc) {
spi = of_register_spi_device(master, nc); // 轮询注册每个spi_device
if (IS_ERR(spi))
dev_warn(&master->dev, "Failed to create SPI device for %s\n",
nc->full_name);
}
}
of_register_spi_device(struct spi_master *master, struct device_node *nc)
{
struct spi_device *spi;
int rc;
u32 value;
/* Alloc an spi_device */
spi = spi_alloc_device(master); // 分配spi_device结构
if (!spi) {
dev_err(&master->dev, "spi_device alloc error for %s\n",
nc->full_name);
rc = -ENOMEM;
goto err_out;
}
/* Select device driver */
rc = of_modalias_node(nc, spi->modalias,
sizeof(spi->modalias));
if (rc < 0) {
dev_err(&master->dev, "cannot find modalias for %s\n",
nc->full_name);
goto err_out;
}
/* Device address */
rc = of_property_read_u32(nc, "reg", &value);
if (rc) {
dev_err(&master->dev, "%s has no valid 'reg' property (%d)\n",
nc->full_name, rc);
goto err_out;
}
spi->chip_select = value; // 配置片选编号
/* Mode (clock phase/polarity/etc.) */
if (of_find_property(nc, "spi-cpha", NULL))
spi->mode |= SPI_CPHA;
if (of_find_property(nc, "spi-cpol", NULL))
spi->mode |= SPI_CPOL;
if (of_find_property(nc, "spi-cs-high", NULL))
spi->mode |= SPI_CS_HIGH;
if (of_find_property(nc, "spi-3wire", NULL))
spi->mode |= SPI_3WIRE;
if (of_find_property(nc, "spi-lsb-first", NULL))
spi->mode |= SPI_LSB_FIRST;
/* Device DUAL/QUAD mode */
if (!of_property_read_u32(nc, "spi-tx-bus-width", &value)) {
switch (value) {
case 1:
break;
case 2:
spi->mode |= SPI_TX_DUAL;
break;
case 4:
spi->mode |= SPI_TX_QUAD;
break;
default:
dev_warn(&master->dev,
"spi-tx-bus-width %d not supported\n",
value);
break;
}
}
if (!of_property_read_u32(nc, "spi-rx-bus-width", &value)) {
switch (value) {
case 1:
break;
case 2:
spi->mode |= SPI_RX_DUAL;
break;
case 4:
spi->mode |= SPI_RX_QUAD;
break;
default:
dev_warn(&master->dev,
"spi-rx-bus-width %d not supported\n",
value);
break;
}
}
/* Device speed */
rc = of_property_read_u32(nc, "spi-max-frequency", &value);
if (rc) {
dev_err(&master->dev, "%s has no valid 'spi-max-frequency' property (%d)\n",
nc->full_name, rc);
goto err_out;
}
spi->max_speed_hz = value;
/* Store a pointer to the node in the device structure */
of_node_get(nc);
spi->dev.of_node = nc;
/* Register the new device */
rc = spi_add_device(spi); // 注册spi_device到spi_bus_type中
if (rc) {
dev_err(&master->dev, "spi_device register error %s\n",
nc->full_name);
goto err_out;
}
return spi;
err_out:
spi_dev_put(spi);
return ERR_PTR(rc);
}
int spi_add_device(struct spi_device *spi)
{
static DEFINE_MUTEX(spi_add_lock);
struct spi_master *master = spi->master;
struct device *dev = master->dev.parent;
int status;
/* Chipselects are numbered 0..max; validate. */
if (spi->chip_select >= master->num_chipselect) {
dev_err(dev, "cs%d >= max %d\n",
spi->chip_select,
master->num_chipselect);
return -EINVAL;
}
/* Set the bus ID string */
spi_dev_set_name(spi);
/* We need to make sure there's no other device with this
* chipselect **BEFORE** we call setup(), else we'll trash
* its configuration. Lock against concurrent add() calls.
*/
mutex_lock(&spi_add_lock);
status = bus_for_each_dev(&spi_bus_type, NULL, spi, spi_dev_check);
if (status) {
dev_err(dev, "chipselect %d already in use\n",
spi->chip_select);
goto done;
}
if (master->cs_gpios)
spi->cs_gpio = master->cs_gpios[spi->chip_select]; // 获取片选gpio
/* Drivers may modify this initial i/o setup, but will
* normally rely on the device being setup. Devices
* using SPI_CS_HIGH can't coexist well otherwise...
*/
status = spi_setup(spi); // 内部调用master->setup设置传输参数
if (status < 0) {
dev_err(dev, "can't setup %s, status %d\n",
dev_name(&spi->dev), status);
goto done;
}
/* Device may be bound to an active driver when this returns */
status = device_add(&spi->dev); // 将spi_device注册进设备驱动框架
if (status < 0)
dev_err(dev, "can't add %s, status %d\n",
dev_name(&spi->dev), status);
else
dev_dbg(dev, "registered child %s\n", dev_name(&spi->dev));
done:
mutex_unlock(&spi_add_lock);
return status;
}
int spi_setup(struct spi_device *spi)
{
unsigned bad_bits, ugly_bits;
int status;
/* check mode to prevent that DUAL and QUAD set at the same time
*/
if (((spi->mode & SPI_TX_DUAL) && (spi->mode & SPI_TX_QUAD)) ||
((spi->mode & SPI_RX_DUAL) && (spi->mode & SPI_RX_QUAD))) {
dev_err(&spi->dev,
"setup: can not select dual and quad at the same time\n");
return -EINVAL;
}
/* if it is SPI_3WIRE mode, DUAL and QUAD should be forbidden
*/
if ((spi->mode & SPI_3WIRE) && (spi->mode &
(SPI_TX_DUAL | SPI_TX_QUAD | SPI_RX_DUAL | SPI_RX_QUAD)))
return -EINVAL;
/* help drivers fail *cleanly* when they need options
* that aren't supported with their current master
*/
bad_bits = spi->mode & ~spi->master->mode_bits;
ugly_bits = bad_bits &
(SPI_TX_DUAL | SPI_TX_QUAD | SPI_RX_DUAL | SPI_RX_QUAD);
if (ugly_bits) {
dev_warn(&spi->dev,
"setup: ignoring unsupported mode bits %x\n",
ugly_bits);
spi->mode &= ~ugly_bits;
bad_bits &= ~ugly_bits;
}
if (bad_bits) {
dev_err(&spi->dev, "setup: unsupported mode bits %x\n",
bad_bits);
return -EINVAL;
}
if (!spi->bits_per_word)
spi->bits_per_word = 8;
status = __spi_validate_bits_per_word(spi->master, spi->bits_per_word);
if (status)
return status;
if (!spi->max_speed_hz)
spi->max_speed_hz = spi->master->max_speed_hz;
if (spi->master->setup)
status = spi->master->setup(spi); // 调用master->setup()
spi_set_cs(spi, false); // 先禁止片选
dev_dbg(&spi->dev, "setup mode %d, %s%s%s%s%u bits/w, %u Hz max --> %d\n",
(int) (spi->mode & (SPI_CPOL | SPI_CPHA)),
(spi->mode & SPI_CS_HIGH) ? "cs_high, " : "",
(spi->mode & SPI_LSB_FIRST) ? "lsb, " : "",
(spi->mode & SPI_3WIRE) ? "3wire, " : "",
(spi->mode & SPI_LOOP) ? "loopback, " : "",
spi->bits_per_word, spi->max_speed_hz,
status);
return status;
}
static void spi_set_cs(struct spi_device *spi, bool enable)
{
if (spi->mode & SPI_CS_HIGH)
enable = !enable;
if (gpio_is_valid(spi->cs_gpio))
gpio_set_value(spi->cs_gpio, !enable); // 优先设置gpio片选信号
else if (spi->master->set_cs)
spi->master->set_cs(spi, !enable); // 否则调用master->set_cs()
}
(2)数据传输(异步传输):int spi_async(struct spi_device *spi, struct spi_message *message)
int spi_async(struct spi_device *spi, struct spi_message *message)
{
struct spi_master *master = spi->master;
int ret;
unsigned long flags;
ret = __spi_validate(spi, message);
if (ret != 0)
return ret;
spin_lock_irqsave(&master->bus_lock_spinlock, flags);
if (master->bus_lock_flag)
ret = -EBUSY;
else
ret = __spi_async(spi, message); // 调用无锁版本
spin_unlock_irqrestore(&master->bus_lock_spinlock, flags);
return ret;
}
static int __spi_async(struct spi_device *spi, struct spi_message *message)
{
struct spi_master *master = spi->master;
message->spi = spi;
SPI_STATISTICS_INCREMENT_FIELD(&master->statistics, spi_async);
SPI_STATISTICS_INCREMENT_FIELD(&spi->statistics, spi_async); // 更新统计信息
trace_spi_message_submit(message);
return master->transfer(spi, message); // 调用master->transfer,即spi_queued_transfer(),将spi_message加入消息队列
}
static int spi_queued_transfer(struct spi_device *spi, struct spi_message *msg)
{
return __spi_queued_transfer(spi, msg, true); // true表示消息加入队列后,需要启动workqueue线程去处理队列中的消息,因为这个方法只会被异步传输方法调用。
}
static int __spi_queued_transfer(struct spi_device *spi,
struct spi_message *msg,
bool need_pump)
{
struct spi_master *master = spi->master;
unsigned long flags;
spin_lock_irqsave(&master->queue_lock, flags);
if (!master->running) {
spin_unlock_irqrestore(&master->queue_lock, flags);
return -ESHUTDOWN;
}
msg->actual_length = 0;
msg->status = -EINPROGRESS;
list_add_tail(&msg->queue, &master->queue); // spi_message加入master消息队列
if (!master->busy && need_pump)
queue_kthread_work(&master->kworker, &master->pump_messages); // need_pump为true,启动workqueue线程处理消息。其中master->pump_message方法为spi_pump_messages()方法
spin_unlock_irqrestore(&master->queue_lock, flags);
return 0;
}
// spi_pump_messages()运行于workqueue线程
static void spi_pump_messages(struct kthread_work *work)
{
struct spi_master *master =
container_of(work, struct spi_master, pump_messages);
__spi_pump_messages(master, true); // 执行实际的数据处理
}
static void __spi_pump_messages(struct spi_master *master, bool in_kthread)
{
unsigned long flags;
bool was_busy = false;
int ret;
/* Lock queue */
spin_lock_irqsave(&master->queue_lock, flags);
/* Make sure we are not already running a message */
if (master->cur_msg) {
spin_unlock_irqrestore(&master->queue_lock, flags);
return;
}
/* If another context is idling the device then defer */
if (master->idling) {
queue_kthread_work(&master->kworker, &master->pump_messages);
spin_unlock_irqrestore(&master->queue_lock, flags);
return;
}
/* Check if the queue is idle */
if (list_empty(&master->queue) || !master->running) {
if (!master->busy) {
spin_unlock_irqrestore(&master->queue_lock, flags);
return;
}
/* Only do teardown in the thread */
if (!in_kthread) {
queue_kthread_work(&master->kworker,
&master->pump_messages);
spin_unlock_irqrestore(&master->queue_lock, flags);
return;
}
master->busy = false;
master->idling = true;
spin_unlock_irqrestore(&master->queue_lock, flags);
kfree(master->dummy_rx);
master->dummy_rx = NULL;
kfree(master->dummy_tx);
master->dummy_tx = NULL;
if (master->unprepare_transfer_hardware &&
master->unprepare_transfer_hardware(master)) // 调用master->unprepare_transfer_hardware结束上一次硬件传输
dev_err(&master->dev,
"failed to unprepare transfer hardware\n");
if (master->auto_runtime_pm) {
pm_runtime_mark_last_busy(master->dev.parent);
pm_runtime_put_autosuspend(master->dev.parent);
}
trace_spi_master_idle(master);
spin_lock_irqsave(&master->queue_lock, flags);
master->idling = false;
spin_unlock_irqrestore(&master->queue_lock, flags);
return;
}
/* Extract head of queue */
master->cur_msg =
list_first_entry(&master->queue, struct spi_message, queue); // 从消息队列头获取一个message
list_del_init(&master->cur_msg->queue);
if (master->busy)
was_busy = true;
else
master->busy = true;
spin_unlock_irqrestore(&master->queue_lock, flags);
if (!was_busy && master->auto_runtime_pm) {
ret = pm_runtime_get_sync(master->dev.parent);
if (ret < 0) {
dev_err(&master->dev, "Failed to power device: %d\n",
ret);
return;
}
}
if (!was_busy)
trace_spi_master_busy(master);
if (!was_busy && master->prepare_transfer_hardware) {
ret = master->prepare_transfer_hardware(master); // 准备好新的传输硬件条件
if (ret) {
dev_err(&master->dev,
"failed to prepare transfer hardware\n");
if (master->auto_runtime_pm)
pm_runtime_put(master->dev.parent);
return;
}
}
trace_spi_message_start(master->cur_msg);
if (master->prepare_message) {
ret = master->prepare_message(master, master->cur_msg); // 如果有传输消息准备方法,就调用
if (ret) {
dev_err(&master->dev,
"failed to prepare message: %d\n", ret);
master->cur_msg->status = ret;
spi_finalize_current_message(master);
return;
}
master->cur_msg_prepared = true;
}
ret = spi_map_msg(master, master->cur_msg);
if (ret) {
master->cur_msg->status = ret;
spi_finalize_current_message(master);
return;
}
ret = master->transfer_one_message(master, master->cur_msg); // 传输一个spi_message,即spi_transfer_one_message()
if (ret) {
dev_err(&master->dev,
"failed to transfer one message from queue\n");
return;
}
}
static int spi_transfer_one_message(struct spi_master *master,
struct spi_message *msg)
{
struct spi_transfer *xfer;
bool keep_cs = false;
int ret = 0;
unsigned long ms = 1;
struct spi_statistics *statm = &master->statistics;
struct spi_statistics *stats = &msg->spi->statistics;
spi_set_cs(msg->spi, true);
SPI_STATISTICS_INCREMENT_FIELD(statm, messages);
SPI_STATISTICS_INCREMENT_FIELD(stats, messages);
list_for_each_entry(xfer, &msg->transfers, transfer_list) { // 轮询message下的所有transfer
trace_spi_transfer_start(msg, xfer);
spi_statistics_add_transfer_stats(statm, xfer, master);
spi_statistics_add_transfer_stats(stats, xfer, master);
if (xfer->tx_buf || xfer->rx_buf) {
reinit_completion(&master->xfer_completion);
ret = master->transfer_one(master, msg->spi, xfer); // 调用master->transfer_one()传输spi_transfer
if (ret < 0) {
SPI_STATISTICS_INCREMENT_FIELD(statm,
errors);
SPI_STATISTICS_INCREMENT_FIELD(stats,
errors);
dev_err(&msg->spi->dev,
"SPI transfer failed: %d\n", ret);
goto out;
}
if (ret > 0) {
ret = 0;
ms = xfer->len * 8 * 1000 / xfer->speed_hz;
ms += ms + 100; /* some tolerance */
ms = wait_for_completion_timeout(&master->xfer_completion,
msecs_to_jiffies(ms)); // 当前线程加入传输完成队列,传输完成时中断处理函数会唤醒当前线程,或者超时唤醒
}
if (ms == 0) {
SPI_STATISTICS_INCREMENT_FIELD(statm,
timedout);
SPI_STATISTICS_INCREMENT_FIELD(stats,
timedout);
dev_err(&msg->spi->dev,
"SPI transfer timed out\n");
msg->status = -ETIMEDOUT;
}
} else {
if (xfer->len)
dev_err(&msg->spi->dev,
"Bufferless transfer has length %u\n",
xfer->len);
}
trace_spi_transfer_stop(msg, xfer);
if (msg->status != -EINPROGRESS)
goto out;
if (xfer->delay_usecs)
udelay(xfer->delay_usecs);
if (xfer->cs_change) {
if (list_is_last(&xfer->transfer_list,
&msg->transfers)) {
keep_cs = true;
} else {
spi_set_cs(msg->spi, false);
udelay(10);
spi_set_cs(msg->spi, true);
}
}
msg->actual_length += xfer->len; // 更新实际传输长度
}
out:
if (ret != 0 || !keep_cs)
spi_set_cs(msg->spi, false);
if (msg->status == -EINPROGRESS)
msg->status = ret;
if (msg->status && master->handle_err)
master->handle_err(master, msg);
spi_finalize_current_message(master); //结束当前消息
return ret;
}
注意,master->transter_one由实际spi主设备驱动实现,用来完成实际的传输协议(在后面SPI bitbang中解释)。
(3)数据传输(同步传输):int spi_sync(struct spi_device *spi, struct spi_message *message)
int spi_sync(struct spi_device *spi, struct spi_message *message)
{
return __spi_sync(spi, message, 0); // 同步数据传输
}
static int __spi_sync(struct spi_device *spi, struct spi_message *message,
int bus_locked)
{
DECLARE_COMPLETION_ONSTACK(done);
int status;
struct spi_master *master = spi->master;
unsigned long flags;
status = __spi_validate(spi, message);
if (status != 0)
return status;
message->complete = spi_complete;
message->context = &done;
message->spi = spi;
SPI_STATISTICS_INCREMENT_FIELD(&master->statistics, spi_sync);
SPI_STATISTICS_INCREMENT_FIELD(&spi->statistics, spi_sync);
if (!bus_locked)
mutex_lock(&master->bus_lock_mutex);
/* If we're not using the legacy transfer method then we will
* try to transfer in the calling context so special case.
* This code would be less tricky if we could remove the
* support for driver implemented message queues.
*/
if (master->transfer == spi_queued_transfer) {
spin_lock_irqsave(&master->bus_lock_spinlock, flags);
trace_spi_message_submit(message);
status = __spi_queued_transfer(spi, message, false); // false表示将spi_message放进消息队列,但是不启动workqueue线程处理
spin_unlock_irqrestore(&master->bus_lock_spinlock, flags);
} else {
status = spi_async_locked(spi, message);
}
if (!bus_locked)
mutex_unlock(&master->bus_lock_mutex);
if (status == 0) {
/* Push out the messages in the calling context if we
* can.
*/
if (master->transfer == spi_queued_transfer) {
SPI_STATISTICS_INCREMENT_FIELD(&master->statistics,
spi_sync_immediate);
SPI_STATISTICS_INCREMENT_FIELD(&spi->statistics,
spi_sync_immediate);
__spi_pump_messages(master, false); // 在当前线程中取出消息队列中的消息,同步处理。
}
wait_for_completion(&done);
status = message->status;
}
message->context = NULL;
return status;
}
SPI master驱动分析:SPI bitbang
spi bitbang是软件模拟的spi主设备驱动。当硬件上没有spi总线主控设备时,可以在软件上模拟spi主控设备,通过gpio信号线同从设备通信。spi bitbang依赖于gpio模拟spi的四根信号线,所以bitbang相关的代码依赖spi-gpio驱动。直接看源码:
static struct platform_driver spi_gpio_driver = {
.driver = {
.name = DRIVER_NAME,
.of_match_table = of_match_ptr(spi_gpio_dt_ids),
},
.probe = spi_gpio_probe, // spi-gpio探测函数
.remove = spi_gpio_remove,
};
static int spi_gpio_probe(struct platform_device *pdev)
{
int status;
struct spi_master *master;
struct spi_gpio *spi_gpio;
struct spi_gpio_platform_data *pdata;
u16 master_flags = 0;
bool use_of = 0;
int num_devices;
status = spi_gpio_probe_dt(pdev); // 从设备树配置中获取spi相关配置信息: MOSI,MISO,SCLK等gpio信息
if (status < 0)
return status;
if (status > 0)
use_of = 1;
pdata = dev_get_platdata(&pdev->dev);
#ifdef GENERIC_BITBANG
if (!pdata || (!use_of && !pdata->num_chipselect))
return -ENODEV;
#endif
if (use_of && !SPI_N_CHIPSEL)
num_devices = 1;
else
num_devices = SPI_N_CHIPSEL;
status = spi_gpio_request(pdata, dev_name(&pdev->dev), &master_flags); // 请求MOSI,MISO,SCLK等gpio信息
if (status < 0)
return status;
master = spi_alloc_master(&pdev->dev, sizeof(*spi_gpio) +
(sizeof(unsigned long) * num_devices)); // 分配spi_master结构对象
if (!master) {
status = -ENOMEM;
goto gpio_free;
}
spi_gpio = spi_master_get_devdata(master);
platform_set_drvdata(pdev, spi_gpio);
spi_gpio->pdev = pdev;
if (pdata)
spi_gpio->pdata = *pdata;
master->bits_per_word_mask = SPI_BPW_RANGE_MASK(1, 32);
master->flags = master_flags;
master->bus_num = pdev->id;
master->num_chipselect = num_devices;
master->setup = spi_gpio_setup;
master->cleanup = spi_gpio_cleanup; // 设置setup和cleanup方法
#ifdef CONFIG_OF
master->dev.of_node = pdev->dev.of_node;
if (use_of) {
int i;
struct device_node *np = pdev->dev.of_node;
/*
* In DT environments, take the CS GPIO from the "cs-gpios"
* property of the node.
*/
if (!SPI_N_CHIPSEL)
spi_gpio->cs_gpios[0] = SPI_GPIO_NO_CHIPSELECT;
else
for (i = 0; i < SPI_N_CHIPSEL; i++) {
status = of_get_named_gpio(np, "cs-gpios", i);
if (status < 0) {
dev_err(&pdev->dev,
"invalid cs-gpios property\n");
goto gpio_free;
}
spi_gpio->cs_gpios[i] = status;
}
}
#endif
spi_gpio->bitbang.master = master;
spi_gpio->bitbang.chipselect = spi_gpio_chipselect; // 设置spi bitbang的片选方法
// 初始化4种时钟模式下的传输方法
if ((master_flags & (SPI_MASTER_NO_TX | SPI_MASTER_NO_RX)) == 0) {
spi_gpio->bitbang.txrx_word[SPI_MODE_0] = spi_gpio_txrx_word_mode0;
spi_gpio->bitbang.txrx_word[SPI_MODE_1] = spi_gpio_txrx_word_mode1;
spi_gpio->bitbang.txrx_word[SPI_MODE_2] = spi_gpio_txrx_word_mode2;
spi_gpio->bitbang.txrx_word[SPI_MODE_3] = spi_gpio_txrx_word_mode3;
} else {
spi_gpio->bitbang.txrx_word[SPI_MODE_0] = spi_gpio_spec_txrx_word_mode0;
spi_gpio->bitbang.txrx_word[SPI_MODE_1] = spi_gpio_spec_txrx_word_mode1;
spi_gpio->bitbang.txrx_word[SPI_MODE_2] = spi_gpio_spec_txrx_word_mode2;
spi_gpio->bitbang.txrx_word[SPI_MODE_3] = spi_gpio_spec_txrx_word_mode3;
}
spi_gpio->bitbang.setup_transfer = spi_bitbang_setup_transfer; //设置bitbang的setup_transfer函数
spi_gpio->bitbang.flags = SPI_CS_HIGH; // 片选信号,高电平有效
status = spi_bitbang_start(&spi_gpio->bitbang); // 启动bitbang,并注册master
if (status < 0) {
gpio_free:
if (SPI_MISO_GPIO != SPI_GPIO_NO_MISO)
gpio_free(SPI_MISO_GPIO);
if (SPI_MOSI_GPIO != SPI_GPIO_NO_MOSI)
gpio_free(SPI_MOSI_GPIO);
gpio_free(SPI_SCK_GPIO);
spi_master_put(master);
}
return status;
}
int spi_bitbang_start(struct spi_bitbang *bitbang)
{
struct spi_master *master = bitbang->master;
int ret;
if (!master || !bitbang->chipselect)
return -EINVAL;
mutex_init(&bitbang->lock);
if (!master->mode_bits)
master->mode_bits = SPI_CPOL | SPI_CPHA | bitbang->flags;
if (master->transfer || master->transfer_one_message)
return -EINVAL;
master->prepare_transfer_hardware = spi_bitbang_prepare_hardware;
master->unprepare_transfer_hardware = spi_bitbang_unprepare_hardware;
master->transfer_one = spi_bitbang_transfer_one;
master->set_cs = spi_bitbang_set_cs; // 设置bitbang硬件准备相关的函数,设置片选函数和spi_transfer传输函数
if (!bitbang->txrx_bufs) {
bitbang->use_dma = 0;
bitbang->txrx_bufs = spi_bitbang_bufs;
if (!master->setup) {
if (!bitbang->setup_transfer)
bitbang->setup_transfer =
spi_bitbang_setup_transfer;
master->setup = spi_bitbang_setup; // 注册时调用bitbang_setup函数
master->cleanup = spi_bitbang_cleanup;
}
}
/* driver may get busy before register() returns, especially
* if someone registered boardinfo for devices
*/
ret = spi_register_master(spi_master_get(master)); // 注册bitbang master
if (ret)
spi_master_put(master);
return 0;
}
(1)注册master加载每个spi_devices时,调用master->setup,即spi_bitbang_setup:
int spi_bitbang_setup(struct spi_device *spi)
{
struct spi_bitbang_cs *cs = spi->controller_state;
struct spi_bitbang *bitbang;
bitbang = spi_master_get_devdata(spi->master);
if (!cs) {
cs = kzalloc(sizeof(*cs), GFP_KERNEL);
if (!cs)
return -ENOMEM;
spi->controller_state = cs;
}
/* per-word shift register access, in hardware or bitbanging */
cs->txrx_word = bitbang->txrx_word[spi->mode & (SPI_CPOL|SPI_CPHA)]; // 根据每个spi_device设备支持的模式选择不同的传输函数
if (!cs->txrx_word)
return -EINVAL;
if (bitbang->setup_transfer) {
int retval = bitbang->setup_transfer(spi, NULL); // NULL表示,调用spi_bitbang_setup_transfer()设置spi_device的默认传输函数和速率
if (retval < 0)
return retval;
}
dev_dbg(&spi->dev, "%s, %u nsec/bit\n", __func__, 2 * cs->nsecs);
/* NOTE we _need_ to call chipselect() early, ideally with adapter
* setup, unless the hardware defaults cooperate to avoid confusion
* between normal (active low) and inverted chipselects.
*/
/* deselect chip (low or high) */
mutex_lock(&bitbang->lock);
if (!bitbang->busy) {
bitbang->chipselect(spi, BITBANG_CS_INACTIVE); // 调用spi_gpio_chipselect()
ndelay(cs->nsecs);
}
mutex_unlock(&bitbang->lock);
return 0;
}
int spi_bitbang_setup_transfer(struct spi_device *spi, struct spi_transfer *t)
{
struct spi_bitbang_cs *cs = spi->controller_state;
u8 bits_per_word;
u32 hz;
if (t) {
bits_per_word = t->bits_per_word;
hz = t->speed_hz;
} else {
bits_per_word = 0;
hz = 0;
}
/* spi_transfer level calls that work per-word */
if (!bits_per_word)
bits_per_word = spi->bits_per_word;
// 设置spi_device各位宽传输函数
if (bits_per_word <= 8)
cs->txrx_bufs = bitbang_txrx_8;
else if (bits_per_word <= 16)
cs->txrx_bufs = bitbang_txrx_16;
else if (bits_per_word <= 32)
cs->txrx_bufs = bitbang_txrx_32;
else
return -EINVAL;
/* nsecs = (clock period)/2 */
// 设置spi_device的传输速率
if (!hz)
hz = spi->max_speed_hz;
if (hz) {
cs->nsecs = (1000000000/2) / hz;
if (cs->nsecs > (MAX_UDELAY_MS * 1000 * 1000))
return -EINVAL;
}
return 0;
}
static void spi_gpio_chipselect(struct spi_device *spi, int is_active)
{
struct spi_gpio *spi_gpio = spi_to_spi_gpio(spi);
unsigned long cs = spi_gpio->cs_gpios[spi->chip_select];
/* set initial clock polarity */
if (is_active)
setsck(spi, spi->mode & SPI_CPOL);
if (cs != SPI_GPIO_NO_CHIPSELECT) {
/* SPI is normally active-low */
gpio_set_value_cansleep(cs, (spi->mode & SPI_CS_HIGH) ? is_active : !is_active); // 设置片选信号
}
}
(2)数据传输过程到master->transfer_one,即调用spi_bitbang_transfer_one:
static int spi_bitbang_transfer_one(struct spi_master *master,
struct spi_device *spi,
struct spi_transfer *transfer)
{
struct spi_bitbang *bitbang = spi_master_get_devdata(master);
int status = 0;
if (bitbang->setup_transfer) {
status = bitbang->setup_transfer(spi, transfer); // 先设置spi的传输函数和传输速率
if (status < 0)
goto out;
}
if (transfer->len)
status = bitbang->txrx_bufs(spi, transfer); // 调用实际的传输函数,即spi_bitbang_bufs
if (status == transfer->len)
status = 0;
else if (status >= 0)
status = -EREMOTEIO;
out:
spi_finalize_current_transfer(master);
return status;
}
static int spi_bitbang_bufs(struct spi_device *spi, struct spi_transfer *t)
{
struct spi_bitbang_cs *cs = spi->controller_state;
unsigned nsecs = cs->nsecs;
return cs->txrx_bufs(spi, cs->txrx_word, nsecs, t); // 根据spi_device的片选传输字长选取不同的传输函数
}
以8bit传输函数为例:
static unsigned bitbang_txrx_8(
struct spi_device *spi,
u32 (*txrx_word)(struct spi_device *spi,
unsigned nsecs,
u32 word, u8 bits),
unsigned ns,
struct spi_transfer *t
) {
unsigned bits = t->bits_per_word;
unsigned count = t->len;
const u8 *tx = t->tx_buf;
u8 *rx = t->rx_buf;
while (likely(count > 0)) {
u8 word = 0;
if (tx)
word = *tx++;
word = txrx_word(spi, ns, word, bits); // 调用实际传输函数spi_gpio_txrx_word_modex
if (rx)
*rx++ = word;
count -= 1;
}
return t->len - count;
}
以spi_gpio_txrx_word_mode0为例,
static u32 spi_gpio_txrx_word_mode0(struct spi_device *spi,
unsigned nsecs, u32 word, u8 bits)
{
return bitbang_txrx_be_cpha0(spi, nsecs, 0, 0, word, bits); // 调用bitbang模拟收发函数
}
// 软件模拟SCLK,MISO和MOSI信号实现数据收发
static inline u32
bitbang_txrx_be_cpha0(struct spi_device *spi,
unsigned nsecs, unsigned cpol, unsigned flags,
u32 word, u8 bits)
{
/* if (cpol == 0) this is SPI_MODE_0; else this is SPI_MODE_2 */
u32 oldbit = (!(word & (1<<(bits-1)))) << 31;
/* clock starts at inactive polarity */
for (word <<= (32 - bits); likely(bits); bits--) {
/* setup MSB (to slave) on trailing edge */
if ((flags & SPI_MASTER_NO_TX) == 0) {
if ((word & (1 << 31)) != oldbit) {
setmosi(spi, word & (1 << 31));
oldbit = word & (1 << 31);
}
}
spidelay(nsecs); /* T(setup) */
setsck(spi, !cpol);
spidelay(nsecs);
/* sample MSB (from slave) on leading edge */
word <<= 1;
if ((flags & SPI_MASTER_NO_RX) == 0)
word |= getmiso(spi);
setsck(spi, cpol);
}
return word;
}
转载于:https://my.oschina.net/yepanl/blog/2992210