diff options
author | Nicolas Ferre <nicolas.ferre@rfo.atmel.com> | 2007-07-09 08:58:16 -0400 |
---|---|---|
committer | Pierre Ossman <drzeus@drzeus.cx> | 2007-07-09 15:29:07 -0400 |
commit | ed99c541e0a15281c57530d54a4a5e3272f74fb9 (patch) | |
tree | 55c5a90415b6d793cd952e85982163b0511d3073 /drivers | |
parent | e8d04d3dba60bdc139644350fcc88f82e40129dc (diff) |
mmc: at91_mci: fix hanging and rework to match flowcharts
Fixes hanging using multi block operations (seen during CMD25).
Follows closely the datasheet flowcharts.
This piece of code handles better big file writing. I had to take care
of the notbusy signal during write (at91_mci_handle_cmdrdy function) and
to rearrange the AT91_MCI_ENDRX and AT91_MCI_RXBUFF flag usage.
Signed-off-by: Nicolas Ferre <nicolas.ferre@rfo.atmel.com>
Signed-off-by: Pierre Ossman <drzeus@drzeus.cx>
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/mmc/host/at91_mci.c | 199 |
1 files changed, 108 insertions, 91 deletions
diff --git a/drivers/mmc/host/at91_mci.c b/drivers/mmc/host/at91_mci.c index 647f29473cf9..28c881895ab7 100644 --- a/drivers/mmc/host/at91_mci.c +++ b/drivers/mmc/host/at91_mci.c | |||
@@ -78,8 +78,6 @@ | |||
78 | 78 | ||
79 | #define DRIVER_NAME "at91_mci" | 79 | #define DRIVER_NAME "at91_mci" |
80 | 80 | ||
81 | #undef SUPPORT_4WIRE | ||
82 | |||
83 | #define FL_SENT_COMMAND (1 << 0) | 81 | #define FL_SENT_COMMAND (1 << 0) |
84 | #define FL_SENT_STOP (1 << 1) | 82 | #define FL_SENT_STOP (1 << 1) |
85 | 83 | ||
@@ -268,8 +266,6 @@ static void at91_mci_post_dma_read(struct at91mci_host *host) | |||
268 | } | 266 | } |
269 | 267 | ||
270 | while (host->in_use_index < host->transfer_index) { | 268 | while (host->in_use_index < host->transfer_index) { |
271 | unsigned int *buffer; | ||
272 | |||
273 | struct scatterlist *sg; | 269 | struct scatterlist *sg; |
274 | 270 | ||
275 | pr_debug("finishing index %d\n", host->in_use_index); | 271 | pr_debug("finishing index %d\n", host->in_use_index); |
@@ -280,20 +276,22 @@ static void at91_mci_post_dma_read(struct at91mci_host *host) | |||
280 | 276 | ||
281 | dma_unmap_page(NULL, sg->dma_address, sg->length, DMA_FROM_DEVICE); | 277 | dma_unmap_page(NULL, sg->dma_address, sg->length, DMA_FROM_DEVICE); |
282 | 278 | ||
283 | /* Swap the contents of the buffer */ | ||
284 | buffer = kmap_atomic(sg->page, KM_BIO_SRC_IRQ) + sg->offset; | ||
285 | pr_debug("buffer = %p, length = %d\n", buffer, sg->length); | ||
286 | |||
287 | data->bytes_xfered += sg->length; | 279 | data->bytes_xfered += sg->length; |
288 | 280 | ||
289 | if (cpu_is_at91rm9200()) { /* AT91RM9200 errata */ | 281 | if (cpu_is_at91rm9200()) { /* AT91RM9200 errata */ |
282 | unsigned int *buffer; | ||
290 | int index; | 283 | int index; |
291 | 284 | ||
285 | /* Swap the contents of the buffer */ | ||
286 | buffer = kmap_atomic(sg->page, KM_BIO_SRC_IRQ) + sg->offset; | ||
287 | pr_debug("buffer = %p, length = %d\n", buffer, sg->length); | ||
288 | |||
292 | for (index = 0; index < (sg->length / 4); index++) | 289 | for (index = 0; index < (sg->length / 4); index++) |
293 | buffer[index] = swab32(buffer[index]); | 290 | buffer[index] = swab32(buffer[index]); |
291 | |||
292 | kunmap_atomic(buffer, KM_BIO_SRC_IRQ); | ||
294 | } | 293 | } |
295 | 294 | ||
296 | kunmap_atomic(buffer, KM_BIO_SRC_IRQ); | ||
297 | flush_dcache_page(sg->page); | 295 | flush_dcache_page(sg->page); |
298 | } | 296 | } |
299 | 297 | ||
@@ -301,8 +299,8 @@ static void at91_mci_post_dma_read(struct at91mci_host *host) | |||
301 | if (host->transfer_index < data->sg_len) | 299 | if (host->transfer_index < data->sg_len) |
302 | at91_mci_pre_dma_read(host); | 300 | at91_mci_pre_dma_read(host); |
303 | else { | 301 | else { |
302 | at91_mci_write(host, AT91_MCI_IDR, AT91_MCI_ENDRX); | ||
304 | at91_mci_write(host, AT91_MCI_IER, AT91_MCI_RXBUFF); | 303 | at91_mci_write(host, AT91_MCI_IER, AT91_MCI_RXBUFF); |
305 | at91_mci_write(host, ATMEL_PDC_PTCR, ATMEL_PDC_RXTDIS | ATMEL_PDC_TXTDIS); | ||
306 | } | 304 | } |
307 | 305 | ||
308 | pr_debug("post dma read done\n"); | 306 | pr_debug("post dma read done\n"); |
@@ -323,7 +321,6 @@ static void at91_mci_handle_transmitted(struct at91mci_host *host) | |||
323 | 321 | ||
324 | /* Now wait for cmd ready */ | 322 | /* Now wait for cmd ready */ |
325 | at91_mci_write(host, AT91_MCI_IDR, AT91_MCI_TXBUFE); | 323 | at91_mci_write(host, AT91_MCI_IDR, AT91_MCI_TXBUFE); |
326 | at91_mci_write(host, AT91_MCI_IER, AT91_MCI_NOTBUSY); | ||
327 | 324 | ||
328 | cmd = host->cmd; | 325 | cmd = host->cmd; |
329 | if (!cmd) return; | 326 | if (!cmd) return; |
@@ -331,18 +328,53 @@ static void at91_mci_handle_transmitted(struct at91mci_host *host) | |||
331 | data = cmd->data; | 328 | data = cmd->data; |
332 | if (!data) return; | 329 | if (!data) return; |
333 | 330 | ||
331 | if (cmd->data->flags & MMC_DATA_MULTI) { | ||
332 | pr_debug("multiple write : wait for BLKE...\n"); | ||
333 | at91_mci_write(host, AT91_MCI_IER, AT91_MCI_BLKE); | ||
334 | } else | ||
335 | at91_mci_write(host, AT91_MCI_IER, AT91_MCI_NOTBUSY); | ||
336 | |||
334 | data->bytes_xfered = host->total_length; | 337 | data->bytes_xfered = host->total_length; |
335 | } | 338 | } |
336 | 339 | ||
340 | /*Handle after command sent ready*/ | ||
341 | static int at91_mci_handle_cmdrdy(struct at91mci_host *host) | ||
342 | { | ||
343 | if (!host->cmd) | ||
344 | return 1; | ||
345 | else if (!host->cmd->data) { | ||
346 | if (host->flags & FL_SENT_STOP) { | ||
347 | /*After multi block write, we must wait for NOTBUSY*/ | ||
348 | at91_mci_write(host, AT91_MCI_IER, AT91_MCI_NOTBUSY); | ||
349 | } else return 1; | ||
350 | } else if (host->cmd->data->flags & MMC_DATA_WRITE) { | ||
351 | /*After sendding multi-block-write command, start DMA transfer*/ | ||
352 | at91_mci_write(host, AT91_MCI_IER, AT91_MCI_TXBUFE); | ||
353 | at91_mci_write(host, AT91_MCI_IER, AT91_MCI_BLKE); | ||
354 | at91_mci_write(host, ATMEL_PDC_PTCR, ATMEL_PDC_TXTEN); | ||
355 | } | ||
356 | |||
357 | /* command not completed, have to wait */ | ||
358 | return 0; | ||
359 | } | ||
360 | |||
361 | |||
337 | /* | 362 | /* |
338 | * Enable the controller | 363 | * Enable the controller |
339 | */ | 364 | */ |
340 | static void at91_mci_enable(struct at91mci_host *host) | 365 | static void at91_mci_enable(struct at91mci_host *host) |
341 | { | 366 | { |
367 | unsigned int mr; | ||
368 | |||
342 | at91_mci_write(host, AT91_MCI_CR, AT91_MCI_MCIEN); | 369 | at91_mci_write(host, AT91_MCI_CR, AT91_MCI_MCIEN); |
343 | at91_mci_write(host, AT91_MCI_IDR, 0xffffffff); | 370 | at91_mci_write(host, AT91_MCI_IDR, 0xffffffff); |
344 | at91_mci_write(host, AT91_MCI_DTOR, AT91_MCI_DTOMUL_1M | AT91_MCI_DTOCYC); | 371 | at91_mci_write(host, AT91_MCI_DTOR, AT91_MCI_DTOMUL_1M | AT91_MCI_DTOCYC); |
345 | at91_mci_write(host, AT91_MCI_MR, AT91_MCI_PDCMODE | 0x34a); | 372 | mr = AT91_MCI_PDCMODE | 0x34a; |
373 | |||
374 | if (cpu_is_at91sam9260() || cpu_is_at91sam9263()) | ||
375 | mr |= AT91_MCI_RDPROOF | AT91_MCI_WRPROOF; | ||
376 | |||
377 | at91_mci_write(host, AT91_MCI_MR, mr); | ||
346 | 378 | ||
347 | /* use Slot A or B (only one at same time) */ | 379 | /* use Slot A or B (only one at same time) */ |
348 | at91_mci_write(host, AT91_MCI_SDCR, host->board->slot_b); | 380 | at91_mci_write(host, AT91_MCI_SDCR, host->board->slot_b); |
@@ -358,9 +390,8 @@ static void at91_mci_disable(struct at91mci_host *host) | |||
358 | 390 | ||
359 | /* | 391 | /* |
360 | * Send a command | 392 | * Send a command |
361 | * return the interrupts to enable | ||
362 | */ | 393 | */ |
363 | static unsigned int at91_mci_send_command(struct at91mci_host *host, struct mmc_command *cmd) | 394 | static void at91_mci_send_command(struct at91mci_host *host, struct mmc_command *cmd) |
364 | { | 395 | { |
365 | unsigned int cmdr, mr; | 396 | unsigned int cmdr, mr; |
366 | unsigned int block_length; | 397 | unsigned int block_length; |
@@ -371,8 +402,7 @@ static unsigned int at91_mci_send_command(struct at91mci_host *host, struct mmc_ | |||
371 | 402 | ||
372 | host->cmd = cmd; | 403 | host->cmd = cmd; |
373 | 404 | ||
374 | /* Not sure if this is needed */ | 405 | /* Needed for leaving busy state before CMD1 */ |
375 | #if 0 | ||
376 | if ((at91_mci_read(host, AT91_MCI_SR) & AT91_MCI_RTOE) && (cmd->opcode == 1)) { | 406 | if ((at91_mci_read(host, AT91_MCI_SR) & AT91_MCI_RTOE) && (cmd->opcode == 1)) { |
377 | pr_debug("Clearing timeout\n"); | 407 | pr_debug("Clearing timeout\n"); |
378 | at91_mci_write(host, AT91_MCI_ARGR, 0); | 408 | at91_mci_write(host, AT91_MCI_ARGR, 0); |
@@ -382,7 +412,7 @@ static unsigned int at91_mci_send_command(struct at91mci_host *host, struct mmc_ | |||
382 | pr_debug("Clearing: SR = %08X\n", at91_mci_read(host, AT91_MCI_SR)); | 412 | pr_debug("Clearing: SR = %08X\n", at91_mci_read(host, AT91_MCI_SR)); |
383 | } | 413 | } |
384 | } | 414 | } |
385 | #endif | 415 | |
386 | cmdr = cmd->opcode; | 416 | cmdr = cmd->opcode; |
387 | 417 | ||
388 | if (mmc_resp_type(cmd) == MMC_RSP_NONE) | 418 | if (mmc_resp_type(cmd) == MMC_RSP_NONE) |
@@ -439,50 +469,48 @@ static unsigned int at91_mci_send_command(struct at91mci_host *host, struct mmc_ | |||
439 | at91_mci_write(host, ATMEL_PDC_TCR, 0); | 469 | at91_mci_write(host, ATMEL_PDC_TCR, 0); |
440 | at91_mci_write(host, ATMEL_PDC_TNPR, 0); | 470 | at91_mci_write(host, ATMEL_PDC_TNPR, 0); |
441 | at91_mci_write(host, ATMEL_PDC_TNCR, 0); | 471 | at91_mci_write(host, ATMEL_PDC_TNCR, 0); |
472 | ier = AT91_MCI_CMDRDY; | ||
473 | } else { | ||
474 | /* zero block length and PDC mode */ | ||
475 | mr = at91_mci_read(host, AT91_MCI_MR) & 0x7fff; | ||
476 | at91_mci_write(host, AT91_MCI_MR, mr | (block_length << 16) | AT91_MCI_PDCMODE); | ||
442 | 477 | ||
443 | at91_mci_write(host, AT91_MCI_ARGR, cmd->arg); | 478 | /* |
444 | at91_mci_write(host, AT91_MCI_CMDR, cmdr); | 479 | * Disable the PDC controller |
445 | return AT91_MCI_CMDRDY; | 480 | */ |
446 | } | 481 | at91_mci_write(host, ATMEL_PDC_PTCR, ATMEL_PDC_RXTDIS | ATMEL_PDC_TXTDIS); |
447 | |||
448 | mr = at91_mci_read(host, AT91_MCI_MR) & 0x7fff; /* zero block length and PDC mode */ | ||
449 | at91_mci_write(host, AT91_MCI_MR, mr | (block_length << 16) | AT91_MCI_PDCMODE); | ||
450 | |||
451 | /* | ||
452 | * Disable the PDC controller | ||
453 | */ | ||
454 | at91_mci_write(host, ATMEL_PDC_PTCR, ATMEL_PDC_RXTDIS | ATMEL_PDC_TXTDIS); | ||
455 | |||
456 | if (cmdr & AT91_MCI_TRCMD_START) { | ||
457 | data->bytes_xfered = 0; | ||
458 | host->transfer_index = 0; | ||
459 | host->in_use_index = 0; | ||
460 | if (cmdr & AT91_MCI_TRDIR) { | ||
461 | /* | ||
462 | * Handle a read | ||
463 | */ | ||
464 | host->buffer = NULL; | ||
465 | host->total_length = 0; | ||
466 | |||
467 | at91_mci_pre_dma_read(host); | ||
468 | ier = AT91_MCI_ENDRX /* | AT91_MCI_RXBUFF */; | ||
469 | } | ||
470 | else { | ||
471 | /* | ||
472 | * Handle a write | ||
473 | */ | ||
474 | host->total_length = block_length * blocks; | ||
475 | host->buffer = dma_alloc_coherent(NULL, | ||
476 | host->total_length, | ||
477 | &host->physical_address, GFP_KERNEL); | ||
478 | |||
479 | at91_mci_sg_to_dma(host, data); | ||
480 | |||
481 | pr_debug("Transmitting %d bytes\n", host->total_length); | ||
482 | 482 | ||
483 | at91_mci_write(host, ATMEL_PDC_TPR, host->physical_address); | 483 | if (cmdr & AT91_MCI_TRCMD_START) { |
484 | at91_mci_write(host, ATMEL_PDC_TCR, host->total_length / 4); | 484 | data->bytes_xfered = 0; |
485 | ier = AT91_MCI_TXBUFE; | 485 | host->transfer_index = 0; |
486 | host->in_use_index = 0; | ||
487 | if (cmdr & AT91_MCI_TRDIR) { | ||
488 | /* | ||
489 | * Handle a read | ||
490 | */ | ||
491 | host->buffer = NULL; | ||
492 | host->total_length = 0; | ||
493 | |||
494 | at91_mci_pre_dma_read(host); | ||
495 | ier = AT91_MCI_ENDRX /* | AT91_MCI_RXBUFF */; | ||
496 | } | ||
497 | else { | ||
498 | /* | ||
499 | * Handle a write | ||
500 | */ | ||
501 | host->total_length = block_length * blocks; | ||
502 | host->buffer = dma_alloc_coherent(NULL, | ||
503 | host->total_length, | ||
504 | &host->physical_address, GFP_KERNEL); | ||
505 | |||
506 | at91_mci_sg_to_dma(host, data); | ||
507 | |||
508 | pr_debug("Transmitting %d bytes\n", host->total_length); | ||
509 | |||
510 | at91_mci_write(host, ATMEL_PDC_TPR, host->physical_address); | ||
511 | at91_mci_write(host, ATMEL_PDC_TCR, host->total_length / 4); | ||
512 | ier = AT91_MCI_CMDRDY; | ||
513 | } | ||
486 | } | 514 | } |
487 | } | 515 | } |
488 | 516 | ||
@@ -497,24 +525,9 @@ static unsigned int at91_mci_send_command(struct at91mci_host *host, struct mmc_ | |||
497 | if (cmdr & AT91_MCI_TRCMD_START) { | 525 | if (cmdr & AT91_MCI_TRCMD_START) { |
498 | if (cmdr & AT91_MCI_TRDIR) | 526 | if (cmdr & AT91_MCI_TRDIR) |
499 | at91_mci_write(host, ATMEL_PDC_PTCR, ATMEL_PDC_RXTEN); | 527 | at91_mci_write(host, ATMEL_PDC_PTCR, ATMEL_PDC_RXTEN); |
500 | else | ||
501 | at91_mci_write(host, ATMEL_PDC_PTCR, ATMEL_PDC_TXTEN); | ||
502 | } | 528 | } |
503 | return ier; | ||
504 | } | ||
505 | 529 | ||
506 | /* | 530 | /* Enable selected interrupts */ |
507 | * Wait for a command to complete | ||
508 | */ | ||
509 | static void at91_mci_process_command(struct at91mci_host *host, struct mmc_command *cmd) | ||
510 | { | ||
511 | unsigned int ier; | ||
512 | |||
513 | ier = at91_mci_send_command(host, cmd); | ||
514 | |||
515 | pr_debug("setting ier to %08X\n", ier); | ||
516 | |||
517 | /* Stop on errors or the required value */ | ||
518 | at91_mci_write(host, AT91_MCI_IER, AT91_MCI_ERRORS | ier); | 531 | at91_mci_write(host, AT91_MCI_IER, AT91_MCI_ERRORS | ier); |
519 | } | 532 | } |
520 | 533 | ||
@@ -525,11 +538,11 @@ static void at91_mci_process_next(struct at91mci_host *host) | |||
525 | { | 538 | { |
526 | if (!(host->flags & FL_SENT_COMMAND)) { | 539 | if (!(host->flags & FL_SENT_COMMAND)) { |
527 | host->flags |= FL_SENT_COMMAND; | 540 | host->flags |= FL_SENT_COMMAND; |
528 | at91_mci_process_command(host, host->request->cmd); | 541 | at91_mci_send_command(host, host->request->cmd); |
529 | } | 542 | } |
530 | else if ((!(host->flags & FL_SENT_STOP)) && host->request->stop) { | 543 | else if ((!(host->flags & FL_SENT_STOP)) && host->request->stop) { |
531 | host->flags |= FL_SENT_STOP; | 544 | host->flags |= FL_SENT_STOP; |
532 | at91_mci_process_command(host, host->request->stop); | 545 | at91_mci_send_command(host, host->request->stop); |
533 | } | 546 | } |
534 | else | 547 | else |
535 | mmc_request_done(host->mmc, host->request); | 548 | mmc_request_done(host->mmc, host->request); |
@@ -698,29 +711,33 @@ static irqreturn_t at91_mci_irq(int irq, void *devid) | |||
698 | at91_mci_handle_transmitted(host); | 711 | at91_mci_handle_transmitted(host); |
699 | } | 712 | } |
700 | 713 | ||
714 | if (int_status & AT91_MCI_ENDRX) { | ||
715 | pr_debug("ENDRX\n"); | ||
716 | at91_mci_post_dma_read(host); | ||
717 | } | ||
718 | |||
701 | if (int_status & AT91_MCI_RXBUFF) { | 719 | if (int_status & AT91_MCI_RXBUFF) { |
702 | pr_debug("RX buffer full\n"); | 720 | pr_debug("RX buffer full\n"); |
703 | at91_mci_write(host, AT91_MCI_IER, AT91_MCI_CMDRDY); | 721 | at91_mci_write(host, ATMEL_PDC_PTCR, ATMEL_PDC_RXTDIS | ATMEL_PDC_TXTDIS); |
722 | at91_mci_write(host, AT91_MCI_IDR, AT91_MCI_RXBUFF | AT91_MCI_ENDRX); | ||
723 | completed = 1; | ||
704 | } | 724 | } |
705 | 725 | ||
706 | if (int_status & AT91_MCI_ENDTX) | 726 | if (int_status & AT91_MCI_ENDTX) |
707 | pr_debug("Transmit has ended\n"); | 727 | pr_debug("Transmit has ended\n"); |
708 | 728 | ||
709 | if (int_status & AT91_MCI_ENDRX) { | ||
710 | pr_debug("Receive has ended\n"); | ||
711 | at91_mci_post_dma_read(host); | ||
712 | } | ||
713 | |||
714 | if (int_status & AT91_MCI_NOTBUSY) { | 729 | if (int_status & AT91_MCI_NOTBUSY) { |
715 | pr_debug("Card is ready\n"); | 730 | pr_debug("Card is ready\n"); |
716 | at91_mci_write(host, AT91_MCI_IER, AT91_MCI_CMDRDY); | 731 | completed = 1; |
717 | } | 732 | } |
718 | 733 | ||
719 | if (int_status & AT91_MCI_DTIP) | 734 | if (int_status & AT91_MCI_DTIP) |
720 | pr_debug("Data transfer in progress\n"); | 735 | pr_debug("Data transfer in progress\n"); |
721 | 736 | ||
722 | if (int_status & AT91_MCI_BLKE) | 737 | if (int_status & AT91_MCI_BLKE) { |
723 | pr_debug("Block transfer has ended\n"); | 738 | pr_debug("Block transfer has ended\n"); |
739 | completed = 1; | ||
740 | } | ||
724 | 741 | ||
725 | if (int_status & AT91_MCI_TXRDY) | 742 | if (int_status & AT91_MCI_TXRDY) |
726 | pr_debug("Ready to transmit\n"); | 743 | pr_debug("Ready to transmit\n"); |
@@ -730,7 +747,7 @@ static irqreturn_t at91_mci_irq(int irq, void *devid) | |||
730 | 747 | ||
731 | if (int_status & AT91_MCI_CMDRDY) { | 748 | if (int_status & AT91_MCI_CMDRDY) { |
732 | pr_debug("Command ready\n"); | 749 | pr_debug("Command ready\n"); |
733 | completed = 1; | 750 | completed = at91_mci_handle_cmdrdy(host); |
734 | } | 751 | } |
735 | } | 752 | } |
736 | 753 | ||
@@ -830,11 +847,11 @@ static int __init at91_mci_probe(struct platform_device *pdev) | |||
830 | host->bus_mode = 0; | 847 | host->bus_mode = 0; |
831 | host->board = pdev->dev.platform_data; | 848 | host->board = pdev->dev.platform_data; |
832 | if (host->board->wire4) { | 849 | if (host->board->wire4) { |
833 | #ifdef SUPPORT_4WIRE | 850 | if (cpu_is_at91sam9260() || cpu_is_at91sam9263()) |
834 | mmc->caps |= MMC_CAP_4_BIT_DATA; | 851 | mmc->caps |= MMC_CAP_4_BIT_DATA; |
835 | #else | 852 | else |
836 | printk("AT91 MMC: 4 wire bus mode not supported by this driver - using 1 wire\n"); | 853 | printk("AT91 MMC: 4 wire bus mode not supported" |
837 | #endif | 854 | " - using 1 wire\n"); |
838 | } | 855 | } |
839 | 856 | ||
840 | /* | 857 | /* |