mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
From: Filippo Falezza <effeffe99@gmail.com>
To: Thinh Nguyen <Thinh.Nguyen@synopsys.com>,
	 Greg Kroah-Hartman <gregkh@linuxfoundation.org>,
	 Manu Gautam <mgautam@codeaurora.org>,
	 Felipe Balbi <felipe.balbi@linux.intel.com>
Cc: Bjorn Andersson <andersson@kernel.org>,
	linux-arm-msm@vger.kernel.org,  linux-usb@vger.kernel.org,
	linux-kernel@vger.kernel.org,
	 Filippo Falezza <effeffe99@gmail.com>
Subject: [PATCH] usb: dwc3: qcom: Restore UTMI PIPE clock on resume
Date: Tue, 27 Oct 2026 01:21:42 +0000	[thread overview]
Message-ID: <20261027-dwc3-utmi-resume-v1-1-ca9f0aa65783@gmail.com> (raw)

Boards whose controller has no SuperSpeed PHY set
qcom,select-utmi-as-pipe-clk, and the glue then programs QSCRATCH
GENERAL_CFG so that the core's PIPE clock domain runs from the UTMI
clock. This is only done at probe.

When the SoC reaches its deepest state in suspend-to-idle (CX power
collapse), the QSCRATCH registers lose their contents. On resume the
core is reinitialised with its PIPE domain unclocked, and the gadget
does not come back:

  dwc3-qcom a600000.usb: failed to enable ep0out

The link stays down until the driver is unbound and bound again.

Remember the property at probe and select the UTMI clock again in
dwc3_qcom_resume(), after the clocks and interconnect paths are enabled
and before the core is resumed. Move dwc3_qcom_select_utmi_clk() up so
that it is defined before its new caller.

Fixes: a4333c3a6ba9 ("usb: dwc3: Add Qualcomm DWC3 glue driver")
Signed-off-by: Filippo Falezza <effeffe99@gmail.com>
---
Hello there,

this fixes USB peripheral mode after suspend on a Motorola Moto G 5G
Plus (SM7250), a phone with a USB 2.0-only port that I'm bringing up on
mainline. Its controller needs qcom,select-utmi-as-pipe-clk. The first
s2idle that reached CX power collapse broke the gadget: "failed to
enable ep0out" on resume, and USB networking stayed down until I
rebound dwc3-qcom by hand. Rebinding works because probe programs
GENERAL_CFG again, which is what this patch does in the resume path.

With the patch, the gadget (NCM plus ACM) reconnects after s2idle, and
the SSH session over the cable that I use for testing survives
suspend; the phone has run with it on linux-next since 11 October.
The other boards that use
qcom,select-utmi-as-pipe-clk should benefit as well, but I have only
tested this one, in peripheral mode. The legacy glue driver
(dwc3-qcom-legacy.c) has the same probe-only programming; I left it
alone because I can't test it.

The patch is on top of usb-linus (6b4b543fa187) and builds with W=1 for
arm64.

I wrote this patch and drafted this note with the help of an AI coding
assistant (Claude Code). The testing above is on the phone.

Thanks,
Filippo
---
 drivers/usb/dwc3/dwc3-qcom.c | 45 +++++++++++++++++++++++++++-----------------
 1 file changed, 28 insertions(+), 17 deletions(-)

diff --git a/drivers/usb/dwc3/dwc3-qcom.c b/drivers/usb/dwc3/dwc3-qcom.c
index ac68b4218b56..6334ca7ba30f 100644
--- a/drivers/usb/dwc3/dwc3-qcom.c
+++ b/drivers/usb/dwc3/dwc3-qcom.c
@@ -81,6 +81,7 @@ struct dwc3_qcom {
 	enum usb_dr_mode	mode;
 	bool			is_suspended;
 	bool			pm_suspended;
+	bool			utmi_as_pipe_clk;
 	struct icc_path		*icc_path_ddr;
 	struct icc_path		*icc_path_apps;
 
@@ -335,6 +336,23 @@ static void dwc3_qcom_enable_interrupts(struct dwc3_qcom *qcom)
 		dwc3_qcom_enable_port_interrupts(&qcom->ports[i]);
 }
 
