Linux 内核提供了一套完整的 SPI 子系统,用统一接口连接 SPI 设备驱动与不同 SoC 的 SPI 控制器。与 I2C 不同,SPI 没有总线内地址和 ACK 机制,设备通常由片选信号区分,时钟极性、相位、位宽和最高频率都需要在设备侧明确配置。
Linux SPI 子系统主要分为应用层、协议/设备驱动层、SPI 核心层和控制器驱动层。核心层负责设备与驱动匹配、消息排队和控制器调度;控制器驱动负责把抽象的
spi_message、spi_transfer 转换为寄存器、FIFO、中断或 DMA 操作。- 应用层:
- 用户空间可通过
/dev/spidevB.C和SPI_IOC_MESSAGE发起全双工传输 - 内核子系统也可通过 IIO、MTD、网络等标准接口间接访问 SPI 设备
- 协议与设备驱动层:
- 面向具体器件,如 ADC、触摸屏、传感器、SPI NOR Flash
- 配置
mode、max_speed_hz、bits_per_word,并组织命令、地址和数据阶段 spidev.c本身也是一个通用 SPI 协议驱动,与具体器件驱动并列;它不会再调用另一个设备驱动,而是直接通过 SPI Core 提交传输- 正式产品中的复杂外设通常应接入 IIO、MTD、DRM、input 等对应内核子系统;
spidev更适合调试、验证和简单用户态访问
- SPI 核心层:
- 管理
spi_driver、spi_device与spi_controller - 负责设备匹配、消息入队、同步/异步调用和统计
- 对上提供
spi_sync、spi_async、spi_write等统一 API
- 控制器驱动层:
- 实现特定 SoC SPI 控制器的 probe、时钟、片选、FIFO、中断与 DMA
- 通过
transfer_one_message或transfer_one承接核心层传输请求 - GPIO 模拟控制器由
spi-gpio.c与 bitbang 框架实现
下面对 SPI 设备驱动、总线匹配机制、控制器驱动、控制器框架与 GPIO 模拟 SPI 分别进行源码分析。
SPI 驱动专题01 - 设备驱动 spidev.c 源码分析SPI 驱动专题02 - SPI 总线驱动匹配机制分析SPI 驱动专题03 - 控制器驱动 spi-imx.c 源码分析SPI 驱动专题04 - SPI Controller 驱动框架与实例SPI 驱动专题05 - GPIO 模拟 SPI 源码分析和实例核心数据结构
spi_controller:控制器抽象,对应一个硬件 SPI 控制器或 GPIO 模拟控制器。
spi_device:挂在 controller 上的一个从设备,由片选号、mode、位宽和最高频率描述。
spi_driver:具体器件的协议驱动,负责 probe/remove 和器件语义。
spi_message:一次完整 SPI 事务,可包含多个连续 transfer;同一 message 内不会被其他设备的 message 插入。
spi_transfer:一次具体收发片段,可单独设置长度、频率、位宽、延时和片选变化。
设备注册流程
核心调用关系:
设备树字段如何进入 spi_device
下面以一个最小 SPI 从设备节点为例:
字段与内核对象的对应关系:
设备树字段 | 内核含义 | 典型问题 |
status = "okay" | 启用 SPI 控制器 | 未启用时控制器不会 probe |
pinctrl-0 | 配置 SCLK、MOSI、MISO、CS 引脚复用 | 复用错误时通常没有时钟或片选波形 |
compatible | 与驱动的 of_match_table 匹配 | 不匹配时不会进入设备驱动 probe() |
reg = <0> | 设置 spi_device.chip_select = 0 | 片选号错误会操作错误的 CS |
spi-max-frequency | 设置 spi_device.max_speed_hz | 频率超过器件能力时可能读写异常 |
spi-cpol / spi-cpha | 设置 spi_device.mode | 模式错误会导致采样边沿错位 |
控制器驱动注册完成后,SPI Core 枚举控制器节点下的子节点,为每个合法从设备创建
spi_device。随后设备模型使用 compatible、ID 表或 ACPI 信息匹配对应的 spi_driver。数据传输流程
SPI 核心实现主要位于
drivers/spi/spi.c。以下代码经过流程化简,用于说明锁、入队和等待关系,不对应某个特定内核版本的完整实现;分析项目问题时应以目标内核或厂商 BSP 源码为准。以下是几个关键接口:
- spi_sync 函数:提交同步消息,调用者睡眠等待传输完成。
- spi_async 函数:提交异步消息,完成后调用 message 的 complete 回调。
- spi_setup 函数:在传输前检查并下发 mode、位宽、频率和片选等设备配置。
- transfer_one / transfer_one_message:控制器驱动真正接管传输的位置。前者由核心层遍历 transfer,后者允许控制器驱动自行处理完整 message。
SPI 故障排查路径
SPI 最终必须落实为 CS、SCLK、MOSI、MISO 上的电气信号。排查时应沿着“控制器 → 设备 → 参数 → 传输 → 波形”的顺序收缩范围,而不是一开始就陷入
spi.c。- 确认控制器已启动
- 控制器节点是否设置
status = "okay" - 控制器驱动是否成功 probe
- clock、reset、寄存器、IRQ、DMA 等资源是否获取成功
- 确认外设已创建并匹配
compatible是否与of_match_table一致reg是否对应实际片选- 设备驱动的
probe()是否执行 - 使用 spidev 时,是否生成正确的
/dev/spidevB.C
- 确认引脚与片选
- pinctrl 是否把引脚复用为 SPI 功能
- CS 使用控制器硬件片选还是 GPIO 片选
- CS 有效电平是否符合器件手册
- 确认传输参数
- CPOL、CPHA 是否正确
- 实际时钟是否低于器件和板级链路上限
bits_per_word、MSB/LSB 顺序是否一致- 命令、地址、dummy cycle 和数据阶段是否符合器件协议
- 确认请求进入控制器驱动
spi_sync()/spi_async()返回值是否正常transfer_one()或transfer_one_message()是否被调用- FIFO、中断、DMA 是否启动并正常完成
- 使用示波器或逻辑分析仪验证波形
- 无 CS、无 SCLK:优先检查控制器、pinctrl、设备树和片选配置
- 有 MOSI、无 MISO:检查从设备供电、复位、接线、协议命令和采样相位
- 波形完整但数据错误:检查 mode、位序、dummy cycle、频率和 message 事务边界
定位原则
先判断错误发生在哪一层,再阅读对应源码。没有 CS 和 SCLK 时,继续研究外设寄存器协议通常没有意义。
RK 平台源码索引
目标 | 常见源码路径 | 作用 |
SPI 通用核心 | drivers/spi/spi.c | controller、device、driver、message 的注册、匹配与调度 |
RK Master 控制器 | drivers/spi/spi-rockchip.c | Rockchip SPI 控制器寄存器、FIFO、中断和 DMA 实现 |
RK Slave 控制器 | drivers/spi/spi-rockchip-slave.c | 部分 RK BSP 中的 SPI 从机模式实现 |
RK 测试驱动 | drivers/spi/spi-rockchip-test.c | 部分厂商 BSP 提供的控制器测试代码,通常需要手动加入编译 |
用户态接口 | drivers/spi/spidev.c | 创建 /dev/spidevB.C 并处理 ioctl |
用户态测试程序 | tools/spi/spidev_test.c | 发起测试传输并验证 mode、频率、位宽和收发数据 |
i.MX 控制器示例 | drivers/spi/spi-imx.c | 展示另一类 SoC 控制器驱动的实现方式 |
具体文件名、目录和接口会随主线内核版本及 Rockchip 厂商 BSP 改变。例如旧版本可能把
spidev_test.c 放在 Documentation/spi/。定位时应先确认项目实际使用的内核版本和源码树,不要把某个 BSP 的路径当成通用标准。SPI 与 I2C 框架对照
SPI | I2C | 主要差异 |
spi_controller | i2c_adapter | 都表示总线控制器抽象 |
spi_device | i2c_client | SPI 通常由 bus + chip select 定位;I2C 由总线 + 从地址定位 |
spi_driver | i2c_driver | 都通过设备模型完成驱动匹配和 probe |
SPI Core | I2C Core | 都负责设备管理、匹配和统一传输接口 |
spidev | i2c-dev | 都向用户空间提供通用字符设备入口 |
spi-rockchip.c | i2c-rk3x.c | 都负责把通用请求转换为 RK 控制器硬件操作 |
该对照只用于理解 Linux 设备模型。SPI 没有 I2C 的总线内寻址和 ACK 机制,二者的电气时序、仲裁和事务语义不能直接类比。
设计理念
Linux SPI 子系统体现了以下设计原则:
- 控制器与协议分离:controller 只负责产生可靠时序,设备驱动只负责器件命令和数据语义。
- message 保证事务边界:多个 transfer 可以在一次片选会话中完成“命令 → 地址 → dummy → 数据”,避免其他设备请求插入。
- 能力协商:设备提出 mode、频率、位宽需求,控制器通过
mode_bits、bits_per_word_mask和速度范围判断能否支持。
- 同步与异步统一:上层可按延迟、并发和上下文选择
spi_sync或spi_async,底层共享同一消息调度机制。
- PIO 与 DMA 解耦:短包可走 PIO,长包可走 DMA;选择策略由控制器能力、长度、对齐和缓存条件决定。
- 固件描述优先:片选、最高频率、CPOL/CPHA 和器件 compatible 应由设备树或 ACPI 准确描述,驱动不应靠猜测补齐硬件事实。
- Author:felixfixit
- URL:http://www.felixmicrospace.top/article/linux_spi_driver_guide
- Copyright:All articles in this blog, except for special statements, adopt BY-NC-SA agreement. Please indicate the source!






