aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSujit Reddy Thumma <sthumma@codeaurora.org>2011-11-14 03:23:29 -0500
committerChris Ball <cjb@laptop.org>2012-01-11 23:58:41 -0500
commit597dd9d79cfbbb1636d00a7fd0880355d9b20c41 (patch)
tree12911a3daaa38018d7ca1678fb28bb8c72536b68
parentc59d44739a8519cb7abdcd7cb7fd88f807dec9fd (diff)
mmc: core: Use delayed work in clock gating framework
Current clock gating framework disables the MCI clock as soon as the request is completed and enables it when a request arrives. This aggressive clock gating framework, when enabled, cause following issues: When there are back-to-back requests from the Queue layer, we unnecessarily end up disabling and enabling the clocks between these requests since 8MCLK clock cycles is a very short duration compared to the time delay between back to back requests reaching the MMC layer. This overhead can effect the overall performance depending on how long the clock enable and disable calls take which is platform dependent. For example on some platforms we can have clock control not on the local processor, but on a different subsystem and the time taken to perform the clock enable/disable can add significant overhead. Also if the host controller driver decides to disable the host clock too when mmc_set_ios function is called with ios.clock=0, it adds additional delay and it is highly possible that the next request had already arrived and unnecessarily blocked in enabling the clocks. This is seen frequently when the processor is executing at high speeds and in multi-core platforms thus reduces the overall throughput compared to if clock gating is disabled. Fix this by delaying turning off the clocks by posting request on delayed workqueue. Also cancel the unscheduled pending work, if any, when there is access to card. sysfs entry is provided to tune the delay as needed, default value set to 200ms. Signed-off-by: Sujit Reddy Thumma <sthumma@codeaurora.org> Acked-by: Linus Walleij <linus.walleij@linaro.org> Signed-off-by: Chris Ball <cjb@laptop.org>
-rw-r--r--Documentation/mmc/mmc-dev-attrs.txt10
-rw-r--r--drivers/mmc/core/host.c57
-rw-r--r--include/linux/mmc/host.h4
3 files changed, 66 insertions, 5 deletions
diff --git a/Documentation/mmc/mmc-dev-attrs.txt b/Documentation/mmc/mmc-dev-attrs.txt
index 8898a95b41e5..b0245565875a 100644
--- a/Documentation/mmc/mmc-dev-attrs.txt
+++ b/Documentation/mmc/mmc-dev-attrs.txt
@@ -64,3 +64,13 @@ Note on Erase Size and Preferred Erase Size:
64 size specified by the card. 64 size specified by the card.
65 65
66 "preferred_erase_size" is in bytes. 66 "preferred_erase_size" is in bytes.
67
68SD/MMC/SDIO Clock Gating Attribute
69==================================
70
71Read and write access is provided to following attribute.
72This attribute appears only if CONFIG_MMC_CLKGATE is enabled.
73
74 clkgate_delay Tune the clock gating delay with desired value in milli seconds.
75
76echo <desired delay> > /sys/class/mmc_host/mmcX/clkgate_delay
diff --git a/drivers/mmc/core/host.c b/drivers/mmc/core/host.c
index d31c78b72b0f..817a76039743 100644
--- a/drivers/mmc/core/host.c
+++ b/drivers/mmc/core/host.c
@@ -54,6 +54,31 @@ static DEFINE_IDR(mmc_host_idr);
54static DEFINE_SPINLOCK(mmc_host_lock); 54static DEFINE_SPINLOCK(mmc_host_lock);
55 55
56#ifdef CONFIG_MMC_CLKGATE 56#ifdef CONFIG_MMC_CLKGATE
57static ssize_t clkgate_delay_show(struct device *dev,
58 struct device_attribute *attr, char *buf)
59{
60 struct mmc_host *host = cls_dev_to_mmc_host(dev);
61 return snprintf(buf, PAGE_SIZE, "%lu millisecs\n",
62 host->clkgate_delay);
63}
64
65static ssize_t clkgate_delay_store(struct device *dev,
66 struct device_attribute *attr, const char *buf, size_t count)
67{
68 struct mmc_host *host = cls_dev_to_mmc_host(dev);
69 unsigned long flags, value;
70
71 if (kstrtoul(buf, 0, &value))
72 return -EINVAL;
73
74 spin_lock_irqsave(&host->clk_lock, flags);
75 host->clkgate_delay = value;
76 spin_unlock_irqrestore(&host->clk_lock, flags);
77
78 pr_info("%s: clock gate delay set to %lu ms\n",
79 mmc_hostname(host), value);
80 return count;
81}
57 82
58/* 83/*
59 * Enabling clock gating will make the core call out to the host 84 * Enabling clock gating will make the core call out to the host
@@ -114,7 +139,7 @@ static void mmc_host_clk_gate_delayed(struct mmc_host *host)
114static void mmc_host_clk_gate_work(struct work_struct *work) 139static void mmc_host_clk_gate_work(struct work_struct *work)
115{ 140{
116 struct mmc_host *host = container_of(work, struct mmc_host, 141 struct mmc_host *host = container_of(work, struct mmc_host,
117 clk_gate_work); 142 clk_gate_work.work);
118 143
119 mmc_host_clk_gate_delayed(host); 144 mmc_host_clk_gate_delayed(host);
120} 145}
@@ -131,6 +156,8 @@ void mmc_host_clk_hold(struct mmc_host *host)
131{ 156{
132 unsigned long flags; 157 unsigned long flags;
133 158
159 /* cancel any clock gating work scheduled by mmc_host_clk_release() */
160 cancel_delayed_work_sync(&host->clk_gate_work);
134 mutex_lock(&host->clk_gate_mutex); 161 mutex_lock(&host->clk_gate_mutex);
135 spin_lock_irqsave(&host->clk_lock, flags); 162 spin_lock_irqsave(&host->clk_lock, flags);
136 if (host->clk_gated) { 163 if (host->clk_gated) {
@@ -180,7 +207,8 @@ void mmc_host_clk_release(struct mmc_host *host)
180 host->clk_requests--; 207 host->clk_requests--;
181 if (mmc_host_may_gate_card(host->card) && 208 if (mmc_host_may_gate_card(host->card) &&
182 !host->clk_requests) 209 !host->clk_requests)
183 queue_work(system_nrt_wq, &host->clk_gate_work); 210 queue_delayed_work(system_nrt_wq, &host->clk_gate_work,
211 msecs_to_jiffies(host->clkgate_delay));
184 spin_unlock_irqrestore(&host->clk_lock, flags); 212 spin_unlock_irqrestore(&host->clk_lock, flags);
185} 213}
186 214
@@ -213,8 +241,13 @@ static inline void mmc_host_clk_init(struct mmc_host *host)
213 host->clk_requests = 0; 241 host->clk_requests = 0;
214 /* Hold MCI clock for 8 cycles by default */ 242 /* Hold MCI clock for 8 cycles by default */
215 host->clk_delay = 8; 243 host->clk_delay = 8;
244 /*
245 * Default clock gating delay is 200ms.
246 * This value can be tuned by writing into sysfs entry.
247 */
248 host->clkgate_delay = 200;
216 host->clk_gated = false; 249 host->clk_gated = false;
217 INIT_WORK(&host->clk_gate_work, mmc_host_clk_gate_work); 250 INIT_DELAYED_WORK(&host->clk_gate_work, mmc_host_clk_gate_work);
218 spin_lock_init(&host->clk_lock); 251 spin_lock_init(&host->clk_lock);
219 mutex_init(&host->clk_gate_mutex); 252 mutex_init(&host->clk_gate_mutex);
220} 253}
@@ -229,7 +262,7 @@ static inline void mmc_host_clk_exit(struct mmc_host *host)
229 * Wait for any outstanding gate and then make sure we're 262 * Wait for any outstanding gate and then make sure we're
230 * ungated before exiting. 263 * ungated before exiting.
231 */ 264 */
232 if (cancel_work_sync(&host->clk_gate_work)) 265 if (cancel_delayed_work_sync(&host->clk_gate_work))
233 mmc_host_clk_gate_delayed(host); 266 mmc_host_clk_gate_delayed(host);
234 if (host->clk_gated) 267 if (host->clk_gated)
235 mmc_host_clk_hold(host); 268 mmc_host_clk_hold(host);
@@ -237,6 +270,17 @@ static inline void mmc_host_clk_exit(struct mmc_host *host)
237 WARN_ON(host->clk_requests > 1); 270 WARN_ON(host->clk_requests > 1);
238} 271}
239 272
273static inline void mmc_host_clk_sysfs_init(struct mmc_host *host)
274{
275 host->clkgate_delay_attr.show = clkgate_delay_show;
276 host->clkgate_delay_attr.store = clkgate_delay_store;
277 sysfs_attr_init(&host->clkgate_delay_attr.attr);
278 host->clkgate_delay_attr.attr.name = "clkgate_delay";
279 host->clkgate_delay_attr.attr.mode = S_IRUGO | S_IWUSR;
280 if (device_create_file(&host->class_dev, &host->clkgate_delay_attr))
281 pr_err("%s: Failed to create clkgate_delay sysfs entry\n",
282 mmc_hostname(host));
283}
240#else 284#else
241 285
242static inline void mmc_host_clk_init(struct mmc_host *host) 286static inline void mmc_host_clk_init(struct mmc_host *host)
@@ -247,6 +291,10 @@ static inline void mmc_host_clk_exit(struct mmc_host *host)
247{ 291{
248} 292}
249 293
294static inline void mmc_host_clk_sysfs_init(struct mmc_host *host)
295{
296}
297
250#endif 298#endif
251 299
252/** 300/**
@@ -335,6 +383,7 @@ int mmc_add_host(struct mmc_host *host)
335#ifdef CONFIG_DEBUG_FS 383#ifdef CONFIG_DEBUG_FS
336 mmc_add_host_debugfs(host); 384 mmc_add_host_debugfs(host);
337#endif 385#endif
386 mmc_host_clk_sysfs_init(host);
338 387
339 mmc_start_host(host); 388 mmc_start_host(host);
340 register_pm_notifier(&host->pm_notify); 389 register_pm_notifier(&host->pm_notify);
diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h
index cea064f73514..706f72279a17 100644
--- a/include/linux/mmc/host.h
+++ b/include/linux/mmc/host.h
@@ -253,10 +253,12 @@ struct mmc_host {
253 int clk_requests; /* internal reference counter */ 253 int clk_requests; /* internal reference counter */
254 unsigned int clk_delay; /* number of MCI clk hold cycles */ 254 unsigned int clk_delay; /* number of MCI clk hold cycles */
255 bool clk_gated; /* clock gated */ 255 bool clk_gated; /* clock gated */
256 struct work_struct clk_gate_work; /* delayed clock gate */ 256 struct delayed_work clk_gate_work; /* delayed clock gate */
257 unsigned int clk_old; /* old clock value cache */ 257 unsigned int clk_old; /* old clock value cache */
258 spinlock_t clk_lock; /* lock for clk fields */ 258 spinlock_t clk_lock; /* lock for clk fields */
259 struct mutex clk_gate_mutex; /* mutex for clock gating */ 259 struct mutex clk_gate_mutex; /* mutex for clock gating */
260 struct device_attribute clkgate_delay_attr;
261 unsigned long clkgate_delay;
260#endif 262#endif
261 263
262 /* host specific block data */ 264 /* host specific block data */