* [PATCH v16 1/6] phy: core: add notifier infrastructure
2026-09-24 17:25 [PATCH v16 0/6] usb: dwc3: introduce Rockchip glue driver Sebastian Reichel
@ 2026-09-24 17:25 ` Sebastian Reichel
2026-09-26 7:47 ` Manivannan Sadhasivam
2026-09-24 17:25 ` [PATCH v16 2/6] usb: dwc3: rockchip: introduce glue driver Sebastian Reichel
` (5 subsequent siblings)
6 siblings, 1 reply; 11+ messages in thread
From: Sebastian Reichel @ 2026-09-24 17:25 UTC (permalink / raw)
To: Thinh Nguyen, Greg Kroah-Hartman, Heiko Stuebner, Vinod Koul,
Neil Armstrong, Manivannan Sadhasivam
Cc: Igor Paunovic, linux-kernel, linux-usb, linux-arm-kernel,
linux-rockchip, linux-phy, kernel, Sebastian Reichel
Some PHY devices with multiple ports (e.g. USB3 and DP) require a reset
if the configuration changes or cable orientation changes. This is a
problem, as the consumer device will run into undefined behavior.
With the new PHY notifier API introduced in this patch, the consumer
driver can hook into reset events coming from a PHY device to handle the
PHY going down gracefully.
Note that this uses -ENOSYS instead of the more sensible -ENOTSUP for
the stub functions when GENERIC_PHY is disabled to stay consistent with
the existing ones.
Tested-by: Igor Paunovic <royalnet026@gmail.com> # Orange Pi 5 Plus
Signed-off-by: Sebastian Reichel <sebastian.reichel@collabora.com>
---
drivers/phy/phy-core.c | 65 +++++++++++++++++++++++++++++++++++++++++++++++++
include/linux/phy/phy.h | 40 ++++++++++++++++++++++++++++++
2 files changed, 105 insertions(+)
diff --git a/drivers/phy/phy-core.c b/drivers/phy/phy-core.c
index 21aaf2f76e53..51d261daae7a 100644
--- a/drivers/phy/phy-core.c
+++ b/drivers/phy/phy-core.c
@@ -542,6 +542,70 @@ int phy_notify_state(struct phy *phy, union phy_notify state)
}
EXPORT_SYMBOL_GPL(phy_notify_state);
+/**
+ * phy_register_notifier() - register a notifier for PHY events
+ * @phy: the phy returned by phy_get()
+ * @nb: notifier block to register
+ *
+ * Allows PHY consumers to receive notifications about PHY reset events.
+ * PHY providers can signal these events using phy_notify_reset().
+ *
+ * Returns: %0 if successful, a negative error code otherwise
+ */
+int phy_register_notifier(struct phy *phy, struct notifier_block *nb)
+{
+ if (!phy)
+ return 0;
+
+ return blocking_notifier_chain_register(&phy->notifier, nb);
+}
+EXPORT_SYMBOL_GPL(phy_register_notifier);
+
+/**
+ * phy_unregister_notifier() - unregister a notifier for PHY events
+ * @phy: the phy returned by phy_get()
+ * @nb: notifier block to unregister
+ *
+ * Returns: %0 if successful, a negative error code otherwise
+ */
+int phy_unregister_notifier(struct phy *phy, struct notifier_block *nb)
+{
+ if (!phy)
+ return 0;
+
+ return blocking_notifier_chain_unregister(&phy->notifier, nb);
+}
+EXPORT_SYMBOL_GPL(phy_unregister_notifier);
+
+/**
+ * phy_notify_reset() - notify consumers of a PHY reset event
+ * @phy: the phy that is being reset
+ * @event: the notification event (PRE_RESET or POST_RESET)
+ *
+ * Called by PHY providers to notify consumers that the PHY is about to
+ * be reset or has completed a reset. This allows consumers to quiesce
+ * hardware before the PHY becomes unavailable.
+ *
+ * This may be called from within PHY provider callbacks (e.g. set_mode,
+ * power_on) where phy->mutex is held. Consumer notification handlers must
+ * therefore NOT call back into the PHY framework (e.g. phy_power_off,
+ * phy_exit) on the same PHY, as this would result in a deadlock.
+ *
+ * Returns: %0 if successful or no notifiers registered, a negative error
+ * code if a notifier returns an error (for PRE_RESET only)
+ */
+int phy_notify_reset(struct phy *phy, enum phy_notification event)
+{
+ int ret;
+
+ if (!phy)
+ return 0;
+
+ ret = blocking_notifier_call_chain(&phy->notifier, event, phy);
+ return notifier_to_errno(ret);
+}
+EXPORT_SYMBOL_GPL(phy_notify_reset);
+
/**
* phy_configure() - Changes the phy parameters
* @phy: the phy returned by phy_get()
@@ -1018,6 +1082,7 @@ struct phy *phy_create(struct device *dev, struct device_node *node,
device_initialize(&phy->dev);
lockdep_register_key(&phy->lockdep_key);
mutex_init_with_key(&phy->mutex, &phy->lockdep_key);
+ BLOCKING_INIT_NOTIFIER_HEAD(&phy->notifier);
phy->dev.class = &phy_class;
phy->dev.parent = dev;
diff --git a/include/linux/phy/phy.h b/include/linux/phy/phy.h
index ea47975e288a..3779a4d0a02c 100644
--- a/include/linux/phy/phy.h
+++ b/include/linux/phy/phy.h
@@ -11,6 +11,7 @@
#define __DRIVERS_PHY_H
#include <linux/err.h>
+#include <linux/notifier.h>
#include <linux/of.h>
#include <linux/device.h>
#include <linux/pm_runtime.h>
@@ -53,6 +54,16 @@ enum phy_media {
PHY_MEDIA_DAC,
};
+/**
+ * enum phy_notification - PHY notification events
+ * @PHY_NOTIFY_PRE_RESET: PHY is about to be reset, consumers should quiesce
+ * @PHY_NOTIFY_POST_RESET: PHY reset is complete, consumers may resume
+ */
+enum phy_notification {
+ PHY_NOTIFY_PRE_RESET,
+ PHY_NOTIFY_POST_RESET,
+};
+
enum phy_ufs_state {
PHY_UFS_HIBERN8_ENTER,
PHY_UFS_HIBERN8_EXIT,
@@ -170,6 +181,7 @@ struct phy_attrs {
* @power_count: used to protect when the PHY is used by multiple consumers
* @attrs: used to specify PHY specific attributes
* @pwr: power regulator associated with the phy
+ * @notifier: notifier head for PHY reset events
* @debugfs: debugfs directory
*/
struct phy {
@@ -182,6 +194,7 @@ struct phy {
int power_count;
struct phy_attrs attrs;
struct regulator *pwr;
+ struct blocking_notifier_head notifier;
struct dentry *debugfs;
};
@@ -267,6 +280,9 @@ int phy_calibrate(struct phy *phy);
int phy_notify_connect(struct phy *phy, int port);
int phy_notify_disconnect(struct phy *phy, int port);
int phy_notify_state(struct phy *phy, union phy_notify state);
+int phy_register_notifier(struct phy *phy, struct notifier_block *nb);
+int phy_unregister_notifier(struct phy *phy, struct notifier_block *nb);
+int phy_notify_reset(struct phy *phy, enum phy_notification event);
static inline int phy_get_bus_width(struct phy *phy)
{
return phy->attrs.bus_width;
@@ -428,6 +444,30 @@ static inline int phy_notify_state(struct phy *phy, union phy_notify state)
return -ENOSYS;
}
+static inline int phy_register_notifier(struct phy *phy,
+ struct notifier_block *nb)
+{
+ if (!phy)
+ return 0;
+ return -ENOSYS;
+}
+
+static inline int phy_unregister_notifier(struct phy *phy,
+ struct notifier_block *nb)
+{
+ if (!phy)
+ return 0;
+ return -ENOSYS;
+}
+
+static inline int phy_notify_reset(struct phy *phy,
+ enum phy_notification event)
+{
+ if (!phy)
+ return 0;
+ return -ENOSYS;
+}
+
static inline int phy_configure(struct phy *phy,
union phy_configure_opts *opts)
{
--
2.53.0
^ permalink raw reply [flat|nested] 11+ messages in thread* Re: [PATCH v16 1/6] phy: core: add notifier infrastructure
2026-09-24 17:25 ` [PATCH v16 1/6] phy: core: add notifier infrastructure Sebastian Reichel
@ 2026-09-26 7:47 ` Manivannan Sadhasivam
0 siblings, 0 replies; 11+ messages in thread
From: Manivannan Sadhasivam @ 2026-09-26 7:47 UTC (permalink / raw)
To: Sebastian Reichel
Cc: Thinh Nguyen, Greg Kroah-Hartman, Heiko Stuebner, Vinod Koul,
Neil Armstrong, Igor Paunovic, linux-kernel, linux-usb,
linux-arm-kernel, linux-rockchip, linux-phy, kernel
On Thu, Sep 24, 2026 at 07:25:37PM +0200, Sebastian Reichel wrote:
> Some PHY devices with multiple ports (e.g. USB3 and DP) require a reset
> if the configuration changes or cable orientation changes. This is a
> problem, as the consumer device will run into undefined behavior.
>
> With the new PHY notifier API introduced in this patch, the consumer
> driver can hook into reset events coming from a PHY device to handle the
> PHY going down gracefully.
>
> Note that this uses -ENOSYS instead of the more sensible -ENOTSUP for
> the stub functions when GENERIC_PHY is disabled to stay consistent with
> the existing ones.
>
> Tested-by: Igor Paunovic <royalnet026@gmail.com> # Orange Pi 5 Plus
> Signed-off-by: Sebastian Reichel <sebastian.reichel@collabora.com>
Reviewed-by: Manivannan Sadhasivam <manivannan.sadhasivam@oss.qualcomm.com>
- Mani
--
மணிவண்ணன் சதாசிவம்
^ permalink raw reply [flat|nested] 11+ messages in thread
* [PATCH v16 2/6] usb: dwc3: rockchip: introduce glue driver
2026-09-24 17:25 [PATCH v16 0/6] usb: dwc3: introduce Rockchip glue driver Sebastian Reichel
2026-09-24 17:25 ` [PATCH v16 1/6] phy: core: add notifier infrastructure Sebastian Reichel
@ 2026-09-24 17:25 ` Sebastian Reichel
2026-09-24 17:25 ` [PATCH v16 3/6] usb: dwc3: core: add post PHY registration hook for platform glue Sebastian Reichel
` (4 subsequent siblings)
6 siblings, 0 replies; 11+ messages in thread
From: Sebastian Reichel @ 2026-09-24 17:25 UTC (permalink / raw)
To: Thinh Nguyen, Greg Kroah-Hartman, Heiko Stuebner, Vinod Koul,
Neil Armstrong, Manivannan Sadhasivam
Cc: Igor Paunovic, linux-kernel, linux-usb, linux-arm-kernel,
linux-rockchip, linux-phy, kernel, Sebastian Reichel
Introduce Rockchip specific glue code for the Synopsys DWC3 USB driver.
For now this handles things identical to the default glue.
Tested-by: Igor Paunovic <royalnet026@gmail.com> # Orange Pi 5 Plus
Signed-off-by: Sebastian Reichel <sebastian.reichel@collabora.com>
---
drivers/usb/dwc3/Kconfig | 11 ++++
drivers/usb/dwc3/Makefile | 1 +
drivers/usb/dwc3/core.c | 15 ++++++
drivers/usb/dwc3/dwc3-rockchip.c | 106 +++++++++++++++++++++++++++++++++++++++
4 files changed, 133 insertions(+)
diff --git a/drivers/usb/dwc3/Kconfig b/drivers/usb/dwc3/Kconfig
index 18169727a413..3c120ea9746d 100644
--- a/drivers/usb/dwc3/Kconfig
+++ b/drivers/usb/dwc3/Kconfig
@@ -190,6 +190,17 @@ config USB_DWC3_OCTEON
Only the host mode is currently supported.
Say 'Y' or 'M' here if you have one such device.
+config USB_DWC3_ROCKCHIP
+ tristate "Rockchip DWC3 Platform Driver"
+ depends on ARCH_ROCKCHIP || COMPILE_TEST
+ depends on OF
+ default USB_DWC3
+ help
+ Rockchip SoCs with DesignWare Core USB3 IP inside,
+ and IP Core configured for USB 2.0 and USB 3.0 in host
+ or dual-role mode.
+ Say 'Y' or 'M' if you have such device.
+
config USB_DWC3_RTK
tristate "Realtek DWC3 Platform Driver"
depends on OF && ARCH_REALTEK
diff --git a/drivers/usb/dwc3/Makefile b/drivers/usb/dwc3/Makefile
index f37971197203..444e7e7f34b2 100644
--- a/drivers/usb/dwc3/Makefile
+++ b/drivers/usb/dwc3/Makefile
@@ -58,6 +58,7 @@ obj-$(CONFIG_USB_DWC3_IMX8MP) += dwc3-imx8mp.o
obj-$(CONFIG_USB_DWC3_IMX) += dwc3-imx.o
obj-$(CONFIG_USB_DWC3_XILINX) += dwc3-xilinx.o
obj-$(CONFIG_USB_DWC3_OCTEON) += dwc3-octeon.o
+obj-$(CONFIG_USB_DWC3_ROCKCHIP) += dwc3-rockchip.o
obj-$(CONFIG_USB_DWC3_RTK) += dwc3-rtk.o
obj-$(CONFIG_USB_DWC3_GENERIC_PLAT) += dwc3-generic-plat.o
obj-$(CONFIG_USB_DWC3_GOOGLE) += dwc3-google.o
diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c
index fd5c2cd36c59..4b7132887a03 100644
--- a/drivers/usb/dwc3/core.c
+++ b/drivers/usb/dwc3/core.c
@@ -2448,11 +2448,26 @@ int dwc3_core_probe(const struct dwc3_probe_data *data)
}
EXPORT_SYMBOL_GPL(dwc3_core_probe);
+/*
+ * List of compatibles, which have "synopsys,dwc3" as a fallback
+ * compatible, but have a vendor specific glue driver that should
+ * be used instead of this one.
+ */
+static const char *const dwc3_compatible_blocklist[] = {
+ "rockchip,rk3588-dwc3",
+ "rockchip,rk3576-dwc3",
+};
+
static int dwc3_probe(struct platform_device *pdev)
{
struct dwc3_probe_data probe_data = {};
struct resource *res;
struct dwc3 *dwc;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(dwc3_compatible_blocklist); i++)
+ if (device_is_compatible(&pdev->dev, dwc3_compatible_blocklist[i]))
+ return -ENODEV;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res) {
diff --git a/drivers/usb/dwc3/dwc3-rockchip.c b/drivers/usb/dwc3/dwc3-rockchip.c
new file mode 100644
index 000000000000..4129aefd6a8b
--- /dev/null
+++ b/drivers/usb/dwc3/dwc3-rockchip.c
@@ -0,0 +1,106 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright (c) 2026, Collabora Ltd. */
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include "glue.h"
+
+struct dwc3_rockchip {
+ struct dwc3 dwc;
+};
+
+static int dwc3_rockchip_probe(struct platform_device *pdev)
+{
+ struct dwc3_probe_data probe_data = {};
+ struct resource *res;
+ struct dwc3_rockchip *dwc_rk;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ dev_err(&pdev->dev, "missing memory resource\n");
+ return -ENODEV;
+ }
+
+ dwc_rk = devm_kzalloc(&pdev->dev, sizeof(*dwc_rk), GFP_KERNEL);
+ if (!dwc_rk)
+ return -ENOMEM;
+
+ dwc_rk->dwc.dev = &pdev->dev;
+ dwc_rk->dwc.glue_ops = NULL;
+
+ probe_data.dwc = &dwc_rk->dwc;
+ probe_data.res = res;
+ probe_data.properties = DWC3_DEFAULT_PROPERTIES;
+
+ return dwc3_core_probe(&probe_data);
+}
+
+static void dwc3_rockchip_remove(struct platform_device *pdev)
+{
+ dwc3_core_remove(platform_get_drvdata(pdev));
+}
+
+static int dwc3_rockchip_runtime_suspend(struct device *dev)
+{
+ return dwc3_runtime_suspend(dev_get_drvdata(dev));
+}
+
+static int dwc3_rockchip_runtime_resume(struct device *dev)
+{
+ return dwc3_runtime_resume(dev_get_drvdata(dev));
+}
+
+static int dwc3_rockchip_runtime_idle(struct device *dev)
+{
+ return dwc3_runtime_idle(dev_get_drvdata(dev));
+}
+
+static int dwc3_rockchip_suspend(struct device *dev)
+{
+ return dwc3_pm_suspend(dev_get_drvdata(dev));
+}
+
+static int dwc3_rockchip_resume(struct device *dev)
+{
+ return dwc3_pm_resume(dev_get_drvdata(dev));
+}
+
+static void dwc3_rockchip_complete(struct device *dev)
+{
+ dwc3_pm_complete(dev_get_drvdata(dev));
+}
+
+static int dwc3_rockchip_prepare(struct device *dev)
+{
+ return dwc3_pm_prepare(dev_get_drvdata(dev));
+}
+
+static const struct dev_pm_ops dwc3_rockchip_dev_pm_ops = {
+ SYSTEM_SLEEP_PM_OPS(dwc3_rockchip_suspend, dwc3_rockchip_resume)
+ RUNTIME_PM_OPS(dwc3_rockchip_runtime_suspend, dwc3_rockchip_runtime_resume,
+ dwc3_rockchip_runtime_idle)
+ .complete = pm_sleep_ptr(dwc3_rockchip_complete),
+ .prepare = pm_sleep_ptr(dwc3_rockchip_prepare),
+};
+
+static const struct of_device_id dwc3_rockchip_of_match[] = {
+ { .compatible = "rockchip,rk3588-dwc3" },
+ { .compatible = "rockchip,rk3576-dwc3" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, dwc3_rockchip_of_match);
+
+static struct platform_driver dwc3_rockchip_driver = {
+ .probe = dwc3_rockchip_probe,
+ .remove = dwc3_rockchip_remove,
+ .driver = {
+ .name = "dwc3-rockchip",
+ .pm = pm_ptr(&dwc3_rockchip_dev_pm_ops),
+ .of_match_table = dwc3_rockchip_of_match,
+ },
+};
+
+module_platform_driver(dwc3_rockchip_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("DesignWare DWC3 Rockchip Glue Driver");
--
2.53.0
^ permalink raw reply [flat|nested] 11+ messages in thread* [PATCH v16 3/6] usb: dwc3: core: add post PHY registration hook for platform glue
2026-09-24 17:25 [PATCH v16 0/6] usb: dwc3: introduce Rockchip glue driver Sebastian Reichel
2026-09-24 17:25 ` [PATCH v16 1/6] phy: core: add notifier infrastructure Sebastian Reichel
2026-09-24 17:25 ` [PATCH v16 2/6] usb: dwc3: rockchip: introduce glue driver Sebastian Reichel
@ 2026-09-24 17:25 ` Sebastian Reichel
2026-09-24 17:25 ` [PATCH v16 4/6] usb: dwc3: rockchip: support PHY reset notifications Sebastian Reichel
` (3 subsequent siblings)
6 siblings, 0 replies; 11+ messages in thread
From: Sebastian Reichel @ 2026-09-24 17:25 UTC (permalink / raw)
To: Thinh Nguyen, Greg Kroah-Hartman, Heiko Stuebner, Vinod Koul,
Neil Armstrong, Manivannan Sadhasivam
Cc: Igor Paunovic, linux-kernel, linux-usb, linux-arm-kernel,
linux-rockchip, linux-phy, kernel, Sebastian Reichel,
Krishna Kurapati
Add support for custom PHY handling steps in platform glue code
by adding a post registration hook. This will be used for handling
PHY reset notifications on the Rockchip platform.
Tested-by: Igor Paunovic <royalnet026@gmail.com> # Orange Pi 5 Plus
Acked-by: Thinh Nguyen <Thinh.Nguyen@synopsys.com>
Reviewed-by: Krishna Kurapati <krishna.kurapati@oss.qualcomm.com>
Signed-off-by: Sebastian Reichel <sebastian.reichel@collabora.com>
---
drivers/usb/dwc3/core.c | 2 +-
drivers/usb/dwc3/core.h | 9 +++++++++
2 files changed, 10 insertions(+), 1 deletion(-)
diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c
index 4b7132887a03..daf5aec07fbf 100644
--- a/drivers/usb/dwc3/core.c
+++ b/drivers/usb/dwc3/core.c
@@ -1613,7 +1613,7 @@ static int dwc3_core_get_phy(struct dwc3 *dwc)
}
}
- return 0;
+ return dwc3_post_phy_registration(dwc);
}
static int dwc3_core_init_mode(struct dwc3 *dwc)
diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h
index 608daeb7ef10..e9d8df0a298e 100644
--- a/drivers/usb/dwc3/core.h
+++ b/drivers/usb/dwc3/core.h
@@ -995,10 +995,12 @@ struct dwc3_scratchpad_array {
* need to be passed on to glue layer
* @pre_set_role: Notify glue of role switch notifications
* @pre_run_stop: Notify run stop enable/disable information to glue
+ * @post_phy_registration: Called directly after PHY got registered
*/
struct dwc3_glue_ops {
void (*pre_set_role)(struct dwc3 *dwc, enum usb_role role);
void (*pre_run_stop)(struct dwc3 *dwc, bool is_on);
+ int (*post_phy_registration)(struct dwc3 *dwc);
};
/**
@@ -1655,6 +1657,13 @@ static inline void dwc3_pre_run_stop(struct dwc3 *dwc, bool is_on)
dwc->glue_ops->pre_run_stop(dwc, is_on);
}
+static inline int dwc3_post_phy_registration(struct dwc3 *dwc)
+{
+ if (dwc->glue_ops && dwc->glue_ops->post_phy_registration)
+ return dwc->glue_ops->post_phy_registration(dwc);
+ return 0;
+}
+
#if IS_ENABLED(CONFIG_USB_DWC3_HOST) || IS_ENABLED(CONFIG_USB_DWC3_DUAL_ROLE)
int dwc3_host_init(struct dwc3 *dwc);
void dwc3_host_exit(struct dwc3 *dwc);
--
2.53.0
^ permalink raw reply [flat|nested] 11+ messages in thread* [PATCH v16 4/6] usb: dwc3: rockchip: support PHY reset notifications
2026-09-24 17:25 [PATCH v16 0/6] usb: dwc3: introduce Rockchip glue driver Sebastian Reichel
` (2 preceding siblings ...)
2026-09-24 17:25 ` [PATCH v16 3/6] usb: dwc3: core: add post PHY registration hook for platform glue Sebastian Reichel
@ 2026-09-24 17:25 ` Sebastian Reichel
2026-09-26 7:51 ` Manivannan Sadhasivam
2026-09-24 17:25 ` [PATCH v16 5/6] usb: gadget: define stub for usb_udc_vbus_handler Sebastian Reichel
` (2 subsequent siblings)
6 siblings, 1 reply; 11+ messages in thread
From: Sebastian Reichel @ 2026-09-24 17:25 UTC (permalink / raw)
To: Thinh Nguyen, Greg Kroah-Hartman, Heiko Stuebner, Vinod Koul,
Neil Armstrong, Manivannan Sadhasivam
Cc: Igor Paunovic, linux-kernel, linux-usb, linux-arm-kernel,
linux-rockchip, linux-phy, kernel, Sebastian Reichel
On recent Rockchip platforms (at least RK3588 & RK3576), DWC3 IP is used
with a USBDP PHY providing USB3 and DP. This PHY needs to be reset when
the mode changes, which may happen when plugging in different USB-C
devices.
If the USBDP PHY resets with the DWC3 IP running, its internal state
corrupts resulting in the USBDP PHY not being able to lock some PLL
clocks, which effectively renders USB3 unusable.
To fix the issue this adds handling for the new PHY framework reset
notifications, which will assert PHYSOFTRST before the actual PHY
is disabled and will deassert it once the PHY returns.
Tested-by: Igor Paunovic <royalnet026@gmail.com> # Orange Pi 5 Plus
Acked-by: Thinh Nguyen <Thinh.Nguyen@synopsys.com>
Signed-off-by: Sebastian Reichel <sebastian.reichel@collabora.com>
---
drivers/usb/dwc3/dwc3-rockchip.c | 127 ++++++++++++++++++++++++++++++++++++++-
drivers/usb/dwc3/trace.c | 3 +
2 files changed, 129 insertions(+), 1 deletion(-)
diff --git a/drivers/usb/dwc3/dwc3-rockchip.c b/drivers/usb/dwc3/dwc3-rockchip.c
index 4129aefd6a8b..17b6c2ca9e19 100644
--- a/drivers/usb/dwc3/dwc3-rockchip.c
+++ b/drivers/usb/dwc3/dwc3-rockchip.c
@@ -2,11 +2,136 @@
/* Copyright (c) 2026, Collabora Ltd. */
#include <linux/module.h>
#include <linux/platform_device.h>
+#include <linux/phy/phy.h>
#include <linux/pm_runtime.h>
#include "glue.h"
+#include "io.h"
+
+struct dwc3_rockchip;
+
+/**
+ * struct dwc3_rk_phy_nb - wrapper for PHY notifier block
+ * @nb: notifier block
+ * @dwc: back-pointer to the DWC3 controller
+ * @port_index: USB3 port index this notifier is registered for
+ */
+struct dwc3_rk_phy_nb {
+ struct notifier_block nb;
+ struct dwc3_rockchip *dwc_rk;
+ u8 port_index;
+};
struct dwc3_rockchip {
struct dwc3 dwc;
+ struct dwc3_rk_phy_nb usb3_phy_nb[DWC3_USB3_MAX_PORTS];
+ u8 phy_reset_active;
+};
+
+static int dwc3_usb3_phy_notify(struct notifier_block *nb,
+ unsigned long action, void *data)
+{
+ struct dwc3_rk_phy_nb *pnb = container_of(nb, struct dwc3_rk_phy_nb, nb);
+ struct dwc3_rockchip *dwc_rk = pnb->dwc_rk;
+ struct dwc3 *dwc = &dwc_rk->dwc;
+ int port = pnb->port_index;
+ unsigned long flags;
+ u32 reg;
+ int ret;
+
+ switch (action) {
+ case PHY_NOTIFY_PRE_RESET:
+ /*
+ * If already suspended, the resume path will reinit GUSB3PIPECTL
+ * via dwc3_core_init(). A forced resume is not possible as that
+ * would call phy_init() resulting in a deadlock. Due to the
+ * phy_init() in the resume path there is also no need to block
+ * async RPM resume on our side, since the PHY synchronizes it
+ * for us.
+ *
+ * pm_runtime_get_if_active() returns 0 when suspended (skip),
+ * 1 when active (ref held), or -EINVAL when PM is disabled
+ * (device always active). In the -EINVAL case PM ref counting
+ * is a no-op, so the unconditional put in POST_RESET is safe.
+ */
+ ret = pm_runtime_get_if_active(dwc->dev);
+ if (!ret)
+ return NOTIFY_OK;
+
+ /*
+ * Assert USB3 PHY soft reset within DWC3 before the external
+ * PHY resets. This disconnects the PIPE interface, preventing
+ * the DWC3 from interfering with PHY reinitialization and
+ * avoiding LCPLL lock failures.
+ */
+ spin_lock_irqsave(&dwc->lock, flags);
+ dwc_rk->phy_reset_active |= BIT(port);
+ reg = dwc3_readl(dwc, DWC3_GUSB3PIPECTL(port));
+ reg |= DWC3_GUSB3PIPECTL_PHYSOFTRST;
+ dwc3_writel(dwc, DWC3_GUSB3PIPECTL(port), reg);
+ spin_unlock_irqrestore(&dwc->lock, flags);
+ break;
+
+ case PHY_NOTIFY_POST_RESET:
+ spin_lock_irqsave(&dwc->lock, flags);
+ if (!(dwc_rk->phy_reset_active & BIT(port))) {
+ spin_unlock_irqrestore(&dwc->lock, flags);
+ return NOTIFY_OK;
+ }
+
+ dwc_rk->phy_reset_active &= ~BIT(port);
+
+ /*
+ * Deassert PHY soft reset to reconnect the PIPE interface
+ * after PHY reinitialization.
+ */
+ reg = dwc3_readl(dwc, DWC3_GUSB3PIPECTL(port));
+ reg &= ~DWC3_GUSB3PIPECTL_PHYSOFTRST;
+ dwc3_writel(dwc, DWC3_GUSB3PIPECTL(port), reg);
+ spin_unlock_irqrestore(&dwc->lock, flags);
+
+ pm_runtime_put_autosuspend(dwc->dev);
+ break;
+ }
+
+ return NOTIFY_OK;
+}
+
+static void dwc3_rk_phy_unregister_notifiers(void *data)
+{
+ struct dwc3_rockchip *dwc_rk = data;
+ struct dwc3 *dwc = &dwc_rk->dwc;
+ int i;
+
+ for (i = 0; i < dwc->num_usb3_ports; i++)
+ phy_unregister_notifier(dwc->usb3_generic_phy[i],
+ &dwc_rk->usb3_phy_nb[i].nb);
+
+ /* Release any PM references from in-flight resets */
+ for (i = 0; i < dwc->num_usb3_ports; i++) {
+ if (dwc_rk->phy_reset_active & BIT(i))
+ pm_runtime_put_autosuspend(dwc->dev);
+ }
+ dwc_rk->phy_reset_active = 0;
+}
+
+static int dwc3_rk_phy_register_notifiers(struct dwc3 *dwc)
+{
+ struct dwc3_rockchip *dwc_rk = container_of(dwc, struct dwc3_rockchip, dwc);
+ int i;
+
+ for (i = 0; i < dwc->num_usb3_ports; i++) {
+ dwc_rk->usb3_phy_nb[i].nb.notifier_call = dwc3_usb3_phy_notify;
+ dwc_rk->usb3_phy_nb[i].dwc_rk = dwc_rk;
+ dwc_rk->usb3_phy_nb[i].port_index = i;
+ phy_register_notifier(dwc->usb3_generic_phy[i],
+ &dwc_rk->usb3_phy_nb[i].nb);
+ }
+
+ return devm_add_action_or_reset(dwc->dev, dwc3_rk_phy_unregister_notifiers, dwc_rk);
+}
+
+static struct dwc3_glue_ops dwc3_rockchip_glue_ops = {
+ .post_phy_registration = dwc3_rk_phy_register_notifiers,
};
static int dwc3_rockchip_probe(struct platform_device *pdev)
@@ -26,7 +151,7 @@ static int dwc3_rockchip_probe(struct platform_device *pdev)
return -ENOMEM;
dwc_rk->dwc.dev = &pdev->dev;
- dwc_rk->dwc.glue_ops = NULL;
+ dwc_rk->dwc.glue_ops = &dwc3_rockchip_glue_ops;
probe_data.dwc = &dwc_rk->dwc;
probe_data.res = res;
diff --git a/drivers/usb/dwc3/trace.c b/drivers/usb/dwc3/trace.c
index 088995885678..8c4e2a7b142e 100644
--- a/drivers/usb/dwc3/trace.c
+++ b/drivers/usb/dwc3/trace.c
@@ -9,3 +9,6 @@
#define CREATE_TRACE_POINTS
#include "trace.h"
+
+EXPORT_TRACEPOINT_SYMBOL_GPL(dwc3_readl);
+EXPORT_TRACEPOINT_SYMBOL_GPL(dwc3_writel);
--
2.53.0
^ permalink raw reply [flat|nested] 11+ messages in thread* Re: [PATCH v16 4/6] usb: dwc3: rockchip: support PHY reset notifications
2026-09-24 17:25 ` [PATCH v16 4/6] usb: dwc3: rockchip: support PHY reset notifications Sebastian Reichel
@ 2026-09-26 7:51 ` Manivannan Sadhasivam
0 siblings, 0 replies; 11+ messages in thread
From: Manivannan Sadhasivam @ 2026-09-26 7:51 UTC (permalink / raw)
To: Sebastian Reichel
Cc: Thinh Nguyen, Greg Kroah-Hartman, Heiko Stuebner, Vinod Koul,
Neil Armstrong, Igor Paunovic, linux-kernel, linux-usb,
linux-arm-kernel, linux-rockchip, linux-phy, kernel
On Thu, Sep 24, 2026 at 07:25:40PM +0200, Sebastian Reichel wrote:
> On recent Rockchip platforms (at least RK3588 & RK3576), DWC3 IP is used
> with a USBDP PHY providing USB3 and DP. This PHY needs to be reset when
> the mode changes, which may happen when plugging in different USB-C
> devices.
>
> If the USBDP PHY resets with the DWC3 IP running, its internal state
> corrupts resulting in the USBDP PHY not being able to lock some PLL
> clocks, which effectively renders USB3 unusable.
>
> To fix the issue this adds handling for the new PHY framework reset
> notifications, which will assert PHYSOFTRST before the actual PHY
> is disabled and will deassert it once the PHY returns.
>
> Tested-by: Igor Paunovic <royalnet026@gmail.com> # Orange Pi 5 Plus
> Acked-by: Thinh Nguyen <Thinh.Nguyen@synopsys.com>
> Signed-off-by: Sebastian Reichel <sebastian.reichel@collabora.com>
Acked-by: Manivannan Sadhasivam <manivannan.sadhasivam@oss.qualcomm.com> # PHY
- Mani
--
மணிவண்ணன் சதாசிவம்
^ permalink raw reply [flat|nested] 11+ messages in thread
* [PATCH v16 5/6] usb: gadget: define stub for usb_udc_vbus_handler
2026-09-24 17:25 [PATCH v16 0/6] usb: dwc3: introduce Rockchip glue driver Sebastian Reichel
` (3 preceding siblings ...)
2026-09-24 17:25 ` [PATCH v16 4/6] usb: dwc3: rockchip: support PHY reset notifications Sebastian Reichel
@ 2026-09-24 17:25 ` Sebastian Reichel
2026-09-24 17:25 ` [PATCH v16 6/6] usb: dwc3: rockchip: fix USB-C reconnect in gadget mode Sebastian Reichel
2026-09-27 12:02 ` [PATCH v16 0/6] usb: dwc3: introduce Rockchip glue driver Igor Paunovic
6 siblings, 0 replies; 11+ messages in thread
From: Sebastian Reichel @ 2026-09-24 17:25 UTC (permalink / raw)
To: Thinh Nguyen, Greg Kroah-Hartman, Heiko Stuebner, Vinod Koul,
Neil Armstrong, Manivannan Sadhasivam
Cc: Igor Paunovic, linux-kernel, linux-usb, linux-arm-kernel,
linux-rockchip, linux-phy, kernel, Sebastian Reichel
Platform drivers with optional gadget mode support may want to
call usb_udc_vbus_handler to properly handle a cable being
re-plugged.
Ensure these drivers can call the function without a hard dependency on
CONFIG_USB_GADGET or having the ifdef logic in the driver.
Signed-off-by: Sebastian Reichel <sebastian.reichel@collabora.com>
---
include/linux/usb/gadget.h | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/include/linux/usb/gadget.h b/include/linux/usb/gadget.h
index 8285b19a25e0..551e71b6eda0 100644
--- a/include/linux/usb/gadget.h
+++ b/include/linux/usb/gadget.h
@@ -979,7 +979,12 @@ extern int usb_gadget_ep_match_desc(struct usb_gadget *gadget,
/*-------------------------------------------------------------------------*/
/* utility to update vbus status for udc core, it may be scheduled */
+#if IS_ENABLED(CONFIG_USB_GADGET)
extern void usb_udc_vbus_handler(struct usb_gadget *gadget, bool status);
+#else
+static inline void usb_udc_vbus_handler(struct usb_gadget *gadget, bool status)
+{}
+#endif
/*-------------------------------------------------------------------------*/
--
2.53.0
^ permalink raw reply [flat|nested] 11+ messages in thread* [PATCH v16 6/6] usb: dwc3: rockchip: fix USB-C reconnect in gadget mode
2026-09-24 17:25 [PATCH v16 0/6] usb: dwc3: introduce Rockchip glue driver Sebastian Reichel
` (4 preceding siblings ...)
2026-09-24 17:25 ` [PATCH v16 5/6] usb: gadget: define stub for usb_udc_vbus_handler Sebastian Reichel
@ 2026-09-24 17:25 ` Sebastian Reichel
2026-09-27 12:02 ` Igor Paunovic
2026-09-27 12:02 ` [PATCH v16 0/6] usb: dwc3: introduce Rockchip glue driver Igor Paunovic
6 siblings, 1 reply; 11+ messages in thread
From: Sebastian Reichel @ 2026-09-24 17:25 UTC (permalink / raw)
To: Thinh Nguyen, Greg Kroah-Hartman, Heiko Stuebner, Vinod Koul,
Neil Armstrong, Manivannan Sadhasivam
Cc: Igor Paunovic, linux-kernel, linux-usb, linux-arm-kernel,
linux-rockchip, linux-phy, kernel, Sebastian Reichel
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@collabora.com>
---
drivers/usb/dwc3/Kconfig | 1 +
drivers/usb/dwc3/dwc3-rockchip.c | 23 +++++++++++++++++++++++
2 files changed, 24 insertions(+)
diff --git a/drivers/usb/dwc3/Kconfig b/drivers/usb/dwc3/Kconfig
index 3c120ea9746d..344291daf1b2 100644
--- a/drivers/usb/dwc3/Kconfig
+++ b/drivers/usb/dwc3/Kconfig
@@ -193,6 +193,7 @@ config USB_DWC3_OCTEON
config USB_DWC3_ROCKCHIP
tristate "Rockchip DWC3 Platform Driver"
depends on ARCH_ROCKCHIP || COMPILE_TEST
+ depends on USB_GADGET || !USB_GADGET # if USB_GADGET=m, this can't be 'y'
depends on OF
default USB_DWC3
help
diff --git a/drivers/usb/dwc3/dwc3-rockchip.c b/drivers/usb/dwc3/dwc3-rockchip.c
index 17b6c2ca9e19..ba8ffef2f27e 100644
--- a/drivers/usb/dwc3/dwc3-rockchip.c
+++ b/drivers/usb/dwc3/dwc3-rockchip.c
@@ -25,8 +25,17 @@ struct dwc3_rockchip {
struct dwc3 dwc;
struct dwc3_rk_phy_nb usb3_phy_nb[DWC3_USB3_MAX_PORTS];
u8 phy_reset_active;
+ enum usb_role role;
};
+static void dwc3_rockchip_vbus_handler(struct dwc3 *dwc, bool present)
+{
+ if (!dwc->gadget || !dwc->gadget_driver)
+ return;
+
+ usb_udc_vbus_handler(dwc->gadget, present);
+}
+
static int dwc3_usb3_phy_notify(struct notifier_block *nb,
unsigned long action, void *data)
{
@@ -57,6 +66,8 @@ static int dwc3_usb3_phy_notify(struct notifier_block *nb,
if (!ret)
return NOTIFY_OK;
+ dwc3_rockchip_vbus_handler(dwc, false);
+
/*
* Assert USB3 PHY soft reset within DWC3 before the external
* PHY resets. This disconnects the PIPE interface, preventing
@@ -69,6 +80,7 @@ static int dwc3_usb3_phy_notify(struct notifier_block *nb,
reg |= DWC3_GUSB3PIPECTL_PHYSOFTRST;
dwc3_writel(dwc, DWC3_GUSB3PIPECTL(port), reg);
spin_unlock_irqrestore(&dwc->lock, flags);
+
break;
case PHY_NOTIFY_POST_RESET:
@@ -89,6 +101,8 @@ static int dwc3_usb3_phy_notify(struct notifier_block *nb,
dwc3_writel(dwc, DWC3_GUSB3PIPECTL(port), reg);
spin_unlock_irqrestore(&dwc->lock, flags);
+ dwc3_rockchip_vbus_handler(dwc, dwc_rk->role == USB_ROLE_DEVICE);
+
pm_runtime_put_autosuspend(dwc->dev);
break;
}
@@ -130,7 +144,16 @@ static int dwc3_rk_phy_register_notifiers(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_notifiers,
};
--
2.53.0
^ permalink raw reply [flat|nested] 11+ messages in thread* Re: [PATCH v16 6/6] usb: dwc3: rockchip: fix USB-C reconnect in gadget mode
2026-09-24 17:25 ` [PATCH v16 6/6] usb: dwc3: rockchip: fix USB-C reconnect in gadget mode Sebastian Reichel
@ 2026-09-27 12:02 ` Igor Paunovic
0 siblings, 0 replies; 11+ messages in thread
From: Igor Paunovic @ 2026-09-27 12:02 UTC (permalink / raw)
To: Sebastian Reichel
Cc: Igor Paunovic, Thinh Nguyen, Greg Kroah-Hartman, Heiko Stuebner,
Vinod Koul, Neil Armstrong, Manivannan Sadhasivam, linux-kernel,
linux-usb, linux-arm-kernel, linux-rockchip, linux-phy, kernel
Hi Sebastian,
I tested gadget mode on the Orange Pi 5 Plus with the same v16
kernel as in my reply to the cover letter. The board was an ACM
gadget (configfs) on fc000000.usb, with the Type-C port set to
port_type=sink for the test, connected with a USB-C to USB-C cable
to a USB4 port of an x86 host (Ubuntu 24.04, kernel
7.0.0-30-generic).
dwc3 forbids runtime PM by default, and with power/control left at
"on" the controller never suspends, so the case from the commit
message does not come up. For the test I set power/control to
"auto", unplugged the cable, waited until runtime_status was
"suspended" (about 6 s after the unplug; autosuspend_delay_ms is
5000), then plugged it back in. I did this 12 times, 2 of them with
the connector flipped. Each time the controller resumed and the
host enumerated the gadget again at SuperSpeed; where my script
timed it, the UDC was "configured" 0.9-1.5 s after the Type-C
partner appeared. Apart from the message mentioned below, neither
the board nor the host logged USB, Type-C or PHY warnings or errors.
On sashiko's question about the early return [1]: I have ftrace
output for 6 of the 12 cycles (the trace of the second run was
lost). usb_udc_vbus_handler() was called 12 times, once on each
unplug and once on each plug, always from dwc3_rockchip_set_role()
and never from dwc3_usb3_phy_notify(). The PHY notifier ran only
inside the runtime suspend and resume, twice each time, never while
the controller was suspended. In each PRE_RESET
pm_runtime_get_if_active() took no reference and the VBUS call was
not reached, so the early return does happen. On each plug the
resume was started by dwc3_gadget_pullup() from
usb_gadget_connect_locked(), after the VBUS update from the
pre_set_role hook, and the gadget reconnected.
About 50 us later a second resume request came from another worker;
from the code this looks like the pm_runtime_get_sync() in
__dwc3_set_mode(). I did not trace that function, so I cannot say
whether it alone would reconnect the gadget without this patch.
Not tested: the board without this patch, data transfer (only
enumeration), sashiko's other three points, system suspend, and a
host with a dual-role port. On every unplug dwc3 logs "request ...
was not queued to ep1in", also with runtime PM forbidden; I did not
look into it.
In one of the 12 cycles my script first marked a failure: the
board's Ethernet link dropped while I handled the cables, and the
script checks the host over ssh. The host's kernel log shows the
gadget enumerated normally in that cycle.
An LLM assistant wrote the test script, analysed the logs and the
ftrace output and drafted this mail. I ran the test on the board
and did the cable pulls.
Tested-by: Igor Paunovic <royalnet026@gmail.com> # Orange Pi 5 Plus
[1] https://lore.kernel.org/all/20260924174014.366B21F000FF@smtp.kernel.org/
Igor
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH v16 0/6] usb: dwc3: introduce Rockchip glue driver
2026-09-24 17:25 [PATCH v16 0/6] usb: dwc3: introduce Rockchip glue driver Sebastian Reichel
` (5 preceding siblings ...)
2026-09-24 17:25 ` [PATCH v16 6/6] usb: dwc3: rockchip: fix USB-C reconnect in gadget mode Sebastian Reichel
@ 2026-09-27 12:02 ` Igor Paunovic
6 siblings, 0 replies; 11+ messages in thread
From: Igor Paunovic @ 2026-09-27 12:02 UTC (permalink / raw)
To: Sebastian Reichel
Cc: Igor Paunovic, Thinh Nguyen, Greg Kroah-Hartman, Heiko Stuebner,
Vinod Koul, Neil Armstrong, Manivannan Sadhasivam,
Krishna Kurapati, linux-kernel, linux-usb, linux-arm-kernel,
linux-rockchip, linux-phy, kernel
Hi Sebastian,
My tags on 1/6-4/6 are from v14. Since then 2/6 changed its PM ops
(v15 and v16) and 4/6 gained the two tracepoint exports, so I re-ran
my tests on v16. The tags still stand, and 5/6 plus the Kconfig line
in 6/6 fix the build failure I reported on v15 5/5 (posted as v1)
[1].
I tested on an Orange Pi 5 Plus with the same base and the same
local DT as my v14 report [2] (rockchip-devel at ea51774c5c42,
7.2-rc7). The v14 glue patches were replaced by v16 2/6, 4/6, 5/6
and 6/6; 1/6 and 3/6 have the same patch-id as in v14, so they
stayed. The glue is built in, with PM and USB_GADGET enabled.
The sink is different this time: a Dell 2408WFP at 1920x1200 on a
USB-C to DP cable, not the Epico converter into the Samsung 4K120
from [2]. This cable also has a GsCooLink chip (VID 0x3679), and its
DPCD (1.2, HBR2 x4) is newer than the 2009 monitor, so the link
partner is most likely that chip, not the monitor.
I ran the same test on my daily kernel (v14 glue, plus a locally
patched tcpm module) and on the v16 kernel. Both gave the same
result:
- dwc3-rockchip bound to fc000000.usb and fc400000.usb
- DP alt mode active (svid ff01), link trained at HBR2 x4
- cable unplug/replug, and replug with the connector flipped
(port0 orientation changed in sysfs): DP-1 connected again about
2 s after each replug
- the xHCI on fc000000.usb went away on each unplug and came back on
each replug, and the cable's billboard device enumerated again
- no messages from dwc3, dwc3-rockchip or the usbdp PHY
With this config, none of the v14 to v16 changes alter what runs on
the board. So this run confirms v16 on hardware, but it cannot tell
v16 from v14.
Build test on v7.3-rc1, arm64 allnoconfig plus a fragment, gcc 13.3.
Host-only dwc3 with USB_GADGET unset now builds, with the glue built
in and as a module. USB_GADGET=m with the glue built in can no
longer be selected. Without the two tracepoint exports in 4/6, the
v14 modpost error comes back, so they are still needed. All builds
that passed had no warnings. They all had PM_SLEEP disabled (PM
itself cannot be, ARCH_ROCKCHIP selects it), so they also built the
new PM ops in 2/6 without sleep support.
Gadget mode (6/6) I tested separately; the results and my tag are
in my reply to 6/6. Not tested: system suspend/resume, a 4K/HDR sink
on USB-C this time, and USB devices in host mode on the USB-C port
other than the cable's own billboard. 5/6 was only build-tested. I
am not adding any tags to 1/6-5/6 here.
An LLM assistant built the v16 test kernel (reverting the three
changed v14 glue commits and applying v16), wrote and ran the build
test, wrote the board test script, compared the logs and drafted
this mail. I ran the board test and did the cable pulls and flips.
[1] https://lore.kernel.org/all/20260918091032.15593-1-royalnet026@gmail.com/
[2] https://lore.kernel.org/all/20260815121608.64818-1-royalnet026@gmail.com/
Igor
^ permalink raw reply [flat|nested] 11+ messages in thread