3. 设备树节点与属性:节点命名规则、节点路径、标准属性、自定义属性

设备树(Device Tree)是嵌入式 Linux 中描述硬件拓扑的核心数据结构。节点(Node)与属性(Property)构成了设备树的骨架与血肉。理解节点命名规则、路径寻址方式以及各类属性的语义,是编写正确设备树源文件(.dts)和设备树覆盖(.dtso)的基础。

3.1 节点命名规则

每个设备树节点必须遵循严格的命名规范,以确保编译器和内核能够正确解析。节点名称由两部分组成:节点名和可选的单元地址。

组成部分 格式 说明 示例
节点名 name 通用名称,描述设备类型,长度 1~31 字符,只能包含小写字母、数字、短横线(-)和下划线(_)。 uart, i2c, gpio-keys
单元地址 @address 可选,通常为设备在父总线上的寄存器地址或片选号,使用十六进制小写。 @1f00000, @0
完整节点名 name@address 节点名与单元地址通过 @ 连接,同一父节点下必须唯一。 serial@1f00000, i2c@0
最佳实践:节点名应尽量使用标准化的设备类别名称(如 serial、ethernet、mmc),而非具体芯片型号。单元地址必须与 reg 属性中的第一个地址段一致,否则编译器会报警告。

3.2 节点路径

设备树采用类似文件系统的树形结构,每个节点都可以通过绝对路径或相对路径唯一标识。路径由斜杠(/)分隔的节点名序列组成。

  • 根节点:用单个斜杠 / 表示,是整个设备树的起点。
  • 绝对路径:从根节点开始,逐级向下,如 /soc/bus@1f00000/serial@1f01000。
  • 标签引用:通过 &label 语法引用节点,常用于覆盖(overlay)中,如 &uart1。
  • 别名(aliases):在 /aliases 节点中定义短名称,如 serial0 = &uart1;,便于 bootloader 和内核快速查找。
路径解析规则:在设备树覆盖中,target 属性或 target-path 属性用于指定要修改的节点。优先使用标签引用(&label),可读性更强且不易受路径变化影响。

3.3 标准属性详解

设备树规范定义了一组标准属性,用于描述设备的基本硬件特征。以下是最常用的四个标准属性。

3.3.1 compatible 属性

compatible 是设备树中最重要的属性,用于将设备节点与内核驱动程序进行匹配。它是一个字符串列表,按优先级从高到低排列。

compatible = "vendor,device", "generic,device-class";
字段 格式 示例 说明
厂商前缀 vendor ti, nvidia, fsl 通常与内核中 vendor-prefixes.txt 定义的厂商缩写一致。
设备型号 device omap4-uart, tegra20-i2c 具体芯片或 IP 核的型号名称。
通用兼容 generic,device-class ns16550, simple-bus 回退兼容的通用驱动类,当专用驱动不存在时使用。

3.3.2 reg 属性

reg 属性描述设备在总线上的地址空间,由一组地址-长度对组成。其格式由父节点的 #address-cells 和 #size-cells 属性决定。

#address-cells = <1>;   // 地址占 1 个 32 位单元
#size-cells    = <1>;   // 长度占 1 个 32 位单元

serial@1f01000 {
    reg = <0x1f01000 0x1000>;  // 起始地址 0x1f01000,长度 0x1000
};
常见错误:在覆盖中修改 reg 属性时,必须确保地址-长度对的数量与父节点定义的 cells 数量一致。例如,#address-cells = <2> 时,每个地址需要两个 32 位值(如 <0x0 0x1f01000>)。

3.3.3 interrupts 属性

interrupts 属性描述设备使用的中断资源。其格式由父节点或根节点中的 #interrupt-cells 属性定义。常见的中断控制器(如 GIC)通常使用 3 个 cell:

interrupts = <0 33 4>;
// cell 0: 中断类型(0=SPI, 1=PPI)
// cell 1: 中断号(33)
// cell 2: 中断触发标志(4=高电平触发)
中断类型 值 说明
SPI(共享外设中断) 0 可被多个 CPU 处理,中断号范围 32~1019
PPI(私有外设中断) 1 每个 CPU 私有,中断号范围 16~31
触发标志 1~4 1=上升沿触发,2=下降沿触发,4=高电平触发,8=低电平触发

