mmc: sdhci-esdhc-imx: add basic imx6q usdhc support
authorShawn Guo <shawn.guo@linaro.org>
Mon, 19 Sep 2011 09:32:21 +0000 (17:32 +0800)
committerChris Ball <cjb@laptop.org>
Wed, 26 Oct 2011 20:32:10 +0000 (16:32 -0400)
This patch adds the basic support for imx6q usdhc, which is a
derivative of esdhc controller.

Signed-off-by: Shawn Guo <shawn.guo@linaro.org>
Signed-off-by: Chris Ball <cjb@laptop.org>
drivers/mmc/host/Kconfig
drivers/mmc/host/sdhci-esdhc-imx.c

index c226d549fd2fcd06cd3edf673140c19bdeb53fc2..87d5067ba629a13b8ce96e602ac88f5734a45973 100644 (file)
@@ -130,13 +130,13 @@ config MMC_SDHCI_CNS3XXX
          If unsure, say N.
 
 config MMC_SDHCI_ESDHC_IMX
-       tristate "SDHCI platform support for the Freescale eSDHC i.MX controller"
+       tristate "SDHCI support for the Freescale eSDHC/uSDHC i.MX controller"
        depends on ARCH_MXC
        depends on MMC_SDHCI_PLTFM
        select MMC_SDHCI_IO_ACCESSORS
        help
-         This selects the Freescale eSDHC controller support on the platform
-         bus, found on i.MX25, i.MX35 and i.MX5x.
+         This selects the Freescale eSDHC/uSDHC controller support
+         found on i.MX25, i.MX35 i.MX5x and i.MX6x.
 
          If you have a controller with this interface, say Y or M here.
 
index 4557aa1567a596b417136c1e19f4ce81124da04d..ae57769ba50d53c129bc1b6399b92db37c9bc010 100644 (file)
@@ -32,6 +32,7 @@
 /* VENDOR SPEC register */
 #define SDHCI_VENDOR_SPEC              0xC0
 #define  SDHCI_VENDOR_SPEC_SDIO_QUIRK  0x00000002
+#define SDHCI_MIX_CTRL                 0x48
 
 /*
  * There is an INT DMA ERR mis-match between eSDHC and STD SDHC SPEC:
@@ -59,6 +60,7 @@ enum imx_esdhc_type {
        IMX35_ESDHC,
        IMX51_ESDHC,
        IMX53_ESDHC,
+       IMX6Q_USDHC,
 };
 
 struct pltfm_imx_data {
@@ -81,6 +83,9 @@ static struct platform_device_id imx_esdhc_devtype[] = {
        }, {
                .name = "sdhci-esdhc-imx53",
                .driver_data = IMX53_ESDHC,
+       }, {
+               .name = "sdhci-usdhc-imx6q",
+               .driver_data = IMX6Q_USDHC,
        }, {
                /* sentinel */
        }
@@ -92,6 +97,7 @@ static const struct of_device_id imx_esdhc_dt_ids[] = {
        { .compatible = "fsl,imx35-esdhc", .data = &imx_esdhc_devtype[IMX35_ESDHC], },
        { .compatible = "fsl,imx51-esdhc", .data = &imx_esdhc_devtype[IMX51_ESDHC], },
        { .compatible = "fsl,imx53-esdhc", .data = &imx_esdhc_devtype[IMX53_ESDHC], },
+       { .compatible = "fsl,imx6q-usdhc", .data = &imx_esdhc_devtype[IMX6Q_USDHC], },
        { /* sentinel */ }
 };
 MODULE_DEVICE_TABLE(of, imx_esdhc_dt_ids);
@@ -116,6 +122,11 @@ static inline int is_imx53_esdhc(struct pltfm_imx_data *data)
        return data->devtype == IMX53_ESDHC;
 }
 
+static inline int is_imx6q_usdhc(struct pltfm_imx_data *data)
+{
+       return data->devtype == IMX6Q_USDHC;
+}
+
 static inline void esdhc_clrset_le(struct sdhci_host *host, u32 mask, u32 val, int reg)
 {
        void __iomem *base = host->ioaddr + (reg & ~0x3);
@@ -220,8 +231,16 @@ static void esdhc_writel_le(struct sdhci_host *host, u32 val, int reg)
 
 static u16 esdhc_readw_le(struct sdhci_host *host, int reg)
 {
-       if (unlikely(reg == SDHCI_HOST_VERSION))
-               reg ^= 2;
+       if (unlikely(reg == SDHCI_HOST_VERSION)) {
+               u16 val = readw(host->ioaddr + (reg ^ 2));
+               /*
+                * uSDHC supports SDHCI v3.0, but it's encoded as value
+                * 0x3 in host controller version register, which violates
+                * SDHCI_SPEC_300 definition.  Work it around here.
+                */
+               if ((val & SDHCI_SPEC_VER_MASK) == 3)
+                       return --val;
+       }
 
        return readw(host->ioaddr + reg);
 }
@@ -252,8 +271,17 @@ static void esdhc_writew_le(struct sdhci_host *host, u16 val, int reg)
                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);
+
+               if (is_imx6q_usdhc(imx_data)) {
+                       u32 m = readl(host->ioaddr + SDHCI_MIX_CTRL);
+                       m = imx_data->scratchpad | (m & 0xffff0000);
+                       writel(m, host->ioaddr + SDHCI_MIX_CTRL);
+                       writel(val << 16,
+                              host->ioaddr + SDHCI_TRANSFER_MODE);
+               } else {
+                       writel(val << 16 | imx_data->scratchpad,
+                              host->ioaddr + SDHCI_TRANSFER_MODE);
+               }
                return;
        case SDHCI_BLOCK_SIZE:
                val &= ~SDHCI_MAKE_BLKSZ(0x7, 0);