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>
This commit is contained in:
Peng Fan 2019-07-10 14:43:07 +08:00
parent 8277171663
commit 44acd49248
4 changed files with 115 additions and 1 deletions

View File

@ -135,6 +135,18 @@ config SPL_MMC_UHS_SUPPORT
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

View File

@ -105,6 +105,23 @@ int mmc_execute_tuning(struct mmc *mmc, uint opcode)
}
#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;

View File

@ -148,6 +148,7 @@ const char *mmc_mode_name(enum bus_mode mode)
[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 @@ static uint mmc_mode2freq(struct mmc *mmc, enum bus_mode mode)
[UHS_SDR104] = 208000000,
[MMC_HS_200] = 200000000,
[MMC_HS_400] = 200000000,
[MMC_HS_400_ES] = 200000000,
};
if (mode == MMC_LEGACY)
@ -838,6 +840,11 @@ static int mmc_set_card_speed(struct mmc *mmc, enum bus_mode mode,
case MMC_HS_400:
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;
@ -909,7 +916,8 @@ static int mmc_get_capabilities(struct mmc *mmc)
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 @@ static int mmc_get_capabilities(struct mmc *mmc)
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 @@ static int mmc_set_lowest_voltage(struct mmc *mmc, enum bus_mode mode,
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 @@ static inline int mmc_set_lowest_voltage(struct mmc *mmc, enum bus_mode mode,
#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 @@ static int mmc_select_hs400(struct mmc *mmc)
}
#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 @@ static int mmc_select_mode_and_width(struct mmc *mmc, uint card_caps)
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);

View File

@ -65,6 +65,7 @@
#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_CAP_NONREMOVABLE BIT(14)
#define MMC_CAP_NEEDS_POLL BIT(15)
@ -223,6 +224,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 */
@ -266,11 +268,13 @@ static inline bool mmc_is_tuning_cmd(uint cmdidx)
#define EXT_CSD_DDR_BUS_WIDTH_4 5 /* Card is in 4 bit DDR mode */
#define EXT_CSD_DDR_BUS_WIDTH_8 6 /* Card is in 8 bit DDR mode */
#define EXT_CSD_DDR_FLAG BIT(2) /* Flag for DDR mode */
#define EXT_CSD_BUS_WIDTH_STROBE BIT(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)
@ -457,6 +461,11 @@ struct dm_mmc_ops {
* @return 0 if dat0 is in the target state, -ve on error
*/
int (*wait_dat0)(struct udevice *dev, int state, int timeout);
#if CONFIG_IS_ENABLED(MMC_HS400_ES_SUPPORT)
/* set_enhanced_strobe() - set HS400 enhanced strobe */
int (*set_enhanced_strobe)(struct udevice *dev);
#endif
};
#define mmc_get_ops(dev) ((struct dm_mmc_ops *)(dev)->driver->ops)
@ -475,6 +484,7 @@ int mmc_getcd(struct mmc *mmc);
int mmc_getwp(struct mmc *mmc);
int mmc_execute_tuning(struct mmc *mmc, uint opcode);
int mmc_wait_dat0(struct mmc *mmc, int state, int timeout);
int mmc_set_enhanced_strobe(struct mmc *mmc);
#else
struct mmc_ops {
@ -520,6 +530,7 @@ enum bus_mode {
UHS_SDR104,
MMC_HS_200,
MMC_HS_400,
MMC_HS_400_ES,
MMC_MODES_END
};
@ -537,6 +548,10 @@ static inline bool mmc_is_mode_ddr(enum bus_mode mode)
#if CONFIG_IS_ENABLED(MMC_HS400_SUPPORT)
else if (mode == MMC_HS_400)
return true;
#endif
#if CONFIG_IS_ENABLED(MMC_HS400_ES_SUPPORT)
else if (mode == MMC_HS_400_ES)
return true;
#endif
else
return false;