3.3.4 status 属性

status 属性控制设备的启用状态,是覆盖操作中最常修改的属性之一。

值 含义 使用场景
"okay" 设备已启用且可正常操作 覆盖中启用默认禁用的外设
"disabled" 设备禁用,驱动不会探测 默认关闭未使用的硬件模块
"reserved" 设备保留,不由操作系统管理 固件或安全世界使用的设备
"fail" 设备检测失败,不可操作 硬件初始化错误时由 bootloader 设置

3.4 自定义属性

设备树允许开发者定义非标准的自定义属性,用于传递板级特定参数或驱动私有数据。自定义属性必须遵循以下规则:

  • 命名空间:建议使用厂商前缀作为属性名开头,如 myvendor,led-gpio,避免与未来标准属性冲突。
  • 数据类型:支持 <u32>(32 位整数)、<u64>(64 位整数)、string(字符串)、byte-array(字节数组)、phandle(节点引用)等。
  • 驱动读取:内核驱动通过 of_property_read_u32()、of_property_read_string() 等 API 读取自定义属性。
gpio-leds {
    compatible = "gpio-leds";
    led-0 {
        label = "heartbeat";
        gpios = <&gpio1 5 GPIO_ACTIVE_LOW>;
        linux,default-trigger = "heartbeat";
        myvendor,blink-pattern = <100 200>;  // 自定义属性:亮100ms,灭200ms
    };
};

3.5 节点与属性结构总览

下图展示了设备树中节点、路径、标准属性与自定义属性的完整结构关系。

设备树节点与属性结构总览 / (根节点) soc@0 serial@1f01000 compatible = "ti,omap4-uart", "ns16550"; reg = <0x1f01000 0x1000>; interrupts = <0 33 4>; status = "okay"; i2c@1f02000 compatible = "ti,omap4-i2c"; reg = <0x1f02000 0x1000>; #address-cells = <1>; #size-cells = <0>; status = "disabled"; eeprom@50 compatible = "atmel,24c02"; reg = <0x50>; myvendor,page-size = <16>; // 自定义属性 总线/桥接节点 外设节点 控制器节点 子设备节点 属性定义

3.6 属性数据类型速查表

设备树属性支持多种数据类型,在编写 .dts 或 .dtso 文件时需正确使用对应的编码语法。

数据类型 编码语法 示例 说明
32 位整数 <value> <0x1000> 单个 32 位无符号整数
64 位整数 <high low> <0x0 0x1f01000> 两个 32 位值拼接为 64 位
字符串 "string" "okay" 双引号包裹的 ASCII 字符串
字符串列表 "str1", "str2" "ti,omap4-uart", "ns16550" 逗号分隔的多个字符串
字节数组 [hex bytes] [01 02 03 0a] 空格分隔的十六进制字节
布尔值 属性存在即为 true dma-coherent; 无值的空属性,存在表示启用
phandle &label &gpio1 引用其他节点的句柄
phandle 列表 &l1 &l2 &dma &gpio2 空格分隔的多个 phandle

3.7 覆盖中的节点与属性操作

在设备树覆盖(.dtso)中,对节点和属性的操作遵循增量修改原则:覆盖只添加或修改指定的属性,未提及的属性保持不变。

覆盖操作规则:
  • 添加属性:在目标节点下直接定义新属性。
  • 修改属性:重新定义同名属性,新值覆盖旧值。
  • 删除属性:使用 /delete-property/ prop-name; 语法。
  • 添加子节点:在目标节点下定义新子节点。
  • 删除子节点:使用 /delete-node/ &label; 或 /delete-node/ name@address; 语法。
// 覆盖示例:启用 I2C 控制器并添加 EEPROM 子设备
/dts-v1/;
/plugin/;

&i2c1 {
    status = "okay";                    // 修改 status 属性
    clock-frequency = <400000>;         // 添加自定义时钟频率属性

    eeprom@50 {
        compatible = "atmel,24c02";
        reg = <0x50>;
        pagesize = <16>;               // 自定义属性
    };
};
调试技巧:使用 dtc -I dtbo -O dts <overlay.dtbo> 反编译覆盖文件,检查节点路径和属性值是否正确。内核启动后,通过 /proc/device-tree/ 目录树可以查看实际生效的设备树结构。