mmc: sdhci-esdhc: enable esdhc on imx53
Richard Zhu [Mon, 21 Mar 2011 05:22:16 +0000 (13:22 +0800)]
Fix the NO INT in the Multi-BLK IO in SD/MMC, and Multi-BLK read in
SDIO on imx53.

The CMDTYPE of the CMD register (offset 0xE) should be set to "11"
when the STOP CMD12 is issued on imx53 to abort one open ended
multi-blk IO. Otherwise the TC INT wouldn't be generated.

In exact block transfer, the controller doesn't complete the
operations automatically as required at the end of the transfer
and remains on hold if the abort command is not sent on imx53.

As a result, the TC flag is not asserted and SW receives timeout
exception. Set bit1 of Vendor Spec register to fix it.

Signed-off-by: Richard Zhu <Hong-Xing.Zhu@freescale.com>
Signed-off-by: Richard Zhao <richard.zhao@freescale.com>
Reviewed-by: Wolfram Sang <w.sang@pengutronix.de>
Signed-off-by: Chris Ball <cjb@laptop.org>

drivers/mmc/host/sdhci-esdhc-imx.c

index 67a2da9..a19967d 100644 (file)
 #include <linux/slab.h>
 #include <linux/mmc/host.h>
 #include <linux/mmc/sdhci-pltfm.h>
+#include <linux/mmc/mmc.h>
+#include <linux/mmc/sdio.h>
 #include <mach/hardware.h>
 #include <mach/esdhc.h>
 #include "sdhci.h"
 #include "sdhci-pltfm.h"
 #include "sdhci-esdhc.h"
 
+/* VENDOR SPEC register */
+#define SDHCI_VENDOR_SPEC              0xC0
+#define  SDHCI_VENDOR_SPEC_SDIO_QUIRK  0x00000002
+
 #define ESDHC_FLAG_GPIO_FOR_CD_WP      (1 << 0)
+/*
+ * The CMDTYPE of the CMD register (offset 0xE) should be set to
+ * "11" when the STOP CMD12 is issued on imx53 to abort one
+ * open ended multi-blk IO. Otherwise the TC INT wouldn't
+ * be generated.
+ * In exact block transfer, the controller doesn't complete the
+ * operations automatically as required at the end of the
+ * transfer and remains on hold if the abort command is not sent.
+ * As a result, the TC flag is not asserted and SW  received timeout
+ * exeception. Bit1 of Vendor Spec registor is used to fix it.
+ */
+#define ESDHC_FLAG_MULTIBLK_NO_INT     (1 << 1)
 
 struct pltfm_imx_data {
        int flags;
@@ -78,6 +96,15 @@ static void esdhc_writel_le(struct sdhci_host *host, u32 val, int reg)
                 */
                val &= ~(SDHCI_INT_CARD_REMOVE | SDHCI_INT_CARD_INSERT);
 
+       if (unlikely((imx_data->flags & ESDHC_FLAG_MULTIBLK_NO_INT)
+                               && (reg == SDHCI_INT_STATUS)
+                               && (val & SDHCI_INT_DATA_END))) {
+                       u32 v;
+                       v = readl(host->ioaddr + SDHCI_VENDOR_SPEC);
+                       v &= ~SDHCI_VENDOR_SPEC_SDIO_QUIRK;
+                       writel(v, host->ioaddr + SDHCI_VENDOR_SPEC);
+       }
+
        writel(val, host->ioaddr + reg);
 }
 
@@ -100,9 +127,21 @@ static void esdhc_writew_le(struct sdhci_host *host, u16 val, int reg)
                 * Postpone this write, we must do it together with a
                 * command write that is down below.
                 */
+               if ((imx_data->flags & ESDHC_FLAG_MULTIBLK_NO_INT)
+                               && (host->cmd->opcode == SD_IO_RW_EXTENDED)
+                               && (host->cmd->data->blocks > 1)
+                               && (host->cmd->data->flags & MMC_DATA_READ)) {
+                       u32 v;
+                       v = readl(host->ioaddr + SDHCI_VENDOR_SPEC);
+                       v |= SDHCI_VENDOR_SPEC_SDIO_QUIRK;
+                       writel(v, host->ioaddr + SDHCI_VENDOR_SPEC);
+               }
                imx_data->scratchpad = val;
                return;
        case SDHCI_COMMAND:
+               if ((host->cmd->opcode == MMC_STOP_TRANSMISSION)
+                       && (imx_data->flags & ESDHC_FLAG_MULTIBLK_NO_INT))
+                       val |= SDHCI_CMD_ABORTCMD;
                writel(val << 16 | imx_data->scratchpad,
                        host->ioaddr + SDHCI_TRANSFER_MODE);
                return;
@@ -215,6 +254,9 @@ static int esdhc_pltfm_init(struct sdhci_host *host, struct sdhci_pltfm_data *pd
                sdhci_esdhc_ops.get_ro = esdhc_pltfm_get_ro;
        }
 
+       if (!(cpu_is_mx25() || cpu_is_mx35() || cpu_is_mx51()))
+               imx_data->flags |= ESDHC_FLAG_MULTIBLK_NO_INT;
+
        if (boarddata) {
                err = gpio_request_one(boarddata->wp_gpio, GPIOF_IN, "ESDHC_WP");
                if (err) {