USB 枚举到一半断了?深入 DWC3 控制器的调试世界 电子说
前置知识:建议先读第1章:USB 子系统架构,了解 RK3576 的 USB 硬件拓扑。本文专注软件层面:DWC3 控制器的驱动、寄存器、调试接口和实战排错。
一、DWC3 控制器到底是个什么东西
RK3576 里的两个 USB 3.1 OTG 控制器(usb@23000000 和usb@23400000)都是 Synopsys 的 DWC3 IP。这个 IP 不是单纯的 Host 控制器,也不是单纯的 Device 控制器,而是一个三合一的架构:

三种工作模式由GCTL.PRTCAPDIR 寄存器控制:
| PRTCAPDIR 值 | 模式 | 行为 |
| 0 | DEVICE | 纯 Device,走 Gadget 模块,暴露 UDC |
| 1 | HOST | 纯 Host,走 xHCI 模块,暴露 USB Host |
| 3 | OTG | Dual-Role,监听 ID/VBUS,自动切换 Host/Device |
RK3576 SDK 中 DTS 配的 dr_mode = "otg" 就是告诉 DWC3 初始模式为 OTG。
二、DWC3 debugfs:第一个调试入口
很多教程说 DWC3 的 debugfs 路径是 /sys/kernel/debug/dwc3/,这在 RK3576 上是错的。RK3576 内核里,DWC3 的 debugfs 挂在平台设备名下面。
2.1 DRD0(OTG0)调试路径
# DRD0 的 debugfs 入口ls /sys/kernel/debug/usb/23000000.usb/# 板子实际输出:# ep0in ep1in ep2in ep3in ep4in ep5in ep6in# ep0out ep1out ep2out ep3out ep4out ep5out# link_state mode regdump testmode
每个文件的作用(对照内核源码kernel-6.1/drivers/usb/dwc3/debugfs.c):
| 文件 | 功能 | 源码对应 |
| regdump | 转储所有 DWC3 全局寄存器 | dwc3_regs[] 数组,含 GCTL/DCFG/DSTS/GSNPSID 等 200+ 寄存器 |
| mode | 查看/切换工作模式 | dwc3_mode_fops,写 host/device/otg |
| link_state | 查看 USB 链路状态 | dwc3_link_state_fops,显示 On/Recovery/SS.Disabled 等 |
| testmode | 设置 USB2 测试模式 | dwc3_testmode_fops,支持 test_j/test_k/test_packet 等 |
| ep*in/ep*out | 端点子目录 | 每个端点有 tx_fifo_size/trb_ring/transfer_type 等属性 |
2.2 DRD1(OTG1)调试路径
# DRD1 的 debugfs 入口ls /sys/kernel/debug/usb/23400000.usb/# 板子实际输出:# link_state lsp_dump mode regdump testmode
DRD1 没有 ep* 子目录,因为它通常配置为纯 Host 模式(dr_mode = "host"),Gadget 端点不会创建。
2.3 XHCI 调试路径
DWC3 切换到 Host 模式后,内核 xHCI 驱动会在下面创建入口:
ls /sys/kernel/debug/usb/xhci/# 板子输出:# xhci-hcd.0.auto
2.4 Trace Events(官方文档确认的三种 trace)
内核提供了三组 USB 相关的 tracepoint,官方 PDF Rockchip_Trouble_Shooting_Linux4.19_USB_Gadget_UVC_CN.pdf 和Rockchip_Developer_Guide_USB_CN.pdf 中均有确认:
# DWC3 控制器事件ls /sys/kernel/debug/tracing/events/dwc3/# 确认的事件:# dwc3_alloc_request dwc3_complete_trb# dwc3_ctrl_req dwc3_ep_dequeue# dwc3_ep_queue dwc3_event# dwc3_free_request dwc3_gadget_ep_cmd# dwc3_gadget_ep_disable dwc3_gadget_ep_enable# dwc3_gadget_generic_cmd dwc3_gadget_giveback# dwc3_prepare_trb dwc3_readl# dwc3_writel# Gadget/UDC 事件ls /sys/kernel/debug/tracing/events/gadget/# usb_ep_alloc_request usb_ep_free_request# usb_ep_queue usb_ep_dequeue# usb_ep_enable usb_ep_disable# usb_ep_set_halt usb_ep_clear_halt# usb_gadget_connect usb_gadget_disconnect# usb_gadget_vbus_connect usb_gadget_vbus_disconnect# XHCI 主机事件ls /sys/kernel/debug/tracing/events/xhci-hcd/
开启 trace 的姿势:
echo 1 > /sys/kernel/debug/tracing/events/dwc3/enablecat /sys/kernel/debug/tracing/trace_pipe# 停止echo 0 > /sys/kernel/debug/tracing/events/dwc3/enable
三、Core 模块:dwc3_probe() 的完整流程
DWC3 控制器的初始化是 Linux 平台驱动框架的标准流程,源码在 kernel-6.1/drivers/usb/dwc3/core.c。

