Linux USB
 help / color / mirror / Atom feed
From: sashiko-bot@kernel.org
To: "Fabrice Gasnier" <fabrice.gasnier@foss.st.com>
Cc: linux-usb@vger.kernel.org
Subject: Re: [PATCH v4 1/6] usb: dwc2: fix recovery after PM domain off
Date: Fri, 13 Nov 2026 19:30:24 +0000	[thread overview]
Message-ID: <sashiko-outbox-213551@kernel.org> (raw)
In-Reply-To: <20261113-dwc2-low-power-fixes-stm32-v4-1-08a8c962a09e@foss.st.com>

Thank you for your contribution! Sashiko AI review found 2 potential
issues to consider:
- [Medium] usb: dwc2: premature IRQ enable and incomplete cleanup in
  device resume
- [Low] usb: dwc2: stale commit message note on HPRT0_CONNSTS in host
  resume
--

commit 3fc088190fe8f18420841b384b9d96f416a409a2
Author: Fabrice Gasnier <fabrice.gasnier@foss.st.com>

usb: dwc2: fix recovery after PM domain off

This commit removes the critical register backup from dwc2_suspend() and
instead reinitializes the DWC2 controller, HCD, and gadget state during
dwc2_resume() when GUSBCFG.TOUTCAL indicates that registers were lost.

> Host resume observes HPRT0_CONNSTS cleared after register loss, so it
> does not skip the recovery path. Ordinary resume and USB-reset behavior
> remain unchanged.

[Severity: Low]
Is this note about HPRT0_CONNSTS still accurate?

dwc2_hcd_restore() now sets hsotg->lx_state = DWC2_L0 during platform
resume, so when _dwc2_hcd_resume() runs later it returns early at the
hsotg->lx_state != DWC2_L2 check before reading HPRT0:

drivers/usb/dwc2/hcd.c:_dwc2_hcd_resume() {
    ...
	if (hsotg->lx_state != DWC2_L2)
		goto unlock;

	hprt0 = dwc2_read_hprt0(hsotg);
    ...
	if (hprt0 & HPRT0_CONNSTS) {
		hsotg->lx_state = DWC2_L0;
		goto unlock;
	}
    ...
}

> diff --git a/drivers/usb/dwc2/gadget.c b/drivers/usb/dwc2/gadget.c
> index 6c8a9f05fff00..225442471aa95 100644
> --- a/drivers/usb/dwc2/gadget.c
> +++ b/drivers/usb/dwc2/gadget.c
> @@ -5200,11 +5200,12 @@ int dwc2_hsotg_suspend(struct dwc2_hsotg *hsotg)
>  	return 0;
>  }
>  
> -int dwc2_hsotg_resume(struct dwc2_hsotg *hsotg)
> +int dwc2_hsotg_resume(struct dwc2_hsotg *hsotg, bool need_restore)
>  {
>  	unsigned long flags;
> +	int ep;
>  
> -	if (hsotg->lx_state == DWC2_L2)
> +	if (!need_restore && hsotg->lx_state == DWC2_L2)
>  		return 0;
>  
>  	if (hsotg->driver) {
> @@ -5212,6 +5213,19 @@ int dwc2_hsotg_resume(struct dwc2_hsotg *hsotg)
>  			 hsotg->driver->driver.name);
>  
>  		spin_lock_irqsave(&hsotg->lock, flags);
> +		if (need_restore) {
> +			hsotg->hibernated = 0;
> +			hsotg->in_ppd = 0;
> +			hsotg->bus_suspended = false;
> +			dwc2_hsotg_disconnect(hsotg);
> +			for (ep = 1; ep < hsotg->num_of_eps; ep++) {
> +				if (hsotg->eps_in[ep])
> +					dwc2_hsotg_ep_disable(&hsotg->eps_in[ep]->ep);
> +				if (hsotg->eps_out[ep])
> +					dwc2_hsotg_ep_disable(&hsotg->eps_out[ep]->ep);
> +			}
> +			hsotg->gadget.speed = USB_SPEED_UNKNOWN;
> +		}
>  		dwc2_hsotg_core_init_disconnected(hsotg, false);
>  		if (hsotg->enabled) {
>  			/* Enable ACG feature in device mode,if supported */

[ ... ]

> diff --git a/drivers/usb/dwc2/platform.c b/drivers/usb/dwc2/platform.c
> index ef0d730770347..72214593f4312 100644
> --- a/drivers/usb/dwc2/platform.c
> +++ b/drivers/usb/dwc2/platform.c

[ ... ]

> @@ -786,8 +749,31 @@ static int __maybe_unused dwc2_resume(struct device *dev)
>  		dwc2_drd_resume(dwc2);
>  	}
>  
> +	/*
> +	 * During suspend it's possible that the power domain for the
> +	 * DWC2 controller is disabled and all register values get lost.
> +	 * In case the GUSBCFG register is not initialized, it's clear the
> +	 * registers must be restored. So invoke here the necessary init
> +	 * as done by dwc2_hcd_init() during probe.
> +	 */
> +	need_restore = !(dwc2_readl(dwc2, GUSBCFG) & GUSBCFG_TOUTCAL_MASK);
> +	if (need_restore) {
> +		if (dwc2->dr_mode != USB_DR_MODE_PERIPHERAL) {
> +			if (dwc2_is_device_mode(dwc2)) {
> +				ret = dwc2_core_init(dwc2, true);
> +				if (ret)
> +					return ret;
> +				dwc2_enable_global_interrupts(dwc2);
> +			} else {
> +				ret = dwc2_hcd_restore(dwc2);
> +				if (ret)
> +					return ret;
> +			}
> +		}
> +	}
> +
>  	if (dwc2_is_device_mode(dwc2))
> -		ret = dwc2_hsotg_resume(dwc2);
> +		ret = dwc2_hsotg_resume(dwc2, need_restore);
>  
>  	return ret;
>  }

