diff options
author | Ulf Hansson <ulf.hansson@linaro.org> | 2012-10-05 12:45:39 -0400 |
---|---|---|
committer | Chris Ball <cjb@laptop.org> | 2012-10-07 17:41:45 -0400 |
commit | e6c085863f97f0a8f009753e1baaf83e4aac7b42 (patch) | |
tree | 10819da5eb65d1919023ea540bb1580ebdb50dae /drivers/mmc/core/mmc.c | |
parent | da764f97df0e5e05e5453279f934531c0cf52e11 (diff) | |
download | lwn-e6c085863f97f0a8f009753e1baaf83e4aac7b42.tar.gz lwn-e6c085863f97f0a8f009753e1baaf83e4aac7b42.zip |
mmc: core: Fixup broken suspend and eMMC4.5 power off notify
This patch fixes up the broken suspend sequence for eMMC with sleep
support. Additionally it reworks the eMMC4.5 Power Off Notification
feature so it fits together with the existing sleep feature.
The CMD0 based re-initialization of the eMMC at resume is re-introduced
to maintain compatiblity for devices using sleep.
A host shall use MMC_CAP2_POWEROFF_NOTIFY to enable the Power Off
Notification feature. We might be able to remove this cap later on,
if we think that Power Off Notification always is preferred over
sleep, even if the host is not able to cut the eMMC VCCQ power.
Signed-off-by: Ulf Hansson <ulf.hansson@linaro.org>
Signed-off-by: Saugata Das <saugata.das@linaro.org>
Acked-by: Linus Walleij <linus.walleij@linaro.org>
Signed-off-by: Chris Ball <cjb@laptop.org>
Diffstat (limited to 'drivers/mmc/core/mmc.c')
-rw-r--r-- | drivers/mmc/core/mmc.c | 46 |
1 files changed, 35 insertions, 11 deletions
diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c index 7509de14aa78..7cc46382fd64 100644 --- a/drivers/mmc/core/mmc.c +++ b/drivers/mmc/core/mmc.c @@ -1007,7 +1007,7 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr, * so check for success and update the flag */ if (!err) - card->poweroff_notify_state = MMC_POWERED_ON; + card->ext_csd.power_off_notification = EXT_CSD_POWER_ON; } /* @@ -1273,6 +1273,35 @@ err: return err; } +static int mmc_can_poweroff_notify(const struct mmc_card *card) +{ + return card && + mmc_card_mmc(card) && + (card->ext_csd.power_off_notification == EXT_CSD_POWER_ON); +} + +static int mmc_poweroff_notify(struct mmc_card *card, unsigned int notify_type) +{ + unsigned int timeout = card->ext_csd.generic_cmd6_time; + int err; + + /* Use EXT_CSD_POWER_OFF_SHORT as default notification type. */ + if (notify_type == EXT_CSD_POWER_OFF_LONG) + timeout = card->ext_csd.power_off_longtime; + + err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, + EXT_CSD_POWER_OFF_NOTIFICATION, + notify_type, timeout); + if (err) + pr_err("%s: Power Off Notification timed out, %u\n", + mmc_hostname(card->host), timeout); + + /* Disable the power off notification after the switch operation. */ + card->ext_csd.power_off_notification = EXT_CSD_NO_POWER_NOTIFICATION; + + return err; +} + /* * Host is being removed. Free up the current card. */ @@ -1333,11 +1362,11 @@ static int mmc_suspend(struct mmc_host *host) BUG_ON(!host->card); mmc_claim_host(host); - if (mmc_card_can_sleep(host)) { + if (mmc_can_poweroff_notify(host->card)) + err = mmc_poweroff_notify(host->card, EXT_CSD_POWER_OFF_SHORT); + else if (mmc_card_can_sleep(host)) err = mmc_card_sleep(host); - if (!err) - mmc_card_set_sleep(host->card); - } else if (!mmc_host_is_spi(host)) + else if (!mmc_host_is_spi(host)) err = mmc_deselect_cards(host); host->card->state &= ~(MMC_STATE_HIGHSPEED | MMC_STATE_HIGHSPEED_200); mmc_release_host(host); @@ -1359,11 +1388,7 @@ static int mmc_resume(struct mmc_host *host) BUG_ON(!host->card); mmc_claim_host(host); - if (mmc_card_is_sleep(host->card)) { - err = mmc_card_awake(host); - mmc_card_clr_sleep(host->card); - } else - err = mmc_init_card(host, host->ocr, host->card); + err = mmc_init_card(host, host->ocr, host->card); mmc_release_host(host); return err; @@ -1374,7 +1399,6 @@ static int mmc_power_restore(struct mmc_host *host) int ret; host->card->state &= ~(MMC_STATE_HIGHSPEED | MMC_STATE_HIGHSPEED_200); - mmc_card_clr_sleep(host->card); mmc_claim_host(host); ret = mmc_init_card(host, host->ocr, host->card); mmc_release_host(host); |