diff options
Diffstat (limited to 'drivers/mmc/core/mmc.c')
-rw-r--r-- | drivers/mmc/core/mmc.c | 84 |
1 files changed, 82 insertions, 2 deletions
diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c index 5d438ad..97a664f 100644 --- a/drivers/mmc/core/mmc.c +++ b/drivers/mmc/core/mmc.c @@ -235,6 +235,11 @@ static void mmc_select_card_type(struct mmc_card *card) avail_type |= EXT_CSD_CARD_TYPE_HS400_1_2V; } + if ((caps2 & MMC_CAP2_HS400_ES) && + card->ext_csd.strobe_support && + (avail_type & EXT_CSD_CARD_TYPE_HS400)) + avail_type |= EXT_CSD_CARD_TYPE_HS400ES; + card->ext_csd.hs_max_dtr = hs_max_dtr; card->ext_csd.hs200_max_dtr = hs200_max_dtr; card->mmc_avail_type = avail_type; @@ -386,6 +391,7 @@ static int mmc_decode_ext_csd(struct mmc_card *card, u8 *ext_csd) mmc_card_set_blockaddr(card); } + card->ext_csd.strobe_support = ext_csd[EXT_CSD_STROBE_SUPPORT]; card->ext_csd.raw_card_type = ext_csd[EXT_CSD_CARD_TYPE]; mmc_select_card_type(card); @@ -1223,6 +1229,78 @@ out_err: return err; } +static int mmc_select_hs400es(struct mmc_card *card) +{ + struct mmc_host *host = card->host; + int err = 0; + u8 val; + + if (!(host->caps & MMC_CAP_8_BIT_DATA)) { + err = -ENOTSUPP; + goto out_err; + } + + err = mmc_select_bus_width(card); + if (err < 0) + goto out_err; + + /* Switch card to HS mode */ + err = mmc_select_hs(card); + if (err) { + pr_err("%s: switch to high-speed failed, err:%d\n", + mmc_hostname(host), err); + goto out_err; + } + + err = mmc_switch_status(card); + if (err) + goto out_err; + + /* Switch card to DDR with strobe bit */ + val = EXT_CSD_DDR_BUS_WIDTH_8 | EXT_CSD_BUS_WIDTH_STROBE; + err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, + EXT_CSD_BUS_WIDTH, + val, + card->ext_csd.generic_cmd6_time); + if (err) { + pr_err("%s: switch to bus width for hs400es failed, err:%d\n", + mmc_hostname(host), err); + goto out_err; + } + + /* Switch card to HS400 */ + val = EXT_CSD_TIMING_HS400 | + card->drive_strength << EXT_CSD_DRV_STR_SHIFT; + err = __mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, + EXT_CSD_HS_TIMING, val, + card->ext_csd.generic_cmd6_time, + true, false, true); + if (err) { + pr_err("%s: switch to hs400es failed, err:%d\n", + mmc_hostname(host), err); + goto out_err; + } + + /* Set host controller to HS400 timing and frequency */ + mmc_set_timing(host, MMC_TIMING_MMC_HS400); + + /* Controller enable enhanced strobe function */ + host->ios.enhanced_strobe = true; + if (host->ops->hs400_enhanced_strobe) + host->ops->hs400_enhanced_strobe(host, &host->ios); + + err = mmc_switch_status(card); + if (err) + goto out_err; + + return 0; + +out_err: + pr_err("%s: %s failed, error %d\n", mmc_hostname(card->host), + __func__, err); + return err; +} + static void mmc_select_driver_type(struct mmc_card *card) { int card_drv_type, drive_strength, drv_type; @@ -1310,7 +1388,7 @@ err: } /* - * Activate High Speed or HS200 mode if supported. + * Activate High Speed, HS200 or HS400ES mode if supported. */ static int mmc_select_timing(struct mmc_card *card) { @@ -1319,7 +1397,9 @@ static int mmc_select_timing(struct mmc_card *card) if (!mmc_can_ext_csd(card)) goto bus_speed; - if (card->mmc_avail_type & EXT_CSD_CARD_TYPE_HS200) + if (card->mmc_avail_type & EXT_CSD_CARD_TYPE_HS400ES) + err = mmc_select_hs400es(card); + else if (card->mmc_avail_type & EXT_CSD_CARD_TYPE_HS200) err = mmc_select_hs200(card); else if (card->mmc_avail_type & EXT_CSD_CARD_TYPE_HS) err = mmc_select_hs(card); |