第四节:节点(Node)与属性(Property)——设备树的“细胞”与“基因”

设备树(Device Tree)的本质是一种描述硬件拓扑与资源配置的树形数据结构。在这棵树中,节点(Node)是构成树形结构的基本单元,代表一个硬件设备、总线、控制器或抽象的功能模块;而属性(Property)则是附着在节点上的键值对,用于描述该硬件的具体特征、地址映射、中断连接、状态信息等。可以这样理解:节点是硬件在软件世界中的“化身”,属性则是这个化身的“基因序列”。本章将深入剖析节点的命名规范、地址单元(unit-address)的编码规则,以及各类属性的数据类型与常见属性的语义,帮助读者建立从硬件手册到设备树源文件(DTS)的映射能力。

设备树节点与属性核心知识结构 根节点 "/" cpus {} soc {} cpu@0 {} uart@1000 {} 节点属性(Property) • compatible = "arm,cortex-a9"; • reg = <0x1000 0x100>; • interrupts = <0 29 4>; • status = "okay"; • clock-frequency = <800000000>; • interrupt-parent = <&gic>; 数据类型与规则 • string: "okay", "disabled" • u32: <0x1000> (32位无符号) • u64: <0x0 0x80000000> (64位) • array: <0x100 0x200 0x300> • phandle: <&label> 引用 • boolean: 存在即 true 节点命名:node-name@unit-address | 属性类型:string / u32 / u64 / array / phandle / boolean

4.1 节点命名规则与 unit-address 编码

设备树中每个节点都必须遵循统一的命名规范,这是内核解析设备树时的第一道门槛。节点名称由两部分组成:节点名(node-name)和可选的地址单元(unit-address),两者之间用“@”符号分隔。节点名应使用小写字母、数字、连字符和下划线,且必须以字母开头;unit-address 则用于区分同一总线或同一控制器下的多个实例,其编码规则与节点的 reg 属性中的地址部分严格对应。

4.1.1 核心命名原则

  • 通用性优先:节点名应反映设备的功能类型,而非具体型号。例如使用 uart、i2c、ethernet,而不是 max3100 或 rtl8211。具体型号由 compatible 属性表达。
  • 地址单元必须与 reg 匹配:如果节点有 reg 属性,则 unit-address 必须等于 reg 中第一个地址值(十六进制小写)。例如 reg = <0x1000 0x100> 对应节点名 uart@1000。
  • 无地址设备可省略 @:如 CPU 节点 cpu@0 中的 0 是 CPU 编号;而像 gpio-keys 这类无内存映射的设备,可以省略 unit-address。
  • 路径分隔符:节点路径从根节点 / 开始,如 /soc/uart@1000,在 phandle 引用中常用 &uart0 这样的标签(label)代替完整路径。
实战示例:在 NXP i.MX6ULL 的 DTS 中,UART1 节点通常定义为:
uart1: serial@02020000 {
    compatible = "fsl,imx6ul-uart", "fsl,imx21-uart";
    reg = <0x02020000 0x4000>;
    interrupts = <GIC_SPI 26 IRQ_TYPE_LEVEL_HIGH>;
    status = "disabled";
};
这里 uart1 是标签(label),serial 是节点名,02020000 是 unit-address,与 reg 的起始地址完全一致。

4.1.2 unit-address 的编码规则

总线类型地址编码格式示例说明
内存映射总线32位或64位十六进制uart@1000对应 reg 中的地址字段
I2C 总线7位从机地址eeprom@50地址为 0x50,与 reg = <0x50> 匹配
SPI 总线片选编号flash@0表示 SPI 片选 0
PCIe 总线域:总线:设备.功能pcie@1,0BDF 编码,需配合 reg 的 PCI 格式
CPU 节点CPU 逻辑编号cpu@0与 reg = <0> 对应
常见误区:不要随意省略 unit-address,也不要让 unit-address 与 reg 不一致。内核的 of_find_node_by_name() 和 of_find_node_by_path() 函数在匹配节点时,会严格校验地址单元。若不一致,可能导致设备驱动无法正确绑定。

4.2 属性类型详解:从 string 到 phandle

属性是设备树中承载信息的核心载体,其数据类型决定了内核如何解析这些信息。设备树规范(DT Spec)定义了五种基本数据类型,理解这些类型是编写正确 DTS 文件的基础。

