4. SPI 设备驱动注册流程:spi_register_driver、spi_add_device 与 of_spi_register_devices 的设备树解析与枚举机制

SPI 子系统中,设备与驱动的匹配是总线工作的核心。本章深入剖析 SPI 设备驱动的注册流程,重点讲解 spi_register_driver、spi_add_device 以及设备树解析函数 of_spi_register_devices 的协同工作方式,揭示从设备树节点到 SPI 设备枚举的完整链路。

4.1 核心数据结构与注册入口

SPI 驱动注册的起点是 spi_register_driver,它将一个 struct spi_driver 注册到 SPI 总线。该结构体包含 probe、remove、shutdown 等回调,以及一个 struct device_driver 基类。

函数/宏 作用 调用时机
spi_register_driver() 注册 SPI 驱动到内核总线 模块初始化
module_spi_driver() 简化注册/注销的宏封装 模块加载/卸载
spi_add_device() 手动添加 SPI 设备到总线 板级代码或控制器初始化
of_spi_register_devices() 解析设备树,自动枚举 SPI 子节点 控制器注册时自动调用
关键点: spi_register_driver 只注册驱动侧,设备侧由 spi_add_device 或设备树解析生成。总线核心通过 spi_bus_type 的 match 函数完成配对。

4.2 spi_register_driver 内部流程

该函数本质是对 driver_register 的封装,将驱动挂接到 SPI 总线类型上。其核心步骤包括:

  1. 初始化驱动结构体:设置 driver->bus = &spi_bus_type。
  2. 注册到驱动核心:调用 driver_register,将驱动加入全局驱动链表。
  3. 触发总线匹配:遍历已注册的 SPI 设备,调用 spi_match_device 进行匹配。
  4. 执行 probe:匹配成功后,调用驱动的 probe 回调。
// 典型 SPI 驱动注册示例
static const struct of_device_id my_spi_of_match[] = {
    { .compatible = "vendor,my-spi-device" },
    { /* sentinel */ }
};

static struct spi_driver my_spi_driver = {
    .driver = {
        .name = "my_spi_driver",
        .of_match_table = my_spi_of_match,
    },
    .probe = my_spi_probe,
    .remove = my_spi_remove,
};

module_spi_driver(my_spi_driver);

4.3 spi_add_device 手动添加设备

在非设备树平台或需要动态添加设备时,使用 spi_add_device。它接收一个 struct spi_device 指针,完成以下操作:

  • 分配并初始化:设置控制器、片选、模式、最大速度等参数。
  • 设置设备名称:根据 modalias 或 driver_override 确定。
  • 注册到设备模型:调用 device_add,触发总线匹配。
  • 异步 probe:设备加入后,驱动核心异步调用 probe。
使用场景: 板级代码(arch/arm/mach-xxx)中通过 spi_new_device 创建并添加设备;热插拔 SPI 设备时也可手动调用。

4.4 设备树解析:of_spi_register_devices 枚举机制

设备树是当前主流平台描述方式。of_spi_register_devices 在 SPI 控制器注册时被调用,自动解析控制器节点下的所有 SPI 子节点,生成 spi_device 实例。其工作流程如下:

  1. 获取控制器节点:从 struct spi_master 的 dev.of_node 获取设备树节点。
  2. 遍历子节点:对每个子节点调用 of_spi_parse_dt 解析属性。
  3. 解析关键属性:读取 reg(片选号)、spi-max-frequency、spi-cpol、spi-cpha 等。
  4. 创建 spi_device:调用 spi_alloc_device + spi_add_device 完成注册。
  5. 设置 of_node:将设备树节点关联到 spi_device,供驱动通过 of_match_device 匹配。
// 设备树节点示例
&spi0 {
    status = "okay";
    cs-gpios = <&gpio1 10 GPIO_ACTIVE_LOW>;

    temperature_sensor: sensor@0 {
        compatible = "vendor,temp-sensor";
        reg = <0>;               // 片选 0
        spi-max-frequency = <1000000>;
        spi-cpol;                 // 时钟极性高
        spi-cpha;                 // 时钟相位第二边沿
    };
};
注意事项: 设备树中 reg 属性必须与控制器 cs-gpios 或硬件片选线对应;spi-max-frequency 不能超过控制器支持的最大频率。

4.5 设备树解析与枚举流程图

下图展示了从设备树节点到 SPI 设备注册的完整流程,以及驱动匹配的触发时机:

设备树 SPI 设备枚举与驱动匹配流程 设备树 SPI 设备枚举与驱动匹配流程 设备树 (DTS) of_spi_register_devices 解析 reg/max-frequency/cpol/cpha spi_alloc_device spi_add_device spi_register_driver driver_register spi_bus_type.match driver->probe 执行 设备加入触发匹配 说明: 左侧:设备树解析生成设备 右侧:驱动注册等待匹配 中间箭头:设备加入触发 probe

4.6 匹配机制与 probe 触发

SPI 总线的匹配函数 spi_match_device 按以下优先级进行匹配:

匹配方式 匹配依据 优先级
driver_override 设备节点显式指定驱动名称 最高
of_match_table 设备树 compatible 属性匹配 高
id_table SPI 设备 ID 表匹配 中
driver.name 驱动名称与设备 modalias 匹配 低
性能提示: 设备树平台优先使用 of_match_table,匹配效率高且无需维护额外的 ID 表。匹配成功后,总线核心调用 driver->probe,驱动在此函数中完成硬件初始化、注册字符设备或 IIO 设备等操作。

4.7 完整注册流程总结

SPI 设备驱动注册的完整生命周期可概括为以下步骤:

  1. 控制器初始化:spi_register_master 注册 SPI 控制器,内部调用 of_spi_register_devices 解析设备树。
  2. 设备枚举:设备树子节点被解析为 spi_device,通过 spi_add_device 加入总线。
  3. 驱动注册:spi_register_driver 将驱动挂接到 SPI 总线。
  4. 总线匹配:spi_bus_type.match 遍历设备链表,寻找匹配的驱动。
  5. probe 执行:匹配成功后,驱动 probe 函数获得 spi_device 指针,开始硬件初始化。
调试技巧: 使用 ls /sys/bus/spi/devices/ 查看已注册的 SPI 设备;cat /sys/bus/spi/drivers/<driver>/bind 可手动绑定驱动。