4. USB控制器设备树绑定:EHCI/OHCI/xHCI控制器节点编写、时钟与复位属性、PHY接口绑定

在嵌入式Linux系统中,USB主机控制器的设备树绑定是驱动移植的核心环节。不同版本的USB控制器(EHCI、OHCI、xHCI)在设备树中的描述方式既有共性,也有各自特定的属性要求。本章将深入讲解这三类控制器的节点编写规范,重点分析时钟与复位属性的配置方法,以及PHY接口的绑定策略。

4.1 USB控制器设备树节点基础结构

所有USB主机控制器在设备树中均遵循相似的基础结构,但根据控制器类型和SoC集成方式的不同,节点属性存在差异。下表列出了三类控制器的核心属性对比:

属性 EHCI OHCI xHCI
compatible "generic-ehci" 或 SoC特定 "generic-ohci" 或 SoC特定 "generic-xhci" 或 SoC特定
reg MMIO基址 + 长度 (通常0x100) MMIO基址 + 长度 (通常0x100) MMIO基址 + 长度 (通常0x10000)
interrupts 1个中断号 1个中断号 1个中断号 (MSI可选)
clocks 至少1个时钟 至少1个时钟 多个时钟 (主时钟+辅助时钟)
resets 可选 可选 必需 (至少1个复位)
phys 1个PHY引用 1个PHY引用 1个PHY引用 (USB3 PHY)
dr_mode 仅host 仅host host/otg/peripheral
关键提示: 在编写设备树节点时,compatible 属性的优先级顺序至关重要。内核驱动匹配时,会从列表的第一个字符串开始匹配,因此应将最具体的SoC兼容字符串放在前面,通用的"generic-ehci"等放在最后作为fallback。

4.2 EHCI控制器节点编写

EHCI(Enhanced Host Controller Interface)是USB 2.0高速控制器的标准接口。在设备树中,EHCI节点通常需要与OHCI节点配对出现,因为大多数SoC将EHCI和OHCI集成在同一个USB IP模块中。

以下是一个典型的EHCI设备树节点示例:

usb_ehci: usb@ee080000 {
    compatible = "soc,vendor-ehci", "generic-ehci";
    reg = <0x0 0xee080000 0x0 0x100>;
    interrupts = <0 67 4>;
    clocks = <&ccu CLK_USB_EHCI>;
    resets = <&ccu RST_USB_EHCI>;
    phys = <&usbphy0>;
    phy-names = "usb";
    status = "okay";
};

EHCI节点编写要点:

  • reg属性: EHCI寄存器空间通常为0x100字节,但某些SoC可能扩展到0x1000字节,需参考数据手册确认。
  • 中断类型: EHCI使用单一边沿触发或电平触发中断,推荐使用高电平触发(类型码4)。
  • 时钟关联: 必须提供AHB/APB总线时钟和USB参考时钟,部分SoC还需要单独的UTMI时钟。
  • PHY绑定: 通过phys属性引用PHY节点,phy-names通常固定为"usb"。
实战经验: 在Allwinner H3/H5平台移植EHCI时,常见错误是遗漏了hctg或usb_rst时钟域。如果EHCI初始化时寄存器读取全为0xFF,请检查时钟门控是否已使能。

4.3 OHCI控制器节点编写

OHCI(Open Host Controller Interface)负责USB 1.1全速/低速通信。在大多数SoC中,OHCI与EHCI共享同一个USB端口,通过端口路由逻辑切换。

OHCI节点示例:

usb_ohci: usb@ee090000 {
    compatible = "soc,vendor-ohci", "generic-ohci";
    reg = <0x0 0xee090000 0x0 0x100>;
    interrupts = <0 68 4>;
    clocks = <&ccu CLK_USB_OHCI>;
    resets = <&ccu RST_USB_OHCI>;
    phys = <&usbphy0>;
    phy-names = "usb";
    status = "okay";
};

OHCI与EHCI的差异对比:

