4. 设备树基础语法:节点、属性与关键绑定详解
设备树(Device Tree)是嵌入式 Linux 中描述硬件拓扑的标准方式。对于 I2C 驱动开发而言,设备树承担着“硬件配置清单”的角色:它告诉内核哪个 I2C 控制器挂在哪个总线上、哪个从机设备挂在哪个控制器下、以及该从机需要哪些资源(地址、中断、时钟等)。本章将系统讲解设备树的核心语法,并聚焦于 I2C 场景下最常用的属性。
4.1 设备树节点与属性基础
设备树由一系列节点(node)和属性(property)组成。每个节点代表一个硬件设备或总线,属性则描述该设备的配置参数。
关键概念:节点路径(如 /soc/i2c@12340000)是设备在树中的唯一位置;属性名(如 compatible、reg)是内核匹配驱动的关键。
4.1.1 节点定义格式
// 基本节点语法
node-name@unit-address {
property-name = <value>;
property-name = "string";
property-name = [byte-sequence];
child-node {
// 子节点
};
};
- node-name:节点名称,通常为设备类型(如 i2c、gpio、uart)。
- unit-address:设备地址(如寄存器基址或 I2C 从机地址),用于区分同名节点。
- 属性值:可以是 32 位整数数组、字符串、字节序列或混合类型。
4.1.2 节点与属性的层次关系
下图展示了一个典型的 I2C 控制器及其从机设备的设备树层次结构:
4.2 关键属性详解
在 I2C 驱动开发中,以下五个属性出现的频率最高,是设备树与驱动代码之间的核心契约。
| 属性名 | 作用 | I2C 场景典型值 | 是否必需 |
|---|---|---|---|
| compatible | 驱动匹配字符串,内核据此查找对应驱动 | "vendor,device-name" | 是 |
| reg | 设备地址(I2C 从机地址)或寄存器基址 | <0x48>、<0x50> | 是 |
| interrupts | 中断号、触发类型 | <0x1e 0x2> | 按需 |
| clocks | 时钟源引用 | <&clkc 15> | 按需 |
| status | 设备启用状态("okay" / "disabled") | "okay" | 推荐 |
4.3 compatible 属性:驱动匹配的核心
compatible 是设备树中最重要的属性。内核通过它找到对应的 platform_driver 或 i2c_driver。其值通常采用 "厂商,设备型号" 的格式,保证全局唯一性。
4.3.1 匹配规则
- 内核遍历设备节点的 compatible 字符串列表。
- 驱动注册时通过
of_match_table声明支持的 compatible 列表。 - 当设备树中的字符串与驱动表中的任意一个匹配时,驱动 probe 函数被调用。
// 设备树中的 compatible(支持多个备选)
compatible = "vendor,tmp117", "ti,tmp117";
// 驱动中的匹配表
static const struct of_device_id tmp117_of_match[] = {
{ .compatible = "vendor,tmp117" },
{ .compatible = "ti,tmp117" },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, tmp117_of_match);
工程建议:compatible 字符串应遵循“厂商前缀+设备型号”的命名规范。Linux 内核维护了官方前缀列表(Documentation/devicetree/bindings/vendor-prefixes.yaml),新设备应优先使用已注册前缀。
4.4 reg 属性:地址空间与从机地址
reg 属性描述设备的地址空间信息。对于 I2C 从机,它表示从机地址;对于内存映射设备,它表示寄存器基址和长度。
4.4.1 I2C 从机地址编码
// 7 位从机地址(常用)
reg = <0x48>;
// 10 位从机地址(需在父节点声明 address-cells = <2>)
reg = <0x0000 0x0050>;
reg 属性的格式由父节点的 #address-cells 和 #size-cells 决定:
| 父节点属性 | 含义 | reg 子项格式 |
|---|---|---|
| #address-cells = <1>; #size-cells = <0> | I2C 从机(只有地址,无长度) | reg = <0x48>; |
| #address-cells = <1>; #size-cells = <1> | 内存映射设备(基址 + 长度) | reg = <0x1000 0x100>; |
| #address-cells = <2>; #size-cells = <1> | 64 位地址空间 | reg = <0x0 0xfe000000 0x1000>; |
4.5 interrupts 属性:中断资源描述
当 I2C 从机设备需要中断通知时,通过 interrupts 属性描述中断号和触发类型。该属性依赖父节点(通常是中断控制器)的 #interrupt-cells 定义。
4.5.1 中断属性格式
// 设备树节点
sensor@48 {
compatible = "vendor,tmp117";
reg = <0x48>;
interrupt-parent = <&gpio1>; // 指定中断控制器
interrupts = <0x1e 0x2>; // GPIO 30 号引脚,下降沿触发
};
// 中断控制器定义(gpio1)
gpio1: gpio@12350000 {
#interrupt-cells = <2>; // 两个 cell:中断号 + 触发类型
interrupt-controller; // 声明本节点是中断控制器
};
4.5.2 常见触发类型编码
| 宏定义 | 值 | 触发方式 |
|---|---|---|
| IRQ_TYPE_EDGE_RISING | 0x01 | 上升沿触发 |
| IRQ_TYPE_EDGE_FALLING | 0x02 | 下降沿触发 |
| IRQ_TYPE_EDGE_BOTH | 0x03 | 双边沿触发 |
| IRQ_TYPE_LEVEL_HIGH | 0x04 | 高电平触发 |
| IRQ_TYPE_LEVEL_LOW | 0x08 | 低电平触发 |
常见错误:忘记设置
interrupt-parent 属性,或者 interrupts 的 cell 数量与中断控制器的 #interrupt-cells 不匹配,会导致中断无法正确映射,驱动 probe 失败。
4.6 完整 I2C 设备树实例
以下是一个包含 I2C 控制器和两个从机设备的完整设备树片段,综合展示了上述关键属性的使用:
/dts-v1/;
#include <dt-bindings/interrupt-controller/irq.h>
/ {
model = "Embedded I2C Demo Board";
compatible = "vendor,emb-board";
soc@0 {
#address-cells = <1>;
#size-cells = <1>;
ranges;
i2c@12340000 {
compatible = "vendor,i2c-controller";
reg = <0x12340000 0x1000>;
interrupts = <0x3d 0x4>; // 中断号 61,高电平触发
interrupt-parent = <&gic>;
#address-cells = <1>;
#size-cells = <0>;
clock-frequency = <400000>; // 400kHz 快速模式
status = "okay";
temp-sensor@48 {
compatible = "vendor,tmp117";
reg = <0x48>;
interrupt-parent = <&gpio1>;
interrupts = <0x1e 0x2>; // 下降沿触发
status = "okay";
};
eeprom@50 {
compatible = "vendor,24c02";
reg = <0x50>;
pagesize = <8>; // 自定义属性:页大小
status = "okay";
};
};
};
};
4.7 设备树到驱动数据流的映射
下图展示了设备树属性如何被内核解析并传递给 I2C 驱动:
4.8 属性继承与覆盖规则
设备树支持通过 &node 引用已有节点并追加或覆盖属性,这在板级配置中非常实用:
// 原始 dtsi(芯片级)
&i2c1 {
status = "disabled";
};
// 板级 dts(板级覆盖)
&i2c1 {
status = "okay";
clock-frequency = <100000>;
temp-sensor@48 {
compatible = "vendor,tmp117";
reg = <0x48>;
};
};
工程要点:属性覆盖遵循“最后生效”原则。dtsi 中定义的属性可以被 dts 中的同名属性覆盖。但
compatible 和 reg 等关键属性一旦在 dts 中重新定义,会完全替换 dtsi 中的值,而非追加。
4.9 本章小结
- 节点是设备树的基本单元,通过路径唯一标识硬件设备。
- compatible 是驱动匹配的核心,必须与驱动中的 of_match_table 一致。
- reg 在 I2C 场景下表示从机地址,格式由父节点的 address-cells / size-cells 决定。
- interrupts 描述中断资源,需配合 interrupt-parent 和中断控制器的 #interrupt-cells 使用。
- 设备树属性通过内核 OF 核心和 I2C 核心自动解析,驱动 probe 时可直接使用 i2c_client 中的字段。