3. Linux SPI子系统框架:分层架构与关键数据结构

Linux SPI子系统采用经典的分层架构设计,将硬件控制、核心机制与设备逻辑解耦。理解这一分层体系是进行SPI驱动开发与性能调优的基础。本章深入剖析SPI核心层、控制器驱动层、设备驱动层的职责边界,并详细解读三个核心数据结构:spi_master、spi_device 和 spi_transfer。

核心要点: SPI子系统通过分层抽象,使得控制器驱动无需关心具体外设协议,设备驱动无需操作寄存器,核心层负责总线、数据传输与同步管理。

3.1 三层架构总览

SPI子系统从上到下分为三层,每一层都有明确的职责和接口契约。

  • 设备驱动层(SPI Protocol Driver): 位于最上层,与具体的SPI外设(如ADC、传感器、显示屏)交互。通过核心层提供的API发起数据传输,不直接操作控制器寄存器。
  • SPI核心层(SPI Core): 中间层,提供统一的框架。管理总线注册、设备枚举、数据传输队列、同步机制。定义了标准的数据结构(spi_master、spi_device、spi_transfer)和回调函数。
  • 控制器驱动层(SPI Controller Driver): 最底层,直接操作SPI控制器硬件寄存器。负责FIFO管理、DMA配置、时钟与片选控制。实现核心层定义的回调接口(如 transfer_one、prepare_message)。
SPI子系统三层架构图 SPI子系统三层架构 设备驱动层 (SPI Protocol Driver) spi_driver / spi_device 绑定 发起 spi_sync / spi_async 传输 SPI 核心层 (SPI Core) spi_master / spi_device / spi_transfer 管理 总线注册、消息队列、同步/异步调度 控制器驱动层 (SPI Controller Driver) transfer_one / prepare_message / unprepare_message FIFO 操作 / DMA 配置 / 时钟与片选控制 数据流方向:上 → 下
设计哲学: 设备驱动只关心“发什么数据”,控制器驱动只关心“怎么发数据”,核心层负责“何时发、发给谁”。这种分离使得同一套SPI控制器驱动可以支持任意数量的不同外设。

3.2 关键数据结构详解

SPI子系统的核心由三个数据结构支撑,它们贯穿整个数据传输生命周期。

3.2.1 spi_master:控制器实例

spi_master 代表一个SPI控制器硬件实例,是控制器驱动的核心对象。它包含了控制器的硬件能力、配置参数以及回调函数指针。

字段/回调 类型 说明
bus_num u16 SPI总线编号,用于标识控制器
num_chipselect u16 支持的片选信号数量
mode_bits u32 支持的SPI模式位掩码(CPOL/CPHA/CS_HIGH等)
min_speed_hz u32 支持的最低SCLK频率
max_speed_hz u32 支持的最高SCLK频率
transfer_one 回调函数 核心传输接口,控制器驱动必须实现
prepare_message 回调函数 消息传输前的准备工作(如DMA设置)
unprepare_message 回调函数 消息传输后的清理工作
can_dma 回调函数 判断当前传输是否适合使用DMA
dma_rx/dma_tx dma_chan* DMA通道句柄(可选)
驱动开发注意: transfer_one 回调是控制器驱动的性能瓶颈所在。对于FIFO模式,该函数通常采用轮询或中断方式逐字传输;对于DMA模式,则在此函数中启动DMA传输并注册完成回调。务必保证该函数的执行效率。

3.2.2 spi_device:外设抽象

spi_device 代表一个连接到SPI总线上的具体外设。它由核心层在设备枚举时创建,并绑定到对应的设备驱动。

struct spi_device {
    struct device           dev;            // 内核通用设备结构
    struct spi_master       *master;        // 所属控制器
    u32                     max_speed_hz;   // 该外设的最大SCLK频率
    u8                      chip_select;    // 片选编号
    u8                      mode;           // SPI模式 (CPOL/CPHA/CS_HIGH/3WIRE等)
    u8                      bits_per_word;  // 字长,通常为8
    int                     irq;            // 外设中断号(可选)
    void                    *controller_data; // 控制器私有数据
    struct spi_device       *dev;           // 链表指针
};
  • 生命周期: 在板级代码或设备树中定义后,由核心层在 spi_register_master 过程中创建。
  • 绑定关系: 通过 spi_driver 的 id_table 或 of_match_table 匹配。
  • 配置隔离: 每个 spi_device 拥有独立的 max_speed_hz 和 mode,控制器驱动在切换片选时自动应用这些配置。

