Support tuning process for eMMC HS200 for eSDHC.

Signed-off-by: Yangbo Lu <yangbo...@nxp.com>
---
Changes for v2:
        - None.
Changes for v3:
        - Rebased.
Changes for v4:
        - None.
---
 drivers/mmc/fsl_esdhc.c | 106 ++++++++++++++++++++++++++++++++++++++++++++++--
 include/fsl_esdhc.h     |  17 ++++++--
 2 files changed, 116 insertions(+), 7 deletions(-)

diff --git a/drivers/mmc/fsl_esdhc.c b/drivers/mmc/fsl_esdhc.c
index 83feaf1..4e04c10 100644
--- a/drivers/mmc/fsl_esdhc.c
+++ b/drivers/mmc/fsl_esdhc.c
@@ -60,7 +60,9 @@ struct fsl_esdhc {
        uint    dmaerrattr;     /* DMA error attribute register */
        char    reserved5[4];   /* reserved */
        uint    hostcapblt2;    /* Host controller capabilities register 2 */
-       char    reserved6[756]; /* reserved */
+       char    reserved6[8];   /* reserved */
+       uint    tbctl;          /* Tuning block control register */
+       char    reserved7[744]; /* reserved */
        uint    esdhcctl;       /* eSDHC control register */
 };
 
