summaryrefslogtreecommitdiffstats
path: root/drivers/mmc
diff options
context:
space:
mode:
authorAndreas Fenkart <afenkart@gmail.com>2014-05-29 04:28:03 -0400
committerUlf Hansson <ulf.hansson@linaro.org>2014-07-09 05:26:05 -0400
commitf945901f9aaff859c6b3244628c766d1939e46e7 (patch)
treed41a99a8675ff8759bc188988f6a822d53f25c0e /drivers/mmc
parent5a52b08b05b01732e338aab7283b3ce4987d71fb (diff)
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')
-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 @@
107#define SRD (1 << 26) 107#define SRD (1 << 26)
108#define SOFTRESET (1 << 1) 108#define SOFTRESET (1 << 1)
109 109
110/* PSTATE */
111#define DLEV_DAT(x) (1 << (20 + (x)))
112
110/* Interrupt masks for IE and ISE register */ 113/* Interrupt masks for IE and ISE register */
111#define CC_EN (1 << 0) 114#define CC_EN (1 << 0)
112#define TC_EN (1 << 1) 115#define TC_EN (1 << 1)
@@ -2387,6 +2390,7 @@ static int omap_hsmmc_runtime_suspend(struct device *dev)
2387{ 2390{
2388 struct omap_hsmmc_host *host; 2391 struct omap_hsmmc_host *host;
2389 unsigned long flags; 2392 unsigned long flags;
2393 int ret = 0;
2390 2394
2391 host = platform_get_drvdata(to_platform_device(dev)); 2395 host = platform_get_drvdata(to_platform_device(dev));
2392 omap_hsmmc_context_save(host); 2396 omap_hsmmc_context_save(host);
@@ -2398,14 +2402,29 @@ static int omap_hsmmc_runtime_suspend(struct device *dev)
2398 /* disable sdio irq handling to prevent race */ 2402 /* disable sdio irq handling to prevent race */
2399 OMAP_HSMMC_WRITE(host->base, ISE, 0); 2403 OMAP_HSMMC_WRITE(host->base, ISE, 0);
2400 OMAP_HSMMC_WRITE(host->base, IE, 0); 2404 OMAP_HSMMC_WRITE(host->base, IE, 0);
2401 OMAP_HSMMC_WRITE(host->base, STAT, STAT_CLEAR); 2405
2406 if (!(OMAP_HSMMC_READ(host->base, PSTATE) & DLEV_DAT(1))) {
2407 /*
2408 * dat1 line low, pending sdio irq
2409 * race condition: possible irq handler running on
2410 * multi-core, abort
2411 */
2412 dev_dbg(dev, "pending sdio irq, abort suspend\n");
2413 OMAP_HSMMC_WRITE(host->base, STAT, STAT_CLEAR);
2414 OMAP_HSMMC_WRITE(host->base, ISE, CIRQ_EN);
2415 OMAP_HSMMC_WRITE(host->base, IE, CIRQ_EN);
2416 pm_runtime_mark_last_busy(dev);
2417 ret = -EBUSY;
2418 goto abort;
2419 }
2402 2420
2403 WARN_ON(host->flags & HSMMC_WAKE_IRQ_ENABLED); 2421 WARN_ON(host->flags & HSMMC_WAKE_IRQ_ENABLED);
2404 enable_irq(host->wake_irq); 2422 enable_irq(host->wake_irq);
2405 host->flags |= HSMMC_WAKE_IRQ_ENABLED; 2423 host->flags |= HSMMC_WAKE_IRQ_ENABLED;
2406 } 2424 }
2425abort:
2407 spin_unlock_irqrestore(&host->irq_lock, flags); 2426 spin_unlock_irqrestore(&host->irq_lock, flags);
2408 return 0; 2427 return ret;
2409} 2428}
2410 2429
2411static int omap_hsmmc_runtime_resume(struct device *dev) 2430static int omap_hsmmc_runtime_resume(struct device *dev)