diff options
author | Shawn Lin <shawn.lin@rock-chips.com> | 2016-04-27 10:53:33 +0800 |
---|---|---|
committer | Huang, Tao <huangtao@rock-chips.com> | 2016-04-28 11:49:40 +0800 |
commit | 87145991406facd537f57c9e6bf0809ceddda851 (patch) | |
tree | 99dcf3b851e3f445812084c43fdf4f22f799a90c /drivers/mmc/core | |
parent | ddfc5a9bcccd6c48d2373f6abc31846d89513e15 (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.c | 3 | ||||
-rw-r--r-- | drivers/mmc/core/host.c | 2 | ||||
-rw-r--r-- | drivers/mmc/core/mmc.c | 85 |
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; |