aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJarkko Lavinen <jarkko.lavinen@nokia.com>2008-03-26 16:09:52 -0400
committerPierre Ossman <drzeus@drzeus.cx>2008-04-18 14:05:31 -0400
commit0807a9b5739a73ba0d0fcd9f36a51794757be881 (patch)
tree5a76ea5d5b62c08e57a6dc4b4201d32b357cc1e8
parent0fb4723d405111a13bb8f04e902eadf14402c7ba (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.c89
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
171void 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
181void 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
167static void mmc_omap_select_slot(struct mmc_omap_slot *slot, int claimed) 196static 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);
182no_claim: 211no_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
198static void mmc_omap_start_request(struct mmc_omap_host *host, 236static void mmc_omap_start_request(struct mmc_omap_host *host,
199 struct mmc_request *req); 237 struct mmc_request *req);
200 238
201static void mmc_omap_release_slot(struct mmc_omap_slot *slot) 239static 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
625static void
626mmc_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 */
580static void 634static void
581mmc_omap_xfer_data(struct mmc_omap_host *host, int write) 635mmc_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
1196exit: 1254exit:
1197 mmc_omap_release_slot(slot); 1255 mmc_omap_release_slot(slot, clk_enabled);
1198} 1256}
1199 1257
1200static const struct mmc_host_ops mmc_omap_ops = { 1258static 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);