Lazy loaded image
🥳嵌入式Linux开发
🔌Linux 驱动专题 - SPI 驱动
Words 2826Read Time≈ 8 min
2026-9-12
2026-9-14
Linux 内核提供了一套完整的 SPI 子系统,用统一接口连接 SPI 设备驱动与不同 SoC 的 SPI 控制器。与 I2C 不同,SPI 没有总线内地址和 ACK 机制,设备通常由片选信号区分,时钟极性、相位、位宽和最高频率都需要在设备侧明确配置。
Linux SPI 子系统主要分为应用层、协议/设备驱动层、SPI 核心层和控制器驱动层。核心层负责设备与驱动匹配、消息排队和控制器调度;控制器驱动负责把抽象的 spi_message、spi_transfer 转换为寄存器、FIFO、中断或 DMA 操作。
  1. 应用层:
      • 用户空间可通过 /dev/spidevB.C 和 SPI_IOC_MESSAGE 发起全双工传输
      • 内核子系统也可通过 IIO、MTD、网络等标准接口间接访问 SPI 设备
  1. 协议与设备驱动层:
      • 面向具体器件,如 ADC、触摸屏、传感器、SPI NOR Flash
      • 配置 mode、max_speed_hz、bits_per_word,并组织命令、地址和数据阶段
      • spidev.c 本身也是一个通用 SPI 协议驱动,与具体器件驱动并列;它不会再调用另一个设备驱动,而是直接通过 SPI Core 提交传输
      • 正式产品中的复杂外设通常应接入 IIO、MTD、DRM、input 等对应内核子系统;spidev 更适合调试、验证和简单用户态访问
  1. SPI 核心层:
      • 管理 spi_driver、spi_device 与 spi_controller
      • 负责设备匹配、消息入队、同步/异步调用和统计
      • 对上提供 spi_sync、spi_async、spi_write 等统一 API
 
  1. 控制器驱动层:
      • 实现特定 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 源码为准。
以下是几个关键接口:
  1. spi_sync 函数:提交同步消息,调用者睡眠等待传输完成。
    1. spi_async 函数:提交异步消息,完成后调用 message 的 complete 回调。
      1. spi_setup 函数:在传输前检查并下发 mode、位宽、频率和片选等设备配置。
        1. transfer_one / transfer_one_message:控制器驱动真正接管传输的位置。前者由核心层遍历 transfer,后者允许控制器驱动自行处理完整 message。
         

        SPI 故障排查路径

        SPI 最终必须落实为 CS、SCLK、MOSI、MISO 上的电气信号。排查时应沿着“控制器 → 设备 → 参数 → 传输 → 波形”的顺序收缩范围,而不是一开始就陷入 spi.c。
        1. 确认控制器已启动
            • 控制器节点是否设置 status = "okay"
            • 控制器驱动是否成功 probe
            • clock、reset、寄存器、IRQ、DMA 等资源是否获取成功
        1. 确认外设已创建并匹配
            • compatible 是否与 of_match_table 一致
            • reg 是否对应实际片选
            • 设备驱动的 probe() 是否执行
            • 使用 spidev 时,是否生成正确的 /dev/spidevB.C
        1. 确认引脚与片选
            • pinctrl 是否把引脚复用为 SPI 功能
            • CS 使用控制器硬件片选还是 GPIO 片选
            • CS 有效电平是否符合器件手册
        1. 确认传输参数
            • CPOL、CPHA 是否正确
            • 实际时钟是否低于器件和板级链路上限
            • bits_per_word、MSB/LSB 顺序是否一致
            • 命令、地址、dummy cycle 和数据阶段是否符合器件协议
        1. 确认请求进入控制器驱动
            • spi_sync() / spi_async() 返回值是否正常
            • transfer_one() 或 transfer_one_message() 是否被调用
            • FIFO、中断、DMA 是否启动并正常完成
        1. 使用示波器或逻辑分析仪验证波形
            • 无 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 子系统体现了以下设计原则:
        1. 控制器与协议分离:controller 只负责产生可靠时序,设备驱动只负责器件命令和数据语义。
        1. message 保证事务边界:多个 transfer 可以在一次片选会话中完成“命令 → 地址 → dummy → 数据”,避免其他设备请求插入。
        1. 能力协商:设备提出 mode、频率、位宽需求,控制器通过 mode_bits、bits_per_word_mask 和速度范围判断能否支持。
        1. 同步与异步统一:上层可按延迟、并发和上下文选择 spi_sync 或 spi_async,底层共享同一消息调度机制。
        1. PIO 与 DMA 解耦:短包可走 PIO,长包可走 DMA;选择策略由控制器能力、长度、对齐和缓存条件决定。
        1. 固件描述优先:片选、最高频率、CPOL/CPHA 和器件 compatible 应由设备树或 ACPI 准确描述,驱动不应靠猜测补齐硬件事实。
         
        上一篇
        Linux 驱动专题 - IIC 驱动
        下一篇
        Linux 驱动专题 - USB 驱动

        Comments
        Loading...