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 总线类型上。其核心步骤包括:
- 初始化驱动结构体:设置
driver->bus = &spi_bus_type。 - 注册到驱动核心:调用
driver_register,将驱动加入全局驱动链表。 - 触发总线匹配:遍历已注册的 SPI 设备,调用
spi_match_device进行匹配。 - 执行 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 实例。其工作流程如下:
- 获取控制器节点:从
struct spi_master的dev.of_node获取设备树节点。 - 遍历子节点:对每个子节点调用
of_spi_parse_dt解析属性。 - 解析关键属性:读取
reg(片选号)、spi-max-frequency、spi-cpol、spi-cpha等。 - 创建 spi_device:调用
spi_alloc_device+spi_add_device完成注册。 - 设置 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 设备注册的完整流程,以及驱动匹配的触发时机:
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 设备驱动注册的完整生命周期可概括为以下步骤:
- 控制器初始化:
spi_register_master注册 SPI 控制器,内部调用of_spi_register_devices解析设备树。 - 设备枚举:设备树子节点被解析为
spi_device,通过spi_add_device加入总线。 - 驱动注册:
spi_register_driver将驱动挂接到 SPI 总线。 - 总线匹配:
spi_bus_type.match遍历设备链表,寻找匹配的驱动。 - probe 执行:匹配成功后,驱动 probe 函数获得
spi_device指针,开始硬件初始化。
调试技巧: 使用
ls /sys/bus/spi/devices/ 查看已注册的 SPI 设备;cat /sys/bus/spi/drivers/<driver>/bind 可手动绑定驱动。