[PATCH 3/3] DMA: AT91: Check fifo empty

Elen Song elen.song at atmel.com
Fri Dec 7 04:12:09 EST 2012


The data may still remain in dma fifo when caculate the residue,
check fifo empty flag until dma timeout, if not, restart a new tasklet to finish remain data.

Signed-off-by: Elen Song <elen.song at atmel.com>
---
 drivers/dma/at_hdmac.c |   20 +++++++++++++++++++-
 1 file changed, 19 insertions(+), 1 deletion(-)

diff --git a/drivers/dma/at_hdmac.c b/drivers/dma/at_hdmac.c
index d076fb7..e773303 100644
--- a/drivers/dma/at_hdmac.c
+++ b/drivers/dma/at_hdmac.c
@@ -24,6 +24,7 @@
 #include <linux/slab.h>
 #include <linux/of.h>
 #include <linux/of_device.h>
+#include <linux/jiffies.h>
 
 #include "at_hdmac_regs.h"
 #include "dmaengine.h"
@@ -41,6 +42,8 @@
 #define	ATC_DEFAULT_CTRLB	(ATC_SIF(AT_DMA_MEM_IF) \
 				|ATC_DIF(AT_DMA_MEM_IF))
 
+/* DMA timeout (10ms)*/
+#define DMA_TIMEOUT		10
 /*
  * Initial number of descriptors to allocate for each channel. This could
  * be increased during dma usage.
@@ -268,9 +271,12 @@ static struct at_desc *atc_get_current_descriptors(struct at_dma_chan *atchan,
 static int atc_get_bytes_left(struct dma_chan *chan)
 {
 	struct at_dma_chan      *atchan = to_at_dma_chan(chan);
+	struct at_dma           *atdma = to_at_dma(chan->device);
+	int	chan_id = atchan->chan_common.chan_id;
 	struct at_desc *desc_first = atc_first_active(atchan);
 	struct at_desc *desc_cur;
 	int ret = 0, count = 0;
+	unsigned long timeout = jiffies + msecs_to_jiffies(DMA_TIMEOUT);
 
 	/*
 	 * Initialize necessary values in the first time.
@@ -280,6 +286,7 @@ static int atc_get_bytes_left(struct dma_chan *chan)
 		/* First descriptor embedds the transaction length */
 		atchan->remain_desc = desc_first->len;
 
+start:
 	/* Channel should be paused before get residue */
 	if (!atc_chan_is_paused(atchan))
 		atc_control(chan, DMA_PAUSE, 0);
@@ -311,7 +318,18 @@ static int atc_get_bytes_left(struct dma_chan *chan)
 				<< (desc_first->tx_buswidth);
 		ret = atchan->remain_desc - count;
 	}
-
+	/*
+	 * Check fifo empty in pre-determined time, if not,
+	 * restart a new taskelt to finish remain task.
+	 */
+	if (!(dma_readl(atdma, CHSR) & AT_DMA_EMPT(chan_id))) {
+		if (time_before(jiffies, timeout)) {
+			goto start;
+		} else {
+			dev_vdbg(chan2dev(chan), "fifo is not empty, restart a new tasklet\n");
+			tasklet_schedule(&atchan->tasklet);
+		}
+	}
 out:
 	if (atc_chan_is_paused(atchan))
 		atc_control(chan, DMA_RESUME, 0);
-- 
1.7.9.5




More information about the linux-arm-kernel mailing list