mmc: msm_sdcc: vote against IDLE power collapse
During the SDCC DMA transfer, if DMA transfer time is
long enough to do IDLE power collapse then system may
go into IDLE power collapse and once SDCC DMA transfer
is completed, system wakes up from Idle Power Collapse
due to SDCC DMA interrupt. But delay for waking up
from Idle Power collapse could be as large as 5 ms which
really degrades the overall read & write throughputs
for SD/eMMC/SDIO cards.
For example, following are the performance numbers with
eMMC card on MSM8960 platform with and without Idle Power
Collapse.
Idle Power collapse enabled:
LMDD Read throughput = ~14 MB/s
LMDD Write throughput = ~6 MB/s
Idle Power Collapse disabled:
LMDD Read throughput = ~25 MB/s
LMDD Write throughput = ~8 MB/s
So this change votes against the Idle power collapse by registering
with PM QOS about it's acceptable DMA latency when SDCC transfer is
active. This latency value is one more than the latency of SWFI
which means system can go into SWFI but not in any of the other
low power modes (including Idle power collapse).
CRs-fixed: 327751
Change-Id: Iae5e12cade383544243f17c448346dd5d0faa60e
Signed-off-by: Subhash Jadavani <subhashj@codeaurora.org>
diff --git a/drivers/mmc/host/msm_sdcc.c b/drivers/mmc/host/msm_sdcc.c
index 57e5696..9a99cf3 100644
--- a/drivers/mmc/host/msm_sdcc.c
+++ b/drivers/mmc/host/msm_sdcc.c
@@ -43,6 +43,7 @@
#include <linux/gpio.h>
#include <linux/regulator/consumer.h>
#include <linux/slab.h>
+#include <linux/pm_qos_params.h>
#include <asm/cacheflush.h>
#include <asm/div64.h>
@@ -148,6 +149,24 @@
return ret;
}
+/* Prevent idle power collapse(pc) while operating in peripheral mode */
+static void msmsdcc_pm_qos_update_latency(struct msmsdcc_host *host, int vote)
+{
+ u32 swfi_latency = 0;
+
+ if (!host->plat->swfi_latency)
+ return;
+
+ swfi_latency = host->plat->swfi_latency + 1;
+
+ if (vote)
+ pm_qos_update_request(&host->pm_qos_req_dma,
+ swfi_latency);
+ else
+ pm_qos_update_request(&host->pm_qos_req_dma,
+ PM_QOS_DEFAULT_VALUE);
+}
+
#ifdef CONFIG_MMC_MSM_SPS_SUPPORT
static int msmsdcc_sps_reset_ep(struct msmsdcc_host *host,
struct msmsdcc_sps_ep_conn_data *ep);
@@ -2454,6 +2473,12 @@
{
int rc;
struct device *dev = mmc->parent;
+ struct msmsdcc_host *host = mmc_priv(mmc);
+
+ msmsdcc_pm_qos_update_latency(host, 1);
+
+ if (mmc->card && mmc_card_sdio(mmc->card) && host->is_resumed)
+ return 0;
if (dev->power.runtime_status == RPM_SUSPENDING) {
if (mmc->suspend_task == current) {
@@ -2469,6 +2494,8 @@
__func__, rc);
return rc;
}
+
+ host->is_resumed = true;
out:
return 0;
}
@@ -2478,16 +2505,22 @@
int rc;
struct msmsdcc_host *host = mmc_priv(mmc);
+ msmsdcc_pm_qos_update_latency(host, 0);
+
+ if (mmc->card && mmc_card_sdio(mmc->card))
+ return 0;
+
if (host->plat->disable_runtime_pm)
return -ENOTSUPP;
- if (mmc->card && mmc->card->type == MMC_TYPE_SDIO)
- return -ENOTSUPP;
rc = pm_runtime_put_sync(mmc->parent);
if (rc < 0)
pr_info("%s: %s: failed with error %d", mmc_hostname(mmc),
__func__, rc);
+ else
+ host->is_resumed = false;
+
return rc;
}
#else
@@ -2496,6 +2529,8 @@
struct msmsdcc_host *host = mmc_priv(mmc);
unsigned long flags;
+ msmsdcc_pm_qos_update_latency(host, 1);
+
spin_lock_irqsave(&host->lock, flags);
if (!host->clks_on) {
msmsdcc_setup_clocks(host, true);
@@ -2511,8 +2546,10 @@
struct msmsdcc_host *host = mmc_priv(mmc);
unsigned long flags;
+ msmsdcc_pm_qos_update_latency(host, 0);
+
if (mmc->card && mmc_card_sdio(mmc->card))
- return -ENOTSUPP;
+ return 0;
spin_lock_irqsave(&host->lock, flags);
if (host->clks_on) {
@@ -4048,6 +4085,11 @@
/* Apply Hard reset to SDCC to put it in power on default state */
msmsdcc_hard_reset(host);
+ /* pm qos request to prevent apps idle power collapse */
+ if (host->plat->swfi_latency)
+ pm_qos_add_request(&host->pm_qos_req_dma,
+ PM_QOS_CPU_DMA_LATENCY, PM_QOS_DEFAULT_VALUE);
+
ret = msmsdcc_vreg_init(host, true);
if (ret) {
pr_err("%s: msmsdcc_vreg_init() failed (%d)\n", __func__, ret);
@@ -4325,6 +4367,8 @@
msmsdcc_vreg_init(host, false);
clk_disable:
clk_disable(host->clk);
+ if (host->plat->swfi_latency)
+ pm_qos_remove_request(&host->pm_qos_req_dma);
clk_put:
clk_put(host->clk);
pclk_disable:
@@ -4397,6 +4441,9 @@
if (!IS_ERR_OR_NULL(host->dfab_pclk))
clk_put(host->dfab_pclk);
+ if (host->plat->swfi_latency)
+ pm_qos_remove_request(&host->pm_qos_req_dma);
+
msmsdcc_vreg_init(host, false);
if (host->is_dma_mode) {
diff --git a/drivers/mmc/host/msm_sdcc.h b/drivers/mmc/host/msm_sdcc.h
index d128984..ee7a3e5 100644
--- a/drivers/mmc/host/msm_sdcc.h
+++ b/drivers/mmc/host/msm_sdcc.h
@@ -26,6 +26,7 @@
#include <linux/dma-mapping.h>
#include <linux/wakelock.h>
#include <linux/earlysuspend.h>
+#include <linux/pm_qos_params.h>
#include <mach/sps.h>
#include <asm/sizes.h>
@@ -383,6 +384,7 @@
unsigned int dummy_52_sent;
unsigned int sdio_irq_disabled;
+ bool is_resumed;
struct wake_lock sdio_wlock;
struct wake_lock sdio_suspend_wlock;
unsigned int sdcc_suspending;
@@ -395,6 +397,7 @@
bool tuning_needed;
bool sdio_gpio_lpm;
bool irq_wake_enabled;
+ struct pm_qos_request_list pm_qos_req_dma;
};
int msmsdcc_set_pwrsave(struct mmc_host *mmc, int pwrsave);