3. 设备树基础

设备树(Device Tree,DT)是嵌入式 Linux 中用于描述硬件拓扑结构的数据结构,它将硬件信息从内核代码中分离出来,使得同一份内核镜像可以支持多种硬件平台。对于 USB 设备驱动开发而言,设备树是连接 USB 控制器、PHY 芯片与内核驱动之间的“硬件配置清单”。

核心认知:设备树不是代码,而是数据。它告诉内核“有什么硬件、在哪里、怎么配置”,驱动则根据这些数据完成初始化与控制。

3.1 设备树语法基础

设备树源文件(.dts)采用树形结构,由节点(node)和属性(property)组成。每个节点代表一个硬件设备或总线,属性则描述设备的特性。

3.1.1 节点定义

节点的基本语法格式如下:

/dts-v1/;

/ {
    node1 {
        property1 = <value>;
        property2 = "string";
        child-node {
            ...
        };
    };
};
  • 根节点:/ 表示设备树的根,整个硬件系统从根节点展开。
  • 子节点:在父节点内通过 node-name { }; 定义。
  • 节点名称:格式为 name@unit-address,其中 unit-address 通常是设备在总线上的地址(如寄存器基址)。
命名惯例:USB 控制器节点通常命名为 usb@xxxxxxx,PHY 节点命名为 usb-phy@xxxxxxx,便于通过名称快速识别设备类型。

3.1.2 属性定义

属性是节点的“键值对”,用于描述设备的硬件特征。常见属性类型如下表:

属性类型 语法示例 说明
32 位整数 reg = <0x110000 0x1000>; 地址或配置值,可包含多个值
字符串 compatible = "snps,dwc3"; 用于匹配驱动
字符串列表 compatible = "ti,am335x-usb", "ti,musb"; 多个兼容字符串,按优先级排列
布尔值 status = "okay"; 通过存在与否或特定字符串表示
二进制数据 reg = <0x01 0x02 0x03>; 以字节数组形式编码
phandle phys = <&usb_phy>; 引用另一个节点的指针

3.2 标准属性详解

设备树规范定义了一组标准属性,所有设备节点都应遵循这些属性的语义。以下逐一解析与 USB 驱动最相关的标准属性。

3.2.1 compatible —— 驱动匹配的关键

compatible 属性是设备树与驱动代码之间的“握手协议”。内核通过该属性查找对应的驱动程序。

usb@fe800000 {
    compatible = "snps,dwc3", "generic-dwc3";
    reg = <0xfe800000 0x10000>;
};
  • 格式:"厂商,设备型号",可包含多个字符串,从左到右优先级递减。
  • 匹配规则:内核遍历 of_match_table,找到第一个匹配的条目即绑定驱动。
  • USB 场景:DWC3 控制器常用 "snps,dwc3",EHCI 控制器常用 "generic-ehci"。

3.2.2 reg —— 地址与内存映射

reg 属性描述设备的寄存器地址范围,驱动通过它获取 MMIO 基址。

usb@fe800000 {
    reg = <0xfe800000 0x10000>;  // 基址 0xfe800000,长度 0x10000
};
字段 含义 示例值
address-cells 地址字段占用的 32 位单元数 1(32 位地址)或 2(64 位地址)
size-cells 长度字段占用的 32 位单元数 1(32 位长度)
reg 值 交替排列的地址和长度对 <0xfe800000 0x10000>
注意:如果 address-cells 为 2,则地址需要写成 <0x00 0xfe800000 0x00 0x10000>,即高 32 位 + 低 32 位 + 高 32 位长度 + 低 32 位长度。

3.2.3 interrupts —— 中断资源配置

USB 控制器通常需要中断来通知事件(如连接/断开、传输完成)。interrupts 属性描述设备使用的中断号与触发方式。

usb@fe800000 {
    interrupts = <0 72 4>;  // 中断类型 0(SPI),中断号 72,触发方式 4(高电平)
    interrupt-parent = <&gic>;
};
字段 含义 常见值
中断类型 0=SPI(共享外设中断),1=PPI(私有外设中断) 0
中断号 硬件中断编号 72(取决于 SoC)
触发方式 1=上升沿,2=下降沿,4=高电平,8=低电平 4(高电平触发)

3.2.4 status —— 设备启用控制

status 属性控制设备是否被内核启用,常用于在 board 文件中覆盖 SoC 默认配置。

usb@fe800000 {
    status = "okay";    // 启用设备
    // status = "disabled";  // 禁用设备
};
状态值 含义 使用场景
"okay" 设备正常启用 板级文件覆盖 SoC 默认禁用状态
"disabled" 设备被禁用,驱动不会 probe 未使用的 USB 控制器
"fail" 设备存在但无法工作 硬件故障或未完成初始化

3.2.5 phandle —— 节点引用机制

phandle(pointer handle)是设备树中实现节点间引用的机制。一个节点可以通过 phandle 属性被其他节点引用,通常使用 &label 语法。

usb_phy: usb-phy@e000 {
    compatible = "ti,am335x-usb-phy";
    reg = <0xe000 0x100>;
    #phy-cells = <0>;
};

usb@fe800000 {
    compatible = "snps,dwc3";
    reg = <0xfe800000 0x10000>;
    phys = <&usb_phy>;          // 通过 phandle 引用 PHY 节点
    phy-names = "usb3-phy";
};
  • 定义:在节点前加 label:,如 usb_phy:,编译器自动生成 phandle。
  • 引用:使用 &label 语法,如 &usb_phy。
  • USB 场景:USB 控制器通过 phys 属性引用 PHY 节点,实现控制器与 PHY 的绑定。

3.3 设备树层级结构:从 SoC 到 USB 控制器

一个完整的设备树文件通常包含 SoC 级描述和板级描述。以下是一个简化的 USB 设备树层级结构:

/ {
    model = "TI AM335x Board";
    compatible = "ti,am335x-board";

    soc {
        #address-cells = <1>;
        #size-cells = <1>;
        ranges;

        usb_phy: usb-phy@e000 {
            compatible = "ti,am335x-usb-phy";
            reg = <0xe000 0x100>;
            #phy-cells = <0>;
        };

        usb@fe800000 {
            compatible = "snps,dwc3";
            reg = <0xfe800000 0x10000>;
            interrupts = <0 72 4>;
            phys = <&usb_phy>;
            phy-names = "usb3-phy";
            status = "okay";
        };
    };
};
层级意义:SoC 节点定义芯片内部外设的通用布局,板级文件通过 &label 引用并覆盖 status、pinctrl 等属性,实现“SoC 通用 + 板级定制”的分离。

3.4 设备树与 USB PHY 的绑定关系

USB PHY 是物理层收发器,负责将数字信号转换为模拟信号。设备树通过 phys 和 phy-names 属性将 USB 控制器与 PHY 绑定。

usb@fe800000 {
    phys = <&usb_phy0 &usb_phy1>;   // 引用多个 PHY
    phy-names = "usb2-phy", "usb3-phy";  // 命名以便驱动区分
};
属性 作用 示例
phys PHY 节点引用列表 <&usb_phy0 &usb_phy1>
phy-names PHY 名称列表,与 phys 一一对应 "usb2-phy", "usb3-phy"
#phy-cells PHY 节点中定义的参数数量 0(无参数)或 1(如指定 PHY 类型)

3.5 设备树编译与反编译

设备树源文件(.dts)需要编译为二进制设备树 blob(.dtb),内核在启动时解析 .dtb 文件。

# 编译 .dts 为 .dtb
dtc -I dts -O dtb -o am335x-board.dtb am335x-board.dts

# 反编译 .dtb 为 .dts
dtc -I dtb -O dts -o am335x-board.dts am335x-board.dtb

# 查看设备树内容
fdtdump am335x-board.dtb
常见错误:语法错误(如缺少分号、括号不匹配)会导致编译失败;address-cells 与 reg 字段数不匹配会导致地址解析错误。

3.6 设备树节点与属性关系图

下图展示了 USB 控制器节点、PHY 节点以及标准属性之间的引用关系:

设备树节点与属性关系图 设备树节点与属性关系图 根节点 / soc { } usb@fe800000 { } compatible / reg / interrupts / status usb-phy@e000 { } compatible / reg / #phy-cells phys = &usb_phy 标准属性说明 • compatible: "snps,dwc3" → 驱动匹配标识 • reg: <0xfe800000 0x10000> → 寄存器基址与长度 • interrupts: <0 72 4> → 中断类型/号/触发方式 • status: "okay" → 设备启用状态 • phandle: &usb_phy → 节点引用指针 • phys / phy-names: 绑定 USB 控制器与 PHY 设备 USB 控制器节点 PHY 节点 SoC 节点 phandle 引用

3.7 实战:解析一个 USB 设备树片段

以下是一个真实的 USB 3.0 控制器设备树片段,结合本节所学知识逐行解析:

&usb3_0: usb@f010000 {
    compatible = "snps,dwc3";
    reg = <0x0f010000 0x10000>;
    interrupts = <0 64 4>;
    phys = <&usb_phy0>;
    phy-names = "usb3-phy";
    maximum-speed = "super-speed";
    dr_mode = "host";
    status = "okay";
};
属性 值 解析
compatible "snps,dwc3" 匹配 Synopsys DWC3 USB 3.0 控制器驱动
reg <0x0f010000 0x10000> 寄存器基址 0x0f010000,映射长度 64KB
interrupts <0 64 4> SPI 中断,中断号 64,高电平触发
phys <&usb_phy0> 引用 usb_phy0 节点作为 PHY
phy-names "usb3-phy" 该 PHY 的名称为 usb3-phy
maximum-speed "super-speed" 限制最高速度为 USB 3.0 SuperSpeed
dr_mode "host" 强制工作在 Host 模式
status "okay" 设备启用
关键洞察:设备树中的每一个属性都对应驱动代码中的一个解析动作。例如,驱动通过 of_property_read_u32() 读取 reg,通过 of_parse_phandle() 解析 phys 引用。理解设备树就是理解驱动与硬件之间的“数据契约”。

3.8 本章小结

  • 设备树语法:树形结构,节点表示设备,属性描述特性。
  • 标准属性:compatible(驱动匹配)、reg(地址映射)、interrupts(中断配置)、status(启用控制)、phandle(节点引用)。
  • USB 绑定:通过 phys 和 phy-names 将 USB 控制器与 PHY 设备关联。
  • 编译工具:使用 dtc 编译/反编译设备树,fdtdump 查看二进制内容。