Commit d25928d1 authored by Shawn Guo's avatar Shawn Guo Committed by Chris Ball

mmc: sdhci: fix interrupt storm from card detection

The issue was initially found by Eric Benard as below.

http://permalink.gmane.org/gmane.linux.ports.arm.kernel/108031

Not sure about other SDHCI based controller, but on Freescale eSDHC,
the SDHCI_INT_CARD_INSERT bits will be immediately set again when it
gets cleared, if a card is inserted. The driver need to mask the irq
to prevent interrupt storm which will freeze the system.  And the
SDHCI_INT_CARD_REMOVE gets the same situation.

The patch fixes the problem based on the initial idea from
Eric Benard.
Signed-off-by: default avatarShawn Guo <shawn.guo@linaro.org>
Cc: Eric Benard <eric@eukrea.com>
Tested-by: default avatarArnaud Patard <arnaud.patard@rtp-net.org>
Signed-off-by: default avatarChris Ball <cjb@laptop.org>
parent e312eb1e
...@@ -127,11 +127,15 @@ static void sdhci_mask_irqs(struct sdhci_host *host, u32 irqs) ...@@ -127,11 +127,15 @@ static void sdhci_mask_irqs(struct sdhci_host *host, u32 irqs)
static void sdhci_set_card_detection(struct sdhci_host *host, bool enable) static void sdhci_set_card_detection(struct sdhci_host *host, bool enable)
{ {
u32 irqs = SDHCI_INT_CARD_REMOVE | SDHCI_INT_CARD_INSERT; u32 present, irqs;
if (host->quirks & SDHCI_QUIRK_BROKEN_CARD_DETECTION) if (host->quirks & SDHCI_QUIRK_BROKEN_CARD_DETECTION)
return; return;
present = sdhci_readl(host, SDHCI_PRESENT_STATE) &
SDHCI_CARD_PRESENT;
irqs = present ? SDHCI_INT_CARD_REMOVE : SDHCI_INT_CARD_INSERT;
if (enable) if (enable)
sdhci_unmask_irqs(host, irqs); sdhci_unmask_irqs(host, irqs);
else else
...@@ -2154,13 +2158,30 @@ static irqreturn_t sdhci_irq(int irq, void *dev_id) ...@@ -2154,13 +2158,30 @@ static irqreturn_t sdhci_irq(int irq, void *dev_id)
mmc_hostname(host->mmc), intmask); mmc_hostname(host->mmc), intmask);
if (intmask & (SDHCI_INT_CARD_INSERT | SDHCI_INT_CARD_REMOVE)) { if (intmask & (SDHCI_INT_CARD_INSERT | SDHCI_INT_CARD_REMOVE)) {
u32 present = sdhci_readl(host, SDHCI_PRESENT_STATE) &
SDHCI_CARD_PRESENT;
/*
* There is a observation on i.mx esdhc. INSERT bit will be
* immediately set again when it gets cleared, if a card is
* inserted. We have to mask the irq to prevent interrupt
* storm which will freeze the system. And the REMOVE gets
* the same situation.
*
* More testing are needed here to ensure it works for other
* platforms though.
*/
sdhci_mask_irqs(host, present ? SDHCI_INT_CARD_INSERT :
SDHCI_INT_CARD_REMOVE);
sdhci_unmask_irqs(host, present ? SDHCI_INT_CARD_REMOVE :
SDHCI_INT_CARD_INSERT);
sdhci_writel(host, intmask & (SDHCI_INT_CARD_INSERT | sdhci_writel(host, intmask & (SDHCI_INT_CARD_INSERT |
SDHCI_INT_CARD_REMOVE), SDHCI_INT_STATUS); SDHCI_INT_CARD_REMOVE), SDHCI_INT_STATUS);
intmask &= ~(SDHCI_INT_CARD_INSERT | SDHCI_INT_CARD_REMOVE);
tasklet_schedule(&host->card_tasklet); tasklet_schedule(&host->card_tasklet);
} }
intmask &= ~(SDHCI_INT_CARD_INSERT | SDHCI_INT_CARD_REMOVE);
if (intmask & SDHCI_INT_CMD_MASK) { if (intmask & SDHCI_INT_CMD_MASK) {
sdhci_writel(host, intmask & SDHCI_INT_CMD_MASK, sdhci_writel(host, intmask & SDHCI_INT_CMD_MASK,
SDHCI_INT_STATUS); SDHCI_INT_STATUS);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment