在 Linux I2C 子系统中,总线驱动 (Bus Driver) 或 适配器驱动 (Adapter Driver) 指的是 SoC 内部 I2C 控制器的驱动程序。它负责操作底层的硬件寄存器,产生 I2C 协议所需的时序(Start, Stop, ACK, Data),从而实现数据的物理传输。
对于 RK3566 平台,使用的是 Rockchip 自定义的 I2C 控制器 IP。
1. 驱动文件位置
Rockchip I2C 控制器驱动通常位于内核源码的: drivers/i2c/busses/i2c-rk3x.c
该驱动兼容多个 Rockchip SoC 系列(如 RK3399, RK3568, RK3566 等)。
2. 关键结构体
2.1 i2c_adapter
struct i2c_adapter 是 Linux 内核中描述一个物理 I2C 控制器的核心结构体。
struct i2c_adapter {
struct module *owner;
unsigned int class; // 允许哪些类型的设备探测
const struct i2c_algorithm *algo; // 算法(通信方法)
void *algo_data;
struct device dev; // 对应的 device 结构
int nr; // 适配器编号 (i2c-0, i2c-1...)
char name[48];
// ...
};2
3
4
5
6
7
8
9
10
11
12
2.2 i2c_algorithm
struct i2c_algorithm 定义了该适配器如何传输数据。
struct i2c_algorithm {
// 核心传输函数:完成一系列 i2c_msg 的传输
int (*master_xfer)(struct i2c_adapter *adap, struct i2c_msg *msgs,
int num);
// SMBus 协议支持(可选,如果不支持则用 master_xfer 模拟)
int (*smbus_xfer)(struct i2c_adapter *adap, u16 addr,
unsigned short flags, char read_write,
u8 command, int size, union i2c_smbus_data *data);
// 返回适配器支持的功能标志 (I2C_FUNC_I2C, I2C_FUNC_SMBUS_...)
u32 (*functionality)(struct i2c_adapter *);
};2
3
4
5
6
7
8
9
10
11
12
13
对于 i2c-rk3x.c,核心就是实现了 master_xfer 接口。
3. 驱动初始化流程
Rockchip I2C 驱动是一个标准的 Platform Driver。
设备树匹配: 驱动定义了
of_device_id表,匹配设备树中的compatible属性(如"rockchip,rk3399-i2c","rockchip,rk3568-i2c")。Probe 函数 (
rk3x_i2c_probe):- 获取资源:从设备树获取寄存器基地址 (
devm_platform_get_and_ioremap_resource)、中断号 (platform_get_irq)、时钟 (devm_clk_get)。 - 初始化结构体:分配并初始化
struct rk3x_i2c(私有数据)和struct i2c_adapter。 - 设置 Algorithm:将
algo指向实现了rk3x_i2c_xfer和rk3x_i2c_func的结构体。 - 注册中断处理函数:
devm_request_irq。Rockchip I2C 通常使用中断驱动的数据传输。 - 注册适配器:调用
i2c_add_adapter向核心层注册。
- 获取资源:从设备树获取寄存器基地址 (
4. 数据传输流程 (master_xfer)
当用户或设备驱动调用 i2c_transfer 时,最终会调用到 rk3x_i2c_xfer。
- 启动传输:驱动配置寄存器,发送 START 信号和从机地址。
- 中断处理:
- 当硬件产生中断(如发送完成、接收满、错误等),进入中断处理函数
rk3x_i2c_irq。 - 在中断中根据当前状态机(State Machine)读取或写入 FIFO,更新传输计数。
- 如果发生错误(如 NACK),记录错误并停止。
- 当硬件产生中断(如发送完成、接收满、错误等),进入中断处理函数
- 等待完成:
rk3x_i2c_xfer通常使用wait_for_completion_timeout睡眠等待传输完成信号。 - 返回结果:传输成功返回消息数量,失败返回错误码。
5. 设备树配置
在 rk356x.dtsi 中定义了 I2C 控制器节点:
i2c0: i2c@fdd40000 {
compatible = "rockchip,rk3568-i2c", "rockchip,rk3399-i2c";
reg = <0x0 0xfdd40000 0x0 0x1000>;
interrupts = <GIC_SPI 36 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&cru CLK_I2C0>, <&cru PCLK_I2C0>;
clock-names = "i2c", "pclk";
pinctrl-names = "default";
pinctrl-0 = <&i2c0_xfer>;
#address-cells = <1>;
#size-cells = <0>;
status = "disabled";
};2
3
4
5
6
7
8
9
10
11
12
在板级 dts 文件中启用:
&i2c0 {
status = "okay";
i2c-scl-rising-time-ns = <160>;
i2c-scl-falling-time-ns = <30>;
// 挂载子设备...
};2
3
4
5
6
7
i2c-scl-rising-time-ns/falling-time-ns:用于计算时钟分频系数,确保时序符合标准。
6. 总结
I2C 总线驱动对上层屏蔽了硬件细节。设备驱动开发者通常不需要修改这部分代码,除非在调试底层时序问题或移植新 SoC 时。理解其工作原理有助于分析 I2C 通信故障(如 Timeout, -EREMOTEIO 等错误)。