4. Linux SPI子系统架构:SPI核心层、SPI控制器驱动层、SPI设备驱动层
Linux SPI子系统采用经典的分层架构设计,将SPI总线操作抽象为三个层次:SPI核心层、SPI控制器驱动层和SPI设备驱动层。这种分层结构实现了硬件无关性与驱动复用,是嵌入式开发人员必须掌握的核心知识。
架构设计目标: SPI子系统通过分层解耦,使得控制器驱动开发者只需关注寄存器操作,设备驱动开发者只需关注协议逻辑,核心层则提供统一的框架与接口。
4.1 三层架构总览
下图展示了SPI子系统的完整分层结构及各层之间的调用关系:
4.2 SPI核心层 (SPI Core)
SPI核心层是子系统的基础设施,位于drivers/spi/spi.c中,负责以下关键功能:
- 总线管理: 注册和管理SPI总线类型 (
spi_bus_type),维护设备与驱动的匹配。 - 设备与驱动注册: 提供
spi_register_master()和spi_register_driver()接口。 - 消息队列调度: 管理
spi_message的排队、拆分与传输调度。 - DMA与PIO选择: 根据传输大小自动选择DMA或PIO模式。
- 设备树解析: 解析设备树中SPI控制器的子节点,自动创建
spi_device。
核心数据结构:
struct spi_master 代表一个SPI控制器,struct spi_device 代表一个SPI从设备,struct spi_driver 代表设备驱动。三者通过核心层串联。
4.3 SPI控制器驱动层
控制器驱动层直接操作硬件寄存器,实现SPI总线的时序控制。每个SoC的SPI控制器都需要独立的驱动实现。
| 控制器驱动职责 | 说明 |
|---|---|
| 初始化与去初始化 | 时钟使能、GPIO配置、中断注册、DMA通道申请 |
| 传输回调实现 | 实现 master->transfer_one_message 或 transfer_one |
| 片选控制 | 硬件CS或GPIO模拟CS的拉高/拉低 |
| 时钟极性/相位配置 | 根据 spi_device->mode 设置CPOL/CPHA |
| FIFO与DMA管理 | 利用硬件FIFO或DMA提高传输效率 |
控制器驱动的核心是填充struct spi_master的回调函数:
static int my_spi_transfer_one(struct spi_master *master,
struct spi_device *spi,
struct spi_transfer *transfer)
{
// 1. 配置时钟极性/相位
// 2. 设置数据长度和比特顺序
// 3. 写入TX FIFO或启动DMA
// 4. 等待传输完成(中断或轮询)
// 5. 读取RX FIFO数据
return 0;
}
static int my_spi_probe(struct platform_device *pdev)
{
struct spi_master *master;
// 分配并初始化spi_master
master = devm_spi_alloc_master(&pdev->dev, 0);
master->transfer_one = my_spi_transfer_one;
master->num_chipselect = 2; // 支持2个片选
master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH;
// 注册到核心层
return devm_spi_register_master(&pdev->dev, master);
}
4.4 SPI设备驱动层
设备驱动层面向具体的SPI从设备(如ADC、传感器、显示屏),开发者只需实现协议逻辑,无需关心底层时序细节。
设备驱动开发者的视角: 你只需要填充
struct spi_driver 结构体,在 probe 函数中获取 spi_device 指针,然后通过 spi_write()、spi_read()、spi_sync_transfer() 等API完成数据收发。
设备驱动的典型实现模板:
static int my_spi_device_probe(struct spi_device *spi)
{
struct device_data *data;
// 设置SPI模式与速率
spi->mode = SPI_MODE_0;
spi->max_speed_hz = 10000000; // 10MHz
spi_setup(spi);
// 分配私有数据
data = devm_kzalloc(&spi->dev, sizeof(*data), GFP_KERNEL);
spi_set_drvdata(spi, data);
// 执行一次读操作验证通信
u8 tx_buf[] = {0x01, 0x02};
u8 rx_buf[2];
struct spi_transfer t = {
.tx_buf = tx_buf,
.rx_buf = rx_buf,
.len = 2,
};
spi_sync_transfer(spi, &t, 1);
return 0;
}
static struct spi_driver my_spi_driver = {
.driver = {
.name = "my_spi_device",
.of_match_table = my_spi_of_match,
},
.probe = my_spi_device_probe,
.remove = my_spi_device_remove,
};
module_spi_driver(my_spi_driver);
4.5 三层协作流程
当应用层发起一次SPI读写时,三层之间的完整调用链如下:
4.6 各层关键数据结构对比
| 层次 | 核心结构体 | 关键字段/回调 | 所在文件 |
|---|---|---|---|
| 核心层 | struct spi_master |
bus_num, num_chipselect, mode_bits, transfer_one_message | include/linux/spi/spi.h |
| 核心层 | struct spi_device |
master, chip_select, mode, max_speed_hz, controller_state | include/linux/spi/spi.h |
| 核心层 | struct spi_driver |
probe, remove, id_table, driver.pm | include/linux/spi/spi.h |
| 控制器驱动 | struct spi_controller |
transfer_one, prepare_message, unprepare_message, set_cs | drivers/spi/spi.c |
| 设备驱动 | struct spi_driver |
probe中调用spi_setup(), spi_sync_transfer() | 设备驱动自身文件 |
常见误区: 初学者容易混淆
spi_master 和 spi_controller。在较新内核(5.10+)中,spi_master 被重命名为 spi_controller,但为了兼容性,spi_master 宏仍然保留。实际开发中建议使用 spi_controller。
4.7 设备树与三层的关联
设备树是连接三层的关键桥梁。控制器驱动通过设备树节点获取硬件资源,核心层解析子节点自动创建设备,设备驱动通过of_match_table匹配节点:
// 设备树片段
&spi1 {
status = "okay";
pinctrl-0 = &spi1_pins;
cs-gpios = <&gpio0 10 GPIO_ACTIVE_LOW>;
adc@0 { // 核心层解析此节点
compatible = "ti,ads1256";
reg = <0>; // 片选索引
spi-max-frequency = <2000000>;
spi-cpol; // 时钟极性
spi-cpha; // 时钟相位
};
};
// 控制器驱动层:通过 platform_driver 匹配 spi1 节点
// 核心层:自动为 adc@0 创建 spi_device
// 设备驱动层:通过 of_match_table 匹配 "ti,ads1256"
理解三层架构是掌握SPI驱动开发的基础。控制器驱动层处理硬件差异,核心层提供统一框架,设备驱动层实现具体功能。在实际项目中,大多数开发者只需要编写设备驱动层代码,但理解底层机制有助于调试和性能优化。