3.1 GSNPSID:芯片身份证
dwc3_core_is_valid() 读的第一个寄存器就是GSNPSID:
// kernel-6.1/drivers/usb/dwc3/core.creg = dwc3_readl(dwc->regs, DWC3_GSNPSID);dwc->ip = DWC3_GSNPS_ID(reg);
RK3576 用的是 DWC31(USB 3.1 版本),GSNPSID 会返回 0x5533xxxx。如果读到其他值,说明寄存器映射错了或者时钟没开。
实战验证:
cat /sys/kernel/debug/usb/23000000.usb/regdump | grep GSNPSID# 正常: GSNPSID: 0x5533102a(示例值,具体随版本变化)
3.2 关键寄存器速查
| 寄存器 | 缩写 | 用途 |
| GSNPSID | Global Synopsys ID | 芯片 ID,确认 DWC3 IP 版本 |
| GCTL | Global Control | 核心控制寄存器,PRTCAPDIR 就在这里,决定 Host/Device/OTG |
| DCFG | Device Configuration | 设备配置:速度、端点数、地址等 |
| DSTS | Device Status | 设备状态:当前链路状态(USBLNKST)、连接速度(CONNECTSPD) |
3.3 时钟和复位
dwc3_probe() 中时钟获取的逻辑:
ret = clk_bulk_get_all(dwc->dev, &dwc->clks);ret = clk_bulk_prepare_enable(dwc->num_clks, dwc->clks);ret = reset_control_deassert(dwc->reset);
probe 卡在 EPROBE_DEFER 通常是时钟或复位控制器还没准备好。
四、Gadget 模块:Device 模式下的调试
DWC3 处于 Device 模式时,内核会注册 UDC(USB Device Controller)。
4.1 UDC sysfs 属性
# 查看 UDC 列表ls /sys/class/udc/# 正常: 23000000.usb# 查看 UDC 属性ls /sys/class/udc/23000000.usb/# state current_speed uevent ...cat /sys/class/udc/23000000.usb/state# 正常: configured / not-attached / attached / powered / default / addresscat /sys/class/udc/23000000.usb/current_speed# 正常: SuperSpeed / HighSpeed / FullSpeed / LowSpeed# 板子实际: state=configured, current_speed=high-speed
state 的完整状态机:not-attached -> attached -> powered -> default -> address -> configured
4.2 ConfigFS Gadget 配置
Rockchip SDK 使用 ConfigFS 配置 USB Gadget 功能(官方 PDF 确认的路径)
# 查看 UDC 列表ls /sys/class/udc/# 正常: 23000000.usb# 查看 UDC 属性ls /sys/class/udc/23000000.usb/# state current_speed uevent ...cat /sys/class/udc/23000000.usb/state# 正常: configured / not-attached / attached / powered / default / addresscat /sys/class/udc/23000000.usb/current_speed# 正常: SuperSpeed / HighSpeed / FullSpeed / LowSpeed# 板子实际: state=configured, current_speed=high-speed
4.3 Gadget Trace
ls /sys/kernel/config/usb_gadget/# rockchip/ls /sys/kernel/config/usb_gadget/rockchip/# UDC bDeviceClass bDeviceProtocol bcdUSB configs functions idProduct idVendor stringsls /sys/kernel/config/usb_gadget/rockchip/functions/# rndis.gs0 uvc.gs6 ...
五、Host 模块:xHCI 驱动分析
DWC3 切换到 Host 模式后,内核加载 xhci-hcd 驱动。
5.1 xHCI 调试入口
ls /sys/kernel/debug/usb/xhci/# xhci-hcd.0.auto# 查看 USB 设备树(所有已枚举的设备)cat /sys/kernel/debug/usb/devices# 典型输出:# T: Bus=01 Lev=00 Prnt=00 Port=00 Cnt=00 Dev#= 1 Spd=480 MxCh= 1# D: Ver= 2.00 Cls=09(hub ) Sub=00 Prot=00 MxPS=64 #Cfgs= 1
5.2 xHCI Trace
echo 1 > /sys/kernel/debug/tracing/events/xhci-hcd/enablecat /sys/kernel/debug/tracing/trace_pipe
六、DRD/OTG 模块:角色切换的内幕
OTG 模式是 RK3576 USB 最容易出问题的地方。角色切换涉及三层机制。
6.1 切换机制总览

