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 字符串列表的顺序依次尝试匹配。
  • 第一个匹配成功的驱动将被绑定到该设备节点。
  • 通常将最具体的兼容性字符串放在前面,通用字符串放在后面。
最佳实践:在 USB 控制器节点中,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:低电平触发
注意:中断号的编码方式因中断控制器而异。在设备树中,中断号是相对于中断控制器的本地编号,而非系统全局中断号。驱动开发时需查阅 SoC 数据手册确认正确的中断号。

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 初始化。

绑定流程:设备树解析 → of_phandle_args 解析 → PHY 框架查找 → phy_get() 返回句柄 → 驱动调用 PHY 操作函数。

3.7 设备树结构总览

下图展示了 USB 控制器、PHY、时钟、复位等设备树节点之间的层级与引用关系:

USB 设备树节点关系图 / (根节点) soc@0 clk@f0a20000 rst@f0a30000 usb@f0a00000 compatible = "snps,dwc3" phy@f0a10000 compatible = "snps,dwc3-usb-phy" 图例说明: • 实线箭头:父子节点层级关系 • 虚线箭头:通过 phandle 引用的跨节点绑定(clocks, resets, phys) • 红色节点:USB 控制器;橙色节点: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 正确

3.10 本章小结