MLK-16496-2 mmc: fsl_esdhc: add hs400 es mode support
authorPeng Fan <peng.fan@nxp.com>
Wed, 20 Sep 2017 07:05:47 +0000 (15:05 +0800)
committerJason Liu <jason.hui.liu@nxp.com>
Thu, 2 Nov 2017 18:37:18 +0000 (02:37 +0800)
Add HS400 ES mode support.

The flow is same as mmc_select_hs400es in kernel drivers/mmc/core/mmc.c.
With HS400 ES, there is no tuning needed.

With Toshiba 32GB Automotive eMMC5.1 on 8QXP ARM2 board, speed test:
'time mmc read 0x90000000 0 0x200000' shows that speed is 282MB/s.

Signed-off-by: Peng Fan <peng.fan@nxp.com>
Reviewed-by: Ye Li <ye.li@nxp.com>
drivers/mmc/fsl_esdhc.c
drivers/mmc/mmc.c
include/fsl_esdhc.h
include/mmc.h

index 84088f9..722a22d 100644 (file)
@@ -79,7 +79,10 @@ struct fsl_esdhc {
        uint    dllctrl;
        uint    dllstat;
        uint    clktunectrlstatus;
-       char    reserved3[84];
+       char    reserved[4];
+       uint    strobe_dllctrl;
+       uint    strobe_dllstat;
+       char    reserved3[72];
        uint    vendorspec;
        uint    mmcboot;
        uint    vendorspec2;
@@ -137,6 +140,7 @@ struct fsl_esdhc_priv {
        int is_ddr;
        u32 tuning_step;
        u32 tuning_start_tap;
+       u32 strobe_dll_delay_target;
        u32 delay_line;
        uint signal_voltage;
 #if CONFIG_IS_ENABLED(DM_REGULATOR)
@@ -698,6 +702,8 @@ static int esdhc_change_pinstate(struct mmc *mmc)
                break;
        case UHS_SDR104:
        case MMC_HS_200:
+       case MMC_HS_400:
+       case MMC_HS_400_ES:
                ret = pinctrl_select_state(dev, "state_200mhz");
                break;
        default:
@@ -726,6 +732,32 @@ static void esdhc_reset_tuning(struct mmc *mmc)
        }
 }
 
+static void esdhc_set_strobe_dll(struct mmc *mmc)
+{
+       struct fsl_esdhc_priv *priv = mmc->priv;
+       struct fsl_esdhc *regs = priv->esdhc_regs;
+       u32 v;
+
+       if (priv->clock > ESDHC_STROBE_DLL_CLK_FREQ) {
+               writel(ESDHC_STROBE_DLL_CTRL_RESET, &regs->strobe_dllctrl);
+
+               /*
+                * enable strobe dll ctrl and adjust the delay target
+                * for the uSDHC loopback read clock
+                */
+               v = ESDHC_STROBE_DLL_CTRL_ENABLE |
+                       (priv->strobe_dll_delay_target << ESDHC_STROBE_DLL_CTRL_SLV_DLY_TARGET_SHIFT);
+               writel(v, &regs->strobe_dllctrl);
+               /* wait 1us to make sure strobe dll status register stable */
+               udelay(1);
+               v = readl(&regs->strobe_dllstat);
+               if (!(v & ESDHC_STROBE_DLL_STS_REF_LOCK))
+                       printf("warning! HS400 strobe DLL status REF not lock!\n");
+               if (!(v & ESDHC_STROBE_DLL_STS_SLV_LOCK))
+                       printf("warning! HS400 strobe DLL status SLV not lock!\n");
+       }
+}
+
 static int esdhc_set_timing(struct mmc *mmc)
 {
        struct fsl_esdhc_priv *priv = mmc->priv;
@@ -741,6 +773,14 @@ static int esdhc_set_timing(struct mmc *mmc)
        case SD_LEGACY:
                esdhc_reset_tuning(mmc);
                break;
+       case MMC_HS_400:
+       case MMC_HS_400_ES:
+               m |= MIX_CTRL_DDREN | MIX_CTRL_HS400_EN;
+               writel(m, &regs->mixctrl);
+               priv->is_ddr = 1;
+               set_sysctl(mmc, mmc->clock);
+               esdhc_set_strobe_dll(mmc);
+               break;
        case MMC_HS:
        case MMC_HS_52:
        case MMC_HS_200:
@@ -898,6 +938,17 @@ static int esdhc_execute_tuning(struct mmc *mmc, uint32_t opcode)
        return ret;
 }
 