两种路径:
1.usb-role-switch(Type-C CC 芯片方案):CC 芯片(HUSB311/FUSB302)通过 I2C 通知内核角色变化,内核调用 usb_role_switch 回调
2.extcon 机制(无 CC 芯片,直接 GPIO 检测 ID):USB2 PHY 通过 extcon 框架传递 USB_HOST 状态变化
6.2 源码中的切换流程
// kernel-6.1/drivers/usb/dwc3/drd.cvoid dwc3_otg_update(struct dwc3 *dwc, bool ignore_idstatus){ id = extcon_get_state(dwc->edev, EXTCON_USB_HOST); vbus = extcon_get_state(dwc->edev, EXTCON_USB); if (id && !vbus) dwc3_set_mode(dwc, DWC3_GCTL_PRTCAP_HOST); else if (vbus) dwc3_set_mode(dwc, DWC3_GCTL_PRTCAP_DEVICE);}
dwc3_set_mode() 把切换请求放入 workqueue:
// kernel-6.1/drivers/usb/dwc3/core.cvoid dwc3_set_mode(struct dwc3 *dwc, u32 mode){ dwc->desired_dr_role = mode; queue_work(system_freezable_wq, &dwc->drd_work);}
6.3 手动切换模式
官方文档确认的方法(RK3576 PDF 中的 OTG mode 章节):
# [New 方法] 通过 DWC3 debugfs 切换echo host > /sys/kernel/debug/usb/23000000.usb/mode # 强制 Hostecho device > /sys/kernel/debug/usb/23000000.usb/mode # 强制 Deviceecho otg > /sys/kernel/debug/usb/23000000.usb/mode # 恢复 OTG# [Legacy 方法] 通过 USB2 PHY 切换(老版本内核)echo host > /sys/devices/platform/2602e000.syscon/2602e000.syscon:usb2-phy@0/otg_modeecho peripheral > /sys/devices/platform/2602e000.syscon/2602e000.syscon:usb2-phy@0/otg_mode
6.4 切换过程中的寄存器操作
角色切换的核心是__dwc3_set_mode()(core.c):
// 切换前:CoreSoftReset 确保时钟同步reg = dwc3_readl(dwc->regs, DWC3_GCTL);reg |= DWC3_GCTL_CORESOFTRESET;dwc3_writel(dwc->regs, DWC3_GCTL, reg);msleep(100); // 等待时钟同步reg &= ~DWC3_GCTL_CORESOFTRESET;dwc3_writel(dwc->regs, DWC3_GCTL, reg);// 切换:修改 GCTL.PRTCAPDIRdwc3_set_prtcap(dwc, desired_dr_role);// 切换后:初始化对应模块// Host -> dwc3_host_init() + phy_set_mode(USB_HOST)// Device -> dwc3_gadget_init() + phy_set_mode(USB_DEVICE)// OTG -> dwc3_otg_init() + dwc3_otg_update()
七、中断处理:DWC3 的中断架构
DWC3 的中断分三类:
7.1 全局中断(GEVT)
处理 USB 总线级事件:连接、断开、复位、suspend/resume。通过 GEVTEN(Global Event Enable)寄存器使能,事件写入 GEVTADR/GEVTSIZ 指定的内存队列。
7.2 端点事件(DEP Event)
每个端点有自己的事件寄存器。DEP_BASE(n) + DEPCMD 下发端点命令,DEPEVT 上报端点事件(transfer complete、stream event 等)。
7.3 OTG 事件(OEVT)
OTG 模式下独有的事件:
| 事件位 | 含义 |
| CONIDSTSCHNGEN | Connector ID 状态变化(插入/拔出) |
| BDEVSESSVLDDETEN | B 设备 Session 有效(VBUS 检测到) |
| ADEVSESSENDDETEN | A 设备 Session 结束 |
| HRRCONFNOTIFEN | Host Negotiation Role 确认 |
中断合并:DWC3 支持中断合并(Interrupt Moderation),通过 GUCTL 寄存器配置,减少高频事件下的中断风暴。在 USB 视频传输等场景下,合理设置合并参数可以显著降低 CPU 占用。
八、调试方法速查
8.1 regdump:寄存器快照
cat /sys/kernel/debug/usb/23000000.usb/regdump | head -40# 输出示例(关键字段):# GSNPSID: 0x5533102a <- DWC31 IP# GCTL: 0x00000003 <- PRTCAPDIR=3 (OTG)# DCFG: 0x04000000 <- 设备配置# DSTS: 0x00000001 <- 设备状态# GSTS: 0x00020001 <- 当前模式
正常工作时拍一份,出问题后再拍一份,diff 对比差异。
8.2 link_state:链路状态
cat /sys/kernel/debug/usb/23000000.usb/link_state# 可能的输出:# On <- 正常工作# Sleep <- USB2 suspend# Recovery <- USB3 恢复中# SS.Disabled <- SuperSpeed 被禁用# Compliance <- 合规测试模式
8.3 mode:工作模式
cat /sys/kernel/debug/usb/23000000.usb/mode# 输出: host / device / otg
8.4 testmode:USB2 合规测试
官方 PDF 中 USB 合规测试章节确认的五种测试模式:
cat /sys/kernel/debug/usb/23000000.usb/testmode# 输出: no_test / test_j / test_k / test_se0_nak / test_packet / test_force_enableecho test_j > /sys/kernel/debug/usb/23000000.usb/testmode
九、实战案例
案例一:DRD 模式切换失败,Type-C 正反插没反应
现象:
RK3576 板子,Type-C 口插入 PC,/sys/class/udc/23000000.usb/state 显示not-attached,dmesg 没有 VBUS 相关日志。
排查:
# 1. 查当前模式cat /sys/kernel/debug/usb/23000000.usb/mode# 输出: otg <- 模式配置对了# 2. 查 TCPM 日志(CC 芯片日志)cat /sys/kernel/debug/usb/tcpm-2-004e/log | tail -20# 没有日志!说明 CC 芯片没检测到插入# 3. 查 extcon 状态cat /sys/class/extcon/usb-role-switch/state# 输出: HOST=0, USB=0 <- 既不是 Host 也不是 Device# 4. 查 DTS 中 CC 芯片配置cat /sys/firmware/devicetree/base/i2c@*/husb311@4e/compatible# 输出空 -> CC 芯片驱动没加载# 5. 查 I2C 总线dmesg | grep husb311# 输出: husb311: probe of 2-004e failed with error -121 (远程I/O错误)
根因:I2C 总线不通,HUSB311 芯片通信失败。可能原因:I2C 引脚复用配置错误、上拉电阻没焊、或者芯片地址不对。
修复:修复 I2C 后验证:
dmesg | grep husb311# 正常: husb311 2-004e: husb311 probedcat /sys/class/udc/23000000.usb/state# 正常: configured
案例二:Gadget 枚举失败,PC 端显示"未知 USB 设备"
现象:RK3576 通过 Type-C 连 PC,Gadget 配置了 RNDIS+UVC,但 PC 端识别为"未知 USB 设备"。
排查:
# 1. 查 UDC 状态cat /sys/class/udc/23000000.usb/state# 输出: configured <- UDC 层已经配置好了cat /sys/class/udc/23000000.usb/current_speed# 输出: high-speed <- 只有 USB 2.0 速度# 2. 查 ConfigFS 配置ls /sys/kernel/config/usb_gadget/rockchip/configs/b.1/# f1 -> ../../functions/rndis.gs0# f2 -> ../../functions/uvc.gs6/# 配置看起来正确# 3. 查 gadget traceecho 1 > /sys/kernel/debug/tracing/events/gadget/enablecat /sys/kernel/debug/tracing/trace_pipe | head -20# usb_ep_queue: ep0in req ... len 0/64# usb_gadget_vbus_connect: ...# 没有错误 <- Gadget 层没问题# 4. 查 DWC3 link_statecat /sys/kernel/debug/usb/23000000.usb/link_state# 输出: On <- 链路正常# 5. 查 PC 端 USB 枚举(换到 PC 端)lsusb -v -d 1d6b: # 查 RK3576 的 VID/PID# 设备描述符能读到,但配置描述符失败# 6. 查 Gadget 描述符配置cat /sys/kernel/config/usb_gadget/rockchip/bcdUSBcat /sys/kernel/config/usb_gadget/rockchip/bcdDevicecat /sys/kernel/config/usb_gadget/rockchip/strings/0x409/serialnumber# 输出: serialnumber 为空 <- 问题找到了
根因:Gadget 的 serialnumber 字符串描述符没配置。某些 Windows 驱动对 serialnumber 有严格要求,空串会导致枚举异常。
修复:
mkdir -p /sys/kernel/config/usb_gadget/rockchip/strings/0x409echo "RK3576-001" > /sys/kernel/config/usb_gadget/rockchip/strings/0x409/serialnumber# 重新绑定echo "" > /sys/kernel/config/usb_gadget/rockchip/UDCecho "23000000.usb" > /sys/kernel/config/usb_gadget/rockchip/UDC
案例三:Host 模式 U 盘不识别
现象:DRD1 口插 U 盘,dmesg 没有任何 USB 设备插入的日志。
排查:
# 1. 确认当前模式cat /sys/kernel/debug/usb/23400000.usb/mode# 输出: host <- 模式正确# 2. 查 USB 设备树cat /sys/kernel/debug/usb/devices# 只有一个 Root Hub,没有下游设备 <- U 盘没枚举# 3. 查 VBUS 供电ls /sys/kernel/debug/regulator/ | grep hostcat /sys/kernel/debug/regulator/vcc5v0_host/uevent# 正常: POWER_SUPPLY_ONLINE=1 <- 供电正常# 4. 查 xHCI 日志dmesg | grep xhci# xhci-hcd xhci-hcd.0.auto: xHCI Host Controller# 没有端口相关的日志 <- 端口没检测到连接# 5. 查 USB2 PHY 状态cat /sys/firmware/devicetree/base/usb2-phy@1/otg-port/status# 输出: disabled <- PHY 没启用!# 6. 查 Combphy 状态(DRD1 的 USB3 PHY)cat /sys/firmware/devicetree/base/phy@2b060000/status# 输出: disabled <- USB3 PHY 也没启用
根因:DRD1 的 PHY 在 DTS 中 status = "disabled"。虽然 DWC3 本身是 host 模式,但 PHY 没初始化,总线上的信号传不到控制器。
修复:在 board DTS 中启用 PHY 节点:
&u2phy1_otg { phy-supply = <&vcc5v0_host>; status = "okay";};&combphy1_psu { status = "okay";};
案例四:USB3 掉到 USB2,SuperSpeed 不工作
现象:U 盘插 DRD0,current_speed=high-speed,没有 SuperSpeed。
排查:
# 1. 查 link_statecat /sys/kernel/debug/usb/23000000.usb/link_state# 输出: On <- 链路是通的# 2. 查 USBDP PHY 的 U3 端口状态cat /sys/firmware/devicetree/base/phy@2b010000/u3-port/status# 输出: disabled <- USB3 PHY 端口没启用# 3. 查最大速度配置cat /sys/firmware/devicetree/base/usb@23000000/maximum-speed# 输出: high-speed <- DTS 里限制了最高速度
根因:DTS 中 maximum-speed = "high-speed" 限制了 DWC3 只跑 USB 2.0,或者 USBDP PHY 的 u3-port 没启用。
修复:DTS 中去掉 maximum-speed 限制,并启用usbdp_phy_u3。
案例五:USB Gadget 数据传输中断
现象:UVC Gadget 工作一段时间后,PC 端画面卡住。
排查:
# 1. 查 UDC 状态cat /sys/class/udc/23000000.usb/state# 输出: configured <- 枚举正常# 2. 查 link_statecat /sys/kernel/debug/usb/23000000.usb/link_state# 输出: Sleep <- USB 进入 suspend!# 3. 查 DWC3 traceecho 1 > /sys/kernel/debug/tracing/events/dwc3/enablecat /sys/kernel/debug/tracing/trace_pipe | grep -i "suspend|link"# dwc3_event: event (...): USB link state change -> Sleep
根因:USB 总线空闲时自动 suspend,但 UVC 应用没有正确 wake up。这通常是因为 snps,dis-enblslpm_quirk 没有配置。
修复:DTS 中添加 snps,dis-enblslpm_quirk; 禁用 LPM(Link Power Management)。
十、排查决策树
下次 USB 出问题,按这张图走:

十一、命令速查表
| 目标 | 命令 | 正常输出 |
| DRD0 模式 | cat /sys/kernel/debug/usb/23000000.usb/mode | otg / host / device |
| DRD0 链路状态 | cat /sys/kernel/debug/usb/23000000.usb/link_state | On / Sleep / Recovery |
| DRD0 寄存器 | cat /sys/kernel/debug/usb/23000000.usb/regdump | head | GSNPSID/GCTL/DCFG/DSTS 值 |
| DRD1 模式 | cat /sys/kernel/debug/usb/23400000.usb/mode | host |
| UDC 状态 | cat /sys/class/udc/23000000.usb/state | configured |
| UDC 速度 | cat /sys/class/udc/23000000.usb/current_speed | high-speed / SuperSpeed |
| 强制 Host | echo host > /sys/kernel/debug/usb/23000000.usb/mode | 切换成功 |
| 强制 Device | echo device > /sys/kernel/debug/usb/23000000.usb/mode | 切换成功 |
| 切换 OTG | echo otg > /sys/kernel/debug/usb/23000000.usb/mode | 切换成功 |
| Gadget 配置 | ls /sys/kernel/config/usb_gadget/rockchip/ | UDC/configs/functions |
| XHCI 入口 | ls /sys/kernel/debug/usb/xhci/ | xhci-hcd.0.auto |
| USB 设备树 | cat /sys/kernel/debug/usb/devices | T: Bus=01 Lev=00... |
| TCPM 日志 | cat /sys/kernel/debug/usb/tcpm-2-004e/log | tail | state change ... |
| DWC3 trace | echo 1 > /sys/kernel/debug/tracing/events/dwc3/enable | 开启 trace |
| Gadget trace | echo 1 > /sys/kernel/debug/tracing/events/gadget/enable | 开启 trace |
| XHCI trace | echo 1 > /sys/kernel/debug/tracing/events/xhci-hcd/enable | 开启 trace |
DWC3 调试的精髓就一句话:先看 mode,再看 link_state,最后看 trace。三步走完,问题基本能定位到寄存器级别。
审核编辑 黄宇
全部0条评论
快来发表一下你的评论吧 !