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

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

1. 文档范围

本文分析目录:

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

核心定位: algos/ 提供 I2C 传输算法(struct i2c_algorithm 的软件实现,本身不直接操作具体 SoC 硬件。各 bus 控制器驱动busses/)或 GPIO I2C 驱动 负责提供底层读写 SDA/SCL 或桥接芯片寄存器的回调,再调用 i2c_bit_add_bus() / i2c_pca_add_bus() / i2c_pcf_add_bus() 将算法挂到 i2c_adapter 上。


2. 在 I2C 子系统中的位置

1
2
3
4
5
6
7
8
9
10
11
12
13
i2c-core (i2c_transfer)


adapter->algo->master_xfer() ← algos/ 目录实现

├── i2c-algo-bit.c (软件位带 START/STOP/字节/ACK)
├── i2c-algo-pca.c (PCA9564/9665 状态机)
└── i2c-algo-pcf.c (PCF8584 状态机)


busses/*.c 提供的底层回调
例: i2c-gpio.c → setsda/setscl/getsda/getscl
i2c-pca-platform.c → read_byte/write_byte/wait_for_completion

RK3588 内置 I2Ci2c-rk3x.c)的关系:

对比项 algos/ i2c-rk3x.c
角色 通用 算法库 硬件主机控制器
master_xfer bit/pca/pcf 软件实现 rk3x_i2c_xfer 寄存器+中断
RK3588 默认 通常不启用(有硬件 I2C) 主要路径
可选场景 CONFIG_I2C_GPIO + I2C_ALGOBIT 用 GPIO 模拟 I2C 所有 i2c0i2c7

3. 目录与构建

3.1 文件列表

文件 说明
i2c-algo-bit.c 位带(bit-banging)算法,最常用
i2c-algo-pca.c NXP PCA9564 / PCA9665 并行总线桥算法
i2c-algo-pcf.c Philips PCF8584 算法
i2c-algo-pcf.h PCF8584 寄存器位定义(内部头文件)
Makefile / Kconfig 三模块独立编译选项

3.2 Kconfig

选项 模块 功能
I2C_ALGOBIT i2c-algo-bit.ko GPIO/并口等位带 I2C
I2C_ALGOPCA i2c-algo-pca.ko PCA9564/9665 桥
I2C_ALGOPCF i2c-algo-pcf.ko PCF8584 桥

顶层 drivers/i2c/KconfigI2C_HELPER_AUTO(默认 y)会在需要时自动选中对应 algo,无需手动配置。

menu "I2C Algorithms" 仅在 !I2C_HELPER_AUTO 时对用户可见。


4. 算法层设计模式(共性)

三类算法均遵循同一模式:

  1. Bus 驱动 分配 i2c_adapter,在 algo_data 中填入硬件相关结构体;
  2. 调用 i2c_*_add_bus() / i2c_*_add_numbered_bus()
  3. Add 函数内设置 adap->algo = &xxx_algo,再 i2c_add_adapter()
  4. Core 调用 adap->algo->master_xfer() 完成传输。

对外导出的符号(供 bus 驱动链接):

算法 导出接口 算法结构体
bit i2c_bit_add_bus, i2c_bit_add_numbered_bus i2c_bit_algo
pca i2c_pca_add_bus, i2c_pca_add_numbered_bus 内部 pca_algo
pcf i2c_pcf_add_bus 内部 pcf_algo

5. i2c-algo-bit.c — 软件位带算法(核心)

5.1 适用场景

  • 无专用 I2C 硬件 IP,仅用 GPIO 或并口线模拟 SDA/SCL;
  • 老平台、FPGA、扩展板、调试用 bit-bang;
  • RK3588 上对应驱动:busses/i2c-gpio.ccompatible = "i2c-gpio")。

5.2 底层回调结构(include/linux/i2c-algo-bit.h

1
2
3
4
5
6
7
8
9
10
11
12
struct i2c_algo_bit_data {
void *data;
void (*setsda)(void *data, int state);
void (*setscl)(void *data, int state);
int (*getsda)(void *data);
int (*getscl)(void *data); /* 可选,NULL 则无法检测 clock stretch */
int (*pre_xfer)(struct i2c_adapter *);
void (*post_xfer)(struct i2c_adapter *);
int udelay; /* 半时钟周期延时 (us) */
int timeout; /* SCL 拉高等待 (jiffies) */
bool can_do_atomic;
};

