aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/mmc/core/host.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/mmc/core/host.c')
-rw-r--r--drivers/mmc/core/host.c57
1 files changed, 53 insertions, 4 deletions
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);