+static void dwc3_qcom_select_utmi_clk(struct dwc3_qcom *qcom)
+{
+	/* Configure dwc3 to use UTMI clock as PIPE clock not present */
+	dwc3_qcom_setbits(qcom->qscratch_base, QSCRATCH_GENERAL_CFG,
+			  PIPE_UTMI_CLK_DIS);
+
+	usleep_range(100, 1000);
+
+	dwc3_qcom_setbits(qcom->qscratch_base, QSCRATCH_GENERAL_CFG,
+			  PIPE_UTMI_CLK_SEL | PIPE3_PHYSTATUS_SW);
+
+	usleep_range(100, 1000);
+
+	dwc3_qcom_clrbits(qcom->qscratch_base, QSCRATCH_GENERAL_CFG,
+			  PIPE_UTMI_CLK_DIS);
+}
+
 static int dwc3_qcom_suspend(struct dwc3_qcom *qcom, bool wakeup)
 {
 	u32 val;
@@ -388,6 +406,15 @@ static int dwc3_qcom_resume(struct dwc3_qcom *qcom, bool wakeup)
 	if (ret)
 		dev_warn(qcom->dev, "failed to enable interconnect: %d\n", ret);
 
+	/*
+	 * The QSCRATCH registers lose their state when the controller's power
+	 * domain is collapsed during system suspend: select the UTMI clock
+	 * for the PIPE domain again, or the core can't run without a PIPE
+	 * clock ("failed to enable ep0out").
+	 */
+	if (qcom->utmi_as_pipe_clk)
+		dwc3_qcom_select_utmi_clk(qcom);
+
 	/* Clear existing events from PHY related to L2 in/out */
 	for (i = 0; i < qcom->num_ports; i++) {
 		dwc3_qcom_setbits(qcom->qscratch_base,
@@ -419,23 +446,6 @@ static irqreturn_t qcom_dwc3_resume_irq(int irq, void *data)
 	return IRQ_HANDLED;
 }
 
-static void dwc3_qcom_select_utmi_clk(struct dwc3_qcom *qcom)
-{
-	/* Configure dwc3 to use UTMI clock as PIPE clock not present */
-	dwc3_qcom_setbits(qcom->qscratch_base, QSCRATCH_GENERAL_CFG,
-			  PIPE_UTMI_CLK_DIS);
-
-	usleep_range(100, 1000);
-
-	dwc3_qcom_setbits(qcom->qscratch_base, QSCRATCH_GENERAL_CFG,
-			  PIPE_UTMI_CLK_SEL | PIPE3_PHYSTATUS_SW);
-
-	usleep_range(100, 1000);
-
-	dwc3_qcom_clrbits(qcom->qscratch_base, QSCRATCH_GENERAL_CFG,
-			  PIPE_UTMI_CLK_DIS);
-}
-
 static int dwc3_qcom_request_irq(struct dwc3_qcom *qcom, int irq,
 				 const char *name)
 {
@@ -680,6 +690,7 @@ static int dwc3_qcom_probe(struct platform_device *pdev)
 	 */
 	ignore_pipe_clk = device_property_read_bool(dev,
 				"qcom,select-utmi-as-pipe-clk");
+	qcom->utmi_as_pipe_clk = ignore_pipe_clk;
 	if (ignore_pipe_clk)
 		dwc3_qcom_select_utmi_clk(qcom);
 

---
base-commit: 6b4b543fa18757297ac756a6fe6e64f506b7844a
change-id: 20261027-dwc3-utmi-resume-5ff551b1ed1d

Best regards,
--  
Filippo Falezza <effeffe99@gmail.com>


             reply	other threads:[~2026-10-27  1:21 UTC|newest]

Thread overview: 3+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-10-27  1:21 Filippo Falezza [this message]
2026-10-27  5:01 ` Prashanth K
2026-10-27 11:06 ` Dmitry Baryshkov

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=20261027-dwc3-utmi-resume-v1-1-ca9f0aa65783@gmail.com \
    --to=effeffe99@gmail.com \
    --cc=Thinh.Nguyen@synopsys.com \
    --cc=andersson@kernel.org \
    --cc=felipe.balbi@linux.intel.com \
    --cc=gregkh@linuxfoundation.org \
    --cc=linux-arm-msm@vger.kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-usb@vger.kernel.org \
    --cc=mgautam@codeaurora.org \
    /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

all inboxes | Powered by JetHome®