Sometime, after a teardown, interrupts are not fired anymore. This happen because the interrupt handler doesn't re-assert IRQ. Once the teardown complete, the teardown descriptor is moved to completion queue, which is causing an interrupt. But cppi41_tear_down_chan() is called from atomic section, and it polls the queue until it got the teardown descriptor. Then, the interrupt handler is called but it is not able detect the cause of the interrupt and assume the interrupt has been fired by USB core. In that situation, the IRQ won't be re-asserted and interrupts won't work anymore. Add the td_complete variable to detect an interrupt fired by DMA during a teardown, and then re-assert IRQ. Signed-off-by: Alexandre Bailon <abailon@xxxxxxxxxxxx> --- drivers/dma/cppi41.c | 30 +++++++++++++++++++++++++----- 1 file changed, 25 insertions(+), 5 deletions(-) diff --git a/drivers/dma/cppi41.c b/drivers/dma/cppi41.c index e8470b1..0060391 100644 --- a/drivers/dma/cppi41.c +++ b/drivers/dma/cppi41.c @@ -171,6 +171,8 @@ struct cppi41_dd { /* da8xx clock */ struct clk *clk; + + bool td_complete; }; static struct chan_queues am335x_usb_queues_tx[] = { @@ -398,19 +400,29 @@ static irqreturn_t da8xx_cppi41_irq(int irq, void *data) struct cppi41_dd *cdd = data; u32 status; u32 usbss_status; + irqreturn_t ret = IRQ_NONE; status = cppi_readl(cdd->qmgr_mem + QMGR_PEND(0)); if (status & DA8XX_QMGR_PENDING_MASK) - cppi41_irq(cdd); - else - return IRQ_NONE; + ret = cppi41_irq(cdd); + + if (cdd->td_complete) { + /* + * Spurious IRQ caused by teardown. + * DMA interrupts are not maskable, so there is now way + * to prevent it. + * Just ensure that the IRQ will be re-asserted. + */ + cdd->td_complete = false; + ret = IRQ_HANDLED; + } /* Re-assert IRQ if there no usb core interrupts pending */ usbss_status = cppi_readl(cdd->usbss_mem + DA8XX_INTR_SRC_MASKED); - if (!usbss_status) + if (ret == IRQ_HANDLED && !usbss_status) cppi_writel(0, cdd->usbss_mem + DA8XX_END_OF_INTR); - return IRQ_HANDLED; + return ret; } static dma_cookie_t cppi41_tx_submit(struct dma_async_tx_descriptor *tx) @@ -740,6 +752,14 @@ static int cppi41_tear_down_chan(struct cppi41_channel *c) WARN_ON(!desc_phys); } + /* On DA8xx, we are using the PEND0 register to determine if + * the interrupt is generated by DMA. But because the teardown has + * already been popped from completion queue, PEND0 is clear and + * the interrupt handler will assume the interrupt has been fired + * by the USB core. + */ + cdd->td_complete = true; + c->td_queued = 0; c->td_seen = 0; c->td_desc_seen = 0; -- 2.10.2 -- To unsubscribe from this list: send the line "unsubscribe linux-omap" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html