2. Linux SPI子系统架构:SPI核心层、SPI控制器驱动、SPI设备驱动、数据结构关系图

Linux SPI子系统采用标准的分层架构设计,将硬件控制逻辑与设备协议处理解耦。整个架构自底向上分为三层:SPI控制器驱动层、SPI核心层和SPI设备驱动层。理解这三层的职责边界与数据结构关系,是开发高质量SPI驱动的基石。

架构设计原则:控制器驱动负责硬件寄存器操作与传输引擎控制;核心层提供统一的框架、消息队列管理与DMA通道协调;设备驱动仅需关注协议解析与数据语义,无需关心底层时序。

2.1 三层架构总览

下图展示了SPI子系统的完整分层结构,以及各层之间的调用关系与数据流向:

Linux SPI子系统三层架构图 Linux SPI子系统三层架构 用户空间应用程序 (APP) SPI设备驱动层 spi_driver / spi_device 协议解析、数据语义、字符设备接口 SPI核心层 (spi.c / spi-mem.c) spi_master / spi_message / spi_transfer 消息队列管理、DMA通道协调、总线锁定 spi_async() / spi_sync() / spi_write_then_read() SPI控制器驱动层 platform_driver / spi_master_ops 硬件寄存器操作、FIFO/DMA引擎、中断处理 SPI控制器硬件 (SoC SPI IP) 数据流:APP → 设备驱动 → 核心层(消息队列) → 控制器驱动 → 硬件
关键认知:设备驱动不直接操作控制器寄存器,所有传输请求都通过核心层的消息队列(spi_message)下发。控制器驱动从队列中取出传输任务,执行实际的硬件操作。

2.2 核心数据结构关系图

SPI子系统的核心数据结构包括 spi_master、spi_device、spi_driver、spi_message 和 spi_transfer。它们之间的关联关系如下图所示:

SPI核心数据结构关系图 SPI核心数据结构关系图 struct spi_master bus_num / num_chipselect min_speed_hz / max_speed_hz transfer_one_message() can_dma / dma_map / dma_unmap struct spi_device controller_state / controller_data chip_select / max_speed_hz mode (CPOL/CPHA/CS_HIGH) bits_per_word / irq struct spi_driver probe() / remove() id_table driver.pm shutdown() master->dev device_driver struct spi_message transfers (链表头) complete() / context status / actual_length spi (指向spi_device) struct spi_transfer tx_buf / rx_buf / len tx_dma / rx_dma speed_hz / bits_per_word cs_change / delay_usecs msg->transfers msg->spi 一个 spi_message 包含一个或多个 spi_transfer,所有 transfer 在同一个 CS 片选周期内连续执行 spi_master 通过 transfer_one_message() 回调处理消息队列中的每个 spi_message 消息队列处理流程 spi_async() → 核心层将 spi_message 加入 master->queue → 控制器驱动通过 transfer_one_message() 逐个处理 DMA传输时:核心层调用 master->can_dma() 判断,然后调用 master->dma_map() 映射缓冲区

2.3 各层职责与关键接口

层次 核心结构体 主要职责 关键回调/接口
控制器驱动 spi_master 硬件寄存器配置、FIFO/DMA引擎控制、中断处理、时钟管理 transfer_one_message()
can_dma()
dma_map()
dma_unmap()
核心层 spi_master
spi_message
spi_transfer
消息队列管理、总线锁定、DMA通道协调、同步/异步传输封装 spi_async()
spi_sync()
spi_write_then_read()
__spi_queued_transfer()
设备驱动 spi_driver
spi_device
协议解析、数据语义转换、字符设备接口、电源管理 probe()
remove()
spi_write()
spi_read()
常见误区:设备驱动中不应直接访问 spi_master 的成员或调用控制器驱动的回调。所有传输必须通过核心层提供的 API(如 spi_sync()、spi_async())发起,否则会破坏消息队列的原子性和DMA协调逻辑。

2.4 数据结构初始化与注册流程

