Skip to content

Commit

Permalink
bcm2835-dma: Fix dma_abort for non-40bit channels
Browse files Browse the repository at this point in the history
The sequence we were doing was not safe.

Clearing CS meant BCM2835_DMA_WAIT_FOR_WRITES was cleared
and so polling BCM2835_DMA_WAITING_FOR_WRITES has no benefit

Broadcom have provided a recommended sequence to abort
a dma lite channel, so switch to that.

Signed-off-by: Dom Cobley <popcornmix@gmail.com>
  • Loading branch information
popcornmix authored and pelwell committed May 16, 2023
1 parent 2510319 commit 9cc6a53
Showing 1 changed file with 27 additions and 4 deletions.
31 changes: 27 additions & 4 deletions drivers/dma/bcm2835-dma.c
Original file line number Diff line number Diff line change
Expand Up @@ -675,20 +675,43 @@ static void bcm2835_dma_abort(struct bcm2835_chan *c)
writel(0, chan_base + BCM2711_DMA40_CS);
writel(0, chan_base + BCM2711_DMA40_CB);
} else {
/*
* A zero control block address means the channel is idle.
* (The ACTIVE flag in the CS register is not a reliable indicator.)
*/
if (!readl(chan_base + BCM2835_DMA_ADDR))
return;

/* Write 0 to the active bit - Pause the DMA */
writel(0, chan_base + BCM2835_DMA_CS);
writel(readl(chan_base + BCM2835_DMA_CS) & ~BCM2835_DMA_ACTIVE,
chan_base + BCM2835_DMA_CS);

/* Wait for any current AXI transfer to complete */
/* wait for DMA to be paused */
while ((readl(chan_base + BCM2835_DMA_CS) & BCM2835_DMA_WAITING_FOR_WRITES)
&& --timeout)
cpu_relax();

/* Peripheral might be stuck and fail to signal AXI write responses */
if (!timeout)
dev_err(c->vc.chan.device->dev,
"failed to complete outstanding writes\n");
"failed to pause dma\n");

/* 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);

writel(BCM2835_DMA_RESET, chan_base + BCM2835_DMA_CS);
/* wait for DMA to have been aborted */
timeout = 10000;
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)
dev_err(c->vc.chan.device->dev,
"failed to abort dma\n");
}
}

Expand Down

0 comments on commit 9cc6a53

Please sign in to comment.