USB 枚举到一半断了?深入 DWC3 控制器的调试世界

电子说

1.4w人已加入

描述

前置知识:建议先读第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。三步走完,问题基本能定位到寄存器级别。

审核编辑 黄宇

打开APP阅读更多精彩内容
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

全部0条评论

快来发表一下你的评论吧 !

×
20
完善资料,
赚取积分