aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-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 @@
79#include <linux/module.h> 79#include <linux/module.h>
80#include <linux/interrupt.h> 80#include <linux/interrupt.h>
81#include <linux/slab.h> 81#include <linux/slab.h>
82#include <linux/delay.h>
82#include <linux/dmapool.h> 83#include <linux/dmapool.h>
83#include <linux/dmaengine.h> 84#include <linux/dmaengine.h>
84#include <linux/amba/bus.h> 85#include <linux/amba/bus.h>
@@ -235,16 +236,19 @@ static void pl08x_start_txd(struct pl08x_dma_chan *plchan,
235} 236}
236 237
237/* 238/*
238 * Overall DMAC remains enabled always. 239 * Pause the channel by setting the HALT bit.
239 * 240 *
240 * Disabling individual channels could lose data. 241 * For M->P transfers, pause the DMAC first and then stop the peripheral -
242 * the FIFO can only drain if the peripheral is still requesting data.
243 * (note: this can still timeout if the DMAC FIFO never drains of data.)
241 * 244 *
242 * Disable the peripheral DMA after disabling the DMAC in order to allow 245 * For P->M transfers, disable the peripheral first to stop it filling
243 * the DMAC FIFO to drain, and hence allow the channel to show inactive 246 * the DMAC FIFO, and then pause the DMAC.
244 */ 247 */
245static void pl08x_pause_phy_chan(struct pl08x_phy_chan *ch) 248static void pl08x_pause_phy_chan(struct pl08x_phy_chan *ch)
246{ 249{
247 u32 val; 250 u32 val;
251 int timeout;
248 252
249 /* Set the HALT bit and wait for the FIFO to drain */ 253 /* Set the HALT bit and wait for the FIFO to drain */
250 val = readl(ch->base + PL080_CH_CONFIG); 254 val = readl(ch->base + PL080_CH_CONFIG);
@@ -252,8 +256,13 @@ static void pl08x_pause_phy_chan(struct pl08x_phy_chan *ch)
252 writel(val, ch->base + PL080_CH_CONFIG); 256 writel(val, ch->base + PL080_CH_CONFIG);
253 257
254 /* Wait for channel inactive */ 258 /* Wait for channel inactive */
255 while (pl08x_phy_channel_busy(ch)) 259 for (timeout = 1000; timeout; timeout--) {
256 cpu_relax(); 260 if (!pl08x_phy_channel_busy(ch))
261 break;
262 udelay(1);
263 }
264 if (pl08x_phy_channel_busy(ch))
265 pr_err("pl08x: channel%u timeout waiting for pause\n", ch->id);
257} 266}
258 267
259static void pl08x_resume_phy_chan(struct pl08x_phy_chan *ch) 268static void pl08x_resume_phy_chan(struct pl08x_phy_chan *ch)