✅ Day 8(下篇):总线驱动模型实战全解析 —— 以 PCA9450 PMIC 为例
在上一篇博文中,我们深入梳理了 Linux 驱动框架中两大核心模型 —— 平台驱动模型与总线驱动模型 —— 的结构差异与匹配逻辑。在本篇下篇中,我们将从一个完整的 I2C 总线驱动模型实战出发,以 NXP i.MX8M Plus EVK 板载的 PCA9450 PMIC 电源芯片为例,详细讲解设备树设计、驱动匹配过程、驱动模型与资源获取机制,帮助你真正掌握“总线驱动模型”的应用与核心技术点。
🔁 一、上篇回顾:平台 vs 总线驱动模型
在上篇中,我们明确区分了两个核心驱动模型:
分类 | 模型名称 | 典型接口 | 设备绑定方式 | 匹配依据 |
---|---|---|---|---|
平台驱动模型 | platform_driver + platform_device |
platform_get_resource() |
设备树 + platform_bus | .compatible + platform_match |
总线驱动模型 | 如 i2c_driver 、spi_driver 、pci_driver 等 |
i2c_add_driver() 、i2c_probe() |
总线设备自动挂载 | 总线类型 + .id_table or .of_match_table |
它们都属于统一的 Linux 设备模型体系,但在驱动注册、设备创建、资源获取方式等方面有着本质差异。
在这篇文章中,我们将把理论落地,从实际硬件和驱动代码出发,对总线驱动模型做一次全面的实战讲解。
🧩 二、硬件结构解析:PCA9450 在 i.MX8MP 上的供电角色
✅ 2.1 电源架构图理解
我们首先看下官方 EVK 的 PWR TREE 图:
这张图显示了 PCA9450 PMIC 所控制的多个电压通道(BUCKx、LDOx),它们为 i.MX8MP 的核心电源、IO 电源、LPDDR4 等子系统提供稳压输出。
从图中可见:
- PCA9450 是通过 I2C 接口与 SoC 相连。
- PMIC 的多个输出通道,如
BUCK1/2/4/5/6
、LDO1/2/3/4/5
,分别负责供电给 CPU、LPDDR4、eMMC、USB、WiFi 等模块。 VDD_ARM
、VDD_DRAM
、NVCC_DRAM
等电源信号直接由 PCA9450 管脚控制。
📁 三、设备树结构讲解(以 i2c1@30a20000 节点为起点)
我们先来看设备树中关于 I2C1 控制器与 PCA9450 的定义:
&i2c1 {
clock-frequency = <400000>;
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_i2c1>;
status = "okay";
pmic@25 {
compatible = "nxp,pca9450c";
reg = <0x25>;
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_pmic>;
interrupt-parent = <&gpio1>;
interrupts = <3 IRQ_TYPE_LEVEL_LOW>;
regulators {
buck1: BUCK1 {
regulator-name = "BUCK1";
regulator-min-microvolt = <600000>;
regulator-max-microvolt = <2187500>;
regulator-boot-on;
regulator-always-on;
};
...
};
};
};
✅ 核心字段解释:
字段 | 含义 |
---|---|
&i2c1 |
表示引用 i2c1 控制器 |
pmic@25 |
PMIC 芯片的 I2C 地址为 0x25 |
compatible |
匹配驱动的关键字段,驱动需声明支持 "nxp,pca9450c" |
regulators |
子节点,定义具体电源通道的电压配置和属性 |
设备树中通过 .compatible
+ reg
进行 设备绑定,再通过 regulators
子节点完成电源控制逻辑的描述。
🧩 四、驱动注册与匹配分析
在 PCA9450 的驱动源码中,我们可以看到典型的 I2C 总线驱动写法:
static const struct of_device_id pca9450_of_match[] = {
{ .compatible = "nxp,pca9450c", .data = (void *)PCA9450_TYPE_PCA9450C },
...
};
MODULE_DEVICE_TABLE(of, pca9450_of_match);
static struct i2c_driver pca9450_i2c_driver = {
.driver = {
.name = "nxp-pca9450",
.of_match_table = pca9450_of_match,
},
.probe = pca9450_i2c_probe,
};
module_i2c_driver(pca9450_i2c_driver);
✅ 匹配流程详解:
- i2c 总线子系统扫描设备树,识别挂载在 i2c1 控制器下的从设备节点。
- 找到
compatible = "nxp,pca9450c"
。 - 内核会遍历
i2c_driver
中的.of_match_table
,发现匹配项,调用.probe()
。
此过程无需手动注册 platform_device
,而是 通过 i2c 子系统自动完成,这就是总线驱动模型的优势:结构清晰,自动匹配。
🔧 五、资源获取方式差异说明
功能 | 平台驱动模型 | 总线驱动模型 |
---|---|---|
获取资源(reg) | platform_get_resource() |
regmap_init_i2c() 或 i2c_smbus_read_*() |
时钟 | devm_clk_get() |
通常不涉及 |
中断 | platform_get_irq() |
通过 client->irq 获得 |
设备数据 | platform_get_drvdata() |
i2c_set_clientdata() |
以 PCA9450 为例,驱动通过 regmap_init_i2c()
来访问内部寄存器:
pca9450->regmap = devm_regmap_init_i2c(i2c, &pca9450_regmap_config);
这本质上就是用 i2c_transfer
或 smbus
API,完成对 PMIC 内部寄存器的读写操作。
🧩 六、regulator 框架接入流程
PCA9450 的每一个电源输出(BUCK/LDO)都以子设备形式注册为一个 regulator 控制器,整个驱动通过如下步骤完成电源输出控制的注册:
- 解析设备树
regulators
节点; - 注册每个 regulator 设备;
- 提供
regulator_ops
函数集支持 enable/disable、set_voltage 等操作; - 提供 ramp_delay、电压范围等配置。
示例代码:
rdev = devm_regulator_register(pca9450->dev, desc, &config);
注册成功后,其他子系统(如 CPU 调频、系统电源管理等)就可以调用 regulator API 控制该 PMIC 电压。
📌 七、I2C 总线驱动模型核心总结
核心要素 | 总结说明 |
---|---|
驱动结构 | i2c_driver 注册、.probe() 初始化、.remove() 清理 |
匹配机制 | 依赖 i2c-core 扫描设备树并调用 .of_match_table |
资源访问 | 通过 regmap_init_i2c() 映射寄存器,使用 i2c_transfer 进行访问 |
驱动模型 | 本质上是总线驱动模型的一种典型实现,属于设备模型框架下的子类 |
与平台模型区别 | 不需创建 platform_device ,资源访问机制不同,结构更自动 |
✅ 结语:理解“设备模型”的统一性
总线驱动模型和平台驱动模型虽然接口风格不同,但都基于 Linux 内核统一的 设备模型(device model) 框架:
- 驱动与设备通过
bus_type
关联; - 匹配靠
of_match_table
或id_table
; - 所有子系统设备(如
i2c_client
、spi_device
、platform_device
)都挂接到统一的/sys/bus/
下。
通过本篇内容,我们以 PCA9450 的完整结构为例,深刻理解了 “从设备树 → 总线匹配 → 驱动注册 → 子设备资源注册” 的全过程。
📚 推荐阅读:
- Documentation/devicetree/bindings/regulator/
- drivers/regulator/core.c
- drivers/i2c/i2c-core-base.c
- NXP i.MX Linux BSP 源码中的
drivers/regulator/pca9450.c
如果你已经理解本篇内容,那么恭喜你已真正掌握 Linux 总线驱动模型的底层结构和实战应用。下一篇,我们将深入 Linux 电源管理机制中的 suspend/resume 实现逻辑,进一步探讨 regulator 与 runtime PM 的协同作用。