对比项 EHCI OHCI
寄存器基址偏移 通常与OHCI相差0x1000 通常与EHCI相差0x1000
中断号 独立中断号 独立中断号(不可共享)
时钟需求 需要高速时钟(48MHz) 需要低速时钟(12MHz/6MHz)
复位时序 先复位OHCI再复位EHCI 必须先于EHCI复位
PHY模式 UTMI+ / ULPI UTMI / 内置模拟PHY
注意: 某些SoC(如NXP i.MX6系列)的OHCI和EHCI共享同一个中断号。此时需要在设备树中为两个节点配置相同的中断,并在驱动中实现中断共享处理。如果中断号配置错误,会导致USB 1.1设备无法被识别。

4.4 xHCI控制器节点编写

xHCI(eXtensible Host Controller Interface)是USB 3.0/3.1/3.2的通用控制器标准,同时向下兼容USB 2.0和1.1。xHCI的设备树节点比EHCI/OHCI更复杂,需要处理更多的时钟域和复位信号。

xHCI节点完整示例:

usb_xhci: usb@f0940000 {
    compatible = "soc,vendor-xhci", "generic-xhci";
    reg = <0x0 0xf0940000 0x0 0x10000>;
    interrupts = <0 72 4>;
    clocks = <&ccu CLK_USB3_SUSPEND>,
             <&ccu CLK_USB3_REF>,
             <&ccu CLK_USB3_MSTR>;
    clock-names = "suspend", "ref", "master";
    resets = <&ccu RST_USB3_PHY>,
             <&ccu RST_USB3_MAC>;
    reset-names = "phy", "mac";
    phys = <&usb3phy0>;
    phy-names = "usb3-phy";
    dr_mode = "host";
    maximum-speed = "super-speed";
    snps,dis_u2_susphy_quirk;
    snps,dis_u3_susphy_quirk;
    status = "okay";
};

xHCI节点关键属性解析:

  • reg空间: xHCI寄存器空间通常为64KB(0x10000),包含能力寄存器、操作寄存器、端口寄存器等。
  • 多时钟管理: 需要至少3个时钟:挂起时钟(suspend)、参考时钟(ref)、主时钟(master)。
  • 双复位域: PHY复位和MAC复位必须分开控制,复位顺序为先PHY后MAC。
  • PHY绑定: 使用USB3 PHY节点,部分SoC还需要额外的USB2 PHY用于兼容模式。
  • Quirk属性: snps,dis_u2_susphy_quirk 和 snps,dis_u3_susphy_quirk 用于禁用USB2/USB3的挂起PHY省电功能,解决某些设备的兼容性问题。
调试建议: 当xHCI在初始化阶段报"Timeout while waiting for controller to stop"错误时,首先检查resets属性中的复位信号是否已正确释放。使用clk_summary和reset_control_status调试接口可以快速定位时钟和复位问题。

4.5 时钟与复位属性配置

时钟和复位是USB控制器正常工作的基础保障。错误的时钟配置会导致控制器无法检测到设备,而复位时序错误则可能造成控制器死锁。

4.5.1 时钟属性配置规范

USB控制器的时钟需求因控制器类型和SoC设计而异。以下表格总结了常见时钟配置:

时钟名称 典型频率 适用控制器 说明
usb_clk / hclk 50MHz - 200MHz EHCI/OHCI/xHCI AHB总线时钟,用于寄存器访问
utmi_clk 60MHz EHCI/OHCI UTMI接口时钟,由PHY提供
ref_clk 24MHz / 48MHz xHCI USB参考时钟,用于PLL倍频
suspend_clk 32kHz xHCI 挂起时钟,低功耗模式使用
master_clk 125MHz - 250MHz xHCI 主控制器时钟,驱动DMA引擎

时钟属性编写示例:

/* 多时钟配置示例 - xHCI */
clocks = <&clkc 15>,  /* suspend_clk: 32kHz */
         <&clkc 16>,  /* ref_clk: 24MHz */
         <&clkc 17>;  /* master_clk: 125MHz */
clock-names = "suspend", "ref", "master";

/* 单时钟配置示例 - EHCI */
clocks = <&clkc 20>;  /* hclk: 100MHz */
clock-names = "usb";

4.5.2 复位属性配置规范

复位信号的正确配置是USB控制器初始化的前提。复位属性使用resets和reset-names描述,内核复位框架会按顺序执行复位操作。

