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;
}