2. Linux SPI子系统架构

Linux SPI子系统采用分层设计,将硬件控制、协议传输和设备操作解耦为三个独立层次,并通过设备树完成硬件描述与平台适配。理解这一架构是开发高性能SPI驱动(尤其是DMA传输优化)的基础。

2.1 架构总览:四层模型

SPI子系统从下到上分为:SPI控制器驱动层(硬件相关)、SPI核心层(通用框架)、SPI设备驱动层(协议相关),以及贯穿硬件描述的SPI设备树绑定。下图展示了各层之间的调用关系与数据流。

Linux SPI子系统四层架构 SPI设备驱动层 spi_driver / spi_device 协议实现(如 MMC、触摸屏、ADC) SPI核心层 spi.c / spi.h / spi-mem.c 总线注册、消息队列、传输抽象 SPI控制器驱动层 spi_controller / master FIFO/DMA/GPIO 硬件操作 SPI设备树绑定 dts / dtsi 文件 硬件拓扑、CS/时钟/DMA通道描述 用户空间 spidev 字符设备 内核空间 通用传输框架 硬件抽象层 SoC 寄存器操作 数据流方向 设备 → 核心 → 控制器 配置描述层 硬件拓扑静态描述

2.2 SPI核心层(SPI Core)

核心层位于 drivers/spi/spi.c,是子系统的心脏。它不直接操作硬件,而是提供统一的框架来管理总线、设备和传输。

核心组件文件/结构体职责
总线注册spi_bus_type管理 SPI 总线上的设备枚举与匹配
控制器管理struct spi_controller抽象 SPI 主机控制器,维护传输队列
设备管理struct spi_device表示一个 SPI 从设备,包含模式、速率、CS 信息
消息传输struct spi_message封装一组有序的 spi_transfer,支持原子化提交
传输抽象struct spi_transfer描述一次单向/双向传输:tx_buf、rx_buf、len、speed_hz
关键设计:核心层通过 spi_async() 和 spi_sync() 提供同步/异步两种提交方式。异步路径是 DMA 传输的入口,同步路径内部最终也调用异步接口 + 等待完成。

2.3 SPI控制器驱动层

控制器驱动直接与 SoC 的 SPI 硬件模块交互,负责配置寄存器、管理 FIFO、触发 DMA 请求。每个 SPI 控制器对应一个 struct spi_controller 实例。

控制器驱动核心接口

// 控制器驱动必须实现的核心回调
struct spi_controller {
    // 传输方法(二选一或同时实现)
    int (*transfer_one)(struct spi_controller *ctlr,
                        struct spi_device *spi,
                        struct spi_transfer *transfer);
    int (*transfer_one_message)(struct spi_controller *ctlr,
                                struct spi_message *msg);

    // DMA 相关
    int (*dma_map)(struct spi_controller *ctlr,
                   struct spi_transfer *xfer);
    void (*dma_unmap)(struct spi_controller *ctlr,
                      struct spi_transfer *xfer);

    // 硬件控制
    void (*set_cs)(struct spi_device *spi, bool enable);
    int (*prepare_message)(struct spi_controller *ctlr,
                           struct spi_message *msg);
    int (*unprepare_message)(struct spi_controller *ctlr,
                             struct spi_message *msg);

    // 控制器能力标志
    u32 mode_bits;          // 支持的 SPI 模式
    u32 flags;              // 如 SPI_CONTROLLER_MUST_TX
    u16 bus_num;            // 总线编号
    size_t max_dma_len;     // 单次 DMA 最大长度
};
性能要点:实现 transfer_one 而非 transfer_one_message 可以获得更细粒度的传输控制,便于在每次 transfer 前后配置 DMA 描述符。多数高性能 SPI 控制器驱动(如 dw-spi、omap2-spi)都采用此方式。

2.4 SPI设备驱动层

设备驱动位于最上层,实现具体的协议逻辑。它通过核心层提供的 API 与 SPI 控制器通信,不关心底层硬件细节。

设备驱动注册模板

static const struct spi_device_id my_spi_ids[] = {
    { "my-spi-device", 0 },
    { }
};
MODULE_DEVICE_TABLE(spi, my_spi_ids);

static const struct of_device_id my_spi_of_match[] = {
    { .compatible = "vendor,my-spi-device" },
    { }
};
MODULE_DEVICE_TABLE(of, my_spi_of_match);

