On 05/13/2017 03:16 AM, Jean-Jacques Hiblot wrote: > The MMC startup process currently handles 4 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 <jjhib...@ti.com> > --- > drivers/mmc/mmc.c | 238 > +++++++++++++++++++++++++++++++++--------------------- > include/mmc.h | 15 +++- > 2 files changed, 157 insertions(+), 96 deletions(-) > > diff --git a/drivers/mmc/mmc.c b/drivers/mmc/mmc.c > index f42a0fe..2931871 100644 > --- a/drivers/mmc/mmc.c > +++ b/drivers/mmc/mmc.c > @@ -200,6 +200,7 @@ static int mmc_select_mode(struct mmc *mmc, enum bus_mode > mode) > { > mmc->selected_mode = mode; > mmc->tran_speed = mmc_mode2freq(mmc, mode); > + mmc->ddr_mode = mmc_is_mode_ddr(mode); > debug("selecting mode %s (freq : %d MHz)\n", mmc_mode_name(mode), > mmc->tran_speed / 1000000); > return 0; > @@ -602,11 +603,46 @@ int mmc_switch(struct mmc *mmc, u8 set, u8 index, u8 > value) > > } > > -static int mmc_change_freq(struct mmc *mmc) > +static int mmc_set_card_speed(struct mmc *mmc, enum bus_mode mode) > { > - ALLOC_CACHE_ALIGN_BUFFER(u8, ext_csd, MMC_MAX_BLOCK_LEN); > - char cardtype; > int err; > + int speed_bits; > + ALLOC_CACHE_ALIGN_BUFFER(u8, test_csd, MMC_MAX_BLOCK_LEN); > + > + switch (mode) { > + case MMC_HS: > + case MMC_HS_52: > + case MMC_DDR_52: > + speed_bits = EXT_CSD_TIMING_HS; > + case MMC_LEGACY: > + speed_bits = EXT_CSD_TIMING_LEGACY; > + break; > + default: > + return -EINVAL; > + } > + err = mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_HS_TIMING, > + speed_bits); > + if (err) > + return err; > + > + if ((mode == MMC_HS) || (mode == MMC_HS_52)) { > + /* Now check to see that it worked */ > + err = mmc_send_ext_csd(mmc, test_csd); > + if (err) > + return err; > + > + /* No high-speed support */ > + if (!test_csd[EXT_CSD_HS_TIMING]) > + return -ENOTSUPP; > + } > + > + return 0; > +} > + > +static int mmc_get_capabilities(struct mmc *mmc) > +{ > + u8 *ext_csd = mmc->ext_csd; > + char cardtype; > > mmc->card_caps = MMC_MODE_1BIT; > > @@ -617,38 +653,23 @@ static int mmc_change_freq(struct mmc *mmc) > if (mmc->version < MMC_VERSION_4) > return 0; > > - mmc->card_caps |= MMC_MODE_4BIT | MMC_MODE_8BIT; > - > - err = mmc_send_ext_csd(mmc, ext_csd); > + if (!ext_csd) { > + error("No ext_csd found!\n"); /* this should enver happen */ > + return -ENOTSUPP; > + } > > - if (err) > - return err; > + mmc->card_caps |= MMC_MODE_4BIT | MMC_MODE_8BIT; > > cardtype = ext_csd[EXT_CSD_CARD_TYPE] & 0xf; > > - err = mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_HS_TIMING, 1); > - > - if (err) > - return err; > - > - /* Now check to see that it worked */ > - err = mmc_send_ext_csd(mmc, ext_csd); > - > - if (err) > - return err; > - > - /* No high-speed support */ > - if (!ext_csd[EXT_CSD_HS_TIMING]) > - return 0; > - > /* High Speed is set, there are two types: 52MHz and 26MHz */ > if (cardtype & EXT_CSD_CARD_TYPE_52) { > - if (cardtype & EXT_CSD_CARD_TYPE_DDR_1_8V) > + if (cardtype & EXT_CSD_CARD_TYPE_DDR_52) > mmc->card_caps |= MMC_MODE_DDR_52MHz; > - mmc->card_caps |= MMC_MODE_HS_52MHz | MMC_MODE_HS; > - } else { > - mmc->card_caps |= MMC_MODE_HS; > + mmc->card_caps |= MMC_MODE_HS_52MHz; > } > + if (cardtype & EXT_CSD_CARD_TYPE_26) > + mmc->card_caps |= MMC_MODE_HS; > > return 0; > } > @@ -1320,33 +1341,58 @@ static int mmc_read_and_compare_ext_csd(struct mmc > *mmc) > } > > > -static int mmc_select_bus_freq_width(struct mmc *mmc) > +static const struct mode_width_tuning mmc_modes_by_pref[] = { > + { > + .mode = MMC_HS_200, > + .widths = MMC_MODE_8BIT | MMC_MODE_4BIT, > + }, > + { > + .mode = MMC_DDR_52, > + .widths = MMC_MODE_8BIT | MMC_MODE_4BIT, > + }, > + { > + .mode = MMC_HS_52, > + .widths = MMC_MODE_8BIT | MMC_MODE_4BIT | MMC_MODE_1BIT, > + }, > + { > + .mode = MMC_HS, > + .widths = MMC_MODE_8BIT | MMC_MODE_4BIT | MMC_MODE_1BIT, > + }, > + { > + .mode = MMC_LEGACY, > + .widths = MMC_MODE_8BIT | MMC_MODE_4BIT | MMC_MODE_1BIT, > + } > +}; > +#define for_each_mmc_mode_by_pref(caps, mwt) \ > + for (mwt = mmc_modes_by_pref;\ > + mwt < mmc_modes_by_pref + ARRAY_SIZE(mmc_modes_by_pref);\ > + mwt++) \ > + if (caps & MMC_CAP(mwt->mode)) > + > +static const struct ext_csd_bus_width { > + uint cap; > + bool is_ddr; > + uint ext_csd_bits; > +} ext_csd_bus_width[] = { > + {MMC_MODE_8BIT, true, EXT_CSD_BUS_WIDTH_8 | EXT_CSD_DDR}, > + {MMC_MODE_4BIT, true, EXT_CSD_BUS_WIDTH_4 | EXT_CSD_DDR}, > + {MMC_MODE_8BIT, false, EXT_CSD_BUS_WIDTH_8}, > + {MMC_MODE_4BIT, false, EXT_CSD_BUS_WIDTH_4}, > + {MMC_MODE_1BIT, false, EXT_CSD_BUS_WIDTH_1}, > +}; > +#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);\ > + ecbv++) \ > + if ((ddr == ecbv->is_ddr) && (caps & ecbv->cap)) > + > +static int mmc_select_mode_and_width(struct mmc *mmc) > { > - /* An array of possible bus widths in order of preference */ > - static const unsigned ext_csd_bits[] = { > - EXT_CSD_DDR_BUS_WIDTH_8, > - EXT_CSD_DDR_BUS_WIDTH_4, > - EXT_CSD_BUS_WIDTH_8, > - EXT_CSD_BUS_WIDTH_4, > - EXT_CSD_BUS_WIDTH_1, > - }; > - /* An array to map CSD bus widths to host cap bits */ > - static const unsigned ext_to_hostcaps[] = { > - [EXT_CSD_DDR_BUS_WIDTH_4] = > - MMC_MODE_DDR_52MHz | MMC_MODE_4BIT, > - [EXT_CSD_DDR_BUS_WIDTH_8] = > - MMC_MODE_DDR_52MHz | MMC_MODE_8BIT, > - [EXT_CSD_BUS_WIDTH_4] = MMC_MODE_4BIT, > - [EXT_CSD_BUS_WIDTH_8] = MMC_MODE_8BIT, > - }; > - /* An array to map chosen bus width to an integer */ > - static const unsigned widths[] = { > - 8, 4, 8, 4, 1, > - }; > int err; > - int idx; > + const struct mode_width_tuning *mwt; > + const struct ext_csd_bus_width *ecbw; > > - err = mmc_change_freq(mmc); > + err = mmc_get_capabilities(mmc); > if (err) > return err; > > @@ -1357,60 +1403,64 @@ static int mmc_select_bus_freq_width(struct mmc *mmc) > if (mmc->version < MMC_VERSION_4) > return 0; > > + > if (!mmc->ext_csd) { > error("No ext_csd found!\n"); /* this should enver happen */ > return -ENOTSUPP; > } > > - for (idx = 0; idx < ARRAY_SIZE(ext_csd_bits); idx++) { > - unsigned int extw = ext_csd_bits[idx]; > - unsigned int caps = ext_to_hostcaps[extw]; > + for_each_mmc_mode_by_pref(mmc->card_caps, mwt) { > + for_each_supported_width(mmc->card_caps & mwt->widths, > + mmc_is_mode_ddr(mwt->mode), ecbw) { > + debug("trying mode %s width %d (at %d MHz)\n", > + mmc_mode_name(mwt->mode), > + bus_width(ecbw->cap), > + mmc_mode2freq(mmc, mwt->mode) / 1000000); > > - /* > - * If the bus width is still not changed, > - * don't try to set the default again. > - * Otherwise, recover from switch attempts > - * by switching to 1-bit bus width. > - */ > - if (extw == EXT_CSD_BUS_WIDTH_1 && > - mmc->bus_width == 1) { > - err = 0; > - break; > - } > - > - /* > - * Check to make sure the card and controller support > - * these capabilities > - */ > - if ((mmc->card_caps & caps) != caps) > - continue; > - > - err = mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL, > - EXT_CSD_BUS_WIDTH, extw); > + /* configure the bus width (card + host) */ > + err = mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL, > + EXT_CSD_BUS_WIDTH, > + ecbw->ext_csd_bits & ~EXT_CSD_DDR); > + if (err) > + goto error; > + mmc_set_bus_width(mmc, bus_width(ecbw->cap)); > > - if (err) > - continue; > + /* 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 (err) > + goto error; > + } > > - mmc->ddr_mode = (caps & MMC_MODE_DDR_52MHz) ? 1 : 0; > - mmc_set_bus_width(mmc, widths[idx]); > + /* configure the bus mode (host) */ > + mmc_select_mode(mmc, mwt->mode); > + mmc_set_clock(mmc, mmc->tran_speed); > > - err = mmc_read_and_compare_ext_csd(mmc); > - if (!err) > - break; > + /* do a transfer to check the configuration */ > + err = mmc_read_and_compare_ext_csd(mmc); > + if (!err) > + return 0; > +error: > + /* if an error occured, revert to a safer bus mode */ > + mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL, > + EXT_CSD_BUS_WIDTH, EXT_CSD_BUS_WIDTH_1); > + mmc_select_mode(mmc, MMC_LEGACY); > + mmc_set_bus_width(mmc, 1); > + } > } > > - if (err) > - return err; > - > - if (mmc->card_caps & MMC_MODE_HS_52MHz) { > - if (mmc->ddr_mode) > - mmc_select_mode(mmc, MMC_DDR_52); > - else > - mmc_select_mode(mmc, MMC_HS_52); > - } else if (mmc->card_caps & MMC_MODE_HS) > - mmc_select_mode(mmc, MMC_HS); > + error("unable to select a mode\n"); > > - return err; > + return -ENOTSUPP; > } > > static int mmc_startup_v4(struct mmc *mmc) > @@ -1747,7 +1797,7 @@ static int mmc_startup(struct mmc *mmc) > if (IS_SD(mmc)) > err = sd_select_mode_and_width(mmc); > else > - err = mmc_select_bus_freq_width(mmc); > + err = mmc_select_mode_and_width(mmc); > > if (err) > return err; > diff --git a/include/mmc.h b/include/mmc.h > index 1ffa7ec..3c6971d 100644 > --- a/include/mmc.h > +++ b/include/mmc.h > @@ -213,9 +213,10 @@ > #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_BUS_WIDTH_4 5 /* Card is in 4 bit DDR mode */ > -#define EXT_CSD_DDR_BUS_WIDTH_8 6 /* Card is in 8 bit DDR mode */ > +#define EXT_CSD_DDR 4 /* Card is in DDR mode */
Why do you change to 4 as DDR mode? > > +#define EXT_CSD_TIMING_LEGACY 0 /* no high speed */ > +#define EXT_CSD_TIMING_HS 1 /* HS */ > #define EXT_CSD_BOOT_ACK_ENABLE (1 << 6) > #define EXT_CSD_BOOT_PARTITION_ENABLE (1 << 3) > #define EXT_CSD_PARTITION_ACCESS_ENABLE (1 << 0) > @@ -424,6 +425,16 @@ enum bus_mode { > > const char *mmc_mode_name(enum bus_mode mode); > 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)) > + return true; > + else > + return false; > +} > + > + > /* > * With CONFIG_DM_MMC enabled, struct mmc can be accessed from the MMC device > * with mmc_get_mmc_dev(). > _______________________________________________ U-Boot mailing list U-Boot@lists.denx.de https://lists.denx.de/listinfo/u-boot