2. Linux SPI子系统架构:三层架构与核心数据结构
Linux SPI子系统采用经典的分层设计思想,将SPI总线操作抽象为SPI核心层、SPI控制器驱动层和SPI设备驱动层。这种分层架构使得控制器厂商和设备厂商可以独立开发驱动,通过标准接口完成数据交互。理解这三层架构及其对应的数据结构,是编写高效SPI控制器驱动的基础。
2.1 SPI核心层(SPI Core)
SPI核心层位于drivers/spi/spi.c,是连接设备驱动与控制器驱动的桥梁。核心层负责总线注册、设备枚举、数据传输调度以及通用API的提供。
核心层主要职责
- 总线管理:注册SPI总线类型(
spi_bus_type),维护spi_master和spi_device的链表。 - 设备与驱动匹配:通过设备树(Device Tree)或ACPI、板级代码创建设备,并与
spi_driver进行匹配。 - 传输调度:将
spi_device发起的spi_transfer队列化,调用控制器驱动的回调执行实际传输。 - 通用API封装:提供同步(
spi_sync)、异步(spi_async)以及便捷函数(spi_write_then_read)。
关键点:核心层不直接操作硬件寄存器,所有硬件访问都通过控制器驱动的回调函数完成。这种设计保证了核心层的平台无关性。
2.2 SPI控制器驱动层
控制器驱动层是SPI子系统的硬件抽象层,直接与SoC的SPI控制器硬件交互。该层需要实现struct spi_master(或struct spi_controller)中的回调函数,完成FIFO操作、DMA配置、片选控制等底层逻辑。
spi_master核心数据结构
| 字段/回调 | 类型 | 说明 |
|---|---|---|
bus_num |
u16 |
SPI总线编号,用于标识控制器实例 |
num_chipselect |
u16 |
支持的片选信号数量 |
mode_bits |
u32 |
支持的SPI模式位掩码(CPOL/CPHA/CS_HIGH等) |
flags |
u32 |
控制器能力标志(如DMA支持、FIFO大小等) |
transfer_one_message |
回调 | 处理整个消息队列,默认实现由核心层提供 |
transfer_one |
回调 | 处理单个spi_transfer,控制器驱动通常重写此函数 |
set_cs |
回调 | 控制片选信号的电平状态 |
prepare_message |
回调 | 在消息传输前进行硬件准备(如配置DMA) |
unprepare_message |
回调 | 消息传输完成后的清理工作 |
dma_rx/dma_tx |
dma_chan* |
DMA通道句柄,用于FIFO+DMA传输优化 |
控制器驱动开发要点:在
probe()函数中分配spi_master,设置bus_num、num_chipselect、mode_bits,并注册transfer_one和set_cs回调。对于支持FIFO和DMA的控制器,还需要配置dma_rx/dma_tx通道。
2.3 SPI设备驱动层
设备驱动层面向具体的SPI外设(如MCP2515 CAN控制器、AD7793 ADC、SPI Flash等)。设备驱动通过struct spi_driver注册到SPI总线,使用struct spi_device访问控制器。
spi_device与spi_driver
| 数据结构 | 关键字段 | 说明 |
|---|---|---|
spi_device |
chip_select, max_speed_hz, mode, bits_per_word, controller_state |
代表一个SPI从设备,由核心层在设备树解析或板级代码中创建 |
spi_driver |
probe, remove, id_table, driver |
设备驱动结构体,通过spi_register_driver()注册 |
spi_transfer |
tx_buf, rx_buf, len, speed_hz, delay_usecs, cs_change |
描述一次SPI传输的缓冲区、长度、速率和时序参数 |
spi_transfer传输流程
- 设备驱动构造一个或多个
spi_transfer,填充发送缓冲区、接收缓冲区、传输长度等参数。 - 将
spi_transfer添加到spi_message中,调用spi_sync()或spi_async()提交消息。 - 核心层将消息加入控制器的消息队列,调用控制器驱动的
transfer_one_message或transfer_one。 - 控制器驱动根据传输参数配置硬件寄存器,启动FIFO或DMA传输。
- 传输完成后,控制器驱动调用
spi_finalize_current_message()通知核心层,核心层唤醒等待的设备驱动。
2.4 三层架构的交互流程
当设备驱动发起一次SPI读写时,三层架构按照以下顺序协同工作:
| 步骤 | 所在层 | 操作描述 |
|---|---|---|
| 1 | 设备驱动层 | 构造spi_transfer,设置tx_buf/rx_buf/len,调用spi_sync() |
| 2 | 核心层 | 将spi_message加入master->queue,调用master->transfer_one_message |
| 3 | 控制器驱动层 | 解析spi_transfer,配置控制器寄存器(波特率、模式、FIFO阈值) |
| 4 | 控制器驱动层 | 启动FIFO传输或触发DMA请求,硬件自动完成数据移位 |
| 5 | 控制器驱动层 | 传输完成中断或DMA完成回调,调用spi_finalize_current_message() |
| 6 | 核心层 | 唤醒等待队列,设备驱动获取传输结果(msg->status和msg->actual_length) |
常见误区:初学者容易在设备驱动中直接操作控制器寄存器,这破坏了分层架构。正确的做法是设备驱动只通过
spi_sync/spi_async与核心层交互,所有硬件寄存器操作必须封装在控制器驱动中。
2.5 数据结构关系总结
以下表格总结了三个核心数据结构在分层架构中的角色和关联:
| 数据结构 | 所属层 | 创建者 | 使用者 | 生命周期 |
|---|---|---|---|---|
spi_master |
控制器驱动层 | 控制器驱动probe() |
核心层、控制器驱动 | 与控制器硬件绑定,直到驱动卸载 |
spi_device |
核心层/设备驱动层 | 核心层(设备树解析) | 设备驱动 | 从设备枚举到移除 |
spi_transfer |
设备驱动层 | 设备驱动 | 核心层、控制器驱动 | 单次传输,完成后释放 |
架构设计优势:三层架构使得SPI控制器驱动可以复用同一套核心层代码,设备驱动无需关心底层硬件差异。例如,一个SPI Flash驱动可以在不同SoC上运行,只要各SoC提供了符合规范的控制器驱动。本课程后续章节将重点讲解如何在控制器驱动层实现FIFO和DMA优化,以提升传输吞吐量并降低CPU占用率。