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

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_lockbus_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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
┌─────────────────────────────────┐
│ i2c-mux.c(本文) │
│ i2c_mux_add_adapter() │
│ 子 adapter algo → mux_xfer │
└───────────────┬─────────────────┘
│ select/deselect 回调
┌───────────────▼─────────────────┐
│ muxes/i2c-mux-pca954x.c 等 │
│ 写 MUX 芯片寄存器 / GPIO / pinctrl │
└───────────────┬─────────────────┘

┌───────────────▼─────────────────┐
│ 父 i2c_adapter (如 i2c-4, rk3x) │
└───────────────┬─────────────────┘

SDA / SCL 物理总线

用户/内核视角:

1
2
3
4
5
i2c-4 (rk3x 物理总线)
└── MUX 设备 @0x70 (client 驱动,如 pca9548)
├── 子 adapter i2c-5 (chan 0) → cam0@31, ser@41 ...
├── 子 adapter i2c-6 (chan 1) → ...
└── ...

4. 核心数据结构

4.1 struct i2c_mux_coreinclude/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
2
3
4
5
6
struct i2c_mux_priv {
struct i2c_adapter adap; /* 对外暴露的子总线 */
struct i2c_algorithm algo; /* 动态填充 master_xfer / smbus_xfer */
struct i2c_mux_core *muxc;
u32 chan_id; /* 传给 select/deselect */
};

5. 传输路径详解

5.1 Master 传输(i2c_mux_master_xfer

1
2
3
4
5
ret = muxc->select(muxc, priv->chan_id);
if (ret >= 0)
ret = i2c_transfer(parent, msgs, num); /* 或 __i2c_transfer */
if (muxc->deselect)
muxc->deselect(muxc, priv->chan_id);
变体 使用条件 父总线调用
i2c_mux_master_xfer I2C_MUX_LOCKED i2c_transfer(含锁)
__i2c_mux_master_xfer 默认 __i2c_transfer(调用方已持锁)

子 adapter 的 master_xferi2c_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_lockI2C_LOCK_ROOT_ADAPTER 时才锁根 bus_lock

mux_locki2c_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
2
3
4
5
6
7
8
struct i2c_mux_core *i2c_mux_alloc(
struct i2c_adapter *parent,
struct device *dev,
int max_adapters,
int sizeof_priv,
u32 flags, /* I2C_MUX_LOCKED | ARBITRATOR | GATE */
int (*select)(...),
int (*deselect)(...));
  • 使用 devm_kzalloc,与 MUX 设备生命周期绑定;
  • adapter[] 柔性数组,max_adapters 为最大通道数;
  • sizeof_priv > 0 时,priv 指向紧跟数组后的私有区(pca954xgpio 等状态)。

典型调用(pca954x):

1
2
muxc = i2c_mux_alloc(adap, dev, PCA954X_MAX_NCHANS, sizeof(*data), 0,
pca954x_select_chan, pca954x_deselect_mux);

8. i2c_mux_add_adapter() — 注册子总线

1
2
3
int i2c_mux_add_adapter(struct i2c_mux_core *muxc,
u32 force_nr, u32 chan_id,
unsigned int class);

8.1 主要步骤

  1. 分配 i2c_mux_priv,设置 chan_id
  2. 根据父 algo 填充子 i2c_algorithm
  3. 设置子 adapter 名称:"i2c-%d-mux (chan_id %d)"
  4. 继承父的 retriestimeoutquirks
  5. DT of_node 绑定(见下节);
  6. ACPI: acpi_preset_companion(若适用);
  7. i2c_add_numbered_adapterforce_nr != 0)或 i2c_add_adapter
  8. 创建 sysfs 符号链接:mux_devicechannel-N
  9. 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
