3. 设备树基础

设备树(Device Tree,DT)是嵌入式 Linux 中用于描述硬件拓扑结构的数据结构,它替代了传统板级文件(board-*.c)中硬编码的硬件信息。在 USB 驱动移植中,设备树是连接 SoC 控制器与物理 USB 端口的关键桥梁。本章将系统讲解设备树的语法、核心节点与属性、标准属性含义以及 dts/dtsi 文件的组织方式。

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

3.1 设备树语法基础

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

3.1.1 节点定义与层次结构

节点以 节点名@单元地址 的形式定义,通过花括号 { } 包含子节点。根节点是 /,所有设备都挂载在根节点下。

/dts-v1/;

/ {
    model = "Embedded USB Dev Board";
    compatible = "vendor,board-rev1";

    #address-cells = <1>;
    #size-cells = <1>;

    soc {
        compatible = "simple-bus";
        #address-cells = <1>;
        #size-cells = <1>;
        ranges;

        usb_controller: usb@1c14000 {
            compatible = "vendor,usb-otg";
            reg = <0x01c14000 0x400>;
            interrupts = <0 39 4>;
            status = "disabled";
        };
    };
};
语法要点:
  • 节点名:如 usb@1c14000,@ 后为寄存器基地址,用于区分同名设备。
  • 标签(label):如 usb_controller:,可在其他位置通过 &usb_controller 引用。
  • 属性值:用 ; 结尾,支持字符串、整数、字节数组、<> 包裹的 32 位整数列表。

3.1.2 属性类型与写法

属性类型 语法示例 说明
字符串 compatible = "vendor,device"; 用于匹配驱动,格式为“厂商,设备型号”
32 位整数 reg = <0x1000 0x200>; 地址与长度对,由 #address-cells 和 #size-cells 决定数量
布尔值 status = "okay"; 字符串形式,常用 "okay" 或 "disabled"
字节数组 local-mac-address = [00 11 22 33 44 55]; 十六进制字节序列,用空格分隔
字符串列表 clock-names = "usb_phy", "usb_hclk"; 多个字符串用空格分隔

3.2 标准属性详解

设备树规范定义了一组标准属性,所有设备节点都应遵循。对于 USB 驱动移植,以下三个属性最为关键。

3.2.1 compatible —— 驱动匹配的钥匙

compatible 是设备树中最核心的属性,内核通过它查找对应的驱动程序。格式为 "厂商,设备型号",按优先级从高到低排列。

// USB 控制器节点示例
usb@1c14000 {
    compatible = "vendor,usb-otg-v2", "vendor,usb-otg", "generic-usb";
    reg = <0x01c14000 0x400>;
    interrupts = <0 39 4>;
};
移植陷阱: compatible 字符串必须与驱动中 of_match_table 定义的字符串完全一致,包括大小写和逗号。常见错误是厂商名拼写错误或版本号遗漏。

3.2.2 reg —— 地址与内存映射

reg 描述设备的寄存器地址空间,由 #address-cells 和 #size-cells 决定地址和长度的单元数。对于 32 位系统,通常各为 1。

属性 含义 USB 场景示例
#address-cells 地址字段占用的 32 位单元数 <1> 表示 32 位地址
#size-cells 长度字段占用的 32 位单元数 <1> 表示 32 位长度
reg 地址与长度对列表 <0x01c14000 0x400> 基址 0x01c14000,长度 0x400

3.2.3 interrupts —— 中断路由

interrupts 描述设备使用的中断号、触发类型等信息。其格式由 #interrupt-cells 在中断控制器节点中定义。

// 中断控制器定义(GIC)
gic: interrupt-controller@1c81000 {
    compatible = "arm,cortex-a7-gic";
    #interrupt-cells = <3>;
    interrupt-controller;
    reg = <0x01c81000 0x1000>;
};

// USB 设备引用中断
usb@1c14000 {
    interrupt-parent = <&gic>;   // 指定中断控制器
    interrupts = <0 39 4>;       // 类型 0(SPI), 中断号 39, 触发类型 4(高电平)
};
中断类型说明: 对于 ARM GIC,#interrupt-cells = <3> 时,第一个字段 0=SPI(共享外设中断)、1=PPI(私有外设中断);第二个字段为中断号;第三个字段为触发类型(1=上升沿、2=下降沿、4=高电平、8=低电平)。

3.3 设备树文件组织:dts 与 dtsi

实际项目中,设备树文件按层次组织:.dtsi 文件存放 SoC 级通用定义,.dts 文件存放板级特定配置。这种分离避免了重复定义,便于维护。

3.3.1 文件层次结构

