|
@@ -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)
|