Oliver Neukum b5c53d
From c8540870af4ce6ddeb27a7bb5498b75fb29b643c Mon Sep 17 00:00:00 2001
Oliver Neukum b5c53d
From: Roger Quadros <rogerq@kernel.org>
Oliver Neukum b5c53d
Date: Wed, 3 May 2023 14:00:48 +0300
Oliver Neukum b5c53d
Subject: [PATCH] usb: dwc3: gadget: Improve dwc3_gadget_suspend() and
Oliver Neukum b5c53d
 dwc3_gadget_resume()
Oliver Neukum b5c53d
Git-commit: c8540870af4ce6ddeb27a7bb5498b75fb29b643c
Oliver Neukum b5c53d
References: git-fixes
Oliver Neukum b5c53d
Patch-mainline: v6.4-rc3
Oliver Neukum b5c53d
Oliver Neukum b5c53d
Prevent -ETIMEDOUT error on .suspend().
Oliver Neukum b5c53d
e.g. If gadget driver is loaded and we are connected to a USB host,
Oliver Neukum b5c53d
all transfers must be stopped before stopping the controller else
Oliver Neukum b5c53d
we will not get a clean stop i.e. dwc3_gadget_run_stop() will take
Oliver Neukum b5c53d
several seconds to complete and will return -ETIMEDOUT.
Oliver Neukum b5c53d
Oliver Neukum b5c53d
Handle error cases properly in dwc3_gadget_suspend().
Oliver Neukum b5c53d
Simplify dwc3_gadget_resume() by using the introduced helper function.
Oliver Neukum b5c53d
Oliver Neukum b5c53d
Fixes: 9f8a67b65a49 ("usb: dwc3: gadget: fix gadget suspend/resume")
Oliver Neukum b5c53d
Cc: stable@vger.kernel.org
Oliver Neukum b5c53d
Suggested-by: Thinh Nguyen <Thinh.Nguyen@synopsys.com>
Oliver Neukum b5c53d
Signed-off-by: Roger Quadros <rogerq@kernel.org>
Oliver Neukum b5c53d
Acked-by: Thinh Nguyen <Thinh.Nguyen@synopsys.com>
Oliver Neukum b5c53d
Link: https://lore.kernel.org/r/20230503110048.30617-1-rogerq@kernel.org
Oliver Neukum b5c53d
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Oliver Neukum b5c53d
Signed-off-by: Oliver Neukum <oneukum@suse.com>
Oliver Neukum b5c53d
---
Oliver Neukum b5c53d
 drivers/usb/dwc3/gadget.c |   50 ++++++++++++++++++++++++++++++++--------------
Oliver Neukum b5c53d
 1 file changed, 35 insertions(+), 15 deletions(-)
Oliver Neukum b5c53d
Oliver Neukum b5c53d
--- a/drivers/usb/dwc3/gadget.c
Oliver Neukum b5c53d
+++ b/drivers/usb/dwc3/gadget.c
Oliver Neukum b5c53d
@@ -2352,6 +2352,21 @@ static int dwc3_gadget_soft_disconnect(s
Oliver Neukum b5c53d
 	return dwc3_gadget_run_stop(dwc, false, false);
Oliver Neukum b5c53d
 }
Oliver Neukum b5c53d
 
Oliver Neukum b5c53d
+static int dwc3_gadget_soft_connect(struct dwc3 *dwc)
Oliver Neukum b5c53d
+{
Oliver Neukum b5c53d
+	/*
Oliver Neukum b5c53d
+	 * In the Synopsys DWC_usb31 1.90a programming guide section
Oliver Neukum b5c53d
+	 * 4.1.9, it specifies that for a reconnect after a
Oliver Neukum b5c53d
+	 * device-initiated disconnect requires a core soft reset
Oliver Neukum b5c53d
+	 * (DCTL.CSftRst) before enabling the run/stop bit.
Oliver Neukum b5c53d
+	 */
Oliver Neukum b5c53d
+	dwc3_core_soft_reset(dwc);
Oliver Neukum b5c53d
+
Oliver Neukum b5c53d
+	dwc3_event_buffers_setup(dwc);
Oliver Neukum b5c53d
+	__dwc3_gadget_start(dwc);
Oliver Neukum b5c53d
+	return dwc3_gadget_run_stop(dwc, true, false);
Oliver Neukum b5c53d
+}
Oliver Neukum b5c53d
+
Oliver Neukum b5c53d
 static int dwc3_gadget_pullup(struct usb_gadget *g, int is_on)
