1. 设备树(DTS)概述

1.1 设备树的起源

在早期的 Linux 内核中,ARM、PowerPC 等嵌入式架构的板级硬件信息被硬编码在 arch/arm/mach-xxx 目录下的 C 语言文件中。每增加一块新板卡,就需要新增或修改大量 platform_device 结构体定义,导致内核源码急剧膨胀,且代码冗余严重。

设备树(Device Tree)是一种描述硬件资源的数据结构,起源于 Open Firmware(IEEE 1275)规范。它使用树形节点描述 CPU、内存、外设、中断、GPIO、时钟等硬件信息,在系统启动时由 Bootloader(如 U-Boot)传递给内核,实现“硬件描述与内核代码分离”。

关键里程碑: Linux 主线内核自 3.x 时代起,强制要求 ARM 架构新平台必须使用设备树,不再接受纯板级文件的新板卡支持。

1.2 设备树的作用

  • 硬件描述标准化: 以统一格式描述 CPU、内存、总线、外设、中断控制器、GPIO 控制器、时钟、DMA 等资源。
  • 驱动与硬件解耦: 驱动通过 compatible 属性匹配设备,无需关心具体板级布局。
  • 减少内核冗余: 同一份内核镜像可支持多款不同板卡,只需更换设备树文件(.dtb)。
  • 启动参数传递: 可携带内存大小、内核命令行、MAC 地址等运行时信息。
  • 社区与厂商协作: 芯片厂商只需提供 SoC 级 .dtsi,板卡厂商继承并定制板级 .dts。

1.3 与传统 platform_device 注册方式的对比

对比维度 传统 platform_device 注册 设备树 DTS 方式
硬件描述位置 硬编码在 C 文件中(如 arch/arm/mach-xxx/devices.c) 独立文本文件(.dts / .dtsi),编译为 .dtb
驱动匹配方式 通过 platform_driver_register() 与 platform_device 名称匹配 通过 of_match_table 匹配 compatible 属性
资源获取 从 platform_device 的 resource 数组获取 通过 of_* API 从设备树节点解析
多板卡支持 需为每块板卡编写或修改 C 代码,重新编译内核 只需更换 .dtb 文件,内核镜像不变
代码维护性 板级文件膨胀,冗余多,社区维护成本高 结构清晰,SoC 级与板级分离,易于维护
启动时灵活性 静态编译,无法动态调整 Bootloader 可修改或覆盖设备树属性
开发者提示: 虽然设备树已成为主流,但在 x86 和部分早期嵌入式平台中,传统 ACPI 或硬编码方式仍然存在。理解两种方式的差异,有助于阅读不同架构的内核代码。

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

设备树在内核启动流程中扮演“硬件信息提供者”的角色,其数据流与关键模块位置如下:

设备树在 Linux 内核启动流程中的位置 Bootloader (U-Boot) 设备树 Blob (.dtb) 内核解压 / 早期启动 OF Core (drivers/of/) 驱动匹配 & probe 内核源码位置:drivers/of/ 目录包含 fdt.c, address.c, irq.c, gpio.c, device.c, platform.c 等核心解析模块

设备树在内核中的关键源码路径与职责:

内核路径 职责说明
drivers/of/fdt.c 扁平设备树(FDT)的早期解析,将 .dtb 二进制数据转换为内核设备树结构
drivers/of/address.c 解析 reg、ranges 等地址映射属性,完成地址转换
drivers/of/irq.c 解析中断控制器及中断映射关系
drivers/of/gpio.c 解析 GPIO 控制器及 GPIO 引用
drivers/of/device.c 设备节点与驱动核心的绑定接口
drivers/of/platform.c 将设备树节点创建为 platform_device,触发驱动 probe
include/linux/of.h OF 核心 API 头文件,包含 of_* 函数声明
注意事项: 设备树并非万能。对于动态热插拔设备(如 USB、PCIe),设备树仅描述控制器本身,具体外设仍需通过标准总线枚举机制识别。设备树主要适用于“静态板级硬件”的描述。