RK3588 kernel-6.1/drivers/i2c/i2c-mux.c 功能详细分析
1. 文档范围与说明
路径说明: 用户表述为「i2c-mux.c 目录」,实际为 单个源文件(I2C MUX 框架核心):
rk3588/kernel-6.1/drivers/i2c/i2c-mux.c(约 467 行)
编译关系: 由 CONFIG_I2C_MUX 控制,编译为模块或内置 i2c-mux.ko:
1 | obj-$(CONFIG_I2C_MUX) += i2c-mux.o |
具体硬件实现位于 drivers/i2c/muxes/(PCA954x、GPIO mux、pinctrl 等),均依赖本文件提供的 API。
文档保存路径: linuxDoc/drivers/i2c/
定位: 将 一条物理 I2C 总线 通过外部 MUX/开关/仲裁逻辑,抽象为 多条逻辑 i2c_adapter。每次传输前 select 通道,在 父 adapter 上执行 i2c_transfer / i2c_smbus_xfer,可选 deselect。支持 多级 mux(mux 下再挂 mux)。
与 RK3588 的关系: 车载/多摄等 RK3588 板级 DTS 中常见 i2c-mux { i2c@0 { ... }; i2c@1 { ... }; }(如 Maxim SerDes 方案),由 i2c-mux-pinctrl 等驱动调用本框架;SoC 内置 i2c-rk3x 仍为 父总线,子总线由本文件注册。
相关文档: 各具体 MUX 芯片驱动见 rk3588-kernel-6.1-drivers-i2c-muxes目录功能详细分析_zh.md。
2. 功能总览
| 模块 | 符号 | 作用 |
|---|---|---|
| 传输代理 | i2c_mux_master_xfer / __i2c_mux_master_xfer |
select → 父总线 i2c_transfer → deselect |
| SMBus 代理 | i2c_mux_smbus_xfer / __i2c_mux_smbus_xfer |
select → i2c_smbus_xfer → deselect |
| 功能查询 | i2c_mux_functionality |
继承父 adapter 的 functionality |
| 总线锁 | i2c_mux_lock_ops / i2c_parent_lock_ops |
协调 mux_lock 与 bus_lock,防死锁 |
| 根 adapter | i2c_root_adapter |
沿 device 树向上找最顶层 I2C adapter |
| 分配 core | i2c_mux_alloc |
分配 i2c_mux_core + 私有数据 + select/deselect |
| 注册子总线 | i2c_mux_add_adapter |
为每个 channel 创建子 i2c_adapter + DT 绑定 |
| 注销 | i2c_mux_del_adapters |
删除所有子 adapter 与 sysfs 链接 |
3. 架构示意
1 | ┌─────────────────────────────────┐ |
用户/内核视角:
1 | i2c-4 (rk3x 物理总线) |
4. 核心数据结构
4.1 struct i2c_mux_core(include/linux/i2c-mux.h)
| 字段 | 含义 |
|---|---|
parent |
上游 i2c_adapter(物理总线或上一级 mux 子总线) |
dev |
MUX 控制器对应的 struct device(常为 i2c_client->dev) |
select / deselect |
硬件相关,由 muxes/*.c 实现 |
mux_locked |
使用 I2C_MUX_LOCKED:子 adapter 自带锁语义 |
arbitrator |
I2C_MUX_ARBITRATOR:DT 子节点名 i2c-arb |
gate |
I2C_MUX_GATE:DT 子节点名 i2c-gate |
priv |
芯片驱动私有数据(i2c_mux_priv(muxc)) |
adapter[] |
已注册的子 adapter 指针数组 |
4.2 struct i2c_mux_priv(本文件内 static)
每个 通道 对应一个子 adapter 的私有数据:
1 | struct i2c_mux_priv { |
5. 传输路径详解
5.1 Master 传输(i2c_mux_master_xfer)
1 | ret = muxc->select(muxc, priv->chan_id); |
| 变体 | 使用条件 | 父总线调用 |
|---|---|---|
i2c_mux_master_xfer |
I2C_MUX_LOCKED |
i2c_transfer(含锁) |
__i2c_mux_master_xfer |
默认 | __i2c_transfer(调用方已持锁) |
子 adapter 的 master_xfer 在 i2c_mux_add_adapter() 中按父 adapter 是否提供 master_xfer / master_xfer_atomic 动态赋值。
5.2 SMBus 传输(i2c_mux_smbus_xfer)
逻辑与 master 相同,父侧为 i2c_smbus_xfer / __i2c_smbus_xfer。
RK3588 rk3x: 父 adapter 无 smbus_xfer,子 adapter 也不会 设置 smbus_xfer;子总线上的 SMBus 仍通过父的 master_xfer 模拟(与 i2c-core-smbus.c 一致)。
5.3 i2c_mux_functionality()
直接返回 parent->algo->functionality(parent),子总线能力与父总线一致(如 I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL)。
6. 总线锁与多级 MUX
6.1 问题
子 adapter 传输时会调用父 adapter 的 i2c_transfer,而 i2c_transfer 会再次对 子 adapter 加锁 → 若使用默认 i2c_adapter_lock_ops 会 重复加锁死锁。
6.2 两套 lock_ops
| 模式 | lock_ops |
行为 |
|---|---|---|
| 默认 | i2c_parent_lock_ops |
先锁 parent->mux_lock(嵌套深度 i2c_adapter_depth),再按需锁 parent->bus_lock |
I2C_MUX_LOCKED |
i2c_mux_lock_ops |
主要操作 parent->mux_lock;I2C_LOCK_ROOT_ADAPTER 时才锁根 bus_lock |
mux_lock 在 i2c_register_adapter()(i2c-core-base.c)中对每个 adapter 初始化。
6.3 多级 MUX
子 adapter 的 parent 可以是 上一级 mux 的子 adapter,形成:
1 | rk3x (i2c-4) → MUX-A chan0 (i2c-5) → MUX-B chan1 (i2c-6) → 传感器 |
i2c_root_adapter(dev) 向上遍历 i2c_parent_is_i2c_adapter() 直到最顶层物理 adapter。
i2c-core-base.c 中地址冲突检查也会 递归 mux 父子树(与 i2c-dev.c 类似)。
7. i2c_mux_alloc()
1 | struct i2c_mux_core *i2c_mux_alloc( |
- 使用
devm_kzalloc,与 MUX 设备生命周期绑定; adapter[]柔性数组,max_adapters为最大通道数;sizeof_priv > 0时,priv指向紧跟数组后的私有区(pca954x、gpio等状态)。
典型调用(pca954x):
1 | muxc = i2c_mux_alloc(adap, dev, PCA954X_MAX_NCHANS, sizeof(*data), 0, |
8. i2c_mux_add_adapter() — 注册子总线
1 | int i2c_mux_add_adapter(struct i2c_mux_core *muxc, |
8.1 主要步骤
- 分配
i2c_mux_priv,设置chan_id; - 根据父 algo 填充子
i2c_algorithm; - 设置子 adapter 名称:
"i2c-%d-mux (chan_id %d)"; - 继承父的
retries、timeout、quirks; - DT
of_node绑定(见下节); - ACPI:
acpi_preset_companion(若适用); i2c_add_numbered_adapter(force_nr != 0)或i2c_add_adapter;- 创建 sysfs 符号链接:
mux_device、channel-N; of_i2c_register_devices()— 在 子 adapter 注册时 自动枚举i2c@N下子设备。
8.2 Device Tree 节点匹配逻辑
当 muxc->dev->of_node 存在时:
| 条件 | 查找的子节点容器 |
|---|---|
I2C_MUX_ARBITRATOR |
i2c-arb |
I2C_MUX_GATE |
i2c-gate |
| 默认 | i2c-mux |
在容器下查找 reg == chan_id 的子节点(如 i2c@0 { reg = <0>; ... }),将其设为 priv->adap.dev.of_node。
若 i2c-mux 节点自身带 reg(旧式 DT),则回退到 MUX 设备节点本身。
RK3588 车载 DTS 示例(rk3588-vehicle-evb-maxim-max96722-dphy3.dtsi):
1 | i2c-mux { |
i2c_mux_add_adapter(muxc, 0, 0, 0) 将 i2c@0 绑到 chan 0 的子 adapter,随后 of_i2c_register_devices 创建 camera/serializer 等 client。
8.3 class 参数
用于遗留 class-based detect;若与祖先 adapter 的 class 冲突会 dev_err 警告。现代 RK 平台主要靠 DT + OF 匹配,通常传 0。
9. i2c_mux_del_adapters()
按注册逆序:
- 删除 sysfs 链接;
i2c_del_adapter(卸载子节点上所有 client);of_node_put;kfree(priv)。
MUX 驱动 remove 中必须调用,与 probe 中多次 i2c_mux_add_adapter 成对。
10. i2c_root_adapter()
1 | struct i2c_adapter *i2c_root_adapter(struct device *dev); |
从任意 dev 向上找 type == i2c_adapter_type 的祖先,再沿 i2c_parent_is_i2c_adapter 走到 最顶层 adapter。
用途: 时钟/电源、全局总线配置、部分驱动需要区分“物理根总线”与“mux 子总线”。
11. 与 I2C 子系统其它模块的关系
| 模块 | 关系 |
|---|---|
i2c-core-base.c |
i2c_add_adapter / i2c_del_adapter、mux_lock、i2c_adapter_depth |
i2c-core-of.c |
子 adapter 的 of_node 绑定后枚举子设备 |
i2c-core-smbus.c |
父有 smbus_xfer 时子 adapter 可透传 SMBus |
i2c-dev.c |
每个子 adapter 可有 /dev/i2c-N |
muxes/*.c |
实现 select/deselect,调用本框架 API |
12. 导出 API 一览
| 符号 | 说明 |
|---|---|
i2c_mux_alloc |
分配 mux core |
i2c_mux_add_adapter |
添加一个通道的子 adapter |
i2c_mux_del_adapters |
删除所有子 adapter |
i2c_root_adapter |
查找根物理 adapter |
头文件辅助:i2c_mux_priv()、I2C_MUX_* flags。
13. RK3588 配置与使用
13.1 Kconfig
1 | config I2C_MUX |
需 CONFIG_I2C_MUX=y/m,并启用具体驱动,例如:
CONFIG_I2C_MUX_PINCTRL— 引脚复用型(车载 SerDes 常见)CONFIG_I2C_MUX_PCA954x— NXP PCA954x/9548CONFIG_I2C_MUX_GPIO— GPIO 控制通道
(具体是否在 rockchip_linux_defconfig 中启用取决于产品方案,多摄/车载 defconfig 通常会打开相应项。)
13.2 典型拓扑
1 | &i2c4 { |
物理 i2c-rk3x 仅一条;摄像头、serializer 挂在 mux 子 adapter 对应的 DT 子树下,驱动 probe 时 client->adapter 为子总线号。
13.3 调试
1 | # 查看总线拓扑 |
14. 设计要点与注意事项
| 要点 | 说明 |
|---|---|
| 每传输切换通道 | select 在每次 i2c_transfer 前调用,有开销;deselect 可选(idle 功耗/总线释放) |
| 通道互斥 | 同一父总线上多子 adapter 共享物理线,mux_lock 保证并发安全 |
| 地址空间独立 | 不同 channel 可有相同 7 位地址(不同子 adapter) |
| 勿在 select 中长阻塞 | 阻塞会持有父总线锁,影响其它 channel |
DT reg 与 chan_id |
必须一致,否则 of_node 绑定错误、子设备无法枚举 |
15. 源码结构速查(约 467 行)
1 | L32-37 struct i2c_mux_priv |
16. 结论
i2c-mux.c 是 Linux I2C 多路复用子系统的核心框架(非具体芯片驱动):
- 将每个 MUX 通道 呈现为独立
i2c_adapter; - 在传输路径中插入
select/deselect钩子,复用父总线硬件驱动(RK3588 上为 rk3x); - 通过
mux_lock/ 专用 lock_ops 支持多级 mux 与并发安全; - 与 Device Tree 的
i2c-mux/i2c@N结构紧密配合,驱动of_i2c_register_devices枚举支路外设。
RK3588 多摄像头、SerDes 等复杂 I2C 拓扑依赖本文件 + muxes/ 驱动;分析板级 I2C 问题时需区分 物理总线号 与 mux 子总线号,并确认 chan_id 与 DTS reg 一致。
17. 推荐阅读顺序
i2c-mux.c—i2c_mux_add_adapter、i2c_mux_master_xferinclude/linux/i2c-mux.h— API 与 flagsmuxes/i2c-mux-pinctrl.c或i2c-mux-pca954x.c— select/deselect 实现i2c-core-of.c— 子 adapter 设备枚举- 板级 DTS 中
i2c-mux节点
18. 相关文档
rk3588-kernel-6.1-drivers-i2c-muxes目录功能详细分析_zh.mdrk3588-kernel-6.1-i2c-core-base.c功能详细分析_zh.mdrk3588-kernel-6.1-i2c-core-of.c功能详细分析_zh.mdrk3588-kernel-6.1-i2c-dev.c功能详细分析_zh.mdrk3588-kernel-6.1-drivers-i2c-busses目录功能详细分析_zh.md
文档版本:基于 rk3588/kernel-6.1 源码树 drivers/i2c/i2c-mux.c 分析。
正在加载留言…