|
@@ -240,7 +240,7 @@ static int mmc_get_ext_csd(struct mmc_card *card, u8 **new_ext_csd)
|
|
|
static void mmc_select_card_type(struct mmc_card *card)
|
|
|
{
|
|
|
struct mmc_host *host = card->host;
|
|
|
- u8 card_type = card->ext_csd.raw_card_type & EXT_CSD_CARD_TYPE_MASK;
|
|
|
+ u8 card_type = card->ext_csd.raw_card_type;
|
|
|
u32 caps = host->caps, caps2 = host->caps2;
|
|
|
unsigned int hs_max_dtr = 0, hs200_max_dtr = 0;
|
|
|
unsigned int avail_type = 0;
|
|
@@ -281,6 +281,18 @@ static void mmc_select_card_type(struct mmc_card *card)
|
|
|
avail_type |= EXT_CSD_CARD_TYPE_HS200_1_2V;
|
|
|
}
|
|
|
|
|
|
+ if (caps2 & MMC_CAP2_HS400_1_8V &&
|
|
|
+ card_type & EXT_CSD_CARD_TYPE_HS400_1_8V) {
|
|
|
+ hs200_max_dtr = MMC_HS200_MAX_DTR;
|
|
|
+ avail_type |= EXT_CSD_CARD_TYPE_HS400_1_8V;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (caps2 & MMC_CAP2_HS400_1_2V &&
|
|
|
+ card_type & EXT_CSD_CARD_TYPE_HS400_1_2V) {
|
|
|
+ hs200_max_dtr = MMC_HS200_MAX_DTR;
|
|
|
+ avail_type |= EXT_CSD_CARD_TYPE_HS400_1_2V;
|
|
|
+ }
|
|
|
+
|
|
|
card->ext_csd.hs_max_dtr = hs_max_dtr;
|
|
|
card->ext_csd.hs200_max_dtr = hs200_max_dtr;
|
|
|
card->mmc_avail_type = avail_type;
|
|
@@ -499,6 +511,8 @@ static int mmc_read_ext_csd(struct mmc_card *card, u8 *ext_csd)
|
|
|
ext_csd[EXT_CSD_PWR_CL_DDR_52_195];
|
|
|
card->ext_csd.raw_pwr_cl_ddr_52_360 =
|
|
|
ext_csd[EXT_CSD_PWR_CL_DDR_52_360];
|
|
|
+ card->ext_csd.raw_pwr_cl_ddr_200_360 =
|
|
|
+ ext_csd[EXT_CSD_PWR_CL_DDR_200_360];
|
|
|
}
|
|
|
|
|
|
if (card->ext_csd.rev >= 5) {
|
|
@@ -665,7 +679,10 @@ static int mmc_compare_ext_csds(struct mmc_card *card, unsigned bus_width)
|
|
|
(card->ext_csd.raw_pwr_cl_ddr_52_195 ==
|
|
|
bw_ext_csd[EXT_CSD_PWR_CL_DDR_52_195]) &&
|
|
|
(card->ext_csd.raw_pwr_cl_ddr_52_360 ==
|
|
|
- bw_ext_csd[EXT_CSD_PWR_CL_DDR_52_360]));
|
|
|
+ bw_ext_csd[EXT_CSD_PWR_CL_DDR_52_360]) &&
|
|
|
+ (card->ext_csd.raw_pwr_cl_ddr_200_360 ==
|
|
|
+ bw_ext_csd[EXT_CSD_PWR_CL_DDR_200_360]));
|
|
|
+
|
|
|
if (err)
|
|
|
err = -EINVAL;
|
|
|
|
|
@@ -768,7 +785,9 @@ static int __mmc_select_powerclass(struct mmc_card *card,
|
|
|
ext_csd->raw_pwr_cl_52_360 :
|
|
|
ext_csd->raw_pwr_cl_ddr_52_360;
|
|
|
else if (host->ios.clock <= MMC_HS200_MAX_DTR)
|
|
|
- pwrclass_val = ext_csd->raw_pwr_cl_200_360;
|
|
|
+ pwrclass_val = (bus_width == EXT_CSD_DDR_BUS_WIDTH_8) ?
|
|
|
+ ext_csd->raw_pwr_cl_ddr_200_360 :
|
|
|
+ ext_csd->raw_pwr_cl_200_360;
|
|
|
break;
|
|
|
default:
|
|
|
pr_warning("%s: Voltage range not supported "
|
|
@@ -832,7 +851,8 @@ static void mmc_set_bus_speed(struct mmc_card *card)
|
|
|
{
|
|
|
unsigned int max_dtr = (unsigned int)-1;
|
|
|
|
|
|
- if (mmc_card_hs200(card) && max_dtr > card->ext_csd.hs200_max_dtr)
|
|
|
+ if ((mmc_card_hs200(card) || mmc_card_hs400(card)) &&
|
|
|
+ max_dtr > card->ext_csd.hs200_max_dtr)
|
|
|
max_dtr = card->ext_csd.hs200_max_dtr;
|
|
|
else if (mmc_card_hs(card) && max_dtr > card->ext_csd.hs_max_dtr)
|
|
|
max_dtr = card->ext_csd.hs_max_dtr;
|
|
@@ -985,6 +1005,61 @@ static int mmc_select_hs_ddr(struct mmc_card *card)
|
|
|
return err;
|
|
|
}
|
|
|
|
|
|
+static int mmc_select_hs400(struct mmc_card *card)
|
|
|
+{
|
|
|
+ struct mmc_host *host = card->host;
|
|
|
+ int err = 0;
|
|
|
+
|
|
|
+ /*
|
|
|
+ * HS400 mode requires 8-bit bus width
|
|
|
+ */
|
|
|
+ if (!(card->mmc_avail_type & EXT_CSD_CARD_TYPE_HS400 &&
|
|
|
+ host->ios.bus_width == MMC_BUS_WIDTH_8))
|
|
|
+ return 0;
|
|
|
+
|
|
|
+ /*
|
|
|
+ * Before switching to dual data rate operation for HS400,
|
|
|
+ * it is required to convert from HS200 mode to HS mode.
|
|
|
+ */
|
|
|
+ mmc_set_timing(card->host, MMC_TIMING_MMC_HS);
|
|
|
+ mmc_set_bus_speed(card);
|
|
|
+
|
|
|
+ err = __mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
|
|
|
+ EXT_CSD_HS_TIMING, EXT_CSD_TIMING_HS,
|
|
|
+ card->ext_csd.generic_cmd6_time,
|
|
|
+ true, true, true);
|
|
|
+ if (err) {
|
|
|
+ pr_warn("%s: switch to high-speed from hs200 failed, err:%d\n",
|
|
|
+ mmc_hostname(host), err);
|
|
|
+ return err;
|
|
|
+ }
|
|
|
+
|
|
|
+ err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
|
|
|
+ EXT_CSD_BUS_WIDTH,
|
|
|
+ EXT_CSD_DDR_BUS_WIDTH_8,
|
|
|
+ card->ext_csd.generic_cmd6_time);
|
|
|
+ if (err) {
|
|
|
+ pr_warn("%s: switch to bus width for hs400 failed, err:%d\n",
|
|
|
+ mmc_hostname(host), err);
|
|
|
+ return err;
|
|
|
+ }
|
|
|
+
|
|
|
+ err = __mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
|
|
|
+ EXT_CSD_HS_TIMING, EXT_CSD_TIMING_HS400,
|
|
|
+ card->ext_csd.generic_cmd6_time,
|
|
|
+ true, true, true);
|
|
|
+ if (err) {
|
|
|
+ pr_warn("%s: switch to hs400 failed, err:%d\n",
|
|
|
+ mmc_hostname(host), err);
|
|
|
+ return err;
|
|
|
+ }
|
|
|
+
|
|
|
+ mmc_set_timing(host, MMC_TIMING_MMC_HS400);
|
|
|
+ mmc_set_bus_speed(card);
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
/*
|
|
|
* For device supporting HS200 mode, the following sequence
|
|
|
* should be done before executing the tuning process.
|
|
@@ -1062,13 +1137,22 @@ bus_speed:
|
|
|
|
|
|
/*
|
|
|
* Execute tuning sequence to seek the proper bus operating
|
|
|
- * conditions for HS200, which sends CMD21 to the device.
|
|
|
+ * conditions for HS200 and HS400, which sends CMD21 to the device.
|
|
|
*/
|
|
|
static int mmc_hs200_tuning(struct mmc_card *card)
|
|
|
{
|
|
|
struct mmc_host *host = card->host;
|
|
|
int err = 0;
|
|
|
|
|
|
+ /*
|
|
|
+ * Timing should be adjusted to the HS400 target
|
|
|
+ * operation frequency for tuning process
|
|
|
+ */
|
|
|
+ if (card->mmc_avail_type & EXT_CSD_CARD_TYPE_HS400 &&
|
|
|
+ host->ios.bus_width == MMC_BUS_WIDTH_8)
|
|
|
+ if (host->ops->prepare_hs400_tuning)
|
|
|
+ host->ops->prepare_hs400_tuning(host, &host->ios);
|
|
|
+
|
|
|
if (host->ops->execute_tuning) {
|
|
|
mmc_host_clk_hold(host);
|
|
|
err = host->ops->execute_tuning(host,
|
|
@@ -1296,6 +1380,10 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr,
|
|
|
err = mmc_hs200_tuning(card);
|
|
|
if (err)
|
|
|
goto err;
|
|
|
+
|
|
|
+ err = mmc_select_hs400(card);
|
|
|
+ if (err)
|
|
|
+ goto err;
|
|
|
} else if (mmc_card_hs(card)) {
|
|
|
/* Select the desired bus width optionally */
|
|
|
err = mmc_select_bus_width(card);
|