fsl_esdhc: Add the workaround for erratum ESDHC135 (enable on P4080)
[platform/kernel/u-boot.git] / drivers / mmc / fsl_esdhc.c
index 0f6f8b1..d01c926 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright 2007,2010 Freescale Semiconductor, Inc
+ * Copyright 2007, 2010-2011 Freescale Semiconductor, Inc
  * Andy Fleming
  *
  * Based vaguely on the pxa mmc code:
@@ -72,11 +72,16 @@ uint esdhc_xfertyp(struct mmc_cmd *cmd, struct mmc_data *data)
        uint xfertyp = 0;
 
        if (data) {
-               xfertyp |= XFERTYP_DPSEL | XFERTYP_DMAEN;
-
+               xfertyp |= XFERTYP_DPSEL;
+#ifndef CONFIG_SYS_FSL_ESDHC_USE_PIO
+               xfertyp |= XFERTYP_DMAEN;
+#endif
                if (data->blocks > 1) {
                        xfertyp |= XFERTYP_MSBSEL;
                        xfertyp |= XFERTYP_BCEN;
+#ifdef CONFIG_SYS_FSL_ERRATUM_ESDHC111
+                       xfertyp |= XFERTYP_AC12EN;
+#endif
                }
 
                if (data->flags & MMC_DATA_READ)
@@ -97,12 +102,78 @@ uint esdhc_xfertyp(struct mmc_cmd *cmd, struct mmc_data *data)
        return XFERTYP_CMD(cmd->cmdidx) | xfertyp;
 }
 
+#ifdef CONFIG_SYS_FSL_ESDHC_USE_PIO
+/*
+ * PIO Read/Write Mode reduce the performace as DMA is not used in this mode.
+ */
+static void
+esdhc_pio_read_write(struct mmc *mmc, struct mmc_data *data)
+{
+       struct fsl_esdhc *regs = mmc->priv;
+       uint blocks;
+       char *buffer;
+       uint databuf;
+       uint size;
+       uint irqstat;
+       uint timeout;
+
+       if (data->flags & MMC_DATA_READ) {
+               blocks = data->blocks;
+               buffer = data->dest;
+               while (blocks) {
+                       timeout = PIO_TIMEOUT;
+                       size = data->blocksize;
+                       irqstat = esdhc_read32(&regs->irqstat);
+                       while (!(esdhc_read32(&regs->prsstat) & PRSSTAT_BREN)
+                               && --timeout);
+                       if (timeout <= 0) {
+                               printf("\nData Read Failed in PIO Mode.");
+                               return;
+                       }
+                       while (size && (!(irqstat & IRQSTAT_TC))) {
+                               udelay(100); /* Wait before last byte transfer complete */
+                               irqstat = esdhc_read32(&regs->irqstat);
+                               databuf = in_le32(&regs->datport);
+                               *((uint *)buffer) = databuf;
+                               buffer += 4;
+                               size -= 4;
+                       }
+                       blocks--;
+               }
+       } else {
+               blocks = data->blocks;
+               buffer = (char *)data->src;
+               while (blocks) {
+                       timeout = PIO_TIMEOUT;
+                       size = data->blocksize;
+                       irqstat = esdhc_read32(&regs->irqstat);
+                       while (!(esdhc_read32(&regs->prsstat) & PRSSTAT_BWEN)
+                               && --timeout);
+                       if (timeout <= 0) {
+                               printf("\nData Write Failed in PIO Mode.");
+                               return;
+                       }
+                       while (size && (!(irqstat & IRQSTAT_TC))) {
+                               udelay(100); /* Wait before last byte transfer complete */
+                               databuf = *((uint *)buffer);
+                               buffer += 4;
+                               size -= 4;
+                               irqstat = esdhc_read32(&regs->irqstat);
+                               out_le32(&regs->datport, databuf);
+                       }
+                       blocks--;
+               }
+       }
+}
+#endif
+
 static int esdhc_setup_data(struct mmc *mmc, struct mmc_data *data)
 {
-       uint wml_value;
        int timeout;
        struct fsl_esdhc_cfg *cfg = (struct fsl_esdhc_cfg *)mmc->priv;
        struct fsl_esdhc *regs = (struct fsl_esdhc *)cfg->esdhc_base;
+#ifndef CONFIG_SYS_FSL_ESDHC_USE_PIO
+       uint wml_value;
 
        wml_value = data->blocksize/4;
 
@@ -124,6 +195,17 @@ static int esdhc_setup_data(struct mmc *mmc, struct mmc_data *data)
                                        wml_value << 16);
                esdhc_write32(&regs->dsaddr, (u32)data->src);
        }
+#else  /* CONFIG_SYS_FSL_ESDHC_USE_PIO */
+       if (!(data->flags & MMC_DATA_READ)) {
+               if ((esdhc_read32(&regs->prsstat) & PRSSTAT_WPSPL) == 0) {
+                       printf("\nThe SD card is locked. "
+                               "Can not write to a locked card.\n\n");
+                       return TIMEOUT;
+               }
+               esdhc_write32(&regs->dsaddr, (u32)data->src);
+       } else
+               esdhc_write32(&regs->dsaddr, (u32)data->dest);
+#endif /* CONFIG_SYS_FSL_ESDHC_USE_PIO */
 
        esdhc_write32(&regs->blkattr, data->blocks << 16 | data->blocksize);
 
@@ -155,6 +237,11 @@ esdhc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, struct mmc_data *data)
        struct fsl_esdhc_cfg *cfg = (struct fsl_esdhc_cfg *)mmc->priv;
        volatile struct fsl_esdhc *regs = (struct fsl_esdhc *)cfg->esdhc_base;
 