@@ -101,7 +103,9 @@ static uint esdhc_xfertyp(struct mmc_cmd *cmd, struct 
mmc_data *data)
        if (data) {
                xfertyp |= XFERTYP_DPSEL;
 #ifndef CONFIG_SYS_FSL_ESDHC_USE_PIO
-               xfertyp |= XFERTYP_DMAEN;
+               if (cmd->cmdidx != MMC_CMD_SEND_TUNING_BLOCK &&
+                   cmd->cmdidx != MMC_CMD_SEND_TUNING_BLOCK_HS200)
+                       xfertyp |= XFERTYP_DMAEN;
 #endif
                if (data->blocks > 1) {
                        xfertyp |= XFERTYP_MSBSEL;
@@ -380,6 +384,10 @@ static int esdhc_send_cmd_common(struct fsl_esdhc_priv 
*priv, struct mmc *mmc,
        esdhc_write32(&regs->cmdarg, cmd->cmdarg);
        esdhc_write32(&regs->xfertyp, xfertyp);
 
+       if (cmd->cmdidx == MMC_CMD_SEND_TUNING_BLOCK ||
+           cmd->cmdidx == MMC_CMD_SEND_TUNING_BLOCK_HS200)
+               flags = IRQSTAT_BRR;
+
        /* Wait for the command to complete */
        start = get_timer(0);
        while (!(esdhc_read32(&regs->irqstat) & flags)) {
@@ -439,6 +447,11 @@ static int esdhc_send_cmd_common(struct fsl_esdhc_priv 
*priv, struct mmc *mmc,
 #ifdef CONFIG_SYS_FSL_ESDHC_USE_PIO
                esdhc_pio_read_write(priv, data);
 #else
+               flags = DATA_COMPLETE;
+               if (cmd->cmdidx == MMC_CMD_SEND_TUNING_BLOCK ||
+                   cmd->cmdidx == MMC_CMD_SEND_TUNING_BLOCK_HS200)
+                       flags = IRQSTAT_BRR;
+
                do {
                        irqstat = esdhc_read32(&regs->irqstat);
 
@@ -451,7 +464,7 @@ static int esdhc_send_cmd_common(struct fsl_esdhc_priv 
*priv, struct mmc *mmc,
                                err = -ECOMM;
                                goto out;
                        }
-               } while ((irqstat & DATA_COMPLETE) != DATA_COMPLETE);
+               } while ((irqstat & flags) != flags);
 
                /*
                 * Need invalidate the dcache here again to avoid any
@@ -555,6 +568,19 @@ static void esdhc_clock_control(struct fsl_esdhc_priv 
*priv, bool enable)
        }
 }
 
+static void esdhc_set_timing(struct fsl_esdhc_priv *priv, enum bus_mode mode)
+{
+       struct fsl_esdhc *regs = priv->esdhc_regs;
+
+       esdhc_clock_control(priv, false);
+
+       if (mode == MMC_HS_200)
+               esdhc_clrsetbits32(&regs->autoc12err, UHSM_MASK,
+                                  UHSM_SDR104_HS200);
+
+       esdhc_clock_control(priv, true);
+}
+
 static int esdhc_set_ios_common(struct fsl_esdhc_priv *priv, struct mmc *mmc)
 {
        struct fsl_esdhc *regs = priv->esdhc_regs;
@@ -570,6 +596,9 @@ static int esdhc_set_ios_common(struct fsl_esdhc_priv 
*priv, struct mmc *mmc)
        if (priv->clock != mmc->clock)
                set_sysctl(priv, mmc, mmc->clock);
 
+       /* Set timing */
+       esdhc_set_timing(priv, mmc->selected_mode);
+
        /* Set the bus width */
        esdhc_clrbits32(&regs->proctl, PROCTL_DTW_4 | PROCTL_DTW_8);
 
@@ -915,6 +944,77 @@ static int fsl_esdhc_reinit(struct udevice *dev)
        return esdhc_init_common(priv, &plat->mmc);
 }
 
+#ifdef MMC_SUPPORTS_TUNING
+static void esdhc_flush_async_fifo(struct fsl_esdhc_priv *priv)
+{
+       struct fsl_esdhc *regs = priv->esdhc_regs;
+       u32 time_out;
+
+       esdhc_setbits32(&regs->esdhcctl, ESDHCCTL_FAF);
+
+       time_out = 20;
+       while (esdhc_read32(&regs->esdhcctl) & ESDHCCTL_FAF) {
+               if (time_out == 0) {
+                       printf("fsl_esdhc: Flush asynchronous FIFO timeout.\n");
+                       break;
+               }
+               time_out--;
+               mdelay(1);
+       }
+}
+
+static void esdhc_tuning_block_enable(struct fsl_esdhc_priv *priv,
+                                     bool en)
+{
+       struct fsl_esdhc *regs = priv->esdhc_regs;
+
+       esdhc_clock_control(priv, false);
+       esdhc_flush_async_fifo(priv);
+       if (en)
+               esdhc_setbits32(&regs->tbctl, TBCTL_TB_EN);
+       else
+               esdhc_clrbits32(&regs->tbctl, TBCTL_TB_EN);
+       esdhc_clock_control(priv, true);
+}
+
+static int fsl_esdhc_execute_tuning(struct udevice *dev, uint32_t opcode)
+{
+       struct fsl_esdhc_plat *plat = dev_get_platdata(dev);
+       struct fsl_esdhc_priv *priv = dev_get_priv(dev);
+       struct fsl_esdhc *regs = priv->esdhc_regs;
+       u32 val, irqstaten;
+       int i;
+
+       esdhc_tuning_block_enable(priv, true);
+       esdhc_setbits32(&regs->autoc12err, EXECUTE_TUNING);
+
+       irqstaten = esdhc_read32(&regs->irqstaten);
+       esdhc_write32(&regs->irqstaten, IRQSTATEN_BRR);
+
+       for (i = 0; i < MAX_TUNING_LOOP; i++) {
+               mmc_send_tuning(&plat->mmc, opcode, NULL);
+               mdelay(1);
+
+               val = esdhc_read32(&regs->autoc12err);
+               if (!(val & EXECUTE_TUNING)) {
+                       if (val & SMPCLKSEL)
+                               break;
+               }
+       }
+
+       esdhc_write32(&regs->irqstaten, irqstaten);
+
+       if (i != MAX_TUNING_LOOP)
+               return 0;
+
+       printf("fsl_esdhc: tuning failed!\n");
+       esdhc_clrbits32(&regs->autoc12err, SMPCLKSEL);
+       esdhc_clrbits32(&regs->autoc12err, EXECUTE_TUNING);
+       esdhc_tuning_block_enable(priv, false);
+       return -ETIMEDOUT;
+}
+#endif
+
 static const struct dm_mmc_ops fsl_esdhc_ops = {
        .get_cd         = fsl_esdhc_get_cd,
        .send_cmd       = fsl_esdhc_send_cmd,
diff --git a/include/fsl_esdhc.h b/include/fsl_esdhc.h
index 7f8f8ed..0a426e1 100644
--- a/include/fsl_esdhc.h
+++ b/include/fsl_esdhc.h
@@ -74,8 +74,10 @@
 #define IRQSTATEN_TC           (0x00000002)
 #define IRQSTATEN_CC           (0x00000001)
 
+/* eSDHC control register */
 #define ESDHCCTL               0x0002e40c
 #define ESDHCCTL_PCS           (0x00080000)
+#define ESDHCCTL_FAF           (0x00040000)
 
 #define PRSSTAT                        0x0002e024
 #define PRSSTAT_DAT0           (0x01000000)
@@ -154,6 +156,12 @@
 #define BLKATTR_SIZE(x)        (x & 0x1fff)
 #define MAX_BLK_CNT    0x7fff  /* so malloc will have enough room with 32M */
 
+/* Auto CMD error status register / system control 2 register */
+#define EXECUTE_TUNING         0x00400000
+#define SMPCLKSEL              0x00800000
+#define UHSM_MASK              0x00070000
+#define UHSM_SDR104_HS200      0x00030000
+
 /* Host controller capabilities register */
 #define HOSTCAPBLT_VS18                0x04000000
 #define HOSTCAPBLT_VS30                0x02000000
@@ -162,6 +170,11 @@
 #define HOSTCAPBLT_DMAS                0x00400000
 #define HOSTCAPBLT_HSS         0x00200000
 
+/* Tuning block control register */
+#define TBCTL_TB_EN            0x00000004
+
+#define MAX_TUNING_LOOP                40
+
 struct fsl_esdhc_cfg {
        phys_addr_t esdhc_base;
        u32     sdhc_clk;
@@ -203,10 +216,6 @@ struct fsl_esdhc_cfg {
 int fsl_esdhc_mmc_init(struct bd_info *bis);
 int fsl_esdhc_initialize(struct bd_info *bis, struct fsl_esdhc_cfg *cfg);
 void fdt_fixup_esdhc(void *blob, struct bd_info *bd);
-#ifdef MMC_SUPPORTS_TUNING
-static inline int fsl_esdhc_execute_tuning(struct udevice *dev,
-                                          uint32_t opcode) {return 0; }
-#endif
 #else
 static inline int fsl_esdhc_mmc_init(struct bd_info *bis) { return -ENOSYS; }
 static inline void fdt_fixup_esdhc(void *blob, struct bd_info *bd) {}
-- 
2.7.4

Reply via email to