aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/mmc/host/sdhci.c
diff options
context:
space:
mode:
authorShawn Guo <shawn.guo@linaro.org>2011-06-21 10:41:48 -0400
committerChris Ball <cjb@laptop.org>2011-07-20 17:21:01 -0400
commitd25928d1eed06a9c23c723466dfa7cbee0a5e07d (patch)
tree215f16acf976b8d7df725be06c3f52d6f6da2c9e /drivers/mmc/host/sdhci.c
parente312eb1e66e4357000e4e7438849d5a5fd738219 (diff)
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/host/sdhci.c')
-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 58d5436ff64..aa25eae6540 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)
127 127
128static void sdhci_set_card_detection(struct sdhci_host *host, bool enable) 128static void sdhci_set_card_detection(struct sdhci_host *host, bool enable)
129{ 129{
130 u32 irqs = SDHCI_INT_CARD_REMOVE | SDHCI_INT_CARD_INSERT; 130 u32 present, irqs;
131 131
132 if (host->quirks & SDHCI_QUIRK_BROKEN_CARD_DETECTION) 132 if (host->quirks & SDHCI_QUIRK_BROKEN_CARD_DETECTION)
133 return; 133 return;
134 134
135 present = sdhci_readl(host, SDHCI_PRESENT_STATE) &
136 SDHCI_CARD_PRESENT;
137 irqs = present ? SDHCI_INT_CARD_REMOVE : SDHCI_INT_CARD_INSERT;
138
135 if (enable) 139 if (enable)
136 sdhci_unmask_irqs(host, irqs); 140 sdhci_unmask_irqs(host, irqs);
137 else 141 else
@@ -2154,13 +2158,30 @@ static irqreturn_t sdhci_irq(int irq, void *dev_id)
2154 mmc_hostname(host->mmc), intmask); 2158 mmc_hostname(host->mmc), intmask);
2155 2159
2156 if (intmask & (SDHCI_INT_CARD_INSERT | SDHCI_INT_CARD_REMOVE)) { 2160 if (intmask & (SDHCI_INT_CARD_INSERT | SDHCI_INT_CARD_REMOVE)) {
2161 u32 present = sdhci_readl(host, SDHCI_PRESENT_STATE) &
2162 SDHCI_CARD_PRESENT;
2163
2164 /*
2165 * There is a observation on i.mx esdhc. INSERT bit will be
2166 * immediately set again when it gets cleared, if a card is
2167 * inserted. We have to mask the irq to prevent interrupt
2168 * storm which will freeze the system. And the REMOVE gets
2169 * the same situation.
2170 *
2171 * More testing are needed here to ensure it works for other
2172 * platforms though.
2173 */
2174 sdhci_mask_irqs(host, present ? SDHCI_INT_CARD_INSERT :
2175 SDHCI_INT_CARD_REMOVE);
2176 sdhci_unmask_irqs(host, present ? SDHCI_INT_CARD_REMOVE :
2177 SDHCI_INT_CARD_INSERT);
2178
2157 sdhci_writel(host, intmask & (SDHCI_INT_CARD_INSERT | 2179 sdhci_writel(host, intmask & (SDHCI_INT_CARD_INSERT |
2158 SDHCI_INT_CARD_REMOVE), SDHCI_INT_STATUS); 2180 SDHCI_INT_CARD_REMOVE), SDHCI_INT_STATUS);
2181 intmask &= ~(SDHCI_INT_CARD_INSERT | SDHCI_INT_CARD_REMOVE);
2159 tasklet_schedule(&host->card_tasklet); 2182 tasklet_schedule(&host->card_tasklet);
2160 } 2183 }
2161 2184
2162 intmask &= ~(SDHCI_INT_CARD_INSERT | SDHCI_INT_CARD_REMOVE);
2163
2164 if (intmask & SDHCI_INT_CMD_MASK) { 2185 if (intmask & SDHCI_INT_CMD_MASK) {
2165 sdhci_writel(host, intmask & SDHCI_INT_CMD_MASK, 2186 sdhci_writel(host, intmask & SDHCI_INT_CMD_MASK,
2166 SDHCI_INT_STATUS); 2187 SDHCI_INT_STATUS);