4.2.1 五种基本属性类型

类型DTS 表示法字节长度典型用途
string"okay"可变状态描述、兼容性标识
u32<0x1000>4 字节地址、大小、频率、标志位
u64<0x0 0x80000000>8 字节大地址空间(如 DDR 基址)
array<0x100 0x200 0x300>4×N 字节多地址/多数据组合
phandle<&label>4 字节节点引用,建立设备间关联

boolean(布尔)类型比较特殊,它不携带值,属性存在即为 true,不存在即为 false。例如 dma-coherent; 表示设备支持 DMA 一致性,无需额外赋值。

4.2.2 复合类型与特殊编码

  • 字符串列表:compatible = "ti,am335x-uart", "ns16550a"; 多个字符串按顺序排列,内核按优先级匹配。
  • u32 数组与 phandle 混合:interrupts-extended = <&gpio1 5 IRQ_TYPE_EDGE_RISING>; 第一个元素是 phandle,后续是参数。
  • 64 位值在 32 位系统中的表示:reg = <0x0 0x80000000 0x0 0x10000000>; 表示基址 0x80000000,大小 0x10000000,需要两个 u32 组合。
  • 空属性:status = "disabled"; 是字符串,而 status; 这种写法不合法,必须赋值。
经验提醒:在编写 compatible 属性时,遵循“厂商,型号”的格式,且越具体的型号放在越前面。例如 "fsl,imx6ul-uart", "fsl,imx21-uart",内核会先尝试匹配第一个,失败后再尝试第二个。

4.3 常见属性深度解析

在 Linux 内核的设备驱动模型中,有几个属性几乎出现在每一个设备节点中,它们是驱动与硬件交互的“桥梁”。本节将逐一拆解这些属性的语义、格式与使用场景。

4.3.1 compatible —— 设备驱动的“身份证”

compatible 属性是设备树与驱动匹配的核心机制。内核通过 of_match_table 中的 compatible 字符串与设备节点的该属性进行匹配。其格式为 "厂商,型号",厂商名通常为芯片或 IP 核的缩写。

compatible = "ti,am335x-uart", "ns16550a";

匹配顺序是从左到右,内核优先使用第一个匹配项。这种设计允许同一驱动兼容多个硬件版本,也允许新硬件复用旧驱动。

4.3.2 reg —— 地址与内存区域的“地图”

reg 属性描述设备在总线上的地址空间。其格式为 <地址 大小 ...>,地址和大小均为 u32 或 u64。对于内存映射设备,地址是物理地址;对于 I2C/SPI 设备,地址是从机地址。

reg = <0x02020000 0x4000>;  // 基址 0x02020000,大小 16KB

在 #address-cells 和 #size-cells 的配合下,内核可以正确解析 reg 中的地址和大小字段。例如父节点定义 #address-cells = <1>; #size-cells = <1>;,则 reg 中每对地址和大小各占一个 u32。

4.3.3 interrupts —— 中断连接的“神经”

interrupts 属性描述设备的中断请求线。其格式依赖于中断控制器(interrupt-controller)的 #interrupt-cells 属性。常见格式为 <中断类型 中断号 触发方式>。

interrupts = <0 29 4>;  // SPI 中断 29,高电平触发

其中第一个字段 0 表示 SPI(共享外设中断),29 是中断号,4 是触发类型(4 表示高电平触发)。对于 GPIO 中断,通常使用 interrupts-extended 属性,并包含 GPIO 控制器的 phandle。

4.3.4 status —— 设备的“开关”

status 属性控制设备是否启用。可选值包括 "okay"(启用)、"disabled"(禁用)、"reserved"(保留)和 "fail"(失败)。在开发阶段,常将未调试完成的设备设为 "disabled",待验证通过后再改为 "okay"。

4.3.5 其他高频属性

属性名类型作用示例
interrupt-parentphandle指定中断控制器interrupt-parent = <&intc>;
clocksphandle+参数关联时钟源clocks = <&clk_uart1>;
resetsphandle+参数关联复位控制器resets = <&rst 10>;
pinctrl-0phandle引脚复用配置pinctrl-0 = <&pinctrl_uart1>;
dma-coherentboolean声明 DMA 一致性dma-coherent;

4.4 实战案例:从零构建一个 UART 节点

