2. Linux SPI子系统架构:分层设计与数据结构
Linux SPI子系统采用经典的分层架构设计,将SPI总线操作抽象为核心层、控制器驱动层和设备驱动层三个层次。这种分层解耦了SPI控制器硬件差异与设备协议实现,使得开发者可以专注于各自层次的逻辑,无需关心底层细节。
2.1 分层设计概述
SPI子系统的三层架构各司其职,通过标准接口进行交互:
| 层次 | 核心职责 | 关键数据结构 | 开发者角色 |
|---|---|---|---|
| SPI设备驱动层 | 实现具体SPI设备的协议逻辑,如ADC、传感器、显示屏等 | spi_driver、spi_device |
设备驱动开发者 |
| SPI核心层 | 管理SPI总线、设备注册与匹配、数据传输框架 | spi_controller、spi_message |
内核维护者 |
| SPI控制器驱动层 | 直接操作SPI控制器寄存器,完成硬件时序 | spi_controller_ops |
SoC厂商/板级开发者 |
设计原则:设备驱动层不关心控制器是哪个SoC的,控制器驱动层不关心挂载的是什么设备。核心层作为桥梁,提供统一的
spi_transfer()接口和消息队列机制。
2.2 SPI核心层数据结构
SPI核心层是子系统的中枢,负责管理总线、设备和数据传输。核心数据结构包括:
2.2.1 spi_controller —— 控制器抽象
每个SPI控制器硬件在核心层对应一个spi_controller实例,包含控制器的属性、队列管理和操作函数集:
struct spi_controller {
struct device dev; /* 设备模型 */
struct list_head list; /* 全局控制器链表 */
u16 bus_num; /* 总线编号 */
u16 num_chipselect; /* 片选数量 */
u32 mode_bits; /* 支持的SPI模式位掩码 */
u32 bits_per_word_mask;
u32 min_speed_hz; /* 最低速率 */
u32 max_speed_hz; /* 最高速率 */
/* 队列与传输 */
struct spi_message *cur_msg;
struct completion xfer_completion;
size_t queue_size;
/* 操作函数集 */
const struct spi_controller_ops *ops;
/* 私有数据 */
void *controller_data;
};
2.2.2 spi_device —— 设备抽象
每个挂载在SPI总线上的从设备对应一个spi_device,由核心层在设备枚举时创建:
struct spi_device {
struct device dev; /* 设备模型 */
struct spi_controller *controller; /* 所属控制器 */
struct spi_bus *bus; /* 所属总线 */
u32 max_speed_hz; /* 设备最大速率 */
u8 chip_select; /* 片选编号 */
u8 mode; /* SPI模式:CPOL/CPHA/CS_HIGH等 */
u8 bits_per_word; /* 每字位数,通常8 */
int irq; /* 中断号(可选) */
void *controller_data;
void *driver_data; /* 驱动私有数据 */
};
2.2.3 spi_message 与 spi_transfer —— 传输单元
数据传输以spi_message为基本单位,一个消息包含多个spi_transfer段:
struct spi_message {
struct list_head transfers; /* 传输段链表 */
struct spi_device *spi; /* 目标设备 */
unsigned is_dma_mapped:1;
/* 完成回调 */
void (*complete)(void *context);
void *context;
unsigned frame_length;
struct spi_transfer *state;
};
struct spi_transfer {
const void *tx_buf; /* 发送缓冲区 */
void *rx_buf; /* 接收缓冲区 */
unsigned len; /* 传输字节数 */
dma_addr_t tx_dma; /* DMA发送地址 */
dma_addr_t rx_dma; /* DMA接收地址 */
unsigned cs_change:1; /* 传输后是否切换片选 */
u8 bits_per_word;
u16 delay_usecs; /* 传输间延时 */
u32 speed_hz; /* 本段速率 */
};
传输模型:一个
spi_message包含一个或多个spi_transfer,所有传输段在片选有效期间连续执行。设备驱动通过spi_async()提交消息,核心层将其加入控制器队列,由控制器驱动依次处理。
2.3 SPI控制器驱动层
控制器驱动层负责实现spi_controller_ops中的回调函数,直接操作硬件寄存器:
struct spi_controller_ops {
/* 设置SPI设备参数(模式、速率、字长等) */
int (*setup)(struct spi_device *spi);
/* 单次传输(同步方式) */
int (*transfer_one)(struct spi_controller *ctlr,
struct spi_device *spi,
struct spi_transfer *transfer);
/* 消息准备(DMA映射、FIFO配置等) */
int (*prepare_message)(struct spi_controller *ctlr,
struct spi_message *msg);
/* 消息完成后的清理 */
int (*unprepare_message)(struct spi_controller *ctlr,
struct spi_message *msg);
/* 队列空闲时的回调 */
void (*handle_idle)(struct spi_controller *ctlr);
};
| 回调函数 | 调用时机 | 典型实现 |
|---|---|---|
setup() |
设备注册或参数变更时 | 配置控制器寄存器:时钟极性、相位、分频系数 |
transfer_one() |
每个传输段执行时 | 写入TX FIFO、启动SPI时钟、等待RX FIFO数据 |
prepare_message() |
消息开始前 | DMA映射缓冲区、清空FIFO、使能控制器 |
unprepare_message() |
消息完成后 | DMA解映射、关闭控制器、触发完成回调 |
2.4 SPI设备驱动层
设备驱动层通过spi_driver结构体注册到核心层,核心层通过设备树或板级代码匹配设备:
static const struct of_device_id my_spi_of_match[] = {
{ .compatible = "myvendor,my-spi-device" },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, my_spi_of_match);
static struct spi_driver my_spi_driver = {
.driver = {
.name = "my_spi_device",
.of_match_table = my_spi_of_match,
},
.probe = my_spi_probe,
.remove = my_spi_remove,
.id_table = my_spi_id_table, /* 非设备树匹配时使用 */
};
module_spi_driver(my_spi_driver);
2.5 关键数据结构关系总结
以下表格总结了各层核心数据结构之间的关联关系:
| 数据结构 | 所属层次 | 关联关系 | 生命周期 |
|---|---|---|---|
spi_controller |
核心层 | 包含 spi_controller_ops,管理多个 spi_device |
系统启动时注册,模块卸载时注销 |
spi_device |
核心层 | 指向所属 spi_controller,由设备树或板级代码创建 |
设备枚举时创建,设备移除时销毁 |
spi_driver |
设备驱动层 | 通过 probe() 绑定到 spi_device |
驱动加载时注册,卸载时注销 |
spi_message |
核心层 | 包含 spi_transfer 链表,指向目标 spi_device |
每次传输动态创建,传输完成后释放 |
spi_transfer |
核心层 | 作为 spi_message 的传输段,包含 tx_buf/rx_buf |
随 spi_message 创建和销毁 |
常见误区:
- 不要混淆
spi_device和spi_driver:前者是设备实例,后者是驱动逻辑。 - 一个
spi_message中的所有spi_transfer在片选有效期间连续执行,中间不会释放总线。 - 控制器驱动的
transfer_one()必须处理cs_change标志,否则多段传输可能出错。
2.6 设备树与板级匹配
SPI设备通常通过设备树(Device Tree)描述,核心层在解析设备树时自动创建spi_device:
/* 设备树示例 */
&spi1 {
status = "okay";
pinctrl-names = "default";
pinctrl-0 = <&spi1_pins>;
my_adc@0 {
compatible = "myvendor,my-spi-adc";
reg = <0>; /* 片选编号 */
spi-max-frequency = <10000000>;
spi-cpol; /* 模式:CPOL=1 */
spi-cpha; /* 模式:CPHA=1 */
};
};
核心层解析设备树后,执行以下步骤:
- 读取
reg属性确定片选编号,创建spi_device。 - 读取
spi-max-frequency设置max_speed_hz。 - 根据
spi-cpol、spi-cpha等标志设置mode字段。 - 调用
spi_controller_ops.setup()配置控制器硬件。 - 遍历已注册的
spi_driver,匹配of_match_table,调用probe()。
开发建议:编写SPI设备驱动时,优先使用设备树描述硬件参数,避免在驱动代码中硬编码片选号、速率等配置。设备树方式使驱动更具可移植性。