[Severity: Medium]
Can enabling global interrupts here race with the state cleanup in
dwc2_hsotg_resume()?

dwc2_core_init() unmasks GINTMSK_COMMON (including GINTSTS_USBSUSP and
GINTSTS_WKUPINT), and dwc2_enable_global_interrupts() sets
GAHBCFG_GLBL_INTR_EN before dwc2_hsotg_resume() takes hsotg->lock and
clears hsotg->hibernated, hsotg->in_ppd, hsotg->bus_suspended, and
hsotg->connected.  In addition, dwc2_hsotg_disconnect() drops and
reacquires hsotg->lock before dwc2_hsotg_core_init_disconnected()
reinitializes the device registers:

dwc2_resume()
  dwc2_core_init(dwc2, true)
  dwc2_enable_global_interrupts(dwc2)
  <hardirq>
    dwc2_handle_common_intr()
      ...
        dwc2_handle_usb_suspend_intr()
          dwc2_gadget_enter_clock_gating()  <-- sees connected == 1, gates clk
  dwc2_hsotg_resume(dwc2, true)
    spin_lock_irqsave(&hsotg->lock, flags)
    ...
    dwc2_hsotg_ep_disable()
      dwc2_readl(hsotg, DIEPCTL(index))     <-- hangs with clock gated

If GINTSTS_USBSUSP fires while hsotg->connected is still 1 and DSTS_SUSPSTS
is set after reset, dwc2_handle_usb_suspend_intr() enters clock gating or
partial power down right before dwc2_hsotg_ep_disable() accesses endpoint
registers, hanging the platform.  Similarly, an early wakeup or common
interrupt while hsotg->hibernated or hsotg->in_ppd is still 1 will run the
hibernation or partial-power-down exit paths using stale register backups.

Also, unlike dwc2_hcd_restore(), the device-mode restore path does not
clear gr_backup.valid, dr_backup.valid, or hr_backup.valid, and if
hsotg->driver is NULL, dwc2_hsotg_resume() skips the need_restore cleanup
entirely, leaving hibernated, in_ppd, bus_suspended, and lx_state stale.

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20261113-dwc2-low-power-fixes-stm32-v4-0-08a8c962a09e@foss.st.com?part=1

  reply	other threads:[~2026-11-13 19:30 UTC|newest]

Thread overview: 11+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-11-13 18:47 [PATCH v4 0/6] usb: dwc2: fix low power issues found on stm32mp1 Fabrice Gasnier
2026-11-13 18:47 ` [PATCH v4 1/6] usb: dwc2: fix recovery after PM domain off Fabrice Gasnier
2026-11-13 19:30   ` sashiko-bot [this message]
2026-11-13 18:47 ` [PATCH v4 2/6] usb: dwc2: fix a possible interrupt storm in hcd resume Fabrice Gasnier
2026-11-13 19:34   ` sashiko-bot
2026-11-13 18:48 ` [PATCH v4 3/6] usb: dwc2: fix a possible interrupt storm on stm32mp1 Fabrice Gasnier
2026-11-13 19:43   ` sashiko-bot
2026-11-13 18:48 ` [PATCH v4 4/6] usb: dwc2: hcd: fix possible successive vbus supply enables Fabrice Gasnier
2026-11-13 19:49   ` sashiko-bot
2026-11-13 18:48 ` [PATCH v4 5/6] usb: dwc2: hcd: don't disable external vbus in system suspend Fabrice Gasnier
2026-11-13 18:48 ` [PATCH v4 6/6] usb: dwc2: platform: fix stm32mp15 detection logic power management Fabrice Gasnier

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=sashiko-outbox-213551@kernel.org \
    --to=sashiko-bot@kernel.org \
    --cc=fabrice.gasnier@foss.st.com \
    --cc=linux-usb@vger.kernel.org \
    --cc=sashiko-reviews@lists.linux.dev \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox