mmc: core: Add support for HS400
HS400 is a new bus speed mode introduced in eMMC5.0 specification. This
patch adds the capability in mmc core to switch to HS400 mode when both
the host and the card supports it.
Signed-off-by: Venkat Gopalakrishnan <venkatg@codeaurora.org>
diff --git a/drivers/mmc/core/bus.c b/drivers/mmc/core/bus.c
index b140510..58ba933 100644
--- a/drivers/mmc/core/bus.c
+++ b/drivers/mmc/core/bus.c
@@ -397,10 +397,11 @@
mmc_card_ddr_mode(card) ? "DDR " : "",
type);
} else {
- pr_info("%s: new %s%s%s%s%s card at address %04x\n",
+ pr_info("%s: new %s%s%s%s%s%s card at address %04x\n",
mmc_hostname(card->host),
mmc_card_uhs(card) ? "ultra high speed " :
(mmc_card_highspeed(card) ? "high speed " : ""),
+ (mmc_card_hs400(card) ? "HS400 " : ""),
(mmc_card_hs200(card) ? "HS200 " : ""),
mmc_card_ddr_mode(card) ? "DDR " : "",
uhs_bus_speed_mode, type, card->rca);
diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c
index bc5dd4d..4fb0675 100644
--- a/drivers/mmc/core/mmc.c
+++ b/drivers/mmc/core/mmc.c
@@ -263,6 +263,12 @@
card_type & EXT_CSD_CARD_TYPE_SDR_1_2V))
hs_max_dtr = MMC_HS200_MAX_DTR;
+ if ((caps2 & MMC_CAP2_HS400_1_8V &&
+ card_type & EXT_CSD_CARD_TYPE_HS400_1_8V) ||
+ (caps2 & MMC_CAP2_HS400_1_2V &&
+ card_type & EXT_CSD_CARD_TYPE_HS400_1_2V))
+ hs_max_dtr = MMC_HS400_MAX_DTR;
+
card->ext_csd.hs_max_dtr = hs_max_dtr;
card->ext_csd.card_type = card_type;
}
@@ -719,7 +725,9 @@
EXT_CSD_PWR_CL_52_195 :
EXT_CSD_PWR_CL_DDR_52_195;
else if (host->ios.clock <= 200000000)
- index = EXT_CSD_PWR_CL_200_195;
+ index = (bus_width <= EXT_CSD_BUS_WIDTH_8) ?
+ EXT_CSD_PWR_CL_200_195 :
+ EXT_CSD_PWR_CL_DDR_200_195;
break;
case MMC_VDD_27_28:
case MMC_VDD_28_29:
@@ -737,7 +745,9 @@
EXT_CSD_PWR_CL_52_360 :
EXT_CSD_PWR_CL_DDR_52_360;
else if (host->ios.clock <= 200000000)
- index = EXT_CSD_PWR_CL_200_360;
+ index = (bus_width <= EXT_CSD_BUS_WIDTH_8) ?
+ EXT_CSD_PWR_CL_200_360 :
+ EXT_CSD_PWR_CL_DDR_200_360;
break;
default:
pr_warning("%s: Voltage range not supported "
@@ -871,7 +881,7 @@
mmc_card_set_highspeed(card);
mmc_set_timing(host, MMC_TIMING_MMC_HS);
- mmc_set_clock(host, card->ext_csd.hs_max_dtr);
+ mmc_set_clock(host, MMC_HIGH_52_MAX_DTR);
err = mmc_select_bus_width(card, 0, ext_csd);
@@ -994,8 +1004,11 @@
* 4. execute tuning for HS200
*/
err = mmc_select_bus_width(card, 0, ext_csd);
- if (err)
+ if (err) {
+ pr_err("%s: select bus width failed\n",
+ mmc_hostname(host));
goto out;
+ }
if (host->ios.bus_width == MMC_BUS_WIDTH_1) {
pr_err("%s: failed to switch to wide bus\n",
@@ -1007,11 +1020,22 @@
err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
EXT_CSD_HS_TIMING, 2, 0);
- if (err && err != -EBADMSG)
+ if (err && err != -EBADMSG) {
+ pr_err("%s: HS200 switch failed\n",
+ mmc_hostname(host));
goto out;
+ }
- mmc_set_timing(host, MMC_TIMING_MMC_HS200);
- mmc_set_clock(host, card->ext_csd.hs_max_dtr);
+ /*
+ * When HS200 activation is performed as part of HS400 selection
+ * set the timing appropriately
+ */
+ if (mmc_card_hs400(card))
+ mmc_set_timing(host, MMC_TIMING_MMC_HS400);
+ else
+ mmc_set_timing(host, MMC_TIMING_MMC_HS200);
+
+ mmc_set_clock(host, MMC_HS200_MAX_DTR);
if (host->ops->execute_tuning) {
mmc_host_clk_hold(host);
@@ -1033,6 +1057,107 @@
return err;
}
+static int mmc_select_hs400(struct mmc_card *card, u8 *ext_csd)
+{
+ int err = 0;
+ struct mmc_host *host;
+
+ host = card->host;
+
+ if (!(host->caps2 & MMC_CAP2_HS400) ||
+ !(card->ext_csd.card_type & EXT_CSD_CARD_TYPE_HS400)) {
+ err = -EOPNOTSUPP;
+ goto out;
+ }
+
+ /*
+ * eMMC5.0 spec doesn't allow switching to HS400 mode from
+ * HS200 mode directly. Hence follow these steps to switch
+ * to HS400 mode:
+ * Enable HS200 mode
+ * Enable HighSpeed mode (The clk should be low enough
+ * to enable HighSpeed mode) - HS_TIMING is 0x1
+ * Enable DDR mode (Set bus width to 8-bit DDR)
+ * Enable HS400 mode (Set HS_TIMING to 0x3 and change
+ * frequency to <= 200MHz)
+ * Perform tuning if required
+ */
+ mmc_card_set_hs400(card);
+ err = mmc_select_hs200(card, ext_csd);
+ if (err)
+ goto out;
+ mmc_card_clr_hs200(card);
+
+ if ((card->ext_csd.card_type & EXT_CSD_CARD_TYPE_HS400_1_2V)
+ && (host->caps2 & MMC_CAP2_HS400_1_2V))
+ if (mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_120, 0))
+ err = mmc_set_signal_voltage(host,
+ MMC_SIGNAL_VOLTAGE_180, 0);
+ /* If fails try again during next card power cycle */
+ if (err)
+ goto out;
+
+ /*
+ * Lower the clock and adjust the timing to be able
+ * to switch to HighSpeed mode
+ */
+ mmc_set_timing(host, MMC_TIMING_LEGACY);
+ mmc_set_clock(host, MMC_HIGH_26_MAX_DTR);
+
+ err = mmc_select_hs(card, ext_csd);
+ if (err)
+ goto out;
+ mmc_card_clr_highspeed(card);
+
+ /* Switch to 8-bit DDR mode */
+ err = mmc_select_hsddr(card, ext_csd);
+ if (err)
+ goto out;
+ mmc_card_clr_ddr_mode(card);
+
+ /*
+ * In HS400 mode only DDR 8-bit bus width is allowed.
+ */
+ if (host->ios.bus_width != MMC_BUS_WIDTH_8) {
+ pr_err("%s: failed to switch to 8-bit bus width\n",
+ mmc_hostname(host));
+ goto out;
+ }
+
+ /* Switch to HS400 mode if bus width set successfully */
+ err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
+ EXT_CSD_HS_TIMING, 3, 0);
+ if (err && err != -EBADMSG) {
+ pr_err("%s: Setting HS_TIMING to HS400 failed (err:%d)\n",
+ mmc_hostname(host), err);
+ goto out;
+ }
+
+ mmc_set_timing(host, MMC_TIMING_MMC_HS400);
+ mmc_set_clock(host, MMC_HS400_MAX_DTR);
+
+ if (host->ops->execute_tuning) {
+ mmc_host_clk_hold(host);
+ err = host->ops->execute_tuning(host,
+ MMC_SEND_TUNING_BLOCK_HS400);
+ mmc_host_clk_release(host);
+ }
+ if (err) {
+ pr_err("%s: tuning execution failed (err:%d)\n",
+ mmc_hostname(host), err);
+ goto out;
+ }
+ mmc_card_set_hs400(card);
+
+out:
+ if (err && err != -EOPNOTSUPP) {
+ pr_warning("%s: Switch to HS400 mode failed (err:%d)\n",
+ mmc_hostname(host), err);
+ mmc_card_clr_hs400(card);
+ }
+ return err;
+}
+
/**
* mmc_change_bus_speed() - Change MMC card bus frequency at runtime
* @host: pointer to mmc host structure
@@ -1064,7 +1189,8 @@
}
if (mmc_card_highspeed(card) || mmc_card_hs200(card)
- || mmc_card_ddr_mode(card)) {
+ || mmc_card_ddr_mode(card)
+ || mmc_card_hs400(card)) {
if (*freq > card->ext_csd.hs_max_dtr)
*freq = card->ext_csd.hs_max_dtr;
} else if (*freq > card->csd.max_dtr) {
@@ -1076,7 +1202,8 @@
mmc_set_clock(host, (unsigned int) (*freq));
- if (mmc_card_hs200(card) && card->host->ops->execute_tuning) {
+ if ((mmc_card_hs400(card) || mmc_card_hs200(card))
+ && card->host->ops->execute_tuning) {
/*
* We try to probe host driver for tuning for any
* frequency, it is host driver responsibility to
@@ -1123,6 +1250,8 @@
BUG_ON(!card);
+ if (!mmc_select_hs400(card, ext_csd))
+ goto out;
if (!mmc_select_hs200(card, ext_csd))
goto out;
if (!mmc_select_hsddr(card, ext_csd))
diff --git a/include/linux/mmc/card.h b/include/linux/mmc/card.h
index 335ca93..7852423 100644
--- a/include/linux/mmc/card.h
+++ b/include/linux/mmc/card.h
@@ -67,6 +67,7 @@
#define MMC_HIGH_52_MAX_DTR 52000000
#define MMC_HIGH_DDR_MAX_DTR 52000000
#define MMC_HS200_MAX_DTR 200000000
+#define MMC_HS400_MAX_DTR 200000000
unsigned int sectors;
unsigned int card_type;
unsigned int hc_erase_size; /* In sectors */
@@ -329,6 +330,7 @@
#define MMC_CARD_SDXC (1<<6) /* card is SDXC */
#define MMC_CARD_REMOVED (1<<7) /* card has been removed */
#define MMC_STATE_HIGHSPEED_200 (1<<8) /* card is in HS200 mode */
+#define MMC_STATE_HIGHSPEED_400 (1<<9) /* card is in HS400 mode */
#define MMC_STATE_DOING_BKOPS (1<<10) /* card is doing BKOPS */
#define MMC_STATE_NEED_BKOPS (1<<11) /* card needs to do BKOPS */
unsigned int quirks; /* card quirks */
@@ -502,6 +504,7 @@
#define mmc_card_readonly(c) ((c)->state & MMC_STATE_READONLY)
#define mmc_card_highspeed(c) ((c)->state & MMC_STATE_HIGHSPEED)
#define mmc_card_hs200(c) ((c)->state & MMC_STATE_HIGHSPEED_200)
+#define mmc_card_hs400(c) ((c)->state & MMC_STATE_HIGHSPEED_400)
#define mmc_card_blockaddr(c) ((c)->state & MMC_STATE_BLOCKADDR)
#define mmc_card_ddr_mode(c) ((c)->state & MMC_STATE_HIGHSPEED_DDR)
#define mmc_card_uhs(c) ((c)->state & MMC_STATE_ULTRAHIGHSPEED)
@@ -517,8 +520,11 @@
#define mmc_card_clr_highspeed(c) ((c)->state &= ~MMC_STATE_HIGHSPEED)
#define mmc_card_set_hs200(c) ((c)->state |= MMC_STATE_HIGHSPEED_200)
#define mmc_card_clr_hs200(c) ((c)->state &= ~MMC_STATE_HIGHSPEED_200)
+#define mmc_card_set_hs400(c) ((c)->state |= MMC_STATE_HIGHSPEED_400)
+#define mmc_card_clr_hs400(c) ((c)->state &= ~MMC_STATE_HIGHSPEED_400)
#define mmc_card_set_blockaddr(c) ((c)->state |= MMC_STATE_BLOCKADDR)
#define mmc_card_set_ddr_mode(c) ((c)->state |= MMC_STATE_HIGHSPEED_DDR)
+#define mmc_card_clr_ddr_mode(c) ((c)->state &= ~MMC_STATE_HIGHSPEED_DDR)
#define mmc_card_set_uhs(c) ((c)->state |= MMC_STATE_ULTRAHIGHSPEED)
#define mmc_sd_card_set_uhs(c) ((c)->state |= MMC_STATE_ULTRAHIGHSPEED)
#define mmc_card_set_ext_capacity(c) ((c)->state |= MMC_CARD_SDXC)
diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h
index 5796b9d..e1dbd21 100644
--- a/include/linux/mmc/host.h
+++ b/include/linux/mmc/host.h
@@ -61,6 +61,7 @@
#define MMC_TIMING_UHS_SDR104 4
#define MMC_TIMING_UHS_DDR50 5
#define MMC_TIMING_MMC_HS200 6
+#define MMC_TIMING_MMC_HS400 7
#define MMC_SDR_MODE 0
#define MMC_1_2V_DDR_MODE 1
@@ -296,6 +297,11 @@
#define MMC_CAP2_CORE_RUNTIME_PM (1 << 19)
/* Allows Asynchronous SDIO irq while card is in 4-bit mode */
#define MMC_CAP2_ASYNC_SDIO_IRQ_4BIT_MODE (1 << 20)
+
+#define MMC_CAP2_HS400_1_8V (1 << 21) /* can support */
+#define MMC_CAP2_HS400_1_2V (1 << 22) /* can support */
+#define MMC_CAP2_HS400 (MMC_CAP2_HS400_1_8V | \
+ MMC_CAP2_HS400_1_2V)
mmc_pm_flag_t pm_caps; /* supported pm features */
int clk_requests; /* internal reference counter */
diff --git a/include/linux/mmc/mmc.h b/include/linux/mmc/mmc.h
index 292c47e..764beec 100644
--- a/include/linux/mmc/mmc.h
+++ b/include/linux/mmc/mmc.h
@@ -52,6 +52,7 @@
#define MMC_READ_MULTIPLE_BLOCK 18 /* adtc [31:0] data addr R1 */
#define MMC_SEND_TUNING_BLOCK 19 /* adtc R1 */
#define MMC_SEND_TUNING_BLOCK_HS200 21 /* adtc R1 */
+#define MMC_SEND_TUNING_BLOCK_HS400 MMC_SEND_TUNING_BLOCK_HS200
/* class 3 */
#define MMC_WRITE_DAT_UNTIL_STOP 20 /* adtc [31:0] data addr R1 */
@@ -328,6 +329,8 @@
#define EXT_CSD_POWER_OFF_LONG_TIME 247 /* RO */
#define EXT_CSD_GENERIC_CMD6_TIME 248 /* RO */
#define EXT_CSD_CACHE_SIZE 249 /* RO, 4 bytes */
+#define EXT_CSD_PWR_CL_DDR_200_195 253 /* RO */
+#define EXT_CSD_PWR_CL_DDR_200_360 254 /* RO */
#define EXT_CSD_TAG_UNIT_SIZE 498 /* RO */
#define EXT_CSD_DATA_TAG_SUPPORT 499 /* RO */
#define EXT_CSD_MAX_PACKED_WRITES 500 /* RO */
@@ -359,7 +362,7 @@
#define EXT_CSD_CARD_TYPE_26 (1<<0) /* Card can run at 26MHz */
#define EXT_CSD_CARD_TYPE_52 (1<<1) /* Card can run at 52MHz */
-#define EXT_CSD_CARD_TYPE_MASK 0x3F /* Mask out reserved bits */
+#define EXT_CSD_CARD_TYPE_MASK 0xFF /* Mask out reserved bits */
#define EXT_CSD_CARD_TYPE_DDR_1_8V (1<<2) /* Card can run at 52MHz */
/* DDR mode @1.8V or 3V I/O */
#define EXT_CSD_CARD_TYPE_DDR_1_2V (1<<3) /* Card can run at 52MHz */
@@ -371,6 +374,12 @@
/* SDR mode @1.2V I/O */
#define EXT_CSD_CARD_TYPE_HS200 (EXT_CSD_CARD_TYPE_SDR_1_8V \
| EXT_CSD_CARD_TYPE_SDR_1_2V)
+#define EXT_CSD_CARD_TYPE_HS400_1_8V (1<<6) /* Card can run at 200MHz */
+ /* DDR mode @1.8V I/O */
+#define EXT_CSD_CARD_TYPE_HS400_1_2V (1<<7) /* Card can run at 200MHz */
+ /* DDR mode @1.2V I/O */
+#define EXT_CSD_CARD_TYPE_HS400 (EXT_CSD_CARD_TYPE_HS400_1_8V \
+ | EXT_CSD_CARD_TYPE_HS400_1_2V)
#define EXT_CSD_BUS_WIDTH_1 0 /* Card is in 1 bit mode */
#define EXT_CSD_BUS_WIDTH_4 1 /* Card is in 4 bit mode */