diff options
Diffstat (limited to 'drivers/dma/img-mdc-dma.c')
-rw-r--r-- | drivers/dma/img-mdc-dma.c | 78 |
1 files changed, 51 insertions, 27 deletions
diff --git a/drivers/dma/img-mdc-dma.c b/drivers/dma/img-mdc-dma.c index 9ca56830c..a4c53be48 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 @@ -979,7 +1004,6 @@ static int mdc_dma_remove(struct platform_device *pdev) vc.chan.device_node) { list_del(&mchan->vc.chan.device_node); - synchronize_irq(mchan->irq); devm_free_irq(&pdev->dev, mchan->irq, mchan); tasklet_kill(&mchan->vc.task); |