2. DTS语法入门

设备树源文件(.dts)是一种结构化的文本描述语言,用于向操作系统(尤其是 Linux 内核)描述不可枚举的硬件信息。本章将系统讲解 DTS 的四大核心语法要素:节点(Node)、属性(Property)、标签(Label)和包含文件(Include)。掌握这些基础是理解设备树覆盖(Overlay)和热插拔驱动的前提。

学习提示: DTS 语法与 JSON 或 XML 类似,但更紧凑。建议读者在阅读时对照一个真实的 .dts 文件(如 Linux 内核源码中的 arch/arm/boot/dts/ 目录下的文件)进行练习,效果更佳。

2.1 节点(Node):硬件设备的树形抽象

节点是设备树的基本组成单元,用于表示一个硬件设备或总线。每个节点从根节点(/)开始,形成一棵树状结构。节点可以嵌套,子节点继承父节点的总线环境。

节点语法格式:

[label:] node-name[@unit-address] {
    [property-list]
    [child-node-list]
};
  • node-name:节点名称,通常为通用设备类型(如 cpu, memory, i2c)。
  • @unit-address:可选,用于区分同类型设备,通常为寄存器地址或设备编号。
  • label:可选,为节点定义一个引用标签(详见 2.3 节)。
  • 花括号 { }:包含该节点的属性和子节点。
  • 分号 ;:节点定义结束符。

示例:一个简单的设备树根节点与子节点

/ {
    #address-cells = <1>;
    #size-cells = <1>;

    cpu@0 {
        compatible = "arm,cortex-a9";
        reg = <0x00 0x100>;
    };

    memory@80000000 {
        device_type = "memory";
        reg = <0x80000000 0x40000000>;  /* 1 GB RAM */
    };
};
关键点: 根节点 / 是设备树的入口,每个系统有且只有一个根节点。子节点 cpu@0 和 memory@80000000 分别描述了 CPU 和内存设备。
设备树节点层级结构 根节点 / cpu@0 memory@80000000 compatible, reg device_type, reg 每个节点代表一个硬件设备或总线 子节点继承父节点的地址空间

2.2 属性(Property):描述设备的键值对

属性是附着在节点上的键值对,用于描述设备的特性、地址、中断、时钟等信息。属性值可以是空、整数、字符串、字节数组或这些类型的组合。

常见属性类型与格式:

属性类型 格式示例 说明
空属性 status = "disabled"; 布尔型标志,存在即表示真
32 位整数 reg = <0x10000000 0x1000>; 尖括号 <> 包裹,空格分隔
64 位整数 clock-frequency = <0x00 0x5f5e100>; 使用两个 32 位单元表示
字符串 compatible = "ti,am335x-gpio"; 双引号包裹,用于驱动匹配
字符串列表 compatible = "ti,am335x-gpio", "ti,omap-gpio"; 逗号分隔多个字符串
字节数组 local-mac-address = [00 11 22 33 44 55]; 方括号 [] 包裹,十六进制字节
混合类型 interrupts = <0 42 4>; 可包含整数、字符串等组合

最重要的属性:compatible

compatible 属性是设备与驱动之间的“握手协议”。内核通过匹配该属性的字符串列表来加载对应的驱动程序。格式通常为 "厂商,设备型号",例如 "ti,am335x-gpio"。

常见错误: 忘记在属性值末尾加分号 ;,或字符串未使用双引号。DTS 编译器(DTC)对语法非常敏感,一个分号缺失会导致整个文件编译失败。
节点与属性关系 uart@44e09000 compatible reg interrupts "ti,omap-uart" <0x44e09000 0x2000> <0 56 4>

2.3 标签(Label):节点的引用别名

标签(Label)是为节点定义的别名,用于在设备树的其他位置引用该节点。标签在节点名称前定义,后跟冒号。使用 &label 语法可以引用标签指向的节点。

标签定义与引用示例:

/* 定义标签 */
uart0: serial@44e09000 {
    compatible = "ti,omap-uart";
    reg = <0x44e09000 0x2000>;
};

/* 在其他地方引用标签 */
&uart0 {
    status = "okay";
    clock-frequency = <48000000>;
};

