mmc: mmci: Enable support for busy detection for ux500 variant
authorUlf Hansson <ulf.hansson@linaro.org>
Mon, 13 Jan 2014 15:49:31 +0000 (16:49 +0100)
committerChris Ball <chris@printf.net>
Sun, 23 Feb 2014 15:41:21 +0000 (10:41 -0500)
The ux500 variants have HW busy detection support, which is indicated
by the busy_detect flag. For these variants let's enable the
MMC_CAP_WAIT_WHILE_BUSY flag and add the support for it.

The mmc core will provide the RSP_BUSY command flag for those requests
we should care about busy detection. Regarding the max_busy_timeout,
the HW don't support busy detection timeouts so at this initial step
let's make it simple and set it to zero to indicate we are able to
support any timeout.

Cc: Russell King <linux@arm.linux.org.uk>
Cc: Johan Rudholm <jrudholm@gmail.com>
Signed-off-by: Ulf Hansson <ulf.hansson@linaro.org>
Signed-off-by: Chris Ball <chris@printf.net>
drivers/mmc/host/mmci.c
drivers/mmc/host/mmci.h

index 8324e3ec21c29c0fe84829865f47923221f7162e..771c60ab4a320a3edc222b108c49d14a8e0076b1 100644 (file)
@@ -921,6 +921,29 @@ mmci_cmd_irq(struct mmci_host *host, struct mmc_command *cmd,
 {
        void __iomem *base = host->base;
        bool sbc = (cmd == host->mrq->sbc);
+       bool busy_resp = host->variant->busy_detect &&
+                       (cmd->flags & MMC_RSP_BUSY);
+
+       /* Check if we need to wait for busy completion. */
+       if (host->busy_status && (status & MCI_ST_CARDBUSY))
+               return;
+
+       /* Enable busy completion if needed and supported. */
+       if (!host->busy_status && busy_resp &&
+               !(status & (MCI_CMDCRCFAIL|MCI_CMDTIMEOUT)) &&
+               (readl(base + MMCISTATUS) & MCI_ST_CARDBUSY)) {
+               writel(readl(base + MMCIMASK0) | MCI_ST_BUSYEND,
+                       base + MMCIMASK0);
+               host->busy_status = status & (MCI_CMDSENT|MCI_CMDRESPEND);
+               return;
+       }
+
+       /* At busy completion, mask the IRQ and complete the request. */
+       if (host->busy_status) {
+               writel(readl(base + MMCIMASK0) & ~MCI_ST_BUSYEND,
+                       base + MMCIMASK0);
+               host->busy_status = 0;
+       }
 
        host->cmd = NULL;
 
@@ -1139,14 +1162,19 @@ static irqreturn_t mmci_irq(int irq, void *dev_id)
                        status &= ~MCI_IRQ1MASK;
                }
 
+               /*
+                * We intentionally clear the MCI_ST_CARDBUSY IRQ here (if it's
+                * enabled) since the HW seems to be triggering the IRQ on both
+                * edges while monitoring DAT0 for busy completion.
+                */
                status &= readl(host->base + MMCIMASK0);
                writel(status, host->base + MMCICLEAR);
 
                dev_dbg(mmc_dev(host->mmc), "irq0 (data+cmd) %08x\n", status);
 
                cmd = host->cmd;
-               if (status & (MCI_CMDCRCFAIL|MCI_CMDTIMEOUT|MCI_CMDSENT|
-                             MCI_CMDRESPEND) && cmd)
+               if ((status|host->busy_status) & (MCI_CMDCRCFAIL|MCI_CMDTIMEOUT|
+                       MCI_CMDSENT|MCI_CMDRESPEND) && cmd)
                        mmci_cmd_irq(host, cmd, status);
 
                data = host->data;
@@ -1155,6 +1183,10 @@ static irqreturn_t mmci_irq(int irq, void *dev_id)
                              MCI_DATABLOCKEND) && data)
                        mmci_data_irq(host, data, status);
 
+               /* Don't poll for busy completion in irq context. */
+               if (host->busy_status)
+                       status &= ~MCI_ST_CARDBUSY;
+
                ret = 1;
        } while (status);
 
@@ -1504,12 +1536,6 @@ static int mmci_probe(struct amba_device *dev,
                goto clk_disable;
        }
 
-       if (variant->busy_detect) {
-               mmci_ops.card_busy = mmci_card_busy;
-               mmci_write_datactrlreg(host, MCI_ST_DPSM_BUSYMODE);
-       }
-
-       mmc->ops = &mmci_ops;
        /*
         * The ARM and ST versions of the block have slightly different
         * clock divider equations which means that the minimum divider
@@ -1543,6 +1569,15 @@ static int mmci_probe(struct amba_device *dev,
        mmc->caps = plat->capabilities;
        mmc->caps2 = plat->capabilities2;
 
+       if (variant->busy_detect) {
+               mmci_ops.card_busy = mmci_card_busy;
+               mmci_write_datactrlreg(host, MCI_ST_DPSM_BUSYMODE);
+               mmc->caps |= MMC_CAP_WAIT_WHILE_BUSY;
+               mmc->max_busy_timeout = 0;
+       }
+
+       mmc->ops = &mmci_ops;
+
        /* We support these PM capabilities. */
        mmc->pm_caps = MMC_PM_KEEP_POWER;
 
index 168bc72f7a94a9b662d7c0a97775c781d4d769aa..b008ace3e8ee6b7ac37302c018174671907e0f63 100644 (file)
 /* Extended status bits for the ST Micro variants */
 #define MCI_ST_SDIOITMASK      (1 << 22)
 #define MCI_ST_CEATAENDMASK    (1 << 23)
+#define MCI_ST_BUSYEND         (1 << 24)
 
 #define MMCIMASK1              0x040
 #define MMCIFIFOCNT            0x048
@@ -186,6 +187,7 @@ struct mmci_host {
        u32                     pwr_reg;
        u32                     clk_reg;
        u32                     datactrl_reg;
+       u32                     busy_status;
        bool                    vqmmc_enabled;
        struct mmci_platform_data *plat;
        struct variant_data     *variant;