Oliver Neukum b5c53d
 {
Oliver Neukum b5c53d
 	struct dwc3		*dwc = gadget_to_dwc(g);
Oliver Neukum b5c53d
@@ -2388,21 +2403,11 @@ static int dwc3_gadget_pullup(struct usb
Oliver Neukum b5c53d
 		return 0;
Oliver Neukum b5c53d
 	}
Oliver Neukum b5c53d
 
Oliver Neukum b5c53d
-	if (!is_on) {
Oliver Neukum b5c53d
+	if (!is_on)
Oliver Neukum b5c53d
 		ret = dwc3_gadget_soft_disconnect(dwc);
Oliver Neukum b5c53d
-	} else {
Oliver Neukum b5c53d
-		/*
Oliver Neukum b5c53d
-		 * In the Synopsys DWC_usb31 1.90a programming guide section
Oliver Neukum b5c53d
-		 * 4.1.9, it specifies that for a reconnect after a
Oliver Neukum b5c53d
-		 * device-initiated disconnect requires a core soft reset
Oliver Neukum b5c53d
-		 * (DCTL.CSftRst) before enabling the run/stop bit.
Oliver Neukum b5c53d
-		 */
Oliver Neukum b5c53d
-		dwc3_core_soft_reset(dwc);
Oliver Neukum b5c53d
+	else
Oliver Neukum b5c53d
+		ret = dwc3_gadget_soft_connect(dwc);
Oliver Neukum b5c53d
 
Oliver Neukum b5c53d
-		dwc3_event_buffers_setup(dwc);
Oliver Neukum b5c53d
-		__dwc3_gadget_start(dwc);
Oliver Neukum b5c53d
-		ret = dwc3_gadget_run_stop(dwc, true, false);
Oliver Neukum b5c53d
-	}
Oliver Neukum b5c53d
 
Oliver Neukum b5c53d
 	pm_runtime_put(dwc->dev);
Oliver Neukum b5c53d
 
Oliver Neukum b5c53d
@@ -4271,14 +4276,29 @@ void dwc3_gadget_exit(struct dwc3 *dwc)
Oliver Neukum b5c53d
 
Oliver Neukum b5c53d
 int dwc3_gadget_suspend(struct dwc3 *dwc)
Oliver Neukum b5c53d
 {
Oliver Neukum b5c53d
+	int ret;
Oliver Neukum b5c53d
+
Oliver Neukum b5c53d
 	if (!dwc->gadget_driver)
Oliver Neukum b5c53d
 		return 0;
Oliver Neukum b5c53d
 
Oliver Neukum b5c53d
-	dwc3_gadget_run_stop(dwc, false, false);
Oliver Neukum b5c53d
+	ret = dwc3_gadget_soft_disconnect(dwc);
Oliver Neukum b5c53d
+	if (ret)
Oliver Neukum b5c53d
+		goto err;
Oliver Neukum b5c53d
+
Oliver Neukum b5c53d
 	dwc3_disconnect_gadget(dwc);
Oliver Neukum b5c53d
-	__dwc3_gadget_stop(dwc);
Oliver Neukum b5c53d
 
Oliver Neukum b5c53d
 	return 0;
Oliver Neukum b5c53d
+
Oliver Neukum b5c53d
+err:
Oliver Neukum b5c53d
+	/*
Oliver Neukum b5c53d
+	 * Attempt to reset the controller's state. Likely no
Oliver Neukum b5c53d
+	 * communication can be established until the host
Oliver Neukum b5c53d
+	 * performs a port reset.
Oliver Neukum b5c53d
+	 */
Oliver Neukum b5c53d
+	if (dwc->softconnect)
Oliver Neukum b5c53d
+		dwc3_gadget_soft_connect(dwc);
Oliver Neukum b5c53d
+
Oliver Neukum b5c53d
+	return ret;
Oliver Neukum b5c53d
 }
Oliver Neukum b5c53d
 
Oliver Neukum b5c53d
 int dwc3_gadget_resume(struct dwc3 *dwc)