mmc: msm_sdcc: Add sysfs attribute to update idle timeout value
Add a sysfs attribute to configure the runtime PM idle timer value
In some cases (eg. audio playback) the power consumption drops if
runtime idle PM timeout is set to 10sec. So this sysfs attribute
should be used to configure the idle timeout for SDCC per target.
The value must be greater than 5secs or else will have a major
performance hit.
Change-Id: Ia5a9a81ce46e9c66a325fd1ec1ccd2e0f89a981f
Signed-off-by: Pratibhasagar V <pratibha@codeaurora.org>
diff --git a/drivers/mmc/host/msm_sdcc.c b/drivers/mmc/host/msm_sdcc.c
index d1d8358..d833707 100644
--- a/drivers/mmc/host/msm_sdcc.c
+++ b/drivers/mmc/host/msm_sdcc.c
@@ -4605,6 +4605,35 @@
return count;
}
+static ssize_t
+show_idle_timeout(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct mmc_host *mmc = dev_get_drvdata(dev);
+ struct msmsdcc_host *host = mmc_priv(mmc);
+
+ return snprintf(buf, PAGE_SIZE, "%u (Min 5 sec)\n",
+ host->idle_tout_ms / 1000);
+}
+
+static ssize_t
+store_idle_timeout(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct mmc_host *mmc = dev_get_drvdata(dev);
+ struct msmsdcc_host *host = mmc_priv(mmc);
+ unsigned int long flags;
+ int timeout; /* in secs */
+
+ if (!kstrtou32(buf, 0, &timeout)
+ && (timeout > MSM_MMC_DEFAULT_IDLE_TIMEOUT / 1000)) {
+ spin_lock_irqsave(&host->lock, flags);
+ host->idle_tout_ms = timeout * 1000;
+ spin_unlock_irqrestore(&host->lock, flags);
+ }
+ return count;
+}
+
#ifdef CONFIG_HAS_EARLYSUSPEND
static void msmsdcc_early_suspend(struct early_suspend *h)
{
@@ -5421,6 +5450,7 @@
pm_runtime_enable(&(pdev)->dev);
}
#endif
+ host->idle_tout_ms = MSM_MMC_DEFAULT_IDLE_TIMEOUT;
setup_timer(&host->req_tout_timer, msmsdcc_req_tout_timer_hdlr,
(unsigned long)host);
@@ -5492,8 +5522,19 @@
if (ret)
goto remove_max_bus_bw_file;
}
+ host->idle_timeout.show = show_idle_timeout;
+ host->idle_timeout.store = store_idle_timeout;
+ sysfs_attr_init(&host->idle_timeout.attr);
+ host->idle_timeout.attr.name = "idle_timeout";
+ host->idle_timeout.attr.mode = S_IRUGO | S_IWUSR;
+ ret = device_create_file(&pdev->dev, &host->idle_timeout);
+ if (ret)
+ goto remove_polling_file;
return 0;
+ remove_polling_file:
+ if (!plat->status_irq)
+ device_remove_file(&pdev->dev, &host->polling);
remove_max_bus_bw_file:
device_remove_file(&pdev->dev, &host->max_bus_bw);
platform_irq_free:
@@ -5574,6 +5615,7 @@
device_remove_file(&pdev->dev, &host->max_bus_bw);
if (!plat->status_irq)
device_remove_file(&pdev->dev, &host->polling);
+ device_remove_file(&pdev->dev, &host->idle_timeout);
del_timer_sync(&host->req_tout_timer);
tasklet_kill(&host->dma_tlet);
@@ -5871,7 +5913,7 @@
return 0;
/* Idle timeout is not configurable for now */
- pm_schedule_suspend(dev, MSM_MMC_IDLE_TIMEOUT);
+ pm_schedule_suspend(dev, host->idle_tout_ms);
return -EAGAIN;
}
diff --git a/drivers/mmc/host/msm_sdcc.h b/drivers/mmc/host/msm_sdcc.h
index 655f2b9..cc41c46 100644
--- a/drivers/mmc/host/msm_sdcc.h
+++ b/drivers/mmc/host/msm_sdcc.h
@@ -213,7 +213,7 @@
#define NR_SG 128
-#define MSM_MMC_IDLE_TIMEOUT 5000 /* msecs */
+#define MSM_MMC_DEFAULT_IDLE_TIMEOUT 5000 /* msecs */
#define MSM_MMC_CLK_GATE_DELAY 200 /* msecs */
/* Set the request timeout to 10secs */
@@ -411,9 +411,11 @@
bool sdio_wakeupirq_disabled;
struct mutex clk_mutex;
bool pending_resume;
+ unsigned int idle_tout_ms; /* Timeout in msecs */
struct msmsdcc_msm_bus_vote msm_bus_vote;
struct device_attribute max_bus_bw;
struct device_attribute polling;
+ struct device_attribute idle_timeout;
};
#define MSMSDCC_VERSION_MASK 0xFFFF