+static void esdhc_hs400_enhanced_strobe(struct mmc *mmc)
+{
+       struct fsl_esdhc_priv *priv = mmc->priv;
+       struct fsl_esdhc *regs = priv->esdhc_regs;
+       u32 m;
+
+       m = readl(&regs->mixctrl);
+       m |= MIX_CTRL_HS400_ES;
+       writel(m, &regs->mixctrl);
+}
+
 static int esdhc_set_vdd(struct mmc *mmc, bool enable)
 {
 #if CONFIG_IS_ENABLED(DM_REGULATOR)
@@ -1073,6 +1124,7 @@ static const struct mmc_ops esdhc_ops = {
 #if CONFIG_IS_ENABLED(DM_MMC)
        .execute_tuning = esdhc_execute_tuning,
        .set_vdd = esdhc_set_vdd,
+       .hs400_enhanced_strobe = esdhc_hs400_enhanced_strobe,
 #endif
 };
 
@@ -1382,6 +1434,8 @@ static int fsl_esdhc_probe(struct udevice *dev)
        priv->tuning_step = val;
        val = fdtdec_get_int(fdt, node, "fsl,tuning-start-tap", ESDHC_TUNING_START_TAP_DEFAULT);
        priv->tuning_start_tap = val;
+       val = fdtdec_get_int(fdt, node, "fsl,strobe-dll-delay-target", ESDHC_STROBE_DLL_CTRL_SLV_DLY_TARGET_DEFAULT);
+       priv->strobe_dll_delay_target = val;
 
        if (fdt_get_property(fdt, node, "non-removable", NULL)) {
                priv->non_removable = 1;
@@ -1421,8 +1475,10 @@ static int fsl_esdhc_probe(struct udevice *dev)
                dev_dbg(dev, "no vmmc supply\n");
 #endif
 
-       if (fdt_get_property(fdt, node, "no-1-8-v", NULL))
-               priv->caps &= ~UHS_CAPS;
+       if (fdt_get_property(fdt, node, "no-1-8-v", NULL)) {
+               priv->caps &= ~(UHS_CAPS | MMC_MODE_HS400 | MMC_MODE_HS400_ES |
+                               MMC_MODE_HS200);
+       }
 
        /*
         * TODO:
@@ -1468,8 +1524,9 @@ static struct esdhc_soc_data usdhc_imx8qm_data = {
        .flags = ESDHC_FLAG_USDHC | ESDHC_FLAG_STD_TUNING
                        | ESDHC_FLAG_HAVE_CAP1 | ESDHC_FLAG_HS200
                        | ESDHC_FLAG_HS400 | ESDHC_FLAG_HS400_ES,
-       .caps = UHS_CAPS | MMC_MODE_HS200 | MMC_MODE_DDR_52MHz
-               | MMC_MODE_HS_52MHz | MMC_MODE_HS,
+       .caps = UHS_CAPS | MMC_MODE_HS400 | MMC_MODE_HS400_ES |
+               MMC_MODE_HS200 | MMC_MODE_DDR_52MHz | MMC_MODE_HS_52MHz |
+               MMC_MODE_HS,
 };
 
 static const struct udevice_id fsl_esdhc_ids[] = {
index 680694f..f659751 100644 (file)
@@ -169,6 +169,8 @@ const char *mmc_mode_name(enum bus_mode mode)
              [MMC_HS_52]       = "MMC High Speed (52MHz)",
              [MMC_DDR_52]      = "MMC DDR52 (52MHz)",
              [MMC_HS_200]      = "HS200 (200MHz)",
+             [MMC_HS_400]      = "HS400 (200MHz)",
+             [MMC_HS_400_ES]   = "HS400ES (200MHz)",
        };
 
        if (mode >= MMC_MODES_END)
@@ -191,6 +193,8 @@ static uint mmc_mode2freq(struct mmc *mmc, enum bus_mode mode)
              [MMC_HS_52]       = 52000000,
              [MMC_DDR_52]      = 52000000,
              [MMC_HS_200]      = 200000000,
+             [MMC_HS_400]      = 200000000,
+             [MMC_HS_400_ES]   = 200000000,
        };
 
        if (mode == MMC_LEGACY)
@@ -804,6 +808,7 @@ static int mmc_get_capabilities(struct mmc *mmc)
 {
        u8 *ext_csd = mmc->ext_csd;
        char cardtype;
+       char strobe_support;
 
        mmc->card_caps = MMC_MODE_1BIT;
 
@@ -821,12 +826,17 @@ static int mmc_get_capabilities(struct mmc *mmc)
 
        mmc->card_caps |= MMC_MODE_4BIT | MMC_MODE_8BIT;
 
-       cardtype = ext_csd[EXT_CSD_CARD_TYPE] & 0x3f;
+       cardtype = ext_csd[EXT_CSD_CARD_TYPE];
+       strobe_support = ext_csd[EXT_CSD_STROBE_SUPPORT];
 
        if (cardtype & (EXT_CSD_CARD_TYPE_HS200_1_2V |
                        EXT_CSD_CARD_TYPE_HS200_1_8V)) {
                mmc->card_caps |= MMC_MODE_HS200;
        }
+       if (cardtype & (EXT_CSD_CARD_TYPE_HS400_1_2V |
+                       EXT_CSD_CARD_TYPE_HS400_1_8V)) {
+               mmc->card_caps |= MMC_MODE_HS400;
+       }
        if (cardtype & EXT_CSD_CARD_TYPE_52) {
                if (cardtype & EXT_CSD_CARD_TYPE_DDR_52)
                        mmc->card_caps |= MMC_MODE_DDR_52MHz;
@@ -835,6 +845,9 @@ static int mmc_get_capabilities(struct mmc *mmc)
        if (cardtype & EXT_CSD_CARD_TYPE_26)
                mmc->card_caps |= MMC_MODE_HS;
 
+       if (strobe_support && (mmc->card_caps & MMC_MODE_HS400))
+               mmc->card_caps |= MMC_MODE_HS400_ES;
+
        return 0;
 }
 
@@ -1641,6 +1654,10 @@ static int mmc_read_and_compare_ext_csd(struct mmc *mmc)
 
 
 static const struct mode_width_tuning mmc_modes_by_pref[] = {
+       {
+               .mode = MMC_HS_400_ES,
+               .widths = MMC_MODE_8BIT,
+       },
        {
                .mode = MMC_HS_200,
                .widths = MMC_MODE_8BIT | MMC_MODE_4BIT,
@@ -1686,6 +1703,40 @@ static const struct ext_csd_bus_width {
            ecbv++) \
                if ((ddr == ecbv->is_ddr) && (caps & ecbv->cap))
 
+static int mmc_select_hs400es(struct mmc *mmc)
+{
+       int err;
+
+       err = mmc_set_card_speed(mmc, MMC_HS);
+       if (err)
+               return err;
+
+       /* configure the bus mode (host) */
+       mmc_select_mode(mmc, MMC_HS);
+       mmc_set_clock(mmc, mmc->tran_speed, false);
+
+       err = mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL,
+               EXT_CSD_BUS_WIDTH, EXT_CSD_BUS_WIDTH_8 |
+               EXT_CSD_DDR | EXT_CSD_BUS_WIDTH_STROBE);
+       if (err) {
+               printf("switch to bus width for hs400 failed\n");
+               return err;
+       }
+       /* TODO: driver strength */
+       err = mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL,
+               EXT_CSD_HS_TIMING, EXT_CSD_TIMING_HS400 | (0 << EXT_CSD_DRV_STR_SHIFT));
+       if (err) {
+               printf("switch to hs400 failed\n");
+               return err;
+       }
+
+       mmc_select_mode(mmc, MMC_HS_400_ES);
+       mmc_set_clock(mmc, mmc->tran_speed, false);
+       mmc->cfg->ops->hs400_enhanced_strobe(mmc);
+
+       return 0;
+}
+
 static int mmc_select_mode_and_width(struct mmc *mmc, uint card_caps)
 {
        int err;
@@ -1724,32 +1775,38 @@ static int mmc_select_mode_and_width(struct mmc *mmc, uint card_caps)
                                goto error;
                        mmc_set_bus_width(mmc, bus_width(ecbw->cap));
 
-                       /* configure the bus speed (card) */
-                       err = mmc_set_card_speed(mmc, mwt->mode);
-                       if (err)
-                               goto error;
-
-                       /*
-                        * configure the bus width AND the ddr mode (card)
-                        * The host side will be taken care of in the next step
-                        */
-                       if (ecbw->ext_csd_bits & EXT_CSD_DDR) {
-                               err = mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL,
-                                       EXT_CSD_BUS_WIDTH, ecbw->ext_csd_bits);
+                       if (mwt->mode == MMC_HS_400_ES) {
+                               err = mmc_select_hs400es(mmc);
+                               if (err)
+                                       goto error;
+                       } else {
+                               /* configure the bus speed (card) */
+                               err = mmc_set_card_speed(mmc, mwt->mode);
                                if (err)
                                        goto error;
-                       }
 
-                       /* configure the bus mode (host) */
-                       mmc_select_mode(mmc, mwt->mode);
-                       mmc_set_clock(mmc, mmc->tran_speed, false);
+                               /*
+                                * configure the bus width AND the ddr mode (card)
+                                * The host side will be taken care of in the next step
+                                */
+                               if (ecbw->ext_csd_bits & EXT_CSD_DDR) {
+                                       err = mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL,
+                                               EXT_CSD_BUS_WIDTH, ecbw->ext_csd_bits);
+                                       if (err)
+                                               goto error;
+                               }
 
