|
@@ -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);
|