1. 设备树基础概念
1.1 什么是设备树(Device Tree)
设备树(Device Tree,DT)是一种描述硬件资源的数据结构,它使用树形节点来记录系统中所有可访问的设备、总线、中断控制器、时钟、GPIO 等硬件信息。设备树源文件(.dts)经过设备树编译器(DTC)编译为二进制设备树 Blob(.dtb),在系统启动时由 Bootloader 传递给内核。
核心要点:设备树本质上是“硬件拓扑的静态快照”,它将板级硬件细节从内核代码中剥离,使同一份内核镜像可以支持多种硬件平台。
1.2 为什么需要设备树
在设备树出现之前,嵌入式 Linux 内核通过大量 板级文件(board file) 来注册硬件信息。每个开发板都需要在 arch/arm/mach-xxx 下编写成百上千行的 C 代码,导致内核代码冗余、维护困难。
| 对比维度 | 传统板级文件方式 | 设备树方式 |
|---|---|---|
| 硬件描述位置 | 内核源码 arch/arm/mach-* |
独立的 .dts 文件 |
| 内核镜像通用性 | 每板编译一次内核 | 同一内核镜像支持多板 |
| 硬件变更影响 | 修改内核源码,重新编译 | 修改 .dts,重新编译 DTB |
| 社区维护成本 | 高(大量重复代码) | 低(描述与驱动分离) |
| 启动参数传递 | ATAG 列表(固定格式) | 设备树节点 + 属性(灵活扩展) |
关键收益:设备树实现了“硬件描述”与“驱动代码”的解耦。驱动开发者只需关注 compatible 匹配,板级开发者只需维护
.dts 文件,双方互不干扰。
1.3 设备树与平台设备(Platform Device)的关系
平台设备(Platform Device)是 Linux 内核中用于表示“非可枚举总线设备”的抽象层,典型代表包括 SoC 内部集成的 UART、I2C 控制器、SPI 控制器、DMA 控制器等。设备树与平台设备的关系可以概括为:设备树是平台设备的“数据源”,平台设备是设备树在内核中的“运行时实例”。
常见误区:设备树本身不是平台设备,也不是驱动。它只是“硬件配置清单”。内核在启动时解析设备树,为每个匹配的节点创建 platform_device,然后由 platform_driver 的 probe 函数接管。
内核启动过程中,设备树到平台设备的转换流程如下:
- Bootloader 加载 DTB 到内存,将 DTB 物理地址传递给内核。
- 内核早期启动阶段,
unflatten_device_tree()将 DTB 展开为device_node树。 - 平台设备创建阶段,
of_platform_populate()遍历设备树节点,为每个 compatible 匹配的节点创建platform_device。 - 驱动匹配阶段,内核根据
platform_device的name或compatible属性,与platform_driver的id_table进行匹配。 - probe 执行,驱动从设备树节点中读取 reg、interrupts、clocks 等资源,完成设备初始化。
1.4 设备树在嵌入式 Linux 中的位置
设备树贯穿整个嵌入式 Linux 系统的启动流程,从 Bootloader 到内核再到驱动,每个环节都依赖设备树提供硬件拓扑信息。以下表格总结了设备树在不同阶段的作用:
| 系统阶段 | 设备树参与方式 | 典型操作 |
|---|---|---|
| Bootloader | 加载 DTB 到内存,修改或覆盖节点 | fdt 命令修改内存大小、MAC 地址 |
| 内核早期启动 | 解析 DTB,建立内存映射、时钟、中断 | setup_machine_fdt()、early_init_dt_scan() |
| 平台设备创建 | 遍历节点,创建 platform_device | of_platform_populate()、of_platform_bus_probe() |
| 驱动 probe | 读取节点属性,获取硬件资源 | of_iomap()、platform_get_resource() |
| 运行时 | 通过 sysfs 导出设备树信息 | /sys/firmware/devicetree/base/ |
实战建议:在调试新板时,优先检查
/sys/firmware/devicetree/base/ 下的节点结构,确认内核实际解析到的设备树内容是否符合预期。这是排查“设备未注册”问题的第一步。