mmc: core: Support aggressive power management for (e)MMC/SD
authorUlf Hansson <ulf.hansson@linaro.org>
Thu, 2 May 2013 12:02:39 +0000 (14:02 +0200)
committerlintao <lintao@rock-chips.com>
Fri, 7 Mar 2014 04:46:23 +0000 (12:46 +0800)
Aggressive power management is suitable when saving power is
essential. At request inactivity timeout, aka pm runtime
autosuspend timeout, the card will be suspended.

Once a new request arrives, the card will be re-initalized and
thus the first request will suffer from a latency. This latency
is card-specific, experiments has shown in general that SD-cards
has quite poor initialization time, around 300ms-1100ms. eMMC is
not surprisingly far better but still a couple of hundreds of ms
has been observed.

Except for the request latency, it is important to know that
suspending the card will also prevent the card from executing
internal house-keeping operations in idle mode. This could mean
degradation in performance.

To use this feature make sure the request inactivity timeout is
chosen carefully. This has not been done as a part of this patch.

Enable this feature by using host cap MMC_CAP_AGGRESSIVE_PM and
by setting CONFIG_MMC_UNSAFE_RESUME.

Signed-off-by: Ulf Hansson <ulf.hansson@linaro.org>
Signed-off-by: Chris Ball <cjb@laptop.org>
drivers/mmc/core/mmc.c
drivers/mmc/core/sd.c
include/linux/mmc/host.h

index 903f381501801b99c050a80514f60b6fddbcc7b6..506f4ee84e122386ed36aa5540e9a2c15df8357d 100644 (file)
@@ -1454,6 +1454,54 @@ static int mmc_resume(struct mmc_host *host)
        return err;
 }
 
+
+/*
+ * Callback for runtime_suspend.
+ */
+static int mmc_runtime_suspend(struct mmc_host *host)
+{
+       int err;
+
+       if (!(host->caps & MMC_CAP_AGGRESSIVE_PM))
+               return 0;
+
+       mmc_claim_host(host);
+
+       err = mmc_suspend(host);
+       if (err) {
+               pr_err("%s: error %d doing aggessive suspend\n",
+                       mmc_hostname(host), err);
+               goto out;
+       }
+       mmc_power_off(host);
+
+out:
+       mmc_release_host(host);
+       return err;
+}
+
+/*
+ * Callback for runtime_resume.
+ */
+static int mmc_runtime_resume(struct mmc_host *host)
+{
+       int err;
+
+       if (!(host->caps & MMC_CAP_AGGRESSIVE_PM))
+               return 0;
+
+       mmc_claim_host(host);
+
+       mmc_power_up(host);
+       err = mmc_resume(host);
+       if (err)
+               pr_err("%s: error %d doing aggessive resume\n",
+                       mmc_hostname(host), err);
+
+       mmc_release_host(host);
+       return 0;
+}
+
 static int mmc_power_restore(struct mmc_host *host)
 {
        int ret;
@@ -1514,6 +1562,8 @@ static const struct mmc_bus_ops mmc_ops_unsafe = {
        .detect = mmc_detect,
        .suspend = mmc_suspend,
        .resume = mmc_resume,
+       .runtime_suspend = mmc_runtime_suspend,
+       .runtime_resume = mmc_runtime_resume,
        .power_restore = mmc_power_restore,
        .alive = mmc_alive,
 };
index 5359ee2a66ce927128688570acbef95425fe1073..44ac2acf426e691b9fe245166deb99180294633d 100644 (file)
@@ -1160,6 +1160,53 @@ static int mmc_sd_resume(struct mmc_host *host)
        return err;
 }
 
+/*
+ * Callback for runtime_suspend.
+ */
+static int mmc_sd_runtime_suspend(struct mmc_host *host)
+{
+       int err;
+
+       if (!(host->caps & MMC_CAP_AGGRESSIVE_PM))
+               return 0;
+
+       mmc_claim_host(host);
+
+       err = mmc_sd_suspend(host);
+       if (err) {
+               pr_err("%s: error %d doing aggessive suspend\n",
+                       mmc_hostname(host), err);
+               goto out;
+       }
+       mmc_power_off(host);
+
+out:
+       mmc_release_host(host);
+       return err;
+}
+
+/*
+ * Callback for runtime_resume.
+ */
+static int mmc_sd_runtime_resume(struct mmc_host *host)
+{
+       int err;
+
+       if (!(host->caps & MMC_CAP_AGGRESSIVE_PM))
+               return 0;
+
+       mmc_claim_host(host);
+
+       mmc_power_up(host);
+       err = mmc_sd_resume(host);
+       if (err)
+               pr_err("%s: error %d doing aggessive resume\n",
+                       mmc_hostname(host), err);
+
+       mmc_release_host(host);
+       return 0;
+}
+
 static int mmc_sd_power_restore(struct mmc_host *host)
 {
        int ret;
@@ -1184,6 +1231,8 @@ static const struct mmc_bus_ops mmc_sd_ops = {
 static const struct mmc_bus_ops mmc_sd_ops_unsafe = {
        .remove = mmc_sd_remove,
        .detect = mmc_sd_detect,
+       .runtime_suspend = mmc_sd_runtime_suspend,
+       .runtime_resume = mmc_sd_runtime_resume,
        .suspend = mmc_sd_suspend,
        .resume = mmc_sd_resume,
        .power_restore = mmc_sd_power_restore,
index 58cbc34add00cd53948b5870f773cf3b54f8da29..a4cc0ee6096a6073cb135a1e2d323258fc7f7195 100755 (executable)
@@ -244,7 +244,7 @@ struct mmc_host {
 #define MMC_CAP_SPI            (1 << 4)        /* Talks only SPI protocols */
 #define MMC_CAP_NEEDS_POLL     (1 << 5)        /* Needs polling for card-detection */
 #define MMC_CAP_8_BIT_DATA     (1 << 6)        /* Can the host do 8 bit transfers */
-
+#define MMC_CAP_AGGRESSIVE_PM  (1 << 7)        /* Suspend (e)MMC/SD at idle  */
 #define MMC_CAP_NONREMOVABLE   (1 << 8)        /* Nonremovable e.g. eMMC */
 #define MMC_CAP_WAIT_WHILE_BUSY        (1 << 9)        /* Waits while card is busy */
 #define MMC_CAP_ERASE          (1 << 10)       /* Allow erase/trim commands */