RK3588 kernel-6.1/drivers/i2c/i2c-core-base.c 功能详细分析
1. 文档范围与说明
路径说明: 用户表述为「i2c-core-base.c 目录」,实际为 单个源文件:
rk3588/kernel-6.1/drivers/i2c/i2c-core-base.c(约 2723 行)
编译关系: 作为 i2c-core 复合模块的主干,始终参与链接(CONFIG_I2C=y 时):
1 | i2c-core-objs := i2c-core-base.o i2c-core-smbus.o |
文档保存路径: linuxDoc/drivers/i2c/
定位: Linux I2C 子系统的 核心实现:总线/设备模型、adapter 与 client 生命周期、i2c_transfer 传输路径、总线锁、总线恢复、驱动注册与(遗留)地址探测、Host Notify、debugfs/sysfs 等。SMBus 高层 API 在 i2c-core-smbus.c;DT 设备枚举在 i2c-core-of.c;ACPI 在 i2c-core-acpi.c。
与 RK3588 的关系: 本文件是 RK3588 I2C 栈的必经核心。i2c-rk3x.c 通过 i2c_add_numbered_adapter() 注册 adapter,经 of_i2c_register_devices() 创建 client,应用/驱动最终调用 i2c_transfer() → rk3x_i2c_xfer()。
2. 功能总览(按模块)
| 模块 | 行号区间(约) | 主要符号 | 作用 |
|---|---|---|---|
| 跟踪与工具 | 73–118 | i2c_freq_mode_string, i2c_match_id |
频率模式字符串、驱动 ID 匹配 |
| 总线恢复 | 179–471 | i2c_generic_scl_recovery, i2c_recover_bus |
SCL 位操作/GPIO 恢复卡死总线 |
| 设备 probe | 489–621 | i2c_device_probe/remove |
client 绑定驱动、IRQ、PM、时钟 |
| 总线类型 | 700–714 | i2c_bus_type, i2c_client_type |
driver core 总线与设备类型 |
| 地址校验 | 736–845 | i2c_check_addr_*, i2c_lock_addr |
7/10 位地址、冲突检测、实例化锁 |
| Client 生命周期 | 965–1230 | i2c_new_client_device, dummy/ancillary |
创建/注销 client |
| Adapter 生命周期 | 1522–1860 | i2c_register_adapter, i2c_del_adapter |
注册/删除 adapter、枚举子设备 |
| Host Notify | 1449–1519 | i2c_handle_smbus_host_notify |
SMBus Host Notify → 虚拟 IRQ |
| Adapter sysfs | 1264–1400 | new_device, delete_device |
用户空间动态创建设备 |
| 驱动注册 | 2014–2062 | i2c_register_driver, i2c_del_driver |
I2C 驱动注册与 adapter 遍历 |
| 子系统初始化 | 2123–2189 | i2c_init / i2c_exit |
postcore_initcall 早期初始化 |
| 数据传输 | 2196–2410 | i2c_transfer, __i2c_transfer |
核心 master 传输与 quirk 检查 |
| 遗留地址探测 | 2456–2590 | i2c_detect, i2c_default_probe |
detect 回调自动发现设备 |
| Adapter 引用 | 2638–2667 | i2c_get_adapter, i2c_put_adapter |
按总线号获取 adapter |
| DMA 安全缓冲 | 2681–2718 | i2c_get/put_dma_safe_msg_buf |
非 DMA 安全 buf 的 bounce buffer |
| DT 时序 | 1933–1988 | i2c_parse_fw_timings |
从 DT 解析 I2C 时序参数 |
3. 源码在 I2C 子系统中的位置
1 | ┌─────────────────────────────────────┐ |
4. 子系统初始化(i2c_init)
入口: postcore_initcall(i2c_init) — 必须 早于 部分 subsys_initcall 中注册的 I2C 驱动。
主要步骤:
of_alias_get_highest_id("i2c")— 根据 DT alias 调整动态总线号起点__i2c_first_dynamic_bus_num;bus_register(&i2c_bus_type)— 注册i2c总线;debugfs_create_dir("i2c", NULL)— 创建/sys/kernel/debug/i2c/;- 注册 dummy_driver(占位,保证总线上有驱动可做匹配);
- 注册 OF/ACPI 重配置 notifier(动态 DT/ACPI 热插拔)。
全局状态:
static DEFINE_MUTEX(core_lock)— 保护i2c_adapter_idr与探测序列化;static DEFINE_IDR(i2c_adapter_idr)— 总线号 →i2c_adapter映射;static bool is_registered— 防止 adapter 在 bus 注册前添加。
5. 设备模型:i2c_bus_type 与 probe 路径
5.1 总线定义
1 | struct bus_type i2c_bus_type = { |
匹配顺序(i2c_device_match): OF 表 → ACPI 表 → i2c_device_id 表 → 按设备名匹配。
5.2 i2c_device_probe() 要点(RK3588 走 DT 分支)
| 步骤 | 说明 |
|---|---|
| IRQ | of_irq_get / of_irq_get_byname;或 ACPI i2c_acpi_get_irq;或 Host Notify |
| 驱动匹配 | 要求 id_table 或 OF/ACPI 匹配之一 |
| 唤醒 | I2C_CLIENT_WAKE + wakeup GPIO/IRQ |
| 时钟 | of_clk_set_defaults() |
| 电源域 | dev_pm_domain_attach()(ACPI 可 i2c_acpi_waive_d0_probe 豁免) |
| 驱动 probe | driver->probe() 或 probe_new() |
Host Notify: I2C_CLIENT_HOST_NOTIFY 时通过 adapter 上 irq_domain 将 SMBus 通知映射为 client 的 client->irq。
6. Adapter 注册与删除
6.1 两种注册 API
| API | 总线号 | RK3588 使用 |
|---|---|---|
i2c_add_numbered_adapter() |
固定 adap->nr(DT aliases) |
i2c-rk3x.c 使用 |
i2c_add_adapter() |
动态分配或 DT alias | USB/PCI 等 |
i2c_add_adapter() 若 adapter 的 dev.of_node 有 i2c alias,则转为 numbered 注册。
6.2 i2c_register_adapter() 流程
1 | i2c_register_adapter(adap) |
6.3 i2c_del_adapter() 流程
注销 adapter 前:卸载所有 client(含 dummy)、移除 ACPI OP region handler、注销设备、释放 Host Notify domain、从 idr 删除。
6.4 devm_i2c_add_adapter()
设备资源管理封装:adapter 随 parent dev 自动 i2c_del_adapter。
7. Client 创建与注销
7.1 i2c_new_client_device()
调用者: of_i2c_register_devices()、i2c_acpi_register_devices()、驱动 detect、sysfs new_device 等。
流程摘要:
- 从
i2c_board_info填充addr、flags、name、fwnode、of_node; i2c_check_addr_validity()— 7 位禁止 0x00,10 位 ≤ 0x3ff;i2c_lock_addr()— 防止并发重复实例化同一 7 位地址;i2c_check_addr_ex()— 统计同地址已有 client 数量(用于命名后缀);device_register()→ 触发i2c_device_probe();- 设备名规则:
i2c-%04x或bus-addr-index(见i2c_dev_set_name)。
7.2 其它 client API
| 函数 | 用途 |
|---|---|
i2c_unregister_device() |
对称注销;清除 OF OF_POPULATED、ACPI enumerated |
i2c_find_device_by_fwnode() |
按 fwnode 查找已注册 client |
i2c_new_dummy_device() / devm_* |
无驱动的占位 client(mux、临时访问) |
i2c_new_ancillary_device() |
主 client 下的附属设备(如 codec 子器件) |
7.3 Dummy 驱动
dummy_driver 在 core 初始化时注册,使未绑定具体驱动的 client 仍可存在于总线上(供 i2c_transfer 直接访问或 mux 使用)。
8. 数据传输核心:i2c_transfer
8.1 调用链(RK3588 典型)
1 | 驱动 i2c_master_send() / i2c_transfer() |
8.2 __i2c_transfer 细节
- 要求
adap->algo->master_xfer存在,否则-EOPNOTSUPP; - 原子上下文: 若
i2c_in_atomic_xfer_mode()且存在master_xfer_atomic,则走原子路径(RK3x:rk3x_i2c_xfer_polling); - Tracepoint:
i2c_transfertrace 启用时记录 read/write/reply; - 仲裁丢失: 返回
-EAGAIN时在adap->timeout内重试。
8.3 Adapter Quirks(i2c_check_for_quirks)
限制单次传输:max_num_msgs、max_read_len、max_write_len、组合消息 I2C_AQ_COMB 等。主机驱动可在 adap->quirks 中声明硬件限制。
8.4 i2c_transfer_buffer_flags()
对单条消息的便捷封装:根据 I2C_M_RD 设置读写方向。
8.5 DMA 安全缓冲
i2c_get_dma_safe_msg_buf() / i2c_put_dma_safe_msg_buf():
- 当
msg->buf非 DMA 安全且长度 ≥ threshold 时分配 bounce buffer; - 读完成后
memcpy回原始 buffer; - RK3x 等 DMA 主机驱动在传输前可调用以避免 DMA 映射失败。
9. 总线锁与 MUX 树
9.1 默认锁实现
1 | static const struct i2c_lock_operations i2c_adapter_lock_ops = { |
i2c_adapter_depth() 沿 i2c_parent_is_i2c_adapter() 向上计数,用于 lockdep 嵌套子类,避免 mux 父子 adapter 死锁。
9.2 地址占用检查
i2c_check_addr_busy()— 本 adapter 及 mux 父/子 链上是否已有该地址;i2c_encode_flags_to_addr()— 10 位地址、I2C_CLIENT_SLAVE等标志编码到唯一键值。
10. 总线恢复(Bus Recovery)
当 SDA 被拉低导致总线挂死时,adapter 可配置 adap->bus_recovery_info:
| 机制 | 函数 |
|---|---|
| 通用 SCL 翻转 | i2c_generic_scl_recovery() — 9 个时钟脉冲 + STOP 条件 |
| GPIO 自动配置 | DT scl-gpios / sda-gpios → i2c_gpio_init_generic_recovery() |
| Pinctrl | recovery / gpio state 切换 |
| 入口 | i2c_recover_bus(adap) — 调用 recover_bus 回调 |
初始化: i2c_register_adapter() 内 i2c_init_recovery();失败则清除 bus_recovery_info(非致命)。
RK3588 i2c-rk3x 是否在 DT 中启用 recovery 取决于板级 dtsi;core 提供通用框架。
11. Host Notify 与 SMBus Alert
| 功能 | 位置 | 说明 |
|---|---|---|
| Host Notify IRQ domain | i2c_setup_host_notify_irq_domain() |
为 7 位地址各建虚拟 IRQ |
| 中断转发 | i2c_handle_smbus_host_notify(adap, addr) |
总线驱动 ISR 中调用 |
| SMBus Alert | i2c_setup_smbus_alert() |
在 i2c-core-smbus.c,adapter 注册时调用 |
12. 驱动注册与遗留探测
12.1 i2c_register_driver() / i2c_del_driver()
- 将
i2c_driver挂到i2c_bus_type; - 对已存在的每个 adapter 调用
i2c_do_add_adapter()→i2c_detect()。
12.2 i2c_detect()(遗留机制)
当驱动提供 address_list + detect() 时:
- 对列表中每个 7 位地址创建临时 client;
i2c_default_probe()— SMBus Quick Write 或 Byte Read(避免损坏 EEPROM);- 调用
driver->detect()填充i2c_board_info; i2c_new_client_device()实例化。
RK3588: 现代平台 几乎不用 class-based detect;设备由 DT 声明。仍保留以兼容旧 x86/工业板。
12.3 i2c_clients_command()
向 adapter 下所有已绑定 client 广播 driver->command()(电源管理等)。
13. Sysfs 与 Debugfs
13.1 Adapter 属性(/sys/bus/i2c/devices/i2c-N/)
| 属性 | 权限 | 功能 |
|---|---|---|
name |
RO | adapter 名称 |
new_device |
WO | echo name addr > new_device 动态创建 client |
delete_device |
WO | 删除 userspace 创建的 client |
uevent |
— | 导出 modalias 等 |
13.2 Client 属性
name、modalias — 支持 udev 冷插拔 modprobe。
13.3 Debugfs
/sys/kernel/debug/i2c/i2c-N/ — 各 adapter 子目录(具体节点由主机驱动或 core 扩展添加)。
14. 设备树时序解析
i2c_parse_fw_timings(dev, &timings, use_defaults) 从 DT 读取:
clock-frequency、i2c-scl-falling-time、i2c-scl-rising-time等;- 供 主机驱动(如 DesignWare、部分 SoC)计算分频,非 RK3x 专用。
RK3x 通常在驱动内直接读 clock-frequency;若需统一时序可调用此 API。
15. 与兄弟文件的分工
| 文件 | 职责 | RK3588 |
|---|---|---|
| i2c-core-base.c | 模型、transfer、adapter/client | 核心,必用 |
| i2c-core-smbus.c | i2c_smbus_*、i2c_smbus_xfer |
传感器/PMIC 常用 |
| i2c-core-of.c | of_i2c_register_devices、OF notifier |
设备枚举主路径 |
| i2c-core-acpi.c | ACPI 枚举、OP region | 通常不编译 |
| i2c-core-slave.c | I2C slave 模式 | 视配置 |
| i2c-boardinfo.c | 静态 boardinfo 表 | 通常不用 |
16. RK3588 端到端路径示例
以 i2c-rk3x + 摄像头传感器为例:
1 | 1. platform_driver probe (i2c-rk3x.c) |
关键配置: CONFIG_I2C=y、CONFIG_I2C_RK3X=y、CONFIG_I2C_CHARDEV(可选 /dev/i2c-N)。
17. 主要导出符号分类
17.1 传输与适配器
i2c_transfer, __i2c_transfer, i2c_transfer_buffer_flags, i2c_get_adapter, i2c_put_adapter, i2c_recover_bus, i2c_generic_scl_recovery, i2c_handle_smbus_host_notify, i2c_get_dma_safe_msg_buf, i2c_put_dma_safe_msg_buf
17.2 设备生命周期
i2c_add_adapter, i2c_add_numbered_adapter, i2c_del_adapter, devm_i2c_add_adapter, i2c_new_client_device, i2c_unregister_device, i2c_new_dummy_device, devm_i2c_new_dummy_device, i2c_new_ancillary_device, i2c_find_device_by_fwnode, i2c_find_adapter_by_fwnode, i2c_get_adapter_by_fwnode
17.3 驱动模型
i2c_bus_type, i2c_client_type, i2c_adapter_type, i2c_register_driver, i2c_del_driver, i2c_verify_client, i2c_verify_adapter, i2c_match_id, i2c_get_match_data, i2c_for_each_dev, i2c_clients_command
17.4 其它
i2c_freq_mode_string, i2c_check_7bit_addr_validity_strict, i2c_dev_irq_from_resources, i2c_adapter_depth, i2c_parse_fw_timings, i2c_probe_func_quick_read, i2c_get_device_id
18. 调试建议
1 | # 查看总线与设备 |
常见问题定位:
| 现象 | 可能涉及 base.c 逻辑 |
|---|---|
| 无 client 设备 | of_i2c_register_devices 未执行或 DT 无子节点 |
| probe defer | adapter 未注册、EPROBE_DEFER on GPIO/recovery |
| 地址冲突 | i2c_check_addr_ex / mux 父子地址检查 |
| 传输超时 | i2c_transfer 重试、adap->timeout |
| 总线卡死 | i2c_recover_bus、recovery info 未配置 |
19. 结论
i2c-core-base.c 是 Linux I2C 子系统的 中枢实现,涵盖:
- 子系统与总线注册(
postcore_initcall); - Adapter/Client 设备模型 与 driver core 集成;
i2c_transfer统一传输入口 及锁、quirk、trace、重试;- 总线恢复、Host Notify、DMA bounce buffer;
- 驱动注册与遗留 detect、sysfs 动态设备、debugfs;
- 与 OF/ACPI/SMBus 子模块的衔接调用点。
对 RK3588:所有 SoC 内置 I2C(i2c-rk3x)与 DT 声明的外设均 必须经过本文件;理解 i2c_register_adapter → of_i2c_register_devices → i2c_new_client_device → i2c_transfer 链路是分析板级 I2C 问题的关键。
20. 推荐阅读顺序
i2c-core-base.c—i2c_register_adapter、i2c_transfer、i2c_new_client_devicei2c-core-of.c— RK3588 设备枚举busses/i2c-rk3x.c— 硬件master_xferinclude/linux/i2c.h— 公共 API 与结构体Documentation/i2c/— 实例化设备、总线恢复说明
21. 相关文档
rk3588-kernel-6.1-drivers-i2c目录功能详细分析_zh.mdrk3588-kernel-6.1-i2c-core-acpi.c功能详细分析_zh.mdrk3588-kernel-6.1-i2c-boardinfo.c功能详细分析_zh.mdrk3588-kernel-6.1-drivers-i2c-busses目录功能详细分析_zh.mdrk3588-kernel-6.1-i2c总线架构分析.md
文档版本:基于 rk3588/kernel-6.1 源码树 drivers/i2c/i2c-core-base.c(约 2723 行)分析。
正在加载留言…