On 05/13/2017 03:16 AM, Jean-Jacques Hiblot wrote: > Add UHS modes to the list of supported modes, get the UHS capabilites of > the SDcard and implement the procedure to switch the voltage (UHS modes > use 1v8 IO lines) > During the voltage switch procedure, DAT0 is used by the card to signal > when it's ready. The optional card_busy() callback can be used to get this > information from the host driver. > > Signed-off-by: Jean-Jacques Hiblot <jjhib...@ti.com> > --- > drivers/mmc/mmc.c | 169 > +++++++++++++++++++++++++++++++++++++++++++++++++++--- > include/mmc.h | 27 ++++++++- > 2 files changed, 188 insertions(+), 8 deletions(-) > > diff --git a/drivers/mmc/mmc.c b/drivers/mmc/mmc.c > index f6509f1..074d286 100644 > --- a/drivers/mmc/mmc.c > +++ b/drivers/mmc/mmc.c > @@ -31,6 +31,7 @@ static const unsigned int sd_au_size[] = { > }; > static int mmc_set_signal_voltage(struct mmc *mmc, uint signal_voltage); > static void mmc_power_cycle(struct mmc *mmc); > +static int mmc_card_busy(struct mmc *mmc); > > #if CONFIG_IS_ENABLED(MMC_TINY) > static struct mmc mmc_static; > @@ -403,7 +404,68 @@ static int mmc_go_idle(struct mmc *mmc) > return 0; > } > > -static int sd_send_op_cond(struct mmc *mmc) > +static int mmc_switch_voltage(struct mmc *mmc, int signal_voltage) > +{ > + struct mmc_cmd cmd; > + int err = 0; > + > + /* > + * Send CMD11 only if the request is to switch the card to > + * 1.8V signalling. > + */ > + if (signal_voltage == MMC_SIGNAL_VOLTAGE_330) > + return mmc_set_signal_voltage(mmc, signal_voltage); > + > + cmd.cmdidx = SD_CMD_SWITCH_UHS18V; > + cmd.cmdarg = 0; > + cmd.resp_type = MMC_RSP_R1; > + > + err = mmc_send_cmd(mmc, &cmd, NULL); > + if (err) > + goto fail;
goto fail..then it's changed the error number..just return err. > + > + if (!mmc_host_is_spi(host) && (cmd.response[0] & MMC_STATUS_ERROR)) > + goto fail; > + > + /* > + * The card should drive cmd and dat[0:3] low immediately > + * after the response of cmd11, but wait 1 ms to be sure > + */ > + udelay(1000); > + if (mmc_card_busy(mmc)) > + goto fail; If Card is busy..it's not -EIO..it may be -EBUSY. > + > + /* > + * 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); > + > + err = mmc_set_signal_voltage(mmc, signal_voltage); > + if (err) > + goto fail; ditto..return err. > + > + /* Keep clock gated for at least 10 ms, though spec only says 5 ms */ > + udelay(10000); > + mmc_set_clock(mmc, mmc->clock, false); > + > + /* Wait for at least 1 ms according to spec */ > + udelay(1000); > + > + /* > + * Failure to switch is indicated by the card holding > + * dat[0:3] low > + */ > + if (mmc_card_busy(mmc)) > + goto fail; ditto -EBUSY. > + > + return 0; > + > +fail: > + return -EIO; > +} > + > +static int sd_send_op_cond(struct mmc *mmc, bool uhs_en) > { > int timeout = 1000; > int err; > @@ -435,6 +497,9 @@ static int sd_send_op_cond(struct mmc *mmc) > if (mmc->version == SD_VERSION_2) > cmd.cmdarg |= OCR_HCS; > > + if (uhs_en) > + cmd.cmdarg |= OCR_S18R; > + > err = mmc_send_cmd(mmc, &cmd, NULL); > > if (err) > @@ -465,6 +530,13 @@ static int sd_send_op_cond(struct mmc *mmc) > > mmc->ocr = cmd.response[0]; > > + if (!(mmc_host_is_spi(mmc)) && (cmd.response[0] & 0x41000000) > + == 0x41000000) { > + err = mmc_switch_voltage(mmc, MMC_SIGNAL_VOLTAGE_180); > + if (err) > + return err; > + } > + > mmc->high_capacity = ((mmc->ocr & OCR_HCS) == OCR_HCS); > mmc->rca = 0; > > @@ -977,6 +1049,7 @@ static int sd_get_capabilities(struct mmc *mmc) > ALLOC_CACHE_ALIGN_BUFFER(uint, switch_status, 16); > struct mmc_data data; > int timeout; > + u32 sd3_bus_mode; > > mmc->card_caps = MMC_MODE_1BIT; > > @@ -1058,6 +1131,22 @@ retry_scr: > if (__be32_to_cpu(switch_status[3]) & SD_HIGHSPEED_SUPPORTED) > mmc->card_caps |= MMC_CAP(SD_HS); > > + /* Version before 3.0 don't support UHS modes */ > + if (mmc->version < SD_VERSION_3) > + return 0; > + > + sd3_bus_mode = __be32_to_cpu(switch_status[3]) >> 16 & 0x1f; > + if (sd3_bus_mode & SD_MODE_UHS_SDR104) > + mmc->card_caps |= MMC_CAP(UHS_SDR104); > + if (sd3_bus_mode & SD_MODE_UHS_SDR50) > + mmc->card_caps |= MMC_CAP(UHS_SDR50); > + if (sd3_bus_mode & SD_MODE_UHS_SDR25) > + mmc->card_caps |= MMC_CAP(UHS_SDR25); > + if (sd3_bus_mode & SD_MODE_UHS_SDR12) > + mmc->card_caps |= MMC_CAP(UHS_SDR12); > + if (sd3_bus_mode & SD_MODE_UHS_DDR50) > + mmc->card_caps |= MMC_CAP(UHS_DDR50); > + > return 0; > } > > @@ -1065,13 +1154,36 @@ static int sd_set_card_speed(struct mmc *mmc, enum > bus_mode mode) > { > int err; > ALLOC_CACHE_ALIGN_BUFFER(uint, switch_status, 16); > + int speed; > + > + switch (mode) { > + case SD_LEGACY: > + case UHS_SDR12: > + speed = UHS_SDR12_BUS_SPEED; > + break; > + case SD_HS: > + case UHS_SDR25: > + speed = UHS_SDR25_BUS_SPEED; > + break; > + case UHS_SDR50: > + speed = UHS_SDR50_BUS_SPEED; > + break; > + case UHS_DDR50: > + speed = UHS_DDR50_BUS_SPEED; > + break; > + case UHS_SDR104: > + speed = UHS_SDR104_BUS_SPEED; > + break; > + default: > + return -EINVAL; > + } > > - err = sd_switch(mmc, SD_SWITCH_SWITCH, 0, 1, (u8 *)switch_status); > + err = sd_switch(mmc, SD_SWITCH_SWITCH, 0, speed, (u8 *)switch_status); > > if (err) > return err; > > - if ((__be32_to_cpu(switch_status[4]) & 0x0f000000) != 0x01000000) > + if ((__be32_to_cpu(switch_status[4]) >> 24) != speed) > return -ENOTSUPP; > > return 0; > @@ -1291,10 +1403,31 @@ static int mmc_set_signal_voltage(struct mmc *mmc, > uint signal_voltage) > > static const struct mode_width_tuning sd_modes_by_pref[] = { > { > + .mode = UHS_SDR104, > + .widths = MMC_MODE_4BIT | MMC_MODE_1BIT, > + .tuning = MMC_SEND_TUNING_BLOCK > + }, > + { > + .mode = UHS_SDR50, > + .widths = MMC_MODE_4BIT | MMC_MODE_1BIT, > + }, > + { > + .mode = UHS_DDR50, > + .widths = MMC_MODE_4BIT | MMC_MODE_1BIT, > + }, > + { > + .mode = UHS_SDR25, > + .widths = MMC_MODE_4BIT | MMC_MODE_1BIT, > + }, > + { > .mode = SD_HS, > .widths = MMC_MODE_4BIT | MMC_MODE_1BIT, > }, > { > + .mode = UHS_SDR12, > + .widths = MMC_MODE_4BIT | MMC_MODE_1BIT, > + }, > + { > .mode = SD_LEGACY, > .widths = MMC_MODE_4BIT | MMC_MODE_1BIT, > } > @@ -1310,18 +1443,24 @@ static int sd_select_mode_and_width(struct mmc *mmc) > int err; > uint widths[] = {MMC_MODE_4BIT, MMC_MODE_1BIT}; > const struct mode_width_tuning *mwt; > + bool uhs_en = (mmc->ocr & OCR_S18R) ? true : false; > + uint caps; > + > > 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 | MMC_MODE_1BIT); > + caps = mmc->card_caps & (mmc->cfg->host_caps | MMC_MODE_1BIT); > > - for_each_sd_mode_by_pref(mmc->card_caps, mwt) { > + if (!uhs_en) > + caps &= ~UHS_CAPS; > + > + for_each_sd_mode_by_pref(caps, mwt) { > uint *w; > > for (w = widths; w < widths + ARRAY_SIZE(widths); w++) { > - if (*w & mmc->card_caps & mwt->widths) { > + if (*w & caps & mwt->widths) { > debug("trying mode %s width %d (at %d MHz)\n", > mmc_mode_name(mwt->mode), > bus_width(*w), > @@ -1342,6 +1481,16 @@ static int sd_select_mode_and_width(struct mmc *mmc) > mmc_select_mode(mmc, mwt->mode); > mmc_set_clock(mmc, mmc->tran_speed, false); > > + /* execute tuning if needed */ > + if (mwt->tuning && !mmc_host_is_spi(mmc)) { > + err = mmc_execute_tuning(mmc, > + mwt->tuning); > + if (err) { > + debug("tuning failed\n"); > + goto error; > + } > + } > + > err = sd_read_ssr(mmc); > if (!err) > return 0; > @@ -1993,6 +2142,7 @@ static void mmc_power_cycle(struct mmc *mmc) > int mmc_start_init(struct mmc *mmc) > { > bool no_card; > + bool uhs_en = supports_uhs(mmc->cfg->host_caps); > int err; > > /* we pretend there's no card when init is NULL */ > @@ -2028,6 +2178,7 @@ int mmc_start_init(struct mmc *mmc) > #endif > mmc->ddr_mode = 0; > > +retry: > mmc_power_cycle(mmc); > > /* Reset the Card */ > @@ -2043,7 +2194,11 @@ int mmc_start_init(struct mmc *mmc) > err = mmc_send_if_cond(mmc); > > /* Now try to get the SD card's operating condition */ > - err = sd_send_op_cond(mmc); > + err = sd_send_op_cond(mmc, uhs_en); > + if (err && uhs_en) { > + uhs_en = false; > + goto retry; > + } > > /* If the command timed out, we check for an MMC card */ > if (err == -ETIMEDOUT) { > diff --git a/include/mmc.h b/include/mmc.h > index b42f686..775c47e 100644 > --- a/include/mmc.h > +++ b/include/mmc.h > @@ -87,6 +87,7 @@ > #define MMC_CMD_SET_BLOCKLEN 16 > #define MMC_CMD_READ_SINGLE_BLOCK 17 > #define MMC_CMD_READ_MULTIPLE_BLOCK 18 > +#define MMC_SEND_TUNING_BLOCK 19 Ditto..Use the MMC_CMD prefix. :) > #define MMC_SEND_TUNING_BLOCK_HS200 21 > #define MMC_CMD_SET_BLOCK_COUNT 23 > #define MMC_CMD_WRITE_SINGLE_BLOCK 24 > @@ -117,7 +118,8 @@ > > static inline bool mmc_is_tuning_cmd(uint cmdidx) > { > - if (cmdidx == MMC_SEND_TUNING_BLOCK_HS200) > + if ((cmdidx == MMC_SEND_TUNING_BLOCK_HS200) || > + (cmdidx == MMC_SEND_TUNING_BLOCK)) > return true; > return false; > } > @@ -126,8 +128,22 @@ static inline bool mmc_is_tuning_cmd(uint cmdidx) > #define SD_HIGHSPEED_BUSY 0x00020000 > #define SD_HIGHSPEED_SUPPORTED 0x00020000 > > +#define UHS_SDR12_BUS_SPEED 0 > +#define HIGH_SPEED_BUS_SPEED 1 > +#define UHS_SDR25_BUS_SPEED 1 > +#define UHS_SDR50_BUS_SPEED 2 > +#define UHS_SDR104_BUS_SPEED 3 > +#define UHS_DDR50_BUS_SPEED 4 > + > +#define SD_MODE_UHS_SDR12 (1 << UHS_SDR12_BUS_SPEED) > +#define SD_MODE_UHS_SDR25 (1 << UHS_SDR25_BUS_SPEED) > +#define SD_MODE_UHS_SDR50 (1 << UHS_SDR50_BUS_SPEED) > +#define SD_MODE_UHS_SDR104 (1 << UHS_SDR104_BUS_SPEED) > +#define SD_MODE_UHS_DDR50 (1 << UHS_DDR50_BUS_SPEED) > + > #define OCR_BUSY 0x80000000 > #define OCR_HCS 0x40000000 > +#define OCR_S18R 0x1000000 > #define OCR_VOLTAGE_MASK 0x007FFF80 > #define OCR_ACCESS_MODE 0x60000000 > > @@ -490,6 +506,15 @@ static inline bool mmc_is_mode_ddr(enum bus_mode mode) > return false; > } > > +#define UHS_CAPS (MMC_CAP(UHS_SDR12) | MMC_CAP(UHS_SDR25) | \ > + MMC_CAP(UHS_SDR50) | MMC_CAP(UHS_SDR104) | \ > + MMC_CAP(UHS_DDR50)) > + > +static inline bool supports_uhs(uint caps) > +{ > + return (caps & UHS_CAPS) ? true : false; > +} > + > > /* > * With CONFIG_DM_MMC enabled, struct mmc can be accessed from the MMC device > _______________________________________________ U-Boot mailing list U-Boot@lists.denx.de https://lists.denx.de/listinfo/u-boot