summaryrefslogtreecommitdiffstats
path: root/drivers/mmc/host
diff options
context:
space:
mode:
authorAndreas Fenkart <afenkart@gmail.com>2014-05-29 10:28:03 +0200
committerUlf Hansson <ulf.hansson@linaro.org>2014-07-09 11:26:05 +0200
commitf945901f9aaff859c6b3244628c766d1939e46e7 (patch)
treed41a99a8675ff8759bc188988f6a822d53f25c0e /drivers/mmc/host
parentmmc: omap_hsmmc: enable wakeup event for sdio OMAP4 (diff)
downloadlinux-f945901f9aaff859c6b3244628c766d1939e46e7.tar.xz
linux-f945901f9aaff859c6b3244628c766d1939e46e7.zip
mmc: omap_hsmmc: abort runtime suspend if pending sdio irq detected
On multicores, an sdio irq handler could be running in parallel to runtime suspend. In the worst case it could be waiting for the spinlock held by the runtime suspend. When runtime suspend is complete and the functional clock (fclk) turned off, the irq handler will continue and cause a SIGBUS on the first register access. Acked-by: Balaji T K <balajitk@ti.com> Signed-off-by: Andreas Fenkart <afenkart@gmail.com> Signed-off-by: Ulf Hansson <ulf.hansson@linaro.org>
Diffstat (limited to 'drivers/mmc/host')
-rw-r--r--drivers/mmc/host/omap_hsmmc.c23
1 files changed, 21 insertions, 2 deletions
diff --git a/drivers/mmc/host/omap_hsmmc.c b/drivers/mmc/host/omap_hsmmc.c
index b12a2882ec74..e97fb9cb24d5 100644
--- a/drivers/mmc/host/omap_hsmmc.c
+++ b/drivers/mmc/host/omap_hsmmc.c
@@ -107,6 +107,9 @@
#define SRD (1 << 26)
#define SOFTRESET (1 << 1)
+/* PSTATE */
+#define DLEV_DAT(x) (1 << (20 + (x)))
+
/* Interrupt masks for IE and ISE register */
#define CC_EN (1 << 0)
#define TC_EN (1 << 1)
@@ -2387,6 +2390,7 @@ static int omap_hsmmc_runtime_suspend(struct device *dev)
{
struct omap_hsmmc_host *host;
unsigned long flags;
+ int ret = 0;
host = platform_get_drvdata(to_platform_device(dev));
omap_hsmmc_context_save(host);
@@ -2398,14 +2402,29 @@ static int omap_hsmmc_runtime_suspend(struct device *dev)
/* disable sdio irq handling to prevent race */
OMAP_HSMMC_WRITE(host->base, ISE, 0);
OMAP_HSMMC_WRITE(host->base, IE, 0);
- OMAP_HSMMC_WRITE(host->base, STAT, STAT_CLEAR);
+
+ if (!(OMAP_HSMMC_READ(host->base, PSTATE) & DLEV_DAT(1))) {
+ /*
+ * dat1 line low, pending sdio irq
+ * race condition: possible irq handler running on
+ * multi-core, abort
+ */
+ dev_dbg(dev, "pending sdio irq, abort suspend\n");
+ OMAP_HSMMC_WRITE(host->base, STAT, STAT_CLEAR);
+ OMAP_HSMMC_WRITE(host->base, ISE, CIRQ_EN);
+ OMAP_HSMMC_WRITE(host->base, IE, CIRQ_EN);
+ pm_runtime_mark_last_busy(dev);
+ ret = -EBUSY;
+ goto abort;
+ }
WARN_ON(host->flags & HSMMC_WAKE_IRQ_ENABLED);
enable_irq(host->wake_irq);
host->flags |= HSMMC_WAKE_IRQ_ENABLED;
}
+abort:
spin_unlock_irqrestore(&host->irq_lock, flags);
- return 0;
+ return ret;
}
static int omap_hsmmc_runtime_resume(struct device *dev)