2. Linux SPI 子系统架构:分层设计与数据结构关系

Linux SPI 子系统采用经典的分层架构设计,从上到下依次为:SPI 设备驱动层、SPI 核心层和SPI 控制器驱动层。这种分层结构实现了硬件控制逻辑与设备业务逻辑的解耦,使得 SPI 控制器驱动可以复用,而 SPI 设备驱动只需关注协议解析与数据处理。

2.1 三层架构总览

下图展示了 SPI 子系统的三层架构及其核心数据结构之间的关联关系:

Linux SPI 子系统三层架构与核心数据结构关系 SPI 设备驱动层 (SPI Device Driver) spi_driver → probe() / remove() / id_table spi_device → chip_select / max_speed_hz / mode / bits_per_word spi_transfer → tx_buf / rx_buf / len / speed_hz / cs_change / delay_usecs SPI 核心层 (SPI Core) spi_register_driver() / spi_register_controller() / spi_async() / spi_sync() spi_bus_type → match() / uevent() / probe() / remove() spi_alloc_device() / spi_setup() / spi_message_init() / spi_message_add_tail() SPI 控制器驱动层 (SPI Controller Driver) spi_controller → transfer_one_message() / transfer_one() / prepare_message() / unprepare_message() spi_master → bus_num / num_chipselect / min_speed_hz / max_speed_hz / mode_bits DMA 配置 / FIFO 深度 / IRQ 处理 / GPIO CS 控制 核心层通过 spi_bus_type 的 match() 函数将 spi_driver 与 spi_device 绑定,完成设备枚举

2.2 核心数据结构关系

SPI 子系统的三层架构通过一组核心数据结构紧密关联。下表列出了各层的关键数据结构及其职责:

数据结构 所属层次 核心字段 / 作用
spi_controller 控制器驱动层 管理 SPI 总线控制器硬件,包含 bus_num、num_chipselect、min/max_speed_hz、transfer_one_message() 回调
spi_device 设备驱动层 代表一个 SPI 从设备,包含 chip_select、max_speed_hz、mode、bits_per_word、controller 指针
spi_driver 设备驱动层 SPI 设备驱动实例,包含 probe()、remove()、id_table,通过 spi_register_driver() 注册
spi_message 核心层 一次 SPI 传输的完整请求,包含多个 spi_transfer,通过链表组织
spi_transfer 核心层 单个 SPI 传输片段,包含 tx_buf、rx_buf、len、speed_hz、cs_change、delay_usecs
spi_board_info 板级描述层 板级初始化时描述 SPI 设备信息,包含 modalias、chip_select、max_speed_hz、platform_data

2.3 设备枚举与绑定流程

SPI 设备的枚举与驱动绑定遵循 Linux 设备模型的标准流程,具体步骤如下:

  1. 控制器注册:SPI 控制器驱动调用 spi_register_controller(),将 spi_controller 注册到核心层,核心层为其分配 bus_num 并创建 /sys/bus/spi/devices/spiX 目录。
  2. 设备创建:通过板级代码的 spi_board_info 数组或设备树中的 SPI 子节点,核心层调用 spi_new_device() 创建 spi_device 实例,并挂载到对应控制器的设备链表。
  3. 驱动注册:SPI 设备驱动调用 spi_register_driver(),将 spi_driver 注册到 spi_bus_type 总线。
  4. 匹配绑定:核心层通过 spi_bus_type->match() 函数,将 spi_driver->id_table 中的名称与 spi_device->modalias 比对,匹配成功后调用 spi_driver->probe()。
  5. 设备初始化:在 probe() 中,驱动调用 spi_setup() 配置 SPI 通信参数(模式、速率、位宽),并准备数据传输所需的资源。

关键点:设备树(Device Tree)是当前主流 SPI 设备描述方式。在设备树中,SPI 从设备作为控制器节点的子节点出现,核心层在解析设备树时自动调用 of_register_spi_devices() 完成设备创建,无需板级代码。

2.4 数据传输路径

SPI 数据传输从设备驱动发起,经过核心层调度,最终由控制器驱动完成硬件操作。下图展示了典型的数据传输路径:

SPI 数据传输路径与消息处理流程 SPI 设备驱动 spi_sync() / spi_async() SPI 核心层 __spi_async() / spi_finalize_current_message() SPI 控制器驱动 transfer_one_message() spi_message 队列调度 控制器驱动内部处理步骤 prepare_message DMA 映射 / 时钟配置 transfer_one 逐 transfer 发送 中断 / DMA 完成 硬件传输结束 unprepare_message DMA 解映射 完成回调:spi_finalize_current_message() → 唤醒等待队列 / 触发异步完成回调

2.5 关键设计要点

性能设计考量:

  • 消息队列:控制器驱动维护一个消息队列,核心层通过 spi_controller->queue 管理待处理消息,支持异步流水线处理。
  • DMA 支持:控制器驱动可在 prepare_message() 中为 spi_transfer 的 tx_buf/rx_buf 建立 DMA 映射,减少 CPU 拷贝开销。
  • CS 控制优化:通过 cs_change 字段控制片选信号在多个 transfer 之间是否拉高,减少 GPIO 操作次数。
  • 速率自适应:每个 spi_transfer 可独立指定 speed_hz,控制器驱动在 transfer_one() 中动态调整时钟分频。

常见陷阱:

  • 在 spi_message 中添加 transfer 时,必须使用 spi_message_add_tail(),不能直接操作链表。
  • 异步传输 spi_async() 的完成回调在原子上下文中执行,不能调用可能导致睡眠的函数。
  • 设备树中 SPI 从设备的 reg 属性值必须与控制器驱动的 chip_select 编号一致,否则设备无法正确寻址。

2.6 数据结构关系总结

下表总结了各数据结构之间的引用关系,帮助理解代码中的指针传递:

源结构 目标结构 指针字段 关系说明
spi_device spi_controller controller 设备所属的控制器
spi_controller spi_device devices 链表 控制器下挂载的所有设备
spi_message spi_device spi 消息目标设备
spi_message spi_transfer transfers 链表 消息包含的传输片段
spi_driver spi_device id_table / match 驱动与设备的匹配关系
spi_board_info spi_device modalias / chip_select 板级描述到设备实例的映射