mmc: core: Fixup broken suspend and eMMC4.5 power off notify
authorUlf Hansson <ulf.hansson@linaro.org>
Fri, 5 Oct 2012 16:45:39 +0000 (12:45 -0400)
committerChris Ball <cjb@laptop.org>
Sun, 7 Oct 2012 21:41:45 +0000 (17:41 -0400)
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>
drivers/mmc/core/core.c
drivers/mmc/core/mmc.c
drivers/mmc/host/dw_mmc.c
drivers/mmc/host/sdhci.c
include/linux/mmc/card.h
include/linux/mmc/host.h

index 66121633c9cdac819fe8c114eaf9477f5689fa1b..06c42cfb7c34d9fa972c48eeb257e5e8b50b43b8 100644 (file)
@@ -1273,48 +1273,6 @@ void mmc_set_driver_type(struct mmc_host *host, unsigned int drv_type)
        mmc_host_clk_release(host);
 }
 
-static void mmc_poweroff_notify(struct mmc_host *host)
-{
-       struct mmc_card *card;
-       unsigned int timeout;
-       unsigned int notify_type = EXT_CSD_NO_POWER_NOTIFICATION;
-       int err = 0;
-
-       card = host->card;
-       mmc_claim_host(host);
-
-       /*
-        * Send power notify command only if card
-        * is mmc and notify state is powered ON
-        */
-       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;
-       }
-       mmc_release_host(host);
-}
-
 /*
  * Apply power to the MMC stack.  This is a two-stage process.
  * First, we enable power to the card without the clock running.
@@ -1377,8 +1335,6 @@ static void mmc_power_up(struct mmc_host *host)
 
 void mmc_power_off(struct mmc_host *host)
 {
-       int err = 0;
-
        if (host->ios.power_mode == MMC_POWER_OFF)
                return;
 
@@ -1387,22 +1343,6 @@ void mmc_power_off(struct mmc_host *host)
        host->ios.clock = 0;
        host->ios.vdd = 0;
 
-       /*
-        * For eMMC 4.5 device send AWAKE command before
-        * POWER_OFF_NOTIFY command, because in sleep state
-        * eMMC 4.5 devices respond to only RESET and AWAKE cmd
-        */
-       if (host->card && mmc_card_is_sleep(host->card) &&
-           host->bus_ops->resume) {
-               err = host->bus_ops->resume(host);
-
-               if (!err)
-                       mmc_poweroff_notify(host);
-               else
-                       pr_warning("%s: error %d during resume "
-                                  "(continue with poweroff sequence)\n",
-                                  mmc_hostname(host), err);
-       }
 
        /*
         * Reset ocr mask to be the highest possible voltage supported for
@@ -2579,7 +2519,6 @@ 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);
 
@@ -2603,7 +2542,6 @@ 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);
 
index 7509de14aa780a15afb3f6b67ab80d83327bd323..7cc46382fd644643f0323184709c551060fd9fa0 100644 (file)
@@ -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);
index a23af77de4cedd50469a3f1788a772e991f9d011..c2828f35c3b8812b4de12cb0fd4b75c28fa09bbf 100644 (file)
@@ -1885,11 +1885,6 @@ static int dw_mci_init_slot(struct dw_mci *host, unsigned int id)
        if (host->pdata->quirks & DW_MCI_QUIRK_HIGHSPEED)
                mmc->caps |= MMC_CAP_SD_HIGHSPEED | MMC_CAP_MMC_HIGHSPEED;
 
-       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;
-
        if (host->pdata->blk_settings) {
                mmc->max_segs = host->pdata->blk_settings->max_segs;
                mmc->max_blk_size = host->pdata->blk_settings->max_blk_size;
index 0e15c79014fb7a680adb7c278aebc890e4dbbd45..7922adb4238625c1b4c9e1d489dea04a3d0e2353 100644 (file)
@@ -2886,15 +2886,6 @@ 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;
index 78cc3be85391c025762fe70392a3ca74ce811852..943550dfe9ea7f7f372c1a5d64d5a6328a371727 100644 (file)
@@ -57,6 +57,7 @@ struct mmc_ext_csd {
        unsigned int            sa_timeout;             /* Units: 100ns */
        unsigned int            generic_cmd6_time;      /* Units: 10ms */
        unsigned int            power_off_longtime;     /* Units: ms */