+#ifdef CONFIG_SYS_FSL_ERRATUM_ESDHC111
+       if (cmd->cmdidx == MMC_CMD_STOP_TRANSMISSION)
+               return 0;
+#endif
+
        esdhc_write32(&regs->irqstat, -1);
 
        sync();
@@ -220,6 +307,9 @@ esdhc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, struct mmc_data *data)
 
        /* Wait until all of the blocks are transferred */
        if (data) {
+#ifdef CONFIG_SYS_FSL_ESDHC_USE_PIO
+               esdhc_pio_read_write(mmc, data);
+#else
                do {
                        irqstat = esdhc_read32(&regs->irqstat);
 
@@ -230,6 +320,7 @@ esdhc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, struct mmc_data *data)
                                return TIMEOUT;
                } while (!(irqstat & IRQSTAT_TC) &&
                                (esdhc_read32(&regs->prsstat) & PRSSTAT_DLA));
+#endif
        }
 
        esdhc_write32(&regs->irqstat, -1);
@@ -301,10 +392,6 @@ static int esdhc_init(struct mmc *mmc)
        int ret = 0;
        u8 card_absent;
 
-       /* Enable cache snooping */
-       if (cfg && !cfg->no_snoop)
-               esdhc_write32(&regs->scr, 0x00000040);
-
        /* Reset the entire host controller */
        esdhc_write32(&regs->sysctl, SYSCTL_RSTA);
 
@@ -312,10 +399,14 @@ static int esdhc_init(struct mmc *mmc)
        while ((esdhc_read32(&regs->sysctl) & SYSCTL_RSTA) && --timeout)
                udelay(1000);
 
+       /* Enable cache snooping */
+       if (cfg && !cfg->no_snoop)
+               esdhc_write32(&regs->scr, 0x00000040);
+
        esdhc_write32(&regs->sysctl, SYSCTL_HCKEN | SYSCTL_IPGEN);
 
        /* Set the initial clock speed */
-       set_sysctl(mmc, 400000);
+       mmc_set_clock(mmc, 400000);
 
        /* Disable the BRR and BWR bits in IRQSTAT */
        esdhc_clrbits32(&regs->irqstaten, IRQSTATEN_BRR | IRQSTATEN_BWR);
@@ -361,7 +452,7 @@ int fsl_esdhc_initialize(bd_t *bis, struct fsl_esdhc_cfg *cfg)
 {
        struct fsl_esdhc *regs;
        struct mmc *mmc;
-       u32 caps;
+       u32 caps, voltage_caps;
 
        if (!cfg)
                return -1;
@@ -379,14 +470,29 @@ int fsl_esdhc_initialize(bd_t *bis, struct fsl_esdhc_cfg *cfg)
        mmc->set_ios = esdhc_set_ios;
        mmc->init = esdhc_init;
 
+       voltage_caps = 0;
        caps = regs->hostcapblt;
 
+#ifdef CONFIG_SYS_FSL_ERRATUM_ESDHC135
+       caps = caps & ~(ESDHC_HOSTCAPBLT_SRS |
+                       ESDHC_HOSTCAPBLT_VS18 | ESDHC_HOSTCAPBLT_VS30);
+#endif
        if (caps & ESDHC_HOSTCAPBLT_VS18)
-               mmc->voltages |= MMC_VDD_165_195;
+               voltage_caps |= MMC_VDD_165_195;
        if (caps & ESDHC_HOSTCAPBLT_VS30)
-               mmc->voltages |= MMC_VDD_29_30 | MMC_VDD_30_31;
+               voltage_caps |= MMC_VDD_29_30 | MMC_VDD_30_31;
        if (caps & ESDHC_HOSTCAPBLT_VS33)
-               mmc->voltages |= MMC_VDD_32_33 | MMC_VDD_33_34;
+               voltage_caps |= MMC_VDD_32_33 | MMC_VDD_33_34;
+
+#ifdef CONFIG_SYS_SD_VOLTAGE
+       mmc->voltages = CONFIG_SYS_SD_VOLTAGE;
+#else
+       mmc->voltages = MMC_VDD_32_33 | MMC_VDD_33_34;
+#endif
+       if ((mmc->voltages & voltage_caps) == 0) {
+               printf("voltage not supported by controller\n");
+               return -1;
+       }
 
        mmc->host_caps = MMC_MODE_4BIT | MMC_MODE_8BIT;
 
@@ -394,7 +500,7 @@ int fsl_esdhc_initialize(bd_t *bis, struct fsl_esdhc_cfg *cfg)
                mmc->host_caps |= MMC_MODE_HS_52MHz | MMC_MODE_HS;
 
        mmc->f_min = 400000;
-       mmc->f_max = MIN(gd->sdhc_clk, 50000000);
+       mmc->f_max = MIN(gd->sdhc_clk, 52000000);
 
        mmc_register(mmc);
 
@@ -415,17 +521,19 @@ int fsl_esdhc_mmc_init(bd_t *bis)
 void fdt_fixup_esdhc(void *blob, bd_t *bd)
 {
        const char *compat = "fsl,esdhc";
-       const char *status = "okay";
 
+#ifdef CONFIG_FSL_ESDHC_PIN_MUX
        if (!hwconfig("esdhc")) {
-               status = "disabled";
-               goto out;
+               do_fixup_by_compat(blob, compat, "status", "disabled",
+                               8 + 1, 1);
+               return;
        }
+#endif
 
        do_fixup_by_compat_u32(blob, compat, "clock-frequency",
                               gd->sdhc_clk, 1);
-out:
-       do_fixup_by_compat(blob, compat, "status", status,
-                          strlen(status) + 1, 1);
+
+       do_fixup_by_compat(blob, compat, "status", "okay",
+                          4 + 1, 1);
 }
 #endif