aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/mmc/host/omap.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/mmc/host/omap.c')
-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);