1、设备树基础:什么是设备树?为什么需要设备树?设备树的历史与演进

1.1 什么是设备树?

设备树,英文叫 Device Tree,是一套用来描述硬件的数据结构。

说白了,它就是一份「硬件配置清单」。你想想看,一块电路板上有什么?CPU、内存、I2C控制器、SPI控制器、GPIO、中断控制器……这些东西怎么连接的?地址是多少?中断号是多少?设备树就是把这些信息用树形结构写出来。

我习惯把设备树比作「硬件的身份证」。内核启动时,不再需要去猜硬件长什么样,直接读设备树就行。

设备树的文件后缀通常是 .dts(Device Tree Source),编译后变成 .dtb(Device Tree Blob),内核在启动时加载它。

核心要点:设备树是描述硬件的结构化数据,不是代码,不执行逻辑,只提供信息。

1.2 为什么需要设备树?

这个问题,得从 Linux 内核的历史说起。

早期的 Linux 内核,硬件信息是硬编码在 C 代码里的。比如你要支持一块开发板,就得在 arch/arm/mach-xxx 目录下写一堆板级文件。每个板子一个文件,里面全是 platform_deviceresourcei2c_board_info 这些东西。

我在项目中遇到过这种情况:公司同时维护 5 款硬件,每款硬件又有 3 个变种。结果内核源码里塞了 15 套板级文件。每次改一个 GPIO,得改 15 个地方。改完还要重新编译整个内核。你说崩溃不崩溃?

设备树解决了三个核心痛点:

  • 硬件与驱动解耦:驱动不再关心硬件具体怎么接的,只关心设备树里有什么属性。
  • 减少内核代码量:板级文件从内核源码里移出去,变成设备树文件,单独维护。
  • 支持多平台启动:同一个内核镜像,换一个 .dtb 文件就能适配不同硬件。

我的经验:有一次客户临时改板子,把 UART2 的引脚换到了 UART1 的位置。我只需要改设备树里的引脚复用配置,重新编译 .dtb,内核都不用动。这在以前,得改板级文件、重新编译内核、再烧录。效率差距太大了。

1.3 设备树的历史与演进

设备树最早不是 Linux 的发明。它来自 Open Firmware,一种用于 PowerPC 和 SPARC 架构的固件标准。Open Firmware 用设备树来描述硬件,然后传递给操作系统。

Linux 内核在 PowerPC 架构上最早用上了设备树。后来 ARM 架构也面临板级文件泛滥的问题,社区开始讨论要不要引入设备树。

我记得大概是 2011 年左右,Linux 3.x 时代,ARM 社区正式决定全面转向设备树。当时 Linus Torvalds 也发过火,说 ARM 的板级文件太乱了,必须统一。

演进过程大致是这样的:

时间 阶段 说明
2005 年以前 PowerPC 使用 设备树在 PowerPC 架构上成熟应用
2011-2012 年 ARM 引入 Linux 3.2 开始支持 ARM 设备树
2013-2015 年 全面推广 新 ARM 平台强制使用设备树,板级文件逐步废弃
2016 年至今 标准化与扩展 设备树规范持续更新,支持更多外设和绑定

现在,不只是 ARM,RISC-V、X86(部分场景)、MIPS 等架构也在用设备树。它已经成为 Linux 内核描述硬件的标准方式。

注意:设备树不是万能的。它只描述「静态硬件」,像 USB 热插拔、PCIe 枚举这类动态硬件,设备树管不了。那些需要 ACPI 或者驱动自己去探测。

1.4 设备树的基本结构

一个最简单的设备树长什么样?我写个例子给你看:

/dts-v1/;

/ {
    model = "MyBoard";
    compatible = "vendor,myboard-v1";

    #address-cells = <1>;
    #size-cells = <1>;

    memory@80000000 {
        device_type = "memory";
        reg = <0x80000000 0x10000000>;
    };

    uart@10000000 {
        compatible = "ns16550";
        reg = <0x10000000 0x1000>;
        interrupts = <0 33 4>;
    };
};

这里有几个关键概念:

  • 根节点/ 表示整棵树的根,相当于电路板本身。
  • 节点memoryuart 都是节点,代表一个硬件设备。
  • 属性compatiblereginterrupts 都是属性,描述设备的特征。
  • 地址与大小#address-cells#size-cells 告诉解析器地址和长度用几个 32 位整数表示。

嗯,这里要注意:compatible 属性是驱动匹配的关键。内核通过它找到对应的驱动程序。命名规则一般是「厂商,设备型号」。

一句话总结:设备树就是硬件的「自我介绍」。内核启动时问它:「你是谁?你有哪些资源?你的中断号是多少?」设备树一一回答。没有设备树,内核就是个瞎子。

我个人觉得,理解设备树最好的方式就是动手写一个。别怕,后面几章我会带你一步步从零开始写设备树,还会结合 Regmap 和 MMIO 讲清楚怎么配置硬件寄存器。到时候你就知道,设备树这东西,其实没那么玄乎。