4. Linux SPI 子系统架构:核心层、控制器驱动、设备驱动与传输队列

SPI(Serial Peripheral Interface)子系统是 Linux 内核中为 SPI 总线提供统一访问接口的核心框架。本章深入剖析 SPI 子系统的四层架构:SPI 核心层、SPI 控制器驱动、SPI 设备驱动以及SPI 消息与传输队列。理解这一分层结构是编写稳定、高效的 SPI 传感器驱动的基石。

架构概览: SPI 子系统采用“核心-控制器-设备”三层驱动模型,外加消息队列机制实现异步传输。核心层负责总线注册、设备枚举与数据传输抽象;控制器驱动管理硬件寄存器与 FIFO;设备驱动实现具体传感器的协议逻辑;消息队列则协调多笔传输的调度与同步。

4.1 SPI 核心层(SPI Core)

SPI 核心层位于 drivers/spi/spi.c,是整个子系统的中枢。它提供以下关键功能:

  • 总线注册与管理: 注册 SPI 总线类型 spi_bus_type,管理控制器与设备的匹配、添加与移除。
  • 设备与驱动模型: 提供 spi_register_driver() 和 spi_register_controller() 接口,实现设备与驱动的绑定。
  • 数据传输抽象: 定义 spi_transfer() 和 spi_async() 等核心 API,屏蔽底层硬件差异。
  • Board Info 与 Device Tree 支持: 解析平台数据或设备树节点,自动创建 SPI 设备。
关键数据结构: struct spi_controller 代表一个 SPI 控制器实例,包含时钟频率、模式、位序等配置;struct spi_device 代表挂载在总线上的从设备,持有片选 GPIO、最大速率等属性。
SPI 核心层架构图 SPI 核心层架构 总线注册与管理 设备与驱动模型 数据传输抽象 Board Info/DT struct spi_controller struct spi_device spi_register_controller() spi_register_driver() 核心层不直接操作硬件,而是通过回调函数调用控制器驱动的底层接口

4.2 SPI 控制器驱动(SPI Controller Driver)

SPI 控制器驱动负责与具体的 SPI 硬件外设交互,通常由 SoC 厂商或板级开发者编写。其核心职责包括:

  • 硬件初始化: 配置时钟极性(CPOL)、相位(CPHA)、位序(MSB/LSB)、波特率等寄存器。
  • FIFO 与 DMA 管理: 利用硬件 FIFO 或 DMA 引擎提高传输吞吐量,减少 CPU 中断负载。
  • 片选(CS)控制: 管理硬件片选信号或 GPIO 片选,确保多设备分时访问。
  • 实现传输回调: 填充 struct spi_controller 中的 transfer_one() 或 transfer_one_message() 回调。
SPI 控制器驱动关键回调函数
回调函数作用典型实现场景
transfer_one()执行单次 SPI 传输(一个片选周期)简单 FIFO 控制器
transfer_one_message()处理完整消息(含多个 transfer)需要消息级排队或 DMA 链表的控制器
prepare_message()消息传输前的硬件预配置配置 DMA 通道或锁定总线时钟
unprepare_message()消息传输后的清理工作释放 DMA 资源或恢复时钟
set_cs()控制片选信号的电平与时序GPIO 模拟片选或硬件片选寄存器
常见陷阱: 控制器驱动必须正确处理时钟极性/相位与从设备的匹配。CPOL=0/CPHA=0 与 CPOL=1/CPHA=1 在数据采样边沿上完全不同,配置错误会导致通信数据全部错位。
SPI 控制器驱动内部结构 SPI 控制器驱动内部结构 struct spi_controller 回调接口 硬件初始化与配置 FIFO / DMA 管理 片选与时序控制 SPI 硬件寄存器组(SPI_CR1/SPI_DR/SPI_SR)

4.3 SPI 设备驱动(SPI Device Driver)

SPI 设备驱动是传感器适配工程中最常编写的部分。它通过 SPI 核心层提供的 API 与控制器驱动交互,实现特定传感器的协议解析。设备驱动的典型工作流程如下:

  1. 注册驱动: 使用 module_spi_driver() 宏或手动调用 spi_register_driver()。
  2. 探测(probe): 在 probe 回调中分配私有数据结构、初始化 GPIO/中断、读取设备 ID 确认连接。
  3. 配置 SPI 参数: 设置 spi->mode(CPOL/CPHA/CS_HIGH)、spi->max_speed_hz、spi->bits_per_word。
  4. 数据传输: 调用 spi_write()、spi_read() 或 spi_write_then_read() 完成传感器寄存器读写。
  5. 移除(remove): 释放资源、关闭设备。