+       u8                      power_off_notification; /* state */
        unsigned int            hs_max_dtr;
 #define MMC_HIGH_26_MAX_DTR    26000000
 #define MMC_HIGH_52_MAX_DTR    52000000
@@ -229,7 +230,6 @@ struct mmc_card {
 #define MMC_CARD_SDXC          (1<<6)          /* card is SDXC */
 #define MMC_CARD_REMOVED       (1<<7)          /* card has been removed */
 #define MMC_STATE_HIGHSPEED_200        (1<<8)          /* card is in HS200 mode */
-#define MMC_STATE_SLEEP                (1<<9)          /* card is in sleep state */
 #define MMC_STATE_DOING_BKOPS  (1<<10)         /* card is doing BKOPS */
        unsigned int            quirks;         /* card quirks */
 #define MMC_QUIRK_LENIENT_FN0  (1<<0)          /* allow SDIO FN0 writes outside of the VS CCCR range */
@@ -246,11 +246,6 @@ struct mmc_card {
 #define MMC_QUIRK_LONG_READ_TIME (1<<9)                /* Data read time > CSD says */
 #define MMC_QUIRK_SEC_ERASE_TRIM_BROKEN (1<<10)        /* Skip secure for erase/trim */
                                                /* byte mode */
-       unsigned int    poweroff_notify_state;  /* eMMC4.5 notify feature */
-#define MMC_NO_POWER_NOTIFICATION      0
-#define MMC_POWERED_ON                 1
-#define MMC_POWEROFF_SHORT             2
-#define MMC_POWEROFF_LONG              3
 
        unsigned int            erase_size;     /* erase size in sectors */
        unsigned int            erase_shift;    /* if erase unit is power 2 */
@@ -397,7 +392,6 @@ static inline void __maybe_unused remove_quirk(struct mmc_card *card, int data)
 #define mmc_sd_card_uhs(c)     ((c)->state & MMC_STATE_ULTRAHIGHSPEED)
 #define mmc_card_ext_capacity(c) ((c)->state & MMC_CARD_SDXC)
 #define mmc_card_removed(c)    ((c) && ((c)->state & MMC_CARD_REMOVED))
-#define mmc_card_is_sleep(c)   ((c)->state & MMC_STATE_SLEEP)
 #define mmc_card_doing_bkops(c)        ((c)->state & MMC_STATE_DOING_BKOPS)
 
 #define mmc_card_set_present(c)        ((c)->state |= MMC_STATE_PRESENT)
@@ -410,11 +404,9 @@ static inline void __maybe_unused remove_quirk(struct mmc_card *card, int data)
 #define mmc_sd_card_set_uhs(c) ((c)->state |= MMC_STATE_ULTRAHIGHSPEED)
 #define mmc_card_set_ext_capacity(c) ((c)->state |= MMC_CARD_SDXC)
 #define mmc_card_set_removed(c) ((c)->state |= MMC_CARD_REMOVED)
-#define mmc_card_set_sleep(c)  ((c)->state |= MMC_STATE_SLEEP)
 #define mmc_card_set_doing_bkops(c)    ((c)->state |= MMC_STATE_DOING_BKOPS)
-
 #define mmc_card_clr_doing_bkops(c)    ((c)->state &= ~MMC_STATE_DOING_BKOPS)
-#define mmc_card_clr_sleep(c)  ((c)->state &= ~MMC_STATE_SLEEP)
+
 /*
  * Quirk add/remove for MMC products.
  */
index d5d9bd4c5aa83678fbbd4bedfa4155e9c0fb2d60..7abb0e1f7bda5b4a72f1477078abfd6d86232ffa 100644 (file)
@@ -259,10 +259,6 @@ struct mmc_host {
 #define MMC_CAP2_RO_ACTIVE_HIGH        (1 << 11)       /* Write-protect signal active high */
 
        mmc_pm_flag_t           pm_caps;        /* supported pm features */
-       unsigned int        power_notify_type;
-#define MMC_HOST_PW_NOTIFY_NONE                0
-#define MMC_HOST_PW_NOTIFY_SHORT       1
-#define MMC_HOST_PW_NOTIFY_LONG                2
 
 #ifdef CONFIG_MMC_CLKGATE
        int                     clk_requests;   /* internal reference counter */