[PATCH v17 2/7] phy: rockchip: usbdp: Add reset hook support for dwc3

From: Sebastian Reichel

Date: Fri Oct 16 2026 - 14:44:30 EST


When TCPM (Type-C Port Manager) triggers a orientation or mux
change, the PHY needs to reset. This results in the PHY no longer
providing a clock to the USB controller. If the USB controller
registers are accessed without the clock, SErrors are generated.

This can be avoided by switching the clock provider for the USB
controller. But after switching the clock, the USB PHY sometimes does
not succeed to lock its "cmn ana lcpll" PLLs again. I suspect this is
due to a clock glitch and after some debugging figured out that this
can be avoided by disabling the USB controller's PIPE interface while
the PHY reset is ongoing.

Implement an API that allows the USBDP to actively request a reset
from DWC3 as a quirk.

Signed-off-by: Sebastian Reichel <sebastian.reichel@xxxxxxxxxxxxx>
---
drivers/phy/rockchip/phy-rockchip-usbdp.c | 57 +++++++++++++++++++++++++++++--
include/linux/phy/rockchip-usbdp.h | 16 +++++++++
2 files changed, 71 insertions(+), 2 deletions(-)

diff --git a/drivers/phy/rockchip/phy-rockchip-usbdp.c b/drivers/phy/rockchip/phy-rockchip-usbdp.c
index db69dd24f73b..b396f286e785 100644
--- a/drivers/phy/rockchip/phy-rockchip-usbdp.c
+++ b/drivers/phy/rockchip/phy-rockchip-usbdp.c
@@ -19,6 +19,7 @@
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/phy/phy.h>
+#include <linux/phy/rockchip-usbdp.h>
#include <linux/platform_device.h>
#include <linux/property.h>
#include <linux/regmap.h>
@@ -179,6 +180,9 @@ struct rk_udphy {

/* utilized for USB */
bool hs; /* flag for high-speed */
+ int (*dwc3_reset_handler)(void *data, int port, bool pre);
+ void *dwc3_reset_data;
+ int dwc3_reset_port;

/* utilized for DP */
struct gpio_desc *sbu1_dc_gpio;
@@ -995,6 +999,43 @@ static int rk_udphy_parse_dt(struct rk_udphy *udphy)
return rk_udphy_reset_init(udphy, dev);
}

+static int rk_udphy_run_reset_handler(struct rk_udphy *udphy, bool pre)
+{
+ if (!udphy->dwc3_reset_handler)
+ return 0;
+
+ if (!(udphy->sw_mode & UDPHY_MODE_USB))
+ return 0;
+
+ return udphy->dwc3_reset_handler(udphy->dwc3_reset_data, udphy->dwc3_reset_port, pre);
+}
+
+void rk_udphy_unregister_reset_handler(struct phy *phy)
+{
+ struct rk_udphy *udphy = phy_get_drvdata(phy);
+
+ guard(mutex)(&udphy->mutex);
+
+ udphy->dwc3_reset_handler = NULL;
+ udphy->dwc3_reset_data = NULL;
+ udphy->dwc3_reset_port = 0;
+}
+EXPORT_SYMBOL(rk_udphy_unregister_reset_handler);
+
+void rk_udphy_register_reset_handler(struct phy *phy,
+ int (*reset_handler)(void *data, int port, bool pre),
+ void *data, int port)
+{
+ struct rk_udphy *udphy = phy_get_drvdata(phy);
+
+ guard(mutex)(&udphy->mutex);
+
+ udphy->dwc3_reset_handler = reset_handler;
+ udphy->dwc3_reset_data = data;
+ udphy->dwc3_reset_port = port;
+}
+EXPORT_SYMBOL(rk_udphy_register_reset_handler);
+
static int rk_udphy_power_on(struct rk_udphy *udphy, u8 mode)
{
int ret;
@@ -1005,24 +1046,36 @@ static int rk_udphy_power_on(struct rk_udphy *udphy, u8 mode)
}

if (udphy->status == UDPHY_MODE_NONE) {
+ rk_udphy_run_reset_handler(udphy, true);
+
ret = rk_udphy_setup(udphy);
- if (ret)
+ if (ret) {
+ rk_udphy_run_reset_handler(udphy, false);
return ret;
+ }

if (!udphy->hs && udphy->mode & UDPHY_MODE_USB)
rk_udphy_u3_port_disable(udphy, false);
udphy->phy_needs_reinit = false;
+
+ rk_udphy_run_reset_handler(udphy, false);
} else if (udphy->phy_needs_reinit) {
+ rk_udphy_run_reset_handler(udphy, true);
+
rk_udphy_u3_port_disable(udphy, true);
udelay(10);

ret = rk_udphy_init(udphy);
- if (ret)
+ if (ret) {
+ rk_udphy_run_reset_handler(udphy, false);
return ret;
+ }

if (!udphy->hs && udphy->mode & UDPHY_MODE_USB)
rk_udphy_u3_port_disable(udphy, false);
udphy->phy_needs_reinit = false;
+
+ rk_udphy_run_reset_handler(udphy, false);
}

udphy->status |= mode;
diff --git a/include/linux/phy/rockchip-usbdp.h b/include/linux/phy/rockchip-usbdp.h
new file mode 100644
index 000000000000..6923ac28303b
--- /dev/null
+++ b/include/linux/phy/rockchip-usbdp.h
@@ -0,0 +1,16 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Rockchip USBDP Combo PHY with Samsung IP block driver
+ *
+ * Copyright (C) 2026 Collabora Ltd
+ */
+
+#ifndef __ROCKCHIP_USBDP
+#define __ROCKCHIP_USBDP
+
+void rk_udphy_unregister_reset_handler(struct phy *phy);
+void rk_udphy_register_reset_handler(struct phy *phy,
+ int (*reset_handler)(void *data, int port, bool pre),
+ void *data, int port);
+
+#endif

--
2.53.0