传感器适配示例: 温度传感器 TMP121 的驱动在 probe 中通过 spi_write_then_read() 发送 0x00 读取温度寄存器,返回 16 位数据经转换后通过 IIO 框架上报。
// SPI 设备驱动 probe 示例片段
static int tmp121_probe(struct spi_device *spi)
{
    struct tmp121_data *data;
    int ret;

    data = devm_kzalloc(&spi->dev, sizeof(*data), GFP_KERNEL);
    if (!data)
        return -ENOMEM;

    spi->mode = SPI_MODE_1;          // CPOL=0, CPHA=1
    spi->max_speed_hz = 1000000;     // 1 MHz
    spi->bits_per_word = 8;
    ret = spi_setup(spi);
    if (ret < 0)
        return ret;

    // 读取设备 ID 寄存器验证连接
    ret = spi_write_then_read(spi, &tmp121_reg_id, 1, data->id_buf, 2);
    if (ret < 0 || data->id_buf[0] != TMP121_EXPECTED_ID)
        return -ENODEV;

    // 注册 IIO 设备
    return devm_iio_device_register(&spi->dev, data->indio_dev);
}

4.4 SPI 消息与传输队列

SPI 消息(struct spi_message)是 SPI 子系统中最核心的传输单元。一个消息包含一个或多个 struct spi_transfer,这些 transfer 在同一个片选周期内连续执行,中间不会释放总线。传输队列机制则负责管理多个待处理的消息。

SPI 消息与传输结构体对比
结构体作用关键字段
struct spi_message一次完整的 SPI 会话(含多个 transfer)transfers 链表、complete 回调、context、status
struct spi_transfer单个片选周期内的一个数据段tx_buf、rx_buf、len、speed_hz、delay_usecs
struct spi_controller 队列控制器维护的待处理消息队列queue 链表、cur_msg、busy 标志

传输队列的工作流程遵循生产者-消费者模型:

  • 生产者(设备驱动): 调用 spi_async() 将消息提交到控制器的队列尾部。
  • 消费者(控制器驱动): 内核工作线程或中断处理函数从队列头部取出消息,调用 transfer_one_message() 执行传输。
  • 完成通知: 传输结束后,控制器驱动调用 spi_finalize_current_message(),触发消息的 complete 回调。
同步 vs 异步: spi_sync() 本质上是 spi_async() 的同步封装,它提交消息后等待完成信号量。在中断上下文或需要高吞吐的场景中,应优先使用 spi_async() 并注册完成回调。
SPI 消息与传输队列流程 SPI 消息与传输队列流程 SPI 设备驱动 spi_message_init() spi_message_add_tail() spi_async() 提交 控制器消息队列 消息 3(排队中) 消息 2(排队中) 消息 1(正在传输) transfer_one_message() complete 回调 spi_finalize_current_message() 消息队列保证同一时刻只有一个消息在传输,避免片选信号冲突

4.5 四层架构协同工作流程

当传感器驱动发起一次 SPI 读取操作时,四层架构的完整调用链如下:

  1. 设备驱动 调用 spi_write_then_read(),内部构造 spi_message 并添加两个 transfer(写地址 + 读数据)。
  2. 核心层 的 spi_async() 将消息挂入控制器驱动的 queue 链表,并唤醒工作队列。
  3. 控制器驱动 的工作线程取出消息,调用 transfer_one_message(),逐条执行 transfer。
  4. 每个 transfer 通过 硬件寄存器 操作 SPI 外设,利用 FIFO 或 DMA 完成数据收发。
  5. 所有 transfer 完成后,控制器驱动调用 spi_finalize_current_message(),触发设备驱动的完成回调。
性能关键点: 对于需要连续读写多个寄存器的传感器(如加速度计读取 XYZ 三轴数据),应使用单个 spi_message 包含多个 transfer,而不是多次调用 spi_write_then_read()。前者只产生一次片选有效信号,后者每次调用都会重新拉低片选,增加协议开销。