diff options
Diffstat (limited to 'drivers/mmc/mmc.c')
-rw-r--r-- | drivers/mmc/mmc.c | 255 |
1 files changed, 180 insertions, 75 deletions
diff --git a/drivers/mmc/mmc.c b/drivers/mmc/mmc.c index 71b52c6cf2..c9aa13b409 100644 --- a/drivers/mmc/mmc.c +++ b/drivers/mmc/mmc.c @@ -21,6 +21,8 @@ #include <div64.h> #include "mmc_private.h" +#define DEFAULT_CMD6_TIMEOUT_MS 500 + 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) @@ -29,12 +31,10 @@ static int mmc_select_mode_and_width(struct mmc *mmc, uint card_caps); #if !CONFIG_IS_ENABLED(DM_MMC) -#if CONFIG_IS_ENABLED(MMC_UHS_SUPPORT) static int mmc_wait_dat0(struct mmc *mmc, int state, int timeout) { return -ENOSYS; } -#endif __weak int board_mmc_getwp(struct mmc *mmc) { @@ -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) @@ -206,7 +208,7 @@ int mmc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, struct mmc_data *data) } #endif -int mmc_send_status(struct mmc *mmc, int timeout) +int mmc_send_status(struct mmc *mmc, unsigned int *status) { struct mmc_cmd cmd; int err, retries = 5; @@ -216,23 +218,43 @@ int mmc_send_status(struct mmc *mmc, int timeout) if (!mmc_host_is_spi(mmc)) cmd.cmdarg = mmc->rca << 16; - while (1) { + while (retries--) { err = mmc_send_cmd(mmc, &cmd, NULL); if (!err) { - if ((cmd.response[0] & MMC_STATUS_RDY_FOR_DATA) && - (cmd.response[0] & MMC_STATUS_CURR_STATE) != - MMC_STATE_PRG) - break; + mmc_trace_state(mmc, &cmd); + *status = cmd.response[0]; + return 0; + } + } + mmc_trace_state(mmc, &cmd); + return -ECOMM; +} + +int mmc_poll_for_busy(struct mmc *mmc, int timeout) +{ + unsigned int status; + int err; + + err = mmc_wait_dat0(mmc, 1, timeout); + if (err != -ENOSYS) + return err; - if (cmd.response[0] & MMC_STATUS_MASK) { + while (1) { + err = mmc_send_status(mmc, &status); + if (err) + return err; + + if ((status & MMC_STATUS_RDY_FOR_DATA) && + (status & MMC_STATUS_CURR_STATE) != + MMC_STATE_PRG) + break; + + if (status & MMC_STATUS_MASK) { #if !defined(CONFIG_SPL_BUILD) || defined(CONFIG_SPL_LIBCOMMON_SUPPORT) - pr_err("Status Error: 0x%08x\n", - cmd.response[0]); + pr_err("Status Error: 0x%08x\n", status); #endif - return -ECOMM; - } - } else if (--retries < 0) - return err; + return -ECOMM; + } if (timeout-- <= 0) break; @@ -240,7 +262,6 @@ int mmc_send_status(struct mmc *mmc, int timeout) udelay(1000); } - mmc_trace_state(mmc, &cmd); if (timeout <= 0) { #if !defined(CONFIG_SPL_BUILD) || defined(CONFIG_SPL_LIBCOMMON_SUPPORT) pr_err("Timeout waiting card ready\n"); @@ -727,36 +748,67 @@ static int mmc_send_ext_csd(struct mmc *mmc, u8 *ext_csd) static int __mmc_switch(struct mmc *mmc, u8 set, u8 index, u8 value, bool send_status) { + unsigned int status, start; struct mmc_cmd cmd; - int timeout = 1000; + int timeout = DEFAULT_CMD6_TIMEOUT_MS; + bool is_part_switch = (set == EXT_CSD_CMD_SET_NORMAL) && + (index == EXT_CSD_PART_CONF); int retries = 3; int ret; + if (mmc->gen_cmd6_time) + timeout = mmc->gen_cmd6_time * 10; + + if (is_part_switch && mmc->part_switch_time) + timeout = mmc->part_switch_time * 10; + cmd.cmdidx = MMC_CMD_SWITCH; cmd.resp_type = MMC_RSP_R1b; cmd.cmdarg = (MMC_SWITCH_MODE_WRITE_BYTE << 24) | (index << 16) | (value << 8); - while (retries > 0) { + do { ret = mmc_send_cmd(mmc, &cmd, NULL); + } while (ret && retries-- > 0); - if (ret) { - retries--; - continue; - } + if (ret) + return ret; - if (!send_status) { - mdelay(50); - return 0; - } + start = get_timer(0); - /* Waiting for the ready status */ - return mmc_send_status(mmc, timeout); - } + /* poll dat0 for rdy/buys status */ + ret = mmc_wait_dat0(mmc, 1, timeout); + if (ret && ret != -ENOSYS) + return ret; - return ret; + /* + * In cases when not allowed to poll by using CMD13 or because we aren't + * capable of polling by using mmc_wait_dat0, then rely on waiting the + * stated timeout to be sufficient. + */ + if (ret == -ENOSYS && !send_status) + mdelay(timeout); + + /* Finally wait until the card is ready or indicates a failure + * to switch. It doesn't hurt to use CMD13 here even if send_status + * is false, because by now (after 'timeout' ms) the bus should be + * reliable. + */ + do { + ret = mmc_send_status(mmc, &status); + + if (!ret && (status & MMC_STATUS_SWITCH_ERROR)) { + pr_debug("switch failed %d/%d/0x%x !\n", set, index, + value); + return -EIO; + } + if (!ret && (status & MMC_STATUS_RDY_FOR_DATA)) + return 0; + udelay(100); + } while (get_timer(start) < timeout); + return -ETIMEDOUT; } int mmc_switch(struct mmc *mmc, u8 set, u8 index, u8 value) @@ -789,6 +841,11 @@ static int mmc_set_card_speed(struct mmc *mmc, enum bus_mode mode, 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; break; @@ -859,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; @@ -873,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 @@ -905,49 +970,17 @@ static int mmc_set_capacity(struct mmc *mmc, int part_num) return 0; } -#if CONFIG_IS_ENABLED(MMC_HS200_SUPPORT) || CONFIG_IS_ENABLED(MMC_HS400_SUPPORT) -static int mmc_boot_part_access_chk(struct mmc *mmc, unsigned int part_num) -{ - int forbidden = 0; - bool change = false; - - if (part_num & PART_ACCESS_MASK) - forbidden = MMC_CAP(MMC_HS_200) | MMC_CAP(MMC_HS_400); - - if (MMC_CAP(mmc->selected_mode) & forbidden) { - pr_debug("selected mode (%s) is forbidden for part %d\n", - mmc_mode_name(mmc->selected_mode), part_num); - change = true; - } else if (mmc->selected_mode != mmc->best_mode) { - pr_debug("selected mode is not optimal\n"); - change = true; - } - - if (change) - return mmc_select_mode_and_width(mmc, - mmc->card_caps & ~forbidden); - - return 0; -} -#else -static inline int mmc_boot_part_access_chk(struct mmc *mmc, - unsigned int part_num) -{ - return 0; -} -#endif - int mmc_switch_part(struct mmc *mmc, unsigned int part_num) { int ret; + int retry = 3; - ret = mmc_boot_part_access_chk(mmc, part_num); - if (ret) - return ret; - - ret = mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_PART_CONF, - (mmc->part_config & ~PART_ACCESS_MASK) - | (part_num & PART_ACCESS_MASK)); + do { + ret = mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL, + EXT_CSD_PART_CONF, + (mmc->part_config & ~PART_ACCESS_MASK) + | (part_num & PART_ACCESS_MASK)); + } while (ret && retry--); /* * Set the capacity if the switch succeeded or was intended @@ -1504,10 +1537,6 @@ static int mmc_execute_tuning(struct mmc *mmc, uint opcode) } #endif -static void mmc_send_init_stream(struct mmc *mmc) -{ -} - static int mmc_set_ios(struct mmc *mmc) { int ret = 0; @@ -1672,6 +1701,13 @@ static int sd_select_mode_and_width(struct mmc *mmc, uint card_caps) mmc_dump_capabilities("host", mmc->host_caps); #endif + if (mmc_host_is_spi(mmc)) { + mmc_set_bus_width(mmc, 1); + mmc_select_mode(mmc, SD_LEGACY); + mmc_set_clock(mmc, mmc->tran_speed, MMC_CLK_ENABLE); + return 0; + } + /* Restrict card's capabilities by what the host can do */ caps = card_caps & mmc->host_caps; @@ -1778,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 | @@ -1820,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, @@ -1917,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);\ @@ -1934,6 +2018,13 @@ static int mmc_select_mode_and_width(struct mmc *mmc, uint card_caps) mmc_dump_capabilities("host", mmc->host_caps); #endif + if (mmc_host_is_spi(mmc)) { + mmc_set_bus_width(mmc, 1); + mmc_select_mode(mmc, MMC_LEGACY); + mmc_set_clock(mmc, mmc->tran_speed, MMC_CLK_ENABLE); + return 0; + } + /* Restrict card's capabilities by what the host can do */ card_caps &= mmc->host_caps; @@ -1988,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); @@ -2122,6 +2220,9 @@ static int mmc_startup_v4(struct mmc *mmc) mmc->capacity_user = capacity; } + if (mmc->version >= MMC_VERSION_4_5) + mmc->gen_cmd6_time = ext_csd[EXT_CSD_GENERIC_CMD6_TIME]; + /* The partition data may be non-zero but it is only * effective if PARTITION_SETTING_COMPLETED is set in * EXT_CSD, so ignore any data if this bit is not set, @@ -2131,6 +2232,11 @@ static int mmc_startup_v4(struct mmc *mmc) part_completed = !!(ext_csd[EXT_CSD_PARTITION_SETTING] & EXT_CSD_PARTITION_SETTING_COMPLETED); + mmc->part_switch_time = ext_csd[EXT_CSD_PART_SWITCH_TIME]; + /* Some eMMC set the value too low so set a minimum */ + if (mmc->part_switch_time < MMC_MIN_PART_SWITCH_TIME && mmc->part_switch_time) + mmc->part_switch_time = MMC_MIN_PART_SWITCH_TIME; + /* store the partition info of emmc */ mmc->part_support = ext_csd[EXT_CSD_PARTITIONING_SUPPORT]; if ((ext_csd[EXT_CSD_PARTITIONING_SUPPORT] & PART_SUPPORT) || @@ -2664,7 +2770,6 @@ int mmc_get_op_cond(struct mmc *mmc) retry: mmc_set_initial_state(mmc); - mmc_send_init_stream(mmc); /* Reset the Card */ err = mmc_go_idle(mmc); |