diff options
-rw-r--r-- | drivers/mmc/host/omap.c | 82 |
1 files changed, 49 insertions, 33 deletions
diff --git a/drivers/mmc/host/omap.c b/drivers/mmc/host/omap.c index 8f393e8ed42f..7cc104ce0f07 100644 --- a/drivers/mmc/host/omap.c +++ b/drivers/mmc/host/omap.c | |||
@@ -134,8 +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; | 137 | struct work_struct cmd_abort_work; |
138 | struct timer_list cmd_timer; | 138 | unsigned abort:1; |
139 | struct timer_list cmd_abort_timer; | ||
139 | 140 | ||
140 | unsigned int sg_len; | 141 | unsigned int sg_len; |
141 | int sg_idx; | 142 | int sg_idx; |
@@ -320,7 +321,7 @@ mmc_omap_start_command(struct mmc_omap_host *host, struct mmc_command *cmd) | |||
320 | if (host->data && !(host->data->flags & MMC_DATA_WRITE)) | 321 | if (host->data && !(host->data->flags & MMC_DATA_WRITE)) |
321 | cmdreg |= 1 << 15; | 322 | cmdreg |= 1 << 15; |
322 | 323 | ||
323 | mod_timer(&host->cmd_timer, jiffies + HZ/2); | 324 | mod_timer(&host->cmd_abort_timer, jiffies + HZ/2); |
324 | 325 | ||
325 | OMAP_MMC_WRITE(host, CTO, 200); | 326 | OMAP_MMC_WRITE(host, CTO, 200); |
326 | OMAP_MMC_WRITE(host, ARGL, cmd->arg & 0xffff); | 327 | OMAP_MMC_WRITE(host, ARGL, cmd->arg & 0xffff); |
@@ -381,7 +382,7 @@ mmc_omap_xfer_done(struct mmc_omap_host *host, struct mmc_data *data) | |||
381 | } | 382 | } |
382 | 383 | ||
383 | static void | 384 | static void |
384 | mmc_omap_send_abort(struct mmc_omap_host *host) | 385 | mmc_omap_send_abort(struct mmc_omap_host *host, int maxloops) |
385 | { | 386 | { |
386 | struct mmc_omap_slot *slot = host->current_slot; | 387 | struct mmc_omap_slot *slot = host->current_slot; |
387 | unsigned int restarts, passes, timeout; | 388 | unsigned int restarts, passes, timeout; |
@@ -390,7 +391,7 @@ mmc_omap_send_abort(struct mmc_omap_host *host) | |||
390 | /* Sending abort takes 80 clocks. Have some extra and round up */ | 391 | /* Sending abort takes 80 clocks. Have some extra and round up */ |
391 | timeout = (120*1000000 + slot->fclk_freq - 1)/slot->fclk_freq; | 392 | timeout = (120*1000000 + slot->fclk_freq - 1)/slot->fclk_freq; |
392 | restarts = 0; | 393 | restarts = 0; |
393 | while (restarts < 10000) { | 394 | while (restarts < maxloops) { |
394 | OMAP_MMC_WRITE(host, STAT, 0xFFFF); | 395 | OMAP_MMC_WRITE(host, STAT, 0xFFFF); |
395 | OMAP_MMC_WRITE(host, CMD, (3 << 12) | (1 << 7)); | 396 | OMAP_MMC_WRITE(host, CMD, (3 << 12) | (1 << 7)); |
396 | 397 | ||
@@ -412,18 +413,13 @@ out: | |||
412 | static void | 413 | static void |
413 | mmc_omap_abort_xfer(struct mmc_omap_host *host, struct mmc_data *data) | 414 | mmc_omap_abort_xfer(struct mmc_omap_host *host, struct mmc_data *data) |
414 | { | 415 | { |
415 | u16 ie; | ||
416 | |||
417 | if (host->dma_in_use) | 416 | if (host->dma_in_use) |
418 | mmc_omap_release_dma(host, data, 1); | 417 | mmc_omap_release_dma(host, data, 1); |
419 | 418 | ||
420 | host->data = NULL; | 419 | host->data = NULL; |
421 | host->sg_len = 0; | 420 | host->sg_len = 0; |
422 | 421 | ||
423 | ie = OMAP_MMC_READ(host, IE); | 422 | mmc_omap_send_abort(host, 10000); |
424 | OMAP_MMC_WRITE(host, IE, 0); | ||
425 | OMAP_MMC_WRITE(host, IE, ie); | ||
426 | mmc_omap_send_abort(host); | ||
427 | } | 423 | } |
428 | 424 | ||
429 | static void | 425 | static void |
@@ -479,7 +475,7 @@ mmc_omap_cmd_done(struct mmc_omap_host *host, struct mmc_command *cmd) | |||
479 | { | 475 | { |
480 | host->cmd = NULL; | 476 | host->cmd = NULL; |
481 | 477 | ||
482 | del_timer(&host->cmd_timer); | 478 | del_timer(&host->cmd_abort_timer); |
483 | 479 | ||
484 | if (cmd->flags & MMC_RSP_PRESENT) { | 480 | if (cmd->flags & MMC_RSP_PRESENT) { |
485 | if (cmd->flags & MMC_RSP_136) { | 481 | if (cmd->flags & MMC_RSP_136) { |
@@ -523,38 +519,48 @@ mmc_omap_cmd_done(struct mmc_omap_host *host, struct mmc_command *cmd) | |||
523 | static void mmc_omap_abort_command(struct work_struct *work) | 519 | static void mmc_omap_abort_command(struct work_struct *work) |
524 | { | 520 | { |
525 | struct mmc_omap_host *host = container_of(work, struct mmc_omap_host, | 521 | struct mmc_omap_host *host = container_of(work, struct mmc_omap_host, |
526 | cmd_abort); | 522 | cmd_abort_work); |
527 | u16 ie; | 523 | BUG_ON(!host->cmd); |
528 | |||
529 | ie = OMAP_MMC_READ(host, IE); | ||
530 | OMAP_MMC_WRITE(host, IE, 0); | ||
531 | |||
532 | if (!host->cmd) { | ||
533 | OMAP_MMC_WRITE(host, IE, ie); | ||
534 | return; | ||
535 | } | ||
536 | 524 | ||
537 | dev_dbg(mmc_dev(host->mmc), "Aborting stuck command CMD%d\n", | 525 | dev_dbg(mmc_dev(host->mmc), "Aborting stuck command CMD%d\n", |
538 | host->cmd->opcode); | 526 | host->cmd->opcode); |
539 | 527 | ||
540 | if (host->data && host->dma_in_use) | 528 | if (host->cmd->error == 0) |
541 | mmc_omap_release_dma(host, host->data, 1); | 529 | host->cmd->error = -ETIMEDOUT; |
542 | 530 | ||
543 | host->data = NULL; | 531 | if (host->data == NULL) { |
544 | host->sg_len = 0; | 532 | struct mmc_command *cmd; |
533 | struct mmc_host *mmc; | ||
534 | |||
535 | cmd = host->cmd; | ||
536 | host->cmd = NULL; | ||
537 | mmc_omap_send_abort(host, 10000); | ||
538 | |||
539 | host->mrq = NULL; | ||
540 | mmc = host->mmc; | ||
541 | mmc_omap_release_slot(host->current_slot); | ||
542 | mmc_request_done(mmc, cmd->mrq); | ||
543 | } else | ||
544 | mmc_omap_cmd_done(host, host->cmd); | ||
545 | 545 | ||
546 | mmc_omap_send_abort(host); | 546 | host->abort = 0; |
547 | host->cmd->error = -ETIMEDOUT; | 547 | enable_irq(host->irq); |
548 | mmc_omap_cmd_done(host, host->cmd); | ||
549 | OMAP_MMC_WRITE(host, IE, ie); | ||
550 | } | 548 | } |
551 | 549 | ||
552 | static void | 550 | static void |
553 | mmc_omap_cmd_timer(unsigned long data) | 551 | mmc_omap_cmd_timer(unsigned long data) |
554 | { | 552 | { |
555 | struct mmc_omap_host *host = (struct mmc_omap_host *) data; | 553 | struct mmc_omap_host *host = (struct mmc_omap_host *) data; |
554 | unsigned long flags; | ||
556 | 555 | ||
557 | schedule_work(&host->cmd_abort); | 556 | spin_lock_irqsave(&host->slot_lock, flags); |
557 | if (host->cmd != NULL && !host->abort) { | ||
558 | OMAP_MMC_WRITE(host, IE, 0); | ||
559 | disable_irq(host->irq); | ||
560 | host->abort = 1; | ||
561 | schedule_work(&host->cmd_abort_work); | ||
562 | } | ||
563 | spin_unlock_irqrestore(&host->slot_lock, flags); | ||
558 | } | 564 | } |
559 | 565 | ||
560 | /* PIO only */ | 566 | /* PIO only */ |
@@ -728,6 +734,15 @@ static irqreturn_t mmc_omap_irq(int irq, void *dev_id) | |||
728 | } | 734 | } |
729 | } | 735 | } |
730 | 736 | ||
737 | if (cmd_error && host->data) { | ||
738 | del_timer(&host->cmd_abort_timer); | ||
739 | host->abort = 1; | ||
740 | OMAP_MMC_WRITE(host, IE, 0); | ||
741 | disable_irq(host->irq); | ||
742 | schedule_work(&host->cmd_abort_work); | ||
743 | return IRQ_HANDLED; | ||
744 | } | ||
745 | |||
731 | if (end_command) | 746 | if (end_command) |
732 | mmc_omap_cmd_done(host, host->cmd); | 747 | mmc_omap_cmd_done(host, host->cmd); |
733 | if (host->data != NULL) { | 748 | if (host->data != NULL) { |
@@ -1316,8 +1331,9 @@ static int __init mmc_omap_probe(struct platform_device *pdev) | |||
1316 | goto err_free_mem_region; | 1331 | goto err_free_mem_region; |
1317 | } | 1332 | } |
1318 | 1333 | ||
1319 | INIT_WORK(&host->cmd_abort, mmc_omap_abort_command); | 1334 | INIT_WORK(&host->cmd_abort_work, mmc_omap_abort_command); |
1320 | setup_timer(&host->cmd_timer, mmc_omap_cmd_timer, (unsigned long) host); | 1335 | setup_timer(&host->cmd_abort_timer, mmc_omap_cmd_timer, |
1336 | (unsigned long) host); | ||
1321 | 1337 | ||
1322 | spin_lock_init(&host->dma_lock); | 1338 | spin_lock_init(&host->dma_lock); |
1323 | setup_timer(&host->dma_timer, mmc_omap_dma_timer, (unsigned long) host); | 1339 | setup_timer(&host->dma_timer, mmc_omap_dma_timer, (unsigned long) host); |