2. 设备树基础:设备树语法、节点与属性详解
设备树(Device Tree,DT)是嵌入式 Linux 系统中用于描述硬件拓扑结构和资源配置的机制。在 RK3399 平台中,设备树是连接内核驱动与硬件外设的桥梁。本章将系统讲解设备树的语法结构、节点定义、关键属性(reg、interrupts、compatible)以及它们在 RK3399 显示驱动中的应用。
2.1 设备树基本语法与结构
设备树采用树形层次结构,以 .dts(Device Tree Source)文件编写,经过 dtc 编译器生成 .dtb(Device Tree Blob)二进制文件,由 Bootloader 传递给内核解析。其核心语法如下:
- 节点(Node):以
/为根节点,每个节点代表一个硬件设备或总线。 - 属性(Property):键值对形式,描述设备的特性,如地址、中断、兼容性等。
- 标签(Label):用于引用节点,格式为
label: node-name。 - 包含文件:使用
#include引入.dtsi公共头文件。
of_* 系列 API 在运行时解析设备树,获取硬件资源信息。
2.1.1 设备树层次结构示例
以下是一个简化的 RK3399 设备树片段,展示根节点、CPU 节点、内存节点和 I2C 控制器的层次关系:
/dts-v1/;
#include "rk3399.dtsi"
/ {
model = "RK3399 Evaluation Board";
compatible = "rockchip,rk3399";
chosen {
stdout-path = &uart2;
};
memory@00200000 {
device_type = "memory";
reg = <0x0 0x00200000 0x0 0x7fe00000>;
};
cpus {
#address-cells = <2>;
#size-cells = <0>;
cpu0: cpu@0 {
device_type = "cpu";
compatible = "arm,cortex-a72", "arm,armv8";
reg = <0x0 0x0>;
enable-method = "psci";
};
cpu1: cpu@1 {
device_type = "cpu";
compatible = "arm,cortex-a72", "arm,armv8";
reg = <0x0 0x1>;
enable-method = "psci";
};
};
i2c0: i2c@ff3c0000 {
compatible = "rockchip,rk3399-i2c", "rockchip,rk3288-i2c";
reg = <0x0 0xff3c0000 0x0 0x1000>;
interrupts = <GIC_SPI 57 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&cru SCLK_I2C0>, <&cru PCLK_I2C0>;
clock-names = "i2c", "pclk";
status = "disabled";
};
};
rk3399.dtsi 文件定义了 SoC 内部所有外设的默认节点,板级 .dts 文件通过 &label 引用并覆盖属性(如 status = "okay")来启用外设。
2.2 节点与属性详解
设备树中的每个节点都包含一组标准属性和特定于设备的属性。以下是最核心的三个属性:
| 属性名 | 作用 | 格式 | RK3399 示例 |
|---|---|---|---|
compatible |
指定设备兼容的驱动名称,用于内核匹配驱动 | 字符串列表,按优先级从高到低排列 | "rockchip,rk3399-i2c", "rockchip,rk3288-i2c" |
reg |
描述设备在总线上的地址空间(基地址 + 长度) | 由 #address-cells 和 #size-cells 决定单元数 |
<0x0 0xff3c0000 0x0 0x1000> |
interrupts |
描述设备使用的中断号、触发类型 | 中断控制器相关,通常包含中断类型、编号、标志 | <GIC_SPI 57 IRQ_TYPE_LEVEL_HIGH> |
2.2.1 compatible 属性:驱动匹配的关键
compatible 是设备树中最关键的属性,它告诉内核该设备应该由哪个驱动程序控制。内核的 of_match_table 机制会遍历设备树节点的 compatible 字符串列表,与驱动注册的匹配表进行比对。
- 格式:
"厂商,型号"或"厂商,型号", "通用型号"。 - 匹配优先级:列表中的第一个字符串优先级最高,内核按顺序匹配。
- RK3399 实践:显示控制器 VOP 的
compatible为"rockchip,rk3399-vop-big"和"rockchip,rk3399-vop-lit",分别对应大核和小核 VOP。
/* RK3399 VOP 节点 compatible 示例 */
vopb: vop@ff900000 {
compatible = "rockchip,rk3399-vop-big";
reg = <0x0 0xff900000 0x0 0x3efc>;
interrupts = <GIC_SPI 118 IRQ_TYPE_LEVEL_HIGH>;
/* ... */
};
vopl: vop@ff8f0000 {
compatible = "rockchip,rk3399-vop-lit";
reg = <0x0 0xff8f0000 0x0 0x3efc>;
interrupts = <GIC_SPI 119 IRQ_TYPE_LEVEL_HIGH>;
/* ... */
};
2.2.2 reg 属性:地址空间描述
reg 属性用于描述设备在总线上的地址范围。其格式由父节点的 #address-cells 和 #size-cells 决定:
#address-cells:地址字段占用的 32 位单元数(RK3399 通常为 2,支持 64 位地址)。#size-cells:长度字段占用的 32 位单元数(通常为 2 或 1)。
对于 RK3399 的显示控制器,reg 通常包含三个区域:
- 寄存器基地址:控制寄存器映射。
- MMU 或 AHB 地址:用于 DMA 或显示缓冲区。
- 保留区域:某些 IP 核的额外配置空间。
/* reg 属性解析示例 */
vopb: vop@ff900000 {
reg = <0x0 0xff900000 0x0 0x3efc>,
<0x0 0xff904000 0x0 0x1000>;
reg-names = "vop", "vop-mmu";
};
reg 的地址值必须与 SoC 数据手册中的物理地址一致。如果地址配置错误,驱动访问寄存器时将导致系统崩溃(MMU 未映射或访问非法地址)。
2.2.3 interrupts 属性:中断资源配置
interrupts 属性描述设备使用的中断线。RK3399 使用 GIC(Generic Interrupt Controller)v3,中断类型包括:
| 中断类型 | 宏定义 | 说明 |
|---|---|---|
| SPI(Shared Peripheral Interrupt) | GIC_SPI |
共享外设中断,多个 CPU 可接收 |
| PPI(Private Peripheral Interrupt) | GIC_PPI |
私有外设中断,仅绑定到特定 CPU |
| 触发类型 | IRQ_TYPE_LEVEL_HIGH / IRQ_TYPE_EDGE_RISING |
电平触发或边沿触发 |
RK3399 VOP 显示控制器使用 SPI 中断,中断号分别为 118(VOPB)和 119(VOPL),触发类型为高电平有效:
interrupts = <GIC_SPI 118 IRQ_TYPE_LEVEL_HIGH>;
2.3 设备树关键属性关系图
以下 SVG 图展示了设备树节点、compatible、reg、interrupts 属性与内核驱动、硬件资源之间的映射关系:
2.4 设备树中的其他重要属性
除了上述三个核心属性,RK3399 显示驱动中常用的属性还包括:
| 属性 | 用途 | RK3399 显示示例 |
|---|---|---|
clocks |
指定设备使用的时钟源 | clocks = <&cru ACLK_VOP0>, <&cru DCLK_VOP0>; |
clock-names |
为时钟指定名称,便于驱动获取 | clock-names = "aclk", "dclk"; |
status |
控制设备启用或禁用 | status = "okay"; 或 "disabled" |
pinctrl-* |
配置引脚复用功能 | pinctrl-0 = <&edp_hpd>; |
ports |
描述显示输出端口(如 HDMI、DP、MIPI) | 包含 endpoint 子节点,连接显示控制器和外部接口 |
2.4.1 显示端口(ports)属性详解
在 RK3399 显示驱动中,ports 属性用于描述 VOP 与外部显示接口(如 HDMI、DP、MIPI DSI)之间的连接关系。每个 port 代表一个显示输出通道,endpoint 子节点定义具体的连接目标。
/* VOPB 显示端口示例 */
vopb: vop@ff900000 {
compatible = "rockchip,rk3399-vop-big";
reg = <0x0 0xff900000 0x0 0x3efc>;
interrupts = <GIC_SPI 118 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&cru ACLK_VOP0>, <&cru DCLK_VOP0>, <&cru HCLK_VOP0>;
clock-names = "aclk", "dclk", "hclk";
status = "disabled";
ports {
#address-cells = <1>;
#size-cells = <0>;
port@0 {
reg = <0>;
#address-cells = <1>;
#size-cells = <0>;
vopb_out_edp: endpoint@0 {
reg = <0>;
remote-endpoint = <&edp_in_vopb>;
};
vopb_out_hdmi: endpoint@1 {
reg = <1>;
remote-endpoint = <&hdmi_in_vopb>;
};
};
};
};
ports 中的 remote-endpoint 是否正确连接,以及对应的外部接口节点(如 hdmi、edp)的 status 是否为 "okay"。
2.5 设备树编译与调试
在 RK3399 平台开发中,设备树的编译和调试是日常操作。以下是常用命令和注意事项:
| 操作 | 命令 | 说明 |
|---|---|---|
| 编译 .dts 为 .dtb | dtc -I dts -O dtb -o rk3399.dtb rk3399.dts |
使用设备树编译器 |
| 反编译 .dtb 为 .dts | dtc -I dtb -O dts -o rk3399.dts rk3399.dtb |
用于查看实际加载的设备树 |
| 查看系统当前设备树 | ls /proc/device-tree/ |
内核导出的设备树文件系统 |
| 检查设备树节点状态 | cat /proc/device-tree/vopb/status |
返回 "okay" 或 "disabled" |
dtc -I dts -O dtb 验证语法正确性。
2.6 本章小结
设备树是 RK3399 平台硬件描述的核心机制。本章重点讲解了:
- 设备树语法:树形层次结构、节点定义、标签引用。
- 关键属性:
compatible用于驱动匹配,reg用于地址映射,interrupts用于中断注册。 - 显示驱动相关属性:
clocks、ports、status等。 - 编译与调试方法:使用
dtc工具和/proc/device-tree文件系统。