diff options
author | Peng Fan <peng.fan@nxp.com> | 2017-09-20 15:05:47 +0800 |
---|---|---|
committer | Peng Fan <peng.fan@nxp.com> | 2017-09-20 15:45:56 +0800 |
commit | aa49bce19a5aa71a98756f30d9b5da130fdae496 (patch) | |
tree | 4a6545e7ab6bb559a4f69c21addf0219f52181bd | |
parent | e7cdf55baf73d67c72fdc61a3e92d5b2e09b702a (diff) |
MLK-16496-2 mmc: fsl_esdhc: add hs400 es mode support
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>
-rw-r--r-- | drivers/mmc/fsl_esdhc.c | 67 | ||||
-rw-r--r-- | drivers/mmc/mmc.c | 103 | ||||
-rw-r--r-- | include/fsl_esdhc.h | 12 | ||||
-rw-r--r-- | include/mmc.h | 21 |
4 files changed, 174 insertions, 29 deletions
diff --git a/drivers/mmc/fsl_esdhc.c b/drivers/mmc/fsl_esdhc.c index 84088f9950..722a22d16e 100644 --- a/drivers/mmc/fsl_esdhc.c +++ b/drivers/mmc/fsl_esdhc.c @@ -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, ®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; @@ -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, ®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: @@ -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(®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) @@ -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[] = { diff --git a/drivers/mmc/mmc.c b/drivers/mmc/mmc.c index 680694fdc6..f659751948 100644 --- a/drivers/mmc/mmc.c +++ b/drivers/mmc/mmc.c @@ -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; } @@ -1642,6 +1655,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, .tuning = MMC_SEND_TUNING_BLOCK_HS200 @@ -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; + } } } diff --git a/include/fsl_esdhc.h b/include/fsl_esdhc.h index 285f2e0496..bca823f26d 100644 --- a/include/fsl_esdhc.h +++ b/include/fsl_esdhc.h @@ -55,6 +55,18 @@ /* 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 diff --git a/include/mmc.h b/include/mmc.h index 7336498189..76005191e6 100644 --- a/include/mmc.h +++ b/include/mmc.h @@ -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; |