1. 设备树基础概念

1.1 什么是设备树(Device Tree)

设备树(Device Tree,DT)是一种描述硬件资源的数据结构,它使用树形节点来记录系统中所有可发现的设备、内存映射、中断、时钟、GPIO 等硬件信息。设备树源文件(.dts)经过编译生成设备树二进制文件(.dtb),在系统启动时由引导加载程序传递给内核,内核据此完成设备驱动与硬件资源的绑定。

核心要点:设备树不是代码,而是数据。它把硬件描述从内核源码中剥离出来,使同一份内核二进制可以适配多种硬件平台。
设备树结构示意图 / (根节点) cpus memory soc chosen i2c0 uart0 gpio0 eeprom rtc 说明: • 每个节点可包含属性(reg、interrupts、clocks 等)描述硬件资源 • 节点路径唯一标识设备,如 /soc/i2c0/eeprom

1.2 为什么需要设备树

在设备树出现之前,Linux 内核使用大量的板级文件(board-*.c)来注册硬件信息。这种做法在 ARM 架构上导致了严重的代码膨胀和维护灾难。设备树的引入解决了以下核心问题:

  • 硬件描述与内核解耦:同一份内核镜像可以支持多种硬件平台,只需更换 .dtb 文件。
  • 减少内核代码量:不再需要为每块开发板编写数百行的板级注册代码。
  • 支持多平台统一:ARM、RISC-V、x86 等架构统一使用设备树描述硬件。
  • 便于社区维护:硬件描述集中管理,驱动代码更通用。
  • 动态加载能力:设备树覆盖(Device Tree Overlay)允许运行时添加或修改硬件描述。
历史背景:设备树最初源自 Open Firmware(IEEE 1275)规范,PowerPC 架构最早采用。2011 年 Linux 3.0 开始,ARM 架构强制要求使用设备树,结束了长达十年的板级文件混乱局面。
设备树 vs 板级文件:内核维护成本对比 板级文件方式(旧) board-omap4.c board-samsung.c board-imx6q.c ... 数百个 board-*.c 文件 设备树方式(新) omap4.dts exynos.dtsi imx6q.dts ... 统一的 .dts/.dtsi 文件 迁移

1.3 设备树与 ACPI 的区别

ACPI(Advanced Configuration and Power Interface)是 x86 平台广泛使用的硬件描述和电源管理标准。设备树与 ACPI 虽然目标相似,但在设计哲学、使用场景和实现方式上有显著差异:

对比维度 设备树(Device Tree) ACPI
起源 Open Firmware(IEEE 1275),PowerPC 率先采用 Intel、Microsoft、Toshiba 联合制定,x86 主导
主要平台 ARM、RISC-V、PowerPC、MIPS 等嵌入式/移动平台 x86/x86_64 服务器、PC、笔记本
数据格式 文本源文件(.dts)→ 二进制(.dtb),人类可读 ASL 源码 → AML 字节码,需反编译查看
运行时能力 支持设备树覆盖(Overlay),动态加载/卸载 支持动态表加载,但机制复杂
电源管理 弱,需驱动自行处理电源控制 强,内置 _PS0/_PS3、C-state、P-state 等标准方法
热插拔 通过 Overlay 实现,需手动触发 原生支持 PCIe、USB 等热插拔事件
内核集成度 设备树由 bootloader 加载,内核解析 ACPI 表由固件(UEFI/BIOS)提供,内核解释执行 AML
适用场景 嵌入式系统、IoT、开发板、定制硬件 服务器、桌面 PC、工作站、复杂电源管理场景
重要提示:虽然设备树和 ACPI 都用于硬件描述,但它们在 Linux 内核中走的是完全不同的驱动路径。设备树驱动使用 of_ 系列 API(如 of_property_read_u32),而 ACPI 驱动使用 acpi_ 系列 API(如 acpi_evaluate_object)。两者不能混用。
设备树与 ACPI 架构对比 设备树架构 Bootloader 加载 .dtb 内核解析设备树(unflatten) of_ 系列 API 读取属性 驱动 probe → 设备绑定 ACPI 架构 UEFI/BIOS 提供 ACPI 表 内核 AML 解释器执行字节码 acpi_ 系列 API 调用方法 驱动 probe → 设备绑定 设备树:静态描述 + 驱动主动解析 | ACPI:动态方法 + 内核解释执行

1.4 设备树的核心文件类型

在实际开发中,你会接触到以下几种设备树相关文件:

文件类型 扩展名 说明
设备树源文件 .dts 描述单块开发板或设备的完整硬件树,是编译入口
设备树包含文件 .dtsi 公共硬件描述,类似头文件,可被多个 .dts 包含
设备树二进制 .dtb .dts 经 dtc 编译后的二进制文件,供内核使用
设备树覆盖源文件 .dtso 描述增量修改的覆盖源文件,用于动态加载
设备树覆盖二进制 .dtbo .dtso 编译后的覆盖二进制文件
开发提示:使用 dtc -I dtb -O dts -o output.dts input.dtb 可以将二进制 .dtb 反编译为可读的 .dts 源文件,这是调试硬件描述问题的常用手段。

1.5 一个简单的设备树示例

以下是一个简化的设备树源文件,展示了基本的节点和属性结构:

/dts-v1/;

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

    cpus {
        #address-cells = <1>;
        #size-cells = <0>;

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

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

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

        serial@101f1000 {
            compatible = "ns16550";
            reg = <0x101f1000 0x1000>;
            interrupts = <0x1 0x0>;
            clock-frequency = <3686400>;
        };

        i2c@101f2000 {
            compatible = "acme,i2c-bus";
            reg = <0x101f2000 0x1000>;
            interrupts = <0x2 0x0>;
            #address-cells = <1>;
            #size-cells = <0>;

            eeprom@50 {
                compatible = "atmel,24c02";
                reg = <0x50>;
                pagesize = <16>;
            };
        };
    };
};
关键语法说明:
• /dts-v1/; — 版本标识,必须位于文件开头
• compatible — 最重要的属性,用于驱动匹配,格式为 "厂商,型号"
• reg — 地址和大小,格式由 #address-cells 和 #size-cells 决定
• interrupts — 中断号与触发类型
• ranges; — 地址转换映射,空值表示 1:1 映射