External VBUS supply may have already been enabled, before entering dwc2_hcd_reinit (e.g. the controller is in host mode, without any device plugged). This results in vbus enable count to increase every time this occurs. As consequence, dwc2_vbus_supply_exit won't disable vbus upon suspend request. Fix this by reading PWR bit before and after dwc2_hcd_reinit() call. Fixes: cd7cd0e6cedf ("usb: dwc2: fix unbalanced use of external vbus-supply") Cc: stable@vger.kernel.org Signed-off-by: Fabrice Gasnier --- Changes in v2: - Add a vbus_was_off boolean to check explicitly an off-on transition on port power, to enable vbus supply, instead of relying an XOR check that would be symmetric, e.g. wrongly enable it in case it gets switched off (1 to 0) as reported by Sashiko. --- drivers/usb/dwc2/hcd.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/usb/dwc2/hcd.c b/drivers/usb/dwc2/hcd.c index 28cde15e0cca..cae9d7e13d47 100644 --- a/drivers/usb/dwc2/hcd.c +++ b/drivers/usb/dwc2/hcd.c @@ -4249,6 +4249,7 @@ static int _dwc2_hcd_start(struct usb_hcd *hcd) struct dwc2_hsotg *hsotg = dwc2_hcd_to_hsotg(hcd); struct usb_bus *bus = hcd_to_bus(hcd); unsigned long flags; + bool vbus_was_off; u32 hprt0; int ret; @@ -4264,11 +4265,13 @@ static int _dwc2_hcd_start(struct usb_hcd *hcd) return 0; /* why 0 ?? */ } + vbus_was_off = !(dwc2_read_hprt0(hsotg) & HPRT0_PWR); + dwc2_hcd_reinit(hsotg); hprt0 = dwc2_read_hprt0(hsotg); /* Has vbus power been turned on in dwc2_core_host_init ? */ - if (hprt0 & HPRT0_PWR) { + if (vbus_was_off && (hprt0 & HPRT0_PWR)) { /* Enable external vbus supply before resuming root hub */ spin_unlock_irqrestore(&hsotg->lock, flags); ret = dwc2_vbus_supply_init(hsotg); -- 2.43.0