Now that the upstream code has been getting broader test coverage by our users we occasionally see issues with USB2 devices plugged in during boot. Before Linux is running, the USB2 PHY has usually been running in device mode and it turns out that sometimes host->device or device->host transitions don't work. The root cause: If the role inside the USB2 PHY is re-configured when it has already been powered on or when dwc2 has already enabled the ULPI interface the new configuration sometimes doesn't take affect until dwc3 is reset again. Fix this rare issue by configuring the role much earlier. Note that the USB3 PHY does not suffer from this issue and actually requires dwc3 to be up before the correct role can be configured there.
Reported-by: James Calligeros jcalligeros99@gmail.com Reported-by: Janne Grunau j@jannau.net Fixes: 0ec946d32ef7 ("usb: dwc3: Add Apple Silicon DWC3 glue layer driver") Cc: stable@vger.kernel.org Signed-off-by: Sven Peter sven@kernel.org --- drivers/usb/dwc3/dwc3-apple.c | 48 +++++++++++++++++++++++++++++-------------- 1 file changed, 33 insertions(+), 15 deletions(-)
diff --git a/drivers/usb/dwc3/dwc3-apple.c b/drivers/usb/dwc3/dwc3-apple.c index cc47cad232e397ac4498b09165dfdb5bd215ded7..c2ae8eb21d514e5e493d2927bc12908c308dfe19 100644 --- a/drivers/usb/dwc3/dwc3-apple.c +++ b/drivers/usb/dwc3/dwc3-apple.c @@ -218,25 +218,31 @@ static int dwc3_apple_core_init(struct dwc3_apple *appledwc) return ret; }
-static void dwc3_apple_phy_set_mode(struct dwc3_apple *appledwc, enum phy_mode mode) -{ - lockdep_assert_held(&appledwc->lock); - - /* - * This platform requires SUSPHY to be enabled here already in order to properly configure - * the PHY and switch dwc3's PIPE interface to USB3 PHY. - */ - dwc3_enable_susphy(&appledwc->dwc, true); - phy_set_mode(appledwc->dwc.usb2_generic_phy[0], mode); - phy_set_mode(appledwc->dwc.usb3_generic_phy[0], mode); -} - static int dwc3_apple_init(struct dwc3_apple *appledwc, enum dwc3_apple_state state) { int ret, ret_reset;
lockdep_assert_held(&appledwc->lock);
+ /* + * The USB2 PHY on this platform must be configured for host or device mode while it is + * still powered off and before dwc3 tries to access it. Otherwise, the new configuration + * will sometimes only take affect after the *next* time dwc3 is brought up which causes + * the connected device to just not work. + * The USB3 PHY must be configured later after dwc3 has already been initialized. + */ + switch (state) { + case DWC3_APPLE_HOST: + phy_set_mode(appledwc->dwc.usb2_generic_phy[0], PHY_MODE_USB_HOST); + break; + case DWC3_APPLE_DEVICE: + phy_set_mode(appledwc->dwc.usb2_generic_phy[0], PHY_MODE_USB_DEVICE); + break; + default: + /* Unreachable unless there's a bug in this driver */ + return -EINVAL; + } + ret = reset_control_deassert(appledwc->reset); if (ret) { dev_err(appledwc->dev, "Failed to deassert reset, err=%d\n", ret); @@ -257,7 +263,13 @@ static int dwc3_apple_init(struct dwc3_apple *appledwc, enum dwc3_apple_state st case DWC3_APPLE_HOST: appledwc->dwc.dr_mode = USB_DR_MODE_HOST; dwc3_apple_set_ptrcap(appledwc, DWC3_GCTL_PRTCAP_HOST); - dwc3_apple_phy_set_mode(appledwc, PHY_MODE_USB_HOST); + /* + * This platform requires SUSPHY to be enabled here already in order to properly + * configure the PHY and switch dwc3's PIPE interface to USB3 PHY. The USB2 PHY + * has already been configured to the correct mode earlier. + */ + dwc3_enable_susphy(&appledwc->dwc, true); + phy_set_mode(appledwc->dwc.usb3_generic_phy[0], PHY_MODE_USB_HOST); ret = dwc3_host_init(&appledwc->dwc); if (ret) { dev_err(appledwc->dev, "Failed to initialize host, ret=%d\n", ret); @@ -268,7 +280,13 @@ static int dwc3_apple_init(struct dwc3_apple *appledwc, enum dwc3_apple_state st case DWC3_APPLE_DEVICE: appledwc->dwc.dr_mode = USB_DR_MODE_PERIPHERAL; dwc3_apple_set_ptrcap(appledwc, DWC3_GCTL_PRTCAP_DEVICE); - dwc3_apple_phy_set_mode(appledwc, PHY_MODE_USB_DEVICE); + /* + * This platform requires SUSPHY to be enabled here already in order to properly + * configure the PHY and switch dwc3's PIPE interface to USB3 PHY. The USB2 PHY + * has already been configured to the correct mode earlier. + */ + dwc3_enable_susphy(&appledwc->dwc, true); + phy_set_mode(appledwc->dwc.usb3_generic_phy[0], PHY_MODE_USB_DEVICE); ret = dwc3_gadget_init(&appledwc->dwc); if (ret) { dev_err(appledwc->dev, "Failed to initialize gadget, ret=%d\n", ret);
--- base-commit: 8f0b4cce4481fb22653697cced8d0d04027cb1e8 change-id: 20260108-dwc3-apple-usb2phy-fix-cf1d26018dd0
Best regards,
On Thu, Jan 08, 2026, Sven Peter wrote:
Now that the upstream code has been getting broader test coverage by our users we occasionally see issues with USB2 devices plugged in during boot. Before Linux is running, the USB2 PHY has usually been running in device mode and it turns out that sometimes host->device or device->host transitions don't work. The root cause: If the role inside the USB2 PHY is re-configured when it has already been powered on or when dwc2 has already enabled the ULPI interface the new configuration sometimes doesn't take affect until dwc3 is reset again. Fix this rare issue by configuring the role much earlier. Note that the USB3 PHY does not suffer from this issue and actually requires dwc3 to be up before the correct role can be configured there.
Reported-by: James Calligeros jcalligeros99@gmail.com Reported-by: Janne Grunau j@jannau.net Fixes: 0ec946d32ef7 ("usb: dwc3: Add Apple Silicon DWC3 glue layer driver") Cc: stable@vger.kernel.org Signed-off-by: Sven Peter sven@kernel.org
drivers/usb/dwc3/dwc3-apple.c | 48 +++++++++++++++++++++++++++++-------------- 1 file changed, 33 insertions(+), 15 deletions(-)
diff --git a/drivers/usb/dwc3/dwc3-apple.c b/drivers/usb/dwc3/dwc3-apple.c index cc47cad232e397ac4498b09165dfdb5bd215ded7..c2ae8eb21d514e5e493d2927bc12908c308dfe19 100644 --- a/drivers/usb/dwc3/dwc3-apple.c +++ b/drivers/usb/dwc3/dwc3-apple.c @@ -218,25 +218,31 @@ static int dwc3_apple_core_init(struct dwc3_apple *appledwc) return ret; } -static void dwc3_apple_phy_set_mode(struct dwc3_apple *appledwc, enum phy_mode mode) -{
- lockdep_assert_held(&appledwc->lock);
- /*
* This platform requires SUSPHY to be enabled here already in order to properly configure* the PHY and switch dwc3's PIPE interface to USB3 PHY.*/- dwc3_enable_susphy(&appledwc->dwc, true);
- phy_set_mode(appledwc->dwc.usb2_generic_phy[0], mode);
- phy_set_mode(appledwc->dwc.usb3_generic_phy[0], mode);
-}
static int dwc3_apple_init(struct dwc3_apple *appledwc, enum dwc3_apple_state state) { int ret, ret_reset; lockdep_assert_held(&appledwc->lock);
- /*
* The USB2 PHY on this platform must be configured for host or device mode while it is* still powered off and before dwc3 tries to access it. Otherwise, the new configuration* will sometimes only take affect after the *next* time dwc3 is brought up which causes* the connected device to just not work.* The USB3 PHY must be configured later after dwc3 has already been initialized.*/- switch (state) {
- case DWC3_APPLE_HOST:
phy_set_mode(appledwc->dwc.usb2_generic_phy[0], PHY_MODE_USB_HOST);break;- case DWC3_APPLE_DEVICE:
phy_set_mode(appledwc->dwc.usb2_generic_phy[0], PHY_MODE_USB_DEVICE);break;- default:
/* Unreachable unless there's a bug in this driver */return -EINVAL;- }
- ret = reset_control_deassert(appledwc->reset); if (ret) { dev_err(appledwc->dev, "Failed to deassert reset, err=%d\n", ret);
@@ -257,7 +263,13 @@ static int dwc3_apple_init(struct dwc3_apple *appledwc, enum dwc3_apple_state st case DWC3_APPLE_HOST: appledwc->dwc.dr_mode = USB_DR_MODE_HOST; dwc3_apple_set_ptrcap(appledwc, DWC3_GCTL_PRTCAP_HOST);
dwc3_apple_phy_set_mode(appledwc, PHY_MODE_USB_HOST);
/** This platform requires SUSPHY to be enabled here already in order to properly* configure the PHY and switch dwc3's PIPE interface to USB3 PHY. The USB2 PHY* has already been configured to the correct mode earlier.*/dwc3_enable_susphy(&appledwc->dwc, true); ret = dwc3_host_init(&appledwc->dwc); if (ret) { dev_err(appledwc->dev, "Failed to initialize host, ret=%d\n", ret);phy_set_mode(appledwc->dwc.usb3_generic_phy[0], PHY_MODE_USB_HOST);@@ -268,7 +280,13 @@ static int dwc3_apple_init(struct dwc3_apple *appledwc, enum dwc3_apple_state st case DWC3_APPLE_DEVICE: appledwc->dwc.dr_mode = USB_DR_MODE_PERIPHERAL; dwc3_apple_set_ptrcap(appledwc, DWC3_GCTL_PRTCAP_DEVICE);
dwc3_apple_phy_set_mode(appledwc, PHY_MODE_USB_DEVICE);
/** This platform requires SUSPHY to be enabled here already in order to properly* configure the PHY and switch dwc3's PIPE interface to USB3 PHY. The USB2 PHY* has already been configured to the correct mode earlier.*/dwc3_enable_susphy(&appledwc->dwc, true); ret = dwc3_gadget_init(&appledwc->dwc); if (ret) { dev_err(appledwc->dev, "Failed to initialize gadget, ret=%d\n", ret);phy_set_mode(appledwc->dwc.usb3_generic_phy[0], PHY_MODE_USB_DEVICE);
base-commit: 8f0b4cce4481fb22653697cced8d0d04027cb1e8 change-id: 20260108-dwc3-apple-usb2phy-fix-cf1d26018dd0
Best regards,
Sven Peter sven@kernel.org
Acked-by: Thinh Nguyen Thinh.Nguyen@synopsys.com
Thanks, Thinh
On Thu, Jan 08, 2026 at 08:21:45PM +0100, Sven Peter wrote:
Now that the upstream code has been getting broader test coverage by our users we occasionally see issues with USB2 devices plugged in during boot. Before Linux is running, the USB2 PHY has usually been running in device mode and it turns out that sometimes host->device or device->host transitions don't work. The root cause: If the role inside the USB2 PHY is re-configured when it has already been powered on or when dwc2 has already enabled the ULPI
"dwc3", typo noticed by Mark Kettenis
interface the new configuration sometimes doesn't take affect until dwc3 is reset again. Fix this rare issue by configuring the role much earlier. Note that the USB3 PHY does not suffer from this issue and actually requires dwc3 to be up before the correct role can be configured there.
Reported-by: James Calligeros jcalligeros99@gmail.com Reported-by: Janne Grunau j@jannau.net Fixes: 0ec946d32ef7 ("usb: dwc3: Add Apple Silicon DWC3 glue layer driver") Cc: stable@vger.kernel.org Signed-off-by: Sven Peter sven@kernel.org
drivers/usb/dwc3/dwc3-apple.c | 48 +++++++++++++++++++++++++++++-------------- 1 file changed, 33 insertions(+), 15 deletions(-)
diff --git a/drivers/usb/dwc3/dwc3-apple.c b/drivers/usb/dwc3/dwc3-apple.c index cc47cad232e397ac4498b09165dfdb5bd215ded7..c2ae8eb21d514e5e493d2927bc12908c308dfe19 100644 --- a/drivers/usb/dwc3/dwc3-apple.c +++ b/drivers/usb/dwc3/dwc3-apple.c @@ -218,25 +218,31 @@ static int dwc3_apple_core_init(struct dwc3_apple *appledwc) return ret; } -static void dwc3_apple_phy_set_mode(struct dwc3_apple *appledwc, enum phy_mode mode) -{
- lockdep_assert_held(&appledwc->lock);
- /*
* This platform requires SUSPHY to be enabled here already in order to properly configure* the PHY and switch dwc3's PIPE interface to USB3 PHY.*/- dwc3_enable_susphy(&appledwc->dwc, true);
- phy_set_mode(appledwc->dwc.usb2_generic_phy[0], mode);
- phy_set_mode(appledwc->dwc.usb3_generic_phy[0], mode);
-}
static int dwc3_apple_init(struct dwc3_apple *appledwc, enum dwc3_apple_state state) { int ret, ret_reset; lockdep_assert_held(&appledwc->lock);
- /*
* The USB2 PHY on this platform must be configured for host or device mode while it is* still powered off and before dwc3 tries to access it. Otherwise, the new configuration* will sometimes only take affect after the *next* time dwc3 is brought up which causes* the connected device to just not work.* The USB3 PHY must be configured later after dwc3 has already been initialized.*/- switch (state) {
- case DWC3_APPLE_HOST:
phy_set_mode(appledwc->dwc.usb2_generic_phy[0], PHY_MODE_USB_HOST);break;- case DWC3_APPLE_DEVICE:
phy_set_mode(appledwc->dwc.usb2_generic_phy[0], PHY_MODE_USB_DEVICE);break;- default:
/* Unreachable unless there's a bug in this driver */return -EINVAL;- }
- ret = reset_control_deassert(appledwc->reset); if (ret) { dev_err(appledwc->dev, "Failed to deassert reset, err=%d\n", ret);
@@ -257,7 +263,13 @@ static int dwc3_apple_init(struct dwc3_apple *appledwc, enum dwc3_apple_state st case DWC3_APPLE_HOST: appledwc->dwc.dr_mode = USB_DR_MODE_HOST; dwc3_apple_set_ptrcap(appledwc, DWC3_GCTL_PRTCAP_HOST);
dwc3_apple_phy_set_mode(appledwc, PHY_MODE_USB_HOST);
/** This platform requires SUSPHY to be enabled here already in order to properly* configure the PHY and switch dwc3's PIPE interface to USB3 PHY. The USB2 PHY* has already been configured to the correct mode earlier.*/dwc3_enable_susphy(&appledwc->dwc, true); ret = dwc3_host_init(&appledwc->dwc); if (ret) { dev_err(appledwc->dev, "Failed to initialize host, ret=%d\n", ret);phy_set_mode(appledwc->dwc.usb3_generic_phy[0], PHY_MODE_USB_HOST);@@ -268,7 +280,13 @@ static int dwc3_apple_init(struct dwc3_apple *appledwc, enum dwc3_apple_state st case DWC3_APPLE_DEVICE: appledwc->dwc.dr_mode = USB_DR_MODE_PERIPHERAL; dwc3_apple_set_ptrcap(appledwc, DWC3_GCTL_PRTCAP_DEVICE);
dwc3_apple_phy_set_mode(appledwc, PHY_MODE_USB_DEVICE);
/** This platform requires SUSPHY to be enabled here already in order to properly* configure the PHY and switch dwc3's PIPE interface to USB3 PHY. The USB2 PHY* has already been configured to the correct mode earlier.*/dwc3_enable_susphy(&appledwc->dwc, true); ret = dwc3_gadget_init(&appledwc->dwc); if (ret) { dev_err(appledwc->dev, "Failed to initialize gadget, ret=%d\n", ret);phy_set_mode(appledwc->dwc.usb3_generic_phy[0], PHY_MODE_USB_DEVICE);
Tested-by: Janne Grunau j@jannau.net Reviewed-by: Janne Grunau j@jannau.net
Janne
linux-stable-mirror@lists.linaro.org