RK3588 kernel-6.1/drivers/i2c/i2c-boardinfo.c 功能详细分析
1. 文档范围与说明
说明: 用户路径写作「i2c-boardinfo.c 目录」,实际为 单个源文件,位于:
rk3588/kernel-6.1/drivers/i2c/i2c-boardinfo.c
并非独立子目录。该文件与 i2c-core-base.c 等共同构成 i2c 子系统,在 Makefile 中按 CONFIG_I2C_BOARDINFO 单独编译为 i2c-boardinfo.o。
文档路径: linuxDoc/drivers/i2c/
分析目标:
- 该文件在 I2C 子系统中的职责;
- 对外 API、内部数据结构与总线编号协调机制;
- 与 adapter 注册、设备创建的时序关系;
- 与 Device Tree(OF)方式的对比;
- RK3588 平台是否使用及实践建议。
2. 功能定位(一句话)
i2c-boardinfo.c 实现 「板级静态 I2C 设备表」的登记与延迟实例化:在 I2C 适配器(adapter)尚未注册之前,由板级/架构初始化代码预先声明「某总线号上会有哪些从设备」;待对应 i2c_adapter 注册成功后,由 core 扫描该表并创建 i2c_client。
这是 无 Device Tree 或早期 ARM/PowerPC/MIPS 板级代码 时代的经典机制;现代 RK3588 + DT 产品路径 几乎不调用 本文件提供的 API。
3. 编译与配置
3.1 Makefile
1 | obj-$(CONFIG_I2C_BOARDINFO) += i2c-boardinfo.o |
i2c-boardinfo.o与i2c-core.o分离链接,减小 core 模块体积的可选裁剪(理论上可CONFIG_I2C_BOARDINFO=n,实际默认开启)。
3.2 Kconfig
1 | config I2C_BOARDINFO |
- 无用户可见菜单项,默认始终使能;
include/linux/i2c.h中:若未使能,则i2c_register_board_info()为空 inline,直接返回 0。
4. 源文件结构概览
整个文件约 94 行,仅包含:
| 组成部分 | 内容 |
|---|---|
| 全局锁 | __i2c_board_lock(读写信号量) |
| 全局链表 | __i2c_board_list |
| 动态总线号边界 | __i2c_first_dynamic_bus_num |
| 唯一公共 API | i2c_register_board_info() |
无 probe/remove、无平台驱动、无设备树解析——纯数据登记层。
5. 核心数据结构
5.1 struct i2c_devinfo(i2c-core.h,内部)
1 | struct i2c_devinfo { |
- 仅 i2c core 可访问(不对外导出结构体定义给任意驱动);
- 每个
i2c_register_board_info()传入的数组元素对应链表中的一个i2c_devinfo节点。
5.2 struct i2c_board_info(include/linux/i2c.h,公开)
设备创建模板,主要字段:
| 字段 | 含义 |
|---|---|
type |
驱动匹配名 → i2c_client.name |
addr |
7/10 位 I2C 地址 |
flags |
I2C_CLIENT_TEN 等 |
platform_data |
传给 client 驱动的平台数据 |
irq |
中断号 |
resources / num_resources |
资源数组 |
of_node / fwnode |
固件节点(boardinfo 路径较少用) |
便捷宏:
1 |
6. 对外 API:i2c_register_board_info()
6.1 函数原型
1 | int i2c_register_board_info(int busnum, |
6.2 参数语义
| 参数 | 说明 |
|---|---|
busnum |
目标 adapter 的 固定总线编号(如 0、1、2),须与后续 i2c_add_numbered_adapter() 的 adap->nr 一致 |
info |
i2c_board_info 数组指针 |
len |
数组元素个数;可为 0,仅用于占用/预留该 busnum |
6.3 调用时机(内核文档要求)
必须在 任何 I2C adapter 驱动注册之前 完成,典型位置:
- 板级
arch_initcall()/device_initcall()之前的初始化; arch/*/boards/*/setup.c中静态表 + 注册函数。
反例: adapter 已 i2c_add_numbered_adapter() 之后再 i2c_register_board_info() — 设备不会自动补注册(除非该 adapter 再次触发扫描,而标准路径只在注册时扫描一次)。
6.4 实现逻辑(逐步)
1 | int i2c_register_board_info(int busnum, struct i2c_board_info const *info, unsigned len) |
要点:
- 浅拷贝
board_info:type、addr、platform_data等指针不会复制指向的内容;文档明确警告__initdata中的platform_data若仅 init 段有效,后续可能失效。 resources例外:通过kmemdup深拷贝,避免原数组释放后悬空。- 失败处理:遇
-ENOMEM中断循环,已入链节点不会回滚(部分注册)。
6.5 返回值
0:成功(或len==0仅更新 bus 号边界);-ENOMEM:分配i2c_devinfo或kmemdup失败。
7. 全局变量与总线号协调
7.1 __i2c_board_list
- 类型:
struct list_head; - 内容:所有尚未被 adapter 消费掉的
i2c_devinfo(注册后不删除节点,靠busnum匹配多次扫描时重复尝试创建,由i2c_new_client_device地址冲突检测处理)。
7.2 __i2c_first_dynamic_bus_num
作用: 划分 静态声明的总线号 与 动态分配的总线号。
| 时机 | 行为 |
|---|---|
i2c_register_board_info(busnum, ...) |
若 busnum >= __i2c_first_dynamic_bus_num,则设为 busnum + 1 |
i2c_init()(core 初始化) |
读 of_alias_get_highest_id("i2c"),若 DT alias 更大则提高该值 |
i2c_add_adapter() 动态分配 |
idr_alloc 从 __i2c_first_dynamic_bus_num 起找空闲号 |
目的: 避免 i2c_add_adapter() 动态分配的 i2c-5 与板级静态声明的 busnum=5 冲突。
7.3 __i2c_board_lock
- 类型:
struct rw_semaphore; - 写锁:
i2c_register_board_info(); - 读锁:
i2c_scan_static_board_info(); - 写锁:
i2c_init()中调整__i2c_first_dynamic_bus_num。
8. 设备实例化路径(与 core 协作)
本文件只登记;真正创建设备在 i2c-core-base.c。
8.1 时序图
1 | [启动早期] |
8.2 i2c_scan_static_board_info()(core 内)
1 | static void i2c_scan_static_board_info(struct i2c_adapter *adapter) |
- 匹配
devinfo->busnum == adapter->nr; - 调用
i2c_new_client_device()→device_register()→ 与i2c_driver匹配 probe。
8.3 条件:何时扫描 boardinfo
1 | if (adap->nr < __i2c_first_dynamic_bus_num) |
- 仅 编号小于动态边界的 adapter 会扫描静态表;
- 纯动态号 adapter(
i2c_add_adapter且 nr 较大)跳过 boardinfo。
9. 与其他设备注册方式对比
| 方式 | API / 机制 | 声明时机 | RK3588 |
|---|---|---|---|
| Boardinfo(本文) | i2c_register_board_info() |
adapter 之前,C 静态表 | 不使用 |
| Device Tree | of_i2c_register_devices() |
adapter 注册时解析子节点 | 主路径 |
| ACPI | i2c_acpi_register_devices() |
adapter 注册时 | 无 |
| 运行时扩展板 | i2c_new_client_device() |
adapter 已存在 | 内核模块/特殊驱动 |
| 地址探测 | i2c_new_scanned_device() |
adapter 已存在,多地址尝试 | 少见 |
9.1 RK3588 实际路径
在 rk3588/kernel-6.1/arch/arm64 下 无 i2c_register_board_info() 调用。
典型 DTS(rk3588s.dtsi + 板级 dtsi):
1 | &i2c2 { |
i2c-rk3x 注册 adapter 后,仅 of_i2c_register_devices() 根据子节点创建 client。
9.2 仍使用 boardinfo 的平台(源码树内示例)
| 架构/路径 | 示例 |
|---|---|
| SH(SuperH) | arch/sh/boards/mach-*/setup.c |
| PowerPC | arch/powerpc/platforms/*/misc.c |
| MIPS Alchemy | arch/mips/alchemy/devboards/*.c |
| MIPS Sibyte | arch/mips/sibyte/swarm/swarm-i2c.c |
10. 典型板级用法示例(历史参考)
1 | /* 设备表 */ |
对应 adapter 须 i2c_add_numbered_adapter() 且 adap->nr == 0。
11. 设计约束与注意事项
11.1 platform_data 生命周期
注释明确:不会复制 platform_data 指向的内容。若表在 __initdata,init 结束后内存可能被回收 → client probe 时访问非法。
正确做法: platform_data 指向全局/静态存储,或在 adapter 注册前仍为有效内存。
11.2 无注销接口
- 无
i2c_unregister_board_info(); - 链表节点常驻;adapter 删除时不会根据 boardinfo 自动移除 client(client 由 driver model 管理)。
11.3 与 DT 并存
同一 adapter 上可同时:
of_i2c_register_devices()创建 DT 子节点设备;i2c_scan_static_board_info()创建静态表设备。
须避免 地址冲突;RK3588 应只使用 DT 一种来源。
11.4 len == 0 的用途
仅更新 __i2c_first_dynamic_bus_num,用于 预留总线编号,确保后续动态 adapter 不会占用该号。
12. 导出符号策略
1 | DECLARE_RWSEM(__i2c_board_lock); |
注释写明:仅 i2c core 使用,不保证给其他模块。模块版本仅限 GPL。
公开给用户/板级代码的只有:
i2c_register_board_info()(include/linux/i2c.h)
13. 调试与验证
13.1 确认 boardinfo 是否参与
1 | # RK3588 预期:无 boardinfo 注册 |
13.2 若移植遗留 boardinfo 代码
- 确认
busnum与aliases/adap->nr一致; - 确认在
i2c-rk3xprobe 之前 调用(通常需改 arch init,RK 上不推荐); - 用
dmesg查看Can't create device at 0x..(地址冲突或 probe 失败)。
14. 功能对照总表
| 功能 | 是否由 i2c-boardinfo.c 实现 |
|---|---|
| 登记静态 I2C 设备表 | 是 |
| 分配/管理 adapter | 否(core) |
创建设备 i2c_client |
否(core 调用 i2c_new_client_device) |
| 解析 Device Tree | 否(i2c-core-of.c) |
| 总线传输 | 否(busses/*) |
| 驱动匹配 probe | 否(各 i2c_driver) |
| 动态总线号边界协调 | 部分(写 __i2c_first_dynamic_bus_num) |
15. 结论
i2c-boardinfo.c 是 I2C 子系统中体量很小但历史上很重要的一层:
- 职责单一:在 adapter 出现之前,把
(busnum, i2c_board_info)登记到全局链表; - 延迟绑定:adapter 注册时由
i2c_scan_static_board_info()实例化i2c_client; - 总线号协调:通过
__i2c_first_dynamic_bus_num避免与动态分配、DT alias 冲突; - RK3588:产品使用 纯 DT(
of_i2c_register_devices),不依赖 本文件;阅读 RK I2C Bring-up 可 跳过 boardinfo,重点放在 DTS +i2c-rk3x.c+ client 驱动。
若在维护旧架构移植代码时遇到 i2c_register_board_info(),应评估是否可改为 DTS 描述,以与当前 Rockchip 内核一致。
16. 推荐阅读顺序
i2c-boardinfo.c(全文)drivers/i2c/i2c-core.h—i2c_devinfoinclude/linux/i2c.h—i2c_board_info、i2c_register_board_infoi2c-core-base.c—i2c_scan_static_board_info、i2c_register_adapteri2c-core-of.c— RK3588 实际设备枚举路径
17. 相关文档
rk3588-kernel-6.1-drivers-i2c目录功能详细分析_zh.mdrk3588-kernel-6.1-drivers-i2c-busses目录功能详细分析_zh.mdrk3588-kernel-6.1-i2c总线架构分析.md
文档版本:基于 rk3588/kernel-6.1 源码树 drivers/i2c/i2c-boardinfo.c 分析。
正在加载留言…