lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [thread-next>] [day] [month] [year] [list]
Message-ID: <20200108113719.21551-1-pawell@cadence.com>
Date:   Wed, 8 Jan 2020 12:37:18 +0100
From:   Pawel Laszczak <pawell@...ence.com>
To:     <felipe.balbi@...ux.intel.com>
CC:     <gregkh@...uxfoundation.org>, <rogerq@...com>, <jbergsagel@...com>,
        <nsekhar@...com>, <nm@...com>, <linux-kernel@...r.kernel.org>,
        <jpawar@...ence.com>, <kurahul@...ence.com>, <sparmar@...ence.com>,
        Pawel Laszczak <pawell@...ence.com>,
        Peter Chan <peter.chan@....com>
Subject: [PATCH] usb: cdns3: Fix: ARM core hang after connect/disconnect operation.

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@...ence.com>
Signed-off-by: Peter Chan <peter.chan@....com>
Reported-by: Peter Chan <peter.chan@....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

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