USB: EHCI: HSIC: Guard against late remote wakeup during suspend

Protect against the possibility of receiving a remote wakeup event
when the controller is about to enter suspend. If it happens, abort
the suspend sequence so the controller can handle it normally,
otherwise it may result in a stuck PHY due to a hardware limitation
when re-entering suspend instead of acknowledging the event within
the spec-defined 20ms of resume signaling.

CRs-fixed: 362865
Change-Id: I4a3593622c59668cf067c5b602e561790268d96e
Signed-off-by: Jack Pham <jackp@codeaurora.org>
diff --git a/drivers/usb/host/ehci-msm-hsic.c b/drivers/usb/host/ehci-msm-hsic.c
index 5ee1908..82373e2 100644
--- a/drivers/usb/host/ehci-msm-hsic.c
+++ b/drivers/usb/host/ehci-msm-hsic.c
@@ -340,6 +340,15 @@
 	}
 
 	disable_irq(hcd->irq);
+
+	/* make sure we don't race against a remote wakeup */
+	if (test_bit(HCD_FLAG_WAKEUP_PENDING, &hcd->flags) ||
+	    readl_relaxed(USB_PORTSC) & PORT_RESUME) {
+		dev_dbg(mehci->dev, "wakeup pending, aborting suspend\n");
+		enable_irq(hcd->irq);
+		return -EBUSY;
+	}
+
 	/*
 	 * PHY may take some time or even fail to enter into low power
 	 * mode (LPM). Hence poll for 500 msec and reset the PHY and link
@@ -973,6 +982,7 @@
 #ifdef CONFIG_PM_SLEEP
 static int msm_hsic_pm_suspend(struct device *dev)
 {
+	int ret;
 	struct usb_hcd *hcd = dev_get_drvdata(dev);
 	struct msm_hsic_hcd *mehci = hcd_to_hsic(hcd);
 
@@ -981,7 +991,12 @@
 	if (device_may_wakeup(dev))
 		enable_irq_wake(hcd->irq);
 
-	return msm_hsic_suspend(mehci);
+	ret = msm_hsic_suspend(mehci);
+
+	if (ret && device_may_wakeup(dev))
+		disable_irq_wake(hcd->irq);
+
+	return ret;
 }
 
 static int msm_hsic_pm_suspend_noirq(struct device *dev)
@@ -1033,14 +1048,7 @@
 #ifdef CONFIG_PM_RUNTIME
 static int msm_hsic_runtime_idle(struct device *dev)
 {
-	struct usb_hcd *hcd = dev_get_drvdata(dev);
-
 	dev_dbg(dev, "EHCI runtime idle\n");
-
-	/*don't allow runtime suspend in the middle of remote wakeup*/
-	if (readl_relaxed(USB_PORTSC) & PORT_RESUME)
-		return -EAGAIN;
-
 	return 0;
 }