1. 设备树基础概念

1.1 什么是设备树(Device Tree)

设备树(Device Tree,DT)是一种描述硬件资源的数据结构,它使用树形节点来记录系统中所有可访问的设备、总线、中断控制器、时钟、GPIO 等硬件信息。设备树源文件(.dts)经过编译生成设备树二进制文件(.dtb),由 Bootloader 传递给内核,内核解析后自动完成设备驱动与硬件资源的绑定。

核心定义:设备树 = 硬件拓扑的静态描述语言 + 内核驱动匹配的桥梁。

一个典型的设备树节点结构如下:

/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";
            reg = <0x0>;
        };
    };

    memory@80000000 {
        device_type = "memory";
        reg = <0x80000000 0x20000000>;  /* 512 MB */
    };

    uart0: serial@44e09000 {
        compatible = "ti,am3352-uart", "ti,omap3-uart";
        reg = <0x44e09000 0x2000>;
        interrupts = <72>;
        status = "okay";
    };
};
关键字段说明:
compatible — 驱动匹配的核心字符串,格式为 "厂商,型号";
reg — 地址/长度对,描述设备占用的物理地址空间;
interrupts — 中断号,依赖父级中断控制器解析。
设备树节点层级结构 / (根节点) cpus memory@80000000 uart0: serial@44e09000 cpu@0 属性: compatible, reg, interrupts 说明: • 每个节点有节点名(如 cpu@0)和一组属性(key-value 对) • 节点通过 compatible 属性与内核驱动匹配 • 子节点继承父节点的总线/中断/时钟上下文

1.2 为什么需要设备树

在设备树出现之前,嵌入式 Linux 内核使用 板级文件(Board File) 来注册硬件信息。每个开发板都需要在 arch/arm/mach-xxx 下编写大量 C 代码,导致内核源码中充斥着平台相关的硬编码。这种模式存在三个严重问题:

  1. 代码膨胀 — 每个新板子都要新增或修改板级文件,内核主线难以维护。
  2. 耦合过高 — 硬件描述与驱动代码深度绑定,更换 SoC 或外设需要重新编译内核。
  3. 碎片化严重 — 厂商各自维护分支,社区无法统一推进新特性。

设备树将硬件描述从内核源码中剥离出来,形成独立的 .dts 文件,实现了以下关键改进:

对比维度 板级文件(旧) 设备树(新)
硬件描述位置 内核源码 arch/arm/mach-xxx 独立 .dts 文件,与内核解耦
更换外设 修改 C 代码,重新编译内核 修改 .dts,重新编译 DTB
多板支持 每个板子一个板级文件 通过 #include 和 .dtsi 复用
驱动匹配方式 静态注册 platform_device compatible 字符串动态匹配
社区维护成本 高(碎片化严重) 低(统一描述语言)
注意:设备树不是万能的。它只描述硬件“有什么”和“在哪里”,不描述硬件“怎么用”。驱动仍然需要实现具体的寄存器操作和中断处理逻辑。
板级文件 vs 设备树:硬件描述流程对比 板级文件(旧) arch/arm/mach-xxx/board-xxx.c 静态注册 platform_device / platform_data 重新编译内核 → 烧录 zImage 设备树(新) board.dts → DTC 编译 → board.dtb Bootloader 加载 DTB 到内存 内核解析 DTB → 动态创建设备 vs 设备树方案:修改 .dts → 重新编译 DTB → 重启即可,无需重新编译内核。 板级文件方案:修改 C 代码 → 重新编译内核 → 烧录整个内核镜像,周期长且风险高。

1.3 设备树与 ACPI 的区别

ACPI(Advanced Configuration and Power Interface)是 x86 平台的标准硬件描述和电源管理接口,与设备树在理念和实现上有本质差异。两者对比如下:

对比维度 设备树(DT) ACPI
主要应用平台 ARM、RISC-V、PowerPC 等嵌入式 x86、x86_64(PC/服务器)
描述语言 DTS 文本格式,编译为二进制 DTB ASL(ACPI Source Language),编译为 AML
运行时行为 静态描述,内核解析后不再变化 AML 解释执行,支持热插拔、电源事件
电源管理 依赖驱动实现,DT 只描述设备存在 内建 _PS0/_PS3 等电源状态控制方法
热插拔支持 有限,需额外驱动逻辑 原生支持 PCIe/USB 热插拔事件
BIOS/UEFI 依赖 不需要,Bootloader 直接加载 DTB 必须由 UEFI/BIOS 提供 ACPI 表
驱动匹配方式 compatible 字符串 HID(Hardware ID)/ CID(Compatible ID)
社区生态 Linux 内核主线 + 设备树邮件列表 UEFI 论坛 + 微软/Intel 主导
核心差异总结:
设备树是“静态硬件清单”,适合资源受限、硬件固定的嵌入式系统;
ACPI 是“可编程硬件抽象层”,适合需要动态电源管理和热插拔的通用计算平台。
设备树 vs ACPI:架构与运行机制对比 设备树(DT)架构 .dts 源文件(文本) DTC 编译器 → .dtb 二进制 Bootloader 加载 DTB 到内存 内核 OF 子系统解析 → 创建设备 ACPI 架构 ASL 源文件 → iASL 编译 → AML UEFI/BIOS 提供 ACPI 表(RSDP→XSDT) 内核 ACPI 解释器执行 AML 动态创建设备 + 电源管理 + 热插拔 设备树:静态描述,无运行时解释执行,适合固定硬件;ACPI:AML 虚拟机解释执行,支持动态事件和电源策略。 两者不可互换,选择取决于目标平台和硬件复杂度。

1.4 设备树在嵌入式 Linux 中的位置

设备树是嵌入式 Linux 启动流程中的关键一环,位于 Bootloader 与内核之间:

  1. Bootloader(U-Boot) 从存储介质读取 .dtb 文件。
  2. Bootloader 将 DTB 加载到内存,并通过寄存器(如 r2)传递地址给内核。
  3. 内核启动早期调用 unflatten_device_tree() 将 DTB 展开为 device_node 树。
  4. 驱动通过 of_match_table 匹配 compatible 字符串,获取 reg、interrupts 等资源。
实战建议:调试设备树问题时,优先检查 /sys/firmware/devicetree/base 下的节点内容,确认内核实际解析到的硬件信息是否与 .dts 一致。