4. USB控制器设备树绑定
在嵌入式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"。
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 |
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省电功能,解决某些设备的兼容性问题。
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描述,内核复位框架会按顺序执行复位操作。
复位时序要求:
- PHY复位: 必须先释放PHY复位,等待PHY稳定(通常需要10μs - 100μs)。
- MAC复位: PHY稳定后释放MAC复位,等待控制器自检完成(通常需要1ms - 5ms)。
- 端口复位: 控制器初始化完成后,由驱动对每个端口执行软件复位。
复位属性编写示例:
/* 双复位域配置 */
resets = <&rst RST_USB_PHY>,
<&rst RST_USB_MAC>;
reset-names = "phy", "mac";
/* 单复位配置(适用于简单SoC) */
resets = <&rst RST_USB>;
reset-names = "usb";
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-cells属性必须与控制器节点的phys引用方式匹配。如果PHY提供者声明#phy-cells = <1>,则控制器引用时必须提供参数,如phys = <&usbphy_provider 0>。参数不匹配会导致PHY查找失败,控制器无法初始化。
4.7 USB控制器设备树结构总览
以下SVG图展示了典型的SoC USB子系统设备树结构,包含EHCI、OHCI、xHCI控制器与PHY、时钟、复位提供者之间的关系:
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-