文章目录
- 参考资料:
- 一、SPI传输概述
- 二、SPI传输的两种方法
- 2.1 旧方法
- 2.2 新方法
参考资料:
参考资料:
- 参考内核源码:
drivers\spi\spi.c
一、SPI传输概述
SPI控制器的作用是发起与它下面挂接的SPI设备之间的数据传输,那么控制器驱动程序的核心就是实现与设备之间的数据传输过程。在内核中,SPI传输的最小单位是spi_transfer
,对于一个设备,可以发起多个spi_transfer
。这些spi_transfer
,会放入一个spi_message
里面。每个SPI设备都有一个自己的spi_message
,同一个spi_master
下的spi_message
,放在一个队里。
- spi_transfer:指定tx_buf、rx_buf、len
struct spi_transfer {
const void *tx_buf;
void *rx_buf;
...
}
- 同一个SPI设备的spi_transfer,使用spi_message来管理:
struct spi_message {
struct list_head transfers; //管理spi_transfer
...
}
- 同一个SPI Master下的spi_message,放在一个队列里:
struct spi_master {
...
struct list_head queue; //存放每个spi_device的spi_message
...
}
所以,反过来,SPI传输的流程是:
- 从
spi_master
的队列里取出每一个spi_message
- 从
spi_message
的队里里取出一个spi_transfer
- 处理
spi_transfer
- 处理
- 从
二、SPI传输的两种方法
在spi_master
结构中,有两个传输函数,函数指针transfer
代表旧方法,函数指针transfer_one
代表新方法。
struct spi_master {
...
/* 旧方法 */
int (*transfer) (struct spi_device *spi, struct spi_message *mesg);
/* 新方法 */
int (*transfer_one)(struct spi_master *master, struct spi_device *spi, struct spi_transfer *transfer);
...
}
2.1 旧方法
内核传输函数入口spi_sync()
:
int spi_sync(struct spi_device *spi, struct spi_message *message)
{
ret = __spi_sync(spi, message);
}
继续调用__spi_sync()
,里面设置了传输完成回调函数spi_complete
,如果master->transfer == spi_queued_transfer
表示使用新方法,新方法使用内核提供的transfer
函数,它会帮我们把spi_message
放入queue
并处理。else分支spi_async_locked
表示的是旧方法,需要我们自己实现transfer
函数,自己管理queue
,自己触发传输。spi_async_locked
是异步传输,触发传输后马上返回,随后wait_for_completion(&done)
等待传输结果。
static int __spi_sync(struct spi_device *spi, struct spi_message *message)
{
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 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);
spin_unlock_irqrestore(&master->bus_lock_spinlock, flags);
} else {
status = spi_async_locked(spi, message); //老方法,异步传输
}
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_async_locked()
, 它调用__spi_async()
, 继续往下,最终调用master->transfer(spi, message)
,这个就是自己实现的transfer
函数。
int spi_async_locked(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);
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); //需要自己实现transfer
}
2.2 新方法
新方法第一步会调用__spi_queued_transfer()
将spi_message
放入队列,然后在调用__spi_pump_messages()
压出数据进行处理。流程如上图。