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: Id8dadf03ef4226f7c4675fadbacac7bb15c0289e Signed-off-by: Talel Shenhar <tatias@codeaurora.org> Signed-off-by: Maya Erez <merez@codeaurora.org> [subhashj@codeaurora.org: fixed merge conflicts & compilation errors] Signed-off-by: Subhash Jadavani <subhashj@codeaurora.org>
This commit is contained in:
parent
17a072dd25
commit
21a32a7af8
3 changed files with 169 additions and 13 deletions
|
@ -1542,7 +1542,7 @@ static int mmc_select_cmdq(struct mmc_card *card)
|
|||
}
|
||||
|
||||
mmc_host_clk_release(card->host);
|
||||
pr_info("%s: CMDQ enabled on card\n", mmc_hostname(host));
|
||||
pr_info_once("%s: CMDQ enabled on card\n", mmc_hostname(host));
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
@ -2181,12 +2181,13 @@ err:
|
|||
return err;
|
||||
}
|
||||
|
||||
static int mmc_can_sleep(struct mmc_card *card)
|
||||
static int mmc_can_sleepawake(struct mmc_host *host)
|
||||
{
|
||||
return (card && card->ext_csd.rev >= 3);
|
||||
return host && (host->caps2 & MMC_CAP2_SLEEP_AWAKE) && host->card &&
|
||||
(host->card->ext_csd.rev >= 3);
|
||||
}
|
||||
|
||||
static int mmc_sleep(struct mmc_host *host)
|
||||
static int mmc_sleepawake(struct mmc_host *host, bool sleep)
|
||||
{
|
||||
struct mmc_command cmd = {0};
|
||||
struct mmc_card *card = host->card;
|
||||
|
@ -2196,13 +2197,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
|
||||
|
@ -2230,6 +2234,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;
|
||||
|
@ -2340,6 +2347,69 @@ 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 */
|
||||
card->ext_csd.raw_ext_csd_cmdq = ext_csd[EXT_CSD_CMDQ];
|
||||
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];
|
||||
|
||||
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 */
|
||||
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],
|
||||
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]) &&
|
||||
(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]));
|
||||
|
||||
kfree(ext_csd);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int _mmc_suspend(struct mmc_host *host, bool is_suspend)
|
||||
{
|
||||
int err = 0;
|
||||
|
@ -2391,10 +2461,13 @@ static int _mmc_suspend(struct mmc_host *host, bool is_suspend)
|
|||
if (err)
|
||||
goto out;
|
||||
|
||||
if (mmc_can_sleep(host->card))
|
||||
err = mmc_sleep(host);
|
||||
else if (!mmc_host_is_spi(host))
|
||||
if (mmc_can_sleepawake(host)) {
|
||||
memcpy(&host->cached_ios, &host->ios, sizeof(host->cached_ios));
|
||||
mmc_cache_card_ext_csd(host);
|
||||
err = mmc_sleepawake(host, true);
|
||||
} else if (!mmc_host_is_spi(host)) {
|
||||
err = mmc_deselect_cards(host);
|
||||
}
|
||||
|
||||
if (!err) {
|
||||
mmc_power_off(host);
|
||||
|
@ -2409,6 +2482,71 @@ out:
|
|||
return err;
|
||||
}
|
||||
|
||||
static int mmc_partial_init(struct mmc_host *host)
|
||||
{
|
||||
int err = 0;
|
||||
struct mmc_card *card = host->card;
|
||||
|
||||
pr_debug("%s: %s: starting partial init\n",
|
||||
mmc_hostname(host), __func__);
|
||||
|
||||
mmc_set_bus_width(host, host->cached_ios.bus_width);
|
||||
mmc_set_timing(host, host->cached_ios.timing);
|
||||
mmc_set_clock(host, host->cached_ios.clock);
|
||||
mmc_set_bus_mode(host, host->cached_ios.bus_mode);
|
||||
|
||||
mmc_host_clk_hold(host);
|
||||
|
||||
if (mmc_card_hs200(card) || mmc_card_hs400(card)) {
|
||||
if (card->ext_csd.strobe_support && host->ops->enhanced_strobe)
|
||||
err = host->ops->enhanced_strobe(host);
|
||||
else
|
||||
err = host->ops->execute_tuning(host,
|
||||
MMC_SEND_TUNING_BLOCK_HS200);
|
||||
if (err)
|
||||
pr_warn("%s: %s: tuning execution failed (%d)\n",
|
||||
mmc_hostname(host), __func__, err);
|
||||
}
|
||||
|
||||
/*
|
||||
* 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.
|
||||
* In addition, full init sequence also transfer ext_csd before moving
|
||||
* to CMDQ mode which has a side affect of configuring SDHCI registers
|
||||
* which needed to be done before moving to CMDQ mode. The same
|
||||
* registers need to be configured for partial init.
|
||||
*/
|
||||
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;
|
||||
}
|
||||
pr_debug("%s: %s: reading and comparing ext_csd successful\n",
|
||||
mmc_hostname(host), __func__);
|
||||
|
||||
if (card->ext_csd.cmdq_support && (card->host->caps2 &
|
||||
MMC_CAP2_CMD_QUEUE)) {
|
||||
err = mmc_select_cmdq(card);
|
||||
if (err) {
|
||||
pr_warn("%s: %s: enabling CMDQ mode failed (%d)\n",
|
||||
mmc_hostname(card->host),
|
||||
__func__, err);
|
||||
}
|
||||
}
|
||||
out:
|
||||
mmc_host_clk_release(host);
|
||||
|
||||
pr_debug("%s: %s: done partial init (%d)\n",
|
||||
mmc_hostname(host), __func__, err);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
/*
|
||||
* Suspend callback
|
||||
*/
|
||||
|
@ -2434,7 +2572,7 @@ static int mmc_suspend(struct mmc_host *host)
|
|||
*/
|
||||
static int _mmc_resume(struct mmc_host *host)
|
||||
{
|
||||
int err = 0;
|
||||
int err = -ENOSYS;
|
||||
int retries;
|
||||
|
||||
BUG_ON(!host);
|
||||
|
@ -2450,7 +2588,18 @@ static int _mmc_resume(struct mmc_host *host)
|
|||
mmc_power_up(host, host->card->ocr);
|
||||
retries = 3;
|
||||
while (retries) {
|
||||
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);
|
||||
if (err)
|
||||
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);
|
||||
|
||||
if (err) {
|
||||
pr_err("%s: MMC card re-init failed rc = %d (retries = %d)\n",
|
||||
mmc_hostname(host), err, retries);
|
||||
|
|
|
@ -93,6 +93,8 @@ 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;
|
||||
u8 raw_ext_csd_cmdq; /* 15 */
|
||||
u8 raw_ext_csd_cache_ctrl; /* 33 */
|
||||
bool ffu_capable; /* Firmware upgrade support */
|
||||
#define MMC_FIRMWARE_LEN 8
|
||||
u8 fwrev[MMC_FIRMWARE_LEN]; /* FW version */
|
||||
|
@ -100,8 +102,10 @@ struct mmc_ext_csd {
|
|||
u8 raw_partition_support; /* 160 */
|
||||
u8 raw_rpmb_size_mult; /* 168 */
|
||||
u8 raw_erased_mem_count; /* 181 */
|
||||
u8 raw_ext_csd_bus_width; /* 183 */
|
||||
u8 strobe_support; /* 184 */
|
||||
#define MMC_STROBE_SUPPORT (1 << 0)
|
||||
u8 raw_ext_csd_hs_timing; /* 185 */
|
||||
u8 raw_ext_csd_structure; /* 194 */
|
||||
u8 raw_card_type; /* 196 */
|
||||
u8 raw_driver_strength; /* 197 */
|
||||
|
|
|
@ -441,6 +441,8 @@ struct mmc_host {
|
|||
#define MMC_CAP2_NONHOTPLUG (1 << 25) /*Don't support hotplug*/
|
||||
#define MMC_CAP2_CMD_QUEUE (1 << 26) /* support eMMC command queue */
|
||||
#define MMC_CAP2_SANITIZE (1 << 27) /* Support Sanitize */
|
||||
#define MMC_CAP2_SLEEP_AWAKE (1 << 28) /* Use Sleep/Awake (CMD5) */
|
||||
|
||||
mmc_pm_flag_t pm_caps; /* supported pm features */
|
||||
|
||||
#ifdef CONFIG_MMC_CLKGATE
|
||||
|
@ -468,6 +470,7 @@ struct mmc_host {
|
|||
spinlock_t lock; /* lock for claim and bus ops */
|
||||
|
||||
struct mmc_ios ios; /* current io bus settings */
|
||||
struct mmc_ios cached_ios;
|
||||
|
||||
/* group bitfields together to minimize padding */
|
||||
unsigned int use_spi_crc:1;
|
||||
|
|
Loading…
Add table
Reference in a new issue