diff options
author | Jarkko Lavinen <jarkko.lavinen@nokia.com> | 2008-03-26 16:09:52 -0400 |
---|---|---|
committer | Pierre Ossman <drzeus@drzeus.cx> | 2008-04-18 14:05:31 -0400 |
commit | 0807a9b5739a73ba0d0fcd9f36a51794757be881 (patch) | |
tree | 5a76ea5d5b62c08e57a6dc4b4201d32b357cc1e8 | |
parent | 0fb4723d405111a13bb8f04e902eadf14402c7ba (diff) |
MMC: OMAP: Lazy clock shutdown
MMCA spec says the mmc clock should be kept running for at least
8 cycles after the last RW request. Ensure this with lazy clock
disable after a request, or with an explicit delay before
switching a slot.
Signed-off-by: Jarkko Lavinen <jarkko.lavinen@nokia.com>
Signed-off-by: Pierre Ossman <drzeus@drzeus.cx>
-rw-r--r-- | drivers/mmc/host/omap.c | 89 |
1 files changed, 75 insertions, 14 deletions
diff --git a/drivers/mmc/host/omap.c b/drivers/mmc/host/omap.c index 7cc104ce0f07..3d59c5d81b3d 100644 --- a/drivers/mmc/host/omap.c +++ b/drivers/mmc/host/omap.c | |||
@@ -161,9 +161,38 @@ struct mmc_omap_host { | |||
161 | wait_queue_head_t slot_wq; | 161 | wait_queue_head_t slot_wq; |
162 | int nr_slots; | 162 | int nr_slots; |
163 | 163 | ||
164 | struct timer_list clk_timer; | ||
165 | spinlock_t clk_lock; /* for changing enabled state */ | ||
166 | unsigned int fclk_enabled:1; | ||
167 | |||
164 | struct omap_mmc_platform_data *pdata; | 168 | struct omap_mmc_platform_data *pdata; |
165 | }; | 169 | }; |
166 | 170 | ||
171 | void mmc_omap_fclk_offdelay(struct mmc_omap_slot *slot) | ||
172 | { | ||
173 | unsigned long tick_ns; | ||
174 | |||
175 | if (slot != NULL && slot->host->fclk_enabled && slot->fclk_freq > 0) { | ||
176 | tick_ns = (1000000000 + slot->fclk_freq - 1) / slot->fclk_freq; | ||
177 | ndelay(8 * tick_ns); | ||
178 | } | ||
179 | } | ||
180 | |||
181 | void mmc_omap_fclk_enable(struct mmc_omap_host *host, unsigned int enable) | ||
182 | { | ||
183 | unsigned long flags; | ||
184 | |||
185 | spin_lock_irqsave(&host->clk_lock, flags); | ||
186 | if (host->fclk_enabled != enable) { | ||
187 | host->fclk_enabled = enable; | ||
188 | if (enable) | ||
189 | clk_enable(host->fclk); | ||
190 | else | ||
191 | clk_disable(host->fclk); | ||
192 | } | ||
193 | spin_unlock_irqrestore(&host->clk_lock, flags); | ||
194 | } | ||
195 | |||
167 | static void mmc_omap_select_slot(struct mmc_omap_slot *slot, int claimed) | 196 | static void mmc_omap_select_slot(struct mmc_omap_slot *slot, int claimed) |
168 | { | 197 | { |
169 | struct mmc_omap_host *host = slot->host; | 198 | struct mmc_omap_host *host = slot->host; |
@@ -180,32 +209,49 @@ static void mmc_omap_select_slot(struct mmc_omap_slot *slot, int claimed) | |||
180 | host->mmc = slot->mmc; | 209 | host->mmc = slot->mmc; |
181 | spin_unlock_irqrestore(&host->slot_lock, flags); | 210 | spin_unlock_irqrestore(&host->slot_lock, flags); |
182 | no_claim: | 211 | no_claim: |
183 | clk_enable(host->fclk); | 212 | del_timer(&host->clk_timer); |
213 | if (host->current_slot != slot || !claimed) | ||
214 | mmc_omap_fclk_offdelay(host->current_slot); | ||
215 | |||
184 | if (host->current_slot != slot) { | 216 | if (host->current_slot != slot) { |
217 | OMAP_MMC_WRITE(host, CON, slot->saved_con & 0xFC00); | ||
185 | if (host->pdata->switch_slot != NULL) | 218 | if (host->pdata->switch_slot != NULL) |
186 | host->pdata->switch_slot(mmc_dev(slot->mmc), slot->id); | 219 | host->pdata->switch_slot(mmc_dev(slot->mmc), slot->id); |
187 | host->current_slot = slot; | 220 | host->current_slot = slot; |
188 | } | 221 | } |
189 | 222 | ||
190 | /* Doing the dummy read here seems to work around some bug | 223 | if (claimed) { |
191 | * at least in OMAP24xx silicon where the command would not | 224 | mmc_omap_fclk_enable(host, 1); |
192 | * start after writing the CMD register. Sigh. */ | 225 | |
193 | OMAP_MMC_READ(host, CON); | 226 | /* Doing the dummy read here seems to work around some bug |
227 | * at least in OMAP24xx silicon where the command would not | ||
228 | * start after writing the CMD register. Sigh. */ | ||
229 | OMAP_MMC_READ(host, CON); | ||
194 | 230 | ||
195 | OMAP_MMC_WRITE(host, CON, slot->saved_con); | 231 | OMAP_MMC_WRITE(host, CON, slot->saved_con); |
232 | } else | ||
233 | mmc_omap_fclk_enable(host, 0); | ||
196 | } | 234 | } |
197 | 235 | ||
198 | static void mmc_omap_start_request(struct mmc_omap_host *host, | 236 | static void mmc_omap_start_request(struct mmc_omap_host *host, |
199 | struct mmc_request *req); | 237 | struct mmc_request *req); |
200 | 238 | ||
201 | static void mmc_omap_release_slot(struct mmc_omap_slot *slot) | 239 | static void mmc_omap_release_slot(struct mmc_omap_slot *slot, int clk_enabled) |
202 | { | 240 | { |
203 | struct mmc_omap_host *host = slot->host; | 241 | struct mmc_omap_host *host = slot->host; |
204 | unsigned long flags; | 242 | unsigned long flags; |
205 | int i; | 243 | int i; |
206 | 244 | ||
207 | BUG_ON(slot == NULL || host->mmc == NULL); | 245 | BUG_ON(slot == NULL || host->mmc == NULL); |
208 | clk_disable(host->fclk); | 246 | |
247 | if (clk_enabled) | ||
248 | /* Keeps clock running for at least 8 cycles on valid freq */ | ||
249 | mod_timer(&host->clk_timer, jiffies + HZ/10); | ||
250 | else { | ||
251 | del_timer(&host->clk_timer); | ||
252 | mmc_omap_fclk_offdelay(slot); | ||
253 | mmc_omap_fclk_enable(host, 0); | ||
254 | } | ||
209 | 255 | ||
210 | spin_lock_irqsave(&host->slot_lock, flags); | 256 | spin_lock_irqsave(&host->slot_lock, flags); |
211 | /* Check for any pending requests */ | 257 | /* Check for any pending requests */ |
@@ -373,7 +419,7 @@ mmc_omap_xfer_done(struct mmc_omap_host *host, struct mmc_data *data) | |||
373 | 419 | ||
374 | host->mrq = NULL; | 420 | host->mrq = NULL; |
375 | mmc = host->mmc; | 421 | mmc = host->mmc; |
376 | mmc_omap_release_slot(host->current_slot); | 422 | mmc_omap_release_slot(host->current_slot, 1); |
377 | mmc_request_done(mmc, data->mrq); | 423 | mmc_request_done(mmc, data->mrq); |
378 | return; | 424 | return; |
379 | } | 425 | } |
@@ -507,7 +553,7 @@ mmc_omap_cmd_done(struct mmc_omap_host *host, struct mmc_command *cmd) | |||
507 | mmc_omap_abort_xfer(host, host->data); | 553 | mmc_omap_abort_xfer(host, host->data); |
508 | host->mrq = NULL; | 554 | host->mrq = NULL; |
509 | mmc = host->mmc; | 555 | mmc = host->mmc; |
510 | mmc_omap_release_slot(host->current_slot); | 556 | mmc_omap_release_slot(host->current_slot, 1); |
511 | mmc_request_done(mmc, cmd->mrq); | 557 | mmc_request_done(mmc, cmd->mrq); |
512 | } | 558 | } |
513 | } | 559 | } |
@@ -538,7 +584,7 @@ static void mmc_omap_abort_command(struct work_struct *work) | |||
538 | 584 | ||
539 | host->mrq = NULL; | 585 | host->mrq = NULL; |
540 | mmc = host->mmc; | 586 | mmc = host->mmc; |
541 | mmc_omap_release_slot(host->current_slot); | 587 | mmc_omap_release_slot(host->current_slot, 1); |
542 | mmc_request_done(mmc, cmd->mrq); | 588 | mmc_request_done(mmc, cmd->mrq); |
543 | } else | 589 | } else |
544 | mmc_omap_cmd_done(host, host->cmd); | 590 | mmc_omap_cmd_done(host, host->cmd); |
@@ -576,6 +622,14 @@ mmc_omap_sg_to_buf(struct mmc_omap_host *host) | |||
576 | host->buffer_bytes_left = host->total_bytes_left; | 622 | host->buffer_bytes_left = host->total_bytes_left; |
577 | } | 623 | } |
578 | 624 | ||
625 | static void | ||
626 | mmc_omap_clk_timer(unsigned long data) | ||
627 | { | ||
628 | struct mmc_omap_host *host = (struct mmc_omap_host *) data; | ||
629 | |||
630 | mmc_omap_fclk_enable(host, 0); | ||
631 | } | ||
632 | |||
579 | /* PIO only */ | 633 | /* PIO only */ |
580 | static void | 634 | static void |
581 | mmc_omap_xfer_data(struct mmc_omap_host *host, int write) | 635 | mmc_omap_xfer_data(struct mmc_omap_host *host, int write) |
@@ -1149,14 +1203,16 @@ static void mmc_omap_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) | |||
1149 | struct mmc_omap_slot *slot = mmc_priv(mmc); | 1203 | struct mmc_omap_slot *slot = mmc_priv(mmc); |
1150 | struct mmc_omap_host *host = slot->host; | 1204 | struct mmc_omap_host *host = slot->host; |
1151 | int i, dsor; | 1205 | int i, dsor; |
1152 | 1206 | int clk_enabled; | |
1153 | dsor = mmc_omap_calc_divisor(mmc, ios); | ||
1154 | 1207 | ||
1155 | mmc_omap_select_slot(slot, 0); | 1208 | mmc_omap_select_slot(slot, 0); |
1156 | 1209 | ||
1210 | dsor = mmc_omap_calc_divisor(mmc, ios); | ||
1211 | |||
1157 | if (ios->vdd != slot->vdd) | 1212 | if (ios->vdd != slot->vdd) |
1158 | slot->vdd = ios->vdd; | 1213 | slot->vdd = ios->vdd; |
1159 | 1214 | ||
1215 | clk_enabled = 0; | ||
1160 | switch (ios->power_mode) { | 1216 | switch (ios->power_mode) { |
1161 | case MMC_POWER_OFF: | 1217 | case MMC_POWER_OFF: |
1162 | mmc_omap_set_power(slot, 0, ios->vdd); | 1218 | mmc_omap_set_power(slot, 0, ios->vdd); |
@@ -1166,6 +1222,8 @@ static void mmc_omap_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) | |||
1166 | mmc_omap_set_power(slot, 1, ios->vdd); | 1222 | mmc_omap_set_power(slot, 1, ios->vdd); |
1167 | goto exit; | 1223 | goto exit; |
1168 | case MMC_POWER_ON: | 1224 | case MMC_POWER_ON: |
1225 | mmc_omap_fclk_enable(host, 1); | ||
1226 | clk_enabled = 1; | ||
1169 | dsor |= 1 << 11; | 1227 | dsor |= 1 << 11; |
1170 | break; | 1228 | break; |
1171 | } | 1229 | } |
@@ -1194,7 +1252,7 @@ static void mmc_omap_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) | |||
1194 | } | 1252 | } |
1195 | 1253 | ||
1196 | exit: | 1254 | exit: |
1197 | mmc_omap_release_slot(slot); | 1255 | mmc_omap_release_slot(slot, clk_enabled); |
1198 | } | 1256 | } |
1199 | 1257 | ||
1200 | static const struct mmc_host_ops mmc_omap_ops = { | 1258 | static const struct mmc_host_ops mmc_omap_ops = { |
@@ -1335,6 +1393,9 @@ static int __init mmc_omap_probe(struct platform_device *pdev) | |||
1335 | setup_timer(&host->cmd_abort_timer, mmc_omap_cmd_timer, | 1393 | setup_timer(&host->cmd_abort_timer, mmc_omap_cmd_timer, |
1336 | (unsigned long) host); | 1394 | (unsigned long) host); |
1337 | 1395 | ||
1396 | spin_lock_init(&host->clk_lock); | ||
1397 | setup_timer(&host->clk_timer, mmc_omap_clk_timer, (unsigned long) host); | ||
1398 | |||
1338 | spin_lock_init(&host->dma_lock); | 1399 | spin_lock_init(&host->dma_lock); |
1339 | setup_timer(&host->dma_timer, mmc_omap_dma_timer, (unsigned long) host); | 1400 | setup_timer(&host->dma_timer, mmc_omap_dma_timer, (unsigned long) host); |
1340 | spin_lock_init(&host->slot_lock); | 1401 | spin_lock_init(&host->slot_lock); |