1. 设备树基础:DTS 语法与关键属性详解
设备树(Device Tree,DT)是描述硬件拓扑与资源分配的一种树形数据结构,在 Linux 内核中用于将平台设备信息从 Bootloader 传递给内核,替代了传统的 board-*.c 硬编码方式。对于 DRM/GPU 驱动移植而言,设备树节点是驱动与硬件之间的“契约”——GPU 的寄存器基址、中断号、时钟、复位、电源域等信息全部通过设备树节点描述。
of_match_table 匹配 compatible 属性来绑定设备节点,再通过 of_iomap、irq_of_parse_and_map 等 API 解析节点中的资源。
1.1 DTS 文件结构与基本语法
设备树源文件(.dts)采用树形缩进结构,每个节点以 节点名@地址 的形式定义,节点内包含属性和子节点。一个典型的 DTS 文件结构如下:
/dts-v1/;
/ {
model = "My GPU Development Board";
compatible = "vendor,board-rev1", "vendor,board";
chosen {
bootargs = "console=ttyS0,115200";
};
cpus {
#address-cells = <1>;
#size-cells = <0>;
cpu@0 {
compatible = "arm,cortex-a72";
reg = <0x0>;
};
};
memory@80000000 {
device_type = "memory";
reg = <0x80000000 0x40000000>; /* 1GB RAM */
};
gpu: gpu@fbc00000 {
compatible = "vendor,awesome-gpu", "simple-bus";
reg = <0xfbc00000 0x10000>;
interrupts = <0 42 4>;
clocks = <&clkc 15>, <&clkc 16>;
clock-names = "core", "mem";
resets = <&rstc 7>;
status = "okay";
};
};
/dts-v1/;必须位于文件头部,标识 DTS 版本。- 根节点
/包含整个系统描述。 - 节点名
gpu@fbc00000中的@fbc00000是单元地址(unit-address),通常与reg属性的首地址一致。 - 标签(label)如
gpu:可在其他文件中通过&gpu引用。
1.2 关键属性详解
设备树属性是节点携带的键值对,驱动通过解析这些属性获取硬件资源。下表列出 GPU 节点最常用的属性:
| 属性名 | 类型 | 说明 | GPU 节点示例 |
|---|---|---|---|
compatible |
字符串列表 | 驱动匹配标识,内核通过此属性查找对应的 of_device_id 表 |
"vendor,awesome-gpu", "simple-bus" |
reg |
u32/u64 数组 | 描述设备地址空间,格式由 #address-cells 和 #size-cells 决定 |
<0xfbc00000 0x10000> |
interrupts |
u32 数组 | 中断描述,格式依赖 #interrupt-cells(通常为 3:中断类型、编号、触发方式) |
<0 42 4> |
clocks |
phandle 数组 | 引用时钟控制器提供的时钟源 | <&clkc 15>, <&clkc 16> |
clock-names |
字符串列表 | 与 clocks 一一对应,驱动通过名字获取时钟 |
"core", "mem" |
resets |
phandle 数组 | 引用复位控制器,用于 GPU 的复位/解复位 | <&rstc 7> |
status |
字符串 | 设备状态:"okay" 启用,"disabled" 禁用 |
"okay" |
dma-coherent |
空属性 | 声明设备支持 DMA 一致性(无值,仅存在即生效) | dma-coherent; |
reg 属性的 #address-cells 和 #size-cells 在父节点中定义,GPU 节点本身不定义这两个属性。如果父节点设置 #address-cells = <2>,则 reg 中的地址和长度都必须使用 64 位(两个 u32 组合)。
1.3 compatible 属性与驱动匹配机制
compatible 是设备树中最关键的属性,它建立了硬件节点与内核驱动之间的桥梁。内核在启动时遍历设备树,对每个节点的 compatible 值与驱动注册的 of_device_id 表进行匹配。
匹配优先级:列表中的第一个字符串优先级最高,后续字符串作为兼容降级。例如:
compatible = "vendor,awesome-gpu-v2", "vendor,awesome-gpu", "simple-bus";
内核会优先尝试匹配 "vendor,awesome-gpu-v2",若没有驱动支持,则尝试 "vendor,awesome-gpu",最后尝试 "simple-bus"。
"厂商名,设备名" 格式,如 "arm,mali-g72"、"vivante,gc2000"。避免使用纯数字或过于通用的名称,防止匹配冲突。
1.4 reg 属性与地址空间描述
reg 属性描述设备占用的地址范围,格式为 <基址1 长度1 基址2 长度2 ...>。对于 GPU 节点,通常只有一个地址段(寄存器空间),但部分 GPU 可能包含多个独立地址区域(如控制寄存器 + 显存窗口)。
地址和长度的位数由父节点的 #address-cells 和 #size-cells 决定:
| 父节点设置 | reg 格式 | 示例 |
|---|---|---|
#address-cells = <1>; #size-cells = <1>; |
<addr size> |
<0xfbc00000 0x10000> |
#address-cells = <2>; #size-cells = <2>; |
<addr_hi addr_lo size_hi size_lo> |
<0x0 0xfbc00000 0x0 0x10000> |
驱动中通过 platform_get_resource(pdev, IORESOURCE_MEM, 0) 获取第一个 reg 段对应的内存资源。
1.5 interrupts 属性与中断描述
GPU 通常需要处理多种中断:渲染完成、页面翻转(VBlank)、错误异常等。interrupts 属性描述设备使用的中断线,其格式由中断控制器的 #interrupt-cells 决定。
ARM GIC(Generic Interrupt Controller)的典型格式为 <中断类型 中断号 触发方式>:
| 字段 | 值 | 说明 |
|---|---|---|
| 中断类型 | 0 = SPI(共享外设中断) 1 = PPI(私有外设中断) |
GPU 通常使用 SPI(类型 0) |
| 中断号 | 32~1019(SPI 范围) | 硬件连接的中断线编号 |
| 触发方式 | 1 = 上升沿触发 4 = 高电平触发 |
GPU 常用高电平触发(4) |
示例:interrupts = <0 42 4>; 表示 SPI 类型、中断号 42、高电平触发。
interrupts = <0 42 4>, <0 43 4>, <0 44 4>;,驱动通过 platform_get_irq(pdev, index) 按索引获取。
1.6 时钟与复位属性
现代 GPU 通常需要多个时钟源(核心时钟、存储器时钟、总线时钟)和复位信号。设备树通过 clocks 和 resets 属性引用时钟/复位控制器中的特定输出。
gpu: gpu@fbc00000 {
compatible = "vendor,awesome-gpu";
reg = <0xfbc00000 0x10000>;
interrupts = <0 42 4>;
/* 时钟:clkc 是时钟控制器节点标签,15 和 16 是时钟输出索引 */
clocks = <&clkc 15>, <&clkc 16>, <&clkc 17>;
clock-names = "core", "mem", "bus";
/* 复位:rstc 是复位控制器节点标签,7 是复位输出索引 */
resets = <&rstc 7>;
reset-names = "gpu";
/* 电源域:引用 power-controller 节点 */
power-domains = <&pd_gpu>;
status = "okay";
};
驱动中通过 devm_clk_get(dev, "core") 获取名为 "core" 的时钟,通过 devm_reset_control_get(dev, "gpu") 获取复位控制句柄。
1.7 设备树层级关系与 GPU 节点定位
下面的 SVG 图展示了一个典型 SoC 中 GPU 节点在设备树中的位置,以及它与时钟控制器、中断控制器、复位控制器、内存控制器之间的关系。
1.8 设备树包含与覆盖机制
实际项目中,设备树通常拆分为多个文件,通过 #include 和 /include/ 组合:
.dtsi:SoC 级描述文件,包含 CPU、中断控制器、时钟控制器、GPU 等所有内部外设的默认节点。.dts:板级描述文件,#include对应的.dtsi,然后通过&gpu引用节点并覆盖属性(如修改status、调整时钟频率)。
/* soc.dtsi - SoC 级定义 */
/ {
gpu: gpu@fbc00000 {
compatible = "vendor,awesome-gpu";
reg = <0xfbc00000 0x10000>;
interrupts = <0 42 4>;
clocks = <&clkc 15>, <&clkc 16>;
clock-names = "core", "mem";
resets = <&rstc 7>;
status = "disabled"; /* 默认禁用 */
};
};
/* board.dts - 板级定义 */
#include "soc.dtsi"
&gpu {
status = "okay"; /* 板级使能 GPU */
/* 可在此处添加板级特有的属性,如 operating-points */
};
.dtsi 中,板级文件只负责使能设备和添加板级特性(如 DVFS 表、电源管理策略)。这样当 SoC 用于不同板卡时,无需重复编写 GPU 节点基础信息。
1.9 本章小结
设备树是 GPU 驱动移植的“第一道关卡”——一个错误的 reg 地址会导致内核崩溃,遗漏 interrupts 会使 VBlank 中断无法触发,缺失 clocks 则 GPU 核心无法上电。掌握 DTS 语法与关键属性,是后续编写 GPU 驱动 probe 函数、实现 DRM 框架对接的必备前提。
compatible是驱动匹配的唯一标识,命名必须与驱动中的of_device_id一致。reg的地址/长度位数由父节点#address-cells/#size-cells决定。interrupts格式依赖中断控制器类型,ARM GIC 通常为<类型 编号 触发方式>。- 时钟和复位通过 phandle + 索引引用,驱动使用名字(
clock-names/reset-names)获取。 - 使用
.dtsi+.dts分离 SoC 通用描述与板级配置,提高复用性。