SPI子系统的初始化遵循自底向上的顺序:先注册控制器驱动,核心层自动创建 spi_device,然后设备驱动通过 spi_driver 匹配并绑定。关键步骤与数据结构操作如下:

  1. 控制器驱动注册:调用 spi_register_master(),核心层分配 spi_master 并初始化消息队列、DMA相关字段。
  2. 设备枚举:核心层根据设备树(Device Tree)或板级代码中的SPI子节点,为每个片选创建 spi_device,并关联到对应的 spi_master。
  3. 设备驱动绑定:设备驱动调用 spi_register_driver(),核心层通过 spi_bus_type 的匹配逻辑将 spi_driver 与 spi_device 配对,触发 probe()。
  4. 传输通道建立:在 probe() 中,设备驱动保存 spi_device 指针,后续通过该指针发起所有传输请求。
/* 控制器驱动注册示例 (伪代码) */
static struct spi_master *master;
master = spi_alloc_master(&pdev->dev, sizeof(struct my_spi_controller));
master->bus_num = 1;
master->num_chipselect = 2;
master->transfer_one_message = my_transfer_one_message;
master->can_dma = my_can_dma;
master->dma_map = my_dma_map;
master->dma_unmap = my_dma_unmap;
spi_register_master(master);

/* 设备驱动 probe 示例 */
static int my_spi_device_probe(struct spi_device *spi)
{
    struct my_device_data *data;
    data = devm_kzalloc(&spi->dev, sizeof(*data), GFP_KERNEL);
    spi_set_drvdata(spi, data);
    data->spi = spi;  /* 保存 spi_device 指针 */
    /* 配置 SPI 参数 */
    spi->mode = SPI_MODE_0;
    spi->max_speed_hz = 10000000;
    spi->bits_per_word = 8;
    spi_setup(spi);
    return 0;
}

2.5 消息队列与DMA传输的整合点

在SPI子系统中,消息队列与DMA传输的整合发生在核心层与控制器驱动的接口处。核心层提供以下关键机制:

整合点 核心层行为 控制器驱动行为
DMA决策 遍历 spi_message 中的每个 spi_transfer,调用 master->can_dma() 判断是否使用DMA 根据传输长度、缓冲区对齐、DMA通道可用性返回 true/false
DMA映射 对 can_dma() 返回 true 的 transfer,调用 master->dma_map() 建立 DMA 映射 调用 dma_map_single() 或 sg_dma_address(),填充 transfer->tx_dma / rx_dma
传输执行 调用 master->transfer_one_message() 将整个消息交给控制器驱动 解析 transfer 链表,对标记了 DMA 的 transfer 启动 DMA 引擎,否则使用 PIO/FIFO
完成回调 控制器驱动完成所有 transfer 后调用 spi_finalize_current_message() 在 DMA 完成中断或 PIO 完成后调用 spi_finalize_current_message()
性能关键路径:对于大数据块传输(通常 > 256 字节),DMA 模式可降低 CPU 占用率 80% 以上。核心层的 can_dma() 回调是决定是否启用 DMA 的决策点,通常建议对小数据量(< 64 字节)使用 PIO 以避免 DMA 建立开销。

2.6 数据结构关系总结

下表汇总了SPI子系统中主要数据结构之间的引用关系与生命周期:

数据结构 生命周期 持有者/创建者 关键引用关系
spi_master 控制器驱动加载 → 卸载 核心层分配,控制器驱动填充 包含 spi_device 链表;通过 dev 关联设备模型
spi_device 设备树解析/板级注册 → 移除 核心层创建 master 指向所属控制器;driver 指向绑定的设备驱动
spi_driver 模块加载 → 卸载 设备驱动定义并注册 通过 id_table 匹配 spi_device;probe() 接收 spi_device
spi_message 传输请求发起 → 完成回调 设备驱动分配,核心层管理队列 spi 指向目标 spi_device;transfers 链表头
spi_transfer 包含在 spi_message 生命周期内 设备驱动分配并添加到 message tx_buf/rx_buf 指向数据缓冲区;tx_dma/rx_dma 由DMA映射填充
开发建议:在编写设备驱动时,优先使用 spi_sync() 进行同步传输,它内部封装了消息队列的入队与等待。对于需要高吞吐的场景,使用 spi_async() 并注册完成回调,避免阻塞当前进程。DMA 传输的缓冲区必须使用 dma_alloc_coherent() 或 kmalloc() 分配(确保物理地址连续),栈上缓冲区会导致 DMA 映射失败。