-                       /* execute tuning if needed */
-                       if (mwt->tuning) {
-                               err = mmc_execute_tuning(mmc, mwt->tuning);
-                               if (err) {
-                                       debug("tuning failed\n");
-                                       goto error;
+                               /* configure the bus mode (host) */
+                               mmc_select_mode(mmc, mwt->mode);
+                               mmc_set_clock(mmc, mmc->tran_speed, false);
+
+                               /* execute tuning if needed */
+                               if (mwt->tuning) {
+                                       err = mmc_execute_tuning(mmc, mwt->tuning);
+                                       if (err) {
+                                               debug("tuning failed\n");
+                                               goto error;
+                                       }
                                }
                        }
 
index 285f2e0..bca823f 100644 (file)
 /* Tuning bits */
 #define        MIX_CTRL_TUNING_MASK    0x03c00000
 
+/* strobe dll register */
+#define ESDHC_STROBE_DLL_CTRL          0x70
+#define ESDHC_STROBE_DLL_CTRL_ENABLE   (1 << 0)
+#define ESDHC_STROBE_DLL_CTRL_RESET    (1 << 1)
+#define ESDHC_STROBE_DLL_CTRL_SLV_DLY_TARGET_DEFAULT   0x7
+#define ESDHC_STROBE_DLL_CTRL_SLV_DLY_TARGET_SHIFT     3
+
+#define ESDHC_STROBE_DLL_STATUS                0x74
+#define ESDHC_STROBE_DLL_STS_REF_LOCK  (1 << 1)
+#define ESDHC_STROBE_DLL_STS_SLV_LOCK  0x1
+#define ESDHC_STROBE_DLL_CLK_FREQ      100000000
+
 #define ESDHC_STD_TUNING_EN             (1 << 24)
 /* NOTE: the minimum valid tuning start tap for mx6sl is 1 */
 #define ESDHC_TUNING_START_TAP_DEFAULT 0x1
index 7336498..7600519 100644 (file)
@@ -57,6 +57,8 @@
 #define MMC_MODE_HS_52MHz      MMC_CAP(MMC_HS_52)
 #define MMC_MODE_DDR_52MHz     MMC_CAP(MMC_DDR_52)
 #define MMC_MODE_HS200         MMC_CAP(MMC_HS_200)
+#define MMC_MODE_HS400         MMC_CAP(MMC_HS_400)
+#define MMC_MODE_HS400_ES      MMC_CAP(MMC_HS_400_ES)
 
 #define MMC_MODE_8BIT          (1 << 30)
 #define MMC_MODE_4BIT          (1 << 29)
@@ -211,6 +213,7 @@ static inline bool mmc_is_tuning_cmd(uint cmdidx)
 #define EXT_CSD_BOOT_BUS_WIDTH         177
 #define EXT_CSD_PART_CONF              179     /* R/W */
 #define EXT_CSD_BUS_WIDTH              183     /* R/W */
+#define EXT_CSD_STROBE_SUPPORT         184     /* R/W */
 #define EXT_CSD_HS_TIMING              185     /* R/W */
 #define EXT_CSD_REV                    192     /* RO */
 #define EXT_CSD_CARD_TYPE              196     /* RO */
@@ -241,14 +244,23 @@ static inline bool mmc_is_tuning_cmd(uint cmdidx)
 #define EXT_CSD_CARD_TYPE_HS200                (EXT_CSD_CARD_TYPE_HS200_1_8V | \
                                         EXT_CSD_CARD_TYPE_HS200_1_2V)
 
