4、SPI驱动框架:Linux SPI子系统架构
Linux SPI 子系统采用主机-设备-驱动三层分离模型,将 SPI 控制器的硬件差异、设备连接拓扑与协议逻辑解耦。理解这一架构是编写可移植、高性能 SPI 驱动的基石,也是后续将 DMA 与内存映射技术融入 SPI 传输的前提。
4.1 三层核心对象:spi_master / spi_device / spi_driver
SPI 子系统的核心数据结构定义在 include/linux/spi/spi.h 中,三者通过内核设备模型(driver model)串联:
| 对象 | 数据结构 | 职责 | 生命周期 |
|---|---|---|---|
| spi_master | struct spi_master |
代表 SPI 控制器硬件,管理 FIFO、时钟、片选、DMA 通道 | 由平台/ACPI/设备树触发,probe 时分配,remove 时释放 |
| spi_device | struct spi_device |
代表挂接在 SPI 总线上的从设备,包含模式、频率、字长等参数 | 由设备树节点或 board info 注册时创建,与 spi_master 绑定 |
| spi_driver | struct spi_driver |
实现从设备的具体协议逻辑(如 ADC 采集、传感器读取) | 模块加载时注册,卸载时注销 |
架构要点:spi_master 负责物理传输,spi_device 描述拓扑与配置,spi_driver 处理协议语义。三者通过总线类型
spi_bus_type 完成匹配与绑定。
4.2 设备树绑定:从硬件拓扑到内核对象
在 ARM/ARM64/RISC-V 等嵌入式平台,设备树(Device Tree)是描述 SPI 拓扑的标准方式。内核通过 of_spi_register_master() 解析设备树子节点,自动创建 spi_device 实例。
4.2.1 典型设备树节点结构
/* SPI 控制器节点 */
spi@ff110000 {
compatible = "vendor,spi-controller";
reg = <0x0 0xff110000 0x0 0x1000>;
#address-cells = <1>;
#size-cells = <0>;
interrupts = <0 56 4>;
clocks = <&clk SPI_CLK>;
/* 从设备子节点 */
adc@0 {
compatible = "vendor,adc-device";
reg = <0>; /* 片选编号 0 */
spi-max-frequency = <10000000>; /* 10 MHz */
spi-cpol; /* 时钟极性 = 1 */
spi-cpha; /* 时钟相位 = 1 */
spi-cs-high; /* 片选高有效 */
};
sensor@1 {
compatible = "vendor,temperature-sensor";
reg = <1>; /* 片选编号 1 */
spi-max-frequency = <5000000>;
};
};
4.2.2 设备树属性与 spi_device 字段映射
| 设备树属性 | spi_device 字段 | 说明 |
|---|---|---|
reg |
chip_select |
片选编号,对应 spi_master 的 cs_gpio 数组索引 |
spi-max-frequency |
max_speed_hz |
从设备支持的最大 SPI 时钟频率 |
spi-cpol |
mode 置位 SPI_CPOL |
时钟极性:空闲时时钟电平 |
spi-cpha |
mode 置位 SPI_CPHA |
时钟相位:数据采样沿 |
spi-cs-high |
mode 置位 SPI_CS_HIGH |
片选信号高电平有效 |
spi-3wire |
mode 置位 SPI_3WIRE |
三线制模式(MISO/MOSI 合并) |
实战建议:设备树中
reg 的值必须与 SPI 控制器硬件片选线一一对应。若使用 GPIO 模拟片选,需在控制器节点中定义 cs-gpios 属性。
4.3 probe/remove 回调:驱动与设备的握手时刻
当 spi_driver 与 spi_device 通过 compatible 匹配成功后,内核调用 probe 回调;设备移除或驱动卸载时调用 remove。这两个回调是驱动开发者最常接触的入口。
4.3.1 probe 回调的标准流程
- 获取设备私有数据:通过
devm_kzalloc()分配驱动私有结构体。 - 读取设备树配置:使用
device_property_read_*系列 API 获取频率、模式、GPIO 等参数。 - 初始化硬件:配置 SPI 控制器时钟、DMA 通道、中断。
- 注册内核框架:如 IIO、Input、HWMON 等子系统。
- 设置 SPI 传输参数:调用
spi_setup()使配置生效。
static int adc_probe(struct spi_device *spi)
{
struct adc_priv *priv;
int ret;
/* 1. 分配私有数据 */
priv = devm_kzalloc(&spi->dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
spi_set_drvdata(spi, priv);
/* 2. 读取设备树属性 */
device_property_read_u32(&spi->dev, "vref-mv", &priv->vref_mv);
/* 3. 配置 SPI 参数 */
spi->mode = SPI_MODE_0;
spi->max_speed_hz = 10 * 1000 * 1000; /* 10 MHz */
ret = spi_setup(spi);
if (ret)
return dev_err_probe(&spi->dev, ret, "spi_setup failed\n");
/* 4. 注册 IIO 设备 */
priv->indio_dev = devm_iio_device_alloc(&spi->dev, 0);
if (!priv->indio_dev)
return -ENOMEM;
/* ... 配置 iio_info、channels ... */
return devm_iio_device_register(&spi->dev, priv->indio_dev);
}
static void adc_remove(struct spi_device *spi)
{
struct adc_priv *priv = spi_get_drvdata(spi);
/* 清理硬件状态:停止 DMA、关闭时钟、复位 FIFO */
adc_hardware_reset(priv);
dev_info(&spi->dev, "ADC device removed\n");
}
4.3.2 probe 失败常见原因
| 错误场景 | 典型错误码 | 排查方向 |
|---|---|---|
| 设备树 compatible 不匹配 | -ENODEV |
检查 dts 与驱动中 of_match_table 是否一致 |
| spi_setup() 参数无效 | -EINVAL |
确认 mode 位合法、频率不超过控制器上限 |
| GPIO 片选申请失败 | -EBUSY |
检查 cs-gpios 引脚是否被其他驱动占用 |
| DMA 通道获取失败 | -EPROBE_DEFER |
DMA 控制器驱动未就绪,内核会自动重试 probe |
注意:probe 中若使用
devm_* 系列资源管理 API,通常不需要在 remove 中手动释放内存,但硬件状态(如 DMA 描述符、中断屏蔽)必须显式清理。
4.4 SPI 子系统架构总览
下图展示了从设备树解析到驱动 probe 完成的完整数据流与对象关系:
4.5 关键数据结构关系
以下代码片段展示了 spi_master 与 spi_device 之间的核心关联:
/* spi_master 关键字段(精简) */
struct spi_master {
struct device dev; /* 内核设备结构 */
s16 bus_num; /* 总线编号 */
u16 num_chipselect; /* 片选数量 */
u16 mode_bits; /* 支持的 SPI 模式位掩码 */
u32 min_speed_hz;
u32 max_speed_hz;
int (*transfer_one)(struct spi_master *master,
struct spi_device *spi,
struct spi_transfer *transfer);
void (*set_cs)(struct spi_device *spi, bool enable);
/* DMA 相关 */
struct dma_chan *dma_tx;
struct dma_chan *dma_rx;
};
/* spi_device 关键字段 */
struct spi_device {
struct device dev; /* 内核设备结构 */
struct spi_master *master; /* 所属控制器 */
u32 max_speed_hz; /* 最大时钟频率 */
u8 chip_select; /* 片选编号 */
u8 mode; /* SPI_MODE_0 ~ SPI_MODE_3 */
u8 bits_per_word; /* 每字位数,通常 8 */
int irq; /* 从设备中断号(可选) */
void *controller_data; /* 控制器私有数据 */
};
性能关注点:
transfer_one 回调是 SPI 传输性能的核心路径。在后续章节中,我们将通过 DMA 传输和内存映射技术重写该回调,消除 CPU 逐字节搬运的瓶颈。
4.6 本章小结
- 三层架构:spi_master 管理硬件,spi_device 描述拓扑,spi_driver 实现协议,三者通过内核设备模型协作。
- 设备树绑定:通过 compatible 匹配、reg 指定片选、spi-max-frequency 等属性完成硬件描述,内核自动创建 spi_device。
- probe/remove 流程:probe 中完成资源分配、参数配置、子系统注册;remove 中清理硬件状态。
- 架构图:从设备树到驱动实例的完整数据流,为后续 DMA 和内存映射优化提供上下文。