EHCI: HSIC: Handle asynchronous interrupt properly
Runtime PM resume and asynchronous interrupt can run in parallel. When
this happens, there is a possibility of PM usage counter mismatch.
Fix this by checking the return value of pm_runtime_get() and by
disabling the USB interrupt while resuming the HSIC.
Consider asynchronous interrupt case when resuming HSIC during system
resume.
(cherry picked from commit 822fbe6e90d32ec7c7b5d7cee69a8cd36869005f)
Conflicts:
drivers/usb/host/ehci-msm-hsic.c
CRs-Fixed: 444883
Change-Id: I63db49d8b45a7413f9456a93c9648511ad622b4b
Signed-off-by: Pavankumar Kondeti <pkondeti@codeaurora.org>
diff --git a/drivers/usb/host/ehci-msm-hsic.c b/drivers/usb/host/ehci-msm-hsic.c
index 7332e48..1e52d79 100644
--- a/drivers/usb/host/ehci-msm-hsic.c
+++ b/drivers/usb/host/ehci-msm-hsic.c
@@ -1,6 +1,6 @@
/* ehci-msm-hsic.c - HSUSB Host Controller Driver Implementation
*
- * Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved.
+ * Copyright (c) 2011-2013, Linux Foundation. All rights reserved.
*
* Partly derived from ehci-fsl.c and ehci-hcd.c
* Copyright (c) 2000-2004 by David Brownell
@@ -76,7 +76,7 @@
struct clk *phy_clk;
struct clk *cal_clk;
struct regulator *hsic_vddcx;
- bool async_int;
+ atomic_t async_int;
atomic_t in_lpm;
struct wake_lock wlock;
int peripheral_status_irq;
@@ -751,6 +751,9 @@
return 0;
}
+ /* Handles race with Async interrupt */
+ disable_irq(hcd->irq);
+
spin_lock_irqsave(&mehci->wakeup_lock, flags);
if (mehci->wakeup_irq_enabled) {
disable_irq_wake(mehci->wakeup_irq);
@@ -813,8 +816,8 @@
atomic_set(&mehci->in_lpm, 0);
- if (mehci->async_int) {
- mehci->async_int = false;
+ if (atomic_read(&mehci->async_int)) {
+ atomic_set(&mehci->async_int, 0);
pm_runtime_put_noidle(mehci->dev);
enable_irq(hcd->irq);
}
@@ -824,6 +827,7 @@
pm_runtime_put_noidle(mehci->dev);
}
+ enable_irq(hcd->irq);
dev_dbg(mehci->dev, "HSIC-USB exited from low power mode\n");
return 0;
@@ -849,12 +853,19 @@
struct ehci_hcd *ehci = hcd_to_ehci(hcd);
struct msm_hsic_hcd *mehci = hcd_to_hsic(hcd);
u32 status;
+ int ret;
if (atomic_read(&mehci->in_lpm)) {
- disable_irq_nosync(hcd->irq);
dev_dbg(mehci->dev, "phy async intr\n");
- mehci->async_int = true;
- pm_runtime_get(mehci->dev);
+ dbg_log_event(NULL, "Async IRQ", 0);
+ ret = pm_runtime_get(mehci->dev);
+ if ((ret == 1) || (ret == -EINPROGRESS)) {
+ pm_runtime_put_noidle(mehci->dev);
+ } else {
+ disable_irq_nosync(hcd->irq);
+ atomic_set(&mehci->async_int, 1);
+ }
+
return IRQ_HANDLED;
}
@@ -1783,7 +1794,7 @@
struct usb_hcd *hcd = dev_get_drvdata(dev);
struct msm_hsic_hcd *mehci = hcd_to_hsic(hcd);
- if (mehci->async_int) {
+ if (atomic_read(&mehci->async_int)) {
dev_dbg(dev, "suspend_noirq: Aborting due to pending interrupt\n");
return -EBUSY;
}
@@ -1810,6 +1821,7 @@
* start I/O.
*/
if (!atomic_read(&mehci->pm_usage_cnt) &&
+ !atomic_read(&mehci->async_int) &&
pm_runtime_suspended(dev))
return 0;