复位时序要求:

  1. PHY复位: 必须先释放PHY复位,等待PHY稳定(通常需要10μs - 100μs)。
  2. MAC复位: PHY稳定后释放MAC复位,等待控制器自检完成(通常需要1ms - 5ms)。
  3. 端口复位: 控制器初始化完成后,由驱动对每个端口执行软件复位。

复位属性编写示例:

/* 双复位域配置 */
resets = <&rst RST_USB_PHY>,
         <&rst RST_USB_MAC>;
reset-names = "phy", "mac";

/* 单复位配置(适用于简单SoC) */
resets = <&rst RST_USB>;
reset-names = "usb";
内核机制说明: Linux内核的通用复位框架(reset controller framework)在设备驱动probe阶段自动处理复位信号的释放。驱动只需在设备树中声明resets属性,内核会在调用驱动的probe函数之前完成复位释放操作。如果需要在特定时机控制复位,可以使用devm_reset_control_get和reset_control_deassert API手动管理。

4.6 PHY接口绑定

PHY(Physical Layer)是USB控制器与外部USB连接器之间的模拟接口层。设备树中通过phys属性将控制器与PHY节点绑定,实现数字逻辑与模拟电路的连接。

4.6.1 PHY绑定方式

PHY绑定有两种主要方式:

绑定方式 适用场景 设备树写法 优点
直接引用 SoC内部集成PHY phys = <&usbphy0>; 简单直接,无需额外配置
PHY提供者 外部PHY芯片 phys = <&usbphy_provider 0>; 支持多端口PHY复用
多PHY绑定 xHCI + USB2兼容 phys = <&usb3phy0>, <&usb2phy0>; 同时支持USB3和USB2

4.6.2 PHY节点编写示例

内部PHY节点通常与USB控制器位于同一个总线域中,需要配置自身的时钟和复位:

usbphy0: usb-phy@ee0a0000 {
    compatible = "soc,vendor-usb-phy";
    reg = <0x0 0xee0a0000 0x0 0x100>;
    clocks = <&ccu CLK_USB_PHY0>;
    clock-names = "phy";
    resets = <&ccu RST_USB_PHY0>;
    reset-names = "phy";
    #phy-cells = <0>;
    status = "okay";
};

外部PHY芯片绑定示例(以TI TUSB1210为例):

usb_phy_ext: usb-phy@1 {
    compatible = "ti,tusb1210";
    reg = <1>;  /* ULPI总线地址 */
    clocks = <&clkc 30>;
    clock-names = "ref_clk";
    #phy-cells = <0>;
    status = "okay";
};

usb_ehci: usb@ee080000 {
    compatible = "generic-ehci";
    reg = <0x0 0xee080000 0x0 0x100>;
    interrupts = <0 67 4>;
    phys = <&usb_phy_ext>;
    phy-names = "usb";
    status = "okay";
};
常见错误: PHY节点中#phy-cells属性必须与控制器节点的phys引用方式匹配。如果PHY提供者声明#phy-cells = <1>,则控制器引用时必须提供参数,如phys = <&usbphy_provider 0>。参数不匹配会导致PHY查找失败,控制器无法初始化。

4.7 USB控制器设备树结构总览

以下SVG图展示了典型的SoC USB子系统设备树结构,包含EHCI、OHCI、xHCI控制器与PHY、时钟、复位提供者之间的关系:

USB控制器设备树结构总览 时钟控制器 (CCU) clocks 提供者 复位控制器 (RST) resets 提供者 USB PHY phys 提供者 EHCI 控制器 USB 2.0 高速 compatible: "generic-ehci" OHCI 控制器 USB 1.1 全速/低速 compatible: "generic-ohci" xHCI 控制器 USB 3.0/2.0/1.1 compatible: "generic-xhci" clocks clocks clocks resets resets resets phys phys phys 设备树绑定要点 • EHCI/OHCI通常成对出现 • xHCI需要多时钟+双复位 • PHY绑定使用phys属性 • 时钟名与驱动匹配是关键

4.8 实战:完整USB控制器设备树配置

以下是一个完整的SoC USB子系统设备树配置示例,包含EHCI、OHCI、xHCI控制器及其关联的PHY、时钟和复位节点:

