3. I2C设备树绑定:通用I2C设备树节点结构、compatible属性、reg属性、地址配置

好,我们进入I2C设备树绑定的核心部分。说实话,I2C的设备树配置比GPIO要复杂一些,但只要你理解了它的骨架,剩下的就是填参数了。我个人习惯把I2C节点看作「挂在总线上的小设备」,每个设备都有自己的地址、驱动匹配名,以及一些私有配置。

3.1 通用I2C设备树节点结构

先看一个典型的I2C设备节点长什么样。嗯,这里我拿一个温度传感器来举例:

&i2c1 {
    status = "okay";
    clock-frequency = <100000>;

    temperature-sensor@48 {
        compatible = "ti,tmp102";
        reg = <0x48>;
        #address-cells = <1>;
        #size-cells = <0>;
    };
};

你注意看,整个结构分两层:

  • 父节点:I2C控制器本身(比如 i2c1),配置总线频率、状态等
  • 子节点:挂在总线上的具体设备,每个设备一个节点

我在项目中遇到过有人把子节点的属性写到父节点里,结果驱动死活不认设备。说白了,父节点管总线,子节点管设备,各司其职。

3.2 compatible属性——驱动匹配的关键

compatible 属性是设备树里最重要的属性之一。它的格式是 "厂商,型号",比如 "ti,tmp102"。内核驱动通过这个字符串来找到对应的驱动程序。

这里有个细节:compatible 可以写多个值,内核会按顺序匹配。比如:

compatible = "ti,tmp102", "nxp,lm75";

内核先尝试匹配 ti,tmp102,如果找不到驱动,再试 nxp,lm75。我曾经在一个项目里用这个技巧兼容了两款引脚兼容的传感器,省去了改板的麻烦。

我的建议:compatible 字符串一定要和驱动里的 of_match_table 完全一致,大小写、连字符都不能错。我见过有人把 "tmp102" 写成 "TMP102",结果驱动加载失败,查了半天才发现是大小写问题。

3.3 reg属性——I2C地址配置

reg 属性指定了设备的I2C从机地址。对于7位地址的I2C设备,写法很简单:

reg = <0x48>;

但这里有个坑——地址到底是7位还是10位?设备树里默认是7位地址,范围是0x03到0x77。如果你要用10位地址,需要额外配置 reg 的格式。

为什么地址范围从0x03开始?因为0x00到0x02是保留地址,用于特殊用途。我刚开始做I2C时,选了个0x00的地址,结果总线直接罢工,后来查手册才知道踩了雷区。

3.4 地址配置的注意事项

地址配置看似简单,但有几个地方容易翻车:

  • 地址冲突:同一总线上不能有两个设备用同一个地址。我遇到过客户把两个相同型号的传感器焊在同一总线上,结果地址冲突,数据全乱套了。
  • 地址偏移:有些芯片的地址是7位,但数据手册里写的是8位(左移了一位)。比如手册写0x90,实际设备树里要填0x48。嗯,这个坑我踩过两次,后来学乖了,每次都要确认是7位还是8位表示。
  • 地址位数:如果设备支持10位地址,需要在父节点里声明 #address-cells = <2>,然后子节点的 reg 写成 reg = <0x0 0x48>
避坑指南:我曾经在一个项目里用了某国产加速度传感器,数据手册写的地址是0x18,但实际设备树里填0x18就是不行。后来用逻辑分析仪抓波形才发现,芯片实际响应的是0x30(左移了一位)。所以,拿到新芯片,先用逻辑分析仪确认地址,别完全信手册。

3.5 其他常用属性

除了 compatiblereg,I2C设备节点还经常用到这些属性:

属性名 说明 示例
status 设备状态,常用 "okay" 或 "disabled" status = "okay";
interrupt-parent 中断控制器引用 interrupt-parent = &gpio1;
interrupts 中断号和触发方式 interrupts = <5 IRQ_TYPE_EDGE_FALLING>;
clock-frequency I2C总线频率(父节点配置) clock-frequency = <400000>;

你想想看,一个完整的I2C设备节点,其实就是把这些属性组合起来。比如带中断的触摸屏:

&i2c2 {
    status = "okay";
    clock-frequency = <400000>;

    touchscreen@38 {
        compatible = "goodix,gt911";
        reg = <0x38>;
        interrupt-parent = <&gpio3>;
        interrupts = <18 IRQ_TYPE_EDGE_FALLING>;
        reset-gpios = <&gpio3 19 GPIO_ACTIVE_LOW>;
        irq-gpios = <&gpio3 18 GPIO_ACTIVE_HIGH>;
    };
};

这里我加了 reset-gpiosirq-gpios,这是触摸屏驱动自己定义的私有属性。设备树的灵活性就在这里——你可以为特定设备添加任意属性,只要驱动能解析就行。

3.6 总结一下

I2C设备树绑定的核心就三点:

  1. 节点结构:父节点管总线,子节点管设备
  2. compatible:驱动匹配的钥匙,格式要严格
  3. reg:设备地址,注意7位和8位的区别

说白了,I2C设备树配置就是个填空题。你把总线参数填好,把设备地址填对,把驱动名写准,剩下的就是调试了。我个人经验是,第一次调I2C设备树时,先用 i2cdetect 工具扫一下总线,确认设备确实在预期的地址上响应,然后再去折腾驱动。这样能省掉一半的排查时间。

核心要点:I2C设备树配置中,80%的问题出在地址错误或compatible不匹配。拿到新板子,先扫地址,再查驱动,最后看中断。按这个顺序排查,基本不会走弯路。