| From 1e8afe99fd28f8072d9eb8ed55f5fbb2be929be8 Mon Sep 17 00:00:00 2001 |
| From: Sasha Levin <sashal@kernel.org> |
| Date: Tue, 4 Nov 2025 08:25:03 +0800 |
| Subject: usb: dwc2: fix hang during suspend if set as peripheral |
| |
| From: Jisheng Zhang <jszhang@kernel.org> |
| |
| [ Upstream commit 2b94b054ac4974ad2f89f7f7461840c851933adb ] |
| |
| dwc2 on most platforms needs phy controller, clock and power supply. |
| All of them must be enabled/activated to properly operate. If dwc2 |
| is configured as peripheral mode, then all the above three hardware |
| resources are disabled at the end of the probe: |
| |
| /* Gadget code manages lowlevel hw on its own */ |
| if (hsotg->dr_mode == USB_DR_MODE_PERIPHERAL) |
| dwc2_lowlevel_hw_disable(hsotg); |
| |
| But the dwc2_suspend() tries to read the dwc2's reg to check whether |
| is_device_mode or not, this would result in hang during suspend if dwc2 |
| is configured as peripheral mode. |
| |
| Fix this hang by bypassing suspend/resume if lowlevel hw isn't |
| enabled. |
| |
| Fixes: 09a75e857790 ("usb: dwc2: refactor common low-level hw code to platform.c") |
| Signed-off-by: Jisheng Zhang <jszhang@kernel.org> |
| Link: https://patch.msgid.link/20251104002503.17158-3-jszhang@kernel.org |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| Signed-off-by: Sasha Levin <sashal@kernel.org> |
| --- |
| drivers/usb/dwc2/platform.c | 9 ++++++++- |
| 1 file changed, 8 insertions(+), 1 deletion(-) |
| |
| diff --git a/drivers/usb/dwc2/platform.c b/drivers/usb/dwc2/platform.c |
| index fad6f68f86bd6..e80982c817d7d 100644 |
| --- a/drivers/usb/dwc2/platform.c |
| +++ b/drivers/usb/dwc2/platform.c |
| @@ -649,9 +649,13 @@ static int dwc2_driver_probe(struct platform_device *dev) |
| static int __maybe_unused dwc2_suspend(struct device *dev) |
| { |
| struct dwc2_hsotg *dwc2 = dev_get_drvdata(dev); |
| - bool is_device_mode = dwc2_is_device_mode(dwc2); |
| + bool is_device_mode; |
| int ret = 0; |
| |
| + if (!dwc2->ll_hw_enabled) |
| + return 0; |
| + |
| + is_device_mode = dwc2_is_device_mode(dwc2); |
| if (is_device_mode) |
| dwc2_hsotg_suspend(dwc2); |
| |
| @@ -702,6 +706,9 @@ static int __maybe_unused dwc2_resume(struct device *dev) |
| struct dwc2_hsotg *dwc2 = dev_get_drvdata(dev); |
| int ret = 0; |
| |
| + if (!dwc2->ll_hw_enabled) |
| + return 0; |
| + |
| if (dwc2->phy_off_for_suspend && dwc2->ll_hw_enabled) { |
| ret = __dwc2_lowlevel_hw_enable(dwc2); |
| if (ret) |
| -- |
| 2.51.0 |
| |