[U-Boot] [PATCH 20/22] mmc: Add support for UHS modes

Jaehoon Chung jh80.chung at samsung.com
Wed May 17 01:59:34 UTC 2017


On 05/16/2017 11:20 PM, Jean-Jacques Hiblot wrote:
> 
> 
> On 12/05/2017 20:16, 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 <jjhiblot at 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;
>> +
>> +    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))
> Hi all,
> 
> there is an error her that I let through while cleaning the code before posting.
> The line above should be:  if (!mmc_card_busy(mmc)).
> I'll fix this in the v2.
> 
> Jaehoon, if you're testing the patches you might want to fix this first (you can just remove the calls to mmc_card_busy() for the moment). I hope it did not impede you.

Thanks for noticing. :)

Best Regards,
Jaehoon Chung

> 
> Jean-Jacques
>> +        goto fail;
>> +
>> +    /*
>> +     * 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;
>> +
>> +    /* 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;
>> +
>> +    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
>>   #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
> 
> 
> 
> 



More information about the U-Boot mailing list