RK3588 kernel-6.1/drivers/i2c/i2c-core-of.c 功能详细分析

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
2
3
4
5
6
7
8
9
10
11
12
13
14
板级 DTS (&i2c2 { hym8563@51 { compatible; reg; }; })


i2c-rk3x probe → i2c_add_numbered_adapter()


i2c_register_adapter() [i2c-core-base.c]

├── of_i2c_register_devices() [i2c-core-of.c] ← 本文
│ for_each_available_child_of_node
│ → of_i2c_get_board_info()
│ → i2c_new_client_device() [i2c-core-base.c]

└── i2c_device_probe() [驱动绑定]

ACPIi2c_acpi_register_devices)和 boardinfoi2c_scan_static_board_info)并列,RK3588 仅走 OF 路径


4. of_i2c_get_board_info() — DT 节点 → i2c_board_info

1
2
int of_i2c_get_board_info(struct device *dev, struct device_node *node,
struct i2c_board_info *info);

4.1 解析流程

步骤 DT 属性 / API 填入字段
清零 memset(info, 0, ...)
设备类型名 of_modalias_node() info->type
I2C 地址 reg(u32) info->addr + 标志位
节点引用 info->of_nodeinfo->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
2
#define I2C_TEN_BIT_ADDRESS    (1 << 31)   /* 10 位寻址 */
#define I2C_OWN_SLAVE_ADDRESS (1 << 30) /* 本机为 slave 设备 */
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 -EINVALof_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
2
if (!adap->dev.of_node)
return;

Adapter 的 struct device 必须已关联 DT 节点(i2c-rk3x 在 probe 中设置 adap.dev.of_node = pdev->dev.of_node)。无 of_node不枚举任何子设备

5.2 遍历哪一层子节点?

1
2
3
4
5
bus = of_get_child_by_name(adap->dev.of_node, "i2c-bus");
if (!bus)
bus = of_node_get(adap->dev.of_node);

for_each_available_child_of_node(bus, node) { ... }
情况 遍历根节点
存在子节点 i2c-bus adapter/i2c-bus/ 下的 available 子节点
不存在 直接 adapter 节点下的子节点(RK3588 常用)

for_each_available_child_of_node 会跳过:

  • status = "disabled" 的节点;
  • 未通过 OF 可用性检查的节点。

5.3 OF_POPULATED 防重复

1
2
if (of_node_test_and_set_flag(node, OF_POPULATED))
continue;

已实例化的节点不会再次注册。注销时 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
2
3
4
5
6
i2c0: i2c@fd880000 {
compatible = "rockchip,rk3588-i2c", "rockchip,rk3399-i2c";
#address-cells = <1>;
#size-cells = <0>;
status = "disabled";
};

板级 overlay(rk3588s-tablet.dtsi):

1
2
3
4
5
6
7
8
9
10
&i2c2 {
status = "okay";

hym8563: hym8563@51 {
compatible = "haoyu,hym8563";
reg = <0x51>;
wakeup-source;
...
};
};

枚举结果:

  • i2c_new_client_device()i2c-2 总线上创建 client;
  • sysfs 设备名类似 2-0051(bus 号 + 地址);
  • client->dev.of_node 指向 hym8563@51 节点。

6. of_i2c_register_device() — 单节点实例化

1
2
static struct i2c_client *of_i2c_register_device(struct i2c_adapter *adap,
struct device_node *node);
  1. of_i2c_get_board_info(&adap->dev, node, &info)
  2. i2c_new_client_device(adap, &info) → 进入 driver core,触发 i2c_device_probe()

of_i2c_register_devices()动态 notifier 共用。


7. i2c_of_match_device() — 驱动与 client 匹配

1
2
3
const struct of_device_id *
i2c_of_match_device(const struct of_device_id *matches,
struct i2c_client *client);

7.1 匹配顺序

  1. of_match_device(matches, &client->dev) — 标准 OF 匹配(client->dev.of_node 存在时);
  2. i2c_of_match_device_sysfs() — 回退:通过 sysfs 手动 new_device 创建的 client 无 of_node,用 client->namecompatible 字符串比较(支持 vendor,model 的短名匹配)。

7.2 在 core 中的使用

位置 用途
i2c_device_match() 总线 match 回调
i2c_device_probe() id_table 时要求 OF/ACPI 匹配之一

RK3588 驱动应提供 of_match_table,例如:

1
2
3
4
5
static const struct of_device_id rk3x_i2c_of_match[] = {
{ .compatible = "rockchip,rk3588-i2c" },
{ .compatible = "rockchip,rk3399-i2c" },
{ }
};

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
2
3
of_find_i2c_adapter_by_node(rd->dn->parent)  /* 父节点必须是 adapter 的 of_node */
→ 清除 FWNODE_FLAG_NOT_DEVICE(便于 fw_devlink)
→ of_i2c_register_device()

要求新节点的 父 DT 节点 对应已注册的 i2c_adapter

8.2 OF_RECONFIG_CHANGE_REMOVE

1
2
of_find_i2c_device_by_node(rd->dn)
→ i2c_unregister_device()

8.3 RK3588 说明

产品 rockchip_linux_defconfig 通常 不强调 CONFIG_OF_DYNAMIC;量产固件多在启动时加载完整 DTB。该路径主要用于 开发/测试 overlay,了解即可。


9. 与 I2C MUX 的协作

i2c-mux.ci2c_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
2
3
4
5
6
7
8
9
10
11
12
13
&i2c4 {
i2c-mux@70 {
compatible = "nxp,pca9548";
reg = <0x70>;
#address-cells = <1>;
#size-cells = <0>;

i2c@0 {
reg = <0>;
sensor@48 { compatible = "..."; reg = <0x48>; };
};
};
};

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_irqdevm_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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
启动
rk3x_i2c_probe(i2c@fea90000) /* i2c1 */
adap.dev.of_node = pdev->dev.of_node
i2c_add_numbered_adapter() /* nr=1, 来自 aliases */

i2c_register_adapter()
of_i2c_register_devices(adap)
节点: &i2c1 下无子设备 → 无 client

rk3x_i2c_probe(i2c@feaa00000) /* i2c2, tablet dtsi 有子节点 */
of_i2c_register_devices()
hym8563@51 → client 2-0051, type "hym8563"
cw2015@62 → client 2-0062
bq25703@6b → client 2-006b

hym8563 i2c_driver probe
of_match_device("haoyu,hym8563")
of_irq_get / clk / pinctrl from of_node

Aliasesrk3588s.dtsi):

1
2
3
4
5
aliases {
i2c0 = &i2c0;
...
i2c8 = &i2c8;
};

保证 i2c_add_numbered_adapteradap->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
2
3
4
5
ls /sys/bus/i2c/devices/
# 例如 2-0051 -> ../../../devices/platform/feaa0000.i2c/i2c-2/2-0051

cat /sys/bus/i2c/devices/2-0051/name
cat /sys/bus/i2c/devices/2-0051/of_node/name # 需 CONFIG_SYSFS 关联

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
2
3
4
5
L22-63   of_i2c_get_board_info()      导出:DT → i2c_board_info
L65-83 of_i2c_register_device() 单设备注册
L85-114 of_i2c_register_devices() adapter 子节点批量枚举
L116-160 i2c_of_match_device*() OF/sysfs 匹配
L162-220 of_i2c_notify / i2c_of_notifier [CONFIG_OF_DYNAMIC]

16. 结论

i2c-core-of.c 体量小,却是 ARM/DT 平台 I2C 设备发现的枢纽

  1. 将 DTS 子节点的 compatible + reg 转为 i2c_board_info
  2. adapter 注册时 自动创建 i2c_client
  3. 通过 OF_POPULATED 避免重复实例化;
  4. 支持可选的 动态 DT 热插拔
  5. 增强 i2c_of_match_device 以兼容 sysfs 创建设备。

RK3588:板级所有 I2C 外设(RTC、PMIC、Codec、触摸、传感器、MUX 下设备等)均依赖本文件与 i2c-core-base.c 的配合;分析硬件 bring-up 问题时应 优先核对 DTS 与 dmesg 中 of_i2c 相关日志


17. 推荐阅读顺序

  1. i2c-core-of.cof_i2c_register_devicesof_i2c_get_board_info
  2. i2c-core-base.ci2c_register_adapteri2c_new_client_device
  3. arch/arm64/boot/dts/rockchip/*.dtsi — 板级 &i2cN 子节点
  4. busses/i2c-rk3x.c — adapter 注册与 of_node 绑定
  5. Documentation/devicetree/bindings/i2c/ — 绑定文档

18. 相关文档


文档版本:基于 rk3588/kernel-6.1 源码树 drivers/i2c/i2c-core-of.c 分析。

文章互动

阅读 --

留言

0 条留言

正在加载留言…