When the orientation of a type C cable changes, usbdp set the new
configuration in its internal state but does not write this to the
hardware.
Make use of phy_ops::set_mode to write this new state. This should be
called by the USB controller when it is notified of a role change
(assuming it is acting as the role switch) and will be called at a point
when the controller does not expect the phy to be operating normally.
Signed-off-by: John Keeping <jkeeping@inmusicbrands.com>
---
drivers/phy/rockchip/phy-rockchip-usbdp.c | 14 ++++++++++++++
1 file changed, 14 insertions(+)
diff --git a/drivers/phy/rockchip/phy-rockchip-usbdp.c b/drivers/phy/rockchip/phy-rockchip-usbdp.c
index c066cc0a7b4f1..00368fb09307a 100644
--- a/drivers/phy/rockchip/phy-rockchip-usbdp.c
+++ b/drivers/phy/rockchip/phy-rockchip-usbdp.c
@@ -1335,9 +1335,23 @@ static int rk_udphy_usb3_phy_exit(struct phy *phy)
return 0;
}
+static int rk_udphy_usb3_phy_set_mode(struct phy *phy, enum phy_mode mode, int submode)
+{
+ struct rk_udphy *udphy = phy_get_drvdata(phy);
+ int ret = 0;
+
+ mutex_lock(&udphy->mutex);
+ if (udphy->mode != UDPHY_MODE_NONE)
+ ret = rk_udphy_init(udphy);
+ mutex_unlock(&udphy->mutex);
+
+ return ret;
+}
+
static const struct phy_ops rk_udphy_usb3_phy_ops = {
.init = rk_udphy_usb3_phy_init,
.exit = rk_udphy_usb3_phy_exit,
+ .set_mode = rk_udphy_usb3_phy_set_mode,
.owner = THIS_MODULE,
};
--
2.50.0
Hi John, Am Donnerstag, 10. Juli 2025, 17:22:50 Mitteleuropäische Sommerzeit schrieb John Keeping: > When the orientation of a type C cable changes, usbdp set the new > configuration in its internal state but does not write this to the > hardware. > > Make use of phy_ops::set_mode to write this new state. This should be > called by the USB controller when it is notified of a role change > (assuming it is acting as the role switch) and will be called at a point > when the controller does not expect the phy to be operating normally. > > Signed-off-by: John Keeping <jkeeping@inmusicbrands.com> with the comments from Ondrej in [0] the whole thing seems to be slightly more complex [0] https://patchwork.kernel.org/project/linux-arm-kernel/patch/20250225184519.3586926-3-heiko@sntech.de/ > --- > drivers/phy/rockchip/phy-rockchip-usbdp.c | 14 ++++++++++++++ > 1 file changed, 14 insertions(+) > > diff --git a/drivers/phy/rockchip/phy-rockchip-usbdp.c b/drivers/phy/rockchip/phy-rockchip-usbdp.c > index c066cc0a7b4f1..00368fb09307a 100644 > --- a/drivers/phy/rockchip/phy-rockchip-usbdp.c > +++ b/drivers/phy/rockchip/phy-rockchip-usbdp.c > @@ -1335,9 +1335,23 @@ static int rk_udphy_usb3_phy_exit(struct phy *phy) > return 0; > } > > +static int rk_udphy_usb3_phy_set_mode(struct phy *phy, enum phy_mode mode, int submode) > +{ > + struct rk_udphy *udphy = phy_get_drvdata(phy); > + int ret = 0; > + > + mutex_lock(&udphy->mutex); > + if (udphy->mode != UDPHY_MODE_NONE) > + ret = rk_udphy_init(udphy); > + mutex_unlock(&udphy->mutex); > + > + return ret; > +} > + > static const struct phy_ops rk_udphy_usb3_phy_ops = { > .init = rk_udphy_usb3_phy_init, > .exit = rk_udphy_usb3_phy_exit, > + .set_mode = rk_udphy_usb3_phy_set_mode, > .owner = THIS_MODULE, > }; > >
On Tue, Jul 15, 2025 at 12:35:47PM +0200, Heiko Stuebner wrote: > Am Donnerstag, 10. Juli 2025, 17:22:50 Mitteleuropäische Sommerzeit schrieb John Keeping: > > When the orientation of a type C cable changes, usbdp set the new > > configuration in its internal state but does not write this to the > > hardware. > > > > Make use of phy_ops::set_mode to write this new state. This should be > > called by the USB controller when it is notified of a role change > > (assuming it is acting as the role switch) and will be called at a point > > when the controller does not expect the phy to be operating normally. > > > > Signed-off-by: John Keeping <jkeeping@inmusicbrands.com> > > with the comments from Ondrej in [0] the whole thing seems to be > slightly more complex > > > [0] https://patchwork.kernel.org/project/linux-arm-kernel/patch/20250225184519.3586926-3-heiko@sntech.de/ I spent some more time looking at this and have found what looks like the vendor kernel's approach to this issue [1]. It seems that Rockchip set the autosuspend time to 100ms and hope that is sufficiently short to allow the controller to suspend whenever a cable is unplugged. [1] https://github.com/rockchip-linux/kernel/commit/5ac62b80f7471ee9858ab0459af07180de938b51 Having experimented some more, I was able to do something similar without needing any changes to the phy driver by applying the changes below on top of patch 1/2 in this thread. This depends on runtime PM to ensure the controller is suspended whenever a cable is unplugged, but if CONFIG_PM is enabled then it should address those concerns because the controller will be shutdown and the phy configured via phy_init() in the normal sequence. It sounds like Thinh doesn't like this approach, but if the requirement is for the controller to be shutdown and restarted whenever the orientation changes I don't think we can avoid some changes in dwc3. --- >8 --- diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c index 6573cca0eeaf5..b707afd38cc27 100644 --- a/drivers/usb/dwc3/core.c +++ b/drivers/usb/dwc3/core.c @@ -199,6 +199,10 @@ static void __dwc3_set_mode(struct work_struct *work) dwc3_otg_update(dwc, 1); break; default: + if (dwc->force_suspended) { + pm_runtime_force_resume(dwc->dev); + dwc->force_suspended = 0; + } break; } @@ -272,6 +276,9 @@ static void __dwc3_set_mode(struct work_struct *work) dwc3_otg_update(dwc, 0); break; default: + ret = pm_runtime_force_suspend(dwc->dev); + if (!ret) + dwc->force_suspended = 1; break; } @@ -2485,7 +2492,7 @@ static int dwc3_suspend_common(struct dwc3 *dwc, pm_message_t msg) dwc3_core_exit(dwc); break; default: - /* do nothing */ + dwc3_core_exit(dwc); break; } @@ -2552,7 +2559,9 @@ static int dwc3_resume_common(struct dwc3 *dwc, pm_message_t msg) break; default: - /* do nothing */ + ret = dwc3_core_init_for_resume(dwc); + if (ret) + return ret; break; } diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h index d5b985fa12f4d..7e1d480c59ae1 100644 --- a/drivers/usb/dwc3/core.h +++ b/drivers/usb/dwc3/core.h @@ -1390,6 +1390,7 @@ struct dwc3 { unsigned sys_wakeup:1; unsigned wakeup_configured:1; unsigned suspended:1; + unsigned force_suspended:1; unsigned susphy_state:1; u16 imod_interval; diff --git a/drivers/usb/dwc3/drd.c b/drivers/usb/dwc3/drd.c index 8f427afa8eb93..16aac8384ca3d 100644 --- a/drivers/usb/dwc3/drd.c +++ b/drivers/usb/dwc3/drd.c @@ -461,6 +461,7 @@ static int dwc3_usb_role_switch_set(struct usb_role_switch *sw, break; } + flush_work(&dwc->drd_work); dwc3_set_mode(dwc, mode); return 0; }
© 2016 - 2025 Red Hat, Inc.