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 v3 1/6] usb: dwc2: fix recovery after PM domain off
Date: Fri, 06 Nov 2026 18:16:22 +0000	[thread overview]
Message-ID: <sashiko-outbox-202003@kernel.org> (raw)
In-Reply-To: <20261106-dwc2-low-power-fixes-stm32-v3-1-68d2a0e9bab6@foss.st.com>

Thank you for your contribution! Sashiko AI review found 2 potential
issues to consider:
- [High] usb: dwc2: incomplete gadget state cleanup in
  dwc2_hsotg_resume()
- [High] usb: dwc2: incomplete host mode recovery in dwc2_resume() after
  PM domain off
--

commit 8e3990c7f49dc88c8bdad98a994d532a0d203385
Author: Fabrice Gasnier <fabrice.gasnier@foss.st.com>

usb: dwc2: fix recovery after PM domain off

This commit removes the critical register backup/restore calls in
dwc2_suspend() and dwc2_resume(), and instead re-initializes the core on
resume when GUSBCFG_TOUTCAL indicates that the power domain was turned off.

> diff --git a/drivers/usb/dwc2/gadget.c b/drivers/usb/dwc2/gadget.c
> index 6c8a9f05fff00..99463e65d5b4b 100644
> --- a/drivers/usb/dwc2/gadget.c
> +++ b/drivers/usb/dwc2/gadget.c
> @@ -5200,11 +5200,11 @@ 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;
>  
> -	if (hsotg->lx_state == DWC2_L2)
> +	if (!need_restore && hsotg->lx_state == DWC2_L2)
>  		return 0;
>  
>  	if (hsotg->driver) {

[Severity: High]
When the host has already suspended the bus before system suspend
(hsotg->lx_state == DWC2_L2), does resuming with need_restore == true leave
the gadget's software state out of sync with the reset hardware?

Because hsotg->lx_state != DWC2_L0 during dwc2_hsotg_suspend(), the suspend
path returns early without calling dwc2_hsotg_disconnect() or disabling
non-EP0 endpoints, and the L2 entry path has set hsotg->bus_suspended,
hsotg->in_ppd, or hsotg->hibernated.  On resume, dwc2_hsotg_resume() calls
dwc2_hsotg_core_init_disconnected(hsotg, false), which resets the core and
sets hsotg->lx_state = DWC2_L0, but only disables non-EP0 endpoints when
is_usb_reset is true:

drivers/usb/dwc2/gadget.c:dwc2_hsotg_core_init_disconnected() {
    ...
	if (!is_usb_reset) {
		if (dwc2_core_reset(hsotg, true))
			return;
	} else {
		/* all endpoints should be shutdown */
		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);
		}
	}
    ...
}

This wipes the hardware endpoint configuration and hsotg->fifo_map while
leaving non-EP0 endpoints enabled with queued requests and leaving the
low-power flags set (for example, stale hsotg->hibernated == 1 causes
dwc2_handle_common_intr() to ignore interrupts, and stale hsotg->in_ppd == 1
causes dwc2_hsotg_pullup() to restore pre-suspend register backups).

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

[ ... ]

> @@ -786,8 +749,25 @@ 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) {
> +			ret = dwc2_core_init(dwc2, true);
> +			if (ret)
> +				return ret;
> +			dwc2_enable_global_interrupts(dwc2);
> +		}
> +	}

[Severity: High]
Does calling only dwc2_core_init() and dwc2_enable_global_interrupts() here
break host-mode recovery when hsotg->params.power_down is
DWC2_POWER_DOWN_PARAM_PARTIAL or DWC2_POWER_DOWN_PARAM_HIBERNATION?

dwc2_core_init() only initializes the common core registers and relies on
_dwc2_hcd_resume() to reinitialize the host registers (HCFG, FIFOs,
HPRT0_PWR, host GINTMSK, and GUSBCFG_TOUTCAL).  However, _dwc2_hcd_resume()
only calls dwc2_hcd_reinit() for DWC2_POWER_DOWN_PARAM_NONE:

drivers/usb/dwc2/hcd.c:_dwc2_hcd_resume() {
    ...
	switch (hsotg->params.power_down) {
	case DWC2_POWER_DOWN_PARAM_PARTIAL:
		ret = dwc2_exit_partial_power_down(hsotg, 0, true);
    ...
		break;
	case DWC2_POWER_DOWN_PARAM_HIBERNATION:
		ret = dwc2_exit_hibernation(hsotg, 0, 0, 1);
    ...
}

When no device was connected during _dwc2_hcd_suspend() (!(dwc2_read_hprt0()
& HPRT0_CONNSTS)), _dwc2_hcd_suspend() jumps to skip_power_saving without
backing up registers, so dwc2_exit_partial_power_down() or
dwc2_exit_hibernation() fails with -EINVAL on gr->valid and hcd_bus_resume()
marks the HCD dead via usb_hc_died().  Even when a device was connected in
DWC2_POWER_DOWN_PARAM_PARTIAL mode, on platforms where GOTGCTL_CURMODE_HOST
is not set in GOTGCTL, dwc2_exit_partial_power_down() takes the device
branch and fails with -EINVAL on dr_backup.valid.  Finally, on an OTG
controller that was in device mode during _dwc2_hcd_suspend() (leaving
lx_state != DWC2_L2) and resumes in host mode, _dwc2_hcd_resume() returns
early at hsotg->lx_state != DWC2_L2 without initializing the host registers.

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20261106-dwc2-low-power-fixes-stm32-v3-0-68d2a0e9bab6@foss.st.com?part=1

  reply	other threads:[~2026-11-06 18:16 UTC|newest]

Thread overview: 13+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-11-06 17:38 [PATCH v3 0/6] usb: dwc2: fix low power issues found on stm32mp1 Fabrice Gasnier
2026-11-06 17:38 ` [PATCH v3 1/6] usb: dwc2: fix recovery after PM domain off Fabrice Gasnier
2026-11-06 18:16   ` sashiko-bot [this message]
2026-11-06 17:38 ` [PATCH v3 2/6] usb: dwc2: fix a possible interrupt storm in hcd resume Fabrice Gasnier
2026-11-06 17:57   ` sashiko-bot
2026-11-06 17:38 ` [PATCH v3 3/6] usb: dwc2: fix a possible interrupt storm on stm32mp1 Fabrice Gasnier
2026-11-06 17:45   ` sashiko-bot
2026-11-06 17:38 ` [PATCH v3 4/6] usb: dwc2: hcd: fix possible successive vbus supply enables Fabrice Gasnier
2026-11-06 18:08   ` sashiko-bot
2026-11-06 17:38 ` [PATCH v3 5/6] usb: dwc2: hcd: don't disable/enable external vbus by system suspend/resume Fabrice Gasnier
2026-11-06 17:57   ` sashiko-bot
2026-11-06 17:38 ` [PATCH v3 6/6] usb: dwc2: platform: fix stm32mp15 detection logic power management Fabrice Gasnier
2026-11-06 18:05   ` 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-202003@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