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
next prev parent reply other threads:[~2026-11-13 19:30 UTC|newest]
Thread overview: 13+ 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 20:00 ` sashiko-bot
2026-11-13 18:48 ` [PATCH v4 6/6] usb: dwc2: platform: fix stm32mp15 detection logic power management Fabrice Gasnier
2026-11-13 20:41 ` sashiko-bot
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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.