1. RK3576 USB2.0 接口调试全记录:从xHCI异常到稳定枚举的完整解决方案
作为一名嵌入式系统开发工程师,最近在基于Rockchip RK3576 SoC的定制开发板上遇到了一个棘手的USB2.0接口问题。经过两周的深入调试,终于找到了完美解决方案。本文将详细记录整个调试过程,包括问题分析、诊断方法、错误尝试和最终解决方案,希望能为遇到类似问题的同行提供参考。
我们的开发板代号CDKJ CD0_V10,基于野火LubanCat RK3576官方设计进行了硬件裁剪,运行Linux 6.1.99内核。在硬件调整中,我们移除了Combo PHY1的供电以降低成本,但仍需使用该通道上的USB2.0功能。最初配置下,USB设备能检测到物理连接但无法完成枚举,xHCI控制器最终会报告"死亡"。
2. 硬件架构深度解析
2.1 RK3576 USB子系统架构
RK3576的USB子系统采用Synopsys DesignWare USB3 (DWC3)控制器,其架构设计值得深入理解:
code复制DWC3 USB控制器
│
├── xHCI主机模式
│ └── 负责USB2.0/3.0设备枚举和数据传输
│
├── 设备模式(Gadget)
│ └── 支持OTG功能(本案例中未使用)
│
└── DWC3核心
├── 管理GCTL、GUSB2PHYCFG等全局寄存器
├── 协调USB2和USB3 PHY的工作
└── 处理中断和DMA传输
在我们的定制板上,硬件连接如下:
- USB2.0 TYPE-A接口(J3)
- D+/D- → USB2_OTG1_DP/DM (连接USB2 PHY1)
- VBUS → 由GPIO0_B1控制的5V电源
- ID引脚悬空(标准TYPE-A接口设计)
2.2 关键硬件改动与影响
| 功能模块 | 官方板状态 | 定制板状态 | 影响范围 |
|---|---|---|---|
| Combo PHY0供电 | 支持 | 电源接地 | PCIe0/SATA0不可用 |
| Combo PHY1供电 | 支持 | 电源接地 | USB3.0功能不可用 |
| GMAC1 | 支持 | 引脚复用 | 仅保留单网口功能 |
特别需要注意的是,虽然我们禁用了Combo PHY1的供电,但仍需要通过它来支持USB2.0功能,这是后续问题的关键所在。
2.3 寄存器组与时钟依赖
系统正常工作需要以下关键寄存器组和时钟:
关键寄存器组:
- php_grf (0x26020000): USB2 PHY控制
- pipe_phy1_grf (0x2602a000): Combo PHY1控制
- DWC3寄存器 (0x2340c000): 控制器核心配置
时钟依赖关系:
code复制XIN24M (24MHz晶振)
├── USB480M_PHY1 (480MHz)
│ └── USB2 PHY工作时钟
├── CLK_REF_USB3OTG1 (24MHz)
│ └── DWC3控制器参考时钟
└── PCLK_PHP_ROOT (APB时钟)
└── php_grf寄存器访问时钟
3. 问题现象与初步诊断
3.1 初始症状表现
在最初的设备树配置中,我们简单地禁用了combphy1_psu:
dts复制&combphy1_psu {
status = "disabled";
};
&usb_drd1_dwc3 {
dr_mode = "host";
phys = <&u2phy1_otg>;
phy-names = "usb2-phy";
status = "okay";
};
系统表现出以下异常:
- 内核日志显示"No USB3 PHY"警告
- 插入USB设备后,lsusb只显示root hub
- xHCI控制器约13秒后报告"死亡"
- USB设备似乎卡在枚举过程中
3.2 深入诊断数据收集
通过一系列诊断命令,我们收集到关键信息:
端口状态检查:
bash复制cat /sys/kernel/debug/usb/xhci/xhci-hcd.0.auto/ports/port01/portsc
输出显示:
code复制Powered Connected Disabled Link:Polling PortSpeed:2 Change: Wake:
关键问题:
- 端口处于Disabled状态
- 链路训练卡在Polling阶段
- 检测到Low/Full Speed设备
中断计数检查:
bash复制cat /proc/interrupts | grep xhci
输出始终为0,表明xHCI控制器从未产生中断。
MFINDEX寄存器检查:
bash复制devmem2 0x23480440 w
输出为0x00000000且不变,说明xHCI调度器未运行。
3.3 问题总结表
| 检查项 | 预期状态 | 实际状态 | 状态 |
|---|---|---|---|
| 物理连接检测 | Connected | Connected | ✅ |
| 端口使能 | Enabled | Disabled | ❌ |
| 链路状态 | U0 (Active) | Polling | ❌ |
| xHCI中断计数 | >0 | 0 | ❌ |
| MFINDEX | 递增 | 0 | ❌ |
| 设备枚举 | 成功 | 失败 | ❌ |
4. 系统化诊断过程
4.1 第一阶段:时钟问题排查
在早期诊断中,我们发现访问php_grf寄存器会导致系统死机:
bash复制devmem2 0x26020038 w
这表明PCLK_PHP_ROOT时钟未使能,导致APB总线访问超时。
解决方案:
修改USB2 PHY驱动(phy-rockchip-inno-usb2.c),显式使能时钟:
c复制static int rockchip_usb2phy_probe(...) {
/* 新增时钟使能代码 */
rphy->usbctrl_grf_clk = devm_clk_get_optional(dev, "usbctrl-grf");
if (!IS_ERR_OR_NULL(rphy->usbctrl_grf_clk)) {
ret = clk_prepare_enable(rphy->usbctrl_grf_clk);
if (ret) return ret;
}
// ...
}
修复后验证:
bash复制cat /sys/kernel/debug/clk/pclk_php_root/clk_enable_count # 输出:1
devmem2 0x26020038 w # 不再死机,可正常读取
4.2 第二阶段:pipe_phystatus信号问题
时钟修复后,USB仍无法枚举。进一步分析发现RK3576的特殊设计:
当Combo PHY1不存在时,USB2 PHY需要模拟pipe_phystatus信号,告知DWC3控制器USB3 PHY不可用。
关键寄存器: php_grf+0x38 (USB_GRF_USB3OTG1_STATUS)
| Bit | 名称 | 作用 |
|---|---|---|
| 0 | pipe_phystatus | USB3 PHY状态信号 |
| 3 | sel_pipe_phystatus | 选择pipe_phystatus来源 |
| 7 | pipe_phystatus_value | 模拟的phystatus值 |
| 8 | sel_pipe_phystatus_en | 使能模拟功能 |
我们在设备树中添加了相关属性:
dts复制&u2phy1_otg {
rockchip,sel-pipe-phystatus; /* 使能pipe_phystatus模拟 */
rockchip,typec-vbus-det; /* VBUS检测 */
status = "okay";
};
虽然寄存器值正确设置为0x189,但USB仍然无法枚举。
4.3 第三阶段:DWC3控制器分析
检查DWC3寄存器状态:
bash复制devmem2 0x2340c110 w # GCTL → 0x30c03004
devmem2 0x2340c2c0 w # GUSB3PIPECTL → 0x010c0002
devmem2 0x2340c200 w # GUSB2PHYCFG → 0x00002500
分析DWC3驱动代码(drivers/usb/dwc3/core.c),发现关键问题:
c复制static int dwc3_phy_setup(struct dwc3 *dwc) {
/* 即使usb3_generic_phy=NULL,仍然配置GUSB3PIPECTL */
reg = dwc3_readl(dwc->regs, DWC3_GUSB3PIPECTL(0));
dwc3_writel(dwc->regs, DWC3_GUSB3PIPECTL(0), reg);
// ...
}
DWC3驱动始终配置GUSB3PIPECTL寄存器,即使没有USB3 PHY,这可能导致控制器尝试与不存在的USB3 PHY通信。
4.4 时钟状态最终验证
确认所有相关时钟已正确使能:
bash复制# USB2 PHY1时钟
cat /sys/kernel/debug/clk/usb480m_phy1/clk_enable_count # 1
cat /sys/kernel/debug/clk/usb480m_phy1/clk_rate # 480000000
# DWC3控制器时钟
cat /sys/kernel/debug/clk/aclk_usb3otg1/clk_enable_count # 1
cat /sys/kernel/debug/clk/clk_ref_usb3otg1/clk_enable_count # 1
结论:时钟配置已完全正确,问题不在时钟层面。
5. 错误修复尝试与经验教训
在找到正确解决方案前,我们尝试了多种方法:
5.1 尝试方案总结表
| 方案 | 修改内容 | 结果 | 失败原因 |
|---|---|---|---|
| A | 完全禁用combphy1_psu | ❌ | DWC3内部状态异常 |
| B | 添加DWC3 quirk | ❌ | 无法解决PHY引用问题 |
| C | 修改DWC3驱动跳过GUSB3PIPECTL配置 | ❌ | 根本原因不在寄存器配置 |
| D | 修复pipe_phystatus时序 | ❌ | 不是时序问题 |
5.2 关键经验教训
- 不要简单禁用PHY:即使硬件不使用某个PHY,控制器可能仍依赖其存在
- 查找厂商特定解决方案:Rockchip提供了rockchip,dis-u3otg1-port等专用属性
- 理解硬件架构:USB2和USB3 PHY在DWC3控制器中协同工作,不能孤立看待
- 系统化诊断方法:从现象到本质,对比分析,代码审计,寄存器验证
6. 根本原因分析与最终解决方案
6.1 根本原因
DWC3控制器在设计上假设USB3 PHY存在。即使只使用USB2.0功能:
- 控制器仍会尝试初始化USB3 PIPE接口
- 等待USB3 PHY的响应
- 配置xHCI的SuperSpeed端口
当combphy1_psu被完全禁用时:
- 设备树解析无法获取有效的PHY句柄
- DWC3控制器的usb3_generic_phy为NULL
- 控制器内部状态机进入异常状态
- xHCI子模块无法正常运行
6.2 Rockchip的专用解决方案
通过分析Rockchip的Combo PHY驱动代码(phy-rockchip-naneng-combphy.c),我们发现专用属性:
c复制static int rockchip_combphy_init(...) {
if (device_property_present(priv->dev, "rockchip,dis-u3otg1-port")) {
/* 禁用USB3端口但保持PHY使能 */
rockchip_combphy_param_write(priv->pipe_grf, &cfg->u3otg1_port_en, false);
/* RK3576特殊处理 */
if (of_device_is_compatible(priv->dev->of_node, "rockchip,rk3576-naneng-combphy"))
rockchip_combphy_param_write(priv->phy_grf, &cfg->usb_mode_set, true);
}
}
6.3 最终设备树配置
Combo PHY1配置:
dts复制&combphy1_psu {
rockchip,dis-u3otg1-port; /* 关键:禁用USB3端口但保持PHY使能 */
status = "okay"; /* 必须使能,否则DWC3获取不到PHY */
};
DWC3控制器配置:
dts复制&usb_drd1_dwc3 {
dr_mode = "host";
maximum-speed = "high-speed"; /* 限制为USB2.0速度 */
phys = <&u2phy1_otg>, <&combphy1_psu PHY_TYPE_USB3>; /* 同时引用两个PHY */
phy-names = "usb2-phy", "usb3-phy";
snps,dis_u2_susphy_quirk; /* 禁用USB2 PHY挂起,提高稳定性 */
snps,usb2-lpm-disable; /* 禁用USB2 LPM,提高兼容性 */
status = "okay";
};
6.4 解决方案对比表
| 配置项 | 错误方案 | 正确方案 |
|---|---|---|
| combphy1_psu status | disabled | okay |
| rockchip,dis-u3otg1-port | 无 | 有 |
| DWC3 phys | 只引用u2phy1_otg | 同时引用两个PHY |
| DWC3 phy-names | 只有usb2-phy | usb2-phy + usb3-phy |
7. 验证与测试结果
应用修复后,插入USB设备测试:
lsusb输出:
code复制Bus 001 Device 002: ID 046d:c31c Logitech, Inc. Keyboard K120
Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
中断计数:
code复制56: 32 0 0 0 GICv2 292 Level xhci-hcd:usb1
端口状态:
code复制Powered Connected Enabled Link:U0 PortSpeed:2 Change: Wake:
对比表:
| 指标 | 修复前 | 修复后 |
|---|---|---|
| lsusb输出 | 只有root hub | 显示外设 |
| xHCI中断计数 | 0 | 32 |
| 端口状态 | Disabled | Enabled |
| 链路状态 | Polling | U0 (Active) |
| 设备枚举 | 失败 | 成功 |
8. 技术原理深度剖析
8.1 rockchip,dis-u3otg1-port工作原理
设置此属性后,Combo PHY驱动的行为变化:
code复制正常初始化流程:
rockchip_combphy_init()
├── 检查PHY类型(PCIe/SATA/USB3)
├── 配置PIPE接口
├── 初始化USB3 SerDes
└── 使能USB3端口
使用dis-u3otg1-port时:
rockchip_combphy_init()
├── 检测到rockchip,dis-u3otg1-port属性
├── 禁用u3otg1_port_en(不初始化USB3端口)
├── 设置usb_mode_set(告知控制器USB3不可用)
└── 直接返回(跳过后续初始化)
8.2 为什么需要同时引用两个PHY
DWC3控制器初始化流程(drivers/usb/dwc3/core.c):
c复制static int dwc3_core_init(...) {
// 获取PHY
ret = dwc3_core_get_phy(dwc); // 获取usb2_generic_phy和usb3_generic_phy
// 初始化PHY
ret = phy_init(dwc->usb2_generic_phy);
ret = phy_init(dwc->usb3_generic_phy); // 如果为NULL,会跳过
// 软复位
ret = dwc3_core_soft_reset(dwc);
// 配置PHY
ret = dwc3_phy_setup(dwc); // 配置GUSB2PHYCFG和GUSB3PIPECTL
// 上电PHY
ret = phy_power_on(dwc->usb2_generic_phy);
ret = phy_power_on(dwc->usb3_generic_phy);
}
关键点:虽然phy_init(NULL)会安全返回,但DWC3控制器的某些内部状态依赖于PHY的存在。当usb3_generic_phy=NULL时,控制器的xHCI子模块可能进入异常状态。
8.3 RK3576的特殊处理
RK3576相比其他Rockchip SoC需要额外配置:
c复制if (of_device_is_compatible(priv->dev->of_node, "rockchip,rk3576-naneng-combphy"))
rockchip_combphy_param_write(priv->phy_grf, &cfg->usb_mode_set, true);
这个usb_mode_set寄存器告知USB控制器当前配置为纯USB2.0模式,USB3功能不可用。
9. 经验总结与调试方法论
9.1 RK3576 USB调试Checklist
code复制□ 1. 时钟检查
□ PCLK_PHP_ROOT enable_count > 0
□ USB480M_PHY1 enable_count > 0
□ CLK_REF_USB3OTG1 enable_count > 0
□ ACLK_USB3OTG1 enable_count > 0
□ 2. PHY状态检查
□ php_grf可访问(不死机)
□ pipe_phystatus正确配置
□ 3. DWC3控制器检查
□ GCTL寄存器正常
□ GUSB2PHYCFG配置正确
□ GUSB3PIPECTL配置正确
□ 4. xHCI状态检查
□ 中断计数 > 0
□ MFINDEX递增
□ 端口状态Enabled
□ 链路状态U0
□ 5. 设备树配置
□ combphy1_psu使用rockchip,dis-u3otg1-port
□ usb_drd1_dwc3同时引用两个PHY
□ u2phy1_otg正确配置
9.2 关键调试技巧
- 寄存器级调试:使用devmem2直接读取硬件寄存器,验证软件配置是否生效
- 对比分析法:将异常端口与正常端口(如USB2 PHY0)进行对比,找出配置差异
- 驱动代码审计:阅读驱动源码,理解初始化流程和厂商特定处理逻辑
- 时序分析:通过内核日志和寄存器状态,分析初始化时序问题
9.3 适用场景
本解决方案适用于:
- RK3576平台
- Combo PHY1硬件不供电或不使用
- 需要使用USB2_OTG1_DP/DM接口的USB2.0功能
- USB3.0功能不需要
10. 附录:完整设备树配置
dts复制/* USB2 PHY1 */
&u2phy1 {
status = "okay";
};
&u2phy1_otg {
phy-supply = <&vcc5v0_usb20_host>;
dr_mode = "host";
status = "okay";
};
/* Combo PHY1 - 使能但禁用USB3端口 */
&combphy1_psu {
rockchip,dis-u3otg1-port;
status = "okay";
};
/* DWC3控制器 */
&usb_drd1_dwc3 {
dr_mode = "host";
maximum-speed = "high-speed";
phys = <&u2phy1_otg>, <&combphy1_psu PHY_TYPE_USB3>;
phy-names = "usb2-phy", "usb3-phy";
snps,dis_u2_susphy_quirk;
snps,usb2-lpm-disable;
status = "okay";
};
/* VBUS电源控制 */
vcc5v0_usb20_host: vcc5v0-usb20-host {
compatible = "regulator-fixed";
regulator-name = "vcc5v0_usb20_host";
regulator-min-microvolt = <5000000>;
regulator-max-microvolt = <5000000>;
regulator-always-on;
regulator-boot-on;
enable-active-high;
gpio = <&gpio0 RK_PB1 GPIO_ACTIVE_HIGH>;
vin-supply = <&vcc_sys>;
pinctrl-names = "default";
pinctrl-0 = <&usb20_host_pwr_en>;
};
11. 附录:诊断命令汇总
bash复制# 时钟状态检查
cat /sys/kernel/debug/clk/pclk_php_root/clk_enable_count
cat /sys/kernel/debug/clk/usb480m_phy1/clk_enable_count
cat /sys/kernel/debug/clk/clk_ref_usb3otg1/clk_enable_count
cat /sys/kernel/debug/clk/aclk_usb3otg1/clk_enable_count
# xHCI状态检查
cat /proc/interrupts | grep xhci
cat /sys/kernel/debug/usb/xhci/xhci-hcd.0.auto/ports/port01/portsc
devmem2 0x23480440 w # MFINDEX
# PHY寄存器检查
devmem2 0x26020038 w # php_grf USB_GRF_USB3OTG1_STATUS
# DWC3寄存器检查
devmem2 0x2340c110 w # GCTL
devmem2 0x2340c200 w # GUSB2PHYCFG
devmem2 0x2340c2c0 w # GUSB3PIPECTL
# USB设备枚举
lsusb
dmesg | grep -iE "usb|dwc3|xhci"
12. 附录:关键源码位置
| 文件 | 函数/位置 | 作用 |
|---|---|---|
| drivers/usb/dwc3/core.c | dwc3_core_init() | DWC3控制器初始化 |
| drivers/usb/dwc3/core.c | dwc3_phy_setup() | PHY配置 |
| drivers/phy/rockchip/phy-rockchip-naneng-combphy.c | rockchip_combphy_init() | Combo PHY初始化 |
| drivers/phy/rockchip/phy-rockchip-inno-usb2.c | rockchip_usb2phy_init() | USB2 PHY初始化 |
| drivers/usb/host/xhci.c | xhci_init() | xHCI控制器初始化 |
13. 实际测试验证
13.1 USB键盘测试
测试设备:Logitech K120 USB键盘(低速设备)
验证步骤:
-
确认设备枚举:
bash复制
lsusb输出应显示键盘设备
-
查看输入设备:
bash复制cat /proc/bus/input/devices | grep -A5 "Keyboard" -
测试按键输入:
bash复制hexdump /dev/input/eventX # 观察按键事件
13.2 USB存储设备测试
测试设备:USB读卡器+SD卡(高速设备)
验证步骤:
-
插入前记录状态:
bash复制
lsusb lsblk -
插入后检查:
bash复制dmesg | tail -30 # 查看内核日志 lsblk # 确认新设备出现 -
挂载与读写测试:
bash复制mount /dev/sdX1 /mnt/usb dd if=/dev/zero of=/mnt/usb/test.bin bs=1M count=100 conv=fsync
13.3 快速验证脚本
bash复制#!/bin/bash
echo "=========================================="
echo "RK3576 USB2.0接口快速验证"
echo "=========================================="
echo "[1] USB设备列表:"
lsusb
echo "[2] xHCI中断计数:"
cat /proc/interrupts | grep xhci
echo "[3] 块设备列表:"
lsblk
echo "[4] 最近USB相关日志:"
dmesg | grep -iE "usb|xhci|hid|storage" | tail -20
echo "=========================================="
echo "验证完成"
echo "=========================================="
14. 最终结论
通过对USB键盘(HID设备)和USB读卡器(Mass Storage设备)的全面测试,验证了USB2.0 TYPE-A接口的以下功能完全正常:
| 功能 | 状态 | 说明 |
|---|---|---|
| Low-Speed设备支持 | ✅ | USB键盘(1.5Mbps) |
| High-Speed设备支持 | ✅ | USB读卡器(480Mbps) |
| HID类设备 | ✅ | 键盘输入正常 |
| Mass Storage类设备 | ✅ | 读写正常 |
| 热插拔 | ✅ | 插入/拔出正常识别 |
| xHCI中断 | ✅ | 中断计数正常递增 |
项目总结:通过系统化的调试方法,我们成功解决了RK3576平台上USB2.0接口的枚举问题。关键点在于理解DWC3控制器对PHY引用的依赖关系,并正确使用Rockchip提供的专用属性rockchip,dis-u3otg1-port。这一解决方案不仅修复了当前问题,也为后续类似硬件设计提供了宝贵参考。
