aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/dma/img-mdc-dma.c
diff options
context:
space:
mode:
authorDamien.Horsley <Damien.Horsley@imgtec.com>2015-12-10 10:07:23 -0500
committerVinod Koul <vinod.koul@intel.com>2015-12-18 00:42:29 -0500
commit0c328de77148ddccaa7a2c31f5751e4d443c213b (patch)
tree9f44db2ab2fd46006f971b7c88a13cdbd24e7227 /drivers/dma/img-mdc-dma.c
parent4fa2d09c1ae879c2ee2760ab419a4f97026dd97b (diff)
dmaengine: mdc: Correct terminate_all handling
Use of the CANCEL bit in mdc_terminate_all creates an additional 'command done' to appear in the registers (in addition to an interrupt). In addition, there is a potential race between mdc_terminate_all and the irq handler if a transfer completes at the same time as the terminate all (presently this results in an inappropriate warning). To handle these issues, any outstanding 'command done' events are cleared during mdc_terminate_all and the irq handler takes no action when there are no new 'command done' events. Signed-off-by: Damien.Horsley <Damien.Horsley@imgtec.com> Signed-off-by: Vinod Koul <vinod.koul@intel.com>
Diffstat (limited to 'drivers/dma/img-mdc-dma.c')
-rw-r--r--drivers/dma/img-mdc-dma.c77
1 files changed, 51 insertions, 26 deletions
diff --git a/drivers/dma/img-mdc-dma.c b/drivers/dma/img-mdc-dma.c
index 42ae58d1e303..a4c53be482cf 100644
--- a/drivers/dma/img-mdc-dma.c
+++ b/drivers/dma/img-mdc-dma.c
@@ -651,6 +651,48 @@ static enum dma_status mdc_tx_status(struct dma_chan *chan,
651 return ret; 651 return ret;
652} 652}
653 653
654static unsigned int mdc_get_new_events(struct mdc_chan *mchan)
655{
656 u32 val, processed, done1, done2;
657 unsigned int ret;
658
659 val = mdc_chan_readl(mchan, MDC_CMDS_PROCESSED);
660 processed = (val >> MDC_CMDS_PROCESSED_CMDS_PROCESSED_SHIFT) &
661 MDC_CMDS_PROCESSED_CMDS_PROCESSED_MASK;
662 /*
663 * CMDS_DONE may have incremented between reading CMDS_PROCESSED
664 * and clearing INT_ACTIVE. Re-read CMDS_PROCESSED to ensure we
665 * didn't miss a command completion.
666 */
667 do {
668 val = mdc_chan_readl(mchan, MDC_CMDS_PROCESSED);
669
670 done1 = (val >> MDC_CMDS_PROCESSED_CMDS_DONE_SHIFT) &
671 MDC_CMDS_PROCESSED_CMDS_DONE_MASK;
672
673 val &= ~((MDC_CMDS_PROCESSED_CMDS_PROCESSED_MASK <<
674 MDC_CMDS_PROCESSED_CMDS_PROCESSED_SHIFT) |
675 MDC_CMDS_PROCESSED_INT_ACTIVE);
676
677 val |= done1 << MDC_CMDS_PROCESSED_CMDS_PROCESSED_SHIFT;
678
679 mdc_chan_writel(mchan, val, MDC_CMDS_PROCESSED);
680
681 val = mdc_chan_readl(mchan, MDC_CMDS_PROCESSED);
682
683 done2 = (val >> MDC_CMDS_PROCESSED_CMDS_DONE_SHIFT) &
684 MDC_CMDS_PROCESSED_CMDS_DONE_MASK;
685 } while (done1 != done2);
686
687 if (done1 >= processed)
688 ret = done1 - processed;
689 else
690 ret = ((MDC_CMDS_PROCESSED_CMDS_PROCESSED_MASK + 1) -
691 processed) + done1;
692
693 return ret;
694}
695
654static int mdc_terminate_all(struct dma_chan *chan) 696static int mdc_terminate_all(struct dma_chan *chan)
655{ 697{
656 struct mdc_chan *mchan = to_mdc_chan(chan); 698 struct mdc_chan *mchan = to_mdc_chan(chan);
@@ -667,6 +709,8 @@ static int mdc_terminate_all(struct dma_chan *chan)
667 mchan->desc = NULL; 709 mchan->desc = NULL;
668 vchan_get_all_descriptors(&mchan->vc, &head); 710 vchan_get_all_descriptors(&mchan->vc, &head);
669 711
712 mdc_get_new_events(mchan);
713
670 spin_unlock_irqrestore(&mchan->vc.lock, flags); 714 spin_unlock_irqrestore(&mchan->vc.lock, flags);
671 715
672 if (mdesc) 716 if (mdesc)
@@ -703,35 +747,17 @@ static irqreturn_t mdc_chan_irq(int irq, void *dev_id)
703{ 747{
704 struct mdc_chan *mchan = (struct mdc_chan *)dev_id; 748 struct mdc_chan *mchan = (struct mdc_chan *)dev_id;
705 struct mdc_tx_desc *mdesc; 749 struct mdc_tx_desc *mdesc;
706 u32 val, processed, done1, done2; 750 unsigned int i, new_events;
707 unsigned int i;
708 751
709 spin_lock(&mchan->vc.lock); 752 spin_lock(&mchan->vc.lock);
710 753
711 val = mdc_chan_readl(mchan, MDC_CMDS_PROCESSED);
712 processed = (val >> MDC_CMDS_PROCESSED_CMDS_PROCESSED_SHIFT) &
713 MDC_CMDS_PROCESSED_CMDS_PROCESSED_MASK;
714 /*
715 * CMDS_DONE may have incremented between reading CMDS_PROCESSED
716 * and clearing INT_ACTIVE. Re-read CMDS_PROCESSED to ensure we
717 * didn't miss a command completion.
718 */
719 do {
720 val = mdc_chan_readl(mchan, MDC_CMDS_PROCESSED);
721 done1 = (val >> MDC_CMDS_PROCESSED_CMDS_DONE_SHIFT) &
722 MDC_CMDS_PROCESSED_CMDS_DONE_MASK;
723 val &= ~((MDC_CMDS_PROCESSED_CMDS_PROCESSED_MASK <<
724 MDC_CMDS_PROCESSED_CMDS_PROCESSED_SHIFT) |
725 MDC_CMDS_PROCESSED_INT_ACTIVE);
726 val |= done1 << MDC_CMDS_PROCESSED_CMDS_PROCESSED_SHIFT;
727 mdc_chan_writel(mchan, val, MDC_CMDS_PROCESSED);
728 val = mdc_chan_readl(mchan, MDC_CMDS_PROCESSED);
729 done2 = (val >> MDC_CMDS_PROCESSED_CMDS_DONE_SHIFT) &
730 MDC_CMDS_PROCESSED_CMDS_DONE_MASK;
731 } while (done1 != done2);
732
733 dev_dbg(mdma2dev(mchan->mdma), "IRQ on channel %d\n", mchan->chan_nr); 754 dev_dbg(mdma2dev(mchan->mdma), "IRQ on channel %d\n", mchan->chan_nr);
734 755
756 new_events = mdc_get_new_events(mchan);
757
758 if (!new_events)
759 goto out;
760
735 mdesc = mchan->desc; 761 mdesc = mchan->desc;
736 if (!mdesc) { 762 if (!mdesc) {
737 dev_warn(mdma2dev(mchan->mdma), 763 dev_warn(mdma2dev(mchan->mdma),
@@ -740,8 +766,7 @@ static irqreturn_t mdc_chan_irq(int irq, void *dev_id)
740 goto out; 766 goto out;
741 } 767 }
742 768
743 for (i = processed; i != done1; 769 for (i = 0; i < new_events; i++) {
744 i = (i + 1) % (MDC_CMDS_PROCESSED_CMDS_PROCESSED_MASK + 1)) {
745 /* 770 /*
746 * The first interrupt in a transfer indicates that the 771 * The first interrupt in a transfer indicates that the
747 * command list has been loaded, not that a command has 772 * command list has been loaded, not that a command has