diff options
Diffstat (limited to 'drivers/mmc/host/omap.c')
-rw-r--r-- | drivers/mmc/host/omap.c | 93 |
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 | ||
375 | static void | 380 | static void |
381 | mmc_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 | } | ||
405 | out: | ||
406 | OMAP_MMC_WRITE(host, STAT, stat); | ||
407 | } | ||
408 | |||
409 | static void | ||
376 | mmc_omap_abort_xfer(struct mmc_omap_host *host, struct mmc_data *data) | 410 | mmc_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 | ||
401 | static void | 426 | static 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 | */ | ||
520 | static 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 | |||
549 | static void | ||
550 | mmc_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 */ |
490 | static void | 558 | static void |
491 | mmc_omap_sg_to_buf(struct mmc_omap_host *host) | 559 | mmc_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); |