diff --git a/drivers/mmc/Kconfig b/drivers/mmc/Kconfig index e9be18b333..c4df67d511 100644 --- a/drivers/mmc/Kconfig +++ b/drivers/mmc/Kconfig @@ -104,6 +104,12 @@ 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 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 a3536b15ae..cf628a8b25 100644 --- a/drivers/mmc/mmc-uclass.c +++ b/drivers/mmc/mmc-uclass.c @@ -123,6 +123,21 @@ int mmc_execute_tuning(struct mmc *mmc, uint opcode) } #endif +#if CONFIG_IS_ENABLED(MMC_HS400_ES_SUPPORT) +void dm_mmc_set_enhanced_strobe(struct udevice *dev) +{ + struct dm_mmc_ops *ops = mmc_get_ops(dev); + + if (ops->set_enhanced_strobe) + ops->set_enhanced_strobe(dev); +} + +void mmc_set_enhanced_strobe(struct mmc *mmc) +{ + 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 eef229c8b4..4a80f556a0 100644 --- a/drivers/mmc/mmc.c +++ b/drivers/mmc/mmc.c @@ -170,6 +170,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) @@ -195,6 +196,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) @@ -797,6 +799,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; @@ -853,7 +860,7 @@ 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; @@ -867,6 +874,11 @@ 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; } @@ -1761,6 +1773,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: if (mmc->cardtype & EXT_CSD_CARD_TYPE_HS400_1_8V) card_mask |= MMC_SIGNAL_VOLTAGE_180; @@ -1806,6 +1819,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, @@ -1904,6 +1923,55 @@ static int mmc_select_hs400(struct mmc *mmc) } #endif +#if CONFIG_IS_ENABLED(MMC_HS400_ES_SUPPORT) +#if !CONFIG_IS_ENABLED(DM_MMC) +static void mmc_set_enhanced_strobe(struct mmc *mmc) +{ + return; +} +#endif + +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_FLAG | 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_set_enhanced_strobe(mmc); + + return 0; +} + +#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);\ @@ -1961,6 +2029,10 @@ static int mmc_select_mode_and_width(struct mmc *mmc, uint card_caps) err = mmc_select_hs400(mmc); if (err) goto error; + } else 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); diff --git a/include/mmc.h b/include/mmc.h index 8c01c6a530..b39ebdcb0f 100644 --- a/include/mmc.h +++ b/include/mmc.h @@ -66,6 +66,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_MODE_8BIT BIT(30) #define MMC_MODE_4BIT BIT(29) @@ -220,6 +221,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 */ @@ -262,11 +264,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 (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) @@ -463,6 +467,11 @@ struct dm_mmc_ops { */ int (*wait_dat0)(struct udevice *dev, int state, int timeout); #endif + +#if CONFIG_IS_ENABLED(MMC_HS400_ES_SUPPORT) + /* set_enhanced_strobe() - set HS400 enhanced strobe */ + void (*set_enhanced_strobe)(struct udevice *dev); +#endif }; #define mmc_get_ops(dev) ((struct dm_mmc_ops *)(dev)->driver->ops) @@ -483,6 +492,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); +void mmc_set_enhanced_strobe(struct mmc *mmc); #else struct mmc_ops { @@ -528,6 +538,7 @@ enum bus_mode { UHS_SDR104, MMC_HS_200, MMC_HS_400, + MMC_HS_400_ES, MMC_MODES_END }; @@ -545,6 +556,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;