]> rtime.felk.cvut.cz Git - hercules2020/nv-tegra/linux-4.4.git/commitdiff
mmc: Fix eMMC suspend/resume sequence
authorPavan Kunapuli <pkunapuli@nvidia.com>
Wed, 13 May 2015 10:55:14 +0000 (16:25 +0530)
committerR Raj Kumar <rrajk@nvidia.com>
Fri, 24 Jun 2016 08:53:39 +0000 (01:53 -0700)
- Fix eMMC sleep/awake calls and set/clear
  required card state flags.
- Add option to parse MMC_PM_IGNORE_PM_NOTIFY
  option from dt.
- Do not use eMMC sleep mode feature, if host
  doesn't support it.

Bug 200212629

Change-Id: I6968be47e5cedac8ec721e64813a09febccb7dc3
Signed-off-by: Pavan Kunapuli <pkunapuli@nvidia.com>
Signed-off-by: R Raj Kumar <rrajk@nvidia.com>
Reviewed-on: http://git-master/r/1170304
Reviewed-by: Automatic_Commit_Validation_User
Reviewed-by: Venu Byravarasu <vbyravarasu@nvidia.com>
drivers/mmc/core/host.c
drivers/mmc/core/mmc.c
drivers/mmc/host/sdhci.c
include/linux/mmc/card.h
include/linux/mmc/host.h

index fcf7829c759e200baa7ef5f9cd456c18dd83ac48..b00d089d2b26a7d5d65d9ef447d1625ebc3cd3b3 100644 (file)
@@ -4,6 +4,7 @@
  *  Copyright (C) 2003 Russell King, All Rights Reserved.
  *  Copyright (C) 2007-2008 Pierre Ossman
  *  Copyright (C) 2010 Linus Walleij