下面通过一个完整的案例,演示如何根据芯片手册和硬件原理图,编写一个符合规范的 UART 设备节点。假设我们使用 TI AM335x 平台,UART3 连接在内部总线 0x48024000 地址上,使用中断 45,且需要引脚复用配置。

4.4.1 硬件信息收集

  • 芯片手册:UART3 基址 0x48024000,地址空间大小 0x1000(4KB)。
  • 中断控制器:使用 GIC(通用中断控制器),SPI 中断号 45,触发方式为高电平(4)。
  • 引脚复用:UART3 的 TXD/RXD 引脚复用为 Mode 1,需要配置 pinctrl 子系统。
  • 时钟:UART3 使用外设时钟 dpll_per_m2_ck,频率 48MHz。

4.4.2 DTS 节点编写

&uart3 {
    compatible = "ti,am3352-uart", "ti,omap3-uart";
    reg = <0x48024000 0x1000>;
    interrupts = <GIC_SPI 45 IRQ_TYPE_LEVEL_HIGH>;
    clocks = <&dpll_per_m2_ck>;
    clock-frequency = <48000000>;
    pinctrl-names = "default";
    pinctrl-0 = <&pinctrl_uart3>;
    status = "okay";
};

4.4.3 关键点分析

属性值设计依据
compatible"ti,am3352-uart"精确到 SoC 型号,驱动优先匹配
reg<0x48024000 0x1000>与手册地址一致,大小覆盖全部寄存器
interrupts<GIC_SPI 45 IRQ_TYPE_LEVEL_HIGH>GIC_SPI 是宏定义,值为 0;45 为中断号
clocks<&dpll_per_m2_ck>引用时钟节点,驱动可获取频率
pinctrl-0<&pinctrl_uart3>引脚复用配置,需在 pinctrl 节点中定义
status"okay"确认硬件已启用
调试复盘:在初次启动时,UART3 无法工作。通过 dmesg 查看发现驱动未 probe,进一步检查发现 pinctrl_uart3 节点中的引脚编号写错(将 PIN 编号与 GPIO 编号混淆)。修正后,UART3 正常输出日志。此案例说明,pinctrl 配置错误是 UART 类设备最常见的故障原因之一。

4.5 常见误区与设计建议

高风险误区:
  • unit-address 与 reg 不一致:导致内核无法通过地址匹配节点,驱动 probe 失败。
  • compatible 字符串拼写错误:驱动匹配不到,且 dmesg 中无任何提示。
  • interrupts 格式错误:中断号或触发方式写错,导致设备无法响应中断。
  • status 误设为 "disabled":设备被内核跳过,且不报错。
  • reg 中地址大小与 #address-cells 不匹配:导致地址解析错位,访问非法内存。
设计建议:
  • 在 DTS 中尽量使用宏定义(如 GIC_SPI、IRQ_TYPE_LEVEL_HIGH)代替魔法数字,提高可读性。
  • 为每个节点添加注释,说明硬件来源(如“参考原理图第 3 页 UART3 部分”)。
  • 使用 dtc -I dtb -O dts 反编译 DTB,验证编译结果是否符合预期。
  • 在驱动中通过 of_property_read_u32() 等 API 读取属性时,注意数据类型匹配。

4.6 总结与知识图谱

节点与属性是设备树的核心语法单元,掌握其命名规则、数据类型和常见属性语义,是进行设备树开发的第一步。本章通过理论讲解、表格对比和实战案例,系统梳理了从硬件手册到 DTS 代码的映射方法。在实际项目中,建议读者先绘制硬件拓扑图,再逐节点编写属性,最后通过 dtc 编译和内核日志验证正确性。

学习维度核心要点验证方法
命名规则node-name@unit-address,unit-address 与 reg 匹配dtc 编译无警告
属性类型string/u32/u64/array/phandle/boolean驱动中读取类型一致
compatible厂商,型号,按优先级匹配dmesg 中驱动 probe 成功
reg地址与大小,受 #address-cells 影响ioremap 后读写正常
interrupts中断类型、编号、触发方式中断触发时回调执行
status"okay"/"disabled" 控制设备启用设备出现在 /sys/devices/ 下

下一阶段,我们将深入探讨设备树中的中断映射机制、时钟与复位框架,以及如何通过设备树描述复杂的电源域和总线拓扑。掌握本章内容后,读者应能独立阅读和编写基础的设备树节点,为后续的驱动开发和系统移植打下坚实基础。