diff mbox

[10/11] dmaengine: cppi41: Fix da8xx interrupt issue

Message ID 20170109160656.3470-11-abailon@baylibre.com (mailing list archive)
State New, archived
Headers show

Commit Message

Alexandre Bailon Jan. 9, 2017, 4:06 p.m. UTC
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@baylibre.com>
---
 drivers/dma/cppi41.c | 30 +++++++++++++++++++++++++-----
 1 file changed, 25 insertions(+), 5 deletions(-)
diff mbox

Patch

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;