From: Thinh Nguyen <Thinh.Nguyen@synopsys.com>
To: Vladyslav Tymoshchuk <tvlad744@gmail.com>
Cc: Thinh Nguyen <Thinh.Nguyen@synopsys.com>,
Greg Kroah-Hartman <gregkh@linuxfoundation.org>,
Krishna Kurapati <krishna.kurapati@oss.qualcomm.com>,
Faisal Hassan <faisal.hassan@oss.qualcomm.com>,
Abel Vesa <abelvesa@kernel.org>,
Prashanth K <prashanth.k@oss.qualcomm.com>,
"linux-usb@vger.kernel.org" <linux-usb@vger.kernel.org>,
"linux-arm-msm@vger.kernel.org" <linux-arm-msm@vger.kernel.org>,
"linux-kernel@vger.kernel.org" <linux-kernel@vger.kernel.org>
Subject: Re: [PATCH v3] usb: dwc3: Notify the glue of role changes from the mode switch work
Date: Thu, 12 Nov 2026 01:59:10 +0000 [thread overview]
Message-ID: <avUdNnZ7GWREGQOp@vbox> (raw)
In-Reply-To: <20261111090122.134-1-tvlad744@gmail.com>
On Wed, Nov 11, 2026, Vladyslav Tymoshchuk wrote:
> dwc3_usb_role_switch_set() and dwc3_drd_notifier() call the glue's
> pre_set_role() synchronously, from the context of whoever changes the
> role, while the mode switch itself runs from drd_work on a freezable
> workqueue. Not every caller is frozen during system sleep: the UCSI
> connector work, for one, runs on the non-freezable system workqueue.
>
> On SM8750, when the USB cable is unplugged while the system is in
> s2idle, the UCSI connector work for the change runs before the devices
> have resumed. It switches the role to USB_ROLE_NONE, and
> dwc3_qcom_set_role_notifier() accesses QSCRATCH while the glue clocks
> are still off. The access does not complete and the SoC resets.
> pm_runtime_resume_and_get() does not prevent this: runtime PM is
> disabled at that point and the device is still marked active, so it
> returns success without resuming anything.
>
> Record the role and call pre_set_role() from __dwc3_set_mode() instead,
> so that the glue is not called while the system is suspended. The mode
> alone can't carry this: USB_ROLE_NONE maps to the default mode, so a
> switch between it and that role leaves the mode unchanged, and the glue
> still has to be told. Make the call before the checks that skip the
> mode switch, and only when a role was recorded, as the mode can also
> change without one from debugfs and from dwc3_setup_role_switch().
>
> Role changes that come in while the work is frozen collapse into the
> last one, which is the state the hardware has to end up in.
>
> Fixes: 21188e8d6d75 ("usb: dwc3: qcom: Implement glue callbacks to facilitate runtime suspend")
> Cc: stable@vger.kernel.org
> Suggested-by: Thinh Nguyen <Thinh.Nguyen@synopsys.com>
> Signed-off-by: Vladyslav Tymoshchuk <tvlad744@gmail.com>
> ---
> Changes in v3:
> - Fix this in the dwc3 core instead of in the Qualcomm glue. The code is
> Thinh's change from [1]; I only updated the two comments that went
> stale with it. All glue drivers benefit, and dwc3-qcom needs no
> deferral or lock of its own.
> - The role is recorded in struct dwc3, as the mode alone can't tell
> USB_ROLE_NONE from the role it maps to (Prashanth, [2]).
> - The extcon path goes through the same work.
> - Drop Krishna's Tested-by, the code is different now.
> - Link to v2: https://urldefense.com/v3/__https://lore.kernel.org/all/20261022224220.480-1-tvlad744@gmail.com/__;!!A4F2R9G_pg!eKOqFW5EvS63b73MwgODm6dZ1aOBc_KIiaxbf0F-4cQcKocNHs2hwe3B0uJT4CH03Fa3_SgHFx7g2fq13Nmtxw$
>
> Tested on OnePlus Pad 3 (SM8750, UCSI over pmic_glink) with v7.3-rc5
> plus board patches, details in [3]. Without the change, unplugging the
> cable in s2idle resets the SoC. With it there was no reset in five
> suspend runs in device mode (unplug, plug in, both, cable left in) and
> five in host mode (USB stick left in or unplugged). The host-mode runs
> were on a kernel that also keeps the USB GDSCs in retention [4]. The
> comment updates were not in the tested kernel.
>
> I used an AI assistant (Claude) for the analysis and to draft the
> description; I reviewed the result and ran the tests on my device.
>
> [1] https://urldefense.com/v3/__https://lore.kernel.org/all/auu9hx_Xnjbi3WkB@vbox/__;!!A4F2R9G_pg!eKOqFW5EvS63b73MwgODm6dZ1aOBc_KIiaxbf0F-4cQcKocNHs2hwe3B0uJT4CH03Fa3_SgHFx7g2fqItiPPZQ$
> [2] https://urldefense.com/v3/__https://lore.kernel.org/all/f9d27fd8-cf33-4e6a-a9fc-6770a933cfb3@oss.qualcomm.com/__;!!A4F2R9G_pg!eKOqFW5EvS63b73MwgODm6dZ1aOBc_KIiaxbf0F-4cQcKocNHs2hwe3B0uJT4CH03Fa3_SgHFx7g2frebDF9aw$
> [3] https://urldefense.com/v3/__https://lore.kernel.org/all/20261110172148.1640-1-tvlad744@gmail.com/__;!!A4F2R9G_pg!eKOqFW5EvS63b73MwgODm6dZ1aOBc_KIiaxbf0F-4cQcKocNHs2hwe3B0uJT4CH03Fa3_SgHFx7g2folWmClAQ$
> [4] https://urldefense.com/v3/__https://lore.kernel.org/all/20261110213346.1359-1-tvlad744@gmail.com/__;!!A4F2R9G_pg!eKOqFW5EvS63b73MwgODm6dZ1aOBc_KIiaxbf0F-4cQcKocNHs2hwe3B0uJT4CH03Fa3_SgHFx7g2frSDJpOFQ$
>
> drivers/usb/dwc3/core.c | 25 ++++++++++++++++++++++++-
> drivers/usb/dwc3/core.h | 8 +++++++-
> drivers/usb/dwc3/drd.c | 6 ++----
> drivers/usb/dwc3/dwc3-qcom.c | 5 +----
> 4 files changed, 34 insertions(+), 10 deletions(-)
>
> diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c
> index fd5c2cd36c59..99a39c828543 100644
> --- a/drivers/usb/dwc3/core.c
> +++ b/drivers/usb/dwc3/core.c
> @@ -169,15 +169,23 @@ static void __dwc3_set_mode(struct work_struct *work)
> int ret;
> u32 reg;
> u32 desired_dr_role;
> + enum usb_role requested_role;
> + bool notify_glue;
> int i;
>
> mutex_lock(&dwc->mutex);
> spin_lock_irqsave(&dwc->lock, flags);
> desired_dr_role = dwc->desired_dr_role;
> + requested_role = dwc->requested_role;
> + notify_glue = dwc->requested_role_pending;
> + dwc->requested_role_pending = false;
> spin_unlock_irqrestore(&dwc->lock, flags);
>
> pm_runtime_get_sync(dwc->dev);
>
> + if (notify_glue)
> + dwc3_pre_set_role(dwc, requested_role);
> +
> if (dwc->current_dr_role == DWC3_GCTL_PRTCAP_OTG)
> dwc3_otg_update(dwc, 0);
>
> @@ -287,7 +295,8 @@ static void __dwc3_set_mode(struct work_struct *work)
> mutex_unlock(&dwc->mutex);
> }
>
> -void dwc3_set_mode(struct dwc3 *dwc, u32 mode)
> +static void __dwc3_queue_mode(struct dwc3 *dwc, u32 mode, enum usb_role role,
> + bool notify_glue)
> {
> unsigned long flags;
>
> @@ -296,11 +305,25 @@ void dwc3_set_mode(struct dwc3 *dwc, u32 mode)
>
> spin_lock_irqsave(&dwc->lock, flags);
> dwc->desired_dr_role = mode;
> + if (notify_glue) {
> + dwc->requested_role = role;
> + dwc->requested_role_pending = true;
> + }
> spin_unlock_irqrestore(&dwc->lock, flags);
>
> queue_work(system_freezable_wq, &dwc->drd_work);
> }
>
> +void dwc3_set_mode(struct dwc3 *dwc, u32 mode)
> +{
> + __dwc3_queue_mode(dwc, mode, USB_ROLE_NONE, false);
> +}
> +
> +void dwc3_set_role(struct dwc3 *dwc, u32 mode, enum usb_role role)
> +{
> + __dwc3_queue_mode(dwc, mode, role, true);
> +}
> +
> u32 dwc3_core_fifo_space(struct dwc3_ep *dep, u8 type)
> {
> struct dwc3 *dwc = dep->dwc;
> diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h
> index f3dabb37f806..dc7cd751ed4d 100644
> --- a/drivers/usb/dwc3/core.h
> +++ b/drivers/usb/dwc3/core.h
> @@ -1000,7 +1000,8 @@ struct dwc3_scratchpad_array {
> /**
> * struct dwc3_glue_ops - The ops indicate the notifications that
> * need to be passed on to glue layer
> - * @pre_set_role: Notify glue of role switch notifications
> + * @pre_set_role: Notify glue of role switch notifications, called from
> + * drd_work so that it does not run during system sleep
> * @pre_run_stop: Notify run stop enable/disable information to glue
> */
> struct dwc3_glue_ops {
> @@ -1058,6 +1059,8 @@ struct dwc3_glue_ops {
> * @dr_mode: requested mode of operation
> * @current_dr_role: current role of operation when in dual-role mode
> * @desired_dr_role: desired role of operation when in dual-role mode
> + * @requested_role: role requested
> + * @requested_role_pending: @requested_role is yet to be passed to the glue
> * @edev: extcon handle
> * @edev_nb: extcon notifier
> * @hsphy_mode: UTMI phy mode, one of following:
> @@ -1261,6 +1264,8 @@ struct dwc3 {
> enum usb_dr_mode dr_mode;
> u32 current_dr_role;
> u32 desired_dr_role;
> + enum usb_role requested_role;
> + bool requested_role_pending;
> struct extcon_dev *edev;
> struct notifier_block edev_nb;
> enum usb_phy_interface hsphy_mode;
> @@ -1614,6 +1619,7 @@ struct dwc3_gadget_ep_cmd_params {
> /* prototypes */
> void dwc3_set_prtcap(struct dwc3 *dwc, u32 mode, bool ignore_susphy);
> void dwc3_set_mode(struct dwc3 *dwc, u32 mode);
> +void dwc3_set_role(struct dwc3 *dwc, u32 mode, enum usb_role role);
> u32 dwc3_core_fifo_space(struct dwc3_ep *dep, u8 type);
>
> #define DWC3_IP_IS(_ip) \
> diff --git a/drivers/usb/dwc3/drd.c b/drivers/usb/dwc3/drd.c
> index f3e37d383627..55036466ec7a 100644
> --- a/drivers/usb/dwc3/drd.c
> +++ b/drivers/usb/dwc3/drd.c
> @@ -439,8 +439,7 @@ static int dwc3_drd_notifier(struct notifier_block *nb,
> enum usb_role role = mode == DWC3_GCTL_PRTCAP_HOST ?
> USB_ROLE_HOST : USB_ROLE_DEVICE;
>
> - dwc3_pre_set_role(dwc, role);
> - dwc3_set_mode(dwc, mode);
> + dwc3_set_role(dwc, mode, role);
>
> return NOTIFY_DONE;
> }
> @@ -468,8 +467,7 @@ static int dwc3_usb_role_switch_set(struct usb_role_switch *sw,
> break;
> }
>
> - dwc3_pre_set_role(dwc, role);
> - dwc3_set_mode(dwc, mode);
> + dwc3_set_role(dwc, mode, role);
> return 0;
> }
>
> diff --git a/drivers/usb/dwc3/dwc3-qcom.c b/drivers/usb/dwc3/dwc3-qcom.c
> index ac68b4218b56..2e263b378966 100644
> --- a/drivers/usb/dwc3/dwc3-qcom.c
> +++ b/drivers/usb/dwc3/dwc3-qcom.c
> @@ -578,10 +578,7 @@ static void dwc3_qcom_set_role_notifier(struct dwc3 *dwc, enum usb_role next_rol
> pm_runtime_mark_last_busy(qcom->dev);
> pm_runtime_put_sync(qcom->dev);
>
> - /*
> - * Current role changes via usb_role_switch_set_role callback protected
> - * internally by mutex lock.
> - */
> + /* Role changes are serialized by dwc->mutex in drd_work */
> qcom->current_role = next_role;
> }
>
>
> base-commit: 6628e7d4c6b25c8d18f1345e3a20d21829aae7e4
Acked-by: Thinh Nguyen <Thinh.Nguyen@synopsys.com>
Thanks,
Thinh
prev parent reply other threads:[~2026-11-12 1:59 UTC|newest]
Thread overview: 3+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-11-11 9:01 [PATCH v3] usb: dwc3: Notify the glue of role changes from the mode switch work Vladyslav Tymoshchuk
2026-11-11 9:18 ` sashiko-bot
2026-11-12 1:59 ` Thinh Nguyen [this message]
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=avUdNnZ7GWREGQOp@vbox \
--to=thinh.nguyen@synopsys.com \
--cc=abelvesa@kernel.org \
--cc=faisal.hassan@oss.qualcomm.com \
--cc=gregkh@linuxfoundation.org \
--cc=krishna.kurapati@oss.qualcomm.com \
--cc=linux-arm-msm@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-usb@vger.kernel.org \
--cc=prashanth.k@oss.qualcomm.com \
--cc=tvlad744@gmail.com \
/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.