diff options
author | Girish K S <girish.shivananjappa@linaro.org> | 2011-10-13 08:34:16 +0200 |
---|---|---|
committer | Chris Ball <cjb@laptop.org> | 2011-10-26 22:32:23 +0200 |
commit | bec8726abc72bf30d2743a722aa37cd69e7a0580 (patch) | |
tree | eed4a3c441ff64f5719b021fce419de0fc5196d9 /drivers/mmc | |
parent | mmc: sdhci-s3c: fix potential NULL dereference (diff) | |
download | linux-bec8726abc72bf30d2743a722aa37cd69e7a0580.tar.xz linux-bec8726abc72bf30d2743a722aa37cd69e7a0580.zip |
mmc: core: Add Power Off Notify Feature eMMC 4.5
This patch adds support for the power off notify feature, available in
eMMC 4.5 devices. If the host has support for this feature, then the
mmc core will notify the device by setting the POWER_OFF_NOTIFICATION
byte in the extended csd register with a value of 1 (POWER_ON).
For suspend mode short timeout is used, whereas for the normal poweroff
long timeout is used.
Signed-off-by: Girish K S <girish.shivananjappa@linaro.org>
Signed-off-by: Jaehoon Chung <jh80.chung@samsung.com>
Signed-off-by: Chris Ball <cjb@laptop.org>
Diffstat (limited to 'drivers/mmc')
-rw-r--r-- | drivers/mmc/core/core.c | 34 | ||||
-rw-r--r-- | drivers/mmc/core/mmc.c | 23 | ||||
-rw-r--r-- | drivers/mmc/host/sdhci.c | 9 |
3 files changed, 64 insertions, 2 deletions
diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c index 61d7730bc8b2..a3c4e0fe9434 100644 --- a/drivers/mmc/core/core.c +++ b/drivers/mmc/core/core.c @@ -1212,11 +1212,43 @@ static void mmc_power_up(struct mmc_host *host) void mmc_power_off(struct mmc_host *host) { + struct mmc_card *card; + unsigned int notify_type; + unsigned int timeout; + int err; + mmc_host_clk_hold(host); + card = host->card; host->ios.clock = 0; host->ios.vdd = 0; + if (card && mmc_card_mmc(card) && + (card->poweroff_notify_state == MMC_POWERED_ON)) { + + if (host->power_notify_type == MMC_HOST_PW_NOTIFY_SHORT) { + notify_type = EXT_CSD_POWER_OFF_SHORT; + timeout = card->ext_csd.generic_cmd6_time; + card->poweroff_notify_state = MMC_POWEROFF_SHORT; + } else { + notify_type = EXT_CSD_POWER_OFF_LONG; + timeout = card->ext_csd.power_off_longtime; + card->poweroff_notify_state = MMC_POWEROFF_LONG; + } + + err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, + EXT_CSD_POWER_OFF_NOTIFICATION, + notify_type, timeout); + + if (err && err != -EBADMSG) + pr_err("Device failed to respond within %d poweroff " + "time. Forcefully powering down the device\n", + timeout); + + /* Set the card state to no notification after the poweroff */ + card->poweroff_notify_state = MMC_NO_POWER_NOTIFICATION; + } + /* * Reset ocr mask to be the highest possible voltage supported for * this mmc host. This value will be used at next power up. @@ -2208,6 +2240,7 @@ int mmc_pm_notify(struct notifier_block *notify_block, spin_lock_irqsave(&host->lock, flags); host->rescan_disable = 1; + host->power_notify_type = MMC_HOST_PW_NOTIFY_SHORT; spin_unlock_irqrestore(&host->lock, flags); cancel_delayed_work_sync(&host->detect); @@ -2231,6 +2264,7 @@ int mmc_pm_notify(struct notifier_block *notify_block, spin_lock_irqsave(&host->lock, flags); host->rescan_disable = 0; + host->power_notify_type = MMC_HOST_PW_NOTIFY_LONG; spin_unlock_irqrestore(&host->lock, flags); mmc_detect_change(host, 0); diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c index 4e869d371a03..f8ea9387d75c 100644 --- a/drivers/mmc/core/mmc.c +++ b/drivers/mmc/core/mmc.c @@ -458,10 +458,12 @@ static int mmc_read_ext_csd(struct mmc_card *card, u8 *ext_csd) else card->erased_byte = 0x0; - if (card->ext_csd.rev >= 6) + if (card->ext_csd.rev >= 6) { card->ext_csd.generic_cmd6_time = 10 * ext_csd[EXT_CSD_GENERIC_CMD6_TIME]; - else + card->ext_csd.power_off_longtime = 10 * + ext_csd[EXT_CSD_POWER_OFF_LONG_TIME]; + } else card->ext_csd.generic_cmd6_time = 0; out: @@ -846,6 +848,23 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr, } /* + * If the host supports the power_off_notify capability then + * set the notification byte in the ext_csd register of device + */ + if ((host->caps2 & MMC_CAP2_POWEROFF_NOTIFY) && + (card->poweroff_notify_state == MMC_NO_POWER_NOTIFICATION)) { + err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, + EXT_CSD_POWER_OFF_NOTIFICATION, + EXT_CSD_POWER_ON, + card->ext_csd.generic_cmd6_time); + if (err && err != -EBADMSG) + goto free_card; + } + + if (!err) + card->poweroff_notify_state = MMC_POWERED_ON; + + /* * Activate high speed (if supported) */ if ((card->ext_csd.hs_max_dtr != 0) && diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index 2cc3ffa7d766..6d8eea323541 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -2739,6 +2739,15 @@ int sdhci_add_host(struct sdhci_host *host) if (caps[1] & SDHCI_DRIVER_TYPE_D) mmc->caps |= MMC_CAP_DRIVER_TYPE_D; + /* + * If Power Off Notify capability is enabled by the host, + * set notify to short power off notify timeout value. + */ + if (mmc->caps2 & MMC_CAP2_POWEROFF_NOTIFY) + mmc->power_notify_type = MMC_HOST_PW_NOTIFY_SHORT; + else + mmc->power_notify_type = MMC_HOST_PW_NOTIFY_NONE; + /* Initial value for re-tuning timer count */ host->tuning_count = (caps[1] & SDHCI_RETUNING_TIMER_COUNT_MASK) >> SDHCI_RETUNING_TIMER_COUNT_SHIFT; |