mmc: core: Add Power Off Notify Feature eMMC 4.5
[pandora-kernel.git] / drivers / mmc / core / core.c
index 61d7730..a3c4e0f 100644 (file)
@@ -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);