diff options
Diffstat (limited to 'drivers/dma/fsldma.c')
-rw-r--r-- | drivers/dma/fsldma.c | 197 |
1 files changed, 144 insertions, 53 deletions
diff --git a/drivers/dma/fsldma.c b/drivers/dma/fsldma.c index 465f16dd78e5..d5d6885ab341 100644 --- a/drivers/dma/fsldma.c +++ b/drivers/dma/fsldma.c | |||
@@ -466,6 +466,88 @@ static struct fsl_desc_sw *fsl_dma_alloc_descriptor(struct fsldma_chan *chan) | |||
466 | } | 466 | } |
467 | 467 | ||
468 | /** | 468 | /** |
469 | * fsldma_clean_completed_descriptor - free all descriptors which | ||
470 | * has been completed and acked | ||
471 | * @chan: Freescale DMA channel | ||
472 | * | ||
473 | * This function is used on all completed and acked descriptors. | ||
474 | * All descriptors should only be freed in this function. | ||
475 | */ | ||
476 | static void fsldma_clean_completed_descriptor(struct fsldma_chan *chan) | ||
477 | { | ||
478 | struct fsl_desc_sw *desc, *_desc; | ||
479 | |||
480 | /* Run the callback for each descriptor, in order */ | ||
481 | list_for_each_entry_safe(desc, _desc, &chan->ld_completed, node) | ||
482 | if (async_tx_test_ack(&desc->async_tx)) | ||
483 | fsl_dma_free_descriptor(chan, desc); | ||
484 | } | ||
485 | |||
486 | /** | ||
487 | * fsldma_run_tx_complete_actions - cleanup a single link descriptor | ||
488 | * @chan: Freescale DMA channel | ||
489 | * @desc: descriptor to cleanup and free | ||
490 | * @cookie: Freescale DMA transaction identifier | ||
491 | * | ||
492 | * This function is used on a descriptor which has been executed by the DMA | ||
493 | * controller. It will run any callbacks, submit any dependencies. | ||
494 | */ | ||
495 | static dma_cookie_t fsldma_run_tx_complete_actions(struct fsldma_chan *chan, | ||
496 | struct fsl_desc_sw *desc, dma_cookie_t cookie) | ||
497 | { | ||
498 | struct dma_async_tx_descriptor *txd = &desc->async_tx; | ||
499 | dma_cookie_t ret = cookie; | ||
500 | |||
501 | BUG_ON(txd->cookie < 0); | ||
502 | |||
503 | if (txd->cookie > 0) { | ||
504 | ret = txd->cookie; | ||
505 | |||
506 | /* Run the link descriptor callback function */ | ||
507 | if (txd->callback) { | ||
508 | chan_dbg(chan, "LD %p callback\n", desc); | ||
509 | txd->callback(txd->callback_param); | ||
510 | } | ||
511 | } | ||
512 | |||
513 | /* Run any dependencies */ | ||
514 | dma_run_dependencies(txd); | ||
515 | |||
516 | return ret; | ||
517 | } | ||
518 | |||
519 | /** | ||
520 | * fsldma_clean_running_descriptor - move the completed descriptor from | ||
521 | * ld_running to ld_completed | ||
522 | * @chan: Freescale DMA channel | ||
523 | * @desc: the descriptor which is completed | ||
524 | * | ||
525 | * Free the descriptor directly if acked by async_tx api, or move it to | ||
526 | * queue ld_completed. | ||
527 | */ | ||
528 | static void fsldma_clean_running_descriptor(struct fsldma_chan *chan, | ||
529 | struct fsl_desc_sw *desc) | ||
530 | { | ||
531 | /* Remove from the list of transactions */ | ||
532 | list_del(&desc->node); | ||
533 | |||
534 | /* | ||
535 | * the client is allowed to attach dependent operations | ||
536 | * until 'ack' is set | ||
537 | */ | ||
538 | if (!async_tx_test_ack(&desc->async_tx)) { | ||
539 | /* | ||
540 | * Move this descriptor to the list of descriptors which is | ||
541 | * completed, but still awaiting the 'ack' bit to be set. | ||
542 | */ | ||
543 | list_add_tail(&desc->node, &chan->ld_completed); | ||
544 | return; | ||
545 | } | ||
546 | |||
547 | dma_pool_free(chan->desc_pool, desc, desc->async_tx.phys); | ||
548 | } | ||
549 | |||
550 | /** | ||
469 | * fsl_chan_xfer_ld_queue - transfer any pending transactions | 551 | * fsl_chan_xfer_ld_queue - transfer any pending transactions |
470 | * @chan : Freescale DMA channel | 552 | * @chan : Freescale DMA channel |
471 | * | 553 | * |
@@ -533,31 +615,58 @@ static void fsl_chan_xfer_ld_queue(struct fsldma_chan *chan) | |||
533 | } | 615 | } |
534 | 616 | ||
535 | /** | 617 | /** |
536 | * fsldma_cleanup_descriptor - cleanup and free a single link descriptor | 618 | * fsldma_cleanup_descriptors - cleanup link descriptors which are completed |
619 | * and move them to ld_completed to free until flag 'ack' is set | ||
537 | * @chan: Freescale DMA channel | 620 | * @chan: Freescale DMA channel |
538 | * @desc: descriptor to cleanup and free | ||
539 | * | 621 | * |
540 | * This function is used on a descriptor which has been executed by the DMA | 622 | * This function is used on descriptors which have been executed by the DMA |
541 | * controller. It will run any callbacks, submit any dependencies, and then | 623 | * controller. It will run any callbacks, submit any dependencies, then |
542 | * free the descriptor. | 624 | * free these descriptors if flag 'ack' is set. |
543 | */ | 625 | */ |
544 | static void fsldma_cleanup_descriptor(struct fsldma_chan *chan, | 626 | static void fsldma_cleanup_descriptors(struct fsldma_chan *chan) |
545 | struct fsl_desc_sw *desc) | ||
546 | { | 627 | { |
547 | struct dma_async_tx_descriptor *txd = &desc->async_tx; | 628 | struct fsl_desc_sw *desc, *_desc; |
629 | dma_cookie_t cookie = 0; | ||
630 | dma_addr_t curr_phys = get_cdar(chan); | ||
631 | int seen_current = 0; | ||
548 | 632 | ||
549 | /* Run the link descriptor callback function */ | 633 | fsldma_clean_completed_descriptor(chan); |
550 | if (txd->callback) { | 634 | |
551 | chan_dbg(chan, "LD %p callback\n", desc); | 635 | /* Run the callback for each descriptor, in order */ |
552 | txd->callback(txd->callback_param); | 636 | list_for_each_entry_safe(desc, _desc, &chan->ld_running, node) { |
637 | /* | ||
638 | * do not advance past the current descriptor loaded into the | ||
639 | * hardware channel, subsequent descriptors are either in | ||
640 | * process or have not been submitted | ||
641 | */ | ||
642 | if (seen_current) | ||
643 | break; | ||
644 | |||
645 | /* | ||
646 | * stop the search if we reach the current descriptor and the | ||
647 | * channel is busy | ||
648 | */ | ||
649 | if (desc->async_tx.phys == curr_phys) { | ||
650 | seen_current = 1; | ||
651 | if (!dma_is_idle(chan)) | ||
652 | break; | ||
653 | } | ||
654 | |||
655 | cookie = fsldma_run_tx_complete_actions(chan, desc, cookie); | ||
656 | |||
657 | fsldma_clean_running_descriptor(chan, desc); | ||
553 | } | 658 | } |
554 | 659 | ||
555 | /* Run any dependencies */ | 660 | /* |
556 | dma_run_dependencies(txd); | 661 | * Start any pending transactions automatically |
662 | * | ||
663 | * In the ideal case, we keep the DMA controller busy while we go | ||
664 | * ahead and free the descriptors below. | ||
665 | */ | ||
666 | fsl_chan_xfer_ld_queue(chan); | ||
557 | 667 | ||
558 | dma_descriptor_unmap(txd); | 668 | if (cookie > 0) |
559 | chan_dbg(chan, "LD %p free\n", desc); | 669 | chan->common.completed_cookie = cookie; |
560 | dma_pool_free(chan->desc_pool, desc, txd->phys); | ||
561 | } | 670 | } |
562 | 671 | ||
563 | /** | 672 | /** |
@@ -627,8 +736,10 @@ static void fsl_dma_free_chan_resources(struct dma_chan *dchan) | |||
627 | 736 | ||
628 | chan_dbg(chan, "free all channel resources\n"); | 737 | chan_dbg(chan, "free all channel resources\n"); |
629 | spin_lock_bh(&chan->desc_lock); | 738 | spin_lock_bh(&chan->desc_lock); |
739 | fsldma_cleanup_descriptors(chan); | ||
630 | fsldma_free_desc_list(chan, &chan->ld_pending); | 740 | fsldma_free_desc_list(chan, &chan->ld_pending); |
631 | fsldma_free_desc_list(chan, &chan->ld_running); | 741 | fsldma_free_desc_list(chan, &chan->ld_running); |
742 | fsldma_free_desc_list(chan, &chan->ld_completed); | ||
632 | spin_unlock_bh(&chan->desc_lock); | 743 | spin_unlock_bh(&chan->desc_lock); |
633 | 744 | ||
634 | dma_pool_destroy(chan->desc_pool); | 745 | dma_pool_destroy(chan->desc_pool); |
@@ -865,6 +976,7 @@ static int fsl_dma_device_control(struct dma_chan *dchan, | |||
865 | /* Remove and free all of the descriptors in the LD queue */ | 976 | /* Remove and free all of the descriptors in the LD queue */ |
866 | fsldma_free_desc_list(chan, &chan->ld_pending); | 977 | fsldma_free_desc_list(chan, &chan->ld_pending); |
867 | fsldma_free_desc_list(chan, &chan->ld_running); | 978 | fsldma_free_desc_list(chan, &chan->ld_running); |
979 | fsldma_free_desc_list(chan, &chan->ld_completed); | ||
868 | chan->idle = true; | 980 | chan->idle = true; |
869 | 981 | ||
870 | spin_unlock_bh(&chan->desc_lock); | 982 | spin_unlock_bh(&chan->desc_lock); |
@@ -923,6 +1035,17 @@ static enum dma_status fsl_tx_status(struct dma_chan *dchan, | |||
923 | dma_cookie_t cookie, | 1035 | dma_cookie_t cookie, |
924 | struct dma_tx_state *txstate) | 1036 | struct dma_tx_state *txstate) |
925 | { | 1037 | { |
1038 | struct fsldma_chan *chan = to_fsl_chan(dchan); | ||
1039 | enum dma_status ret; | ||
1040 | |||
1041 | ret = dma_cookie_status(dchan, cookie, txstate); | ||
1042 | if (ret == DMA_COMPLETE) | ||
1043 | return ret; | ||
1044 | |||
1045 | spin_lock_bh(&chan->desc_lock); | ||
1046 | fsldma_cleanup_descriptors(chan); | ||
1047 | spin_unlock_bh(&chan->desc_lock); | ||
1048 | |||
926 | return dma_cookie_status(dchan, cookie, txstate); | 1049 | return dma_cookie_status(dchan, cookie, txstate); |
927 | } | 1050 | } |
928 | 1051 | ||
@@ -1000,51 +1123,18 @@ static irqreturn_t fsldma_chan_irq(int irq, void *data) | |||
1000 | static void dma_do_tasklet(unsigned long data) | 1123 | static void dma_do_tasklet(unsigned long data) |
1001 | { | 1124 | { |
1002 | struct fsldma_chan *chan = (struct fsldma_chan *)data; | 1125 | struct fsldma_chan *chan = (struct fsldma_chan *)data; |
1003 | struct fsl_desc_sw *desc, *_desc; | ||
1004 | LIST_HEAD(ld_cleanup); | ||
1005 | 1126 | ||
1006 | chan_dbg(chan, "tasklet entry\n"); | 1127 | chan_dbg(chan, "tasklet entry\n"); |
1007 | 1128 | ||
1008 | spin_lock_bh(&chan->desc_lock); | 1129 | spin_lock_bh(&chan->desc_lock); |
1009 | 1130 | ||
1010 | /* update the cookie if we have some descriptors to cleanup */ | ||
1011 | if (!list_empty(&chan->ld_running)) { | ||
1012 | dma_cookie_t cookie; | ||
1013 | |||
1014 | desc = to_fsl_desc(chan->ld_running.prev); | ||
1015 | cookie = desc->async_tx.cookie; | ||
1016 | dma_cookie_complete(&desc->async_tx); | ||
1017 | |||
1018 | chan_dbg(chan, "completed_cookie=%d\n", cookie); | ||
1019 | } | ||
1020 | |||
1021 | /* | ||
1022 | * move the descriptors to a temporary list so we can drop the lock | ||
1023 | * during the entire cleanup operation | ||
1024 | */ | ||
1025 | list_splice_tail_init(&chan->ld_running, &ld_cleanup); | ||
1026 | |||
1027 | /* the hardware is now idle and ready for more */ | 1131 | /* the hardware is now idle and ready for more */ |
1028 | chan->idle = true; | 1132 | chan->idle = true; |
1029 | 1133 | ||
1030 | /* | 1134 | /* Run all cleanup for descriptors which have been completed */ |
1031 | * Start any pending transactions automatically | 1135 | fsldma_cleanup_descriptors(chan); |
1032 | * | ||
1033 | * In the ideal case, we keep the DMA controller busy while we go | ||
1034 | * ahead and free the descriptors below. | ||
1035 | */ | ||
1036 | fsl_chan_xfer_ld_queue(chan); | ||
1037 | spin_unlock_bh(&chan->desc_lock); | ||
1038 | |||
1039 | /* Run the callback for each descriptor, in order */ | ||
1040 | list_for_each_entry_safe(desc, _desc, &ld_cleanup, node) { | ||
1041 | |||
1042 | /* Remove from the list of transactions */ | ||
1043 | list_del(&desc->node); | ||
1044 | 1136 | ||
1045 | /* Run all cleanup for this descriptor */ | 1137 | spin_unlock_bh(&chan->desc_lock); |
1046 | fsldma_cleanup_descriptor(chan, desc); | ||
1047 | } | ||
1048 | 1138 | ||
1049 | chan_dbg(chan, "tasklet exit\n"); | 1139 | chan_dbg(chan, "tasklet exit\n"); |
1050 | } | 1140 | } |
@@ -1228,6 +1318,7 @@ static int fsl_dma_chan_probe(struct fsldma_device *fdev, | |||
1228 | spin_lock_init(&chan->desc_lock); | 1318 | spin_lock_init(&chan->desc_lock); |
1229 | INIT_LIST_HEAD(&chan->ld_pending); | 1319 | INIT_LIST_HEAD(&chan->ld_pending); |
1230 | INIT_LIST_HEAD(&chan->ld_running); | 1320 | INIT_LIST_HEAD(&chan->ld_running); |
1321 | INIT_LIST_HEAD(&chan->ld_completed); | ||
1231 | chan->idle = true; | 1322 | chan->idle = true; |
1232 | #ifdef CONFIG_PM | 1323 | #ifdef CONFIG_PM |
1233 | chan->pm_state = RUNNING; | 1324 | chan->pm_state = RUNNING; |