summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSujit Reddy Thumma <sthumma@codeaurora.org>2013-06-19 20:15:37 +0530
committerSubhash Jadavani <subhashj@codeaurora.org>2016-05-27 10:28:44 -0700
commit0c196acfcbb766a07151cfae76c2318eca661c40 (patch)
tree6a5372ed2599b99f74486511e79bbde34fb427fd
parent573858a3b64167f27a2968dbebf661007b324439 (diff)
mmc: sdhci: Defer release of CPU DMA PM QoS vote in high load cases
PM QoS vote of default value mean that the CPU is allowed to move into deepest low power mode whenever possible. Currently, if there are back-to-back MMC requests, with a short delay, the PM QoS vote to default value is done immediately which cause the immediate request to have high latency as the CPU might have idle'd and moved to deepest low power mode. To avoid this defer the PM QoS vote till a defined timeout (pm_qos_timeout_us), so that back-to-back requests may not suffer from additional latencies. In addition, if the load on MMC is low, the additional latency may be sustainable. Hence, aggressively release the vote in order to achieve additional power savings. CRs-Fixed: 501712 Change-Id: I82166b0ce9416eb0d519f7da26e5a96956093cb2 Signed-off-by: Sujit Reddy Thumma <sthumma@codeaurora.org> [subhashj@codeaurora.org: fixed minor merge conflict and fixed compilation errors] Signed-off-by: Subhash Jadavani <subhashj@codeaurora.org>
-rw-r--r--drivers/mmc/host/sdhci.c49
-rw-r--r--drivers/mmc/host/sdhci.h8
-rw-r--r--include/linux/mmc/host.h7
3 files changed, 61 insertions, 3 deletions
diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
index de0bcb9149d2..f9f22a2fe0dd 100644
--- a/drivers/mmc/host/sdhci.c
+++ b/drivers/mmc/host/sdhci.c
@@ -1430,15 +1430,55 @@ static int sdhci_disable(struct mmc_host *mmc)
{
struct sdhci_host *host = mmc_priv(mmc);
- if (host->cpu_dma_latency_us)
- pm_qos_update_request(&host->pm_qos_req_dma,
+ if (host->cpu_dma_latency_us) {
+ /*
+ * In performance mode, release QoS vote after a timeout to
+ * make sure back-to-back requests don't suffer from latencies
+ * that are involved to wake CPU from low power modes in cases
+ * where the CPU goes into low power mode as soon as QoS vote is
+ * released.
+ */
+ if (host->power_policy == SDHCI_PERFORMANCE_MODE)
+ pm_qos_update_request_timeout(&host->pm_qos_req_dma,
+ host->cpu_dma_latency_us,
+ host->pm_qos_timeout_us);
+ else
+ pm_qos_update_request(&host->pm_qos_req_dma,
PM_QOS_DEFAULT_VALUE);
+ }
+
if (host->ops->platform_bus_voting)
host->ops->platform_bus_voting(host, 0);
return 0;
}
+static inline void sdhci_update_power_policy(struct sdhci_host *host,
+ enum sdhci_power_policy policy)
+{
+ host->power_policy = policy;
+}
+
+static int sdhci_notify_load(struct mmc_host *mmc, enum mmc_load state)
+{
+ int err = 0;
+ struct sdhci_host *host = mmc_priv(mmc);
+
+ switch (state) {
+ case MMC_LOAD_HIGH:
+ sdhci_update_power_policy(host, SDHCI_PERFORMANCE_MODE);
+ break;
+ case MMC_LOAD_LOW:
+ sdhci_update_power_policy(host, SDHCI_POWER_SAVE_MODE);
+ break;
+ default:
+ err = -EINVAL;
+ break;
+ }
+
+ return err;
+}
+
static bool sdhci_check_state(struct sdhci_host *host)
{
if (!host->clock || !host->pwr)
@@ -2345,6 +2385,7 @@ static const struct mmc_host_ops sdhci_ops = {
.card_busy = sdhci_card_busy,
.enable = sdhci_enable,
.disable = sdhci_disable,
+ .notify_load = sdhci_notify_load,
};
/*****************************************************************************\
@@ -3588,9 +3629,11 @@ int sdhci_add_host(struct sdhci_host *host)
mmiowb();
- if (host->cpu_dma_latency_us)
+ if (host->cpu_dma_latency_us) {
+ host->pm_qos_timeout_us = 10000; /* default value */
pm_qos_add_request(&host->pm_qos_req_dma,
PM_QOS_CPU_DMA_LATENCY, PM_QOS_DEFAULT_VALUE);
+ }
mmc_add_host(mmc);
pr_info("%s: SDHCI controller on %s [%s] using %s\n",
diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h
index 1a39c46439c5..8aa0f4e077de 100644
--- a/drivers/mmc/host/sdhci.h
+++ b/drivers/mmc/host/sdhci.h
@@ -316,6 +316,11 @@ enum sdhci_cookie {
COOKIE_GIVEN,
};
+enum sdhci_power_policy {
+ SDHCI_PERFORMANCE_MODE,
+ SDHCI_POWER_SAVE_MODE,
+};
+
struct sdhci_host {
/* Data set by hardware interface driver */
const char *hw_name; /* Hardware bus name */
@@ -568,6 +573,9 @@ struct sdhci_host {
struct pm_qos_request pm_qos_req_dma;
ktime_t data_start_time;
+ unsigned int pm_qos_timeout_us; /* timeout for PM QoS request */
+ enum sdhci_power_policy power_policy;
+
unsigned long private[0] ____cacheline_aligned;
};
diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h
index 2b6159a85a15..1939cd47b3aa 100644
--- a/include/linux/mmc/host.h
+++ b/include/linux/mmc/host.h
@@ -79,6 +79,12 @@ struct mmc_ios {
#define MMC_SET_DRIVER_TYPE_D 3
};
+/* states to represent load on the host */
+enum mmc_load {
+ MMC_LOAD_HIGH,
+ MMC_LOAD_LOW,
+};
+
struct mmc_host_ops {
/*
* 'enable' is called when the host is claimed and 'disable' is called
@@ -150,6 +156,7 @@ struct mmc_host_ops {
*/
int (*multi_io_quirk)(struct mmc_card *card,
unsigned int direction, int blk_size);
+ int (*notify_load)(struct mmc_host *, enum mmc_load);
};
struct mmc_card;