diff options
author | Jean-Jacques Hiblot | 2017-09-21 16:29:57 +0200 |
---|---|---|
committer | Jaehoon Chung | 2018-01-12 18:11:04 +0900 |
commit | d0c221fe7336fc7d9ada57d96f4a8911a3aac041 (patch) | |
tree | 2f77d13094264e9c5af62e5564e4b73af8985a40 /drivers | |
parent | 7a96ec74592847cd7bf5664cb72996500ebde92b (diff) |
mmc: refactor SD startup to make it easier to support new modes
The SDcard startup process currently handles only 2 modes. To make it
easier to add support for more modes, let's make the process more generic
and use a list of the modes to try.
The major functional change is that when a mode fails we try the next one.
Not all modes are tried, only those supported by the card and the host.
Signed-off-by: Jean-Jacques Hiblot <jjhiblot@ti.com>
Reviewed-by: Simon Glass <sjg@chromium.org>
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/mmc/mmc.c | 183 |
1 files changed, 129 insertions, 54 deletions
diff --git a/drivers/mmc/mmc.c b/drivers/mmc/mmc.c index deb4786585a..3002b727c83 100644 --- a/drivers/mmc/mmc.c +++ b/drivers/mmc/mmc.c @@ -237,7 +237,8 @@ int mmc_send_status(struct mmc *mmc, int timeout) (cmd.response[0] & MMC_STATUS_CURR_STATE) != MMC_STATE_PRG) break; - else if (cmd.response[0] & MMC_STATUS_MASK) { + + if (cmd.response[0] & MMC_STATUS_MASK) { #if !defined(CONFIG_SPL_BUILD) || defined(CONFIG_SPL_LIBCOMMON_SUPPORT) printf("Status Error: 0x%08X\n", cmd.response[0]); @@ -610,7 +611,7 @@ static int mmc_change_freq(struct mmc *mmc) char cardtype; int err; - mmc->card_caps = 0; + mmc->card_caps = MMC_MODE_1BIT; if (mmc_host_is_spi(mmc)) return 0; @@ -936,7 +937,7 @@ static int sd_switch(struct mmc *mmc, int mode, int group, u8 value, u8 *resp) } -static int sd_change_freq(struct mmc *mmc) +static int sd_get_capabilities(struct mmc *mmc) { int err; struct mmc_cmd cmd; @@ -945,7 +946,7 @@ static int sd_change_freq(struct mmc *mmc) struct mmc_data data; int timeout; - mmc->card_caps = 0; + mmc->card_caps = MMC_MODE_1BIT; if (mmc_host_is_spi(mmc)) return 0; @@ -1022,26 +1023,53 @@ retry_scr: } /* If high-speed isn't supported, we return */ - if (!(__be32_to_cpu(switch_status[3]) & SD_HIGHSPEED_SUPPORTED)) - return 0; + if (__be32_to_cpu(switch_status[3]) & SD_HIGHSPEED_SUPPORTED) + mmc->card_caps |= MMC_CAP(SD_HS); - /* - * If the host doesn't support SD_HIGHSPEED, do not switch card to - * HIGHSPEED mode even if the card support SD_HIGHSPPED. - * This can avoid furthur problem when the card runs in different - * mode between the host. - */ - if (!((mmc->cfg->host_caps & MMC_MODE_HS_52MHz) && - (mmc->cfg->host_caps & MMC_MODE_HS))) - return 0; + return 0; +} + +static int sd_set_card_speed(struct mmc *mmc, enum bus_mode mode) +{ + int err; + + ALLOC_CACHE_ALIGN_BUFFER(uint, switch_status, 16); err = sd_switch(mmc, SD_SWITCH_SWITCH, 0, 1, (u8 *)switch_status); + if (err) + return err; + + if ((__be32_to_cpu(switch_status[4]) & 0x0f000000) != 0x01000000) + return -ENOTSUPP; + return 0; +} + +int sd_select_bus_width(struct mmc *mmc, int w) +{ + int err; + struct mmc_cmd cmd; + + if ((w != 4) && (w != 1)) + return -EINVAL; + + cmd.cmdidx = MMC_CMD_APP_CMD; + cmd.resp_type = MMC_RSP_R1; + cmd.cmdarg = mmc->rca << 16; + + err = mmc_send_cmd(mmc, &cmd, NULL); if (err) return err; - if ((__be32_to_cpu(switch_status[4]) & 0x0f000000) == 0x01000000) - mmc->card_caps |= MMC_MODE_HS; + cmd.cmdidx = SD_CMD_APP_SET_BUS_WIDTH; + cmd.resp_type = MMC_RSP_R1; + if (w == 4) + cmd.cmdarg = 2; + else if (w == 1) + cmd.cmdarg = 0; + err = mmc_send_cmd(mmc, &cmd, NULL); + if (err) + return err; return 0; } @@ -1133,6 +1161,18 @@ static const u8 multipliers[] = { 80, }; +static inline int bus_width(uint cap) +{ + if (cap == MMC_MODE_8BIT) + return 8; + if (cap == MMC_MODE_4BIT) + return 4; + if (cap == MMC_MODE_1BIT) + return 1; + printf("invalid bus witdh capability 0x%x\n", cap); + return 0; +} + #if !CONFIG_IS_ENABLED(DM_MMC) static void mmc_set_ios(struct mmc *mmc) { @@ -1176,8 +1216,9 @@ void mmc_dump_capabilities(const char *text, uint caps) printf("8, "); if (caps & MMC_MODE_4BIT) printf("4, "); - printf("1] modes ["); - + if (caps & MMC_MODE_1BIT) + printf("1, "); + printf("\b\b] modes ["); for (mode = MMC_LEGACY; mode < MMC_MODES_END; mode++) if (MMC_CAP(mode) & caps) printf("%s, ", mmc_mode_name(mode)); @@ -1185,47 +1226,81 @@ void mmc_dump_capabilities(const char *text, uint caps) } #endif -static int sd_select_bus_freq_width(struct mmc *mmc) +struct mode_width_tuning { + enum bus_mode mode; + uint widths; +}; + +static const struct mode_width_tuning sd_modes_by_pref[] = { + { + .mode = SD_HS, + .widths = MMC_MODE_4BIT | MMC_MODE_1BIT, + }, + { + .mode = SD_LEGACY, + .widths = MMC_MODE_4BIT | MMC_MODE_1BIT, + } +}; + +#define for_each_sd_mode_by_pref(caps, mwt) \ + for (mwt = sd_modes_by_pref;\ + mwt < sd_modes_by_pref + ARRAY_SIZE(sd_modes_by_pref);\ + mwt++) \ + if (caps & MMC_CAP(mwt->mode)) + +static int sd_select_mode_and_width(struct mmc *mmc) { int err; - struct mmc_cmd cmd; + uint widths[] = {MMC_MODE_4BIT, MMC_MODE_1BIT}; + const struct mode_width_tuning *mwt; - err = sd_change_freq(mmc); + err = sd_get_capabilities(mmc); if (err) return err; - /* Restrict card's capabilities by what the host can do */ - mmc->card_caps &= mmc->cfg->host_caps; - - if (mmc->card_caps & MMC_MODE_4BIT) { - cmd.cmdidx = MMC_CMD_APP_CMD; - cmd.resp_type = MMC_RSP_R1; - cmd.cmdarg = mmc->rca << 16; - - err = mmc_send_cmd(mmc, &cmd, NULL); - if (err) - return err; - - cmd.cmdidx = SD_CMD_APP_SET_BUS_WIDTH; - cmd.resp_type = MMC_RSP_R1; - cmd.cmdarg = 2; - err = mmc_send_cmd(mmc, &cmd, NULL); - if (err) - return err; - - mmc_set_bus_width(mmc, 4); + mmc->card_caps &= (mmc->cfg->host_caps | MMC_MODE_1BIT); + + for_each_sd_mode_by_pref(mmc->card_caps, mwt) { + uint *w; + + for (w = widths; w < widths + ARRAY_SIZE(widths); w++) { + if (*w & mmc->card_caps & mwt->widths) { + debug("trying mode %s width %d (at %d MHz)\n", + mmc_mode_name(mwt->mode), + bus_width(*w), + mmc_mode2freq(mmc, mwt->mode) / 1000000); + + /* configure the bus width (card + host) */ + err = sd_select_bus_width(mmc, bus_width(*w)); + if (err) + goto error; + mmc_set_bus_width(mmc, bus_width(*w)); + + /* configure the bus mode (card) */ + err = sd_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); + + err = sd_read_ssr(mmc); + if (!err) + return 0; + + printf("bad ssr\n"); + +error: + /* revert to a safer bus speed */ + mmc_select_mode(mmc, SD_LEGACY); + mmc_set_clock(mmc, mmc->tran_speed); + } + } } - err = sd_read_ssr(mmc); - if (err) - return err; - - if (mmc->card_caps & MMC_MODE_HS) - mmc_select_mode(mmc, SD_HS); - else - mmc_select_mode(mmc, SD_LEGACY); - - return 0; + printf("unable to select a mode\n"); + return -ENOTSUPP; } /* @@ -1290,7 +1365,7 @@ static int mmc_select_bus_freq_width(struct mmc *mmc) return err; /* Restrict card's capabilities by what the host can do */ - mmc->card_caps &= mmc->cfg->host_caps; + mmc->card_caps &= (mmc->cfg->host_caps | MMC_MODE_1BIT); /* Only version 4 of MMC supports wider bus widths */ if (mmc->version < MMC_VERSION_4) @@ -1685,7 +1760,7 @@ static int mmc_startup(struct mmc *mmc) return err; if (IS_SD(mmc)) - err = sd_select_bus_freq_width(mmc); + err = sd_select_mode_and_width(mmc); else err = mmc_select_bus_freq_width(mmc); |