RK3588 kernel-6.1/drivers/i2c/i2c-core-of.c 功能详细分析
1. 文档范围与说明
路径说明: 用户表述为「i2c-core-of.c 目录」,实际为 单个源文件:
rk3588/kernel-6.1/drivers/i2c/i2c-core-of.c(约 220 行)
编译关系: 在启用 Open Firmware / Device Tree 支持时编入 i2c-core.ko:
1 | i2c-core-$(CONFIG_OF) += i2c-core-of.o |
ARM64 Rockchip 内核 必然启用 CONFIG_OF,故本文件在 RK3588 上 始终参与链接。
文档保存路径: linuxDoc/drivers/i2c/
定位: I2C 子系统的 Device Tree 支持层:将 DTS 中挂在 I2C adapter 下的子节点解析为 i2c_board_info,并调用 i2c_new_client_device() 实例化 i2c_client;同时提供 OF 匹配辅助 与(可选)动态 DT 热插拔 通知。
与 RK3588 的关系: 这是 RK3588 上 I2C 从设备枚举的主路径。i2c-rk3x.c 注册 adapter 后,板级 dtsi 中 &i2cN { ... } 下的 compatible + reg 子节点均经本文件创建 client,再与对应 i2c_driver 匹配 probe。
2. 功能总览
| 功能模块 | 符号 | 可见性 | 作用 |
|---|---|---|---|
| DT → board_info | of_i2c_get_board_info() |
导出 | 从 device_node 填充 i2c_board_info |
| 单设备注册 | of_i2c_register_device() |
static | 解析节点并 i2c_new_client_device() |
| 批量枚举 | of_i2c_register_devices() |
core 内 | adapter 注册时遍历子节点创建设备 |
| 驱动匹配 | i2c_of_match_device() |
导出 | OF compatible 匹配(含 sysfs 回退) |
| 动态 DT | i2c_of_notifier / of_i2c_notify() |
CONFIG_OF_DYNAMIC |
运行时增删 I2C 设备节点 |
3. 在 I2C 栈中的位置
1 | 板级 DTS (&i2c2 { hym8563@51 { compatible; reg; }; }) |
与 ACPI(i2c_acpi_register_devices)和 boardinfo(i2c_scan_static_board_info)并列,RK3588 仅走 OF 路径。
4. of_i2c_get_board_info() — DT 节点 → i2c_board_info
1 | int of_i2c_get_board_info(struct device *dev, struct device_node *node, |
4.1 解析流程
| 步骤 | DT 属性 / API | 填入字段 |
|---|---|---|
| 清零 | memset(info, 0, ...) |
— |
| 设备类型名 | of_modalias_node() |
info->type |
| I2C 地址 | reg(u32) |
info->addr + 标志位 |
| 节点引用 | — | info->of_node、info->fwnode |
| Host Notify | host-notify(bool) |
I2C_CLIENT_HOST_NOTIFY |
| 唤醒源 | wakeup-source |
I2C_CLIENT_WAKE |
4.2 of_modalias_node() 行为
取 compatible 属性 第一个 字符串;若含 ,,则 modalias 为 逗号后 部分(去掉厂商前缀)。
示例:
1 | compatible = "haoyu,hym8563"; |
→ info->type = "hym8563"(与 MODULE_DEVICE_TABLE(of, ...) 中条目或驱动名对应)。
4.3 reg 属性与地址标志(dt-bindings/i2c/i2c.h)
1 |
reg 高位 |
处理 |
|---|---|
I2C_TEN_BIT_ADDRESS |
清除高位 → info->flags |= I2C_CLIENT_TEN |
I2C_OWN_SLAVE_ADDRESS |
清除高位 → info->flags |= I2C_CLIENT_SLAVE |
| 低 7/10 位 | 实际从机地址 |
RK3588 板级 DTS 通常仅使用 7 位地址,例如 reg = <0x51>。
4.4 错误返回
| 条件 | 返回值 |
|---|---|
| modalias 失败 | -EINVAL |
缺少/非法 reg |
-EINVAL 或 of_property_read_u32 错误码 |
5. of_i2c_register_devices() — 核心枚举入口
1 | void of_i2c_register_devices(struct i2c_adapter *adap); |
调用点: i2c_register_adapter()(i2c-core-base.c),在 adapter device_register() 之后、ACPI/boardinfo 扫描之前执行。
5.1 前置条件
1 | if (!adap->dev.of_node) |
Adapter 的 struct device 必须已关联 DT 节点(i2c-rk3x 在 probe 中设置 adap.dev.of_node = pdev->dev.of_node)。无 of_node 则 不枚举任何子设备。
5.2 遍历哪一层子节点?
1 | bus = of_get_child_by_name(adap->dev.of_node, "i2c-bus"); |
| 情况 | 遍历根节点 |
|---|---|
存在子节点 i2c-bus |
adapter/i2c-bus/ 下的 available 子节点 |
| 不存在 | 直接 adapter 节点下的子节点(RK3588 常用) |
for_each_available_child_of_node 会跳过:
status = "disabled"的节点;- 未通过 OF 可用性检查的节点。
5.3 OF_POPULATED 防重复
1 | if (of_node_test_and_set_flag(node, OF_POPULATED)) |
已实例化的节点不会再次注册。注销时 i2c_unregister_device() 会 of_node_clear_flag(..., OF_POPULATED)。
注册失败时清除标志:
1 | of_node_clear_flag(node, OF_POPULATED); |
5.4 与 RK3588 DTS 的对应关系
SoC dtsi 中控制器模板(rk3588s.dtsi):
1 | i2c0: i2c@fd880000 { |
板级 overlay(rk3588s-tablet.dtsi):
1 | &i2c2 { |
枚举结果:
i2c_new_client_device()在 i2c-2 总线上创建 client;- sysfs 设备名类似
2-0051(bus 号 + 地址); client->dev.of_node指向hym8563@51节点。
6. of_i2c_register_device() — 单节点实例化
1 | static struct i2c_client *of_i2c_register_device(struct i2c_adapter *adap, |
of_i2c_get_board_info(&adap->dev, node, &info);i2c_new_client_device(adap, &info)→ 进入 driver core,触发i2c_device_probe()。
供 of_i2c_register_devices() 与 动态 notifier 共用。
7. i2c_of_match_device() — 驱动与 client 匹配
1 | const struct of_device_id * |
7.1 匹配顺序
of_match_device(matches, &client->dev)— 标准 OF 匹配(client->dev.of_node存在时);i2c_of_match_device_sysfs()— 回退:通过 sysfs 手动new_device创建的 client 无 of_node,用client->name与compatible字符串比较(支持vendor,model的短名匹配)。
7.2 在 core 中的使用
| 位置 | 用途 |
|---|---|
i2c_device_match() |
总线 match 回调 |
i2c_device_probe() |
无 id_table 时要求 OF/ACPI 匹配之一 |
RK3588 驱动应提供 of_match_table,例如:
1 | static const struct of_device_id rk3x_i2c_of_match[] = { |
8. 动态 Device Tree(CONFIG_OF_DYNAMIC)
当内核启用 动态 OF(运行时 overlay / 配置扩展)时,编译 of_i2c_notify() 与全局 i2c_of_notifier。
注册: i2c_init() → of_reconfig_notifier_register(&i2c_of_notifier)。
8.1 OF_RECONFIG_CHANGE_ADD
1 | of_find_i2c_adapter_by_node(rd->dn->parent) /* 父节点必须是 adapter 的 of_node */ |
要求新节点的 父 DT 节点 对应已注册的 i2c_adapter。
8.2 OF_RECONFIG_CHANGE_REMOVE
1 | of_find_i2c_device_by_node(rd->dn) |
8.3 RK3588 说明
产品 rockchip_linux_defconfig 通常 不强调 CONFIG_OF_DYNAMIC;量产固件多在启动时加载完整 DTB。该路径主要用于 开发/测试 overlay,了解即可。
9. 与 I2C MUX 的协作
i2c-mux.c 在 i2c_mux_add_adapter() 中为每个 channel 创建子 adapter,并设置:
1 | priv->adap.dev.of_node = child; /* mux 节点下对应 channel 的子节点 */ |
随后 i2c_add_numbered_adapter() / i2c_add_adapter() 同样会调用 of_i2c_register_devices(),从而枚举 该 channel 下 DT 子设备。
典型 DTS 结构:
1 | &i2c4 { |
MUX 框架负责 channel adapter 的 of_node 指向 i2c@N 节点;具体传感器仍由 i2c-core-of.c 枚举。
10. 与 i2c-core-base.c 的衔接
| base.c 逻辑 | of.c 参与点 |
|---|---|
i2c_register_adapter() |
调用 of_i2c_register_devices(adap) |
i2c_device_probe() |
of_irq_get / of_clk_set_defaults(非 of.c,但依赖 client->dev.of_node) |
i2c_unregister_device() |
清除 OF_POPULATED |
i2c_init() |
注册 i2c_of_notifier(若 CONFIG_OF_DYNAMIC) |
i2c_add_adapter() |
of_alias_get_id(dev->of_node, "i2c") 决定 bus 号(在 base.c,非 of.c) |
IRQ / 时钟 / pinctrl 不在 i2c-core-of.c 解析,由 各 i2c_driver 的 probe 通过 dev->of_node 自行读取(如 devm_request_irq、devm_clk_get)。
11. 三种实例化方式对比(RK3588)
| 方式 | 实现 | RK3588 |
|---|---|---|
| Device Tree | i2c-core-of.c |
主路径 |
| ACPI | i2c-core-acpi.c |
不使用 |
| 静态 boardinfo | i2c-boardinfo.c |
不使用 |
sysfs new_device |
i2c-core-base.c |
调试可选 |
遗留 detect |
i2c-core-base.c |
基本不用 |
12. RK3588 端到端示例
1 | 启动 |
Aliases(rk3588s.dtsi):
1 | aliases { |
保证 i2c_add_numbered_adapter 的 adap->nr 与 DTS i2cN 编号一致。
13. 导出符号与桩函数
| 符号 | CONFIG_OF 开启 |
关闭 |
|---|---|---|
of_i2c_get_board_info |
实现 | inline 空/错误 |
of_i2c_register_devices |
实现 | inline 空 |
i2c_of_match_device |
实现 | NULL |
of_find_i2c_*_by_node |
include/linux/i2c.h inline |
返回 NULL |
14. 调试与常见问题
14.1 检查枚举结果
1 | ls /sys/bus/i2c/devices/ |
14.2 设备未出现
| 可能原因 | 检查 |
|---|---|
adapter status = "disabled" |
父节点 &i2cN { status = "okay"; } |
子设备 status = "disabled" |
for_each_available_child_of_node 会跳过 |
adapter 无 of_node |
主机驱动是否正确设置 adap.dev.of_node |
reg 缺失 |
dmesg: of_i2c: invalid reg |
compatible 问题 |
dmesg: of_i2c: modalias failure |
| 重复枚举 | OF_POPULATED 已置位 |
| 地址冲突 | i2c_new_client_device 失败日志 |
14.3 驱动未 probe
i2c_of_match_device失败:检查compatible与驱动of_match_table;- 模块未加载:
modalias/depmod; - probe defer:依赖时钟/_regulator 未就绪(与 of.c 无直接关系)。
14.4 内核日志
1 | dmesg | grep -iE 'of_i2c|i2c-[0-9]|Failed to create I2C device' |
15. 源码结构速查(全文约 220 行)
1 | L22-63 of_i2c_get_board_info() 导出:DT → i2c_board_info |
16. 结论
i2c-core-of.c 体量小,却是 ARM/DT 平台 I2C 设备发现的枢纽:
- 将 DTS 子节点的
compatible+reg转为i2c_board_info; - 在 adapter 注册时 自动创建
i2c_client; - 通过
OF_POPULATED避免重复实例化; - 支持可选的 动态 DT 热插拔;
- 增强
i2c_of_match_device以兼容 sysfs 创建设备。
对 RK3588:板级所有 I2C 外设(RTC、PMIC、Codec、触摸、传感器、MUX 下设备等)均依赖本文件与 i2c-core-base.c 的配合;分析硬件 bring-up 问题时应 优先核对 DTS 与 dmesg 中 of_i2c 相关日志。
17. 推荐阅读顺序
i2c-core-of.c—of_i2c_register_devices、of_i2c_get_board_infoi2c-core-base.c—i2c_register_adapter、i2c_new_client_devicearch/arm64/boot/dts/rockchip/*.dtsi— 板级&i2cN子节点busses/i2c-rk3x.c— adapter 注册与of_node绑定Documentation/devicetree/bindings/i2c/— 绑定文档
18. 相关文档
rk3588-kernel-6.1-i2c-core-base.c功能详细分析_zh.mdrk3588-kernel-6.1-i2c-core-acpi.c功能详细分析_zh.mdrk3588-kernel-6.1-i2c-boardinfo.c功能详细分析_zh.mdrk3588-kernel-6.1-drivers-i2c-muxes目录功能详细分析_zh.mdrk3588-kernel-6.1-i2c总线架构分析.md
文档版本:基于 rk3588/kernel-6.1 源码树 drivers/i2c/i2c-core-of.c 分析。
正在加载留言…