标签的主要用途:

  • 覆盖(Overlay):在设备树覆盖文件中,通过 &label 向已有节点添加或修改属性。
  • 引用(Phandle):在属性中通过 &label 引用其他节点,形成设备间的连接关系(如中断控制器、时钟源)。
  • 代码可读性:避免使用冗长的节点路径(如 /soc/serial@44e09000),使用简短标签更清晰。
实战技巧: 在编写设备树覆盖文件时,几乎所有的修改操作都通过 &label 完成。因此,为关键设备节点(如 I2C、SPI、GPIO、UART)定义有意义的标签是良好的工程习惯。
标签引用与覆盖流程 uart0: serial@44e09000 compatible, reg (基础定义) &uart0 覆盖片段 status, clock-frequency 编译后最终节点 compatible + reg + status + clock-frequency DTC 编译合并 标签使覆盖文件无需知道节点完整路径,只需引用标签即可 这是设备树覆盖(Overlay)实现热插拔的基础机制

2.4 包含文件(Include):模块化与复用

DTS 支持使用 C 语言风格的 #include 预处理指令,将公共定义、头文件或其它设备树片段包含到当前文件中。这极大地提高了代码的复用性和可维护性。

包含文件的使用方式:

/* 包含 .dtsi 文件(设备树包含文件,通常存放 SoC 公共定义) */
#include "am335x.dtsi"

/* 包含 .h 头文件(通常定义宏、引脚复用编号) */
#include "am335x-pinmux.h"

/ {
    model = "TI AM335x BeagleBone Black";
    compatible = "ti,am335x-bone-black", "ti,am335x-bone", "ti,am335x";

    /* 使用头文件中定义的宏 */
    &gpio0 {
        pinctrl-0 = <&gpio0_pins>;
    };
};

包含文件的典型层次结构:

文件类型 扩展名 用途
设备树源文件 .dts 具体板级的设备树文件,是编译的入口
设备树包含文件 .dtsi SoC 公共定义、IP 核描述,被多个 .dts 包含
C 语言头文件 .h 定义宏常量(如 GPIO 编号、时钟频率),供 DTS 使用
最佳实践: 将 SoC 级别的固定描述(如 CPU、中断控制器、时钟)放在 .dtsi 文件中,板级差异(如外设使能、引脚复用)放在 .dts 文件中。这样,同一 SoC 的不同板卡只需维护一个 .dtsi 和多个小型 .dts 文件。
设备树包含文件层次结构 am335x.dtsi (SoC 公共定义) am335x-boneblack.dts (板级文件) am335x-boneblack.dtb (编译输出) #include 指令在预处理阶段将 .dtsi 内容插入 .dts 文件 最终由 DTC 编译器生成单一的 .dtb 二进制文件

2.5 综合示例:一个完整的 DTS 片段

以下示例综合展示了节点、属性、标签和包含文件的用法。该片段描述了一个基于 AM335x SoC 的板级设备树,包含 GPIO 控制器和 LED 子节点。

#include "am335x.dtsi"
#include "am335x-pinmux.h"

/ {
    model = "TI AM335x Custom Board";
    compatible = "ti,am335x-custom", "ti,am335x";

    leds {
        compatible = "gpio-leds";
        pinctrl-0 = <&led_pins>;

        led0: led@0 {
            label = "heartbeat";
            gpios = <&gpio1 21 GPIO_ACTIVE_LOW>;
            linux,default-trigger = "heartbeat";
        };
    };
};

&gpio1 {
    status = "okay";
};

&am33xx_pinmux {
    led_pins: led_pins {
        pinctrl-single,pins = <
            AM33XX_IOPAD(0x848, PIN_OUTPUT | MUX_MODE7)
        >;
    };
};
代码解析:
  • #include "am335x.dtsi":引入 SoC 级定义,包含 gpio1、am33xx_pinmux 等节点。
  • leds 节点:定义了一个 GPIO LED 子节点,使用 gpios 属性引用 &gpio1。
  • &gpio1 和 &am33xx_pinmux:通过标签引用,在板级文件中使能 GPIO 并配置引脚复用。
  • led_pins 标签:为引脚复用子节点定义标签,供 pinctrl-0 引用。

通过本章的学习,读者应能够独立阅读和编写基础的 DTS 文件,理解节点如何组织硬件、属性如何描述细节、标签如何实现跨文件引用,以及包含文件如何实现模块化设计。这些知识是后续学习设备树覆盖(Overlay)和热插拔驱动的基石。