一、前言
前面我们简单的介绍了 I2C总线 的基本框架,本文则将继续讲述另一种常用的 外设总线,即 SPI总线。SPI总线 常用于 传感器或者flash等外设。本文将沿着 SPI总线驱动代码,从整体上梳理一下 SPI框架 的使用及原理。
注意:本文将假设读者已经熟悉SPI总线协议,如若有读者不熟悉请自行查阅资料
二、正文
本节将使用 m25p80 这个驱动文件,m25p80 是常用 flash 的驱动代码,其中使用的就是 SPI总线。我们将以该模块为例,取出其中的 SPI总线 部分进行代码说明,而说明顺序将按照使用的步骤来说明,分为:
- SPI驱动注册
- SPI驱动写
- SPI驱动读
2.1 SPI控制器注册
每个 Soc 上都带有 SPI控制器 用以与外设进行同行,在了解 SPI驱动框架 之前,我们可以先大致地了解一下 SPI控制器 的注册过程,这个过程与 SPI设备的生成 有关系,有助于我们理解后面 SPI框架 的一些操作。代码示例如下:
/* SPI控制器注册调用图谱 */
spi_alloc_master
spi_register_controller
->spi_controller_initialize_queue
->spi_queued_transfer
->spi_transfer_one_message
->spi_init_queue
->spi_pump_messages
->spi_start_queue
->of_register_spi_devices
->of_register_spi_device
->spi_alloc_device
->of_modalias_node
->spi_add_device
->device_add
->bus_add_device
/***********SPI控制器probe函数***********/
static int sun6i_spi_probe(struct platform_device *pdev)
{
struct spi_master *master;
struct sun6i_spi *sspi;
struct resource *res;
int ret = 0, irq;
/* 分配一个SPI控制器 */
master = spi_alloc_master(&pdev->dev, sizeof(struct sun6i_spi));
if (!master) {
dev_err(&pdev->dev, "Unable to allocate SPI Master\n");
return -ENOMEM;
}
......
sspi->master = master;
sspi->fifo_depth = (unsigned long)of_device_get_match_data(&pdev->dev);
/* 初始化并填充SPI控制器 */
master->max_speed_hz = 100 * 1000 * 1000;
master->min_speed_hz = 3 * 1000;
master->set_cs = sun6i_spi_set_cs;
master->transfer_one = sun6i_spi_transfer_one;
master->num_chipselect = 4;
master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH | SPI_LSB_FIRST;
master->bits_per_word_mask = SPI_BPW_MASK(8);
master->dev.of_node = pdev->dev.of_node;
master->auto_runtime_pm = true;
master->max_transfer_size = sun6i_spi_max_transfer_size;
......
/* 注册SPI控制器,该接口完成了大部分的工作 */
ret = devm_spi_register_master(&pdev->dev, master);
if (ret) {
dev_err(&pdev->dev, "cannot register SPI master\n");
goto err_pm_disable;
}
return 0;
err_pm_disable:
pm_runtime_disable(&pdev->dev);
sun6i_spi_runtime_suspend(&pdev->dev);
err_free_master:
spi_master_put(master);
return ret;
}
/* 注册SPI控制器 */
int spi_register_controller(struct spi_controller *ctlr)
{
struct device *dev = ctlr->dev.parent;
struct boardinfo *bi;
int status = -ENODEV;
int id, first_dynamic;
if (!dev)
return -ENODEV;
if (!spi_controller_is_slave(ctlr)) {
status = of_spi_register_master(ctlr);
if (status)
return status;
}
/* even if it's just one always-selected device, there must
* be at least one chipselect
*/
if (ctlr->num_chipselect == 0)
return -EINVAL;
......
/* 初始化和填充SPI控制器的相关成员 */
INIT_LIST_HEAD(&ctlr->queue);
spin_lock_init(&ctlr->queue_lock);
spin_lock_init(&ctlr->bus_lock_spinlock);
mutex_init(&ctlr->bus_lock_mutex);
mutex_init(&ctlr->io_mutex);
ctlr->bus_lock_flag = 0;
init_completion(&ctlr->xfer_completion);
if (!ctlr->max_dma_len)
ctlr->max_dma_len = INT_MAX;
......
if (ctlr->transfer)
dev_info(dev, "controller is unqueued, this is deprecated\n");
else {
/*
初始化控制器的消息队列,该队列在SPI驱动读写时会用到,需要重点关注。
所有SPI数据传输会都被以消息的方式加入消息对列,再有内核线程完成消息的传输,即数据的读写
*/
status = spi_controller_initialize_queue(ctlr);
if (status) {
......
goto done;
}
}
......
/*
使用设备树注册SPI设备。
SPI设备也就是在这里生成 ,生成SPI设备会在匹配时使用到
*/
of_register_spi_devices(ctlr);
......
done:
return status;
}
/* 初始化SPI控制器的消息队列和数据传输相关的回调接口 */
static int spi_controller_initialize_queue(struct spi_controller *ctlr)
{
int ret;
/* 注册SPI控制器的transfer回调,该回调会在传输数据时使用到 */
ctlr->transfer = spi_queued_transfer;
/* 注册SPI控制器的transfer_one_message回调,该回调会在传输数据时使用到 */
if (!ctlr->transfer_one_message)
ctlr->transfer_one_message = spi_transfer_one_message;
/* 初始化消息队列 */
ret = spi_init_queue(ctlr);
if (ret) {
......
}
ctlr->queued = true;
/* 开始队列任务,每个SPI控制器对有开辟一个线程来维护SPI的消息队列 */
ret = spi_start_queue(ctlr);
if (ret) {
......
}
return 0;
......
}
/* 初始化SPI控制器队列 */
static int spi_init_queue(struct spi_controller *ctlr)
{
/* 维护SPI消息队列的线程的相关参数,比如优先级 */
struct sched_param param = { .sched_priority = MAX_RT_PRIO - 1 };
ctlr->running = false;
ctlr->busy = false;
/*
初始化一个内核worker和线程来执行spi_pump_messages。
关于worker和内核线程有兴趣的读者请自行查阅其机制
*/
kthread_init_worker(&ctlr->kworker);
ctlr->kworker_task = kthread_run(kthread_worker_fn, &ctlr->kworker,
"%s", dev_name(&ctlr->dev));
if (IS_ERR(ctlr->kworker_task)) {
dev_err(&ctlr->dev, "failed to create message pump task\n");
return PTR_ERR(ctlr->kworker_task);
}
kthread_init_work(&ctlr->pump_messages, spi_pump_messages);
......
return 0;
}
/* 开始维护消息队列 */
static int spi_start_queue(struct spi_controller *ctlr)
{
unsigned long flags;
spin_lock_irqsave(&ctlr->queue_lock, flags);
if (ctlr->running || ctlr->busy) {
spin_unlock_irqrestore(&ctlr->queue_lock, flags);
return -EBUSY;
}
ctlr->running = true;
ctlr->cur_msg = NULL;
spin_unlock_irqrestore(&ctlr->queue_lock, flags);
/* 这里添加到队列里面的函数其实就是前面指定的>spi_pump_messages */
kthread_queue_work(&ctlr->kworker, &ctlr->pump_messages);
return 0;
}
/* 从设备树中解析并生成SPI设备 */
static void of_register_spi_devices(struct spi_controller *ctlr)
{
struct spi_device *spi;
struct device_node *nc;
if (!ctlr->dev.of_node)
return;
/* 遍历设备树中的子节点,并对有效的子节点调用of_register_spi_device */
for_each_available_child_of_node(ctlr->dev.of_node, nc) {
if (of_node_test_and_set_flag(nc, OF_POPULATED))
continue;
spi = of_register_spi_device(ctlr, nc);
if (IS_ERR(spi)) {
dev_warn(&ctlr->dev,
"Failed to create SPI device for %pOF\n", nc);
of_node_clear_flag(nc, OF_POPULATED);
}
}
}
/* 从设备树节点中生成并注册SPI设备 */
static struct spi_device * of_register_spi_device(struct spi_controller *ctlr, struct device_node *nc)
{
struct spi_device *spi;
int rc;
/* 分配一个PSI设备 */
spi = spi_alloc_device(ctlr);
......
/* 使用设备树中的节点属性名字为SPI的别名进行赋值 */
rc = of_modalias_node(nc, spi->modalias,
sizeof(spi->modalias));
......
/* 向内核中添加SPI设备 */
rc = spi_add_device(spi);
if (rc) {
dev_err(&ctlr->dev, "spi_device register error %pOF\n", nc);
goto err_of_node_put;
}
return spi;
......
}
/* 分配并初始化一个SPI设备 */
struct spi_device *spi_alloc_device(struct spi_controller *ctlr)
{
struct spi_device *spi;
......
spi = kzalloc(sizeof(*spi), GFP_KERNEL);
......
spi->master = spi->controller = ctlr;
spi->dev.parent = &ctlr->dev;
spi->dev.bus = &spi_bus_type;
spi->dev.release = spidev_release;
spi->cs_gpio = -ENOENT;
spin_lock_init(&spi->statistics.lock);
device_initialize(&spi->dev);
return spi;
}
/* 使用节点名设置别名 */
int of_modalias_node(struct device_node *node, char *modalias, int len)
{
const char *compatible, *p;
int cplen;
/* 获取节点中的compatible属性值 */
compatible = of_get_property(node, "compatible", &cplen);
......
/* 从中以 ,号 进行截断 */
p = strchr(compatible, ',');
/* 将p指向的compatible值赋值给别名modalias */
strlcpy(modalias, p ? p + 1 : compatible, len);
return 0;
}
int spi_add_device(struct spi_device *spi)
{
static DEFINE_MUTEX(spi_add_lock);
struct spi_controller *ctlr = spi->controller;
struct device *dev = ctlr->dev.parent;
int status;
......
/* 将SPI设备中的device成员注册到内核中 */
status = device_add(&spi->dev);
......
}
2.2 SPI设备驱动注册
编写 SPI驱动 的第一步就是需要注册相关驱动,让内核可以操作相应的外设。而 SPI驱动结构体 及 驱动代码示例如下:
/* SPI驱动注册调用图谱 */
module_spi_driver
->__spi_register_driver
->driver_register
->bus_add_driver
->driver_attach
->__driver_attach
->driver_match_device
->spi_match_device
->driver_probe_device
->really_probe
->dev->bus->probe(spi_drv_probe)
->m25p_probe
/*************** SPI驱动结构体说明 ***************/
struct spi_driver {
/* SPI设备ID表,根据笔者理解该表的作用不大。一般声明设备ID表的驱动都是为了支持热插拔 */
const struct spi_device_id *id_table;
/* SPI驱动匹配后的probe函数及卸载时的remove函数 */
int (*probe)(struct spi_device *spi);
int (*remove)(struct spi_device *spi);
/* 设备驱动抽象结构体 */
struct device_driver driver;
};
/*************** SPI驱动注册代码示例 ***************/
/*
m25p_ids为SPI设备ID表
按照笔者理解设备ID表一般用于热插拔,而该表在笔者阅读代码中似乎没起到作用
欢迎指导的读者与笔者进行交流
*/
static const struct spi_device_id m25p_ids[] = {
{"spi-nor"},
{"s25sl064a"}, {"w25x16"}, {"m25p10"}, {"m25px64"},
{"at25df321a"}, {"at25df641"}, {"at26df081a"},
{"mx25l4005a"}, {"mx25l1606e"}, {"mx25l6405d"}, {"mx25l12805d"},
{"mx25l25635e"},{"mx66l51235l"},
{"n25q064"}, {"n25q128a11"}, {"n25q128a13"}, {"n25q512a"},
{"s25fl256s1"}, {"s25fl512s"}, {"s25sl12801"}, {"s25fl008k"},
{"s25fl064k"},
{"sst25vf040b"},{"sst25vf016b"},{"sst25vf032b"},{"sst25wf040"},
{"m25p40"}, {"m25p80"}, {"m25p16"}, {"m25p32"},
{"m25p64"}, {"m25p128"},
{"w25x80"}, {"w25x32"}, {"w25q32"}, {"w25q32dw"},
{"w25q80bl"}, {"w25q128"}, {"w25q256"},
{"m25p05-nonjedec"}, {"m25p10-nonjedec"}, {"m25p20-nonjedec"},
{"m25p40-nonjedec"}, {"m25p80-nonjedec"}, {"m25p16-nonjedec"},
{"m25p32-nonjedec"}, {"m25p64-nonjedec"}, {"m25p128-nonjedec"},
{ "mr25h256" }, /* 256 Kib, 40 MHz */
{ "mr25h10" }, /* 1 Mib, 40 MHz */
{ "mr25h40" }, /* 4 Mib, 40 MHz */
{ },
};
/*
MODULE_DEVICE_TABLE一般用于声明对应的设备表,
该表会在编译阶段被加入设备依赖文件中,在热插拔设备时,可以通过该表来判断并加载相应的驱动。
其代码如下:
可以看到只是使用了typeof关键字来声明相同类型的表格结构体
#define MODULE_DEVICE_TABLE(type, name) \
extern typeof(name) __mod_##type##__##name##_device_table \
__attribute__ ((unused, alias(__stringify(name))))
*/
MODULE_DEVICE_TABLE(spi, m25p_ids);
/* SPI驱动的设备树匹配ID表,每个驱动都需要的,这里不再赘述 */
static const struct of_device_id m25p_of_table[] = {
/*
* Generic compatibility for SPI NOR that can be identified by the
* JEDEC READ ID opcode (0x9F). Use this, if possible.
*/
{ .compatible = "jedec,spi-nor" },
{}
};
MODULE_DEVICE_TABLE(of, m25p_of_table);
/* SPI驱动结构体声明 */
static struct spi_driver m25p80_driver = {
/* driver成员跟一般的驱动一样,声明驱动名称及匹配表 */
.driver = {
.name = "m25p80",
.of_match_table = m25p_of_table,
},
/* 热插拔时用到设备ID表 */
.id_table = m25p_ids,
/* 驱动的probe函数 */
.probe = m25p_probe,
/* 驱动的remove函数 */
.remove = m25p_remove,
};
/* 注册驱动 */
module_spi_driver(m25p80_driver);
上面简单的说明了 SPI驱动 相关的一些初始化工作,那么完成结构体的填充及初始化后我们就需要进行驱动注册,其代码如下:
/* 使用 module_driver 宏进行设备注册函数的调用 */
#define module_spi_driver(__spi_driver) \
module_driver(__spi_driver, spi_register_driver, \
spi_unregister_driver)
#define module_driver(__driver, __register, __unregister, ...) \ //在__init__段声明一个__driver##_init函数
static int __init __driver##_init(void) \
{ \
return __register(&(__driver) , ##__VA_ARGS__); \
} \
module_init(__driver##_init); \ //将__driver##_init函数使用module_init注册进内核
static void __exit __driver##_exit(void) \ //在__exit__ 段声明一个__driver##_exit函数
{ \
__unregister(&(__driver) , ##__VA_ARGS__); \
} \
module_exit(__driver##_exit); //将__driver##_exit函数使用module_exit注册进内核
/* __driver##_init函数调用的就是spi_register_driver函数 */
#define spi_register_driver(driver) \
__spi_register_driver(THIS_MODULE, driver)
/* 经过宏的层层展开,最终来到了真正的注册函数 */
struct bus_type spi_bus_type = {
.name = "spi",
.dev_groups = spi_dev_groups,
.match = spi_match_device,
.uevent = spi_uevent,
};
int __spi_register_driver(struct module *owner, struct spi_driver *sdrv)
{
sdrv->driver.owner = owner;
/* 指定驱动的总线类型为spi总线 */
sdrv->driver.bus = &spi_bus_type;
/* 如果spi驱动已经实现了probe函数,则将设备驱动抽象的probe函数设置spi_drv_probe */
if (sdrv->probe)
sdrv->driver.probe = spi_drv_probe;
/* remove 和 shutdown函数同理 */
if (sdrv->remove)
sdrv->driver.remove = spi_drv_remove;
if (sdrv->shutdown)
sdrv->driver.shutdown = spi_drv_shutdown;
/*
使用driver_register注册驱动。
有兴趣的小伙伴可以看I2C那一节,那一节简单的说明了driver_register的过程
*/
return driver_register(&sdrv->driver);
}
/*
driver_register经过一系列操作后最终会调用到spi_match_device。
至于该接口的设备如何而来,可以参考SPI控制器初始化的那个小节
调用过程读者们可以参考调用图谱进行索引
*/
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);
/* 使用设备树判断device和driver是否匹配 */
if (of_driver_match_device(dev, drv))
return 1;
......
/*
这里可以看到spi驱动注册是所填充的设备id表
可以看到,如果使用设备树的ID不匹配时也没关系,依旧可以使用该表进行匹配
但一般情况下我们都直接使用设备树完成匹配了,不再需要该表。所以笔者才说作用不大
*/
if (sdrv->id_table)
return !!spi_match_id(sdrv->id_table, spi);
/*
如果还不匹配哈可以使用spi设备的别名和驱动的名称进行匹配
spi设备的别名在设备创建时指定,可以参考SPI控制器初始化小节
*/
return strcmp(spi->modalias, drv->name) == 0;
}
/* SPI设备匹配SPI驱动的ID表 */
static const struct spi_device_id *spi_match_id(const struct spi_device_id *id,
const struct spi_device *sdev)
{
/* 直接遍历整个ID表并使用strcmp进行判断 */
while (id->name[0]) {
if (!strcmp(sdev->modalias, id->name))
return id;
id++;
}
return NULL;
}
/*
I2C章节讲到如果完成匹配则会调用总线驱动的device_driver成员的probe函数。
而SPI驱动对应的函数为spi_drv_probe,可以参考前面驱动注册时的函数
*/
static int spi_drv_probe(struct device *dev)
{
const struct spi_driver *sdrv = to_spi_driver(dev->driver);
struct spi_device *spi = to_spi_device(dev);
int ret;
......
if (ret != -EPROBE_DEFER) {
/*
调用SPI驱动本身的probe函数
到了这里就完成了函数入口的调用
*/
ret = sdrv->probe(spi);
if (ret)
dev_pm_domain_detach(dev, true);
}
return ret;
}
2.3 SPI设备读写
按照笔者的理解,SPI设备 的 读写 使用的方法都是一样的,其步骤如下:
- 初始化消息:该消息可以是读、写、读写
- 添加消息:设置传输任务并添加传输任务到消息中
- 执行读写
按照上面的步骤,那么其对应的接口一般如下:
- spi_message_init
- spi_message_add_tail
- spi_sync
我们可以看调用图谱,可以看到读写的执行在 spi_sync 中执行。代码示例如下,其中结构体说明仅说明部分常用成员:
/************SPI设备读写调用图谱************/
spi_message_init
->spi_message_init_no_memset
spi_message_add_tail
spi_sync
->__spi_sync
->__spi_validate
->__spi_queued_transfer
->list_add_tail
->kthread_queue_work
->spi_pump_messages
->__spi_pump_messages
->transfer_one_message
->spi_transfer_one_message
->ctlr->transfer_one
->sun6i_spi_transfer_one
->sun6i_spi_handler
/* 每个消息都有多个spi_transfer组成,1个spi_transfer代表一次传输任务 */
struct spi_transfer {
/* 写缓冲 */
const void *tx_buf;
/* 读缓冲 */
void *rx_buf;
/* 缓存长度 */
unsigned len;
/* 写缓冲的字节宽度 */
unsigned tx_nbits:3;
/* 读缓冲的字节宽度 */
unsigned rx_nbits:3;
/* 添加到消息中的链表节点 */
struct list_head transfer_list;.
......
};
struct spi_message {
/* 该消息的传输任务链表 */
struct list_head transfers;
/* 发送该消息的SPI设备 */
struct spi_device *spi;
/* 完成消息传输后的回调函数 */
void (*complete)(void *context);
......
};
/* 初始化SPI传输消息 */
static inline void spi_message_init(struct spi_message *m)
{
memset(m, 0, sizeof *m);
spi_message_init_no_memset(m);
}
/* 初始化SPI传输消息结构体 */
static inline void spi_message_init_no_memset(struct spi_message *m)
{
/* 初始化传输任务队列 */
INIT_LIST_HEAD(&m->transfers);
......
}
/* 添加传输任务到消息中 */
spi_message_add_tail(struct spi_transfer *t, struct spi_message *m)
{
/*
将传输任务中的链表节点直接链入到消息的transfers队列中
一般在调用该接口前是要先完成spi_transfer结构体的初始化跟填充
*/
list_add_tail(&t->transfer_list, &m->transfers);
}
/* 执行SPI传输任务 */
int spi_sync(struct spi_device *spi, struct spi_message *message)
{
int ret;
......
ret = __spi_sync(spi, message);
......
return ret;
}
static int __spi_sync(struct spi_device *spi, struct spi_message *message)
{
DECLARE_COMPLETION_ONSTACK(done);
int status;
struct spi_controller *ctlr = spi->controller;
unsigned long flags;
......
message->complete = spi_complete;
message->context = &done;
message->spi = spi;
/* 从前面的初始化过程可以看到transfer成员就是spi_queued_transfer */
if (ctlr->transfer == spi_queued_transfer) {
spin_lock_irqsave(&ctlr->bus_lock_spinlock, flags);
......
/* 将传输任务入列,注意这里的flags为false */
status = __spi_queued_transfer(spi, message, false);
spin_unlock_irqrestore(&ctlr->bus_lock_spinlock, flags);
}
......
if (status == 0) {
if (ctlr->transfer == spi_queued_transfer) {
......
/* 执行__spi_pump_messages函数 */
__spi_pump_messages(ctlr, false);
}
/* 等待操作完成 */
wait_for_completion(&done);
status = message->status;
}
message->context = NULL;
return status;
}
/* 将传输任务入列 */
static int __spi_queued_transfer(struct spi_device *spi,
struct spi_message *msg,
bool need_pump)
{
struct spi_controller *ctlr = spi->controller;
unsigned long flags;
......
/* 将msg链入SPI控制器的消息队列中 */
list_add_tail(&msg->queue, &ctlr->queue);
/* 上一层传进来的need_pump为false,所以不执行 */
if (!ctlr->busy && need_pump)
kthread_queue_work(&ctlr->kworker, &ctlr->pump_messages);
spin_unlock_irqrestore(&ctlr->queue_lock, flags);
return 0;
}
static void __spi_pump_messages(struct spi_controller *ctlr, bool in_kthread)
{
unsigned long flags;
bool was_busy = false;
int ret;
/*
这里省略了大量的锁操作跟标志判断操作,有兴趣的读者可以去阅读。
这里为了梳理框架就不具体展开
*/
......
/*
执行transfer_one_message回调。
从前面的初始化步骤可以看出是执行函数spi_transfer_one_message
*/
ret = ctlr->transfer_one_message(ctlr, ctlr->cur_msg);
if (ret) {
dev_err(&ctlr->dev,
"failed to transfer one message from queue\n");
goto out;
}
out:
mutex_unlock(&ctlr->io_mutex);
/* Prod the scheduler in case transfer_one() was busy waiting */
if (!ret)
cond_resched();
}
/* 执行传输任务 */
static int spi_transfer_one_message(struct spi_controller *ctlr, struct spi_message *msg)
{
struct spi_transfer *xfer;
bool keep_cs = false;
int ret = 0;
unsigned long long ms = 1;
......
/* 遍历消息上的传输任务 */
list_for_each_entry(xfer, &msg->transfers, transfer_list) {
......
/* 判断读缓冲和写缓冲的有效性 */
if (xfer->tx_buf || xfer->rx_buf) {
reinit_completion(&ctlr->xfer_completion);
/*
执行SPI控制器的transfer_one回调,即每个控制器驱动自己实现的传输函数
每个SPI控制器驱动的实现都有所不同,在这里面会实现读写操作
*/
ret = ctlr->transfer_one(ctlr, msg->spi, xfer);
......
/* 这里省略了一下延迟操作跟长度计算 */
......
}
}
/* 这里以全志的SPI驱动为例简单说明一下读操作的完成步骤 */
static int sun6i_spi_transfer_one(struct spi_master *master,
struct spi_device *spi,
struct spi_transfer *tfr)
{
/* 获取私有数据结构体sspi,该结构体会在接收中断中使用 */
struct sun6i_spi *sspi = spi_master_get_devdata(master);
unsigned int mclk_rate, div, timeout;
unsigned int start, end, tx_time;
unsigned int trig_level;
unsigned int tx_len = 0;
int ret = 0;
u32 reg;
if (tfr->len > SUN6I_MAX_XFER_SIZE)
return -EINVAL;
reinit_completion(&sspi->done);
sspi->tx_buf = tfr->tx_buf;
/* 设置接收缓冲 */
sspi->rx_buf = tfr->rx_buf;
sspi->len = tfr->len;
......
/* 启动中断 */
if (tx_len > sspi->fifo_depth)
sun6i_spi_enable_interrupt(sspi, SUN6I_INT_CTL_TF_ERQ);
......
}
static irqreturn_t sun6i_spi_handler(int irq, void *dev_id)
{
/* sspi为控制器的私有数据,其中的读缓冲已经在sun6i_spi_transfer_one进行设置 */
struct sun6i_spi *sspi = dev_id;
u32 status = sun6i_spi_read(sspi, SUN6I_INT_STA_REG);
......
if (status & SUN6I_INT_CTL_RF_RDY) {
/* 读取SPI总线上的数据 */
sun6i_spi_drain_fifo(sspi, SUN6I_FIFO_DEPTH);
......
return IRQ_HANDLED;
}
......
return IRQ_NONE;
}
static inline void sun6i_spi_drain_fifo(struct sun6i_spi *sspi, int len)
{
u32 reg, cnt;
u8 byte;
......
/* 循环读取接收到的数据 */
while (len--) {
byte = readb(sspi->base_addr + SUN6I_RXDATA_REG);
if (sspi->rx_buf)
*sspi->rx_buf++ = byte;
}
}
到了这里,SPI框架 的梳理基本就完成了。当然,以上为了使得思路更加清晰,精简了不少内容。所以本文只是 SPI框架 的冰山一角,其中的一些 共享资源处理、片选操作及时序延时等等没有进行讲述,有兴趣的读者可以自行查阅相关代码。
三、参考链接
为什么教程都不讲MODULE_DEVICE_TABLE的作用
MODULE_DEVICE_TABLE
Linux 内核 MODULEDEVICETABLE 宏
Linux SPI 驱动分析(1)— 结构框架
linux驱动基础系列--linux spi驱动框架分析
SPI 总线协议和驱动
网友评论