3. 设备树基础:设备树语法、节点、属性、reg、interrupts、compatible
设备树(Device Tree)是嵌入式 Linux 系统中描述硬件拓扑结构的核心机制。对于 USB 驱动开发而言,设备树负责将 USB 控制器、PHY、时钟、复位等硬件资源以标准化的方式呈现给内核。本章将系统讲解设备树的语法结构、关键节点与属性,并重点分析 USB 驱动中常用的 reg、interrupts、compatible 属性。
3.1 设备树语法基础
设备树源文件(.dts)采用树形结构描述硬件,每个硬件单元对应一个节点(node),节点内包含若干属性(property)。基本语法规则如下:
- 节点:以
node-name@unit-address形式定义,@后跟设备在总线上的地址。 - 属性:键值对,值可以是字符串、整数、字节数组、字符串列表等。
- 标签:使用
label: node-name格式,便于在其它文件中引用。 - 包含文件:通过
#include引入.dtsi头文件,实现 SoC 级与板级描述分离。
.dtb 文件适配不同硬件平台,无需重新编译内核。
3.2 节点与属性详解
一个典型的 USB 控制器节点示例如下:
/ {
usb@f0a00000 {
compatible = "snps,dwc3", "generic-usb";
reg = <0x0 0xf0a00000 0x0 0x10000>;
interrupts = <0 72 4>;
dr_mode = "otg";
maximum-speed = "super-speed";
phys = <&usb_phy0>;
phy-names = "usb2-phy";
};
};
各属性含义如下表:
| 属性 | 格式 | 说明 | USB 驱动中的典型值 |
|---|---|---|---|
compatible |
字符串列表 | 驱动匹配标识,内核据此查找对应驱动 | "snps,dwc3", "generic-usb" |
reg |
地址/长度对 | 描述设备在总线上的地址空间 | <0x0 0xf0a00000 0x0 0x10000> |
interrupts |
中断描述符 | 指定中断控制器、中断号、触发类型 | <0 72 4> |
phys |
phandle 列表 | 引用 PHY 节点,建立控制器与 PHY 的绑定 | <&usb_phy0> |
dr_mode |
字符串 | USB 控制器工作模式 | "otg", "host", "peripheral" |
3.3 compatible 属性:驱动匹配的核心
compatible 是设备树中最关键的属性,它建立了硬件节点与内核驱动之间的桥梁。内核通过 of_match_table 将设备树中的 compatible 字符串与驱动支持的设备 ID 进行匹配。
匹配规则:
- 内核按
compatible字符串列表的顺序依次尝试匹配。 - 第一个匹配成功的驱动将被绑定到该设备节点。
- 通常将最具体的兼容性字符串放在前面,通用字符串放在后面。
compatible 应同时包含具体 IP 核名称(如 "snps,dwc3")和通用名称(如 "generic-usb"),以提高驱动兼容性。
3.4 reg 属性:地址空间描述
reg 属性描述设备在系统总线上的地址范围,格式为 <address length> 对。对于 64 位地址空间,每个地址/长度对包含两个 32 位单元:
reg = <0x0 0xf0a00000 0x0 0x10000>;
// 解释:基地址 0xf0a00000,长度 0x10000(64KB)
在 USB 驱动中,reg 通常映射到 USB 控制器的寄存器空间,驱动通过 devm_ioremap_resource() 获取虚拟地址后访问硬件寄存器。
4.5 interrupts 属性:中断资源绑定
interrupts 属性描述设备使用的中断资源,其格式依赖于中断控制器。在 ARM GIC(通用中断控制器)中,典型格式为:
interrupts = <中断类型 中断号 触发标志>;
// 示例:<0 72 4> 表示 SPI 类型,中断号 72,高电平触发
中断类型:
0:PPI(私有外设中断),每个 CPU 私有1:SPI(共享外设中断),所有 CPU 共享
触发标志:
1:上升沿触发2:下降沿触发4:高电平触发8:低电平触发
3.6 设备树与 USB PHY 绑定
USB PHY 节点通常独立描述,并通过 phys 和 phy-names 属性与控制器节点绑定。一个完整的 USB PHY 节点示例:
usb_phy0: phy@f0a10000 {
compatible = "snps,dwc3-usb-phy";
reg = <0x0 0xf0a10000 0x0 0x1000>;
clocks = <&clk_usb_phy>;
clock-names = "main";
resets = <&rst_usb_phy>;
reset-names = "phy";
#phy-cells = <0>;
};
控制器节点通过 phys = <&usb_phy0> 引用 PHY 节点,驱动在初始化时调用 devm_phy_get() 获取 PHY 句柄,然后通过 phy_init() 和 phy_power_on() 完成 PHY 初始化。
3.7 设备树结构总览
下图展示了 USB 控制器、PHY、时钟、复位等设备树节点之间的层级与引用关系:
3.8 设备树编译与调试
设备树源文件需要通过编译生成二进制 .dtb 文件,内核在启动时解析 .dtb 构建设备树结构。常用命令:
# 编译设备树
dtc -I dts -O dtb -o myboard.dtb myboard.dts
# 反编译设备树(调试用)
dtc -I dtb -O dts -o myboard.dts myboard.dtb
# 查看当前系统设备树
ls /sys/firmware/devicetree/base/
cat /sys/firmware/devicetree/base/usb@f0a00000/compatible
of_device_is_compatible() 检查,或在 probe() 函数中打印 pdev->dev.of_node->full_name,可以快速确认设备树节点是否被正确匹配。
3.9 常见错误与避坑指南
| 错误类型 | 现象 | 原因 | 解决方案 |
|---|---|---|---|
| compatible 不匹配 | 驱动 probe 不被调用 | 设备树与驱动中的 compatible 字符串不一致 | 检查 of_match_table 与设备树节点 |
| reg 地址错误 | ioremap 失败或访问异常 | 地址范围与硬件手册不符 | 核对 SoC 数据手册中的寄存器基地址 |
| 中断号错误 | 中断无法触发或触发错误 | 中断号编码方式理解错误 | 确认 GIC 类型(SPI/PPI)和中断号偏移 |
| PHY 引用缺失 | USB 控制器初始化失败 | 未定义 phys 属性或 phandle 错误 |
确保 PHY 节点存在且 #phy-cells 正确 |