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图展示了设备树中根节点、总线节点、设备节点以及关键属性之间的层次关系:
2.7 节点标签与引用
DTS支持使用标签(label)为节点命名,方便在其他位置引用。标签通常放在节点名称之前,使用冒号分隔。引用时使用 &label 语法。
/* 定义带标签的节点 */
uart0: serial@10000000 {
status = "okay";
};
/* 在另一个文件中引用并修改 */
&uart0 {
status = "disabled"; /* 覆盖原节点的status属性 */
clock-frequency = <48000000>; /* 添加新属性 */
};
&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>;
};
status = "disabled",然后在板级DTS中按需启用。这种“默认禁用,按需启用”的策略,天然支持启动加速优化。
2.9 关键概念总结表
| 概念 | 说明 | 与启动优化的关系 |
|---|---|---|
| 节点(node) | 设备树的层次单元,描述一个设备或总线 | 节点数量直接影响内核遍历时间 |
| 属性(property) | 节点的键值对,描述设备特性 | 属性解析消耗CPU时间 |
| reg | 物理地址空间描述 | 禁用节点可跳过ioremap |
| interrupts | 中断资源描述 | 禁用节点可减少中断描述符分配 |
| status | 设备启用状态 | 直接控制设备初始化流程 |
| compatible | 驱动匹配字符串 | 禁用节点可减少驱动匹配次数 |
| 标签与引用 | 节点复用与覆盖机制 | 支持按需启用,减少冗余代码 |
| DTSI包含 | 公共定义与板级配置分离 | 便于统一管理禁用策略 |