3. 设备树节点与属性:标准节点与关键属性详解
设备树(Device Tree)的核心是描述硬件拓扑的节点(Node)与属性(Property)。节点代表硬件设备或总线,属性则描述节点的具体特征。理解标准节点与关键属性是编写和调试设备树的基础。
3.1 标准节点:硬件骨架
设备树根节点(/)下必须包含若干标准节点,它们构成了系统硬件的顶层骨架。下表列出了最核心的四个标准节点及其作用:
| 节点路径 | 节点名称 | 作用与说明 | 是否必须 |
|---|---|---|---|
/ |
根节点 | 设备树的入口,所有其他节点的父节点。包含 model、compatible 等顶层属性。 |
是 |
/chosen |
chosen | 传递引导参数,如 bootargs(内核命令行)、stdout-path(控制台设备路径)。不描述硬件。 |
推荐 |
/memory |
memory | 描述系统物理内存的起始地址与大小,通过 reg 属性定义。 |
是 |
/cpus |
cpus | 包含所有 CPU 核心的子节点,每个核心用 cpu@<id> 表示,描述频率、缓存等信息。 |
是 |
实战提示: 在覆盖(overlay)中,
/chosen 节点常用于动态修改内核启动参数,例如在调试阶段追加 earlyprintk 或 initcall_debug。
3.2 关键属性详解:硬件描述语言
属性是设备树的“词汇”。以下三个属性是嵌入式开发中最常用、最关键的属性,它们直接决定了驱动与硬件的匹配方式。
3.2.1 compatible:驱动匹配的钥匙
compatible 属性是一个字符串列表,用于将设备节点与内核驱动进行匹配。格式为 "厂商,型号",优先级从前往后递减。
// 示例:I2C控制器节点
i2c@ff010000 {
compatible = "snps,designware-i2c", "generic-i2c";
reg = <0x0 0xff010000 0x0 0x1000>;
interrupts = <0 56 4>;
};
- 匹配规则: 内核驱动通过
of_match_table查找第一个匹配的字符串。 - 回退机制: 第一个字符串不匹配时,尝试第二个(如
"generic-i2c"),提高兼容性。 - 命名规范: 厂商名小写,型号使用连字符,避免下划线。
3.2.2 reg:地址与内存映射
reg 属性定义设备的地址空间,格式为 <地址 长度> 的数组。对于内存映射设备,它指定了总线上的基地址和映射范围。
// 单地址段示例
memory@80000000 {
device_type = "memory";
reg = <0x0 0x80000000 0x0 0x40000000>; // 起始0x80000000,大小1GB
};
// 多地址段示例(如带内部SRAM的网卡)
ethernet@ff020000 {
compatible = "cadence,gem";
reg = <0x0 0xff020000 0x0 0x1000>, // MAC寄存器
<0x0 0xff030000 0x0 0x2000>; // 描述符RAM
};
常见错误: 在 64 位系统中,
reg 的地址和长度必须使用两个 32 位单元表示(如 <0x0 0x80000000 0x0 0x40000000>),否则会导致地址截断。
3.2.3 interrupts:中断连接
interrupts 属性描述设备的中断信号,其格式由父节点的 #interrupt-cells 决定。最常用的 ARM GIC(通用中断控制器)使用 3 个 cell:
| Cell 位置 | 含义 | 常见值 |
|---|---|---|
| Cell 1 | 中断类型 | 0 = SPI(共享外设中断),1 = PPI(私有外设中断) |
| Cell 2 | 中断编号 | SPI 范围 32~1019,PPI 范围 16~31 |
| Cell 3 | 触发方式 | 1 = 上升沿,2 = 下降沿,4 = 高电平,8 = 低电平 |
// 中断控制器定义
gic: interrupt-controller@ff100000 {
compatible = "arm,gic-400";
reg = <0x0 0xff100000 0x0 0x1000>;
interrupt-controller;
#interrupt-cells = <3>;
};
// 使用中断的设备
uart@ff200000 {
compatible = "ns16550a";
reg = <0x0 0xff200000 0x0 0x1000>;
interrupts = <0 33 4>; // SPI 33号,高电平触发
interrupt-parent = <&gic>;
};
3.3 自定义属性:扩展硬件描述
除了标准属性,设备树允许开发者定义私有属性,用于传递板级特定参数。这些属性通常以厂商前缀开头,避免命名冲突。
// 自定义属性示例
gpio-keys {
compatible = "gpio-keys";
button@0 {
label = "power";
gpios = <&gpio0 3 GPIO_ACTIVE_LOW>;
linux,code = <KEY_POWER>;
debounce-interval = <50>; // 自定义属性:去抖时间
poll-interval = <100>; // 自定义属性:轮询间隔
};
};
- 命名规则: 使用
厂商,属性名格式(如fsl,clock-frequency),避免与内核标准属性冲突。 - 数据类型: 支持
<u32>、<u64>、字符串、prop-encoded-array、phandle等。 - 驱动读取: 内核驱动通过
of_property_read_u32()、of_property_read_string()等 API 获取自定义属性值。
3.4 节点与属性关系:SVG 结构图
下图展示了标准节点、关键属性与自定义属性在设备树中的层级关系:
3.5 属性使用对比:标准 vs 自定义
| 对比维度 | 标准属性(compatible/reg/interrupts) | 自定义属性 |
|---|---|---|
| 定义位置 | 设备树规范(ePAPR) | 厂商私有或板级特定 |
| 驱动支持 | 内核通用解析框架 | 需驱动显式调用 of_property_* API |
| 命名约束 | 固定名称,不可更改 | 建议加厂商前缀,避免冲突 |
| 覆盖兼容性 | 覆盖时需严格匹配父节点 | 覆盖时更灵活,可新增或修改 |
| 典型用途 | 设备识别、地址映射、中断路由 | GPIO 极性、时钟频率、去抖时间 |
核心要点: 标准属性是设备树的“语法规则”,自定义属性是“方言”。在设备树覆盖中,修改标准属性需要谨慎(可能影响驱动匹配),而自定义属性可以自由扩展,是传递板级参数的首选方式。