1. 设备树概述:起源、作用、与ACPI的区别、在Linux内核中的位置

1.1 设备树的起源:从“硬编码”到“数据驱动”

在早期的嵌入式Linux系统中(如ARM Linux 2.6时代),内核通过大量板级文件(board files)来描述硬件。每个开发板都对应一个 arch/arm/mach-xxx/board-xxx.c 文件,其中硬编码了内存地址、中断号、GPIO映射、外设寄存器基址等信息。这种方式的弊端随着硬件种类的爆炸式增长而迅速暴露:

  • 代码冗余:同一款SoC搭配不同外设时,需要复制粘贴大量相似代码。
  • 维护困难:每增加一个板子,就需要提交一个新的board文件,导致内核源码树急剧膨胀。
  • 二进制耦合:内核镜像与特定硬件绑定,无法实现通用内核启动。
关键转折点:PowerPC架构率先引入设备树(Device Tree)概念,将硬件描述从内核代码中剥离为独立的数据结构。ARM社区在2011年(Linux 3.0左右)开始大规模迁移,到2014年(Linux 3.16)基本完成过渡。如今,设备树已成为ARM、RISC-V、MIPS等嵌入式架构的标准硬件描述方式。
设备树起源演进时间线 板级文件时代 arch/arm/mach-xxx 硬编码、重复、难维护 PowerPC先行 Open Firmware 设备树原型诞生 ARM大规模迁移 Linux 3.0 ~ 3.16 2011 ~ 2014年 当前 核心变化: • 硬件描述从 C代码 变为 独立数据文件(.dts / .dtsi) • 内核镜像与板级硬件解耦,同一内核可启动不同设备树描述的硬件 • 硬件变更只需修改设备树源文件,无需重新编译内核

1.2 设备树的作用:硬件描述的“桥梁”

设备树(Device Tree,简称DT)是一种树形数据结构,用于描述不可枚举的硬件信息。它的核心作用可以概括为以下三点:

作用维度 具体说明 典型示例
硬件拓扑描述 描述CPU、内存、总线、外设之间的连接关系和地址映射 CPU核心数、内存基址与大小、SPI/I2C总线上的设备
资源配置传递 向驱动程序传递中断号、寄存器地址、时钟频率、DMA通道等参数 interrupts = <0 31 4>; 表示SPI中断31,高电平触发
板级差异抽象 同一SoC的不同开发板通过不同设备树文件区分,内核代码无需修改 imx6ull-14x14-evk.dts vs imx6ull-mini.dts
一句话总结:设备树是连接硬件板级设计与内核驱动程序的“契约文档”。Bootloader(如U-Boot)将编译后的设备树二进制(DTB)传递给内核,内核解析后自动匹配驱动并完成初始化。

1.3 设备树与ACPI的区别:嵌入式 vs 桌面/服务器

ACPI(Advanced Configuration and Power Interface)是x86/ARM64服务器领域的主流硬件描述标准。两者虽然目标相似,但设计哲学和应用场景截然不同:

对比维度 设备树(DT) ACPI
起源 Open Firmware,PowerPC/嵌入式领域 PC/服务器领域,Intel主导
数据格式 纯文本源文件(.dts)→ 二进制(.dtb) ASL源文件 → AML字节码(二进制表)
运行时修改 Bootloader可修改/叠加(overlay) 操作系统通过AML解释器动态评估
电源管理 基本支持,依赖驱动实现 原生深度集成(C状态、P状态、热管理)
热插拔 有限支持(通过overlay) 原生支持(PCIe、USB等)
适用场景 嵌入式、IoT、ARM/RISC-V单板机 桌面、服务器、笔记本、x86/ARM64服务器
复杂度 低,学习曲线平缓 高,需要理解AML虚拟机
内核代码量 约5万行(驱动+核心框架) 约30万行(解释器+表+驱动)
设备树 vs ACPI:适用场景对比 设备树(DT) • 嵌入式系统(ARM/RISC-V/MIPS) • IoT设备、智能家居、工业控制 • 单板计算机(Raspberry Pi、BeagleBone) • 硬件固定、无需热插拔的场景 • 资源受限、需要快速启动的设备 ACPI • 桌面PC、笔记本电脑 • 企业服务器、数据中心 • x86/ARM64服务器平台 • 需要高级电源管理(ACPI 6.x) • 支持PCIe热插拔、USB设备枚举 vs
重要提示:虽然ACPI功能更强大,但设备树在嵌入式领域具有不可替代的优势——轻量、透明、易于调试。对于嵌入式开发者而言,设备树是必须掌握的核心技能。即使ARM64服务器开始支持ACPI,设备树在嵌入式领域的统治地位在未来10年内不会改变。

