[PATCH] usb: dwc3: Fix latency of DSTS while receiving wakeup event

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



When operating in High-Speed, it is observed that DSTS[USBLNKST] doesn't
update link state immediately after receiving the wakeup interrupt. Since
wakeup event handler calls the resume callbacks, there is a chance that
function drivers can perform an ep queue. Which in turn tries to perform
remote wakeup from send_gadget_ep_cmd(), this happens because DSTS[[21:18]
wasn't updated to U0 yet. It is observed that the latency of DSTS can be
in order of milli-seconds. Hence update the dwc->link_state from evtinfo,
and use this variable to prevent calling remote wakup unnecessarily.

Fixes: ecba9bc9946b ("usb: dwc3: gadget: Check for L1/L2/U3 for Start Transfer")
Cc: <stable@xxxxxxxxxxxxxxx>
Signed-off-by: Prashanth K <quic_prashk@xxxxxxxxxxx>
---
 drivers/usb/dwc3/gadget.c | 25 ++++++++++++++++++-------
 1 file changed, 18 insertions(+), 7 deletions(-)

diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c
index 89fc690fdf34..3b55285118b0 100644
--- a/drivers/usb/dwc3/gadget.c
+++ b/drivers/usb/dwc3/gadget.c
@@ -328,7 +328,8 @@ int dwc3_send_gadget_ep_cmd(struct dwc3_ep *dep, unsigned int cmd,
 	}
 
 	if (DWC3_DEPCMD_CMD(cmd) == DWC3_DEPCMD_STARTTRANSFER) {
-		int link_state;
+		int	link_state;
+		bool	remote_wakeup = false;
 
 		/*
 		 * Initiate remote wakeup if the link state is in U3 when
@@ -339,15 +340,26 @@ int dwc3_send_gadget_ep_cmd(struct dwc3_ep *dep, unsigned int cmd,
 		link_state = dwc3_gadget_get_link_state(dwc);
 		switch (link_state) {
 		case DWC3_LINK_STATE_U2:
-			if (dwc->gadget->speed >= USB_SPEED_SUPER)
+			if (dwc->gadget->speed < USB_SPEED_SUPER)
+				remote_wakeup = true;
+			break;
+		case DWC3_LINK_STATE_U3:
+			/*
+			 * In HS, DSTS can take few milliseconds to update linkstate bits,
+			 * so rely on dwc->link_state to identify whether gadget woke up.
+			 * Don't issue remote wakuep again if link is already in U0.
+			 */
+			if (dwc->link_state == DWC3_LINK_STATE_U0)
 				break;
 
-			fallthrough;
-		case DWC3_LINK_STATE_U3:
+			remote_wakeup = true;
+			break;
+		}
+
+		if (remote_wakeup) {
 			ret = __dwc3_gadget_wakeup(dwc, false);
 			dev_WARN_ONCE(dwc->dev, ret, "wakeup failed --> %d\n",
 					ret);
-			break;
 		}
 	}
 
@@ -4214,6 +4226,7 @@ static void dwc3_gadget_conndone_interrupt(struct dwc3 *dwc)
 static void dwc3_gadget_wakeup_interrupt(struct dwc3 *dwc, unsigned int evtinfo)
 {
 	dwc->suspended = false;
+	dwc->link_state = evtinfo & DWC3_LINK_STATE_MASK;
 
 	/*
 	 * TODO take core out of low power mode when that's
@@ -4225,8 +4238,6 @@ static void dwc3_gadget_wakeup_interrupt(struct dwc3 *dwc, unsigned int evtinfo)
 		dwc->gadget_driver->resume(dwc->gadget);
 		spin_lock(&dwc->lock);
 	}
-
-	dwc->link_state = evtinfo & DWC3_LINK_STATE_MASK;
 }
 
 static void dwc3_gadget_linksts_change_interrupt(struct dwc3 *dwc,
-- 
2.25.1





[Index of Archives]     [Linux Kernel]     [Kernel Development Newbies]     [Linux USB Devel]     [Video for Linux]     [Linux Audio Users]     [Yosemite Hiking]     [Linux Kernel]     [Linux SCSI]

  Powered by Linux