summaryrefslogtreecommitdiff
path: root/drivers/mmc/core
diff options
context:
space:
mode:
authorShawn Lin <shawn.lin@rock-chips.com>2016-04-27 10:53:33 +0800
committerHuang, Tao <huangtao@rock-chips.com>2016-04-28 11:49:40 +0800
commit87145991406facd537f57c9e6bf0809ceddda851 (patch)
tree99dcf3b851e3f445812084c43fdf4f22f799a90c /drivers/mmc/core
parentddfc5a9bcccd6c48d2373f6abc31846d89513e15 (diff)
mmc: add hs400 enhanced strobe support for mmc subsystem
HS400 enhanced strobe is a new feature introduced by eMMC spec 5.1, let's implement it and enjoy it! please note that currently I have no much bandwith to split this big patch into patchset. So please use, test and applied! Thanks. Change-Id: I874f18a617a1b69e3ff56f5c134feb817b6985b9 Signed-off-by: Shawn Lin <shawn.lin@rock-chips.com>
Diffstat (limited to 'drivers/mmc/core')
-rw-r--r--drivers/mmc/core/bus.c3
-rw-r--r--drivers/mmc/core/host.c2
-rw-r--r--drivers/mmc/core/mmc.c85
3 files changed, 82 insertions, 8 deletions
diff --git a/drivers/mmc/core/bus.c b/drivers/mmc/core/bus.c
index 972ff844cf5a..c40d700d424d 100644
--- a/drivers/mmc/core/bus.c
+++ b/drivers/mmc/core/bus.c
@@ -332,12 +332,13 @@ int mmc_add_card(struct mmc_card *card)
mmc_card_ddr52(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_hs(card) ? "high speed " : ""),
mmc_card_hs400(card) ? "HS400 " :
(mmc_card_hs200(card) ? "HS200 " : ""),
+ mmc_card_hs400es(card) ? "Enhanced strobe " : "",
mmc_card_ddr52(card) ? "DDR " : "",
uhs_bus_speed_mode, type, card->rca);
}
diff --git a/drivers/mmc/core/host.c b/drivers/mmc/core/host.c
index 871989e7c228..71bb2372f71d 100644
--- a/drivers/mmc/core/host.c
+++ b/drivers/mmc/core/host.c
@@ -289,6 +289,8 @@ int mmc_of_parse(struct mmc_host *host)
host->caps2 |= MMC_CAP2_HS400_1_8V | MMC_CAP2_HS200_1_8V_SDR;
if (of_property_read_bool(np, "mmc-hs400-1_2v"))
host->caps2 |= MMC_CAP2_HS400_1_2V | MMC_CAP2_HS200_1_2V_SDR;
+ if (of_property_read_bool(np, "mmc-hs400-enhanced-strobe"))
+ host->caps2 |= MMC_CAP2_HS400_ENHANCED_STROBE;
if (of_property_read_bool(np, "supports-sd"))
host->restrict_caps |= RESTRICT_CARD_TYPE_SD;
diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c
index 3d5087b03999..94bde1d5dae8 100644
--- a/drivers/mmc/core/mmc.c
+++ b/drivers/mmc/core/mmc.c
@@ -235,6 +235,12 @@ static void mmc_select_card_type(struct mmc_card *card)
avail_type |= EXT_CSD_CARD_TYPE_HS400_1_2V;
}
+ if ((caps2 & MMC_CAP2_HS400_ENHANCED_STROBE) &&
+ card->ext_csd.strobe_support &&
+ ((card_type & EXT_CSD_CARD_TYPE_HS400_1_2V) ||
+ (card_type & EXT_CSD_CARD_TYPE_HS400_1_8V)))
+ 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;
@@ -383,6 +389,13 @@ static int mmc_decode_ext_csd(struct mmc_card *card, u8 *ext_csd)
mmc_card_set_blockaddr(card);
}
+ /*
+ * Enhance Strobe is supported since v5.1 which rev should be
+ * 8 but some eMMC devices can support it with rev 7. So handle
+ * Enhance Strobe here.
+ */
+ 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);
@@ -1062,10 +1075,11 @@ static int mmc_select_hs400(struct mmc_card *card)
u8 val;
/*
- * HS400 mode requires 8-bit bus width
+ * HS400(ES) mode requires 8-bit bus width
*/
- if (!(card->mmc_avail_type & EXT_CSD_CARD_TYPE_HS400 &&
- host->ios.bus_width == MMC_BUS_WIDTH_8))
+ if (!(((card->mmc_avail_type & EXT_CSD_CARD_TYPE_HS400) ||
+ (card->mmc_avail_type & EXT_CSD_CARD_TYPE_HS400ES)) &&
+ host->ios.bus_width == MMC_BUS_WIDTH_8))
return 0;
if (host->caps & MMC_CAP_WAIT_WHILE_BUSY)
@@ -1097,9 +1111,26 @@ static int mmc_select_hs400(struct mmc_card *card)
}
/* Switch card to DDR */
+ val = EXT_CSD_DDR_BUS_WIDTH_8;
+ if (card->mmc_avail_type & EXT_CSD_CARD_TYPE_HS400ES) {
+ val |= EXT_CSD_BUS_WIDTH_STROBE;
+ /*
+ * Make sure we are in non-enhanced strobe mode before we
+ * actually enable it in ext_csd.
+ */
+ if (host->ops->prepare_enhanced_strobe)
+ err = host->ops->prepare_enhanced_strobe(host, false);
+
+ if (err) {
+ pr_err("%s: unprepare_enhanced strobe 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,
+ val,
card->ext_csd.generic_cmd6_time);
if (err) {
pr_err("%s: switch to bus width for hs400 failed, err:%d\n",
@@ -1124,6 +1155,35 @@ static int mmc_select_hs400(struct mmc_card *card)
mmc_set_timing(host, MMC_TIMING_MMC_HS400);
mmc_set_bus_speed(card);
+ if (card->mmc_avail_type & EXT_CSD_CARD_TYPE_HS400ES) {
+ /* Controller enable enhanced strobe function */
+ if (host->ops->prepare_enhanced_strobe)
+ err = host->ops->prepare_enhanced_strobe(host, true);
+
+ if (err) {
+ pr_err("%s: prepare enhanced strobe failed, err:%d\n",
+ mmc_hostname(host), err);
+ return err;
+ }
+
+ /*
+ * After switching to hs400 enhanced strobe mode, we expect to
+ * verify whether it works or not. If controller can't handle
+ * bus width test, compare ext_csd previously read in 1 bit mode
+ * against ext_csd at new bus width
+ */
+ if (!(host->caps & MMC_CAP_BUS_WIDTH_TEST))
+ err = mmc_compare_ext_csds(card, MMC_BUS_WIDTH_8);
+ else
+ err = mmc_bus_test(card, MMC_BUS_WIDTH_8);
+
+ if (err) {
+ pr_warn("%s: switch to enhanced strobe failed\n",
+ mmc_hostname(host));
+ return err;
+ }
+ }
+
if (!send_status) {
err = mmc_switch_status(card);
if (err)
@@ -1297,7 +1357,8 @@ err:
}
/*
- * Activate High Speed or HS200 mode if supported.
+ * Activate High Speed or HS200 mode if supported. For HS400
+ * with enhanced strobe mode, we should activate High Speed.
*/
static int mmc_select_timing(struct mmc_card *card)
{
@@ -1306,7 +1367,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_hs(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);
@@ -1577,7 +1640,15 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr,
} else if (mmc_card_hs(card)) {
/* Select the desired bus width optionally */
err = mmc_select_bus_width(card);
- if (!IS_ERR_VALUE(err)) {
+ if (IS_ERR_VALUE(err))
+ goto free_card;
+
+ if (card->mmc_avail_type & EXT_CSD_CARD_TYPE_HS400ES) {
+ /* Directly from HS to HS400-ES */
+ err = mmc_select_hs400(card);
+ if (err)
+ goto free_card;
+ } else {
err = mmc_select_hs_ddr(card);
if (err)
goto free_card;