+#define EXT_CSD_CARD_TYPE_HS400_1_8V   (1<<6)
+#define EXT_CSD_CARD_TYPE_HS400_1_2V   (1<<7)
+#define EXT_CSD_CARD_TYPE_HS400                (EXT_CSD_CARD_TYPE_HS400_1_8V | \
+                                        EXT_CSD_CARD_TYPE_HS400_1_2V)
+#define EXT_CSD_CARD_TYPE_HS400ES      (1<<8)
+
 #define EXT_CSD_BUS_WIDTH_1    0       /* Card is in 1 bit mode */
 #define EXT_CSD_BUS_WIDTH_4    1       /* Card is in 4 bit mode */
 #define EXT_CSD_BUS_WIDTH_8    2       /* Card is in 8 bit mode */
 #define EXT_CSD_DDR            4       /* Card is in DDR mode */
+#define EXT_CSD_BUS_WIDTH_STROBE (1 << 7) /* Enhanced strobe mode */
 
 #define EXT_CSD_TIMING_LEGACY  0       /* no high speed */
 #define EXT_CSD_TIMING_HS      1       /* HS */
 #define EXT_CSD_TIMING_HS200   2       /* HS200 */
+#define EXT_CSD_TIMING_HS400   3       /* HS400 */
+#define EXT_CSD_DRV_STR_SHIFT  4       /* Driver Strength shift */
 
 #define EXT_CSD_BOOT_ACK_ENABLE                        (1 << 6)
 #define EXT_CSD_BOOT_PARTITION_ENABLE          (1 << 3)