static struct spi_driver my_spi_driver = {
    .driver = {
        .name   = "my-spi-device",
        .of_match_table = my_spi_of_match,
    },
    .id_table = my_spi_ids,
    .probe    = my_spi_probe,
    .remove   = my_spi_remove,
};
module_spi_driver(my_spi_driver);

设备驱动中的传输模式选择

传输方式API适用场景DMA 支持
同步阻塞spi_sync()小数据量(< 64 bytes)、低并发由核心层自动选择
异步回调spi_async()大数据量、需要非阻塞推荐,DMA 友好
同步单次spi_write_then_read()写后立即读(如寄存器访问)通常 PIO
内存映射spi_mem_exec_op()SPI NOR/NAND Flash控制器依赖

2.5 SPI设备树绑定

设备树是 SPI 子系统的硬件描述语言,用于在系统启动时静态描述 SPI 控制器和从设备的拓扑、参数及资源(包括 DMA 通道)。

典型 SPI 设备树节点结构

// SPI 控制器节点(位于 SoC dtsi 中)
spi0: spi@ff010000 {
    compatible = "vendor,spi-controller";
    reg = <0xff010000 0x1000>;
    interrupts = <0 32 4>;
    clocks = <&clkc 15>;
    #address-cells = <1>;
    #size-cells = <0>;

    // DMA 通道描述(用于控制器驱动)
    dmas = <&dma 0 2>, <&dma 1 2>;
    dma-names = "tx", "rx";

    // SPI 从设备节点(位于板级 dts 中)
    spi-device@0 {
        compatible = "vendor,my-spi-device";
        reg = <0>;              // CS0
        spi-max-frequency = <20000000>;
        spi-cpha;               // CPHA=1
        spi-cpol;               // CPOL=1
        spi-cs-high;            // CS 高有效
        spi-3wire;              // 三线模式
    };
};

设备树关键属性说明

属性作用域说明
compatible控制器/设备驱动匹配字符串,必须与 of_match_table 一致
reg从设备片选编号(0 对应 CS0,1 对应 CS1)
spi-max-frequency从设备该设备支持的最大 SPI 时钟频率(Hz)
spi-cpha / spi-cpol从设备配置 SPI 模式(0~3),默认 mode 0
dmas / dma-names控制器指定 TX/RX DMA 通道,由控制器驱动解析
spi-cs-high从设备片选信号高有效(默认低有效)
spi-3wire从设备三线模式(无 MISO/MOSI 区分)
设备树 → 驱动匹配与资源绑定流程 设备树(.dts) compatible + reg + dmas OF 核心层 of_match_table 匹配 SPI 控制器驱动 probe() → 解析 dmas/reg SPI 设备驱动 probe() → spi_setup() 运行时传输 spi_sync / spi_async 设备树解析时机 boot 时由 OF 框架完成 驱动 probe 时读取属性

2.6 各层协作示例:一次 DMA 传输的完整路径

以设备驱动发起一次 4KB 读请求为例,展示数据在各层的流转:

  1. 设备驱动构造 spi_message + spi_transfer,调用 spi_async()。
  2. 核心层将消息加入控制器队列,调用控制器驱动的 transfer_one 回调。
  3. 控制器驱动检查传输长度 > FIFO 深度阈值,决定启用 DMA。
  4. 控制器驱动调用 dma_map 映射缓冲区,配置 DMA 描述符(源/目的地址、长度、中断)。
  5. 启动 DMA 传输,硬件自动完成数据搬移,完成后触发 DMA 完成中断。
  6. 控制器驱动在中断处理中调用 spi_finalize_current_transfer(),通知核心层传输完成。
  7. 核心层唤醒等待队列或调用设备驱动的完成回调。
常见误区:设备驱动不应直接操作 DMA 描述符或控制器寄存器。所有 DMA 配置必须由控制器驱动在 transfer_one 中完成,设备驱动只需通过 spi_transfer 的 tx_dma/rx_dma 标志告知核心层“此传输适合 DMA”。

2.7 架构设计对性能优化的启示

层次优化切入点对 DMA 传输的影响
设备驱动使用 spi_async + 预分配 DMA 缓冲区减少内存拷贝,支持流水线传输
核心层调整 spi_controller.rt 实时标志避免调度延迟影响 DMA 完成响应
控制器驱动实现 can_dma 回调 + 动态阈值精确控制 PIO/DMA 切换点
设备树正确配置 dmas 和 dma-names确保 DMA 通道分配正确,避免资源冲突