+ *  Copyright (c) 2016, NVIDIA CORPORATION.  All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 as
@@ -158,7 +159,7 @@ int mmc_of_parse(struct mmc_host *host)
 {
        struct device_node *np;
        u32 bus_width;
-       int ret;
+       int len, ret;
        bool cd_cap_invert, cd_gpio_invert = false;
        bool ro_cap_invert, ro_gpio_invert = false;
 
@@ -277,6 +278,8 @@ int mmc_of_parse(struct mmc_host *host)
                host->pm_caps |= MMC_PM_KEEP_POWER;
        if (of_property_read_bool(np, "enable-sdio-wakeup"))
                host->pm_caps |= MMC_PM_WAKE_SDIO_IRQ;
+       if (of_find_property(np, "ignore-pm-notify", &len))
+               host->pm_caps |= MMC_PM_IGNORE_PM_NOTIFY;
        if (of_property_read_bool(np, "mmc-ddr-1_8v"))
                host->caps |= MMC_CAP_1_8V_DDR;
        if (of_property_read_bool(np, "mmc-ddr-1_2v"))
index 72563bbc882917712d825186f368ba8ab682547f..daa8ffc2413b476abcdf8816f780532b5613d3c6 100644 (file)
@@ -1721,10 +1721,11 @@ err:
 
 static int mmc_can_sleep(struct mmc_card *card)
 {
-       return (card && card->ext_csd.rev >= 3);
+       return ((card && card->ext_csd.rev >= 3) &&
+               !(card->host->caps2 & MMC_CAP2_NO_SLEEP_CMD));
 }
 
-static int mmc_sleep(struct mmc_host *host)
+static int mmc_sleep(struct mmc_host *host, int sleep)
 {
        struct mmc_command cmd = {0};
        struct mmc_card *card = host->card;
@@ -1734,13 +1735,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
@@ -1768,6 +1772,8 @@ 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;
@@ -1878,12 +1884,16 @@ static int _mmc_suspend(struct mmc_host *host, bool is_suspend)
        if (mmc_can_poweroff_notify(host->card) &&
                ((host->caps2 & MMC_CAP2_FULL_PWR_CYCLE) || !is_suspend))
                err = mmc_poweroff_notify(host->card, notify_type);
-       else if (mmc_can_sleep(host->card))
-               err = mmc_sleep(host);
-       else if (!mmc_host_is_spi(host))
+       else if (mmc_can_sleep(host->card)) {
+               err = mmc_sleep(host, 1);
+               if (!err) {
+                       mmc_card_set_sleep(host->card);
+                       mmc_card_set_suspended(host->card);
+               }
+       } else if (!mmc_host_is_spi(host))
                err = mmc_deselect_cards(host);
 
-       if (!err) {
+       if (!err && !mmc_card_in_sleep(host->card)) {
                mmc_power_off(host);
                mmc_card_set_suspended(host->card);
        }
@@ -1924,9 +1934,18 @@ static int _mmc_resume(struct mmc_host *host)
        if (!mmc_card_suspended(host->card))
                goto out;
 
-       mmc_power_up(host, host->card->ocr);
-       err = mmc_init_card(host, host->card->ocr, host->card);
-       mmc_card_clr_suspended(host->card);
+       if (mmc_can_sleep(host->card) &&
+               mmc_card_in_sleep(host->card)) {
+               err = mmc_sleep(host, 0);
+               if (!err) {
+                       mmc_card_clr_sleep(host->card);
+                       mmc_card_clr_suspended(host->card);
+               }
+       } else {
+               mmc_power_up(host, host->card->ocr);
+               err = mmc_init_card(host, host->card->ocr, host->card);
+               mmc_card_clr_suspended(host->card);
+       }
 
 out:
        mmc_release_host(host);
index fe4cab6ac414213060ad23776f45ff5d50e1cc24..fbfeeb56a70a3c4e927ca5c48d5a63b7ba699ade 100644 (file)
@@ -2882,6 +2882,15 @@ int sdhci_suspend_host(struct sdhci_host *host)
        mmc_retune_timer_stop(host->mmc);
        mmc_retune_needed(host->mmc);
 
+       /*
+        * If eMMC cards are put in sleep state, Vccq can be disabled
+        * but Vcc would still be powered on. In resume, we only restore
+        * the controller context. So, set MMC_PM_KEEP_POWER flag.
+        */
+       if (!(host->mmc->caps2 & MMC_CAP2_NO_SLEEP_CMD) &&
+               (host->mmc->pm_caps & MMC_PM_KEEP_POWER))
+               host->mmc->pm_flags |= MMC_PM_KEEP_POWER;
+
        if (!device_may_wakeup(mmc_dev(host->mmc))) {
                host->ier = 0;
                sdhci_writel(host, 0, SDHCI_INT_ENABLE);
index 6092a805dc54828f09ea97ecff5e579ab77f53b6..8c66d0b97fb7aaf03bfc685744e43dbc4d66350d 100644 (file)
@@ -271,6 +271,8 @@ struct mmc_card {
 #define MMC_STATE_DOING_BKOPS  (1<<5)          /* card is doing BKOPS */
 #define MMC_STATE_SUSPENDED    (1<<6)          /* card is suspended */
 #define MMC_STATE_CMDQ         (1<<8)          /* card is in cmd queue mode */
+#define MMC_STATE_SLEEP        (1<<9)  /* Card is in sleep mode */
+
        unsigned int            quirks;         /* card quirks */
 #define MMC_QUIRK_LENIENT_FN0  (1<<0)          /* allow SDIO FN0 writes outside of the VS CCCR range */
 #define MMC_QUIRK_BLKSZ_FOR_BYTE_MODE (1<<1)   /* use func->cur_blksize */
@@ -441,6 +443,7 @@ static inline void __maybe_unused remove_quirk(struct mmc_card *card, int data)
 #define mmc_card_doing_bkops(c)        ((c)->state & MMC_STATE_DOING_BKOPS)
 #define mmc_card_suspended(c)  ((c)->state & MMC_STATE_SUSPENDED)
 #define mmc_card_cmdq(c)   ((c)->state & MMC_STATE_CMDQ)
+#define mmc_card_in_sleep(c)   ((c)->state & MMC_STATE_SLEEP)
 
 #define mmc_card_set_present(c)        ((c)->state |= MMC_STATE_PRESENT)
 #define mmc_card_set_readonly(c) ((c)->state |= MMC_STATE_READONLY)
@@ -448,11 +451,13 @@ static inline void __maybe_unused remove_quirk(struct mmc_card *card, int data)
 #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_doing_bkops(c)    ((c)->state |= MMC_STATE_DOING_BKOPS)
+#define mmc_card_set_sleep(c)  ((c)->state |= MMC_STATE_SLEEP)
 #define mmc_card_clr_doing_bkops(c)    ((c)->state &= ~MMC_STATE_DOING_BKOPS)
 #define mmc_card_set_suspended(c) ((c)->state |= MMC_STATE_SUSPENDED)
 #define mmc_card_clr_suspended(c) ((c)->state &= ~MMC_STATE_SUSPENDED)
 #define mmc_card_set_cmdq(c)       ((c)->state |= MMC_STATE_CMDQ)
 #define mmc_card_clr_cmdq(c)       ((c)->state &= ~MMC_STATE_CMDQ)
+#define mmc_card_clr_sleep(c)  ((c)->state &= ~MMC_STATE_SLEEP)
 
 /*
  * Quirk add/remove for MMC products.
index 6b54839c0df90254f988600b49e7ef64cca48071..15f58cdf93f8f6dfdb737e45961e8e677bcbb724 100644 (file)
@@ -359,6 +359,7 @@ struct mmc_host {
 #define MMC_CAP2_SDIO_IRQ_NOTHREAD (1 << 17)
 #define MMC_CAP2_NO_WRITE_PROTECT (1 << 18)    /* No physical write protect pin, assume that card is always read-write */
 #define MMC_CAP2_EN_STROBE (1 << 19)
+#define MMC_CAP2_NO_SLEEP_CMD  (1 << 20)       /* cannot support sleep mode */
 #define MMC_CAP2_HW_CQ         (1 << 23)       /* support eMMC command queue */
 #define MMC_CAP2_CMDQ_QBR      (1 << 24)       /* CMDQ Queue barrier supported */