mmc: support hs400 enhanced strobe mode
eMMC 5.1+ supports HS400 Enhances Strobe mode without the need for
tuning procedure.
The flow is as following:
- set HS_TIMIMG (Highspeed)
- Host change freq to <= 52Mhz
- set the bus width to Enhanced strobe and DDR8Bit(CMD6),
EXT_CSD[183] = 0x86 instead of 0x80
- set HS_TIMING to 0x3 (HS400)
- Host change freq to <= 200Mhz
- Host select HS400 enhanced strobe complete
Signed-off-by: Peng Fan <peng.fan@nxp.com>
diff --git a/drivers/mmc/Kconfig b/drivers/mmc/Kconfig
index a4a6a6c..890ef35 100644
--- a/drivers/mmc/Kconfig
+++ b/drivers/mmc/Kconfig
@@ -135,6 +135,18 @@
cards. The IO voltage must be switchable from 3.3v to 1.8v. The bus
frequency can go up to 208MHz (SDR104)
+config MMC_HS400_ES_SUPPORT
+ bool "enable HS400 Enhanced Strobe support"
+ help
+ The HS400 Enhanced Strobe mode is support by some eMMC. The bus
+ frequency is up to 200MHz. This mode does not tune the IO.
+
+config SPL_MMC_HS400_ES_SUPPORT
+ bool "enable HS400 Enhanced Strobe support in SPL"
+ help
+ The HS400 Enhanced Strobe mode is support by some eMMC. The bus
+ frequency is up to 200MHz. This mode does not tune the IO.
+
config MMC_HS400_SUPPORT
bool "enable HS400 support"
select MMC_HS200_SUPPORT
diff --git a/drivers/mmc/mmc-uclass.c b/drivers/mmc/mmc-uclass.c
index 9327c83..8bf63bb 100644
--- a/drivers/mmc/mmc-uclass.c
+++ b/drivers/mmc/mmc-uclass.c
@@ -105,6 +105,23 @@
}
#endif
+#if CONFIG_IS_ENABLED(MMC_HS400_ES_SUPPORT)
+int dm_mmc_set_enhanced_strobe(struct udevice *dev)
+{
+ struct dm_mmc_ops *ops = mmc_get_ops(dev);
+
+ if (ops->set_enhanced_strobe)
+ return ops->set_enhanced_strobe(dev);
+
+ return -ENOTSUPP;
+}
+
+int mmc_set_enhanced_strobe(struct mmc *mmc)
+{
+ return dm_mmc_set_enhanced_strobe(mmc->dev);
+}
+#endif
+
int mmc_of_parse(struct udevice *dev, struct mmc_config *cfg)
{
int val;
diff --git a/drivers/mmc/mmc.c b/drivers/mmc/mmc.c
index 2a1ba6e..c9aa13b 100644
--- a/drivers/mmc/mmc.c
+++ b/drivers/mmc/mmc.c
@@ -148,6 +148,7 @@
[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)
@@ -173,6 +174,7 @@
[UHS_SDR104] = 208000000,
[MMC_HS_200] = 200000000,
[MMC_HS_400] = 200000000,
+ [MMC_HS_400_ES] = 200000000,
};
if (mode == MMC_LEGACY)
@@ -839,6 +841,11 @@
speed_bits = EXT_CSD_TIMING_HS400;
break;
#endif
+#if CONFIG_IS_ENABLED(MMC_HS400_ES_SUPPORT)
+ case MMC_HS_400_ES:
+ speed_bits = EXT_CSD_TIMING_HS400;
+ break;
+#endif
case MMC_LEGACY:
speed_bits = EXT_CSD_TIMING_LEGACY;
break;
@@ -909,7 +916,8 @@
mmc->card_caps |= MMC_MODE_HS200;
}
#endif
-#if CONFIG_IS_ENABLED(MMC_HS400_SUPPORT)
+#if CONFIG_IS_ENABLED(MMC_HS400_SUPPORT) || \
+ CONFIG_IS_ENABLED(MMC_HS400_ES_SUPPORT)
if (cardtype & (EXT_CSD_CARD_TYPE_HS400_1_2V |
EXT_CSD_CARD_TYPE_HS400_1_8V)) {
mmc->card_caps |= MMC_MODE_HS400;
@@ -923,6 +931,13 @@
if (cardtype & EXT_CSD_CARD_TYPE_26)
mmc->card_caps |= MMC_MODE_HS;
+#if CONFIG_IS_ENABLED(MMC_HS400_ES_SUPPORT)
+ if (ext_csd[EXT_CSD_STROBE_SUPPORT] &&
+ (mmc->card_caps & MMC_MODE_HS400)) {
+ mmc->card_caps |= MMC_MODE_HS400_ES;
+ }
+#endif
+
return 0;
}
#endif
@@ -1799,6 +1814,7 @@
u32 card_mask = 0;
switch (mode) {
+ case MMC_HS_400_ES:
case MMC_HS_400:
case MMC_HS_200:
if (mmc->cardtype & (EXT_CSD_CARD_TYPE_HS200_1_8V |
@@ -1841,6 +1857,12 @@
#endif
static const struct mode_width_tuning mmc_modes_by_pref[] = {
+#if CONFIG_IS_ENABLED(MMC_HS400_ES_SUPPORT)
+ {
+ .mode = MMC_HS_400_ES,
+ .widths = MMC_MODE_8BIT,
+ },
+#endif
#if CONFIG_IS_ENABLED(MMC_HS400_SUPPORT)
{
.mode = MMC_HS_400,
@@ -1938,6 +1960,47 @@
}
#endif
+#if CONFIG_IS_ENABLED(MMC_HS400_ES_SUPPORT)
+#if !CONFIG_IS_ENABLED(DM_MMC)
+static int mmc_set_enhanced_strobe(struct mmc *mmc)
+{
+ return -ENOTSUPP;
+}
+#endif
+static int mmc_select_hs400es(struct mmc *mmc)
+{
+ int err;
+
+ err = mmc_set_card_speed(mmc, MMC_HS, true);
+ if (err)
+ return err;
+
+ err = mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_BUS_WIDTH,
+ EXT_CSD_BUS_WIDTH_8 | EXT_CSD_DDR_FLAG |
+ EXT_CSD_BUS_WIDTH_STROBE);
+ if (err) {
+ printf("switch to bus width for hs400 failed\n");
+ return err;
+ }
+ /* TODO: driver strength */
+ err = mmc_set_card_speed(mmc, MMC_HS_400_ES, false);
+ if (err)
+ return err;
+
+ mmc_select_mode(mmc, MMC_HS_400_ES);
+ err = mmc_set_clock(mmc, mmc->tran_speed, false);
+ if (err)
+ return err;
+
+ return mmc_set_enhanced_strobe(mmc);
+}
+#else
+static int mmc_select_hs400es(struct mmc *mmc)
+{
+ return -ENOTSUPP;
+}
+#endif
+
#define for_each_supported_width(caps, ddr, ecbv) \
for (ecbv = ext_csd_bus_width;\
ecbv < ext_csd_bus_width + ARRAY_SIZE(ext_csd_bus_width);\
@@ -2016,6 +2079,13 @@
printf("Select HS400 failed %d\n", err);
goto error;
}
+ } else if (mwt->mode == MMC_HS_400_ES) {
+ err = mmc_select_hs400es(mmc);
+ if (err) {
+ printf("Select HS400ES failed %d\n",
+ err);
+ goto error;
+ }
} else {
/* configure the bus speed (card) */
err = mmc_set_card_speed(mmc, mwt->mode, false);