2
3
4
5
6
7
8
9
10
11
12
13
14
i2c-mux {
#address-cells = <1>;
#size-cells = <0>;

i2c@0 {
reg = <0>;
max9295@41 { ... };
ov2311@31 { ... };
};
i2c@1 {
reg = <1>;
...
};
};

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_adaptermux_locki2c_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
2
config I2C_MUX
tristate "I2C bus multiplexing support"

CONFIG_I2C_MUX=y/m,并启用具体驱动,例如:

  • CONFIG_I2C_MUX_PINCTRL — 引脚复用型(车载 SerDes 常见)
  • CONFIG_I2C_MUX_PCA954x — NXP PCA954x/9548
  • CONFIG_I2C_MUX_GPIO — GPIO 控制通道

(具体是否在 rockchip_linux_defconfig 中启用取决于产品方案,多摄/车载 defconfig 通常会打开相应项。)

13.2 典型拓扑

1
2
3
4
5
6
7
&i2c4 {
status = "okay";
maxim_serdes: max96722@29 {
...
i2c-mux { ... }; /* 逻辑子总线在 SerDes 驱动/子节点下 */
};
};

物理 i2c-rk3x 仅一条;摄像头、serializer 挂在 mux 子 adapter 对应的 DT 子树下,驱动 probe 时 client->adapter 为子总线号。

13.3 调试

1
2
3
4
5
6
7
8
9
10
# 查看总线拓扑
ls /sys/bus/i2c/devices/
i2cdetect -l

# 查看 mux 与子通道链接
ls -l /sys/bus/i2c/devices/i2c-*/mux_device
ls -l /sys/bus/i2c/devices/*/channel-*

# 对子总线探测(N 为子 adapter 号)
i2cdetect -y N

14. 设计要点与注意事项

要点 说明
每传输切换通道 select 在每次 i2c_transfer 前调用,有开销;deselect 可选(idle 功耗/总线释放)
通道互斥 同一父总线上多子 adapter 共享物理线,mux_lock 保证并发安全
地址空间独立 不同 channel 可有相同 7 位地址(不同子 adapter)
勿在 select 中长阻塞 阻塞会持有父总线锁,影响其它 channel
DT regchan_id 必须一致,否则 of_node 绑定错误、子设备无法枚举

15. 源码结构速查(约 467 行)

1
2
3
4
5
6
7
8
L32-37   struct i2c_mux_priv
L39-119 mux master/smbus xfer(locked / unlocked)
L122-141 functionality / parent_classes
L143-211 lock_ops(mux / parent)
L213-237 i2c_root_adapter
L239-269 i2c_mux_alloc
L283-438 i2c_mux_add_adapter(含 DT/ACPI/sysfs)
L441-461 i2c_mux_del_adapters

16. 结论

i2c-mux.c 是 Linux I2C 多路复用子系统的核心框架(非具体芯片驱动):

  1. 将每个 MUX 通道 呈现为独立 i2c_adapter
  2. 在传输路径中插入 select / deselect 钩子,复用父总线硬件驱动(RK3588 上为 rk3x);
  3. 通过 mux_lock / 专用 lock_ops 支持多级 mux 与并发安全;
  4. Device Treei2c-mux / i2c@N 结构紧密配合,驱动 of_i2c_register_devices 枚举支路外设。

RK3588 多摄像头、SerDes 等复杂 I2C 拓扑依赖本文件 + muxes/ 驱动;分析板级 I2C 问题时需区分 物理总线号mux 子总线号,并确认 chan_id 与 DTS reg 一致


17. 推荐阅读顺序

  1. i2c-mux.ci2c_mux_add_adapteri2c_mux_master_xfer
  2. include/linux/i2c-mux.h — API 与 flags
  3. muxes/i2c-mux-pinctrl.ci2c-mux-pca954x.c — select/deselect 实现
  4. i2c-core-of.c — 子 adapter 设备枚举
  5. 板级 DTS 中 i2c-mux 节点

18. 相关文档


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

文章互动

阅读 --

留言

0 条留言

正在加载留言…