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

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
2
3
4
i2c-core-objs := i2c-core-base.o i2c-core-smbus.o
i2c-core-$(CONFIG_ACPI) += i2c-core-acpi.o
i2c-core-$(CONFIG_OF) += i2c-core-of.o
i2c-core-$(CONFIG_I2C_SLAVE) += i2c-core-slave.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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
           ┌─────────────────────────────────────┐
│ 用户空间 / 其它内核模块 │
│ i2c-dev.c │ 各 i2c_driver 驱动 │
└─────────────┬───────────────────────┘

┌─────────────▼───────────────────────┐
│ i2c-core-base.c(本文) │
│ bus_type、adapter/client、transfer │
└─┬─────────┬─────────┬───────────────┘
│ │ │
i2c-core-smbus.c i2c-core-of.c i2c-core-acpi.c
(SMBus API) (DT 枚举) (ACPI 枚举, x86)

┌────────────▼────────────┐
│ busses/i2c-rk3x.c 等 │
│ algo->master_xfer │
└─────────────────────────┘

4. 子系统初始化(i2c_init

入口: postcore_initcall(i2c_init) — 必须 早于 部分 subsys_initcall 中注册的 I2C 驱动。

主要步骤:

  1. of_alias_get_highest_id("i2c") — 根据 DT alias 调整动态总线号起点 __i2c_first_dynamic_bus_num
  2. bus_register(&i2c_bus_type) — 注册 i2c 总线;
  3. debugfs_create_dir("i2c", NULL) — 创建 /sys/kernel/debug/i2c/
  4. 注册 dummy_driver(占位,保证总线上有驱动可做匹配);
  5. 注册 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
2
3
4
5
6
7
struct bus_type i2c_bus_type = {
.name = "i2c",
.match = i2c_device_match, /* OF / ACPI / i2c_device_id */
.probe = i2c_device_probe,
.remove = i2c_device_remove,
.shutdown = i2c_device_shutdown,
};

匹配顺序(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_nodei2c alias,则转为 numbered 注册。

6.2 i2c_register_adapter() 流程

1
2
3
4
5
6
7
8
9
10
11
12
i2c_register_adapter(adap)
├─ 校验 adap->algo、设置 lock_ops、timeout
├─ i2c_setup_host_notify_irq_domain()
├─ device_register() → sysfs: /sys/bus/i2c/devices/i2c-N
├─ debugfs_create_dir()
├─ i2c_setup_smbus_alert() [i2c-core-smbus.c]
├─ pm_runtime_enable()
├─ i2c_init_recovery() [GPIO / 通用 SCL 恢复]
├─ of_i2c_register_devices() [DT 子设备, RK3588 主路径]
├─ i2c_acpi_*() [x86, 通常无]
├─ i2c_scan_static_board_info() [CONFIG_I2C_BOARDINFO, RK 通常无]
└─ bus_for_each_drv → i2c_do_add_adapter → i2c_detect()

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 等。

流程摘要:

  1. i2c_board_info 填充 addrflagsnamefwnodeof_node
  2. i2c_check_addr_validity() — 7 位禁止 0x00,10 位 ≤ 0x3ff;
  3. i2c_lock_addr() — 防止并发重复实例化同一 7 位地址;
  4. i2c_check_addr_ex() — 统计同地址已有 client 数量(用于命名后缀);
  5. device_register() → 触发 i2c_device_probe()
  6. 设备名规则:i2c-%04xbus-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
2
3
4
5
6
7
8
9
驱动 i2c_master_send() / i2c_transfer()
→ i2c_transfer(adap, msgs, num)
→ __i2c_lock_bus_helper(adap) /* rt_mutex, 支持 mux 树深度 */
→ __i2c_transfer(adap, msgs, num)
→ __i2c_check_suspended()
→ i2c_check_for_quirks() /* adap->quirks */
→ adap->algo->master_xfer() /* rk3x: rk3x_i2c_xfer */
→ 失败 -EAGAIN 时按 adap->retries/timeout 重试
→ i2c_unlock_bus()

8.2 __i2c_transfer 细节

  • 要求 adap->algo->master_xfer 存在,否则 -EOPNOTSUPP
  • 原子上下文:i2c_in_atomic_xfer_mode() 且存在 master_xfer_atomic,则走原子路径(RK3x:rk3x_i2c_xfer_polling);
  • Tracepoint: i2c_transfer trace 启用时记录 read/write/reply;
  • 仲裁丢失: 返回 -EAGAIN 时在 adap->timeout 内重试。

8.3 Adapter Quirks(i2c_check_for_quirks

限制单次传输:max_num_msgsmax_read_lenmax_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
2
3
4
5
static const struct i2c_lock_operations i2c_adapter_lock_ops = {
.lock_bus = i2c_adapter_lock_bus, /* rt_mutex_lock_nested */
.trylock_bus = i2c_adapter_trylock_bus,
.unlock_bus = i2c_adapter_unlock_bus,
};

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-gpiosi2c_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() 时:

  1. 对列表中每个 7 位地址创建临时 client;
  2. i2c_default_probe() — SMBus Quick Write 或 Byte Read(避免损坏 EEPROM);
  3. 调用 driver->detect() 填充 i2c_board_info
  4. 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 属性

namemodalias — 支持 udev 冷插拔 modprobe

13.3 Debugfs

/sys/kernel/debug/i2c/i2c-N/ — 各 adapter 子目录(具体节点由主机驱动或 core 扩展添加)。


14. 设备树时序解析

i2c_parse_fw_timings(dev, &timings, use_defaults) 从 DT 读取:

  • clock-frequencyi2c-scl-falling-timei2c-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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
1. platform_driver probe (i2c-rk3x.c)
adap->algo->master_xfer = rk3x_i2c_xfer
i2c_add_numbered_adapter(&i2c->adap) // nr 来自 DT alias

2. i2c_register_adapter() [i2c-core-base.c]
of_i2c_register_devices(adap) [i2c-core-of.c]
→ 解析 i2cN 下子节点
→ i2c_new_client_device() [i2c-core-base.c]

3. i2c_device_probe() [i2c-core-base.c]
of_irq_get → client->irq
sensor i2c_driver->probe()

4. 传感器驱动读写
i2c_transfer(adap, msgs, 2)
→ rk3x_i2c_xfer() [i2c-rk3x.c 硬件寄存器]

关键配置: CONFIG_I2C=yCONFIG_I2C_RK3X=yCONFIG_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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 查看总线与设备
ls /sys/bus/i2c/devices/
cat /sys/bus/i2c/devices/i2c-*/name

# 查看 client 与驱动绑定
ls -l /sys/bus/i2c/devices/*-*

# 内核日志
dmesg | grep -iE 'i2c-core|i2c-[0-9]|rk3x'

# 启用 transfer 跟踪(需 CONFIG_EVENT_TRACING)
# echo 1 > /sys/kernel/debug/tracing/events/i2c/enable

# debugfs
ls /sys/kernel/debug/i2c/

常见问题定位:

现象 可能涉及 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 子系统的 中枢实现,涵盖:

  1. 子系统与总线注册postcore_initcall);
  2. Adapter/Client 设备模型 与 driver core 集成;
  3. i2c_transfer 统一传输入口 及锁、quirk、trace、重试;
  4. 总线恢复、Host Notify、DMA bounce buffer
  5. 驱动注册与遗留 detect、sysfs 动态设备、debugfs;
  6. OF/ACPI/SMBus 子模块的衔接调用点。

RK3588:所有 SoC 内置 I2C(i2c-rk3x)与 DT 声明的外设均 必须经过本文件;理解 i2c_register_adapterof_i2c_register_devicesi2c_new_client_devicei2c_transfer 链路是分析板级 I2C 问题的关键。


20. 推荐阅读顺序

  1. i2c-core-base.ci2c_register_adapteri2c_transferi2c_new_client_device
  2. i2c-core-of.c — RK3588 设备枚举
  3. busses/i2c-rk3x.c — 硬件 master_xfer
  4. include/linux/i2c.h — 公共 API 与结构体
  5. Documentation/i2c/ — 实例化设备、总线恢复说明

21. 相关文档


文档版本:基于 rk3588/kernel-6.1 源码树 drivers/i2c/i2c-core-base.c(约 2723 行)分析。

文章互动

阅读 --

留言

0 条留言

正在加载留言…