Merge git://git.kernel.org/pub/scm/linux/kernel/git/davem/net-next
[pandora-kernel.git] / drivers / mmc / host / sdhci.c
index 58d5436..c31a334 100644 (file)
@@ -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);
@@ -2488,6 +2509,11 @@ int sdhci_add_host(struct sdhci_host *host)
        } else
                mmc->f_min = host->max_clk / SDHCI_MAX_DIV_SPEC_200;
 
+       if (host->quirks & SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK)
+               mmc->max_discard_to = (1 << 27) / (mmc->f_max / 1000);
+       else
+               mmc->max_discard_to = (1 << 27) / host->timeout_clk;
+
        mmc->caps |= MMC_CAP_SDIO_IRQ | MMC_CAP_ERASE | MMC_CAP_CMD23;
 
        if (host->quirks & SDHCI_QUIRK_MULTIBLOCK_READ_ACMD12)