设备树文件组织层次 soc.dtsi (SoC 级) board-common.dtsi my-board.dts #include "soc.dtsi" → #include "board-common.dtsi" → 板级覆盖 soc.dtsi 内容 • CPU 核心与 GIC • 内存控制器 • USB 控制器(disabled) • 时钟与复位控制器 board-common.dtsi • 板级外设使能 • GPIO 引脚复用 • 电源管理配置 • 时钟频率设置 my-board.dts • USB 端口使能 • 引脚功能覆盖 • 板级特有属性 • 设备别名定义 编译流程:.dts + .dtsi → dtc 编译器 → .dtb(二进制设备树) 关键原则:dtsi 定义硬件能力,dts 决定哪些能力被使能

3.3.2 实际项目中的文件组织

以某 ARM 平台 USB 驱动移植为例,典型文件结构如下:

arch/arm/boot/dts/
├── soc.dtsi                    # SoC 级定义:USB 控制器、GIC、时钟
│   └── usb_otg: usb@1c14000 {
│           compatible = "vendor,usb-otg";
│           reg = <0x01c14000 0x400>;
│           interrupts = <0 39 4>;
│           status = "disabled";   // 默认关闭
│       };
├── board-common.dtsi           # 板级通用配置
│   └── &usb_otg {
│           status = "okay";      // 使能 USB 控制器
│           dr_mode = "otg";      // 双角色模式
│       };
└── my-board.dts                # 具体板卡配置
    └── #include "board-common.dtsi"
        &usb_otg {
            vbus-supply = <&reg_usb_vbus>;  // 板级特有:VBUS 电源
        };
文件组织最佳实践:
  • SoC 级(.dtsi):只描述硬件存在,不使能任何功能,所有设备默认 status = "disabled"。
  • 板级通用(.dtsi):使能 SoC 级设备,配置引脚复用、时钟等通用设置。
  • 具体板卡(.dts):覆盖板级特有属性,如电源、GPIO 别名、USB 端口角色。

3.4 USB 设备树节点实战解析

下面是一个完整的 USB OTG 控制器设备树节点,包含所有标准属性和 USB 特有属性。

usb_otg: usb@1c14000 {
    compatible = "vendor,usb-otg-v2", "vendor,usb-otg";
    reg = <0x01c14000 0x400>;
    interrupts = <0 39 4>;
    interrupt-parent = <&gic>;
    clocks = <&ccu CLK_USB_OTG>, <&ccu CLK_BUS_USB_OTG>;
    clock-names = "usb_otg", "bus_usb_otg";
    resets = <&ccu RST_USB_OTG>;
    reset-names = "usb_otg";
    phys = <&usbphy 0>;
    phy-names = "usb2-phy";
    dr_mode = "otg";           // 可选:host, peripheral, otg
    status = "disabled";
};
属性 说明 USB 移植常见值
clocks 时钟句柄列表,对应 clock-names USB 控制器时钟、总线时钟
resets 复位控制器句柄 USB 模块复位
phys PHY(物理层)句柄 USB 2.0 PHY 或 USB 3.0 PHY
dr_mode 双角色模式 otg / host / peripheral
vbus-supply VBUS 电源 regulator 板级特有,需在 .dts 中定义

3.5 设备树编译与调试

设备树源文件需要通过 dtc(Device Tree Compiler)编译为二进制 .dtb 文件。内核启动时,bootloader 将 .dtb 传递给内核。

# 编译设备树
dtc -I dts -O dtb -o my-board.dtb my-board.dts

# 反编译查看
dtc -I dtb -O dts -o my-board.dts my-board.dtb

# 检查设备树是否被正确加载(内核启动后)
ls /proc/device-tree/
cat /proc/device-tree/model
调试提示: 如果 USB 驱动无法 probe,首先检查 /proc/device-tree/ 下对应节点是否存在,以及 compatible 字符串是否与驱动匹配。使用 dtc -I fs -O dts /proc/device-tree/ 可以查看内核当前使用的设备树。

3.6 本章小结

设备树是 USB 驱动移植的硬件描述基础。掌握节点语法、标准属性(compatible/reg/interrupts)以及 dts/dtsi 文件组织方式,是正确配置 USB 控制器的前提。在后续章节中,我们将基于本章知识,具体讲解如何为 USB 主机控制器、设备控制器和 PHY 编写设备树节点。

关键记忆点:
  • compatible 是驱动匹配的唯一标识,必须与驱动代码完全一致。
  • reg 描述寄存器地址空间,注意 address-cells 和 size-cells 的匹配。
  • interrupts 需要配合 interrupt-parent 使用,格式由中断控制器决定。
  • dtsi 定义硬件能力,dts 决定使能哪些能力,遵循“分层覆盖”原则。