sdhci: Add support for card-detection polling
authorAnton Vorontsov <avorontsov@ru.mvista.com>
Mon, 16 Mar 2009 21:13:52 +0000 (00:13 +0300)
committerPierre Ossman <drzeus@drzeus.cx>
Tue, 24 Mar 2009 20:30:08 +0000 (21:30 +0100)
This patch adds SDHCI_QUIRK_BROKEN_CARD_DETECTION quirk. When specified,
sdhci driver will set MMC_CAP_NEEDS_POLL MMC host capability, and won't
enable card insert/remove interrupts.

This is needed for hosts with unreliable card detection, such as FSL
eSDHC. The original eSDHC driver was tring to "debounce" card-detection
IRQs by reading present state and disabling particular interrupts. But
with this debouncing scheme I noticed that sometimes we miss card
insertion/removal events.

Signed-off-by: Anton Vorontsov <avorontsov@ru.mvista.com>
Signed-off-by: Pierre Ossman <drzeus@drzeus.cx>
drivers/mmc/host/sdhci.c
drivers/mmc/host/sdhci.h

index 6fbbc005dd7f39d515bbe70e06c38917f73575da..fc7cb489bdb7336772b6ac01d5f36afec7fbdacf 100644 (file)
@@ -112,6 +112,9 @@ static void sdhci_set_card_detection(struct sdhci_host *host, bool enable)
 {
        u32 irqs = SDHCI_INT_CARD_REMOVE | SDHCI_INT_CARD_INSERT;
 
+       if (host->quirks & SDHCI_QUIRK_BROKEN_CARD_DETECTION)
+               return;
+
        if (enable)
                sdhci_unmask_irqs(host, irqs);
        else
@@ -1041,6 +1044,7 @@ out:
 static void sdhci_request(struct mmc_host *mmc, struct mmc_request *mrq)
 {
        struct sdhci_host *host;
+       bool present;
        unsigned long flags;
 
        host = mmc_priv(mmc);
@@ -1055,8 +1059,14 @@ static void sdhci_request(struct mmc_host *mmc, struct mmc_request *mrq)
 
        host->mrq = mrq;
 
-       if (!(sdhci_readl(host, SDHCI_PRESENT_STATE) & SDHCI_CARD_PRESENT)
-               || (host->flags & SDHCI_DEVICE_DEAD)) {
+       /* If polling, assume that the card is always present. */
+       if (host->quirks & SDHCI_QUIRK_BROKEN_CARD_DETECTION)
+               present = true;
+       else
+               present = sdhci_readl(host, SDHCI_PRESENT_STATE) &
+                               SDHCI_CARD_PRESENT;
+
+       if (!present || host->flags & SDHCI_DEVICE_DEAD) {
                host->mrq->cmd->error = -ENOMEDIUM;
                tasklet_schedule(&host->finish_tasklet);
        } else
@@ -1690,6 +1700,9 @@ int sdhci_add_host(struct sdhci_host *host)
        if (caps & SDHCI_CAN_DO_HISPD)
                mmc->caps |= MMC_CAP_SD_HIGHSPEED;
 
+       if (host->quirks & SDHCI_QUIRK_BROKEN_CARD_DETECTION)
+               mmc->caps |= MMC_CAP_NEEDS_POLL;
+
        mmc->ocr_avail = 0;
        if (caps & SDHCI_CAN_VDD_330)
                mmc->ocr_avail |= MMC_VDD_32_33|MMC_VDD_33_34;
index a9e25c6c0c0e44da696d50393074e175099f0063..968d713950f11730c53381feba3fb5e75a20fb8d 100644 (file)
@@ -214,6 +214,8 @@ struct sdhci_host {
 #define SDHCI_QUIRK_BROKEN_SMALL_PIO                   (1<<13)
 /* Controller does not provide transfer-complete interrupt when not busy */
 #define SDHCI_QUIRK_NO_BUSY_IRQ                                (1<<14)
+/* Controller has unreliable card detection */
+#define SDHCI_QUIRK_BROKEN_CARD_DETECTION              (1<<15)
 
        int                     irq;            /* Device IRQ */
        void __iomem *          ioaddr;         /* Mapped address */