public inbox for linux-kernel@vger.kernel.org
 help / color / mirror / Atom feed
* [PATCH] usb: cdns3: Fix: ARM core hang after connect/disconnect operation.
@ 2020-01-08 11:37 Pawel Laszczak
  2020-01-08 14:22 ` Greg KH
  2020-01-08 14:28 ` Greg KH
  0 siblings, 2 replies; 11+ messages in thread
From: Pawel Laszczak @ 2020-01-08 11:37 UTC (permalink / raw)
  To: felipe.balbi
  Cc: gregkh, rogerq, jbergsagel, nsekhar, nm, linux-kernel, jpawar,
	kurahul, sparmar, Pawel Laszczak, Peter Chan

The ARM core hang when access USB register after tens of thousands
connect/disconnect operation.

The issue was observed on platform with android system and is not easy
to reproduce. During test controller works at HS device mode with host
connected.

The test is based on continuous disabling/enabling USB device function
what cause continuous setting DEVDS/DEVEN bit in USB_CONF register.

For testing was used composite device consisting from ADP and RNDIS
function.

Presumably the problem was caused by DMA transfer made after setting
DEVDS bit. To resolve this issue fix stops all DMA transfer before
setting DEVDS bit.

Signed-off-by: Pawel Laszczak <pawell@cadence.com>
Signed-off-by: Peter Chan <peter.chan@nxp.com>
Reported-by: Peter Chan <peter.chan@nxp.com>
Fixes: 7733f6c32e36 ("usb: cdns3: Add Cadence USB3 DRD Driver")
---
 drivers/usb/cdns3/gadget.c | 84 ++++++++++++++++++++++++++------------
 drivers/usb/cdns3/gadget.h |  1 +
 2 files changed, 58 insertions(+), 27 deletions(-)

diff --git a/drivers/usb/cdns3/gadget.c b/drivers/usb/cdns3/gadget.c
index 4c1e75509303..277ed8484032 100644
--- a/drivers/usb/cdns3/gadget.c
+++ b/drivers/usb/cdns3/gadget.c
@@ -1516,6 +1516,49 @@ static int cdns3_ep_onchip_buffer_reserve(struct cdns3_device *priv_dev,
 	return 0;
 }
 
+static int cdns3_disable_reset_ep(struct cdns3_device *priv_dev,
+				  struct cdns3_endpoint *priv_ep)
+{
+	unsigned long flags;
+	u32 val;
+	int ret;
+
+	spin_lock_irqsave(&priv_dev->lock, flags);
+
+	if (priv_ep->flags & EP_HW_RESETED) {
+		spin_unlock_irqrestore(&priv_dev->lock, flags);
+		return 0;
+	}
+
+	cdns3_select_ep(priv_dev, priv_ep->endpoint.desc->bEndpointAddress);
+
+	val = readl(&priv_dev->regs->ep_cfg);
+	val &= ~EP_CFG_ENABLE;
+	writel(val, &priv_dev->regs->ep_cfg);
+
+	/**
+	 * Driver needs some time before resetting endpoint.
+	 * It need waits for clearing DBUSY bit or for timeout expired.
+	 * 10us is enough time for controller to stop transfer.
+	 */
+	readl_poll_timeout_atomic(&priv_dev->regs->ep_sts, val,
+				  !(val & EP_STS_DBUSY), 1, 10);
+	writel(EP_CMD_EPRST, &priv_dev->regs->ep_cmd);
+
+	ret = readl_poll_timeout_atomic(&priv_dev->regs->ep_cmd, val,
+					!(val & (EP_CMD_CSTALL | EP_CMD_EPRST)),
+					1, 1000);
+
+	if (unlikely(ret))
+		dev_err(priv_dev->dev, "Timeout: %s resetting failed.\n",
+			priv_ep->name);
+
+	priv_ep->flags |= EP_HW_RESETED;
+	spin_unlock_irqrestore(&priv_dev->lock, flags);
+
+	return ret;
+}
+
 void cdns3_configure_dmult(struct cdns3_device *priv_dev,
 			   struct cdns3_endpoint *priv_ep)
 {
@@ -1893,8 +1936,6 @@ static int cdns3_gadget_ep_disable(struct usb_ep *ep)
 	struct usb_request *request;
 	unsigned long flags;
 	int ret = 0;
-	u32 ep_cfg;
-	int val;
 
 	if (!ep) {
 		pr_err("usbss: invalid parameters\n");
@@ -1908,32 +1949,11 @@ static int cdns3_gadget_ep_disable(struct usb_ep *ep)
 			  "%s is already disabled\n", priv_ep->name))
 		return 0;
 
