1. 设备树基础概念

1.1 什么是设备树(Device Tree)

设备树(Device Tree,DT)是一种描述硬件资源的数据结构,它使用树形节点来记录系统中所有可访问的设备、总线、中断控制器、时钟、GPIO 等硬件信息。设备树源文件(.dts)经过设备树编译器(DTC)编译为二进制设备树 Blob(.dtb),在系统启动时由 Bootloader 传递给内核。

核心要点:设备树本质上是“硬件拓扑的静态快照”,它将板级硬件细节从内核代码中剥离,使同一份内核镜像可以支持多种硬件平台。
设备树整体结构示意图 / (根节点) cpus memory soc chosen i2c@... spi@... uart@... eeprom rtc 每个节点包含属性(reg、interrupts、clocks 等),描述设备的地址、中断、时钟等资源 内核通过设备树节点名称和 compatible 属性匹配驱动

1.2 为什么需要设备树

在设备树出现之前,嵌入式 Linux 内核通过大量 板级文件(board file) 来注册硬件信息。每个开发板都需要在 arch/arm/mach-xxx 下编写成百上千行的 C 代码,导致内核代码冗余、维护困难。

对比维度 传统板级文件方式 设备树方式
硬件描述位置 内核源码 arch/arm/mach-* 独立的 .dts 文件
内核镜像通用性 每板编译一次内核 同一内核镜像支持多板
硬件变更影响 修改内核源码,重新编译 修改 .dts,重新编译 DTB
社区维护成本 高(大量重复代码) 低(描述与驱动分离)
启动参数传递 ATAG 列表(固定格式) 设备树节点 + 属性(灵活扩展)
关键收益:设备树实现了“硬件描述”与“驱动代码”的解耦。驱动开发者只需关注 compatible 匹配,板级开发者只需维护 .dts 文件,双方互不干扰。
设备树解耦示意图:硬件描述 vs 驱动代码 硬件描述(.dts) reg = <0x1c00000 0x1000> interrupts = <0 31 4> clocks = <&clk_uart> compatible = "ns16550a" pinctrl-0 = <&uart0_pins> 匹配 驱动代码(.c) .compatible = "ns16550a" .probe = uart_probe .remove = uart_remove of_match_table = match // 通用驱动,不依赖板级 解耦效果 更换硬件时 只需修改 .dts 驱动代码无需变更 内核镜像保持通用

1.3 设备树与平台设备(Platform Device)的关系

平台设备(Platform Device)是 Linux 内核中用于表示“非可枚举总线设备”的抽象层,典型代表包括 SoC 内部集成的 UART、I2C 控制器、SPI 控制器、DMA 控制器等。设备树与平台设备的关系可以概括为:设备树是平台设备的“数据源”,平台设备是设备树在内核中的“运行时实例”。

常见误区:设备树本身不是平台设备,也不是驱动。它只是“硬件配置清单”。内核在启动时解析设备树,为每个匹配的节点创建 platform_device,然后由 platform_driver 的 probe 函数接管。

内核启动过程中,设备树到平台设备的转换流程如下:

  1. Bootloader 加载 DTB 到内存,将 DTB 物理地址传递给内核。
  2. 内核早期启动阶段,unflatten_device_tree() 将 DTB 展开为 device_node 树。
  3. 平台设备创建阶段,of_platform_populate() 遍历设备树节点,为每个 compatible 匹配的节点创建 platform_device。
  4. 驱动匹配阶段,内核根据 platform_device 的 name 或 compatible 属性,与 platform_driver 的 id_table 进行匹配。
  5. probe 执行,驱动从设备树节点中读取 reg、interrupts、clocks 等资源,完成设备初始化。
设备树 → 平台设备 → 驱动匹配流程 Bootloader 加载 DTB 内核启动 展开 device_node of_platform_populate 创建 platform_device 驱动匹配 probe 执行 关键数据结构对应关系 .dts 节点: uart0: serial@1c00000 { compatible = "ns16550a"; reg = <0x1c00000 0x1000>; } device_node: 内核内存中的树形节点,包含 full_name、type、compatible 等字段 platform_device: 包含 resource[] 数组(从 reg/interrupts 转换而来)、dev.of_node 指针 platform_driver: 通过 of_match_table 或 id_table 与 platform_device 匹配 probe 函数: 使用 of_iomap()、of_get_property()、irq_of_parse_and_map() 读取资源

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/ 下的节点结构,确认内核实际解析到的设备树内容是否符合预期。这是排查“设备未注册”问题的第一步。
设备树在嵌入式 Linux 系统中的位置 应用层(用户空间) 内核驱动层(platform_driver / 其他驱动) 设备树层(device_node / platform_device) 硬件层(SoC / 外设 / 总线) 驱动通过设备树 获取硬件资源 设备树描述 硬件拓扑