3. Linux SPI 子系统架构

Linux SPI 子系统采用分层架构设计,将 SPI 控制器硬件差异、核心协议逻辑与具体设备功能解耦。理解这一架构是开发稳定、可移植 SPI 驱动的基石。本章深入剖析 SPI 核心层、控制器驱动层、设备驱动层的职责与交互,并详细解读核心数据结构之间的关系。

3.1 三层架构总览

SPI 子系统在 Linux 内核中位于 drivers/spi/ 目录,主要分为三个层次:

  • SPI 核心层 (spi.c):提供总线注册、设备枚举、数据传输抽象、驱动模型管理等基础设施。
  • SPI 控制器驱动层:直接操作 SPI 控制器硬件寄存器,实现 FIFO 控制、DMA 传输、中断处理等底层操作。
  • SPI 设备驱动层:面向具体 SPI 外设(如 ADC、传感器、显示屏),通过核心层 API 与控制器通信,实现设备特定功能。
架构优势: 设备驱动无需关心硬件寄存器细节,控制器驱动无需理解设备协议,核心层负责两者之间的标准化通信。这种分层极大提升了代码复用性和可移植性。
Linux SPI 子系统三层架构 用户空间 (User Space) 内核空间 SPI 设备驱动层 (Device Driver) spi_driver → probe() / remove() / id_table SPI 核心层 (Core Layer) spi.c / spi.h → 总线注册 / 数据传输 / 设备枚举 SPI 控制器驱动层 (Controller Driver) spi_master → transfer_one() / setup() / prepare_message() 硬件 (SPI Controller + 外设) 系统调用接口

3.2 SPI 核心层 (spi.c)

核心层是 SPI 子系统的中枢,主要功能包括:

  • 总线管理:注册 SPI 总线类型 (spi_bus_type),管理总线上设备和驱动的匹配。
  • 数据传输抽象:提供 spi_sync()、spi_async()、spi_write_then_read() 等标准 API。
  • 设备枚举:解析设备树 (Device Tree) 或平台数据,创建 spi_device 实例。
  • 消息队列管理:将 spi_message 组织成队列,调度给控制器驱动处理。
关键文件: drivers/spi/spi.c 是核心实现,include/linux/spi/spi.h 是核心头文件,定义了所有关键数据结构和 API 原型。

3.3 SPI 控制器驱动层

控制器驱动负责与硬件 SPI 控制器交互,必须实现 spi_master 结构体中的关键回调:

回调函数 功能描述 必须实现
transfer_one() 执行单次 SPI 传输(发送+接收) 是
setup() 配置 SPI 控制器参数(时钟、极性、相位) 是
prepare_message() 在消息传输前进行准备(如 DMA 设置) 否
unprepare_message() 消息传输完成后的清理工作 否
transfer_one_message() 处理整个消息队列(默认实现调用 transfer_one) 否
注意事项: 控制器驱动通常需要处理中断、DMA、FIFO 溢出等硬件细节。在实现 transfer_one() 时,必须考虑 SPI 模式 (CPOL/CPHA)、位序 (MSB/LSB) 和时钟频率的配置。

3.4 SPI 设备驱动层

设备驱动是开发者最常编写的部分,通过 spi_driver 结构体注册到核心层:

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);

设备驱动的主要职责:

  • 在 probe() 中初始化设备、分配缓冲区、注册字符设备或 IIO 框架。
  • 使用 spi_write()、spi_read()、spi_write_then_read() 等 API 与设备通信。
  • 在 remove() 中释放资源、注销设备。

3.5 核心数据结构关系图

SPI 子系统的三个核心数据结构——spi_master、spi_device、spi_driver——通过内核驱动模型紧密关联:

SPI 核心数据结构关系图 struct spi_master - bus_num: u16 - num_chipselect: u16 - min/max_speed_hz: u32 - mode_bits: u32 - transfer_one() / setup() - cur_msg: struct spi_message * struct spi_device - dev: struct device - master: struct spi_master * - max_speed_hz: u32 - chip_select: u8 - mode: u8 (CPOL/CPHA/CS_HIGH) - irq: int struct spi_driver - driver: struct device_driver - probe() : 设备匹配成功后调用 - remove() : 设备移除时调用 - id_table: struct spi_device_id * - shutdown() : 系统关机时调用 master->dev spi_dev->master 匹配后调用 probe() id_table 匹配 关系总结:spi_master 管理硬件控制器 → spi_device 代表具体外设 → spi_driver 提供设备驱动逻辑

3.6 数据结构交互流程

当系统启动或设备插入时,SPI 子系统的数据流如下:

  1. 控制器注册:控制器驱动调用 spi_register_master(),核心层创建 spi_master 实例并注册到总线。
  2. 设备创建:核心层解析设备树或平台数据,为每个 SPI 从设备创建 spi_device,并关联到对应的 spi_master。
  3. 驱动匹配:核心层遍历已注册的 spi_driver,通过 of_match_table 或 id_table 与 spi_device 匹配。
  4. 设备绑定:匹配成功后,核心层调用 spi_driver->probe(),传入 spi_device 指针,驱动开始初始化。
  5. 数据传输:设备驱动通过 spi_sync() 提交 spi_message,核心层将其传递给 spi_master 的 transfer_one() 执行。
关键理解: spi_device 是连接控制器和设备的桥梁。它既持有 spi_master 的指针用于传输,又作为 struct device 参与驱动模型匹配。这种设计使得设备驱动只需操作 spi_device,无需直接接触硬件寄存器。

3.7 数据传输路径

一次典型的 SPI 数据传输路径如下:

步骤 调用链 所在层
1 spi_write() / spi_read() 设备驱动
2 spi_sync() 核心层
3 __spi_validate() 检查消息合法性 核心层
4 spi_queued_transfer() 加入队列 核心层
5 spi_master->transfer_one_message() 控制器驱动
6 spi_master->transfer_one() 硬件操作 控制器驱动
7 中断/DMA 完成回调 控制器驱动
8 spi_finalize_current_message() 核心层
性能提示: 对于批量小数据,使用 spi_write_then_read() 可以避免多次消息队列调度。对于大数据块,建议使用 DMA 模式,并在控制器驱动的 prepare_message() 中配置 DMA 描述符。

3.8 本章小结