|
@@ -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
|