Bus 驱动只需实现 拉高/拉低/读取 SDA/SCL;时序由 algo 统一完成。

5.3 时序原语

函数 I2C 语义
i2c_start() START:SDA 高→低,SCL 高→低
i2c_repstart() Repeated START
i2c_stop() STOP:SCL 高,SDA 低→高
i2c_outb() 发送 8 bit + 读 ACK
i2c_inb() 接收 8 bit
sclhi() SCL 置高并等待(支持 clock stretching 检测)

udelay 决定速率:例如 udelay=5 约对应标准模式量级(需结合上升沿实测)。

5.4 传输主流程 bit_xfer()

1
2
3
4
5
6
7
8
pre_xfer (可选)
→ i2c_start()
→ 对每条 i2c_msg:
若非 I2C_M_NOSTART: bit_doAddress() (7/10 位地址 + 重试)
若 I2C_M_RD: readbytes() + ACK/NAK
否则: sendbytes()
→ i2c_stop()
post_xfer (可选)

支持特性bit_func()):

  • 标准 I2C、SMBus 模拟(含 block read I2C_M_RECV_LEN);
  • I2C_M_NOSTARTI2C_M_STOPI2C_M_IGNORE_NAK
  • 10 位地址 I2C_M_TEN
  • master_xfer_atomic(需 can_do_atomic)。

5.5 总线自检 test_bus()

i2c_bit_add_bus() 时若 bit_test 模块参数非 0,会:

  • 检查 SDA/SCL 是否空闲;
  • 拉低/拉高 SDA、SCL 验证线路未 stuck;
  • bit_test=2 时失败则拒绝注册 adapter。

5.6 依赖本算法的 bus 驱动(节选)