@@ -425,6 +437,9 @@ struct dm_mmc_ops {
         * @return 1 if busy, O if not busy
         */
        int (*card_busy)(struct udevice *dev);
+
+       /* hs400_enhanced_strobe() - set enhanced strobe */
+       void (*hs400_enhanced_strobe)(struct udevice *dev);
 };
 
 #define mmc_get_ops(dev)        ((struct dm_mmc_ops *)(dev)->driver->ops)
@@ -457,6 +472,7 @@ struct mmc_ops {
        int (*getwp)(struct mmc *mmc);
        int (*execute_tuning)(struct mmc *mmc, uint opcode);
        int (*card_busy)(struct mmc *mmc);
+       void (*hs400_enhanced_strobe)(struct mmc *mmc);
 };
 #endif
 
@@ -492,6 +508,8 @@ enum bus_mode {
        MMC_HS_52       = 9,
        MMC_DDR_52      = 10,
        MMC_HS_200      = 11,
+       MMC_HS_400      = 12,
+       MMC_HS_400_ES   = 13,
        MMC_MODES_END
 };
 
@@ -500,7 +518,8 @@ void mmc_dump_capabilities(const char *text, uint caps);
 
 static inline bool mmc_is_mode_ddr(enum bus_mode mode)
 {
-       if ((mode == MMC_DDR_52) || (mode == UHS_DDR50))
+       if ((mode == MMC_HS_400) || (mode == MMC_HS_400_ES) || \
+           (mode == MMC_DDR_52) || (mode == UHS_DDR50))
                return true;
        else
                return false;