From 81065c3d1b004b89398ba24e6d52306f1609895c Mon Sep 17 00:00:00 2001 From: Alex Smith <alex.smith@imgtec.com> Date: Wed, 24 Jun 2015 12:37:31 +0100 Subject: [PATCH] mmc: jz47xx: Fix race condition in IRQ mask update A spinlock is held while updating the internal copy of the IRQ mask, but not while writing it to the actual IMASK register. After the lock is released, an IRQ can occur before the IMASK register is written. If handling this IRQ causes the mask to be changed, when the handler returns back to the middle of the first mask update, a stale value will be written to the mask register. If this causes an IRQ to become unmasked that cannot have its status cleared by writing a 1 to it in the IFLG register, e.g. the SDIO IRQ, then we can end up stuck with the same IRQ repeatedly being fired but not handled. Normally the MMC IRQ handler attempts to clear any unexpected IRQs by writing IFLG, but for those that cannot be cleared in this way then the IRQ will just repeatedly fire. This was resulting in lockups after a while of using Wi-Fi on the CI20 (GitHub issue #19). Resolve by holding the spinlock until after the IMASK register has been updated. Signed-off-by: Alex Smith <alex.smith@imgtec.com> --- drivers/mmc/host/jz47xx_mmc.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/mmc/host/jz47xx_mmc.c b/drivers/mmc/host/jz47xx_mmc.c index 60dffd3194ebd1..7987761b2cd7e1 100644 --- a/drivers/mmc/host/jz47xx_mmc.c +++ b/drivers/mmc/host/jz47xx_mmc.c @@ -184,17 +184,19 @@ static void jz47xx_mmc_set_irq_enabled(struct jz47xx_mmc_host *host, unsigned long flags; spin_lock_irqsave(&host->lock, flags); + if (enabled) host->irq_mask &= ~irq; else host->irq_mask |= irq; - spin_unlock_irqrestore(&host->lock, flags); /* In the 4750 onwards, IMASK is expanded to 32 bits. */ if (host->version >= JZ_MMC_JZ4750) writel(host->irq_mask, host->base + JZ_REG_MMC_IMASK); else writew(host->irq_mask, host->base + JZ_REG_MMC_IMASK); + + spin_unlock_irqrestore(&host->lock, flags); } static void jz47xx_mmc_clock_enable(struct jz47xx_mmc_host *host,