diff options
Diffstat (limited to 'drivers/dma')
-rw-r--r-- | drivers/dma/amba-pl08x.c | 53 | ||||
-rw-r--r-- | drivers/dma/imx-sdma.c | 12 | ||||
-rw-r--r-- | drivers/dma/ipu/ipu_idmac.c | 50 |
3 files changed, 39 insertions, 76 deletions
diff --git a/drivers/dma/amba-pl08x.c b/drivers/dma/amba-pl08x.c index 297f48b0cba9..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) |
@@ -267,19 +276,24 @@ static void pl08x_resume_phy_chan(struct pl08x_phy_chan *ch) | |||
267 | } | 276 | } |
268 | 277 | ||
269 | 278 | ||
270 | /* Stops the channel */ | 279 | /* |
271 | static void pl08x_stop_phy_chan(struct pl08x_phy_chan *ch) | 280 | * pl08x_terminate_phy_chan() stops the channel, clears the FIFO and |
281 | * clears any pending interrupt status. This should not be used for | ||
282 | * an on-going transfer, but as a method of shutting down a channel | ||
283 | * (eg, when it's no longer used) or terminating a transfer. | ||
284 | */ | ||
285 | static void pl08x_terminate_phy_chan(struct pl08x_driver_data *pl08x, | ||
286 | struct pl08x_phy_chan *ch) | ||
272 | { | 287 | { |
273 | u32 val; | 288 | u32 val = readl(ch->base + PL080_CH_CONFIG); |
274 | 289 | ||
275 | pl08x_pause_phy_chan(ch); | 290 | val &= ~(PL080_CONFIG_ENABLE | PL080_CONFIG_ERR_IRQ_MASK | |
291 | PL080_CONFIG_TC_IRQ_MASK); | ||
276 | 292 | ||
277 | /* Disable channel */ | ||
278 | val = readl(ch->base + PL080_CH_CONFIG); | ||
279 | val &= ~PL080_CONFIG_ENABLE; | ||
280 | val &= ~PL080_CONFIG_ERR_IRQ_MASK; | ||
281 | val &= ~PL080_CONFIG_TC_IRQ_MASK; | ||
282 | writel(val, ch->base + PL080_CH_CONFIG); | 293 | writel(val, ch->base + PL080_CH_CONFIG); |
294 | |||
295 | writel(1 << ch->id, pl08x->base + PL080_ERR_CLEAR); | ||
296 | writel(1 << ch->id, pl08x->base + PL080_TC_CLEAR); | ||
283 | } | 297 | } |
284 | 298 | ||
285 | static inline u32 get_bytes_in_cctl(u32 cctl) | 299 | static inline u32 get_bytes_in_cctl(u32 cctl) |
@@ -404,13 +418,12 @@ static inline void pl08x_put_phy_channel(struct pl08x_driver_data *pl08x, | |||
404 | { | 418 | { |
405 | unsigned long flags; | 419 | unsigned long flags; |
406 | 420 | ||
421 | spin_lock_irqsave(&ch->lock, flags); | ||
422 | |||
407 | /* Stop the channel and clear its interrupts */ | 423 | /* Stop the channel and clear its interrupts */ |
408 | pl08x_stop_phy_chan(ch); | 424 | pl08x_terminate_phy_chan(pl08x, ch); |
409 | writel((1 << ch->id), pl08x->base + PL080_ERR_CLEAR); | ||
410 | writel((1 << ch->id), pl08x->base + PL080_TC_CLEAR); | ||
411 | 425 | ||
412 | /* Mark it as free */ | 426 | /* Mark it as free */ |
413 | spin_lock_irqsave(&ch->lock, flags); | ||
414 | ch->serving = NULL; | 427 | ch->serving = NULL; |
415 | spin_unlock_irqrestore(&ch->lock, flags); | 428 | spin_unlock_irqrestore(&ch->lock, flags); |
416 | } | 429 | } |
@@ -1449,7 +1462,7 @@ static int pl08x_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd, | |||
1449 | plchan->state = PL08X_CHAN_IDLE; | 1462 | plchan->state = PL08X_CHAN_IDLE; |
1450 | 1463 | ||
1451 | if (plchan->phychan) { | 1464 | if (plchan->phychan) { |
1452 | pl08x_stop_phy_chan(plchan->phychan); | 1465 | pl08x_terminate_phy_chan(pl08x, plchan->phychan); |
1453 | 1466 | ||
1454 | /* | 1467 | /* |
1455 | * Mark physical channel as free and free any slave | 1468 | * Mark physical channel as free and free any slave |
diff --git a/drivers/dma/imx-sdma.c b/drivers/dma/imx-sdma.c index 0123740b973d..b6d1455fa936 100644 --- a/drivers/dma/imx-sdma.c +++ b/drivers/dma/imx-sdma.c | |||
@@ -771,15 +771,15 @@ static void sdma_enable_channel(struct sdma_engine *sdma, int channel) | |||
771 | __raw_writel(1 << channel, sdma->regs + SDMA_H_START); | 771 | __raw_writel(1 << channel, sdma->regs + SDMA_H_START); |
772 | } | 772 | } |
773 | 773 | ||
774 | static dma_cookie_t sdma_assign_cookie(struct sdma_channel *sdma) | 774 | static dma_cookie_t sdma_assign_cookie(struct sdma_channel *sdmac) |
775 | { | 775 | { |
776 | dma_cookie_t cookie = sdma->chan.cookie; | 776 | dma_cookie_t cookie = sdmac->chan.cookie; |
777 | 777 | ||
778 | if (++cookie < 0) | 778 | if (++cookie < 0) |
779 | cookie = 1; | 779 | cookie = 1; |
780 | 780 | ||
781 | sdma->chan.cookie = cookie; | 781 | sdmac->chan.cookie = cookie; |
782 | sdma->desc.cookie = cookie; | 782 | sdmac->desc.cookie = cookie; |
783 | 783 | ||
784 | return cookie; | 784 | return cookie; |
785 | } | 785 | } |
@@ -1142,7 +1142,7 @@ static int __init sdma_get_firmware(struct sdma_engine *sdma, | |||
1142 | /* download the RAM image for SDMA */ | 1142 | /* download the RAM image for SDMA */ |
1143 | sdma_load_script(sdma, ram_code, | 1143 | sdma_load_script(sdma, ram_code, |
1144 | header->ram_code_size, | 1144 | header->ram_code_size, |
1145 | sdma->script_addrs->ram_code_start_addr); | 1145 | addr->ram_code_start_addr); |
1146 | clk_disable(sdma->clk); | 1146 | clk_disable(sdma->clk); |
1147 | 1147 | ||
1148 | sdma_add_scripts(sdma, addr); | 1148 | sdma_add_scripts(sdma, addr); |
@@ -1354,7 +1354,7 @@ err_clk: | |||
1354 | err_request_region: | 1354 | err_request_region: |
1355 | err_irq: | 1355 | err_irq: |
1356 | kfree(sdma); | 1356 | kfree(sdma); |
1357 | return 0; | 1357 | return ret; |
1358 | } | 1358 | } |
1359 | 1359 | ||
1360 | static int __exit sdma_remove(struct platform_device *pdev) | 1360 | static int __exit sdma_remove(struct platform_device *pdev) |
diff --git a/drivers/dma/ipu/ipu_idmac.c b/drivers/dma/ipu/ipu_idmac.c index cb26ee9773d6..c1a125e7d1df 100644 --- a/drivers/dma/ipu/ipu_idmac.c +++ b/drivers/dma/ipu/ipu_idmac.c | |||
@@ -1145,29 +1145,6 @@ static int ipu_disable_channel(struct idmac *idmac, struct idmac_channel *ichan, | |||
1145 | reg = idmac_read_icreg(ipu, IDMAC_CHA_EN); | 1145 | reg = idmac_read_icreg(ipu, IDMAC_CHA_EN); |
1146 | idmac_write_icreg(ipu, reg & ~chan_mask, IDMAC_CHA_EN); | 1146 | idmac_write_icreg(ipu, reg & ~chan_mask, IDMAC_CHA_EN); |
1147 | 1147 | ||
1148 | /* | ||
1149 | * Problem (observed with channel DMAIC_7): after enabling the channel | ||
1150 | * and initialising buffers, there comes an interrupt with current still | ||
1151 | * pointing at buffer 0, whereas it should use buffer 0 first and only | ||
1152 | * generate an interrupt when it is done, then current should already | ||
1153 | * point to buffer 1. This spurious interrupt also comes on channel | ||
1154 | * DMASDC_0. With DMAIC_7 normally, is we just leave the ISR after the | ||
1155 | * first interrupt, there comes the second with current correctly | ||
1156 | * pointing to buffer 1 this time. But sometimes this second interrupt | ||
1157 | * doesn't come and the channel hangs. Clearing BUFx_RDY when disabling | ||
1158 | * the channel seems to prevent the channel from hanging, but it doesn't | ||
1159 | * prevent the spurious interrupt. This might also be unsafe. Think | ||
1160 | * about the IDMAC controller trying to switch to a buffer, when we | ||
1161 | * clear the ready bit, and re-enable it a moment later. | ||
1162 | */ | ||
1163 | reg = idmac_read_ipureg(ipu, IPU_CHA_BUF0_RDY); | ||
1164 | idmac_write_ipureg(ipu, 0, IPU_CHA_BUF0_RDY); | ||
1165 | idmac_write_ipureg(ipu, reg & ~(1UL << channel), IPU_CHA_BUF0_RDY); | ||
1166 | |||
1167 | reg = idmac_read_ipureg(ipu, IPU_CHA_BUF1_RDY); | ||
1168 | idmac_write_ipureg(ipu, 0, IPU_CHA_BUF1_RDY); | ||
1169 | idmac_write_ipureg(ipu, reg & ~(1UL << channel), IPU_CHA_BUF1_RDY); | ||
1170 | |||
1171 | spin_unlock_irqrestore(&ipu->lock, flags); | 1148 | spin_unlock_irqrestore(&ipu->lock, flags); |
1172 | 1149 | ||
1173 | return 0; | 1150 | return 0; |
@@ -1246,33 +1223,6 @@ static irqreturn_t idmac_interrupt(int irq, void *dev_id) | |||
1246 | 1223 | ||
1247 | /* Other interrupts do not interfere with this channel */ | 1224 | /* Other interrupts do not interfere with this channel */ |
1248 | spin_lock(&ichan->lock); | 1225 | spin_lock(&ichan->lock); |
1249 | if (unlikely(chan_id != IDMAC_SDC_0 && chan_id != IDMAC_SDC_1 && | ||
1250 | ((curbuf >> chan_id) & 1) == ichan->active_buffer && | ||
1251 | !list_is_last(ichan->queue.next, &ichan->queue))) { | ||
1252 | int i = 100; | ||
1253 | |||
1254 | /* This doesn't help. See comment in ipu_disable_channel() */ | ||
1255 | while (--i) { | ||
1256 | curbuf = idmac_read_ipureg(&ipu_data, IPU_CHA_CUR_BUF); | ||
1257 | if (((curbuf >> chan_id) & 1) != ichan->active_buffer) | ||
1258 | break; | ||
1259 | cpu_relax(); | ||
1260 | } | ||
1261 | |||
1262 | if (!i) { | ||
1263 | spin_unlock(&ichan->lock); | ||
1264 | dev_dbg(dev, | ||
1265 | "IRQ on active buffer on channel %x, active " | ||
1266 | "%d, ready %x, %x, current %x!\n", chan_id, | ||
1267 | ichan->active_buffer, ready0, ready1, curbuf); | ||
1268 | return IRQ_NONE; | ||
1269 | } else | ||
1270 | dev_dbg(dev, | ||
1271 | "Buffer deactivated on channel %x, active " | ||
1272 | "%d, ready %x, %x, current %x, rest %d!\n", chan_id, | ||
1273 | ichan->active_buffer, ready0, ready1, curbuf, i); | ||
1274 | } | ||
1275 | |||
1276 | if (unlikely((ichan->active_buffer && (ready1 >> chan_id) & 1) || | 1226 | if (unlikely((ichan->active_buffer && (ready1 >> chan_id) & 1) || |
1277 | (!ichan->active_buffer && (ready0 >> chan_id) & 1) | 1227 | (!ichan->active_buffer && (ready0 >> chan_id) & 1) |
1278 | )) { | 1228 | )) { |