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 内核中的位置
设备树在内核启动流程中扮演“硬件信息提供者”的角色,其数据流与关键模块位置如下:
设备树在内核中的关键源码路径与职责:
| 内核路径 | 职责说明 |
|---|---|
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),设备树仅描述控制器本身,具体外设仍需通过标准总线枚举机制识别。设备树主要适用于“静态板级硬件”的描述。