summaryrefslogtreecommitdiff
path: root/drivers/dma
diff options
context:
space:
mode:
authorDamien.Horsley <Damien.Horsley@imgtec.com>2015-12-10 15:07:23 +0000
committerVinod Koul <vinod.koul@intel.com>2015-12-18 11:12:29 +0530
commit0c328de77148ddccaa7a2c31f5751e4d443c213b (patch)
tree9f44db2ab2fd46006f971b7c88a13cdbd24e7227 /drivers/dma
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')
-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,
return ret;
}
+static unsigned int mdc_get_new_events(struct mdc_chan *mchan)
+{
+ u32 val, processed, done1, done2;
+ unsigned int ret;
+
+ val = mdc_chan_readl(mchan, MDC_CMDS_PROCESSED);
+ processed = (val >> MDC_CMDS_PROCESSED_CMDS_PROCESSED_SHIFT) &
+ MDC_CMDS_PROCESSED_CMDS_PROCESSED_MASK;
+ /*
+ * CMDS_DONE may have incremented between reading CMDS_PROCESSED
+ * and clearing INT_ACTIVE. Re-read CMDS_PROCESSED to ensure we
+ * didn't miss a command completion.
+ */
+ do {
+ val = mdc_chan_readl(mchan, MDC_CMDS_PROCESSED);
+
+ done1 = (val >> MDC_CMDS_PROCESSED_CMDS_DONE_SHIFT) &
+ MDC_CMDS_PROCESSED_CMDS_DONE_MASK;
+
+ val &= ~((MDC_CMDS_PROCESSED_CMDS_PROCESSED_MASK <<
+ MDC_CMDS_PROCESSED_CMDS_PROCESSED_SHIFT) |
+ MDC_CMDS_PROCESSED_INT_ACTIVE);
+
+ val |= done1 << MDC_CMDS_PROCESSED_CMDS_PROCESSED_SHIFT;
+
+ mdc_chan_writel(mchan, val, MDC_CMDS_PROCESSED);
+
+ val = mdc_chan_readl(mchan, MDC_CMDS_PROCESSED);
+
+ done2 = (val >> MDC_CMDS_PROCESSED_CMDS_DONE_SHIFT) &
+ MDC_CMDS_PROCESSED_CMDS_DONE_MASK;
+ } while (done1 != done2);
+
+ if (done1 >= processed)
+ ret = done1 - processed;
+ else
+ ret = ((MDC_CMDS_PROCESSED_CMDS_PROCESSED_MASK + 1) -
+ processed) + done1;
+
+ return ret;
+}
+
static int mdc_terminate_all(struct dma_chan *chan)
{
struct mdc_chan *mchan = to_mdc_chan(chan);
@@ -667,6 +709,8 @@ static int mdc_terminate_all(struct dma_chan *chan)
mchan->desc = NULL;
vchan_get_all_descriptors(&mchan->vc, &head);
+ mdc_get_new_events(mchan);
+
spin_unlock_irqrestore(&mchan->vc.lock, flags);
if (mdesc)
@@ -703,35 +747,17 @@ static irqreturn_t mdc_chan_irq(int irq, void *dev_id)
{
struct mdc_chan *mchan = (struct mdc_chan *)dev_id;
struct mdc_tx_desc *mdesc;
- u32 val, processed, done1, done2;
- unsigned int i;
+ unsigned int i, new_events;
spin_lock(&mchan->vc.lock);
- val = mdc_chan_readl(mchan, MDC_CMDS_PROCESSED);
- processed = (val >> MDC_CMDS_PROCESSED_CMDS_PROCESSED_SHIFT) &
- MDC_CMDS_PROCESSED_CMDS_PROCESSED_MASK;
- /*
- * CMDS_DONE may have incremented between reading CMDS_PROCESSED
- * and clearing INT_ACTIVE. Re-read CMDS_PROCESSED to ensure we
- * didn't miss a command completion.
- */
- do {
- val = mdc_chan_readl(mchan, MDC_CMDS_PROCESSED);
- done1 = (val >> MDC_CMDS_PROCESSED_CMDS_DONE_SHIFT) &
- MDC_CMDS_PROCESSED_CMDS_DONE_MASK;
- val &= ~((MDC_CMDS_PROCESSED_CMDS_PROCESSED_MASK <<
- MDC_CMDS_PROCESSED_CMDS_PROCESSED_SHIFT) |
- MDC_CMDS_PROCESSED_INT_ACTIVE);
- val |= done1 << MDC_CMDS_PROCESSED_CMDS_PROCESSED_SHIFT;
- mdc_chan_writel(mchan, val, MDC_CMDS_PROCESSED);
- val = mdc_chan_readl(mchan, MDC_CMDS_PROCESSED);
- done2 = (val >> MDC_CMDS_PROCESSED_CMDS_DONE_SHIFT) &
- MDC_CMDS_PROCESSED_CMDS_DONE_MASK;
- } while (done1 != done2);
-
dev_dbg(mdma2dev(mchan->mdma), "IRQ on channel %d\n", mchan->chan_nr);
+ new_events = mdc_get_new_events(mchan);
+
+ if (!new_events)
+ goto out;
+
mdesc = mchan->desc;
if (!mdesc) {
dev_warn(mdma2dev(mchan->mdma),
@@ -740,8 +766,7 @@ static irqreturn_t mdc_chan_irq(int irq, void *dev_id)
goto out;
}
- for (i = processed; i != done1;
- i = (i + 1) % (MDC_CMDS_PROCESSED_CMDS_PROCESSED_MASK + 1)) {
+ for (i = 0; i < new_events; i++) {
/*
* The first interrupt in a transfer indicates that the
* command list has been loaded, not that a command has