aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/dma
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/dma')
-rw-r--r--drivers/dma/amba-pl08x.c53
-rw-r--r--drivers/dma/imx-sdma.c12
-rw-r--r--drivers/dma/ipu/ipu_idmac.c50
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 */
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)
@@ -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/*
271static 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 */
285static 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
285static inline u32 get_bytes_in_cctl(u32 cctl) 299static 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
774static dma_cookie_t sdma_assign_cookie(struct sdma_channel *sdma) 774static 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:
1354err_request_region: 1354err_request_region:
1355err_irq: 1355err_irq:
1356 kfree(sdma); 1356 kfree(sdma);
1357 return 0; 1357 return ret;
1358} 1358}
1359 1359
1360static int __exit sdma_remove(struct platform_device *pdev) 1360static 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 )) {