1.4 设备树在Linux内核中的位置

设备树在Linux内核源码树中占据明确的物理位置和逻辑层级:

1.4.1 源码目录结构

linux/
├── arch/
│   ├── arm/boot/dts/          # ARM架构设备树源文件(.dts/.dtsi)
│   ├── arm64/boot/dts/        # ARM64架构设备树源文件
│   ├── riscv/boot/dts/        # RISC-V架构设备树源文件
│   ├── microblaze/boot/dts/   # MicroBlaze软核设备树
│   └── ...
├── drivers/
│   ├── of/                    # Device Tree核心框架(of_* API)
│   │   ├── base.c             # 基础解析API
│   │   ├── address.c          # 地址转换(#address-cells / #size-cells)
│   │   ├── irq.c              # 中断映射
│   │   ├── device.c           # 设备创建与匹配
│   │   ├── platform.c         # platform_device创建
│   │   └── ...
│   └── ...
├── include/
│   ├── linux/of.h             # 设备树核心头文件
│   ├── linux/of_address.h     # 地址相关API
│   ├── linux/of_irq.h         # 中断相关API
│   ├── linux/of_platform.h    # platform设备相关API
│   └── ...
└── scripts/
    ├── dtc/                   # Device Tree Compiler(DTC)工具
    │   ├── dtc.c              # 编译器主程序
    │   ├── dtc-lexer.l        # 词法分析器
    │   ├── dtc-parser.y       # 语法分析器
    │   ├── checks.c           # 语法/语义检查
    │   └── ...
    └── ...

1.4.2 设备树在内核启动流程中的位置

设备树在Linux内核启动流程中的位置 Bootloader 加载DTB到内存 早期启动(head.S) DTB地址传递给内核 start_kernel() unflatten_device_tree() 驱动匹配与初始化 of_match_table → probe() 设备创建与注册 platform_device / i2c_client / spi_device 关键API调用链: start_kernel() → setup_arch() → unflatten_device_tree() → of_platform_populate() → driver probe()

1.4.3 设备树与内核子系统的交互关系

内核子系统 设备树提供的核心信息 对应API/框架
中断子系统 中断控制器类型、中断号、触发类型、级联关系 of_irq_parse_one()、irq_domain
时钟框架 时钟源、频率、时钟门控、父时钟关系 of_clk_get()、clk-provider.h
GPIO子系统 GPIO控制器、引脚号、复用功能、上拉/下拉 of_get_named_gpio()、pinctrl
DMA引擎 DMA控制器、通道号、传输方向、请求线 of_dma_request_slave_channel()
Pinctrl子系统 引脚复用配置、电气特性、组定义 pinctrl-bindings.txt、pinconf
Regulator框架 电压调节器、输出电压、负载开关 of_regulator_match()
输入子系统 按键、触摸屏、ADC输入映射 input.h + 设备树键值绑定
核心理解:设备树不是驱动程序,而是硬件配置数据库。驱动程序通过 of_* 系列API从设备树中读取配置,然后执行硬件初始化。这种“数据与逻辑分离”的设计,使得同一份驱动代码可以适配不同板级配置,极大提高了代码复用率。

1.5 本章小结

  • 起源:设备树从PowerPC的Open Firmware演化而来,解决了ARM Linux板级文件泛滥的问题。
  • 作用:作为硬件描述的标准格式,向内核传递拓扑结构、资源配置和板级差异信息。
  • 与ACPI的区别:设备树轻量、透明、适合嵌入式;ACPI功能全面但复杂,适合桌面/服务器。
  • 内核位置:源文件位于 arch/*/boot/dts/,核心框架位于 drivers/of/,编译工具为 scripts/dtc/。