2. 设备树基础回顾:DTS语法、节点、属性、reg、interrupts等关键概念

设备树(Device Tree)是一种描述硬件资源的数据结构,它使用节点(node)和属性(property)来描述系统中的设备、内存映射、中断连接、时钟关系等。在嵌入式Linux系统中,设备树源文件(.dts)经过编译生成设备树二进制文件(.dtb),由Bootloader传递给内核。理解DTS语法是进行节点精简和未用外设禁用的前提。

2.1 DTS文件结构与节点层次

一个典型的DTS文件以/dts-v1/;开头,表示版本。根节点/是设备树的起点,所有设备节点都挂载在根节点下,形成树形层次结构。每个节点可以包含子节点和属性。

核心语法规则: 节点名称格式为 node-name@unit-address,其中 unit-address 通常对应设备在总线上的起始地址,用于区分同名节点。
/dts-v1/;

/ {
    compatible = "vendor,board-model";
    #address-cells = <1>;
    #size-cells = <1>;

    cpu@0 {
        compatible = "arm,cortex-a7";
        reg = <0x0>;
    };

    memory@80000000 {
        device_type = "memory";
        reg = <0x80000000 0x40000000>;  /* 1GB RAM */
    };

    soc {
        #address-cells = <1>;
        #size-cells = <1>;
        ranges;

        uart0: serial@10000000 {
            compatible = "ns16550a";
            reg = <0x10000000 0x1000>;
            interrupts = <0 33 4>;
            clock-frequency = <24000000>;
            status = "okay";
        };

        gpio0: gpio@20000000 {
            compatible = "vendor,gpio-controller";
            reg = <0x20000000 0x1000>;
            interrupts = <0 45 4>;
            gpio-controller;
            #gpio-cells = <2>;
            status = "okay";
        };
    };
};
优化切入点: 节点层次越深,内核解析路径越长。在启动加速优化中,可以将不必要的外设节点从 soc 总线中移除或设置 status = "disabled",减少内核遍历开销。

2.2 关键属性详解

属性是设备节点的核心,用于描述设备的硬件特性、资源需求和驱动绑定信息。以下表格列出了DTS中最常用的属性及其作用:

属性名 作用 示例 优化影响
compatible 驱动匹配字符串,内核通过该属性查找对应驱动 "ns16550a" 精简不必要节点可减少驱动匹配次数
reg 描述设备地址空间(起始地址 + 长度) <0x10000000 0x1000> 禁用节点后,内核跳过地址映射
interrupts 描述中断号、触发类型、中断域 <0 33 4> 禁用节点可释放中断资源
status 设备状态:"okay" / "disabled" "disabled" 直接控制设备是否被内核初始化
clocks 关联时钟控制器与时钟ID <&clkc 15> 禁用节点可关闭对应时钟门控
pinctrl-* 引脚复用配置 pinctrl-0 = <&pinctrl_uart0> 禁用节点可释放引脚资源

2.3 reg属性与地址映射

reg 属性是设备树中描述物理地址空间的核心机制。其格式由父节点的 #address-cells 和 #size-cells 决定。对于32位系统,通常 #address-cells = <1>,#size-cells = <1>,每个 reg 条目包含一个地址和一个长度。

/* 父节点定义地址单元 */
soc {
    #address-cells = <1>;   /* 地址用1个32位单元表示 */
    #size-cells = <1>;      /* 长度用1个32位单元表示 */
    ranges;                  /* 直接映射,子节点地址 = 父节点地址 */

    ethernet@30000000 {
        reg = <0x30000000 0x10000>;  /* 起始0x30000000,长度64KB */
        interrupts = <0 56 4>;
    };
};
优化陷阱: 如果禁用节点后未清理 reg 中的地址范围,内核仍会尝试保留该地址区域,导致内存映射表膨胀。建议在禁用节点时,同时移除 reg 属性或将其置空。

2.4 interrupts属性与中断映射

interrupts 属性描述设备的中断请求线。其格式由中断控制器父节点的 #interrupt-cells 决定。常见的中断描述格式为 <中断域 中断号 触发类型>。

中断域 含义 常见值
0 SPI(共享外设中断) 用于外设共享中断线
1 PPI(私有外设中断) 用于CPU私有中断
2 虚拟中断 用于虚拟化场景
/* 中断触发类型编码(ARM GIC) */
#define IRQ_TYPE_LEVEL_HIGH  4
#define IRQ_TYPE_EDGE_RISING 1

