4. RK Sensor I2C 驱动框架:Linux I2C 子系统、设备树绑定、probe 流程、regmap 接口

在瑞芯微平台上开发 Sensor 驱动,核心是理解 Linux I2C 子系统的分层架构,以及如何通过设备树描述硬件、利用 probe 机制完成驱动初始化、借助 regmap 简化寄存器访问。本章将拆解这四个关键环节,并给出 RK 平台上的典型实现模式。

4.1 Linux I2C 子系统三层架构

Linux I2C 子系统自下而上分为三层:I2C 控制器驱动层、I2C 核心层、I2C 设备驱动层。Sensor 驱动属于最上层的设备驱动,通过核心层提供的 API 与底层控制器交互。

关键理解:Sensor 驱动开发者只需关注设备驱动层,调用 i2c_transfer()、i2c_smbus_read_byte_data() 等标准接口,无需关心 I2C 控制器是 RK3399 的、RK3588 的还是其他 SoC 的。
Linux I2C 子系统三层架构 I2C 设备驱动层 Sensor 驱动 (accel/gyro/mag/light) I2C 核心层 i2c-core / i2c-dev / 算法与适配器管理 I2C 控制器驱动层 i2c-rk3x (RK平台) / i2c-designware / i2c-s3c2410 Sensor 驱动开发范围:仅需实现 I2C 设备驱动层,调用核心层 API 完成通信

4.2 设备树绑定:描述 Sensor 硬件拓扑

在 RK 平台,Sensor 的硬件连接信息通过设备树(Device Tree)描述。设备树节点需包含:compatible(匹配驱动)、reg(I2C 从设备地址)、中断引脚(若使用中断)、电源控制(上电时序相关)。

属性 说明 示例值
compatible 驱动匹配字符串,格式 "厂商,型号" "bosch,bma423"
reg I2C 从设备 7 位地址 0x18
interrupt-parent 中断控制器 phandle &gpio1
interrupts 中断号与触发类型 <RK_PB2 IRQ_TYPE_EDGE_RISING>
vdd-supply 数字电源 regulator &vcc_3v3
vddio-supply IO 电源 regulator &vcc_1v8

典型设备树节点示例:

&i2c1 {
    status = "okay";
    clock-frequency = <400000>;  /* 400kHz 快速模式 */

    bma423: accelerometer@18 {
        compatible = "bosch,bma423";
        reg = <0x18>;
        interrupt-parent = <&gpio1>;
        interrupts = <RK_PB2 IRQ_TYPE_EDGE_RISING>;
        vdd-supply = <&vcc_3v3>;
        vddio-supply = <&vcc_1v8>;
        mount-matrix = "0", "1", "0",
                       "-1", "0", "0",
                       "0", "0", "1";
    };
};
RK 平台提示:RK 的 I2C 控制器驱动 i2c-rk3x 支持标准模式(100kHz)和快速模式(400kHz)。Sensor 通常使用 400kHz,但若 Sensor 不支持快速模式,需在设备树中降低 clock-frequency。

4.3 probe 流程:从设备树到驱动实例

当内核启动时,I2C 核心层会遍历设备树中每个 I2C 子节点,与已注册的 I2C 驱动进行 compatible 匹配。匹配成功后,调用驱动的 probe 函数。以下是 RK Sensor 驱动 probe 的典型流程:

  1. 获取设备树资源:读取 reg、interrupts、电源 regulator 等属性。
  2. 电源上电与复位:通过 regulator 框架使能 vdd/vddio,等待 Sensor 稳定(通常 1-10ms)。
  3. I2C 通信验证:读取 Sensor 的 CHIP_ID 寄存器,确认通信正常。
  4. 中断申请:若使用中断,调用 devm_request_threaded_irq() 注册中断处理函数。
  5. 初始化 Sensor 配置:设置量程、采样率、工作模式等寄存器。
  6. 注册输入子系统 / IIO 设备:将 Sensor 注册到内核框架,供用户空间访问。
RK Sensor I2C 驱动 probe 流程 1. 获取设备树资源 2. 电源上电与复位 3. I2C 通信验证 (读 CHIP_ID) 4. 中断申请 (若使用) 5. 初始化 Sensor 配置 6. 注册输入子系统 / IIO 设备 步骤 2-3 是 I2C 通信与上电时序的关键检查点,常见问题多发生在此阶段

4.4 regmap 接口:简化 I2C 寄存器访问

