diff options
Diffstat (limited to 'drivers/mmc/core/host.c')
-rw-r--r-- | drivers/mmc/core/host.c | 57 |
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); | |||
54 | static DEFINE_SPINLOCK(mmc_host_lock); | 54 | static DEFINE_SPINLOCK(mmc_host_lock); |
55 | 55 | ||
56 | #ifdef CONFIG_MMC_CLKGATE | 56 | #ifdef CONFIG_MMC_CLKGATE |
57 | static 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 | |||
65 | static 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) | |||
114 | static void mmc_host_clk_gate_work(struct work_struct *work) | 139 | static 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 | ||
273 | static 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 | ||
242 | static inline void mmc_host_clk_init(struct mmc_host *host) | 286 | static 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 | ||
294 | static 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); |