-	spin_lock_irqsave(&priv_dev->lock, flags);
-
 	trace_cdns3_gadget_ep_disable(priv_ep);
 
-	cdns3_select_ep(priv_dev, ep->desc->bEndpointAddress);
-
-	ep_cfg = readl(&priv_dev->regs->ep_cfg);
-	ep_cfg &= ~EP_CFG_ENABLE;
-	writel(ep_cfg, &priv_dev->regs->ep_cfg);
-
-	/**
-	 * Driver needs some time before resetting endpoint.
-	 * It need waits for clearing DBUSY bit or for timeout expired.
-	 * 10us is enough time for controller to stop transfer.
-	 */
-	readl_poll_timeout_atomic(&priv_dev->regs->ep_sts, val,
-				  !(val & EP_STS_DBUSY), 1, 10);
-	writel(EP_CMD_EPRST, &priv_dev->regs->ep_cmd);
-
-	readl_poll_timeout_atomic(&priv_dev->regs->ep_cmd, val,
-				  !(val & (EP_CMD_CSTALL | EP_CMD_EPRST)),
-				  1, 1000);
-	if (unlikely(ret))
-		dev_err(priv_dev->dev, "Timeout: %s resetting failed.\n",
-			priv_ep->name);
+	cdns3_disable_reset_ep(priv_dev, priv_ep);
 
+	spin_lock_irqsave(&priv_dev->lock, flags);
 	while (!list_empty(&priv_ep->pending_req_list)) {
 		request = cdns3_next_request(&priv_ep->pending_req_list);
 
@@ -1962,6 +1982,7 @@ static int cdns3_gadget_ep_disable(struct usb_ep *ep)
 
 	ep->desc = NULL;
 	priv_ep->flags &= ~EP_ENABLED;
+	priv_ep->flags |= EP_CLAIMED | EP_HW_RESETED;
 
 	spin_unlock_irqrestore(&priv_dev->lock, flags);
 
@@ -2282,11 +2303,20 @@ static int cdns3_gadget_set_selfpowered(struct usb_gadget *gadget,
 static int cdns3_gadget_pullup(struct usb_gadget *gadget, int is_on)
 {
 	struct cdns3_device *priv_dev = gadget_to_cdns3_device(gadget);
+	int i;
 
-	if (is_on)
+	if (is_on) {
 		writel(USB_CONF_DEVEN, &priv_dev->regs->usb_conf);
-	else
+	} else {
+		for (i = 1; i < CDNS3_ENDPOINTS_MAX_COUNT; i++) {
+			if (priv_dev->eps[i] &&
+			    priv_dev->eps[i]->flags & EP_ENABLED)
+				cdns3_disable_reset_ep(priv_dev,
+						       priv_dev->eps[i]);
+		}
+
 		writel(USB_CONF_DEVDS, &priv_dev->regs->usb_conf);
+	}
 
 	return 0;
 }
diff --git a/drivers/usb/cdns3/gadget.h b/drivers/usb/cdns3/gadget.h
index bc4024041ef2..b6cc222b9f58 100644
--- a/drivers/usb/cdns3/gadget.h
+++ b/drivers/usb/cdns3/gadget.h
@@ -1142,6 +1142,7 @@ struct cdns3_endpoint {
 #define EP_QUIRK_END_TRANSFER	BIT(11)
 #define EP_QUIRK_EXTRA_BUF_DET	BIT(12)
 #define EP_QUIRK_EXTRA_BUF_EN	BIT(13)
+#define EP_HW_RESETED		BIT(14)
 	u32			flags;
 
 	struct cdns3_request	*descmis_req;
-- 
2.17.1


^ permalink raw reply related	[flat|nested] 11+ messages in thread

end of thread, other threads:[~2020-01-14  9:09 UTC | newest]

Thread overview: 11+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2020-01-08 11:37 [PATCH] usb: cdns3: Fix: ARM core hang after connect/disconnect operation Pawel Laszczak
2020-01-08 14:22 ` Greg KH
2020-01-09  4:16   ` Pawel Laszczak
2020-01-08 14:28 ` Greg KH
2020-01-09  6:27   ` Pawel Laszczak
2020-01-09  6:38     ` Greg KH
2020-01-09  8:34       ` Pawel Laszczak
2020-01-09  9:38         ` Greg KH
2020-01-14  8:57           ` Peter Chen
2020-01-14  9:06             ` Pawel Laszczak
2020-01-14  9:09             ` Pawel Laszczak

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox