summaryrefslogtreecommitdiff
path: root/drivers/mmc
diff options
context:
space:
mode:
authorShawn Guo <shawn.guo@linaro.org>2011-06-21 18:41:48 +0400
committerChris Ball <cjb@laptop.org>2011-07-21 01:21:01 +0400
commitd25928d1eed06a9c23c723466dfa7cbee0a5e07d (patch)
tree215f16acf976b8d7df725be06c3f52d6f6da2c9e /drivers/mmc
parente312eb1e66e4357000e4e7438849d5a5fd738219 (diff)
downloadlinux-d25928d1eed06a9c23c723466dfa7cbee0a5e07d.tar.xz
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: Shawn Guo <shawn.guo@linaro.org> Cc: Eric Benard <eric@eukrea.com> Tested-by: Arnaud Patard <arnaud.patard@rtp-net.org> Signed-off-by: Chris Ball <cjb@laptop.org>
Diffstat (limited to 'drivers/mmc')
-rw-r--r--drivers/mmc/host/sdhci.c29
1 files changed, 25 insertions, 4 deletions
diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
index 58d5436ff649..aa25eae65402 100644
--- a/drivers/mmc/host/sdhci.c
+++ b/drivers/mmc/host/sdhci.c
@@ -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)
{
- u32 irqs = SDHCI_INT_CARD_REMOVE | SDHCI_INT_CARD_INSERT;
+ u32 present, irqs;
if (host->quirks & SDHCI_QUIRK_BROKEN_CARD_DETECTION)
return;
+ present = sdhci_readl(host, SDHCI_PRESENT_STATE) &
+ SDHCI_CARD_PRESENT;
+ irqs = present ? SDHCI_INT_CARD_REMOVE : SDHCI_INT_CARD_INSERT;
+
if (enable)
sdhci_unmask_irqs(host, irqs);
else
@@ -2154,13 +2158,30 @@ static irqreturn_t sdhci_irq(int irq, void *dev_id)
mmc_hostname(host->mmc), intmask);
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_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);
}
- intmask &= ~(SDHCI_INT_CARD_INSERT | SDHCI_INT_CARD_REMOVE);
-
if (intmask & SDHCI_INT_CMD_MASK) {
sdhci_writel(host, intmask & SDHCI_INT_CMD_MASK,
SDHCI_INT_STATUS);