Skip to content

Commit

Permalink
mmc: omap_hsmmc: abort runtime suspend if pending sdio irq detected
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Andreas Fenkart authored and Ulf Hansson committed Jul 9, 2014
1 parent 5a52b08 commit f945901
Showing 1 changed file with 21 additions and 2 deletions.
23 changes: 21 additions & 2 deletions drivers/mmc/host/omap_hsmmc.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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);
Expand All @@ -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)
Expand Down

0 comments on commit f945901

Please sign in to comment.