diff options
| -rw-r--r-- | drivers/dma/amba-pl08x.c | 21 |
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 | */ |
| 245 | static void pl08x_pause_phy_chan(struct pl08x_phy_chan *ch) | 248 | static 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 | ||
| 259 | static void pl08x_resume_phy_chan(struct pl08x_phy_chan *ch) | 268 | static void pl08x_resume_phy_chan(struct pl08x_phy_chan *ch) |
