RK3588 kernel-6.1/drivers/i2c/muxes 目录功能详细分析

RK3588 kernel-6.1/drivers/i2c/muxes 目录功能详细分析

1. 文档范围

本文分析目录:

  • 源码:rk3588/kernel-6.1/drivers/i2c/muxes
  • 文档:linuxDoc/drivers/i2c/

前提: muxes/ 中的驱动都依赖上层 i2c-mux.c(位于 drivers/i2c/ 提供的多路复用框架。本文先说明框架机制,再逐项分析 muxes/ 下 9 个具体驱动/模块的功能、实现差异与 RK3588 使用场景。


2. 为什么需要 I2C Mux

2.1 问题场景

场景 说明
地址冲突 多条支路挂相同 7 位地址设备(如多个 EEPROM 0x50)
拓扑扩展 一条物理 I2C 需服务多组独立外设(摄像头 SerDes、传感器等)
引脚复用 SoC 同一 I2C 控制器 SDA/SCL 可路由到不同引脚组
多主仲裁 两个主机共享一条从总线,需互斥获取总线
多主机切换 运行时在不同物理 I2C 主控之间切换(demux)

2.2 内核抽象

Linux 不把 mux 做成“透明转发”,而是为每个通道(或逻辑段)注册独立的 i2c_adapter

1
2
3
4
5
6
7
8
物理 I2C 主控 (i2c-2, rk3x)


MUX 芯片 / GPIO / pinctrl / 寄存器

├── 子 adapter: i2c-2-mux (chan 0) → 设备 A、B
├── 子 adapter: i2c-3-mux (chan 1) → 设备 C
└── 子 adapter: i2c-4-mux (chan 2) → 设备 D

用户态可见多个 /dev/i2c-*;client 驱动挂在子 adapter 对应的 DT 子节点下。


3. 与 i2c-mux.c 核心的关系

muxes/ 不包含通用 mux 框架,只实现各硬件/机制的 select / deselect 回调

3.1 核心数据结构(include/linux/i2c-mux.h

1
2
3
4
5
6
7
8
9
struct i2c_mux_core {
struct i2c_adapter *parent; /* 上游物理/父 adapter */
struct device *dev;
int (*select)(struct i2c_mux_core *muxc, u32 chan);
int (*deselect)(struct i2c_mux_core *muxc, u32 chan);
int num_adapters;
int max_adapters;
/* ... flags: LOCKED, ARBITRATOR, GATE ... */
};

3.2 传输路径(i2c-mux.c

每次在子 adapter 上 i2c_transfer() 时:

1
2
3
4
i2c_mux_master_xfer()
→ muxc->select(muxc, chan_id) // muxes/*.c 实现
→ i2c_transfer(parent, msgs) // 在父总线上真正收发
→ muxc->deselect(muxc, chan_id) // 可选,恢复 idle
  • 支持 SMBus 透传(i2c_mux_smbus_xfer);
  • 支持 多级 mux(子 adapter 下再挂 mux);
  • 通过 parent->mux_locklock_ops 避免父子 adapter 重复加锁死锁。

3.3 驱动开发模板

典型 mux 芯片驱动流程:

1
2
3
4
probe
→ i2c_mux_alloc(parent, dev, nchans, priv_size, flags, select, deselect)
→ 对每个 channel: i2c_mux_add_adapter(muxc, force_nr, chan_id, class)
→ DT 子节点 reg = channel 号,core 在子 adapter 上注册 client

4. 目录与构建配置

4.1 文件列表

源文件 Kconfig 模块名
i2c-mux-pca954x.c I2C_MUX_PCA954x i2c-mux-pca954x
i2c-mux-pca9541.c I2C_MUX_PCA9541 i2c-mux-pca9541
i2c-mux-gpio.c I2C_MUX_GPIO i2c-mux-gpio
i2c-mux-pinctrl.c I2C_MUX_PINCTRL i2c-mux-pinctrl
i2c-mux-reg.c I2C_MUX_REG i2c-mux-reg
i2c-mux-gpmux.c I2C_MUX_GPMUX i2c-mux-gpmux
i2c-mux-ltc4306.c I2C_MUX_LTC4306 i2c-mux-ltc4306
i2c-mux-mlxcpld.c I2C_MUX_MLXCPLD i2c-mux-mlxcpld
i2c-demux-pinctrl.c I2C_DEMUX_PINCTRL i2c-demux-pinctrl
i2c-arb-gpio-challenge.c I2C_ARB_GPIO_CHALLENGE i2c-arb-gpio-challenge

依赖:全部需要 CONFIG_I2C_MUXi2c-mux.c 核心)。

4.2 分类概览

1
2
3
4
5
6
7
8
                  i2c-mux.c (核心框架)

┌──────────────────┼──────────────────┐
│ │ │
芯片型 MUX 控制型 MUX 特殊机制
│ │ │
pca954x, pca9541 gpio, reg, arb-gpio
ltc4306, mlxcpld pinctrl, gpmux demux-pinctrl

5. 各驱动功能详细分析

5.1 i2c-mux-pca954x.c — NXP PCA954x/PCA984x(最常用)

硬件: NXP I2C 多路复用器/开关,自身也是 I2C 从设备,通过写 8 位控制寄存器 选通通道。

支持型号:

类型 型号 通道数 行为
Mux PCA9540/42/44/45/47/9548 2~8 互斥,同一时刻仅一路
Switch PCA9543/46/9846~9849 2~8 多路同时 接通(位掩码)

核心实现:

函数 功能
pca954x_reg_write() __i2c_smbus_xfer() 写寄存器,避免 i2c_transfer 二次锁总线
pca954x_select_chan() 计算 regval,若与 last_chan 不同则写入
pca954x_deselect_mux() idle-state:保持 / 断开 / 切到指定通道
pca954x_irq_setup() 部分型号支持 INT,为每通道建 nested irq_domain
idle_state sysfs 运行时配置空闲时通道策略

设备树示例:

1
2
3
4
5
6
7
8
9
pca9548: mux@70 {
compatible = "nxp,pca9548";
reg = <0x70>;
#address-cells = <1>;
#size-cells = <0>;

i2c@0 { reg = <0>; /* 子设备 */ };
i2c@1 { reg = <1>; };
};

RK3588 关联: 车载/多摄板级 DTS 中常见 GPIO 位控制的 i2c-mux 节点(见 §8);若硬件使用 PCA9548,则绑定 nxp,pca9548 由本驱动管理。


5.2 i2c-mux-pca9541.c — NXP PCA9541 总线主选择器

硬件语义: 不是“多通道扩展”,而是 两个 I2C 主机争用一条从总线 的仲裁芯片。

功能要点:

  • 建模为 单通道 muxselect = 获取总线所有权,deselect = 释放;
  • 通过 PCA9541_CONTROL / PCA9541_ISTAT 寄存器协商;
  • 超时:ARB_TIMEOUT(125ms 强制)、ARB2_TIMEOUT(250ms 失败);
  • 读写寄存器使用 __i2c_smbus_xfer,同样避免重入锁。

适用: 双 CPU / 双 BMC 共享传感器总线;RK3588 普通嵌入式板较少使用。


5.3 i2c-mux-gpio.c — GPIO 控制的多路复用

硬件: 用 N 根 GPIO 编码选择 2^N 条 I2C 支路(板级 CPLD/逻辑电路)。

核心实现:

函数 功能
i2c_mux_gpio_set() gpiod_set_array_value_cansleep() 一次性设置所有 GPIO
i2c_mux_gpio_select() chan 作为 GPIO 位图写入
i2c_mux_gpio_deselect() 写入 idle-stateI2C_MUX_GPIO_NO_IDLE 表示不恢复)

设备树:

  • i2c-parent:phandle 指向父 adapter;
  • 子节点 reg = 该通道的 GPIO 编码值;
  • idle-state:空闲时 GPIO 值。

平台: platform_driver,支持 OF 与 ACPI。


5.4 i2c-mux-pinctrl.c — pinctrl 引脚复用型 Mux

场景: SoC 的 I2C 信号可通过 pinctrl 路由到不同引脚组,每组对应一条“逻辑总线”(如 I2C0 走 pinmux A 或 B)。

核心实现:

函数 功能
i2c_mux_pinctrl_select() pinctrl_select_state(pinctrl, states[chan])
i2c_mux_pinctrl_deselect() 切到 states[num_adapters](idle 状态)
i2c_mux_pinctrl_root_adapter() 校验各 state 是否指向同一 root adapter

设备树:

  • i2c-parent:父 adapter;
  • pinctrl-names / pinctrl-0…:每个 channel 对应一个 state;
  • 额外一个 idle state(pinctrl-names 最后一项)。

与 GPIO mux 区别: 切换的是 SoC 引脚复用,不是外部 mux 芯片。


5.5 i2c-mux-reg.c — MMIO 寄存器控制型 Mux

硬件: 板级用一个 内存映射寄存器 的若干位选择 I2C 路由(常见于 PowerPC/QorIQ 等)。

核心实现:

函数 功能
i2c_mux_reg_set() iowrite8/16/32 写通道号,posted bus 上 ioread 确保写完成
i2c_mux_reg_select/deselect() select 写 chan_id;deselect 可选写 idle

设备树: reg 指定寄存器物理地址;little-endian / big-endianidle-in-use + idle


5.6 i2c-mux-gpmux.c — 通用 MUX 子系统接口

设计: 不直接操作 GPIO/寄存器,而是调用 mux_control_select() / mux_control_deselect()(Linux multiplexer 子系统)。

核心实现:

1
2
mux_control_select(mux->control, chan);   // select
mux_control_deselect(mux->control); // deselect

设备树:

1
2
3
compatible = "i2c-mux";
i2c-parent = <&i2c2>;
/* control-mux 由 mux 子系统绑定 */

优点: 硬件选通逻辑可复用于 SPI/I2C 等其他子系统;抽象层次最高


5.7 i2c-mux-ltc4306.c — Analog LTC4305/4306

硬件: ADI I2C mux/switch,带 rise time accelerator、upstream/downstream 加速配置。

功能:

特性 说明
regmap LTC_REG_STATUS/CONFIG/MODE/SWITCH
LTC4306 4 通道 + 2 个可选 GPIO(gpio_chip 导出)
LTC4305 2 通道
select LTC_REG_SWITCH 选通通道

与 pca954x 类似: I2C 控制的专用 mux 芯片,适合长走线、电容负载大的场景。


5.8 i2c-mux-mlxcpld.c — Mellanox CPLD Mux

场景: Mellanox 网卡/服务器主板,CPLD 寄存器控制 I2C 路由。

核心实现:

函数 功能
mlxcpld_mux_reg_write() 1/2 字节寄存器写入选通值
mlxcpld_mux_select_chan() 仅当 last_val != regval 时写入,减少总线流量
平台数据 mlxcpld_mux_plat_data:寄存器地址、宽度

拓扑(驱动注释): i2c-mlxcpld 桥接 → CPLD mux 寄存器 → 多条虚拟 bus → 外设。

RK3588 产品树通常 不包含 此驱动。


5.9 i2c-demux-pinctrl.c — pinctrl 反多路复用(Demux)

与 mux 的本质区别:

对比项 Mux (i2c-mux-*.c) Demux (i2c-demux-pinctrl)
父总线数量 1 个 parent 多个 parent(≥2)
子 adapter 每个 channel 一个,同时存在 仅 1 个 当前 active adapter
切换方式 每次传输 select 通道 运行时 换主控 + OF overlay
用户接口 标准 i2c adapter 编号 sysfs current_master

工作流程:

1
2
3
4
5
6
用户写 sysfs current_master = N
→ i2c_demux_deactivate_master() // 删除旧 adapter
→ of_changeset_apply() // 应用 DT 变更
→ 获取新 parent adapter
→ pinctrl_select_state() // 切换引脚
→ i2c_add_adapter() // 注册新逻辑 adapter

sysfs:

  • available_masters:可读所有候选主控;
  • current_master:读写当前主控索引。

场景: 同一组引脚在“内部 I2C0”与“外部 I2C 扩展”之间动态切换(Renesas 等平台常见)。


5.10 i2c-arb-gpio-challenge.c — GPIO 挑战-应答仲裁

不是通道扩展,而是 多主机互斥

GPIO 作用
our-gpio 本方声明要占用总线
their-gpio 检测对方是否已占用

select 流程:

  1. 拉高 our-gpio
  2. 轮询 their-gpio 是否为低(对方释放);
  3. 超时则放弃并重试;
  4. 成功则返回 0,允许在 parent 上传输。

标志: i2c_mux_alloc(..., I2C_MUX_ARBITRATOR, ...)

场景: 两个主控(如 AP + MCU)共享一条 I2C,无专用 PCA9541 时的软件仲裁方案。


6. Mux 与 Switch 语义对比(以 PCA954x 为代表)

类型 寄存器写法 同时导通
Mux chan | enable 否,仅一路
Switch 1 << chan 是,位掩码可多 bit

驱动在 pca954x_regval() 中统一处理,对上层均暴露为独立 channel adapter。


7. 锁机制与嵌套 Mux

i2c-mux.c 提供两套 lock_ops

模式 行为
默认 子 adapter 锁 parent->mux_lock + 可选 root lock
I2C_MUX_LOCKED mux 自身持有段锁,防止 select 期间父总线被其他客户端抢占

多级 mux 示例:

1
2
3
4
5
i2c-2 (rk3x)
└── pca9548 @0x70
└── channel 2
└── pca9544 @0x71
└── channel 0 → 传感器

传输时依次 select 两级;i2c_adapter_depth() 用于嵌套锁层级。


8. RK3588 平台使用场景

8.1 内置 I2C 控制器

RK3588 自带多路 rockchip,rk3588-i2c不经过 muxes/ 目录驱动,由 i2c-rk3x.c 直接提供 adapter。

8.2 板级 DTS 中的 i2c-mux 节点

在 RK3588 车载/多摄相关 DTS 中大量出现 逻辑名 i2c-mux 的节点,例如:

  • rk3588-vehicle-evb-maxim-max96722-dphy0.dtsi
  • rk3588-vehicle-maxim-cameras-s66.dtsi

典型结构:

1
2
3
4
5
6
7
8
9
10
11
/* 父 I2C 上 */
i2c-mux {
#address-cells = <1>;
#size-cells = <0>;

i2c@0 {
reg = <0>;
/* Serializer / Camera 等 */
};
i2c@1 { reg = <1>; /* ... */ };
};

说明:

  • 节点名 i2c-muxDT 拓扑约定,表示其下每个 i2c@N 对应一条逻辑子总线;
  • 实际选通可能由 板级 GPIO、SerDes 内部逻辑或外部 PCA954x 完成;
  • compatible = "i2c-mux" 且使用 MUX 子系统,内核加载 i2c-mux-gpmux.c
  • 若为 nxp,pca9548 等,加载 i2c-mux-pca954x.c
  • 部分方案可能在 驱动/固件内部 完成通道切换,DT 仅描述拓扑,不绑定上述 mux 驱动之一。

8.3 RK3588 常用驱动优先级建议

硬件实现 推荐驱动
NXP PCA954x/984x 芯片 i2c-mux-pca954x
GPIO 选通 i2c-mux-gpio
SoC 引脚复用多组 I2C 走线 i2c-mux-pinctrl
绑定 mux-controller i2c-mux-gpmux
双主争用 i2c-mux-pca9541i2c-arb-gpio-challenge
运行时切换不同 I2C 主控 i2c-demux-pinctrl

9. 调试与排障

9.1 确认子 adapter

1
2
3
ls /sys/bus/i2c/devices/
# 可见 i2c-N-mux (chan_id M) 或独立编号
ls /dev/i2c-*

9.2 在正确通道上探测

1
2
# 应对子 adapter 编号,而非仅父 bus
i2cdetect -y <子adapter编号>

9.3 常见问题

现象 可能原因
子总线无设备 未 select 到正确 channel;reg 与硬件通道不一致
偶发 -EBUSY 多主仲裁失败(pca9541 / arb-gpio)
仅某 channel 失败 switch 型芯片被配成多通,电气冲突
probe defer i2c-parent phandle 对应 adapter 尚未注册
pca954x idle 异常 检查 idle-state sysfs / DT 属性

9.4 PCA954x 专用

1
2
3
# 查看/设置空闲通道策略(若 sysfs 存在)
cat /sys/.../idle_state
echo -2 > .../idle_state # MUX_IDLE_DISCONNECT

10. 功能对照总表

文件 控制手段 通道模型 典型平台
i2c-mux-pca954x.c I2C 寄存器 2~8 路 mux/switch 通用嵌入式、服务器
i2c-mux-pca9541.c I2C 寄存器 单通道仲裁 双主共享总线
i2c-mux-gpio.c GPIO 位编码 N 路 自定义 CPLD/逻辑
i2c-mux-pinctrl.c pinctrl state N 路引脚组 Tegra 等 SoC 引脚复用
i2c-mux-reg.c MMIO 寄存器 N 路 PowerPC/QorIQ 等
i2c-mux-gpmux.c mux 子系统 N 路 现代 DT 通用方案
i2c-mux-ltc4306.c I2C 寄存器 2/4 路 + 加速 长电缆/大电容负载
i2c-mux-mlxcpld.c CPLD I2C 寄存器 多路 Mellanox 网卡
i2c-demux-pinctrl.c pinctrl + OF 多主控选一 Renesas 等
i2c-arb-gpio-challenge.c GPIO 握手 单通道仲裁 双 GPIO 多主

11. 结论

drivers/i2c/muxes 目录提供 9 种 I2C 拓扑扩展/仲裁实现,均通过 i2c-mux.c 统一为:

  1. select:传输前选通通道或获取总线;
  2. deselect:传输后恢复 idle(可选);
  3. i2c_mux_add_adapter:为每个 channel 导出独立 i2c_adapter

对 RK3588 开发:

  • 物理层i2c-rk3x.c 提供父 adapter;
  • 拓扑层 根据硬件选用 pca954xgpiogpmuxpinctrl 等;
  • 车载多摄 DTSi2c-mux 节点描述子总线结构,需结合具体 compatible 与原理图确认实际加载的 mux 驱动。

12. 推荐阅读顺序

  1. drivers/i2c/i2c-mux.c — 框架与锁
  2. include/linux/i2c-mux.h — API
  3. muxes/i2c-mux-pca954x.c — 最典型芯片实现
  4. muxes/i2c-mux-gpio.ci2c-mux-gpmux.c — 板级常见方案
  5. Documentation/devicetree/bindings/i2c/ 下各 binding
  6. RK3588 板级 DTS 中带 i2c-mux 的 dtsi

13. 相关文档


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

文章互动

阅读 --

留言

0 条留言

正在加载留言…