3.2.3 spi_transfer:数据传输单元

spi_transfer 是SPI数据传输的最小单元。一个SPI消息(spi_message)可以包含多个 spi_transfer,它们按顺序执行,片选在整个消息期间保持有效。

struct spi_transfer {
    const void      *tx_buf;        // 发送缓冲区(NULL表示只接收)
    void            *rx_buf;        // 接收缓冲区(NULL表示只发送)
    unsigned        len;            // 传输字节数
    unsigned        speed_hz;       // 本次传输的SCLK频率(0表示使用默认值)
    u16             bits_per_word;  // 本次传输的字长(0表示使用默认值)
    u8              cs_change;      // 传输完成后是否切换片选
    u8              tx_nbits;       // 发送线宽(1/2/4,用于双/四线模式)
    u8              rx_nbits;       // 接收线宽
    dma_addr_t      tx_dma;         // DMA发送地址(由控制器驱动填充)
    dma_addr_t      rx_dma;         // DMA接收地址(由控制器驱动填充)
    struct sg_table tx_sg;          // 发送的SG列表(用于SG-DMA)
    struct sg_table rx_sg;          // 接收的SG列表(用于SG-DMA)
};
性能调优关键字段:
  • tx_sg / rx_sg:使用SG(Scatter-Gather)列表可以实现非连续内存的DMA传输,避免数据拷贝,是高性能驱动的重要标志。
  • cs_change:合理设置该字段可以控制片选信号,在多设备连续操作时减少片选切换开销。
  • speed_hz:允许在同一消息内对不同传输段使用不同速率,适用于配置+数据分离的外设协议。
SPI核心数据结构关系图 SPI核心数据结构关系图 spi_master bus_num: 0 num_chipselect: 2 transfer_one() spi_device chip_select: 0 max_speed_hz: 10MHz mode: SPI_MODE_0 spi_device chip_select: 1 max_speed_hz: 5MHz mode: SPI_MODE_3 spi_message 包含多个 spi_transfer spi_transfer tx_buf / rx_buf / len 关系说明:一个 spi_master 管理多个 spi_device;每个 spi_device 通过 spi_message 发起传输; 每个 spi_message 包含一个或多个顺序执行的 spi_transfer。

3.3 数据传输流程

一个典型的SPI数据传输从设备驱动发起,经过核心层调度,最终由控制器驱动执行。以下是标准流程的步骤分解:

  1. 设备驱动构造消息: 初始化 spi_message,添加一个或多个 spi_transfer,设置缓冲区与长度。
  2. 发起同步/异步传输: 调用 spi_sync()(阻塞)或 spi_async()(非阻塞,需提供完成回调)。
  3. 核心层排队: 将消息加入 spi_master 的消息队列,通过 spi_pump_messages() 工作队列调度。
  4. 控制器驱动执行: 核心层调用 master->transfer_one(),控制器驱动在此函数中操作硬件寄存器完成数据收发。
  5. 完成通知: 传输完成后,控制器驱动调用 spi_finalize_current_message(),核心层唤醒等待的同步线程或调用异步回调。
异步传输优势: 在DMA模式下,spi_async() 允许CPU在DMA搬运数据的同时处理其他任务,显著提升系统吞吐量。对于高频率、大数据量的SPI外设(如显示驱动、高速ADC),应优先使用异步传输。

3.4 分层架构的模块交互

下表总结了各层之间的核心交互接口与数据流向:

交互方向 接口/机制 说明
设备驱动 → 核心层 spi_sync() / spi_async() 发起数据传输请求
核心层 → 控制器驱动 transfer_one() 回调 传递单个 spi_transfer 给控制器执行
核心层 → 控制器驱动 prepare_message() 回调 消息开始前的硬件准备(如DMA通道配置)
控制器驱动 → 核心层 spi_finalize_current_message() 通知核心层当前消息传输完成
核心层 → 设备驱动 完成回调 / 唤醒等待队列 通知设备驱动传输结果
常见错误: 在 transfer_one() 中直接调用 spi_finalize_current_message() 时,必须确保所有DMA传输已完成。若在DMA传输未完成时调用,会导致数据错乱或缓冲区被提前释放。正确的做法是在DMA完成中断处理函数中调用该接口。