uart0: serial@10000000 {
    interrupts = <0 33 4>;  /* SPI中断号33,高电平触发 */
};
优化技巧: 禁用未使用外设时,内核会跳过中断注册流程。对于大量未用外设,可减少中断描述符的分配和初始化时间,这在中断控制器初始化阶段尤为明显。

2.5 status属性与设备启用控制

status 属性是设备树中最直接的设备启用/禁用开关。内核在遍历设备树时,会检查每个节点的 status 属性:

  • "okay":设备启用,内核会尝试匹配驱动并初始化。
  • "disabled":设备禁用,内核跳过该节点,不分配资源。
  • 缺失:部分内核版本视为 "okay",建议显式声明。
/* 禁用未使用的I2C控制器 */
i2c2: i2c@21020000 {
    compatible = "vendor,i2c-controller";
    reg = <0x21020000 0x1000>;
    interrupts = <0 78 4>;
    status = "disabled";  /* 内核不会初始化此设备 */
};

2.6 设备树结构关系图

以下SVG图展示了设备树中根节点、总线节点、设备节点以及关键属性之间的层次关系:

设备树节点层次与关键属性关系图 根节点 / compatible, #address-cells soc 总线节点 ranges, #address-cells uart0: serial@10000000 compatible = "ns16550a" reg = <0x10000000 0x1000> interrupts = <0 33 4> status = "okay" gpio0: gpio@20000000 compatible = "vendor,gpio" reg = <0x20000000 0x1000> interrupts = <0 45 4> status = "okay" i2c2: i2c@21020000 status = "disabled" reg = <0x21020000 0x1000> interrupts = <0 78 4> 优化要点 将未用外设 status 设为 "disabled",内核跳过驱动匹配、中断注册和地址映射

2.7 节点标签与引用

DTS支持使用标签(label)为节点命名,方便在其他位置引用。标签通常放在节点名称之前,使用冒号分隔。引用时使用 &label 语法。

/* 定义带标签的节点 */
uart0: serial@10000000 {
    status = "okay";
};

/* 在另一个文件中引用并修改 */
&uart0 {
    status = "disabled";  /* 覆盖原节点的status属性 */
    clock-frequency = <48000000>;  /* 添加新属性 */
};
优化应用: 在板级DTS文件中,通过 &label 引用SoC级DTSI中定义的外设节点,统一将未用外设设置为 status = "disabled",无需修改原始DTSI文件,便于维护。

2.8 包含文件与DTSI

设备树源文件支持 #include 预处理指令,通常将SoC公共定义放在 .dtsi 文件中,板级特定配置放在 .dts 文件中。这种分离有利于代码复用和优化管理。

/* soc.dtsi - SoC公共定义 */
/ {
    soc {
        uart0: serial@10000000 {
            compatible = "ns16550a";
            reg = <0x10000000 0x1000>;
            interrupts = <0 33 4>;
            status = "disabled";  /* 默认禁用 */
        };

        uart1: serial@10001000 {
            compatible = "ns16550a";
            reg = <0x10001000 0x1000>;
            interrupts = <0 34 4>;
            status = "disabled";
        };
    };
};

/* board.dts - 板级配置 */
#include "soc.dtsi"

&uart0 {
    status = "okay";  /* 仅启用UART0 */
    clock-frequency = <24000000>;
};
最佳实践: 在SoC级DTSI中,将所有外设默认设为 status = "disabled",然后在板级DTS中按需启用。这种“默认禁用,按需启用”的策略,天然支持启动加速优化。

2.9 关键概念总结表

概念 说明 与启动优化的关系
节点(node) 设备树的层次单元,描述一个设备或总线 节点数量直接影响内核遍历时间
属性(property) 节点的键值对,描述设备特性 属性解析消耗CPU时间
reg 物理地址空间描述 禁用节点可跳过ioremap
interrupts 中断资源描述 禁用节点可减少中断描述符分配
status 设备启用状态 直接控制设备初始化流程
compatible 驱动匹配字符串 禁用节点可减少驱动匹配次数
标签与引用 节点复用与覆盖机制 支持按需启用,减少冗余代码
DTSI包含 公共定义与板级配置分离 便于统一管理禁用策略