+// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright 2008, Freescale Semiconductor, Inc
* Andy Fleming
*
* Based vaguely on the Linux code
- *
- * SPDX-License-Identifier: GPL-2.0+
*/
#include <config.h>
static int mmc_set_signal_voltage(struct mmc *mmc, uint signal_voltage);
static int mmc_power_cycle(struct mmc *mmc);
+#if !CONFIG_IS_ENABLED(MMC_TINY)
static int mmc_select_mode_and_width(struct mmc *mmc, uint card_caps);
-
-#if CONFIG_IS_ENABLED(MMC_TINY)
-static struct mmc mmc_static;
-struct mmc *find_mmc_device(int dev_num)
-{
- return &mmc_static;
-}
-
-void mmc_do_preinit(void)
-{
- struct mmc *m = &mmc_static;
-#ifdef CONFIG_FSL_ESDHC_ADAPTER_IDENT
- mmc_set_preinit(m, 1);
-#endif
- if (m->preinit)
- mmc_start_init(m);
-}
-
-struct blk_desc *mmc_get_blk_desc(struct mmc *mmc)
-{
- return &mmc->block_dev;
-}
#endif
#if !CONFIG_IS_ENABLED(DM_MMC)
[MMC_HS_52] = "MMC High Speed (52MHz)",
[MMC_DDR_52] = "MMC DDR52 (52MHz)",
[MMC_HS_200] = "HS200 (200MHz)",
+ [MMC_HS_400] = "HS400 (200MHz)",
};
if (mode >= MMC_MODES_END)
[UHS_DDR50] = 50000000,
[UHS_SDR104] = 208000000,
[MMC_HS_200] = 200000000,
+ [MMC_HS_400] = 200000000,
};
if (mode == MMC_LEGACY)
* During a signal voltage level switch, the clock must be gated
* for 5 ms according to the SD spec
*/
- mmc_set_clock(mmc, mmc->clock, true);
+ mmc_set_clock(mmc, mmc->clock, MMC_CLK_DISABLE);
err = mmc_set_signal_voltage(mmc, signal_voltage);
if (err)
/* Keep clock gated for at least 10 ms, though spec only says 5 ms */
mdelay(10);
- mmc_set_clock(mmc, mmc->clock, false);
+ mmc_set_clock(mmc, mmc->clock, MMC_CLK_ENABLE);
/*
* Failure to switch is indicated by the card holding
{
struct mmc_cmd cmd;
int timeout = 1000;
- uint start;
+ ulong start;
int err;
mmc->op_cond_pending = 0;
}
+#if !CONFIG_IS_ENABLED(MMC_TINY)
static int mmc_set_card_speed(struct mmc *mmc, enum bus_mode mode)
{
int err;
speed_bits = EXT_CSD_TIMING_HS200;
break;
#endif
+#if CONFIG_IS_ENABLED(MMC_HS400_SUPPORT)
+ case MMC_HS_400:
+ speed_bits = EXT_CSD_TIMING_HS400;
+ break;
+#endif
case MMC_LEGACY:
speed_bits = EXT_CSD_TIMING_LEGACY;
break;
mmc->card_caps |= MMC_MODE_4BIT | MMC_MODE_8BIT;
- cardtype = ext_csd[EXT_CSD_CARD_TYPE] & 0x3f;
+ cardtype = ext_csd[EXT_CSD_CARD_TYPE];
mmc->cardtype = cardtype;
#if CONFIG_IS_ENABLED(MMC_HS200_SUPPORT)
mmc->card_caps |= MMC_MODE_HS200;
}
#endif
+#if CONFIG_IS_ENABLED(MMC_HS400_SUPPORT)
+ if (cardtype & (EXT_CSD_CARD_TYPE_HS400_1_2V |
+ EXT_CSD_CARD_TYPE_HS400_1_8V)) {
+ mmc->card_caps |= MMC_MODE_HS400;
+ }
+#endif
if (cardtype & EXT_CSD_CARD_TYPE_52) {
if (cardtype & EXT_CSD_CARD_TYPE_DDR_52)
mmc->card_caps |= MMC_MODE_DDR_52MHz;
return 0;
}
+#endif
static int mmc_set_capacity(struct mmc *mmc, int part_num)
{
}
#endif
+#if !CONFIG_IS_ENABLED(MMC_TINY)
static int sd_switch(struct mmc *mmc, int mode, int group, u8 value, u8 *resp)
{
struct mmc_cmd cmd;
return mmc_send_cmd(mmc, &cmd, &data);
}
-
static int sd_get_capabilities(struct mmc *mmc)
{
int err;
ALLOC_CACHE_ALIGN_BUFFER(uint, switch_status, 16);
int speed;
+ /* SD version 1.00 and 1.01 does not support CMD 6 */
+ if (mmc->version == SD_VERSION_1_0)
+ return 0;
+
switch (mode) {
case SD_LEGACY:
speed = UHS_SDR12_BUS_SPEED;
return 0;
}
+#endif
#if CONFIG_IS_ENABLED(MMC_WRITE)
static int sd_read_ssr(struct mmc *mmc)
mmc->clock = clock;
mmc->clk_disable = disable;
+ debug("clock is %s (%dHz)\n", disable ? "disabled" : "enabled", clock);
+
return mmc_set_ios(mmc);
}
}
#endif
+#if !CONFIG_IS_ENABLED(MMC_TINY)
static const struct mode_width_tuning sd_modes_by_pref[] = {
#if CONFIG_IS_ENABLED(MMC_UHS_SUPPORT)
#ifdef MMC_SUPPORTS_TUNING
/* configure the bus mode (host) */
mmc_select_mode(mmc, mwt->mode);
- mmc_set_clock(mmc, mmc->tran_speed, false);
+ mmc_set_clock(mmc, mmc->tran_speed,
+ MMC_CLK_ENABLE);
#ifdef MMC_SUPPORTS_TUNING
/* execute tuning if needed */
#if CONFIG_IS_ENABLED(MMC_WRITE)
err = sd_read_ssr(mmc);
- if (!err)
+ if (err)
pr_warn("unable to read ssr\n");
#endif
if (!err)
error:
/* revert to a safer bus speed */
mmc_select_mode(mmc, SD_LEGACY);
- mmc_set_clock(mmc, mmc->tran_speed, false);
+ mmc_set_clock(mmc, mmc->tran_speed,
+ MMC_CLK_ENABLE);
}
}
}
u32 card_mask = 0;
switch (mode) {
+ case MMC_HS_400:
case MMC_HS_200:
- if (mmc->cardtype & EXT_CSD_CARD_TYPE_HS200_1_8V)
+ if (mmc->cardtype & (EXT_CSD_CARD_TYPE_HS200_1_8V |
+ EXT_CSD_CARD_TYPE_HS400_1_8V))
card_mask |= MMC_SIGNAL_VOLTAGE_180;
- if (mmc->cardtype & EXT_CSD_CARD_TYPE_HS200_1_2V)
+ if (mmc->cardtype & (EXT_CSD_CARD_TYPE_HS200_1_2V |
+ EXT_CSD_CARD_TYPE_HS400_1_2V))
card_mask |= MMC_SIGNAL_VOLTAGE_120;
break;
case MMC_DDR_52:
#endif
static const struct mode_width_tuning mmc_modes_by_pref[] = {
+#if CONFIG_IS_ENABLED(MMC_HS400_SUPPORT)
+ {
+ .mode = MMC_HS_400,
+ .widths = MMC_MODE_8BIT,
+ .tuning = MMC_CMD_SEND_TUNING_BLOCK_HS200
+ },
+#endif
#if CONFIG_IS_ENABLED(MMC_HS200_SUPPORT)
{
.mode = MMC_HS_200,
{MMC_MODE_1BIT, false, EXT_CSD_BUS_WIDTH_1},
};
+#if CONFIG_IS_ENABLED(MMC_HS400_SUPPORT)
+static int mmc_select_hs400(struct mmc *mmc)
+{
+ int err;
+
+ /* Set timing to HS200 for tuning */
+ err = mmc_set_card_speed(mmc, MMC_HS_200);
+ if (err)
+ return err;
+
+ /* configure the bus mode (host) */
+ mmc_select_mode(mmc, MMC_HS_200);
+ mmc_set_clock(mmc, mmc->tran_speed, false);
+
+ /* execute tuning if needed */
+ err = mmc_execute_tuning(mmc, MMC_CMD_SEND_TUNING_BLOCK_HS200);
+ if (err) {
+ debug("tuning failed\n");
+ return err;
+ }
+
+ /* Set back to HS */
+ mmc_set_card_speed(mmc, MMC_HS);
+ mmc_set_clock(mmc, mmc_mode2freq(mmc, MMC_HS), false);
+
+ err = mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_BUS_WIDTH,
+ EXT_CSD_BUS_WIDTH_8 | EXT_CSD_DDR_FLAG);
+ if (err)
+ return err;
+
+ err = mmc_set_card_speed(mmc, MMC_HS_400);
+ if (err)
+ return err;
+
+ mmc_select_mode(mmc, MMC_HS_400);
+ err = mmc_set_clock(mmc, mmc->tran_speed, false);
+ if (err)
+ return err;
+
+ return 0;
+}
+#else
+static int mmc_select_hs400(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);\
return -ENOTSUPP;
}
- mmc_set_clock(mmc, mmc->legacy_speed, false);
+ mmc_set_clock(mmc, mmc->legacy_speed, MMC_CLK_ENABLE);
for_each_mmc_mode_by_pref(card_caps, mwt) {
for_each_supported_width(card_caps & mwt->widths,
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_FLAG) {
- err = mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL,
- EXT_CSD_BUS_WIDTH,
- ecbw->ext_csd_bits);
+ if (mwt->mode == MMC_HS_400) {
+ err = mmc_select_hs400(mmc);
+ if (err) {
+ printf("Select HS400 failed %d\n", 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_FLAG) {
+ err = mmc_switch(mmc,
+ EXT_CSD_CMD_SET_NORMAL,
+ EXT_CSD_BUS_WIDTH,
+ ecbw->ext_csd_bits);
+ if (err)
+ goto error;
+ }
+
+ /* configure the bus mode (host) */
+ mmc_select_mode(mmc, mwt->mode);
+ mmc_set_clock(mmc, mmc->tran_speed,
+ MMC_CLK_ENABLE);
#ifdef MMC_SUPPORTS_TUNING
- /* execute tuning if needed */
- if (mwt->tuning) {
- err = mmc_execute_tuning(mmc, mwt->tuning);
- if (err) {
- pr_debug("tuning failed\n");
- goto error;
+ /* execute tuning if needed */
+ if (mwt->tuning) {
+ err = mmc_execute_tuning(mmc,
+ mwt->tuning);
+ if (err) {
+ pr_debug("tuning failed\n");
+ goto error;
+ }
}
- }
#endif
+ }
/* do a transfer to check the configuration */
err = mmc_read_and_compare_ext_csd(mmc);
return -ENOTSUPP;
}
+#endif
+
+#if CONFIG_IS_ENABLED(MMC_TINY)
+DEFINE_CACHE_ALIGN_BUFFER(u8, ext_csd_bkup, MMC_MAX_BLOCK_LEN);
+#endif
static int mmc_startup_v4(struct mmc *mmc)
{
MMC_VERSION_5_1
};
+#if CONFIG_IS_ENABLED(MMC_TINY)
+ u8 *ext_csd = ext_csd_bkup;
+
+ if (IS_SD(mmc) || mmc->version < MMC_VERSION_4)
+ return 0;
+
+ if (!mmc->ext_csd)
+ memset(ext_csd_bkup, 0, sizeof(ext_csd_bkup));
+
+ err = mmc_send_ext_csd(mmc, ext_csd);
+ if (err)
+ goto error;
+
+ /* store the ext csd for future reference */
+ if (!mmc->ext_csd)
+ mmc->ext_csd = ext_csd;
+#else
ALLOC_CACHE_ALIGN_BUFFER(u8, ext_csd, MMC_MAX_BLOCK_LEN);
if (IS_SD(mmc) || (mmc->version < MMC_VERSION_4))
if (!mmc->ext_csd)
return -ENOMEM;
memcpy(mmc->ext_csd, ext_csd, MMC_MAX_BLOCK_LEN);
-
+#endif
if (ext_csd[EXT_CSD_REV] >= ARRAY_SIZE(mmc_versions))
return -EINVAL;
return 0;
error:
if (mmc->ext_csd) {
+#if !CONFIG_IS_ENABLED(MMC_TINY)
free(mmc->ext_csd);
+#endif
mmc->ext_csd = NULL;
}
return err;
if (err)
return err;
+#if CONFIG_IS_ENABLED(MMC_TINY)
+ mmc_set_clock(mmc, mmc->legacy_speed, false);
+ mmc_select_mode(mmc, IS_SD(mmc) ? SD_LEGACY : MMC_LEGACY);
+ mmc_set_bus_width(mmc, 1);
+#else
if (IS_SD(mmc)) {
err = sd_get_capabilities(mmc);
if (err)
return err;
mmc_select_mode_and_width(mmc, mmc->card_caps);
}
-
+#endif
if (err)
return err;
bdesc->product[0] = 0;
bdesc->revision[0] = 0;
#endif
-#if !defined(CONFIG_SPL_BUILD) || defined(CONFIG_SPL_LIBDISK_SUPPORT)
+
+#if !defined(CONFIG_DM_MMC) && (!defined(CONFIG_SPL_BUILD) || defined(CONFIG_SPL_LIBDISK_SUPPORT))
part_init(bdesc);
#endif
mmc_select_mode(mmc, MMC_LEGACY);
mmc_set_bus_width(mmc, 1);
- mmc_set_clock(mmc, 0, false);
+ mmc_set_clock(mmc, 0, MMC_CLK_ENABLE);
}
static int mmc_power_on(struct mmc *mmc)
static int mmc_power_off(struct mmc *mmc)
{
- mmc_set_clock(mmc, 0, true);
+ mmc_set_clock(mmc, 0, MMC_CLK_DISABLE);
#if CONFIG_IS_ENABLED(DM_MMC) && CONFIG_IS_ENABLED(DM_REGULATOR)
if (mmc->vmmc_supply) {
int ret = regulator_set_enable(mmc->vmmc_supply, false);
return mmc_power_on(mmc);
}
-int mmc_start_init(struct mmc *mmc)
+int mmc_get_op_cond(struct mmc *mmc)
{
- bool no_card;
bool uhs_en = supports_uhs(mmc->cfg->host_caps);
int err;
- /*
- * all hosts are capable of 1 bit bus-width and able to use the legacy
- * timings.
- */
- mmc->host_caps = mmc->cfg->host_caps | MMC_CAP(SD_LEGACY) |
- MMC_CAP(MMC_LEGACY) | MMC_MODE_1BIT;
-
-#if !defined(CONFIG_MMC_BROKEN_CD)
- /* we pretend there's no card when init is NULL */
- no_card = mmc_getcd(mmc) == 0;
-#else
- no_card = 0;
-#endif
-#if !CONFIG_IS_ENABLED(DM_MMC)
- no_card = no_card || (mmc->cfg->ops->init == NULL);
-#endif
- if (no_card) {
- mmc->has_init = 0;
-#if !defined(CONFIG_SPL_BUILD) || defined(CONFIG_SPL_LIBCOMMON_SUPPORT)
- pr_err("MMC: no card present\n");
-#endif
- return -ENOMEDIUM;
- }
-
if (mmc->has_init)
return 0;
}
}
+ return err;
+}
+
+int mmc_start_init(struct mmc *mmc)
+{
+ bool no_card;
+ int err = 0;
+
+ /*
+ * all hosts are capable of 1 bit bus-width and able to use the legacy
+ * timings.
+ */
+ mmc->host_caps = mmc->cfg->host_caps | MMC_CAP(SD_LEGACY) |
+ MMC_CAP(MMC_LEGACY) | MMC_MODE_1BIT;
+
+#if !defined(CONFIG_MMC_BROKEN_CD)
+ /* we pretend there's no card when init is NULL */
+ no_card = mmc_getcd(mmc) == 0;
+#else
+ no_card = 0;
+#endif
+#if !CONFIG_IS_ENABLED(DM_MMC)
+ no_card = no_card || (mmc->cfg->ops->init == NULL);
+#endif
+ if (no_card) {
+ mmc->has_init = 0;
+#if !defined(CONFIG_SPL_BUILD) || defined(CONFIG_SPL_LIBCOMMON_SUPPORT)
+ pr_err("MMC: no card present\n");
+#endif
+ return -ENOMEDIUM;
+ }
+
+ err = mmc_get_op_cond(mmc);
+
if (!err)
mmc->init_in_progress = 1;
int mmc_init(struct mmc *mmc)
{
int err = 0;
- __maybe_unused unsigned start;
+ __maybe_unused ulong start;
#if CONFIG_IS_ENABLED(DM_MMC)
struct mmc_uclass_priv *upriv = dev_get_uclass_priv(mmc->dev);