2. Linux SPI子系统架构
在车载嵌入式Linux系统中,SPI(Serial Peripheral Interface)子系统采用经典的分层架构设计,从上到下依次为:SPI设备驱动层、SPI核心层、SPI控制器驱动层。这种分层设计实现了硬件无关性与驱动可移植性,是车载ECU之间高速同步通信的基础。
2.1 三层架构总览
SPI子系统位于Linux内核的 drivers/spi/ 目录下,其核心思想是将SPI控制器(主机端)的硬件操作与SPI设备(从机端)的协议处理解耦。三层职责如下表所示:
| 层级 | 内核路径/文件 | 核心职责 | 关键数据结构 |
|---|---|---|---|
| SPI设备驱动层 | drivers/spi/spidev.c 或自定义驱动 |
实现具体SPI外设(如ADC、CAN控制器、传感器)的协议解析与数据交互 | spi_driver、spi_device |
| SPI核心层 | drivers/spi/spi.c | 提供统一的框架:总线注册、设备枚举、数据传输队列管理、回调调度 | spi_controller、spi_transfer、spi_message |
| SPI控制器驱动层 | drivers/spi/spi-*.c (如spi-imx.c、spi-dw.c) |
直接操作SPI控制器寄存器,完成时钟、片选、数据移位等硬件时序 | spi_controller(填充ops)、spi_master |
车载工程要点: 在车载项目中,SPI控制器驱动通常由SoC厂商(如NXP i.MX8、TI Jacinto)提供,而设备驱动层则需要工程师根据具体外设手册(如车载音频DSP、安全气囊传感器)自行开发。核心层一般无需修改,但需要理解其回调机制以配置DMA或中断模式。
2.2 三层架构数据流与回调机制
SPI数据传输的完整路径从用户空间或内核子系统发起,经过设备驱动层封装为 spi_message,由核心层排队调度,最终由控制器驱动层完成硬件收发。下图展示了这一数据流与回调关系:
2.3 各层关键数据结构与回调详解
2.3.1 SPI设备驱动层
设备驱动层通过 spi_driver 结构体注册到SPI总线。核心回调包括:
- probe():当SPI核心层检测到设备与驱动匹配时调用,完成设备初始化、配置spi_device参数(模式、频率、bits_per_word)。
- remove():设备移除或驱动卸载时调用,释放资源。
- shutdown():系统关机或休眠时调用,用于车载安全场景下的外设下电时序。
/* SPI设备驱动模板 - 车载传感器示例 */
static int car_sensor_probe(struct spi_device *spi)
{
struct car_sensor_data *data;
/* 配置SPI通信参数 */
spi->mode = SPI_MODE_0;
spi->max_speed_hz = 10 * 1000 * 1000; /* 10MHz */
spi->bits_per_word = 8;
spi_setup(spi);
data = devm_kzalloc(&spi->dev, sizeof(*data), GFP_KERNEL);
spi_set_drvdata(spi, data);
/* 注册输入子系统或IIO设备 */
return 0;
}
static struct spi_driver car_sensor_driver = {
.driver = {
.name = "car_sensor",
.of_match_table = car_sensor_of_match,
},
.probe = car_sensor_probe,
.remove = car_sensor_remove,
};
module_spi_driver(car_sensor_driver);
2.3.2 SPI核心层
核心层是SPI子系统的中枢,负责管理 spi_controller 和 spi_device 的生命周期。其核心回调机制如下:
| 回调函数 | 触发时机 | 车载场景作用 |
|---|---|---|
transfer_one_message() |
核心层从消息队列取出一个spi_message时 | 将消息拆解为多个transfer,逐个下发到控制器驱动 |
prepare_message() |
transfer_one_message执行前 | 预配置DMA通道或锁定SPI总线(防止多核竞态) |
unprepare_message() |
transfer_one_message执行完成后 | 释放DMA资源或解锁总线 |
can_dma() |
核心层判断是否使用DMA传输时 | 根据transfer长度决定是否启用DMA(车载大数据流场景关键) |
车载优化提示: 在车载多传感器数据采集场景中,核心层的消息队列深度和DMA阈值直接影响实时性。建议通过
spi_controller.rt 标志启用实时调度,并合理设置 spi_controller.max_dma_len 以匹配车载SPI帧长(通常为8~64字节)。
2.3.3 SPI控制器驱动层
控制器驱动层直接与硬件寄存器交互,是三层中唯一与SoC绑定的部分。其回调函数通过 spi_controller.ops 结构体注册:
/* 控制器驱动核心回调结构 */
struct spi_controller_mem_ops {
int (*exec_op)(struct spi_mem *mem, const struct spi_mem_op *op);
};
/* 传统SPI控制器回调(更常见) */
static const struct spi_controller_ops imx_spi_ops = {
.transfer_one = imx_spi_transfer_one, /* 单次传输 */
.set_cs = imx_spi_set_cs, /* 片选控制 */
.handle_irq = imx_spi_irq_handler, /* 中断处理 */
.prepare_transfer_hardware = imx_spi_prepare_hw,
.unprepare_transfer_hardware = imx_spi_unprepare_hw,
};
控制器驱动层的关键回调功能:
- transfer_one():执行单次SPI传输(发送+接收),通常通过FIFO或DMA完成。
- set_cs():控制片选信号(GPIO或硬件片选),车载多从机场景需注意片选延迟。
- handle_irq():处理传输完成中断、错误中断,唤醒等待队列。
2.4 数据流完整路径:从spi_sync到硬件时序
以下是一个典型的同步SPI传输数据流,展示三层之间的协作:
- 设备驱动层:调用
spi_sync(spi, &msg),将spi_message提交到核心层。 - 核心层:将消息加入
spi_controller的传输队列,调用__spi_pump_messages()进行调度。 - 核心层:调用
spi_controller.prepare_message()进行传输前准备。 - 核心层:调用
spi_controller.transfer_one_message(),该函数遍历消息中的所有spi_transfer。 - 控制器驱动层:对每个transfer调用
.transfer_one(),配置SPI控制器的数据寄存器、时钟分频、片选极性。 - 硬件层:SPI控制器通过MOSI/MISO线完成数据移位,产生SCK时钟。
- 回调返回:传输完成后,控制器驱动触发中断或DMA完成回调,核心层调用
.unprepare_message(),最终唤醒等待的spi_sync调用者。
2.5 车载工程中的回调定制要点
车载安全关键场景注意: 在ADAS或线控底盘系统中,SPI通信失败可能导致功能安全等级(ASIL)违规。定制回调时需注意:
- 在
.transfer_one()中增加超时看门狗,防止SPI总线锁死。 - 在
.handle_irq()中区分传输完成中断与错误中断(如帧错误、溢出),并上报错误状态。 - 在
.set_cs()中确保片选信号满足从机时序要求(如CS建立时间 > 50ns)。
通过理解SPI子系统的三层架构与回调机制,车载Linux工程师可以精准定位驱动问题,并针对特定硬件优化传输性能。下一节将深入讲解SPI控制器驱动中DMA与中断模式的配置参数。