summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRussell King - ARM Linux <linux@arm.linux.org.uk>2011-01-27 13:37:44 +0100
committerDan Williams <dan.j.williams@intel.com>2011-01-31 07:03:47 +0100
commit8179661694595eb3a4f2ff9bb0b73acbb7d2f4a9 (patch)
tree5251a404b7b58f0486f422d3db333b2449e33e5c
parentDMA: PL08x: fix infinite wait when terminating transfers (diff)
downloadlinux-8179661694595eb3a4f2ff9bb0b73acbb7d2f4a9.tar.xz
linux-8179661694595eb3a4f2ff9bb0b73acbb7d2f4a9.zip
DMA: PL08x: fix channel pausing to timeout rather than lockup
If a transfer is initiated from memory to a peripheral, then data is fetched and the channel is marked busy. This busy status persists until the HALT bit is set and the queued data has been transfered to the peripheral. Waiting indefinitely after setting the HALT bit results in system lockups. Timeout this operation, and print an error when this happens. Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk> Acked-by: Linus Walleij <linus.walleij@stericsson.com> Signed-off-by: Dan Williams <dan.j.williams@intel.com>
-rw-r--r--drivers/dma/amba-pl08x.c21
1 files changed, 15 insertions, 6 deletions
diff --git a/drivers/dma/amba-pl08x.c b/drivers/dma/amba-pl08x.c
index 8321a3997c95..07bca4970e50 100644
--- a/drivers/dma/amba-pl08x.c
+++ b/drivers/dma/amba-pl08x.c
@@ -79,6 +79,7 @@
#include <linux/module.h>
#include <linux/interrupt.h>
#include <linux/slab.h>
+#include <linux/delay.h>
#include <linux/dmapool.h>
#include <linux/dmaengine.h>
#include <linux/amba/bus.h>
@@ -235,16 +236,19 @@ static void pl08x_start_txd(struct pl08x_dma_chan *plchan,
}
/*
- * Overall DMAC remains enabled always.
+ * Pause the channel by setting the HALT bit.
*
- * Disabling individual channels could lose data.
+ * For M->P transfers, pause the DMAC first and then stop the peripheral -
+ * the FIFO can only drain if the peripheral is still requesting data.
+ * (note: this can still timeout if the DMAC FIFO never drains of data.)
*
- * Disable the peripheral DMA after disabling the DMAC in order to allow
- * the DMAC FIFO to drain, and hence allow the channel to show inactive
+ * For P->M transfers, disable the peripheral first to stop it filling
+ * the DMAC FIFO, and then pause the DMAC.
*/
static void pl08x_pause_phy_chan(struct pl08x_phy_chan *ch)
{
u32 val;
+ int timeout;
/* Set the HALT bit and wait for the FIFO to drain */
val = readl(ch->base + PL080_CH_CONFIG);
@@ -252,8 +256,13 @@ static void pl08x_pause_phy_chan(struct pl08x_phy_chan *ch)
writel(val, ch->base + PL080_CH_CONFIG);
/* Wait for channel inactive */
- while (pl08x_phy_channel_busy(ch))
- cpu_relax();
+ for (timeout = 1000; timeout; timeout--) {
+ if (!pl08x_phy_channel_busy(ch))
+ break;
+ udelay(1);
+ }
+ if (pl08x_phy_channel_busy(ch))
+ pr_err("pl08x: channel%u timeout waiting for pause\n", ch->id);
}
static void pl08x_resume_phy_chan(struct pl08x_phy_chan *ch)