Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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>

authored by

Andreas Fenkart and committed by
Ulf Hansson
f945901f 5a52b08b

+21 -2
+21 -2
drivers/mmc/host/omap_hsmmc.c
··· 107 107 #define SRD (1 << 26) 108 108 #define SOFTRESET (1 << 1) 109 109 110 + /* PSTATE */ 111 + #define DLEV_DAT(x) (1 << (20 + (x))) 112 + 110 113 /* Interrupt masks for IE and ISE register */ 111 114 #define CC_EN (1 << 0) 112 115 #define TC_EN (1 << 1) ··· 2390 2387 { 2391 2388 struct omap_hsmmc_host *host; 2392 2389 unsigned long flags; 2390 + int ret = 0; 2393 2391 2394 2392 host = platform_get_drvdata(to_platform_device(dev)); 2395 2393 omap_hsmmc_context_save(host); ··· 2402 2398 /* disable sdio irq handling to prevent race */ 2403 2399 OMAP_HSMMC_WRITE(host->base, ISE, 0); 2404 2400 OMAP_HSMMC_WRITE(host->base, IE, 0); 2405 - OMAP_HSMMC_WRITE(host->base, STAT, STAT_CLEAR); 2401 + 2402 + if (!(OMAP_HSMMC_READ(host->base, PSTATE) & DLEV_DAT(1))) { 2403 + /* 2404 + * dat1 line low, pending sdio irq 2405 + * race condition: possible irq handler running on 2406 + * multi-core, abort 2407 + */ 2408 + dev_dbg(dev, "pending sdio irq, abort suspend\n"); 2409 + OMAP_HSMMC_WRITE(host->base, STAT, STAT_CLEAR); 2410 + OMAP_HSMMC_WRITE(host->base, ISE, CIRQ_EN); 2411 + OMAP_HSMMC_WRITE(host->base, IE, CIRQ_EN); 2412 + pm_runtime_mark_last_busy(dev); 2413 + ret = -EBUSY; 2414 + goto abort; 2415 + } 2406 2416 2407 2417 WARN_ON(host->flags & HSMMC_WAKE_IRQ_ENABLED); 2408 2418 enable_irq(host->wake_irq); 2409 2419 host->flags |= HSMMC_WAKE_IRQ_ENABLED; 2410 2420 } 2421 + abort: 2411 2422 spin_unlock_irqrestore(&host->irq_lock, flags); 2412 - return 0; 2423 + return ret; 2413 2424 } 2414 2425 2415 2426 static int omap_hsmmc_runtime_resume(struct device *dev)