[PATCH v18 7/7] usb: dwc3: rockchip: fix USB-C reconnect in gadget mode
Sebastian Reichel
sebastian.reichel at collabora.com
Wed Oct 21 11:05:32 PDT 2026
When USB-C is configured in gadget mode and the cable is unplugged
the USB controller is suspended. After plugging in the cable again,
the USB controller stays suspended and thus the port status remains
not-attached.
Fix this by informing the USB core about VBUS updates when the PHY
mode changes.
Signed-off-by: Sebastian Reichel <sebastian.reichel at collabora.com>
---
drivers/usb/dwc3/Kconfig | 1 +
drivers/usb/dwc3/dwc3-rockchip.c | 27 +++++++++++++++++++++++++++
2 files changed, 28 insertions(+)
diff --git a/drivers/usb/dwc3/Kconfig b/drivers/usb/dwc3/Kconfig
index c86c1ac293bf..4cbc949c6abd 100644
--- a/drivers/usb/dwc3/Kconfig
+++ b/drivers/usb/dwc3/Kconfig
@@ -194,6 +194,7 @@ config USB_DWC3_ROCKCHIP
tristate "Rockchip DWC3 Platform Driver"
depends on ARCH_ROCKCHIP || COMPILE_TEST
depends on PHY_ROCKCHIP_USBDP || !PHY_ROCKCHIP_USBDP
+ depends on USB_GADGET || !USB_GADGET
depends on OF
default USB_DWC3
help
diff --git a/drivers/usb/dwc3/dwc3-rockchip.c b/drivers/usb/dwc3/dwc3-rockchip.c
index 75e647e04cc5..65d17536af14 100644
--- a/drivers/usb/dwc3/dwc3-rockchip.c
+++ b/drivers/usb/dwc3/dwc3-rockchip.c
@@ -10,8 +10,19 @@
struct dwc3_rockchip {
struct dwc3 dwc;
u8 phy_reset_active;
+ enum usb_role role;
};
+static void dwc3_rockchip_vbus_handler(struct dwc3 *dwc, bool present)
+{
+ guard(spinlock_irqsave)(&dwc->lock);
+
+ if (!dwc->gadget || !dwc->gadget_driver)
+ return;
+
+ usb_udc_vbus_handler(dwc->gadget, present);
+}
+
static void dwc3_rockchip_phy_pre_reset(struct dwc3_rockchip *dwc_rk, int port)
{
struct dwc3 *dwc = &dwc_rk->dwc;
@@ -26,6 +37,8 @@ static void dwc3_rockchip_phy_pre_reset(struct dwc3_rockchip *dwc_rk, int port)
if (!pm_runtime_active(dwc->dev))
return;
+ dwc3_rockchip_vbus_handler(dwc, false);
+
/*
* Assert USB3 PHY soft reset within DWC3 before the external
* PHY resets. This disconnects the PIPE interface, preventing
@@ -43,6 +56,9 @@ static void dwc3_rockchip_phy_pre_reset(struct dwc3_rockchip *dwc_rk, int port)
static void dwc3_rockchip_phy_post_reset(struct dwc3_rockchip *dwc_rk, int port)
{
struct dwc3 *dwc = &dwc_rk->dwc;
+ bool gadget_mode = (dwc->current_dr_role == DWC3_GCTL_PRTCAP_DEVICE) ||
+ ((dwc->current_dr_role == DWC3_GCTL_PRTCAP_OTG) &&
+ (dwc_rk->role == USB_ROLE_DEVICE));
u32 reg;
scoped_guard(spinlock_irqsave, &dwc->lock) {
@@ -56,6 +72,8 @@ static void dwc3_rockchip_phy_post_reset(struct dwc3_rockchip *dwc_rk, int port)
reg &= ~DWC3_GUSB3PIPECTL_PHYSOFTRST;
dwc3_writel(dwc, DWC3_GUSB3PIPECTL(port), reg);
}
+
+ dwc3_rockchip_vbus_handler(dwc, gadget_mode);
}
static void dwc3_rockchip_phy_reset(void *data, int port, bool pre)
@@ -99,7 +117,16 @@ static int dwc3_rk_phy_register_reset_handler(struct dwc3 *dwc)
return devm_add_action_or_reset(dwc->dev, dwc3_rk_phy_unregister_notifiers, dwc_rk);
}
+static void dwc3_rockchip_set_role(struct dwc3 *dwc, enum usb_role role)
+{
+ struct dwc3_rockchip *dwc_rk = container_of(dwc, struct dwc3_rockchip, dwc);
+
+ dwc_rk->role = role;
+ dwc3_rockchip_vbus_handler(dwc, role == USB_ROLE_DEVICE);
+}
+
static struct dwc3_glue_ops dwc3_rockchip_glue_ops = {
+ .pre_set_role = dwc3_rockchip_set_role,
.post_phy_registration = dwc3_rk_phy_register_reset_handler,
};
--
2.53.0
More information about the Linux-rockchip
mailing list