mirror of
https://github.com/openwrt/openwrt.git
synced 2024-12-27 17:18:59 +00:00
69 lines
2.4 KiB
Diff
69 lines
2.4 KiB
Diff
|
From 6609cb1bb479d833d11703b711cd12ade9bf8750 Mon Sep 17 00:00:00 2001
|
||
|
From: Dom Cobley <popcornmix@gmail.com>
|
||
|
Date: Wed, 26 Apr 2023 13:40:34 +0100
|
||
|
Subject: [PATCH] bcm2835-dma: Fix dma_abort for non-40bit channels
|
||
|
|
||
|
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>
|
||
|
---
|
||
|
drivers/dma/bcm2835-dma.c | 31 +++++++++++++++++++++++++++----
|
||
|
1 file changed, 27 insertions(+), 4 deletions(-)
|
||
|
|
||
|
--- a/drivers/dma/bcm2835-dma.c
|
||
|
+++ b/drivers/dma/bcm2835-dma.c
|
||
|
@@ -675,10 +675,18 @@ static void bcm2835_dma_abort(struct bcm
|
||
|
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();
|
||
|
@@ -686,9 +694,24 @@ static void bcm2835_dma_abort(struct bcm
|
||
|
/* 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);
|
||
|
+
|
||
|
+ /* wait for DMA to have been aborted */
|
||
|
+ timeout = 10000;
|
||
|
+ while ((readl(chan_base + BCM2835_DMA_CS) & BCM2835_DMA_ABORT) && --timeout)
|
||
|
+ cpu_relax();
|
||
|
|
||
|
- writel(BCM2835_DMA_RESET, chan_base + BCM2835_DMA_CS);
|
||
|
+ /* 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");
|
||
|
}
|
||
|
}
|
||
|
|