1. 设备树基础概念
1.1 什么是设备树 (Device Tree)
设备树(Device Tree,DT)是一种描述硬件资源的数据结构,它使用树形节点来记录系统中所有可发现的硬件设备、内存映射、中断连接、时钟关系等非可枚举信息。设备树源文件(.dts)经过编译生成设备树二进制文件(.dtb),在系统启动时由引导加载程序传递给内核,内核解析该结构完成硬件资源的注册与驱动匹配。
核心定义:设备树 = 硬件拓扑的静态描述文件,将板级细节从内核代码中剥离,实现“一套内核二进制,适配多种硬件平台”。
关键文件后缀:
.dts — 设备树源文件(文本,可读).dtsi — 设备树包含文件(类似头文件,用于 SoC 公共定义).dtb — 设备树二进制文件(编译产物,内核直接使用).dtbo — 设备树叠加层(运行时动态修改)
1.2 为什么需要设备树
在设备树出现之前(Linux 2.6 ~ 3.x 早期),ARM 架构的板级支持包(BSP)采用大量 board-*.c 和 mach-*.c 文件硬编码硬件信息。这种模式导致以下严重问题:
| 问题 | 传统方式(无设备树) | 设备树方式 |
|---|---|---|
| 代码冗余 | 每块新板卡需要新增数百行 C 代码描述硬件 | 只需编写 .dts 文件,内核代码零改动 |
| 内核膨胀 | ARM 内核包含大量板级文件,单内核镜像无法通用 | 同一内核镜像通过不同 .dtb 适配不同硬件 |
| 维护困难 | 硬件变更需要重新编译内核,社区难以统一 | 硬件描述与驱动分离,驱动只需匹配 compatible 字符串 |
| 启动流程耦合 | Bootloader 与内核强依赖固定物理地址 | Bootloader 动态加载 .dtb,内核解析后自动映射 |
典型痛点案例: 在设备树普及前,Linux 主线内核中 ARM 架构的
arch/arm/mach-* 目录包含超过 3000 个板级文件。每添加一款开发板,就需要向社区提交大量板级 C 代码,审核周期长达数月。设备树将这一过程简化为一个 .dts 文件(通常 200~500 行),大幅降低了硬件适配门槛。
1.3 设备树与 ACPI 的区别
ACPI(Advanced Configuration and Power Interface)是 x86/x86_64 平台的标准硬件抽象接口,与设备树在理念、使用场景和技术实现上有本质差异。以下从多个维度进行对比:
| 对比维度 | 设备树 (Device Tree) | ACPI |
|---|---|---|
| 主要平台 | ARM、RISC-V、PowerPC、MIPS 等嵌入式架构 | x86 / x86_64(PC、服务器) |
| 描述方式 | 静态树形结构,编译为二进制 .dtb |
AML(ACPI Machine Language)字节码,解释执行 |
| 运行时灵活性 | 静态加载,启动后一般不修改(除 overlay) | 支持运行时动态枚举、热插拔、电源事件 |
| 电源管理 | 依赖驱动自身实现,设备树仅提供描述 | 内建完整的电源状态、睡眠/唤醒、设备电源策略 |
| 硬件枚举 | 内核解析 .dtb 节点,匹配 compatible 驱动 |
BIOS/UEFI 提供 ACPI 表,内核通过 AML 解释器枚举 |
| 复杂度 | 轻量,源文件可读性强,学习曲线平缓 | 庞大,ASL 源码编译为 AML,调试工具链复杂 |
| 社区生态 | Linux 内核主线 + 开源社区主导 | UEFI 论坛标准化,微软/Intel 主导 |
选择原则:
• 嵌入式系统(ARM/RISC-V)→ 设备树,轻量、直接、可控
• PC/服务器(x86)→ ACPI,需要热插拔、高级电源管理、标准化接口
• 部分 ARM 服务器(如 Ampere、华为鲲鹏)同时支持设备树和 ACPI,由固件决定启动方式
• 嵌入式系统(ARM/RISC-V)→ 设备树,轻量、直接、可控
• PC/服务器(x86)→ ACPI,需要热插拔、高级电源管理、标准化接口
• 部分 ARM 服务器(如 Ampere、华为鲲鹏)同时支持设备树和 ACPI,由固件决定启动方式
1.4 设备树的核心语法速览
一个典型的设备树节点包含以下要素:
/dts-v1/;
/ {
model = "TI AM335x BeagleBone Black";
compatible = "ti,am335x-bone-black", "ti,am335x-bone", "ti,am33xx";
cpus {
#address-cells = <1>;
#size-cells = <0>;
cpu@0 {
compatible = "arm,cortex-a8";
device_type = "cpu";
reg = <0x00>;
};
};
memory@80000000 {
device_type = "memory";
reg = <0x80000000 0x20000000>; /* 512 MB */
};
&i2c0 {
status = "okay";
clock-frequency = <100000>;
touchscreen@1a {
compatible = "ti,tsc2046";
reg = <0x1a>;
interrupt-parent = <&gpio1>;
interrupts = <18 2>;
};
};
};
语法要点:
•
•
•
•
•
•
/dts-v1/; — 版本声明,必须位于文件首行•
compatible — 驱动匹配的核心字符串,优先级从具体到通用•
reg — 地址/长度对,由 #address-cells 和 #size-cells 决定宽度•
&i2c0 — 引用标签,用于在 .dtsi 基础上追加或覆盖属性•
status — 设备状态,"okay" 启用,"disabled" 禁用
1.5 设备树在驱动移植中的角色
在驱动移植适配工作中,设备树承担以下关键职责:
- 硬件资源描述: 提供寄存器地址范围、中断号、DMA 通道、时钟频率等驱动所需的资源信息。
- 驱动匹配锚点: 驱动通过
of_match_table中的compatible字符串与设备树节点建立绑定关系。 - 平台数据传递: 自定义属性(如
gpio-leds、pinctrl-*)传递板级特定配置,避免硬编码。 - 总线拓扑定义: 描述 I2C、SPI、MMC 等总线上的从设备连接关系,驱动据此枚举子设备。
- 电源域与时钟域: 通过
power-domains、clocks属性建立设备与电源管理框架的关联。
驱动移植第一原则: 先确认设备树节点是否存在且属性完整,再调试驱动代码。80% 的移植问题源于设备树配置错误,而非驱动逻辑本身。