驱动 平台/用途
i2c-gpio.c GPIO 模拟 I2C(与嵌入式最相关
i2c-parport.c 并口
i2c-acorn.c Acorn
i2c-hydra.c Mac Hydra
i2c-via.c VIA 芯片组
i2c-simtec.c Simtec 板
i2c-versatile.c ARM Versatile

5.7 RK3588 上的 i2c-gpio 示例

i2c-gpio.c 通过 GPIO 子系统获取 sda/scl,填入 i2c_algo_bit_data,最后:

1
ret = i2c_bit_add_numbered_bus(adap);

设备树典型节点:

1
2
3
4
5
6
7
8
i2c_gpio: i2c-gpio {
compatible = "i2c-gpio";
gpios = <&gpio SDA GPIO_ACTIVE_HIGH>,
<&gpio SCL GPIO_ACTIVE_HIGH>;
i2c-gpio,delay-us = <5>;
#address-cells = <1>;
#size-cells = <0>;
};

RK3588 默认 defconfig 通常不启用 I2C_GPIO,仅在缺硬件通道或特殊板级需要时打开。


6. i2c-algo-pca.c — PCA9564/PCA9665 算法

6.1 适用场景

NXP PCA9564 / PCA9665I2C 转并行总线 的桥接芯片:主机通过访问桥上的寄存器,由芯片硬件状态机产生 SDA/SCL 波形。

6.2 底层回调(include/linux/i2c-algo-pca.h

1
2
3
4
5
6
7
8
9
10
struct i2c_algo_pca_data {
void *data;
void (*write_byte)(void *data, int reg, int val);
int (*read_byte)(void *data, int reg);
int (*wait_for_completion)(void *data); /* 等待 SI 中断 */
void (*reset_chip)(void *data);
unsigned int i2c_clock;
unsigned int chip; /* 9564 or 9665 */
struct pca_i2c_bus_settings bus_settings;
};

Bus 驱动(i2c-pca-platform.ci2c-pca-isa.c)负责 MMIO/ISA 端口读写与中断等待。

6.3 寄存器与芯片差异

寄存器 PCA9564 PCA9665
控制 I2C_PCA_CON (STA/STO/SI/ENSIO) 间接寄存器 I2C_PCA_INDPTR + I2C_PCA_IND
数据 I2C_PCA_DAT 同上
状态 I2C_PCA_STA (类似 0xF8 idle) 状态机码相同风格

pca_probe_chip():写 I2C_PCA_IADR 探测是否为 9665,否则按 9564 处理。

6.4 传输状态机 pca_xfer()

基于 状态寄存器值(如 0xF8 idle、0x08 START 已发、0x18 SLA+W ACK)驱动:

1
2
3
4
5
6
等待 bus idle (0xF8)
→ pca_start / pca_repeated_start
→ pca_address (写 DAT + 清 SI)
→ 写路径: pca_tx_byte 循环
→ 读路径: pca_rx_ack + pca_rx_byte
→ pca_stop

错误处理:0x20/0x30/0x38 NAK/仲裁丢失、0x70/0x90 SDA/SCL stuck 时 pca_reset()

6.5 时钟配置

  • PCA9564:8 档固定频率(330kHz~36kHz),i2c_clock 可为 Hz 或档位索引;
  • PCA9665:按目标 Hz 计算 tlow/thi,支持 Standard / Fast / Fast+ / Turbo 模式。

6.6 能力

pca_func()I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL(无 10bit、无 NOSTART 等 bit 算法的灵活标志)。


7. i2c-algo-pcf.c — PCF8584 算法

7.1 适用场景

Philips PCF8584 为较老的 I2C 总线控制器(并行接口),用于 ISA/定制总线扩展。现多见于 复古硬件(如 i2c-elektor.ci2c-icy.c)。

7.2 底层回调(include/linux/i2c-algo-pcf.h

1
2
3
4
5
6
7
8
9
10
struct i2c_algo_pcf_data {
void *data;
void (*setpcf)(void *data, int ctl, int val); /* ctl=0 数据, 1 控制 */
int (*getpcf)(void *data, int ctl);
int (*getown)(void *data);
int (*getclock)(void *data);
void (*waitforpin)(void *data);
void (*xfer_begin/end)(void *data);
unsigned long lab_mdelay; /* 多主仲裁丢失后延时 */
};

7.3 控制/状态位(i2c-algo-pcf.h

含义
I2C_PCF_STA / STO START / STOP
I2C_PCF_BB Bus Busy
I2C_PCF_LRB Last Received Bit (ACK)
I2C_PCF_LAB Lost Arbitration

7.4 传输流程 pcf_xfer()

1
2
3
4
5
6
7
8
9
xfer_begin
→ wait_for_bb() /* 等待总线空闲 */
→ 每条 msg:
pcf_doAddress
i2c_start (首条)
wait_for_pin /* 等待 PIN 中断 */
写: pcf_sendbytes
读: pcf_readbytes (含 dummy read)
→ xfer_end

多主: wait_for_pin 返回 -EINTR 表示仲裁丢失,handle_lab() 复位 PCF 并 lab_mdelay 退避。

7.5 初始化 pcf_init_8584()

检测 PCF8584 是否存在(写 OWNADR/CLKREG 等),配置时钟与 ESO。

7.6 能力

I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL | I2C_FUNC_PROTOCOL_MANGLING(无 atomic xfer)。


8. 三种算法对比总表

特性 bit-bang PCA9564/9665 PCF8584
硬件形态 GPIO/并口线 I2C-Parallel 桥 并行 I2C 控制器
时序实现 软件 bit 操作 芯片状态机+主机轮询/中断 芯片状态机+PIN
Clock stretch 支持(需 getscl) 硬件处理 硬件处理
10 位地址 支持 不支持 不支持
SMBus block 支持(RECV_LEN) 模拟 模拟
多主仲裁 未完整实现 有(0x38) LAB + mdelay
典型年代 一直使用 2000s 工业板 1990s
RK3588 相关性 可选(GPIO I2C) 极低

9. 与 i2c-core 的衔接细节

9.1 谁调用算法

1
2
3
4
5
6
/* i2c-core-base.c */
int i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
{
...
ret = adap->algo->master_xfer(adap, msgs, num);
}

9.2 Quirks(bit 专用)

getscl == NULLi2c_bit_add_bus() 设置:

1
adap->quirks = &i2c_bit_quirk_no_clk_stretch;  /* I2C_AQ_NO_CLK_STRETCH */

告知上层该总线可能无法正确处理从设备拉长 SCL。

9.3 调试

  • CONFIG_I2C_DEBUG_ALGOccflags -DDEBUG(bit 中 bit_dbg);
  • bit 模块参数:bit_testi2c_debug
  • pca/pcf 模块参数:i2c_debug

10. RK3588 平台实践说明

10.1 默认路径

RK3588 使用 DesignWare 风格 Rockchip I2C IPi2c-rk3x.c),在 adapter 内直接实现 i2c_algorithm不经过 algos/

10.2 何时需要 algos

需求 配置建议
额外 GPIO 模拟 I2C CONFIG_I2C_GPIO=y + CONFIG_I2C_ALGOBIT=y(通常由 HELPER_AUTO 选中)
外接 PCA9564 并口桥 CONFIG_I2C_ALGOPCA + i2c-pca-platform
复古/特殊 PCF8584 板 CONFIG_I2C_ALGOPCF + 对应 bus 驱动

arch/arm64/configs/*rk3588* 中一般看不到 I2C_ALGOBIT;部分 RK 衍生配置(如 rv1126b_robot.config)会显式 CONFIG_I2C_ALGOBIT=y

10.3 与总线恢复的关系

i2c-core-base.ci2c_generic_scl_recovery() 在 adapter 有 bus_recovery_info 时,也可用 GPIO bit-bang 恢复卡死的 SDA/SCL;这与 i2c-algo-bit 思想相同,但是 core 内独立路径,不要求加载 i2c-algo-bit 模块。


11. 数据流示意(以 GPIO I2C 为例)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
传感器驱动 / i2c_transfer()


i2c-gpio adapter (i2c-10)


i2c_bit_algo.master_xfer ← i2c-algo-bit.c

├── setsda(0/1) ──► gpiod_set_value (SDA)
├── setscl(0/1) ──► gpiod_set_value (SCL)
└── getsda/getscl ──► gpiod_get_value


物理 SDA/SCL 引脚

12. 调试建议

问题 排查
GPIO I2C 无响应 检查 i2c-gpio,delay-us、上拉、开漏配置
Not I2C compliant: can't read SCL 未接 getscl,clock stretch 不可靠
test_bus 失败 SDA/SCL stuck,硬件短路或外设拉死
PCA 超时 0x?? wait_for_completion 中断未接;时钟配置错误
PCF Timeout waiting for BB 总线被占用或多主冲突
1
2
3
4
5
# 查看是否加载算法模块
lsmod | grep i2c_algo
# 查看 GPIO I2C adapter
ls /sys/bus/i2c/devices/ | grep mux
i2cdetect -y <bus>

13. 结论

drivers/i2c/algos 目录包含 3 个可加载的 I2C 算法模块,职责是:

  1. 实现统一的 i2c_algorithm.master_xfer
  2. 将 I2C 协议时序(START/STOP/字节/ACK)从 bus 驱动中解耦
  3. 通过 回调结构体 适配 GPIO、PCA 桥、PCF8584 等不同硬件形态。

对 RK3588 而言:

  • 主 I2Ci2c-rk3x,与 algos/ 无关
  • 扩展/备用 总线可通过 i2c-gpio + i2c-algo-bit 实现;
  • i2c-algo-pca / i2c-algo-pcf 主要用于特定外设桥与 legacy 硬件,产品树中较少出现。

理解 algos/ 有助于区分「硬件控制器驱动」与「纯软件/GPIO/桥片算法」两条 I2C 主机实现路径。


14. 推荐阅读顺序

  1. include/linux/i2c-algo-bit.h — 回调接口
  2. algos/i2c-algo-bit.cbit_xferi2c_bit_add_bus
  3. busses/i2c-gpio.c — RK 平台最可能用到的组合
  4. algos/i2c-algo-pca.c — 状态机桥接范例
  5. i2c-core-base.ci2c_transfer 入口

15. 相关文档


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

文章互动

阅读 --

留言

0 条留言

正在加载留言…