/ {
    soc {
        /* 时钟控制器 */
        ccu: clock-controller@f0000000 {
            compatible = "soc,ccu";
            reg = <0x0 0xf0000000 0x0 0x1000>;
            #clock-cells = <1>;
            #reset-cells = <1>;
        };

        /* USB PHY 0 - 用于EHCI/OHCI */
        usbphy0: usb-phy@ee0a0000 {
            compatible = "soc,usb-phy";
            reg = <0x0 0xee0a0000 0x0 0x100>;
            clocks = <&ccu 30>;
            clock-names = "phy";
            resets = <&ccu 40>;
            reset-names = "phy";
            #phy-cells = <0>;
            status = "okay";
        };

        /* USB PHY 1 - 用于xHCI */
        usbphy1: usb-phy@ee0b0000 {
            compatible = "soc,usb3-phy";
            reg = <0x0 0xee0b0000 0x0 0x1000>;
            clocks = <&ccu 31>, <&ccu 32>;
            clock-names = "ref", "phy";
            resets = <&ccu 41>;
            reset-names = "phy";
            #phy-cells = <0>;
            status = "okay";
        };

        /* EHCI控制器 */
        usb_ehci: usb@ee080000 {
            compatible = "soc,ehci", "generic-ehci";
            reg = <0x0 0xee080000 0x0 0x100>;
            interrupts = <0 67 4>;
            clocks = <&ccu 20>;
            clock-names = "usb";
            resets = <&ccu 50>;
            reset-names = "usb";
            phys = <&usbphy0>;
            phy-names = "usb";
            status = "okay";
        };

        /* OHCI控制器 */
        usb_ohci: usb@ee090000 {
            compatible = "soc,ohci", "generic-ohci";
            reg = <0x0 0xee090000 0x0 0x100>;
            interrupts = <0 68 4>;
            clocks = <&ccu 21>;
            clock-names = "usb";
            resets = <&ccu 51>;
            reset-names = "usb";
            phys = <&usbphy0>;
            phy-names = "usb";
            status = "okay";
        };

        /* xHCI控制器 */
        usb_xhci: usb@f0940000 {
            compatible = "soc,xhci", "generic-xhci";
            reg = <0x0 0xf0940000 0x0 0x10000>;
            interrupts = <0 72 4>;
            clocks = <&ccu 22>, <&ccu 23>, <&ccu 24>;
            clock-names = "suspend", "ref", "master";
            resets = <&ccu 52>, <&ccu 53>;
            reset-names = "phy", "mac";
            phys = <&usbphy1>;
            phy-names = "usb3-phy";
            dr_mode = "host";
            maximum-speed = "super-speed";
            status = "okay";
        };
    };
};
移植检查清单:
  • 确认compatible字符串与内核驱动匹配表一致
  • 验证reg地址范围与SoC数据手册一致
  • 检查clocks数量与clock-names一一对应
  • 确认resets的复位顺序符合硬件要求
  • 验证PHY节点的#phy-cells与控制器引用方式匹配
  • 使用dtc -I dtb -O dts反编译检查语法正确性

4.9 常见问题与调试方法

USB控制器设备树配置中常见的问题及解决方案:

问题现象 可能原因 排查方法 解决方案
dmesg报"no usb phy configured" PHY节点未正确绑定 检查phys属性引用路径 修正PHY节点引用或添加缺失的PHY节点
控制器寄存器读取全为0xFF 时钟未使能或复位未释放 使用clk_summary查看时钟状态 添加缺失的时钟或复位属性
USB设备无法枚举 PHY配置错误或电源问题 检查PHY寄存器状态 确认PHY时钟频率和模式配置正确
中断风暴导致系统卡死 中断类型配置错误 检查中断触发方式 将中断类型从边沿触发改为电平触发
xHCI报"port reset failed" 复位时序错误 检查复位顺序和延时 调整reset-names顺序或添加复位延时
调试工具推荐: 使用lsusb -t查看USB设备树拓扑,usbmon捕获USB总线事务,trace-cmd跟踪USB驱动函数调用。对于设备树问题,dt-