2. Linux SPI子系统架构:SPI核心层、SPI控制器驱动、SPI设备驱动、数据结构关系图
Linux SPI子系统采用标准的分层架构设计,将硬件控制逻辑与设备协议处理解耦。整个架构自底向上分为三层:SPI控制器驱动层、SPI核心层和SPI设备驱动层。理解这三层的职责边界与数据结构关系,是开发高质量SPI驱动的基石。
架构设计原则:控制器驱动负责硬件寄存器操作与传输引擎控制;核心层提供统一的框架、消息队列管理与DMA通道协调;设备驱动仅需关注协议解析与数据语义,无需关心底层时序。
2.1 三层架构总览
下图展示了SPI子系统的完整分层结构,以及各层之间的调用关系与数据流向:
关键认知:设备驱动不直接操作控制器寄存器,所有传输请求都通过核心层的消息队列(spi_message)下发。控制器驱动从队列中取出传输任务,执行实际的硬件操作。
2.2 核心数据结构关系图
SPI子系统的核心数据结构包括 spi_master、spi_device、spi_driver、spi_message 和 spi_transfer。它们之间的关联关系如下图所示:
2.3 各层职责与关键接口
| 层次 | 核心结构体 | 主要职责 | 关键回调/接口 |
|---|---|---|---|
| 控制器驱动 | spi_master |
硬件寄存器配置、FIFO/DMA引擎控制、中断处理、时钟管理 | transfer_one_message()can_dma()dma_map()dma_unmap() |
| 核心层 | spi_masterspi_messagespi_transfer |
消息队列管理、总线锁定、DMA通道协调、同步/异步传输封装 | spi_async()spi_sync()spi_write_then_read()__spi_queued_transfer() |
| 设备驱动 | spi_driverspi_device |
协议解析、数据语义转换、字符设备接口、电源管理 | probe()remove()spi_write()spi_read() |
常见误区:设备驱动中不应直接访问
spi_master 的成员或调用控制器驱动的回调。所有传输必须通过核心层提供的 API(如 spi_sync()、spi_async())发起,否则会破坏消息队列的原子性和DMA协调逻辑。
2.4 数据结构初始化与注册流程
SPI子系统的初始化遵循自底向上的顺序:先注册控制器驱动,核心层自动创建 spi_device,然后设备驱动通过 spi_driver 匹配并绑定。关键步骤与数据结构操作如下:
- 控制器驱动注册:调用
spi_register_master(),核心层分配spi_master并初始化消息队列、DMA相关字段。 - 设备枚举:核心层根据设备树(Device Tree)或板级代码中的SPI子节点,为每个片选创建
spi_device,并关联到对应的spi_master。 - 设备驱动绑定:设备驱动调用
spi_register_driver(),核心层通过spi_bus_type的匹配逻辑将spi_driver与spi_device配对,触发probe()。 - 传输通道建立:在
probe()中,设备驱动保存spi_device指针,后续通过该指针发起所有传输请求。
/* 控制器驱动注册示例 (伪代码) */
static struct spi_master *master;
master = spi_alloc_master(&pdev->dev, sizeof(struct my_spi_controller));
master->bus_num = 1;
master->num_chipselect = 2;
master->transfer_one_message = my_transfer_one_message;
master->can_dma = my_can_dma;
master->dma_map = my_dma_map;
master->dma_unmap = my_dma_unmap;
spi_register_master(master);
/* 设备驱动 probe 示例 */
static int my_spi_device_probe(struct spi_device *spi)
{
struct my_device_data *data;
data = devm_kzalloc(&spi->dev, sizeof(*data), GFP_KERNEL);
spi_set_drvdata(spi, data);
data->spi = spi; /* 保存 spi_device 指针 */
/* 配置 SPI 参数 */
spi->mode = SPI_MODE_0;
spi->max_speed_hz = 10000000;
spi->bits_per_word = 8;
spi_setup(spi);
return 0;
}
2.5 消息队列与DMA传输的整合点
在SPI子系统中,消息队列与DMA传输的整合发生在核心层与控制器驱动的接口处。核心层提供以下关键机制:
| 整合点 | 核心层行为 | 控制器驱动行为 |
|---|---|---|
| DMA决策 | 遍历 spi_message 中的每个 spi_transfer,调用 master->can_dma() 判断是否使用DMA |
根据传输长度、缓冲区对齐、DMA通道可用性返回 true/false |
| DMA映射 | 对 can_dma() 返回 true 的 transfer,调用 master->dma_map() 建立 DMA 映射 |
调用 dma_map_single() 或 sg_dma_address(),填充 transfer->tx_dma / rx_dma |
| 传输执行 | 调用 master->transfer_one_message() 将整个消息交给控制器驱动 |
解析 transfer 链表,对标记了 DMA 的 transfer 启动 DMA 引擎,否则使用 PIO/FIFO |
| 完成回调 | 控制器驱动完成所有 transfer 后调用 spi_finalize_current_message() |
在 DMA 完成中断或 PIO 完成后调用 spi_finalize_current_message() |
性能关键路径:对于大数据块传输(通常 > 256 字节),DMA 模式可降低 CPU 占用率 80% 以上。核心层的
can_dma() 回调是决定是否启用 DMA 的决策点,通常建议对小数据量(< 64 字节)使用 PIO 以避免 DMA 建立开销。
2.6 数据结构关系总结
下表汇总了SPI子系统中主要数据结构之间的引用关系与生命周期:
| 数据结构 | 生命周期 | 持有者/创建者 | 关键引用关系 |
|---|---|---|---|
spi_master |
控制器驱动加载 → 卸载 | 核心层分配,控制器驱动填充 | 包含 spi_device 链表;通过 dev 关联设备模型 |
spi_device |
设备树解析/板级注册 → 移除 | 核心层创建 | master 指向所属控制器;driver 指向绑定的设备驱动 |
spi_driver |
模块加载 → 卸载 | 设备驱动定义并注册 | 通过 id_table 匹配 spi_device;probe() 接收 spi_device |
spi_message |
传输请求发起 → 完成回调 | 设备驱动分配,核心层管理队列 | spi 指向目标 spi_device;transfers 链表头 |
spi_transfer |
包含在 spi_message 生命周期内 |
设备驱动分配并添加到 message | tx_buf/rx_buf 指向数据缓冲区;tx_dma/rx_dma 由DMA映射填充 |
开发建议:在编写设备驱动时,优先使用
spi_sync() 进行同步传输,它内部封装了消息队列的入队与等待。对于需要高吞吐的场景,使用 spi_async() 并注册完成回调,避免阻塞当前进程。DMA 传输的缓冲区必须使用 dma_alloc_coherent() 或 kmalloc() 分配(确保物理地址连续),栈上缓冲区会导致 DMA 映射失败。