3、设备树基础概念:设备树的作用、结构、节点、属性、兼容性字符串与绑定文档
在RK3588这类高性能ARM SoC的嵌入式Linux开发中,设备树(Device Tree,DT)是连接硬件描述与内核驱动之间的核心桥梁。与传统的ACPI(x86平台)或硬编码板级文件(早期ARM平台)不同,设备树通过一种结构化的、与硬件描述解耦的数据格式,将“板级硬件拓扑”与“内核驱动代码”彻底分离。对于RK3588开发者而言,理解设备树不仅是编写DTS文件的前提,更是进行引脚复用(IOMUX)、电源域管理、外设时钟配置以及驱动匹配的基础。本章将深入剖析设备树的作用机制、语法结构、节点与属性规则,并重点讲解兼容性字符串与绑定文档的实战意义。
上图展示了RK3588设备树的核心概念框架。从根节点出发,系统会划分出soc、chosen、memory等顶层节点,而具体的外设(如UART、I2C、GPIO)则作为子节点挂载在soc节点下。每个节点通过属性描述硬件资源(地址、中断、时钟、引脚),而兼容性字符串则是驱动与节点匹配的关键索引。下面我们将逐一拆解这些概念。
3.1 设备树的作用:为什么RK3588离不开它?
设备树的核心作用可以概括为“硬件描述的标准化与解耦”。在RK3588的Linux开发中,它解决了三个核心痛点:
- 板级差异隔离:RK3588芯片本身是固定的,但不同开发板(如Rock 5B、香橙派5、自研核心板)的外设配置不同。设备树允许我们通过修改DTS文件来适配不同板卡,而无需重新编译内核镜像。
- 资源描述集中化:外设的寄存器基地址、中断号、DMA通道、时钟频率、引脚复用功能等,全部以节点和属性的形式集中描述。内核驱动通过标准API(如
of_iomap()、platform_get_resource())获取这些资源,避免硬编码。 - 减少内核代码冗余:在没有设备树的时代,每个板卡都需要在
arch/arm/mach-xxx下编写大量板级文件。设备树将这部分信息移出内核,使得RK3588的mainline内核可以一套代码支持多种设备。
在RK3588的实际启动流程中,Bootloader(U-Boot)会加载rk3588-evb.dtb文件,并将其传递给内核。内核在启动阶段解析DTB(Device Tree Blob),构建platform_device,然后与驱动进行匹配。如果设备树描述错误,即使驱动代码正确,外设也无法正常工作。
3.2 设备树的结构:从根节点到叶子节点
设备树是一个具有单一根节点的树状结构。在RK3588的DTS文件中,结构通常如下:
/dts-v1/;
#include "rk3588.dtsi" // 包含芯片级公共定义
/ {
model = "Rockchip RK3588 EVB";
compatible = "rockchip,rk3588-evb", "rockchip,rk3588";
chosen {
stdout-path = &uart2;
};
memory@200000 {
device_type = "memory";
reg = <0x0 0x200000 0x0 0x8000000>; // 起始地址0x200000,大小128MB
};
soc {
#address-cells = <2>;
#size-cells = <2>;
uart2: serial@fd890000 {
compatible = "rockchip,rk3588-uart";
reg = <0x0 0xfd890000 0x0 0x100>;
interrupts = ;
clocks = <&cru SCLK_UART2>, <&cru PCLK_UART2>;
clock-names = "baudclk", "apb_pclk";
pinctrl-names = "default";
pinctrl-0 = <&uart2m0_xfer>;
status = "okay";
};
};
};
关键结构要素包括:
- 根节点"/":包含
model(板卡型号)和compatible(板级兼容性)。 - soc节点:通常定义
#address-cells和#size-cells,用于描述子节点reg属性中的地址长度。RK3588是64位SoC,因此使用<2>表示64位地址。 - 外设节点:每个外设节点包含
compatible、reg、interrupts、clocks、pinctrl等属性。节点名称格式为名称@地址,如serial@fd890000。
3.3 节点与属性:设备树的“语法”核心
节点(Node)是设备树的基本单元,属性(Property)是节点的键值对。在RK3588开发中,最常用的属性如下表所示:
| 属性名 | 含义 | RK3588典型示例 |
|---|---|---|
compatible |
兼容性字符串,驱动匹配的关键 | "rockchip,rk3588-uart" |
reg |
寄存器地址及范围 | <0x0 0xfd890000 0x0 0x100> |
interrupts |
中断号及触发类型 | <GIC_SPI 317 IRQ_TYPE_LEVEL_HIGH> |
clocks / clock-names |
时钟句柄及名称 | <&cru SCLK_UART2> / "baudclk" |
pinctrl-0 |
引脚复用配置 | <&uart2m0_xfer> |
status |
设备状态(okay/disabled) | "okay" |
在编写节点时,必须遵循以下规则:
- 节点名称:小写字母、数字、逗号和连字符,格式为
device-type@unit-address。例如i2c@fd880000。 - 属性值类型:支持
<u32>(尖括号)、"字符串"(双引号)、[二进制](方括号)、&引用(&符号)。 - 标签(Label):如
uart2:,用于在DTS文件中通过&uart2进行引用追加或修改。
3.4 兼容性字符串与绑定文档:驱动匹配的“身份证”
兼容性字符串是设备树中最重要的属性。它的格式为"厂商,型号",例如"rockchip,rk3588-uart"。内核驱动通过of_match_table来匹配该字符串。匹配规则如下:
- 驱动中的
compatible列表与设备节点中的compatible属性进行完全字符串匹配。 - 设备节点可以包含多个兼容性字符串,按优先级从高到低排列。例如
compatible = "rockchip,rk3588-evb", "rockchip,rk3588",内核会先尝试匹配第一个,失败后再尝试第二个。
绑定文档(Binding Documentation)是设备树节点的“使用说明书”。它位于内核源码的Documentation/devicetree/bindings/目录下。例如,RK3588的UART绑定文档会定义:
Required properties:
- compatible: Should be "rockchip,rk3588-uart"
- reg: Register base address and size
- interrupts: Should contain the UART interrupt
Optional properties:
- dmas: DMA channel specifiers
- clock-frequency: UART clock frequency
在开发中,务必遵循绑定文档,否则即使驱动能匹配,也可能因缺少必要属性而初始化失败。
实战场景:如何为RK3588新增一个SPI设备?
假设我们需要在RK3588的SPI2总线上挂载一个MCP2515 CAN控制器。步骤如下:
- 在
rk3588.dtsi中找到spi2节点,确认其默认状态为disabled。 - 在板级DTS文件中追加子节点:
&spi2 {
status = "okay";
mcp2515: can@0 {
compatible = "microchip,mcp2515";
reg = <0>;
clocks = <&cru CLK_REF_MCP2515>;
interrupt-parent = <&gpio1>;
interrupts = <RK_PB2 IRQ_TYPE_LEVEL_LOW>;
spi-max-frequency = <10000000>;
};
};
这里的关键是compatible必须与内核中mcp251x驱动的of_match_table一致。同时,需要确认CLK_REF_MCP2515时钟已在CRU节点中定义。
3.5 常见误区与总结复盘
在RK3588设备树开发中,以下误区需要特别注意:
误区1:忘记修改status属性。RK3588的.dtsi中大部分外设默认status = "disabled",如果板级文件只添加了子节点而未将父节点设为"okay",设备不会被注册。
误区2:reg地址与芯片手册不一致。RK3588的寄存器地址是固定的,例如UART2基地址为0xFD890000。如果拼写错误,驱动通过of_iomap()映射时会得到错误地址,导致访问异常。
误区3:忽略pinctrl配置。RK3588的引脚复用非常灵活,同一个UART可以映射到多组引脚。如果pinctrl-0未正确指向&uart2m0_xfer,即使驱动加载成功,引脚功能也不正确。
总结复盘:设备树是RK3588 Linux开发的基石。掌握节点、属性、兼容性字符串和绑定文档,是进行后续驱动开发的前提。建议开发者在编写DTS时,先查阅芯片手册确认寄存器地址,再参考内核中的Documentation/devicetree/bindings/目录,最后通过dtc工具编译DTS并检查警告信息。在调试阶段,可以使用ls /proc/device-tree/查看内核实际解析的设备树内容,以验证节点是否被正确加载。