summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorUlf Hansson <ulf.hansson@linaro.org>2013-05-02 16:02:39 +0400
committerChris Ball <cjb@laptop.org>2013-05-26 22:23:17 +0400
commitc4d770d72492df510077b277f21ac5f0dad9e5eb (patch)
tree60412abaad7c69267483d368afeff4d0a769a82b
parente94cfef698aae6b209d8918dd319312e4b02118d (diff)
downloadlinux-c4d770d72492df510077b277f21ac5f0dad9e5eb.tar.xz
mmc: core: Support aggressive power management for (e)MMC/SD
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>
-rw-r--r--drivers/mmc/core/mmc.c50
-rw-r--r--drivers/mmc/core/sd.c49
-rw-r--r--include/linux/mmc/host.h2
3 files changed, 100 insertions, 1 deletions
diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c
index 903f38150180..506f4ee84e12 100644
--- a/drivers/mmc/core/mmc.c
+++ b/drivers/mmc/core/mmc.c
@@ -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,
};
diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c
index 17fa2d271dd4..aeaae7c3b22b 100644
--- a/drivers/mmc/core/sd.c
+++ b/drivers/mmc/core/sd.c
@@ -1100,6 +1100,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;
@@ -1124,6 +1171,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,
diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h
index 6f3851533de0..374098bae831 100644
--- a/include/linux/mmc/host.h
+++ b/include/linux/mmc/host.h
@@ -239,7 +239,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 */