regmap 是 Linux 内核提供的一套通用寄存器映射框架,封装了 I2C、SPI、MMIO 等总线访问细节。在 RK Sensor 驱动中使用 regmap,可以显著减少样板代码,并自动获得缓存、批量读写、调试文件系统导出等能力。

使用 regmap 的典型步骤:

  1. 定义 regmap_config 结构体,指定寄存器位宽、读写标志等。
  2. 调用 devm_regmap_init_i2c() 创建 regmap 实例。
  3. 使用 regmap_read() / regmap_write() / regmap_update_bits() 访问寄存器。

代码示例:

static const struct regmap_config bma423_regmap_config = {
    .reg_bits = 8,          /* 寄存器地址 8 位 */
    .val_bits = 8,          /* 寄存器值 8 位 */
    .read_flag_mask = 0x80, /* 某些 Sensor 读操作需置位最高位 */
    .max_register = 0x7F,   /* 最大寄存器地址 */
};

static int bma423_probe(struct i2c_client *client)
{
    struct regmap *regmap;
    unsigned int chip_id;
    int ret;

    /* 创建 regmap 实例 */
    regmap = devm_regmap_init_i2c(client, &bma423_regmap_config);
    if (IS_ERR(regmap)) {
        dev_err(&client->dev, "regmap init failed\n");
        return PTR_ERR(regmap);
    }

    /* 读取 CHIP_ID 寄存器验证通信 */
    ret = regmap_read(regmap, BMA423_CHIP_ID_REG, &chip_id);
    if (ret) {
        dev_err(&client->dev, "failed to read chip id: %d\n", ret);
        return ret;
    }

    if (chip_id != BMA423_CHIP_ID_VALUE) {
        dev_err(&client->dev, "chip id mismatch: 0x%02x\n", chip_id);
        return -ENODEV;
    }

    /* 使用 regmap_update_bits 修改特定位 */
    regmap_update_bits(regmap, BMA423_PWR_CTRL_REG,
                       BMA423_PWR_EN_MASK, BMA423_PWR_ENABLE);

    /* 后续初始化... */
    return 0;
}
regmap 优势总结:
  • 自动处理 I2C 读写的协议细节(如重复起始条件)。
  • 支持缓存,减少不必要的 I2C 总线访问。
  • 通过 debugfs 导出所有寄存器,方便调试。
  • 驱动代码可在 I2C 和 SPI 之间轻松移植(只需修改 regmap 初始化函数)。

4.5 框架对比:传统 I2C 接口 vs regmap 接口

对比维度 传统 i2c_transfer / i2c_smbus regmap 接口
代码量 需手动构造 msg 数组,代码冗长 一行 regmap_read/write 完成
缓存支持 无,每次读写都发起总线事务 支持 volatile 与 cache 区分
调试能力 需手动添加 dev_dbg 打印 自动导出 /sys/kernel/debug/regmap
总线抽象 强依赖 I2C 总线 可切换 I2C/SPI/MMIO 不改核心逻辑
批量读写 需自行实现循环 regmap_bulk_read/write 原生支持
RK 平台推荐 仅用于简单验证 生产驱动首选
注意事项:部分 Sensor 的 I2C 协议有特殊要求(如读操作前需先写地址、或使用重复起始条件)。regmap 的 read_flag_mask 和 write_flag_mask 可满足大多数场景,但若遇到非标准协议,仍需回退到 i2c_transfer 手动构造消息。

4.6 RK 平台 I2C 驱动调试要点

  • 确认 I2C 控制器时钟已使能:RK 平台 I2C 控制器时钟由 CLK 框架管理,若时钟未使能,I2C 传输会超时。
  • 检查 I2C 引脚复用:通过 pinctrl 确认 I2C 的 SDA/SCL 引脚已正确配置为 I2C 功能,而非 GPIO。
  • 使用 i2c-detect 工具:在用户空间运行 i2c-detect -y <bus> 确认 Sensor 是否在预期地址响应。
  • 开启 I2C 调试日志:动态调试 i2c-core 或 i2c-rk3x 驱动,观察每次传输的细节。
  • regmap 调试:挂载 debugfs 后查看 /sys/kernel/debug/regmap/<设备名>/registers 确认寄存器值。
RK Sensor I2C 驱动调试工具链 i2c-detect i2cget/i2cset debugfs regmap 动态调试 定位 I2C 通信问题