一、SPI子系统介绍
- SPI总线的四种模式,CPOL决定空闲时SCLK的电平,CPHA决定第几个时钟边沿采集数据,常用模式0和模式3,均是上升沿采集数据
- SPI存在一个主从模式的分别,CPU中常用的是主模式
二、SPI子系统的分层介绍
- SPI设备驱动,包含一般的SPI设备驱动和spidev.c,形成两种节点/dev/xxx和/dev/spiB.C
- SPI核心层,包含spi.c,提供了各种API供上下层使用
- SPI控制器驱动,包含SPI控制器的驱动,和使用gpio模拟SPI控制器的驱动
三、典型的设备树文件
// 挂在spi0下的spi设备,片选引脚0
// 对于spidev.c对应的设备,compatible属性应该参考源文件
&spi0{
spi_dev@0{
compatible = "spi_dev_compatible";
reg = <0>;
spi-max-frequency = <10000>;
... // 其他的配置参考documentation
};
};
// spi控制器
spi_adap_virt: virtual_spi_adapter {
compatible = "user,virtual_spi_adapter";
#address-cells = <1>;
#size-cells = <0>;
... // 其他信息
... // 可能存在的pinctrl信息
};
// 使用GPIO模拟的SPI控制器
spix{
compatible = "spi-gpio";
#address-cells = <1>;
#size-cells = <0>;
sck-gpios = <&gpio 95 0>;
miso-gpios = <&gpio 96 0>;
mosi-gpios = <&gpio 97 0>;
cs-gpios = <&gpio 125 0>;
num-chipselects = <1>; // 片选线的数目
};
四、SPI的重要结构体
// 描述SPI控制器
#define spi_master spi_controller
struct spi_controller{
struct device dev; // 所有设备都继承自device
struct list_head list; // 组织spi控制器的链表
... // spi控制器的其余属性
struct list_head queue; // 存储每个msg需要发送的queue
struct spi_message *cur_msg;// 当前传输的message
int (*transfer)(struct spi_device *spi, struct spi_message *mesg); // 老方法
iny (*transfer_one)(struct spi_controller *ctlr, struct spi_device *spi, struct spi_transfer *transfer); // 新方法
};
// 描述SPI传输的最基本单位
struct spi_transfer{
const void *tx_buf;
void *rx_buf;
unsigned len;
...
};
// 管理SPI传输的单位,组织spi_transfer
struct spi_message{
struct list_head transferes; // 一组需要传输的spi_transfer
struct spi_device *spi; // 一个SPI设备
void (*complete)(void*context); // 完成量增加函数,提交完成量
void *context; // 函数参数,保存完成量
unsigned actual_length; // 传输长度,每次传输完成增加
int status; // 传输完成0,等待工作队列传输-EINPROGESS
struct list_head queue; // 放入master的queue,用来遍历寻找spi_message
...
};
// 描述SPI驱动,类似platform_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设备
struct spi_device{
struct deie dev; // 父对象
struct spi_controller *master; // 属于哪个控制器
... // 其他成员
};
五、驱动程序的流程
// SPI设备驱动
1. 入口函数中,使用spi_register_driver注册一个spi_driver,出口函数中spi_unregister_driver注销一个spi_driver
2. SPI总线和platform总线类似,compatible属性匹配时,调用probe函数
3. probe函数中,类似其他字符设备。使用alloc_chrdev_region、cdev_init、cdev_add、class_create、device_create
4. 关键的file_operation结构体,构建一个spi_transfer,并加入spi_message,调用spi_sync完成读写操作
// SPI控制器驱动
1. 使用平台设备驱动程序的框架,注册SPI控制器驱动
2. probe函数使用spi_alloc_master构建一个spi_master结构体,设置结构体的transfer函数,设置好工作队列和对应函数,使用spi_register_master注册一个spi_master结构体
3. remove函数中spi_register_master注销spi_master结构体
4. 关键的transfer函数,传输完成使用回调函数通知,或者启动工作队列,在工作队列中对控制器队列找到mesg的队列,传输完成使用回调函数通知
// SPI控制器的驱动-新
1. 使用平台设备驱动程序的框架,注册SPI控制器驱动
2. probe函数中使用spi_alloc_master构建一个spi_master结构体,并额外分配一个spi_bitbang结构体,设置bitbang结构体的txrx_bufs函数,初始化一个完成量,最后spi_bitbang_start启动
3. remove函数中,使用spi_bitbang_stop停止,使用spi_master_put释放spi_master结构体
4. 关键的txrx_bufs函数中,reinit_completion,等待ISR发送完成量,此处等待completion,最后返回transfer->len
六、spi传输函数的解析
// spi_sync老方法
spi_sync(struct spi_device *spi, struct spi_message *message); // 实际的spi传输函数,传入参数spi_device指针、spi_message指针
__spi_sync // 传入参数spi_device指针、spi_message指针
message->complete = spi_complete; //完成量唤醒任务
message->context = &done; //完成量
message->spi = spi; //对应的spi设备
spi_async_locked(spi, message);
__spi_async(spi, message);
struct spi_controller *ctlr = spi->controller; // 找到控制器
ctlr->transfer(spi, message); // 调用控制器提供的transfer函数
wait_for_complete(&done); //等待完成量,完成量在spi_complete被增加
spi控制器的transfer函数
list_add_tail(&mesg->queue, &master->queue); // 将msg的queue放入maser的queue
schedule_work(); // 启动工作队列
mesg = list_entry(master->queue.next, struct spi_message, queue); // 取出mesg
list_del_init(&mesg->queue); // 移除当前mesg
// 取出mesg的xfer,进行传输
mesg->status = 0;
if(mesg->complete)
mesg->complete(mesg->context); // 。回调函数,提交完成量
// spi_sync新方法
spi_sync(struct spi_device *spi, struct spi_message *message); // 实际的spi传输函数,传入参数spi_device指针、spi_message指针
__spi_sync // 传入参数spi_device指针、spi_message指针
struct spi_controller *ctlr = spi->controller; // 找到控制器
message->complete = spi_complete; //完成量唤醒任务
message->context = &done; //完成量
message->spi = spi; //对应的spi设备
__spi_queued_transfer(spi, message, false); //实现了master的transfer函数的第一步,将msg的queue放入maser的queue
__spi_pump_message(ctrl, false);
ctrl->cur_msg = list_first_entry(&ctrl->queue, struct spi_message, queue); // 取出mesg
list_del_init(&msg->queue); // 移除mesg
ctrl->transfer_one_message(ctrl, msg); // 调用控制器提供的transfer_one_message函数
wait_for_complete(&done); //等待完成量,完成量在spi_complete被增加
spi控制器的transfer_one_message函数