diff options
-rw-r--r-- | drivers/mmc/core/Kconfig | 11 | ||||
-rw-r--r-- | drivers/mmc/core/core.c | 62 | ||||
-rw-r--r-- | drivers/mmc/core/core.h | 3 | ||||
-rw-r--r-- | drivers/mmc/core/debugfs.c | 5 | ||||
-rw-r--r-- | drivers/mmc/core/host.c | 205 | ||||
-rw-r--r-- | drivers/mmc/core/host.h | 21 | ||||
-rw-r--r-- | include/linux/mmc/host.h | 10 |
7 files changed, 315 insertions, 2 deletions
diff --git a/drivers/mmc/core/Kconfig b/drivers/mmc/core/Kconfig index bb22ffd76ef8..ef103871517f 100644 --- a/drivers/mmc/core/Kconfig +++ b/drivers/mmc/core/Kconfig | |||
@@ -16,3 +16,14 @@ config MMC_UNSAFE_RESUME | |||
16 | 16 | ||
17 | This option sets a default which can be overridden by the | 17 | This option sets a default which can be overridden by the |
18 | module parameter "removable=0" or "removable=1". | 18 | module parameter "removable=0" or "removable=1". |
19 | |||
20 | config MMC_CLKGATE | ||
21 | bool "MMC host clock gating (EXPERIMENTAL)" | ||
22 | depends on EXPERIMENTAL | ||
23 | help | ||
24 | This will attempt to aggressively gate the clock to the MMC card. | ||
25 | This is done to save power due to gating off the logic and bus | ||
26 | noise when the MMC card is not in use. Your host driver has to | ||
27 | support handling this in order for it to be of any use. | ||
28 | |||
29 | If unsure, say N. | ||
diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c index a3a780faf85a..722af2dce3bb 100644 --- a/drivers/mmc/core/core.c +++ b/drivers/mmc/core/core.c | |||
@@ -130,6 +130,8 @@ void mmc_request_done(struct mmc_host *host, struct mmc_request *mrq) | |||
130 | 130 | ||
131 | if (mrq->done) | 131 | if (mrq->done) |
132 | mrq->done(mrq); | 132 | mrq->done(mrq); |
133 | |||
134 | mmc_host_clk_gate(host); | ||
133 | } | 135 | } |
134 | } | 136 | } |
135 | 137 | ||
@@ -190,6 +192,7 @@ mmc_start_request(struct mmc_host *host, struct mmc_request *mrq) | |||
190 | mrq->stop->mrq = mrq; | 192 | mrq->stop->mrq = mrq; |
191 | } | 193 | } |
192 | } | 194 | } |
195 | mmc_host_clk_ungate(host); | ||
193 | host->ops->request(host, mrq); | 196 | host->ops->request(host, mrq); |
194 | } | 197 | } |
195 | 198 | ||
@@ -296,7 +299,7 @@ void mmc_set_data_timeout(struct mmc_data *data, const struct mmc_card *card) | |||
296 | 299 | ||
297 | timeout_us = data->timeout_ns / 1000; | 300 | timeout_us = data->timeout_ns / 1000; |
298 | timeout_us += data->timeout_clks * 1000 / | 301 | timeout_us += data->timeout_clks * 1000 / |
299 | (card->host->ios.clock / 1000); | 302 | (mmc_host_clk_rate(card->host) / 1000); |
300 | 303 | ||
301 | if (data->flags & MMC_DATA_WRITE) | 304 | if (data->flags & MMC_DATA_WRITE) |
302 | /* | 305 | /* |
@@ -614,6 +617,8 @@ static inline void mmc_set_ios(struct mmc_host *host) | |||
614 | ios->power_mode, ios->chip_select, ios->vdd, | 617 | ios->power_mode, ios->chip_select, ios->vdd, |
615 | ios->bus_width, ios->timing); | 618 | ios->bus_width, ios->timing); |
616 | 619 | ||
620 | if (ios->clock > 0) | ||
621 | mmc_set_ungated(host); | ||
617 | host->ops->set_ios(host, ios); | 622 | host->ops->set_ios(host, ios); |
618 | } | 623 | } |
619 | 624 | ||
@@ -641,6 +646,61 @@ void mmc_set_clock(struct mmc_host *host, unsigned int hz) | |||
641 | mmc_set_ios(host); | 646 | mmc_set_ios(host); |
642 | } | 647 | } |
643 | 648 | ||
649 | #ifdef CONFIG_MMC_CLKGATE | ||
650 | /* | ||
651 | * This gates the clock by setting it to 0 Hz. | ||
652 | */ | ||
653 | void mmc_gate_clock(struct mmc_host *host) | ||
654 | { | ||
655 | unsigned long flags; | ||
656 | |||
657 | spin_lock_irqsave(&host->clk_lock, flags); | ||
658 | host->clk_old = host->ios.clock; | ||
659 | host->ios.clock = 0; | ||
660 | host->clk_gated = true; | ||
661 | spin_unlock_irqrestore(&host->clk_lock, flags); | ||
662 | mmc_set_ios(host); | ||
663 | } | ||
664 | |||
665 | /* | ||
666 | * This restores the clock from gating by using the cached | ||
667 | * clock value. | ||
668 | */ | ||
669 | void mmc_ungate_clock(struct mmc_host *host) | ||
670 | { | ||
671 | /* | ||
672 | * We should previously have gated the clock, so the clock shall | ||
673 | * be 0 here! The clock may however be 0 during initialization, | ||
674 | * when some request operations are performed before setting | ||
675 | * the frequency. When ungate is requested in that situation | ||
676 | * we just ignore the call. | ||
677 | */ | ||
678 | if (host->clk_old) { | ||
679 | BUG_ON(host->ios.clock); | ||
680 | /* This call will also set host->clk_gated to false */ | ||
681 | mmc_set_clock(host, host->clk_old); | ||
682 | } | ||
683 | } | ||
684 | |||
685 | void mmc_set_ungated(struct mmc_host *host) | ||
686 | { | ||
687 | unsigned long flags; | ||
688 | |||
689 | /* | ||
690 | * We've been given a new frequency while the clock is gated, | ||
691 | * so make sure we regard this as ungating it. | ||
692 | */ | ||
693 | spin_lock_irqsave(&host->clk_lock, flags); | ||
694 | host->clk_gated = false; | ||
695 | spin_unlock_irqrestore(&host->clk_lock, flags); | ||
696 | } | ||
697 | |||
698 | #else | ||
699 | void mmc_set_ungated(struct mmc_host *host) | ||
700 | { | ||
701 | } | ||
702 | #endif | ||
703 | |||
644 | /* | 704 | /* |
645 | * Change the bus mode (open drain/push-pull) of a host. | 705 | * Change the bus mode (open drain/push-pull) of a host. |
646 | */ | 706 | */ |
diff --git a/drivers/mmc/core/core.h b/drivers/mmc/core/core.h index 77240cd11bcf..026c975b99a9 100644 --- a/drivers/mmc/core/core.h +++ b/drivers/mmc/core/core.h | |||
@@ -33,6 +33,9 @@ void mmc_init_erase(struct mmc_card *card); | |||
33 | 33 | ||
34 | void mmc_set_chip_select(struct mmc_host *host, int mode); | 34 | void mmc_set_chip_select(struct mmc_host *host, int mode); |
35 | void mmc_set_clock(struct mmc_host *host, unsigned int hz); | 35 | void mmc_set_clock(struct mmc_host *host, unsigned int hz); |
36 | void mmc_gate_clock(struct mmc_host *host); | ||
37 | void mmc_ungate_clock(struct mmc_host *host); | ||
38 | void mmc_set_ungated(struct mmc_host *host); | ||
36 | void mmc_set_bus_mode(struct mmc_host *host, unsigned int mode); | 39 | void mmc_set_bus_mode(struct mmc_host *host, unsigned int mode); |
37 | void mmc_set_bus_width(struct mmc_host *host, unsigned int width); | 40 | void mmc_set_bus_width(struct mmc_host *host, unsigned int width); |
38 | void mmc_set_bus_width_ddr(struct mmc_host *host, unsigned int width, | 41 | void mmc_set_bus_width_ddr(struct mmc_host *host, unsigned int width, |
diff --git a/drivers/mmc/core/debugfs.c b/drivers/mmc/core/debugfs.c index eed1405fd742..998797ed67a6 100644 --- a/drivers/mmc/core/debugfs.c +++ b/drivers/mmc/core/debugfs.c | |||
@@ -183,6 +183,11 @@ void mmc_add_host_debugfs(struct mmc_host *host) | |||
183 | &mmc_clock_fops)) | 183 | &mmc_clock_fops)) |
184 | goto err_node; | 184 | goto err_node; |
185 | 185 | ||
186 | #ifdef CONFIG_MMC_CLKGATE | ||
187 | if (!debugfs_create_u32("clk_delay", (S_IRUSR | S_IWUSR), | ||
188 | root, &host->clk_delay)) | ||
189 | goto err_node; | ||
190 | #endif | ||
186 | return; | 191 | return; |
187 | 192 | ||
188 | err_node: | 193 | err_node: |
diff --git a/drivers/mmc/core/host.c b/drivers/mmc/core/host.c index 10b8af27e03a..92e33703e437 100644 --- a/drivers/mmc/core/host.c +++ b/drivers/mmc/core/host.c | |||
@@ -3,6 +3,7 @@ | |||
3 | * | 3 | * |
4 | * Copyright (C) 2003 Russell King, All Rights Reserved. | 4 | * Copyright (C) 2003 Russell King, All Rights Reserved. |
5 | * Copyright (C) 2007-2008 Pierre Ossman | 5 | * Copyright (C) 2007-2008 Pierre Ossman |
6 | * Copyright (C) 2010 Linus Walleij | ||
6 | * | 7 | * |
7 | * This program is free software; you can redistribute it and/or modify | 8 | * This program is free software; you can redistribute it and/or modify |
8 | * it under the terms of the GNU General Public License version 2 as | 9 | * it under the terms of the GNU General Public License version 2 as |
@@ -20,6 +21,7 @@ | |||
20 | #include <linux/suspend.h> | 21 | #include <linux/suspend.h> |
21 | 22 | ||
22 | #include <linux/mmc/host.h> | 23 | #include <linux/mmc/host.h> |
24 | #include <linux/mmc/card.h> | ||
23 | 25 | ||
24 | #include "core.h" | 26 | #include "core.h" |
25 | #include "host.h" | 27 | #include "host.h" |
@@ -50,6 +52,204 @@ void mmc_unregister_host_class(void) | |||
50 | static DEFINE_IDR(mmc_host_idr); | 52 | static DEFINE_IDR(mmc_host_idr); |
51 | static DEFINE_SPINLOCK(mmc_host_lock); | 53 | static DEFINE_SPINLOCK(mmc_host_lock); |
52 | 54 | ||
55 | #ifdef CONFIG_MMC_CLKGATE | ||
56 | |||
57 | /* | ||
58 | * Enabling clock gating will make the core call out to the host | ||
59 | * once up and once down when it performs a request or card operation | ||
60 | * intermingled in any fashion. The driver will see this through | ||
61 | * set_ios() operations with ios.clock field set to 0 to gate (disable) | ||
62 | * the block clock, and to the old frequency to enable it again. | ||
63 | */ | ||
64 | static void mmc_host_clk_gate_delayed(struct mmc_host *host) | ||
65 | { | ||
66 | unsigned long tick_ns; | ||
67 | unsigned long freq = host->ios.clock; | ||
68 | unsigned long flags; | ||
69 | |||
70 | if (!freq) { | ||
71 | pr_debug("%s: frequency set to 0 in disable function, " | ||
72 | "this means the clock is already disabled.\n", | ||
73 | mmc_hostname(host)); | ||
74 | return; | ||
75 | } | ||
76 | /* | ||
77 | * New requests may have appeared while we were scheduling, | ||
78 | * then there is no reason to delay the check before | ||
79 | * clk_disable(). | ||
80 | */ | ||
81 | spin_lock_irqsave(&host->clk_lock, flags); | ||
82 | |||
83 | /* | ||
84 | * Delay n bus cycles (at least 8 from MMC spec) before attempting | ||
85 | * to disable the MCI block clock. The reference count may have | ||
86 | * gone up again after this delay due to rescheduling! | ||
87 | */ | ||
88 | if (!host->clk_requests) { | ||
89 | spin_unlock_irqrestore(&host->clk_lock, flags); | ||
90 | tick_ns = DIV_ROUND_UP(1000000000, freq); | ||
91 | ndelay(host->clk_delay * tick_ns); | ||
92 | } else { | ||
93 | /* New users appeared while waiting for this work */ | ||
94 | spin_unlock_irqrestore(&host->clk_lock, flags); | ||
95 | return; | ||
96 | } | ||
97 | mutex_lock(&host->clk_gate_mutex); | ||
98 | spin_lock_irqsave(&host->clk_lock, flags); | ||
99 | if (!host->clk_requests) { | ||
100 | spin_unlock_irqrestore(&host->clk_lock, flags); | ||
101 | /* This will set host->ios.clock to 0 */ | ||
102 | mmc_gate_clock(host); | ||
103 | spin_lock_irqsave(&host->clk_lock, flags); | ||
104 | pr_debug("%s: gated MCI clock\n", mmc_hostname(host)); | ||
105 | } | ||
106 | spin_unlock_irqrestore(&host->clk_lock, flags); | ||
107 | mutex_unlock(&host->clk_gate_mutex); | ||
108 | } | ||
109 | |||
110 | /* | ||
111 | * Internal work. Work to disable the clock at some later point. | ||
112 | */ | ||
113 | static void mmc_host_clk_gate_work(struct work_struct *work) | ||
114 | { | ||
115 | struct mmc_host *host = container_of(work, struct mmc_host, | ||
116 | clk_gate_work); | ||
117 | |||
118 | mmc_host_clk_gate_delayed(host); | ||
119 | } | ||
120 | |||
121 | /** | ||
122 | * mmc_host_clk_ungate - ungate hardware MCI clocks | ||
123 | * @host: host to ungate. | ||
124 | * | ||
125 | * Makes sure the host ios.clock is restored to a non-zero value | ||
126 | * past this call. Increase clock reference count and ungate clock | ||
127 | * if we're the first user. | ||
128 | */ | ||
129 | void mmc_host_clk_ungate(struct mmc_host *host) | ||
130 | { | ||
131 | unsigned long flags; | ||
132 | |||
133 | mutex_lock(&host->clk_gate_mutex); | ||
134 | spin_lock_irqsave(&host->clk_lock, flags); | ||
135 | if (host->clk_gated) { | ||
136 | spin_unlock_irqrestore(&host->clk_lock, flags); | ||
137 | mmc_ungate_clock(host); | ||
138 | spin_lock_irqsave(&host->clk_lock, flags); | ||
139 | pr_debug("%s: ungated MCI clock\n", mmc_hostname(host)); | ||
140 | } | ||
141 | host->clk_requests++; | ||
142 | spin_unlock_irqrestore(&host->clk_lock, flags); | ||
143 | mutex_unlock(&host->clk_gate_mutex); | ||
144 | } | ||
145 | |||
146 | /** | ||
147 | * mmc_host_may_gate_card - check if this card may be gated | ||
148 | * @card: card to check. | ||
149 | */ | ||
150 | static bool mmc_host_may_gate_card(struct mmc_card *card) | ||
151 | { | ||
152 | /* If there is no card we may gate it */ | ||
153 | if (!card) | ||
154 | return true; | ||
155 | /* | ||
156 | * Don't gate SDIO cards! These need to be clocked at all times | ||
157 | * since they may be independent systems generating interrupts | ||
158 | * and other events. The clock requests counter from the core will | ||
159 | * go down to zero since the core does not need it, but we will not | ||
160 | * gate the clock, because there is somebody out there that may still | ||
161 | * be using it. | ||
162 | */ | ||
163 | if (mmc_card_sdio(card)) | ||
164 | return false; | ||
165 | |||
166 | return true; | ||
167 | } | ||
168 | |||
169 | /** | ||
170 | * mmc_host_clk_gate - gate off hardware MCI clocks | ||
171 | * @host: host to gate. | ||
172 | * | ||
173 | * Calls the host driver with ios.clock set to zero as often as possible | ||
174 | * in order to gate off hardware MCI clocks. Decrease clock reference | ||
175 | * count and schedule disabling of clock. | ||
176 | */ | ||
177 | void mmc_host_clk_gate(struct mmc_host *host) | ||
178 | { | ||
179 | unsigned long flags; | ||
180 | |||
181 | spin_lock_irqsave(&host->clk_lock, flags); | ||
182 | host->clk_requests--; | ||
183 | if (mmc_host_may_gate_card(host->card) && | ||
184 | !host->clk_requests) | ||
185 | schedule_work(&host->clk_gate_work); | ||
186 | spin_unlock_irqrestore(&host->clk_lock, flags); | ||
187 | } | ||
188 | |||
189 | /** | ||
190 | * mmc_host_clk_rate - get current clock frequency setting | ||
191 | * @host: host to get the clock frequency for. | ||
192 | * | ||
193 | * Returns current clock frequency regardless of gating. | ||
194 | */ | ||
195 | unsigned int mmc_host_clk_rate(struct mmc_host *host) | ||
196 | { | ||
197 | unsigned long freq; | ||
198 | unsigned long flags; | ||
199 | |||
200 | spin_lock_irqsave(&host->clk_lock, flags); | ||
201 | if (host->clk_gated) | ||
202 | freq = host->clk_old; | ||
203 | else | ||
204 | freq = host->ios.clock; | ||
205 | spin_unlock_irqrestore(&host->clk_lock, flags); | ||
206 | return freq; | ||
207 | } | ||
208 | |||
209 | /** | ||
210 | * mmc_host_clk_init - set up clock gating code | ||
211 | * @host: host with potential clock to control | ||
212 | */ | ||
213 | static inline void mmc_host_clk_init(struct mmc_host *host) | ||
214 | { | ||
215 | host->clk_requests = 0; | ||
216 | /* Hold MCI clock for 8 cycles by default */ | ||
217 | host->clk_delay = 8; | ||
218 | host->clk_gated = false; | ||
219 | INIT_WORK(&host->clk_gate_work, mmc_host_clk_gate_work); | ||
220 | spin_lock_init(&host->clk_lock); | ||
221 | mutex_init(&host->clk_gate_mutex); | ||
222 | } | ||
223 | |||
224 | /** | ||
225 | * mmc_host_clk_exit - shut down clock gating code | ||
226 | * @host: host with potential clock to control | ||
227 | */ | ||
228 | static inline void mmc_host_clk_exit(struct mmc_host *host) | ||
229 | { | ||
230 | /* | ||
231 | * Wait for any outstanding gate and then make sure we're | ||
232 | * ungated before exiting. | ||
233 | */ | ||
234 | if (cancel_work_sync(&host->clk_gate_work)) | ||
235 | mmc_host_clk_gate_delayed(host); | ||
236 | if (host->clk_gated) | ||
237 | mmc_host_clk_ungate(host); | ||
238 | BUG_ON(host->clk_requests > 0); | ||
239 | } | ||
240 | |||
241 | #else | ||
242 | |||
243 | static inline void mmc_host_clk_init(struct mmc_host *host) | ||
244 | { | ||
245 | } | ||
246 | |||
247 | static inline void mmc_host_clk_exit(struct mmc_host *host) | ||
248 | { | ||
249 | } | ||
250 | |||
251 | #endif | ||
252 | |||
53 | /** | 253 | /** |
54 | * mmc_alloc_host - initialise the per-host structure. | 254 | * mmc_alloc_host - initialise the per-host structure. |
55 | * @extra: sizeof private data structure | 255 | * @extra: sizeof private data structure |
@@ -82,6 +282,8 @@ struct mmc_host *mmc_alloc_host(int extra, struct device *dev) | |||
82 | host->class_dev.class = &mmc_host_class; | 282 | host->class_dev.class = &mmc_host_class; |
83 | device_initialize(&host->class_dev); | 283 | device_initialize(&host->class_dev); |
84 | 284 | ||
285 | mmc_host_clk_init(host); | ||
286 | |||
85 | spin_lock_init(&host->lock); | 287 | spin_lock_init(&host->lock); |
86 | init_waitqueue_head(&host->wq); | 288 | init_waitqueue_head(&host->wq); |
87 | INIT_DELAYED_WORK(&host->detect, mmc_rescan); | 289 | INIT_DELAYED_WORK(&host->detect, mmc_rescan); |
@@ -163,6 +365,8 @@ void mmc_remove_host(struct mmc_host *host) | |||
163 | device_del(&host->class_dev); | 365 | device_del(&host->class_dev); |
164 | 366 | ||
165 | led_trigger_unregister_simple(host->led); | 367 | led_trigger_unregister_simple(host->led); |
368 | |||
369 | mmc_host_clk_exit(host); | ||
166 | } | 370 | } |
167 | 371 | ||
168 | EXPORT_SYMBOL(mmc_remove_host); | 372 | EXPORT_SYMBOL(mmc_remove_host); |
@@ -183,4 +387,3 @@ void mmc_free_host(struct mmc_host *host) | |||
183 | } | 387 | } |
184 | 388 | ||
185 | EXPORT_SYMBOL(mmc_free_host); | 389 | EXPORT_SYMBOL(mmc_free_host); |
186 | |||
diff --git a/drivers/mmc/core/host.h b/drivers/mmc/core/host.h index 8c87e1109a34..de199f911928 100644 --- a/drivers/mmc/core/host.h +++ b/drivers/mmc/core/host.h | |||
@@ -10,10 +10,31 @@ | |||
10 | */ | 10 | */ |
11 | #ifndef _MMC_CORE_HOST_H | 11 | #ifndef _MMC_CORE_HOST_H |
12 | #define _MMC_CORE_HOST_H | 12 | #define _MMC_CORE_HOST_H |
13 | #include <linux/mmc/host.h> | ||
13 | 14 | ||
14 | int mmc_register_host_class(void); | 15 | int mmc_register_host_class(void); |
15 | void mmc_unregister_host_class(void); | 16 | void mmc_unregister_host_class(void); |
16 | 17 | ||
18 | #ifdef CONFIG_MMC_CLKGATE | ||
19 | void mmc_host_clk_ungate(struct mmc_host *host); | ||
20 | void mmc_host_clk_gate(struct mmc_host *host); | ||
21 | unsigned int mmc_host_clk_rate(struct mmc_host *host); | ||
22 | |||
23 | #else | ||
24 | static inline void mmc_host_clk_ungate(struct mmc_host *host) | ||
25 | { | ||
26 | } | ||
27 | |||
28 | static inline void mmc_host_clk_gate(struct mmc_host *host) | ||
29 | { | ||
30 | } | ||
31 | |||
32 | static inline unsigned int mmc_host_clk_rate(struct mmc_host *host) | ||
33 | { | ||
34 | return host->ios.clock; | ||
35 | } | ||
36 | #endif | ||
37 | |||
17 | void mmc_host_deeper_disable(struct work_struct *work); | 38 | void mmc_host_deeper_disable(struct work_struct *work); |
18 | 39 | ||
19 | #endif | 40 | #endif |
diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h index 30f6fad99a58..381c77fd4dca 100644 --- a/include/linux/mmc/host.h +++ b/include/linux/mmc/host.h | |||
@@ -172,6 +172,16 @@ struct mmc_host { | |||
172 | 172 | ||
173 | mmc_pm_flag_t pm_caps; /* supported pm features */ | 173 | mmc_pm_flag_t pm_caps; /* supported pm features */ |
174 | 174 | ||
175 | #ifdef CONFIG_MMC_CLKGATE | ||
176 | int clk_requests; /* internal reference counter */ | ||
177 | unsigned int clk_delay; /* number of MCI clk hold cycles */ | ||
178 | bool clk_gated; /* clock gated */ | ||
179 | struct work_struct clk_gate_work; /* delayed clock gate */ | ||
180 | unsigned int clk_old; /* old clock value cache */ | ||
181 | spinlock_t clk_lock; /* lock for clk fields */ | ||
182 | struct mutex clk_gate_mutex; /* mutex for clock gating */ | ||
183 | #endif | ||
184 | |||
175 | /* host specific block data */ | 185 | /* host specific block data */ |
176 | unsigned int max_seg_size; /* see blk_queue_max_segment_size */ | 186 | unsigned int max_seg_size; /* see blk_queue_max_segment_size */ |
177 | unsigned short max_segs; /* see blk_queue_max_segments */ | 187 | unsigned short max_segs; /* see blk_queue_max_segments */ |