概述
linux SPI驱动框架主要分为核心层,控制器驱动层以及设备驱动层。具体结构可参考下图
图中,最下层是硬件空间,SPI总线控制器,总线控制器负责硬件上的数据交互。内核空间中,需要有对应的控制器驱动,对硬件进行操作。
核心层的作用是,向控制器驱动层提供注册SPI控制器驱动的接口,并提供一些需要控制器驱动实现的回调函数。
核心层向上,对SPI设备驱动,提供标准的spi收发API,以及设备注册函数。
所以当有SPI设备驱动发起一次传输时,设备驱动会调用SPI核心层的收发函数(spi_sync/spi_async),核心层的收发函数,会回调控制器驱动层实现的硬件相关的发送回调函数。从而实现SPI数据的收发。
本文主要介绍SPI核心层相关API及数据结构。
SPI核心层
核心层目录说明
SPI核心层代码位于(drivers/spi/spi.c),头文件位于(include/linux/spi/spi.h)
spi.c一方面对SPI子系统进行初始化工作,注册spi bus,注册spi_master class,同事提供spi设备驱动对spi总线进行操作的API。
spi.h包含了spi核心层的一些重要数据结构,struct spi_master; struct spi_transfer; struct spi_message,以及一些实现比较简单的函数等。
子系统初始化
struct bus_type spi_bus_type = {
.name = "spi",
.dev_groups = spi_dev_groups,
.match = spi_match_device,
.uevent = spi_uevent,
};
static struct class spi_master_class = {
.name = "spi_master",
.owner = THIS_MODULE,
.dev_release = spi_master_release,
.dev_groups = spi_master_groups,
};
spi_bus_type为spi总线类型,通过bus_register()函数将SPI 总线注册进总线,成功注册后,在/sys/bus 下即可找到spi 文件目录。
spi_master_class为spi控制器设备类,通过调用class_register()函数注册设备类,成功注册后,在/sys/class目录下即可找到spi_master文件目录。
然后来看spi子系统初始化函数,仅仅是注册了spi bus,以及spi_master class。
spi_init函数
static int __init spi_init(void)
{
int status;
buf = kmalloc(SPI_BUFSIZ, GFP_KERNEL);
if (!buf) {
status = -ENOMEM;
goto err0;
}
status = bus_register(&spi_bus_type); /*注册spi bus*/
if (status < 0)
goto err1;
status = class_register(&spi_master_class); /* 注册spi_master类 */
if (status < 0)
goto err2;
if (IS_ENABLED(CONFIG_OF_DYNAMIC))
WARN_ON(of_reconfig_notifier_register(&spi_of_notifier));
if (IS_ENABLED(CONFIG_ACPI))
WARN_ON(acpi_reconfig_notifier_register(&spi_acpi_notifier));
return 0;
err2:
bus_unregister(&spi_bus_type);
err1:
kfree(buf);
buf = NULL;
err0:
return status;
}
再来看一下spi从设备和从设备驱动匹配函数,也即是spi_bus_type.match函数。
spi_match_device函数
static int spi_match_device(struct device *dev, struct device_driver *drv)
{
const struct spi_device *spi = to_spi_device(dev);
const struct spi_driver *sdrv = to_spi_driver(drv);
/* Attempt an OF style match */
if (of_driver_match_device(dev, drv)) (1)
return 1;
/* Then try ACPI */
if (acpi_driver_match_device(dev, drv)) (2)
return 1;
if (sdrv->id_table) (3)
return !!spi_match_id(sdrv->id_table, spi);
return strcmp(spi->modalias, drv->name) == 0; (4)
}
说明:
(1)比较驱动中的of_match_table的compatible和device的of_node的compatible,判断两者是否相等
(2)比较驱动中的acpi_match_table的compatible和device的of_node的compatible,判断两者是否相等
(3)判断驱动中是否支持id数组,如果支持,查找匹配此id的spi_device。
(4)比较设备的名字的和驱动的名字是否相同。
核心数据结构
struct spi_master
struct spi_master {
struct device dev; /* SPI设备的device数据结构 */
s16 bus_num; /* SPI总线序号 */
u16 num_chipselect; /* 片选信号数量 */
u16 dma_alignment; /* SPI控制器DMA缓冲区对齐定义 */
u16 mode_bits; /* 工作模式位,由驱动定义 */
u32 min_speed_hz; /* 最小速度 */
u32 max_speed_hz; /* 最小速度 */
u16 flags; /* 限制条件标志 */
int (*setup)(struct spi_device *spi); /* 设置SPI设备的工作参数 */
int (*transfer)(struct spi_device *spi, /* SPI发送函数1 */
struct spi_message *mesg);
void (*cleanup)(struct spi_device *spi); /* SPI清除函数,当spi_master被释放时调用 */
int (*transfer_one_message)(struct spi_master *master, /* SPI发送函数2 */
int (*transfer_one)(struct spi_master *master, struct spi_device *spi, /* SPI发送函数3 */
struct spi_transfer *transfer);
...
};
上述为struct spi_master数据结构,结构体本身比较大,挑选了一些个人认为比较核心的成员。
该结构体为spi控制器驱动核心数据结构。控制器驱动需要配置其中一些参数,填充一些回调函数。
需要注意的是,上述代码段中,有三个transfer函数。这里先简单的说明一下,在spi_master注册时,会首先判断transfer函数是否实现,如果没实现,核心层会自动填充一个默认的transfer函数。当填充了默认的transfer函数后,会判断控制器驱动是否实现transfer_one_message,如果没实现,则核心层会填充一个默认的transfer_one_message函数,最后控制器驱动只需要实现transfer_one回调函数,就可以让驱动层实现spi数据收发了。
当然,有些厂商会自己实现transfer或transfer_one_message函数,替代内核默认的逻辑。不过总结来说,三个transfer函数只需要实现其中一个就可以了。
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;
};
spi_driver比较简单,根据上文介绍的spi_match_device逻辑,只需要实现of_match_table或acpi_match_table或id_table,或者最简单的driver->name,能让spi_match_device返回match成功即可。match成功会调用probe函数。remove函数在驱动卸载时调用。
spi_device数据结构就不介绍了,一般用设备树,会自动生成。
struct spi_transfer & struct spi_message
struct spi_transfer {
const void *tx_buf;
void *rx_buf;
unsigned len;
...
u8 bits_per_word;
u16 delay_usecs;
u32 speed_hz;
...
struct list_head transfer_list;
};
struct spi_message {
struct list_head transfers;
...
struct spi_device *spi;
...
void (*complete)(void *context);
void *context;
...
struct list_head queue;
};
一个spi_message是一次数据交换的原子请求,而spi_message由多个spi_transfer结构组成,这些spi_transfer通过一个链表组织在一起。
spi_message通过spi_sync函数或spi_async函数发送。
核心API
spi_register_master函数
int spi_register_master(struct spi_master *master)
{
...
status = of_spi_register_master(master); (1)
if (status)
return status;
...
dev_set_name(&master->dev, "spi%u", master->bus_num);
status = device_add(&master->dev); (2)
if (status < 0)
goto done;
dev_dbg(dev, "registered master %s%s\n", dev_name(&master->dev),
dynamic ? " (dynamic)" : "");
if (master->transfer)
dev_info(dev, "master is unqueued, this is deprecated\n");
else {
status = spi_master_initialize_queue(master); (3)
if (status) {
device_del(&master->dev);
goto done;
}
}
...
mutex_lock(&board_lock);
list_add_tail(&master->list, &spi_master_list);
list_for_each_entry(bi, &board_list, list)
spi_match_master_to_boardinfo(master, &bi->board_info); (4)
mutex_unlock(&board_lock);
...
of_register_spi_devices(master); (5)
...
done:
return status;
}
spi_register_master是控制器驱动中最核心的api,控制器驱动通过该api注册到系统中。
说明:
(1)of_spi_register_master函数内部,根据设备树节点中的"cs-gpios",向struct spi_master添加gpio cs引脚。
(2)将device注册到设备模型中。
(3)如果控制器驱动没有自己实现transfer函数,则初始化发送队列。(核心层填充默认transfer函数)
(4)老的方式,遍历所有spi_board_info数据结构,并注册spi_device
(5)新的设备树方式,遍历spi控制器节点下所有子节点,并注册成对应的spi_device设备。
再来仔细看下spi_master_initialize_queue函数
static int spi_master_initialize_queue(struct spi_master *master)
{
......
master->queued = true;
master->transfer = spi_queued_transfer;
if (!master->transfer_one_message)
master->transfer_one_message = spi_transfer_one_message;
/* Initialize and start queue */
ret = spi_init_queue(master);
......
ret = spi_start_queue(master);
......
}
该函数把master->transfer回调字段设置为默认的实现函数:spi_queued_transfer,如果控制器驱动没有实现transfer_one_message回调,用默认的spi_transfer_one_message函数进行赋值。然后分别调用spi_init_queue和spi_start_queue函数初始化队列并启动工作线程。spi_init_queue函数最主要的作用就是建立一个内核工作线程。
然后来一起看下spi_init_queue和spi_start_queue函数
static int spi_init_queue(struct spi_master *master)
{
......
kthread_init_worker(&master->kworker);
master->kworker_task = kthread_run(kthread_worker_fn, &master->kworker, "%s", dev_name(&master->dev));
......
kthread_init_work(&master->pump_messages, spi_pump_messages);
......
return 0;
}
static int spi_start_queue(struct spi_master *master)
{
......
master->running = true;
master->cur_msg = NULL;
......
kthread_queue_work(&master->kworker, &master->pump_messages);
......
return 0;
}
spi_init_queue函数先初始化kthread_worker,为kthread_worker创建一个内核线程来处理work,随后初始化kthread_work,设置work执行函数,work执行函数为spi_pump_messages
spi_start_queue就相对简单了,只是唤醒该工作线程而已;自此,队列化的相关工作已经完成,系统等待message请求被发起,然后在工作线程中处理message的传送工作。
spi_message_init函数
static inline void spi_message_init_no_memset(struct spi_message *m)
{
INIT_LIST_HEAD(&m->transfers);
INIT_LIST_HEAD(&m->resources);
}
static inline void spi_message_init(struct spi_message *m)
{
memset(m, 0, sizeof *m);
spi_message_init_no_memset(m);
}
spi_message_init函数用于对spi_massage进行初始化,并初始化链表头。
spi_message_add_tail函数
static inline void
spi_message_add_tail(struct spi_transfer *t, struct spi_message *m)
{
list_add_tail(&t->transfer_list, &m->transfers);
}
spi_message_add_tail函数用于把spi_transfer添加到spi_message传输链表中。
一般spi设备驱动的发送函数,会定义spi_message和若干个spi_transfer,设置spi_transfer的txbuff/rxbuff,len等,调用spi_message_init函数初始化spi_message,调用spi_message_add_tail把spi_transfer添加到spi_message链表中。最后调用spi_sync或spi_async发送。
spi_async
spi_async是异步执行的,不会等待传输是否完成,就直接返回。调用关系为:
spi_async->
__spi_async->
master->transfer
在上文中的spi_register_master函数中,我们已经介绍过,当控制器驱动没有实现transfer函数时,内核会自己填充默认的transfer函数(spi_queued_transfer)。这里我们假设控制器驱动既没有实现transfer,也没有实现transfer_one_message,只实现了transfer_one函数。来看一下内核默认transfer函数的逻辑。
spi_queued_transfer(struct spi_device *spi, struct spi_message *msg)->
__spi_queued_transfer(spi, msg, true)
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);
...
list_add_tail(&msg->queue, &master->queue);
if (!master->busy && need_pump)
kthread_queue_work(&master->kworker, &master->pump_messages);
spin_unlock_irqrestore(&master->queue_lock, flags);
return 0;
}
这里我们可以看到,__spi_queued_transfer(spi, msg, true)函数中,注意这里第三个参数是true,把message添加到master的发送链表中。随后仅仅是唤醒工作线程,完成该步后,spi_async即返回,不等待发送完成。
spi_sync
与spi_async不同,spi_sync是同步执行的,会等待传输完成,随后返回。调用关系为:
spi_sync->
__spi_sync
然后看一下__spi_sync函数代码,这里我们也假设控制器驱动既没有实现transfer,也没有实现transfer_one_message,只实现了transfer_one函数:
static void spi_complete(void *arg)
{
complete(arg);
}
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;
...
message->complete = spi_complete; (1)
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); (2)
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); (3)
}
wait_for_completion(&done); (4)
status = message->status;
}
message->context = NULL;
return status;
}
说明:
(1)设置spi_message完成后回调函数,回调函数中仅仅调用complete函数,唤醒等待唤醒的线程。意思就是,当发送spi_message的工作线程完成发送后,唤醒在等待的spi发送完成的spi_sync函数所在的线程。
(2)由于使用内核默认的spi_queued_transfer,所以会调用__spi_queued_transfer(spi, message, false);这个函数上文介绍spi_async时已经介绍过了,注意这里第三个传参是false。所以所做的操作仅仅是把spi_message添加到master的发送链表中。随即返回
(3)__spi_pump_messages(master, false),false意思是标记不在工作线程中执行该函数。调用该函数,把spi_message发出去,该函数中会判断当前状态,如果可以直接发,则直接在当前线程中发送,如果不能直接发,则唤醒工作线程,稍后发送。
(4)睡眠等待发送完成函数释放的完成信号量,当接收到信号量时,证明发送完成唤醒并结束spi_sync函数。
工作线程的工作函数是spi_pump_messages来看一下该函数实现:
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);
}
上文中不管是spi_sync,还是spi_async中,不管是直接调用该函数中的__spi_pump_messages(sync)还是通过工作线程,作为线程服务函数调用,最终发送数据都是通过__spi_pump_messages实现的。
与__spi_sync中不同的是工作函数中调用的__spi_pump_messages(master, true);第二个参数为true,标记为是否在工作线程中。
最后来看一下__spi_pump_messages函数的实现:
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) {
kthread_queue_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) {
kthread_queue_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))
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);
list_del_init(&master->cur_msg->queue);
if (master->busy)
was_busy = true;
else
master->busy = true;
spin_unlock_irqrestore(&master->queue_lock, flags);
mutex_lock(&master->io_mutex);
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);
mutex_unlock(&master->io_mutex);
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);
mutex_unlock(&master->io_mutex);
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);
goto out;
}
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);
goto out;
}
ret = master->transfer_one_message(master, master->cur_msg);
if (ret) {
dev_err(&master->dev,
"failed to transfer one message from queue\n");
goto out;
}
out:
mutex_unlock(&master->io_mutex);
/* Prod the scheduler in case transfer_one() was busy waiting */
if (!ret)
cond_resched();
}
函数源码比较多,总结一下大概可以概括为,作为工作线程服务函数时,会调用master->transfer_one_message函数。当__spi_sync中调用该函数时,如果资源没有冲突,可以直接在当前线程中调用transfer_one_message则调用,如果不能,则添加到工作线程队列中,通过工作线程再次调用__spi_pump_messages,把数据发送出去。
在上文介绍spi_register_master->spi_master_initialize_queue中,我们已经看到,内核默认填充的master->transfer_one_message函数为spi_transfer_one_message,下面来看一下源码:
static int spi_transfer_one_message(struct spi_master *master,
struct spi_message *msg)
{
struct spi_transfer *xfer;
...
spi_set_cs(msg->spi, true); /* cs引脚拉高 */
SPI_STATISTICS_INCREMENT_FIELD(statm, messages);
SPI_STATISTICS_INCREMENT_FIELD(stats, messages);
list_for_each_entry(xfer, &msg->transfers, transfer_list) {
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); (1)
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;
}
...
}
out:
if (ret != 0 || !keep_cs)
spi_set_cs(msg->spi, false); /* 拉低cs */
spi_finalize_current_message(master); (2)
return ret;
}
void spi_finalize_current_message(struct spi_master *master)
{
...
kthread_queue_work(&master->kworker, &master->pump_messages);
...
mesg->state = NULL;
if (mesg->complete)
mesg->complete(mesg->context);
}
提炼了一下代码,(1)可以看到函数中先是遍历了message中的transfer链表,然后一个个调用控制器驱动的transfer_one函数。随后(2)调用spi_finalize_current_message函数,函数代码也在代码段中贴出来了。重要的两个操作一个是,唤醒下一次工作线程,然后判断message的回调函数是否填充,如果存在,则回调。对于spi_sync来说,message的回调函数是complete(&done),spi_sync阻塞的wait_for_completion(&done)接收到信号量会被唤醒,意味着数据以及收发完成,结束本次spi_sync。
spi_sync&spi_async总结
spi_sync和spi_async是spi设备驱动通过spi总线发送数据的重要函数。经过上文的分析。简单的总结下。
spi_sync函数是同步的,把spi_message发送出去,会等待发送完成,发送完成才会返回,正常情况下调用的都是spi_sync函数。
spi_async函数本身的异步的,把spi_message推到工作线程中,就不管了,所以试想一下,当发送数据时,可以不管是否发送完成。但是读取数据时,如果一次发送(数据交换)没有完成就返回了,那么rx_buff中是否有数据时不能保障的。那么,spi_async是不是就不能用来读取数据了呢?
答案是否定的,可以仿照spi_sync,在spi_async调用之前,手动设置spi_message的逻辑,不过有点麻烦就是了,还不如直接调用spi_sync。。
static void spi_complete(void *arg)
{
complete(arg);
}
{
...
message->complete = spi_complete;
message->context = &done;
...
spi_async(spi, message);
...
wait_for_completion(&done);
...
}
SPI驱动框架链接:
Linux SPI驱动框架(2)——控制器驱动层
Linux SPI驱动框架(3)——设备驱动层