@@ -435,7 +435,7 @@ static void bcm2835_dma_fill_cb_chain_with_sg(
static void bcm2835_dma_abort(struct bcm2835_chan *c)
{
void __iomem *chan_base = c->chan_base;
- long timeout = 10000;
+ long timeout = 100;
/*
* A zero control block address means the channel is idle.
@@ -444,19 +444,33 @@ static void bcm2835_dma_abort(struct bcm2835_chan *c)
if (!readl(chan_base + BCM2835_DMA_ADDR))
return;
- /* Write 0 to the active bit - Pause the DMA */
- writel(0, chan_base + BCM2835_DMA_CS);
+ /* We need to clear the next DMA block pending */
+ writel(0, chan_base + BCM2835_DMA_NEXTCB);
+
+ /* Abort the DMA, which needs to be enabled to complete */
+ writel(readl(chan_base + BCM2835_DMA_CS) | BCM2835_DMA_ABORT | BCM2835_DMA_ACTIVE,
+ chan_base + BCM2835_DMA_CS);
- /* Wait for any current AXI transfer to complete */
- while ((readl(chan_base + BCM2835_DMA_CS) &
- BCM2835_DMA_WAITING_FOR_WRITES) && --timeout)
+ /* wait for DMA to be aborted */
+ while ((readl(chan_base + BCM2835_DMA_CS) & BCM2835_DMA_ABORT) && --timeout)
cpu_relax();
- /* Peripheral might be stuck and fail to signal AXI write responses */
- if (!timeout)
+ /* Write 0 to the active bit - Pause the DMA */
+ writel(readl(chan_base + BCM2835_DMA_CS) & ~BCM2835_DMA_ACTIVE,
+ chan_base + BCM2835_DMA_CS);
+
+ /*
+ * Peripheral might be stuck and fail to complete
+ * This is expected when dreqs are enabled but not asserted
+ * so only report error in non dreq case
+ */
+ if (!timeout && !(readl(chan_base + BCM2835_DMA_TI) &
+ (BCM2835_DMA_S_DREQ | BCM2835_DMA_D_DREQ)))
dev_err(c->vc.chan.device->dev,
- "failed to complete outstanding writes\n");
+ "failed to complete pause on dma %d (CS:%08x)\n", c->ch,
+ readl(chan_base + BCM2835_DMA_CS));
+ /* Set CS back to default state and reset the DMA */
writel(BCM2835_DMA_RESET, chan_base + BCM2835_DMA_CS);
}