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>
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;
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)
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:
}
}
+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, ®s->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, ®s->strobe_dllctrl);
+ /* wait 1us to make sure strobe dll status register stable */
+ udelay(1);
+ v = readl(®s->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;
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, ®s->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:
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(®s->mixctrl);
+ m |= MIX_CTRL_HS400_ES;
+ writel(m, ®s->mixctrl);
+}
+
static int esdhc_set_vdd(struct mmc *mmc, bool enable)
{
#if CONFIG_IS_ENABLED(DM_REGULATOR)
#if CONFIG_IS_ENABLED(DM_MMC)
.execute_tuning = esdhc_execute_tuning,
.set_vdd = esdhc_set_vdd,
+ .hs400_enhanced_strobe = esdhc_hs400_enhanced_strobe,
#endif
};
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;
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:
.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[] = {
[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)
[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)
{
u8 *ext_csd = mmc->ext_csd;
char cardtype;
+ char strobe_support;
mmc->card_caps = MMC_MODE_1BIT;
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;
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;
}
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,
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;
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;
+ }
}
}
/* 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
#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)
#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 */
#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)
* @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)
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
MMC_HS_52 = 9,
MMC_DDR_52 = 10,
MMC_HS_200 = 11,
+ MMC_HS_400 = 12,
+ MMC_HS_400_ES = 13,
MMC_MODES_END
};
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;