aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJarkko Lavinen <jarkko.lavinen@nokia.com>2008-03-26 16:09:29 -0400
committerPierre Ossman <drzeus@drzeus.cx>2008-04-18 14:05:30 -0400
commiteb1860bccd01a75b20fd7298af89b9cbda2202ba (patch)
treedb5beda40e127efe0ec8b02c97e360dcfaf712de
parent2a50b8889b707b9c7bcd09c0ae4cbcef7dca4e29 (diff)
MMC: OMAP: Abort stuck commands
When a card is removed while it is being accessed, a command can get stuck so that no timeout or end of command interrupt ever occurs. The command getting stuck is almost always CDM12, but also the other commands can get stuck. Catch a stuck command with a timer and try sending the initialization stream until the controller starts running again and responds with the end of command status. Signed-off-by: Jarkko Lavinen <jarkko.lavinen@nokia.com> Signed-off-by: Carlos Eduardo Aguiar <carlos.aguiar@indt.org.br> Signed-off-by: Tony Lindgren <tony@atomide.com> Signed-off-by: Pierre Ossman <drzeus@drzeus.cx>
-rw-r--r--drivers/mmc/host/omap.c93
1 files changed, 83 insertions, 10 deletions
diff --git a/drivers/mmc/host/omap.c b/drivers/mmc/host/omap.c
index 535499f11e7c..a1dfa74c50ab 100644
--- a/drivers/mmc/host/omap.c
+++ b/drivers/mmc/host/omap.c
@@ -134,6 +134,9 @@ struct mmc_omap_host {
134 unsigned char bus_mode; 134 unsigned char bus_mode;
135 unsigned char hw_bus_mode; 135 unsigned char hw_bus_mode;
136 136
137 struct work_struct cmd_abort;
138 struct timer_list cmd_timer;
139
137 unsigned int sg_len; 140 unsigned int sg_len;
138 int sg_idx; 141 int sg_idx;
139 u16 * buffer; 142 u16 * buffer;
@@ -314,6 +317,8 @@ mmc_omap_start_command(struct mmc_omap_host *host, struct mmc_command *cmd)
314 if (host->data && !(host->data->flags & MMC_DATA_WRITE)) 317 if (host->data && !(host->data->flags & MMC_DATA_WRITE))
315 cmdreg |= 1 << 15; 318 cmdreg |= 1 << 15;
316 319
320 mod_timer(&host->cmd_timer, jiffies + HZ/2);
321
317 OMAP_MMC_WRITE(host, CTO, 200); 322 OMAP_MMC_WRITE(host, CTO, 200);
318 OMAP_MMC_WRITE(host, ARGL, cmd->arg & 0xffff); 323 OMAP_MMC_WRITE(host, ARGL, cmd->arg & 0xffff);
319 OMAP_MMC_WRITE(host, ARGH, cmd->arg >> 16); 324 OMAP_MMC_WRITE(host, ARGH, cmd->arg >> 16);
@@ -373,9 +378,37 @@ mmc_omap_xfer_done(struct mmc_omap_host *host, struct mmc_data *data)
373} 378}
374 379
375static void 380static void
381mmc_omap_send_abort(struct mmc_omap_host *host)
382{
383 struct mmc_omap_slot *slot = host->current_slot;
384 unsigned int restarts, passes, timeout;
385 u16 stat = 0;
386
387 /* Sending abort takes 80 clocks. Have some extra and round up */
388 timeout = (120*1000000 + slot->fclk_freq - 1)/slot->fclk_freq;
389 restarts = 0;
390 while (restarts < 10000) {
391 OMAP_MMC_WRITE(host, STAT, 0xFFFF);
392 OMAP_MMC_WRITE(host, CMD, (3 << 12) | (1 << 7));
393
394 passes = 0;
395 while (passes < timeout) {
396 stat = OMAP_MMC_READ(host, STAT);
397 if (stat & OMAP_MMC_STAT_END_OF_CMD)
398 goto out;
399 udelay(1);
400 passes++;
401 }
402
403 restarts++;
404 }
405out:
406 OMAP_MMC_WRITE(host, STAT, stat);
407}
408
409static void
376mmc_omap_abort_xfer(struct mmc_omap_host *host, struct mmc_data *data) 410mmc_omap_abort_xfer(struct mmc_omap_host *host, struct mmc_data *data)
377{ 411{
378 int loops;
379 u16 ie; 412 u16 ie;
380 413
381 if (host->dma_in_use) 414 if (host->dma_in_use)
@@ -386,16 +419,8 @@ mmc_omap_abort_xfer(struct mmc_omap_host *host, struct mmc_data *data)
386 419
387 ie = OMAP_MMC_READ(host, IE); 420 ie = OMAP_MMC_READ(host, IE);
388 OMAP_MMC_WRITE(host, IE, 0); 421 OMAP_MMC_WRITE(host, IE, 0);
389 OMAP_MMC_WRITE(host, CMD, 1 << 7);
390 loops = 0;
391 while (!(OMAP_MMC_READ(host, STAT) & OMAP_MMC_STAT_END_OF_CMD)) {
392 udelay(1);
393 loops++;
394 if (loops == 100000)
395 break;
396 }
397 OMAP_MMC_WRITE(host, STAT, OMAP_MMC_STAT_END_OF_CMD);
398 OMAP_MMC_WRITE(host, IE, ie); 422 OMAP_MMC_WRITE(host, IE, ie);
423 mmc_omap_send_abort(host);
399} 424}
400 425
401static void 426static void
@@ -451,6 +476,8 @@ mmc_omap_cmd_done(struct mmc_omap_host *host, struct mmc_command *cmd)
451{ 476{
452 host->cmd = NULL; 477 host->cmd = NULL;
453 478
479 del_timer(&host->cmd_timer);
480
454 if (cmd->flags & MMC_RSP_PRESENT) { 481 if (cmd->flags & MMC_RSP_PRESENT) {
455 if (cmd->flags & MMC_RSP_136) { 482 if (cmd->flags & MMC_RSP_136) {
456 /* response type 2 */ 483 /* response type 2 */
@@ -486,6 +513,47 @@ mmc_omap_cmd_done(struct mmc_omap_host *host, struct mmc_command *cmd)
486 } 513 }
487} 514}
488 515
516/*
517 * Abort stuck command. Can occur when card is removed while it is being
518 * read.
519 */
520static void mmc_omap_abort_command(struct work_struct *work)
521{
522 struct mmc_omap_host *host = container_of(work, struct mmc_omap_host,
523 cmd_abort);
524 u16 ie;
525
526 ie = OMAP_MMC_READ(host, IE);
527 OMAP_MMC_WRITE(host, IE, 0);
528
529 if (!host->cmd) {
530 OMAP_MMC_WRITE(host, IE, ie);
531 return;
532 }
533
534 dev_dbg(mmc_dev(host->mmc), "Aborting stuck command CMD%d\n",
535 host->cmd->opcode);
536
537 if (host->data && host->dma_in_use)
538 mmc_omap_release_dma(host, host->data, 1);
539
540 host->data = NULL;
541 host->sg_len = 0;
542
543 mmc_omap_send_abort(host);
544 host->cmd->error = -ETIMEDOUT;
545 mmc_omap_cmd_done(host, host->cmd);
546 OMAP_MMC_WRITE(host, IE, ie);
547}
548
549static void
550mmc_omap_cmd_timer(unsigned long data)
551{
552 struct mmc_omap_host *host = (struct mmc_omap_host *) data;
553
554 schedule_work(&host->cmd_abort);
555}
556
489/* PIO only */ 557/* PIO only */
490static void 558static void
491mmc_omap_sg_to_buf(struct mmc_omap_host *host) 559mmc_omap_sg_to_buf(struct mmc_omap_host *host)
@@ -1233,6 +1301,11 @@ static int __init mmc_omap_probe(struct platform_device *pdev)
1233 goto err_free_mem_region; 1301 goto err_free_mem_region;
1234 } 1302 }
1235 1303
1304 INIT_WORK(&host->cmd_abort, mmc_omap_abort_command);
1305 init_timer(&host->cmd_timer);
1306 host->cmd_timer.function = mmc_omap_cmd_timer;
1307 host->cmd_timer.data = (unsigned long) host;
1308
1236 spin_lock_init(&host->dma_lock); 1309 spin_lock_init(&host->dma_lock);
1237 init_timer(&host->dma_timer); 1310 init_timer(&host->dma_timer);
1238 spin_lock_init(&host->slot_lock); 1311 spin_lock_init(&host->slot_lock);