mmc: core: Add partial initialization support

This change adds the ability to partially initialize
the MMC card by using card Sleep/Awake sequence (CMD5).
Card will be sent to Sleep state during runtime/system suspend
and will be woken up during runtime/system resume.
By using this sequence the card doesn't need full initialization
which gives time reduction in system/runtime resume path.

Change-Id: I0ece06feb8a5c0f4837126f44934d665c55294a2
Signed-off-by: Maya Erez <merez@codeaurora.org>
Signed-off-by: Veerabhadrarao Badiganti <vbadigan@codeaurora.org>
Signed-off-by: Shaik Sajida Bhanu <sbhanu@codeaurora.org>
This commit is contained in:
Maya Erez 2020-06-04 18:59:37 +05:30 • committed by Shaik Sajida Bhanu
commit 87592d6fab
3 changed files with 169 additions and 9 deletions

View file

@ -2111,7 +2111,18 @@ static int mmc_can_sleep(struct mmc_card *card)
return (card && card->ext_csd.rev >= 3);
}
static int mmc_sleep(struct mmc_host *host)
static int mmc_can_sleepawake(struct mmc_host *host)
{
#if defined(CONFIG_SDC_QTI)
return host && (host->caps2 & MMC_CAP2_SLEEP_AWAKE) && host->card &&
(host->card->ext_csd.rev >= 3);
#else
return host && host->card && (host->card->ext_csd.rev >= 3);
#endif
}
static int mmc_sleepawake(struct mmc_host *host, bool sleep)
{
struct mmc_command cmd = {};
struct mmc_card *card = host->card;
@ -2121,13 +2132,16 @@ static int mmc_sleep(struct mmc_host *host)
/* Re-tuning can't be done once the card is deselected */
mmc_retune_hold(host);
err = mmc_deselect_cards(host);
if (err)
goto out_release;
if (sleep) {
err = mmc_deselect_cards(host);
if (err)
goto out_release;
}
cmd.opcode = MMC_SLEEP_AWAKE;
cmd.arg = card->rca << 16;
cmd.arg |= 1 << 15;
if (sleep)
cmd.arg |= 1 << 15;
/*
* If the max_busy_timeout of the host is specified, validate it against
@ -2158,6 +2172,9 @@ static int mmc_sleep(struct mmc_host *host)
if (!cmd.busy_timeout || !(host->caps & MMC_CAP_WAIT_WHILE_BUSY))
mmc_delay(timeout_ms);
if (!sleep)
err = mmc_select_card(card);
out_release:
mmc_retune_release(host);
return err;
@ -2238,6 +2255,71 @@ static void mmc_detect(struct mmc_host *host)
}
}
static int mmc_cache_card_ext_csd(struct mmc_host *host)
{
int err;
u8 *ext_csd;
struct mmc_card *card = host->card;
err = mmc_get_ext_csd(card, &ext_csd);
if (err || !ext_csd) {
pr_err("%s: %s: mmc_get_ext_csd failed (%d)\n",
mmc_hostname(host), __func__, err);
return err;
}
/* only cache read/write fields that the sw changes */
#if defined(CONFIG_SDC_QTI)
card->ext_csd.raw_ext_csd_cmdq = ext_csd[EXT_CSD_CMDQ_MODE_EN];
card->ext_csd.raw_ext_csd_cache_ctrl = ext_csd[EXT_CSD_CACHE_CTRL];
card->ext_csd.raw_ext_csd_bus_width = ext_csd[EXT_CSD_BUS_WIDTH];
card->ext_csd.raw_ext_csd_hs_timing = ext_csd[EXT_CSD_HS_TIMING];
#endif
kfree(ext_csd);
return 0;
}
static int mmc_test_awake_ext_csd(struct mmc_host *host)
{
int err;
u8 *ext_csd;
struct mmc_card *card = host->card;
err = mmc_get_ext_csd(card, &ext_csd);
if (err) {
pr_err("%s: %s: mmc_get_ext_csd failed (%d)\n",
mmc_hostname(host), __func__, err);
return err;
}
/* only compare read/write fields that the sw changes */
#if defined(CONFIG_SDC_QTI)
pr_debug("%s: %s: type(cached:current) cmdq(%d:%d) cache_ctrl(%d:%d) bus_width (%d:%d) timing(%d:%d)\n",
mmc_hostname(host), __func__,
card->ext_csd.raw_ext_csd_cmdq,
ext_csd[EXT_CSD_CMDQ_MODE_EN],
card->ext_csd.raw_ext_csd_cache_ctrl,
ext_csd[EXT_CSD_CACHE_CTRL],
card->ext_csd.raw_ext_csd_bus_width,
ext_csd[EXT_CSD_BUS_WIDTH],
card->ext_csd.raw_ext_csd_hs_timing,
ext_csd[EXT_CSD_HS_TIMING]);
err = !((card->ext_csd.raw_ext_csd_cmdq ==
ext_csd[EXT_CSD_CMDQ_MODE_EN]) &&
(card->ext_csd.raw_ext_csd_cache_ctrl ==
ext_csd[EXT_CSD_CACHE_CTRL]) &&
(card->ext_csd.raw_ext_csd_bus_width ==
ext_csd[EXT_CSD_BUS_WIDTH]) &&
(card->ext_csd.raw_ext_csd_hs_timing ==
ext_csd[EXT_CSD_HS_TIMING]));
#endif
kfree(ext_csd);
return err;
}
static int _mmc_suspend(struct mmc_host *host, bool is_suspend)
{
int err = 0;
@ -2264,8 +2346,14 @@ static int _mmc_suspend(struct mmc_host *host, bool is_suspend)
if (mmc_can_poweroff_notify(host->card) &&
((host->caps2 & MMC_CAP2_FULL_PWR_CYCLE) || !is_suspend))
err = mmc_poweroff_notify(host->card, notify_type);
else if (mmc_can_sleep(host->card))
err = mmc_sleep(host);
if (mmc_can_sleepawake(host)) {
#if defined(CONFIG_SDC_QTI)
memcpy(&host->cached_ios, &host->ios, sizeof(host->cached_ios));
#endif
mmc_cache_card_ext_csd(host);
}
if (mmc_can_sleep(host->card))
err = mmc_sleepawake(host, true);
else if (!mmc_host_is_spi(host))
err = mmc_deselect_cards(host);
@ -2284,6 +2372,52 @@ out:
return err;
}
static int mmc_partial_init(struct mmc_host *host)
{
int err = 0;
struct mmc_card *card = host->card;
#if defined(CONFIG_SDC_QTI)
mmc_set_bus_width(host, host->cached_ios.bus_width);
mmc_set_timing(host, host->cached_ios.timing);
if (host->cached_ios.enhanced_strobe) {
#endif
host->ios.enhanced_strobe = true;
if (host->ops->hs400_enhanced_strobe)
host->ops->hs400_enhanced_strobe(host, &host->ios);
#if defined(CONFIG_SDC_QTI)
}
mmc_set_clock(host, host->cached_ios.clock);
mmc_set_bus_mode(host, host->cached_ios.bus_mode);
#endif
if (!mmc_card_hs400es(card) &&
(mmc_card_hs200(card) || mmc_card_hs400(card))) {
err = mmc_execute_tuning(card);
if (err) {
pr_err("%s: %s: Tuning failed (%d)\n",
mmc_hostname(host), __func__, err);
goto out;
}
}
/*
* The ext_csd is read to make sure the card did not went through
* Power-failure during sleep period.
* A subset of the W/E_P, W/C_P register will be tested. In case
* these registers values are different from the values that were
* cached during suspend, we will conclude that a Power-failure occurred
* and will do full initialization sequence.
*/
err = mmc_test_awake_ext_csd(host);
if (err) {
pr_debug("%s: %s: fail on ext_csd read (%d)\n",
mmc_hostname(host), __func__, err);
goto out;
}
out:
return err;
}
/*
* Suspend callback
*/
@ -2306,7 +2440,7 @@ static int mmc_suspend(struct mmc_host *host)
*/
static int _mmc_resume(struct mmc_host *host)
{
int err = 0;
int err = -EINVAL;
mmc_claim_host(host);
@ -2323,7 +2457,19 @@ static int _mmc_resume(struct mmc_host *host)
mmc_log_string(host, "Enter\n");
mmc_power_up(host, host->card->ocr);
err = mmc_init_card(host, host->card->ocr, host->card);
if (mmc_can_sleepawake(host)) {
err = mmc_sleepawake(host, false);
if (!err)
err = mmc_partial_init(host);
else
pr_err("%s: %s: awake failed (%d), fallback to full init\n",
mmc_hostname(host), __func__, err);
}
if (err)
err = mmc_init_card(host, host->card->ocr, host->card);
mmc_card_clr_suspended(host->card);
#if !defined(CONFIG_SDC_QTI)

View file

@ -85,6 +85,10 @@ struct mmc_ext_csd {
unsigned int data_tag_unit_size; /* DATA TAG UNIT size */
unsigned int boot_ro_lock; /* ro lock support */
bool boot_ro_lockable;
#if defined(CONFIG_SDC_QTI)
u8 raw_ext_csd_cmdq; /* 15 */
u8 raw_ext_csd_cache_ctrl; /* 33 */
#endif
bool ffu_capable; /* Firmware upgrade support */
bool cmdq_en; /* Command Queue enabled */
bool cmdq_support; /* Command Queue supported */
@ -95,7 +99,13 @@ struct mmc_ext_csd {
u8 raw_partition_support; /* 160 */
u8 raw_rpmb_size_mult; /* 168 */
u8 raw_erased_mem_count; /* 181 */
#if defined(CONFIG_SDC_QTI)
u8 raw_ext_csd_bus_width; /* 183 */
#endif
u8 strobe_support; /* 184 */
#if defined(CONFIG_SDC_QTI)
u8 raw_ext_csd_hs_timing; /* 185 */
#endif
u8 raw_ext_csd_structure; /* 194 */
u8 raw_card_type; /* 196 */
u8 raw_driver_strength; /* 197 */

View file

@ -479,6 +479,7 @@ struct mmc_host {
#define MMC_CAP2_CRYPTO (1 << 27) /* Host supports inline encryption */
#if defined(CONFIG_SDC_QTI)
#define MMC_CAP2_CLK_SCALE (1 << 28) /* Allow dynamic clk scaling */
#define MMC_CAP2_SLEEP_AWAKE (1 << 29) /* Use Sleep/Awake (CMD5) */
#endif
int fixed_drv_type; /* fixed driver type for non-removable media */
@ -498,6 +499,9 @@ struct mmc_host {
spinlock_t lock; /* lock for claim and bus ops */
struct mmc_ios ios; /* current io bus settings */
#if defined(CONFIG_SDC_QTI)
struct mmc_ios cached_ios;
#endif
/* group bitfields together to minimize padding */
unsigned int use_spi_crc:1;