aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/dma/mmp_pdma.c
diff options
context:
space:
mode:
authorDaniel Mack <zonque@gmail.com>2013-08-21 08:08:54 -0400
committerVinod Koul <vinod.koul@intel.com>2013-08-25 12:34:52 -0400
commitb721f9e800571ca724eed6f1956e58e8f1d47d7d (patch)
treea3fadd9386ddcabf89e8ec7df39d6bc71afe168a /drivers/dma/mmp_pdma.c
parentb4d6d336762aea282921a3283f2a00c7e95d2bef (diff)
dma: mmp_pdma: only complete one transaction from dma_do_tasklet()
Currently, when an interrupt has occured for a channel, the tasklet worker code will only look at the very last entry in the running list and complete its cookie, and then dispose the entire running chain. Hence, the first transaction's cookie will never complete. In fact, the interrupt we should handle will be the one related to the first descriptor in the chain with the ENDIRQEN bit set, so complete the second transaction that is in fact still running. As a result, the driver can't currently handle multiple transactions on one chanel, and it's likely that no drivers exist that rely on this feature. Fix this by walking the running_chain and look for the first descriptor that has the interrupt-enable bit set. Only queue descriptors up to that point for completion handling, while leaving the rest intact. Also, only make the channel idle if the list is completely empty after such a cycle. Signed-off-by: Daniel Mack <zonque@gmail.com> Signed-off-by: Vinod Koul <vinod.koul@intel.com>
Diffstat (limited to 'drivers/dma/mmp_pdma.c')
-rw-r--r--drivers/dma/mmp_pdma.c35
1 files changed, 21 insertions, 14 deletions
diff --git a/drivers/dma/mmp_pdma.c b/drivers/dma/mmp_pdma.c
index 5c4318c5a5b1..b0a9c94bc0de 100644
--- a/drivers/dma/mmp_pdma.c
+++ b/drivers/dma/mmp_pdma.c
@@ -703,25 +703,32 @@ static void dma_do_tasklet(unsigned long data)
703 703
704 spin_lock_irqsave(&chan->desc_lock, flags); 704 spin_lock_irqsave(&chan->desc_lock, flags);
705 705
706 /* update the cookie if we have some descriptors to cleanup */ 706 list_for_each_entry_safe(desc, _desc, &chan->chain_running, node) {
707 if (!list_empty(&chan->chain_running)) { 707 /*
708 dma_cookie_t cookie; 708 * move the descriptors to a temporary list so we can drop
709 709 * the lock during the entire cleanup operation
710 desc = to_mmp_pdma_desc(chan->chain_running.prev); 710 */
711 cookie = desc->async_tx.cookie; 711 list_del(&desc->node);
712 dma_cookie_complete(&desc->async_tx); 712 list_add(&desc->node, &chain_cleanup);
713 713
714 dev_dbg(chan->dev, "completed_cookie=%d\n", cookie); 714 /*
715 * Look for the first list entry which has the ENDIRQEN flag
716 * set. That is the descriptor we got an interrupt for, so
717 * complete that transaction and its cookie.
718 */
719 if (desc->desc.dcmd & DCMD_ENDIRQEN) {
720 dma_cookie_t cookie = desc->async_tx.cookie;
721 dma_cookie_complete(&desc->async_tx);
722 dev_dbg(chan->dev, "completed_cookie=%d\n", cookie);
723 break;
724 }
715 } 725 }
716 726
717 /* 727 /*
718 * move the descriptors to a temporary list so we can drop the lock 728 * The hardware is idle and ready for more when the
719 * during the entire cleanup operation 729 * chain_running list is empty.
720 */ 730 */
721 list_splice_tail_init(&chan->chain_running, &chain_cleanup); 731 chan->idle = list_empty(&chan->chain_running);
722
723 /* the hardware is now idle and ready for more */
724 chan->idle = true;
725 732
726 /* Start any pending transactions automatically */ 733 /* Start any pending transactions automatically */
727 start_pending_queue(chan); 734 start_pending_queue(chan);