summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLaxman Dewangan <ldewangan@nvidia.com>2011-02-22 14:46:55 +0530
committerVarun Colbert <vcolbert@nvidia.com>2011-03-08 17:40:13 -0800
commit849552d9be1871e621ba61bc21e9ce95968be32e (patch)
tree2b905910895be22924cdb22de60abee771de69d7
parent03bc8a7c8aec3ed2c2bbe60442df9d4789975f1f (diff)
arm: tegra: dma: Api for getting transfer count
Adding api for getting the amount of data trsnaferred by dma. Change-Id: Ic8cdd6f0056589a1ec5610944d0cd0b3bd0000f2 Reviewed-on: http://git-master/r/21997 Tested-by: Sumit Bhattacharya <sumitb@nvidia.com> Reviewed-by: Bharat Nihalani <bnihalani@nvidia.com>
-rw-r--r--arch/arm/mach-tegra/dma.c83
-rw-r--r--arch/arm/mach-tegra/include/mach/dma.h2
2 files changed, 67 insertions, 18 deletions
diff --git a/arch/arm/mach-tegra/dma.c b/arch/arm/mach-tegra/dma.c
index b054764aa214..a80c71819c19 100644
--- a/arch/arm/mach-tegra/dma.c
+++ b/arch/arm/mach-tegra/dma.c
@@ -189,6 +189,39 @@ int tegra_dma_cancel(struct tegra_dma_channel *ch)
return 0;
}
+static unsigned int get_channel_status(struct tegra_dma_channel *ch,
+ struct tegra_dma_req *req, bool is_stop_dma)
+{
+ void __iomem *addr = IO_ADDRESS(TEGRA_APB_DMA_BASE);
+ unsigned int status;
+
+ if (is_stop_dma) {
+ /* STOP the DMA and get the transfer count.
+ * Getting the transfer count is tricky.
+ * - Globally disable DMA on all channels
+ * - Read the channel's status register to know the number
+ * of pending bytes to be transfered.
+ * - Stop the dma channel
+ * - Globally re-enable DMA to resume other transfers
+ */
+ spin_lock(&enable_lock);
+ writel(0, addr + APB_DMA_GEN);
+ udelay(20);
+ status = readl(ch->addr + APB_DMA_CHAN_STA);
+ tegra_dma_stop(ch);
+ writel(GEN_ENABLE, addr + APB_DMA_GEN);
+ spin_unlock(&enable_lock);
+ if (status & STA_ISE_EOC) {
+ pr_err("Got Dma Int here clearing");
+ writel(status, ch->addr + APB_DMA_CHAN_STA);
+ }
+ req->status = TEGRA_DMA_REQ_ERROR_ABORTED;
+ } else {
+ status = readl(ch->addr + APB_DMA_CHAN_STA);
+ }
+ return status;
+}
+
/* should be called with the channel lock held */
static unsigned int dma_active_count(struct tegra_dma_channel *ch,
struct tegra_dma_req *req, unsigned int status)
@@ -232,7 +265,6 @@ int tegra_dma_dequeue_req(struct tegra_dma_channel *ch,
unsigned int status;
unsigned long irq_flags;
int stop = 0;
- void __iomem *addr = IO_ADDRESS(TEGRA_APB_DMA_BASE);
spin_lock_irqsave(&ch->lock, irq_flags);
@@ -254,22 +286,7 @@ int tegra_dma_dequeue_req(struct tegra_dma_channel *ch,
if (!stop)
goto skip_status;
- /* STOP the DMA and get the transfer count.
- * Getting the transfer count is tricky.
- * - Globally disable DMA on all channels
- * - Read the channel's status register to know the number of pending
- * bytes to be transfered.
- * - Stop the dma channel
- * - Globally re-enable DMA to resume other transfers
- */
- spin_lock(&enable_lock);
- writel(0, addr + APB_DMA_GEN);
- udelay(20);
- status = readl(ch->addr + APB_DMA_CHAN_STA);
- tegra_dma_stop(ch);
- writel(GEN_ENABLE, addr + APB_DMA_GEN);
- spin_unlock(&enable_lock);
-
+ status = get_channel_status(ch, req, true);
req->bytes_transferred = dma_active_count(ch, req, status);
if (!list_empty(&ch->list)) {
@@ -322,6 +339,36 @@ bool tegra_dma_is_req_inflight(struct tegra_dma_channel *ch,
return false;
}
EXPORT_SYMBOL(tegra_dma_is_req_inflight);
+int tegra_dma_get_transfer_count(struct tegra_dma_channel *ch,
+ struct tegra_dma_req *req, bool is_stop_dma)
+{
+ unsigned int status;
+ unsigned long irq_flags;
+ int bytes_transferred = 0;
+
+ if (IS_ERR_OR_NULL(ch))
+ BUG();
+
+ spin_lock_irqsave(&ch->lock, irq_flags);
+
+ if (list_entry(ch->list.next, struct tegra_dma_req, node) != req) {
+ spin_unlock_irqrestore(&ch->lock, irq_flags);
+ pr_debug("The dma request is not the head req\n");
+ return req->bytes_transferred;
+ }
+
+ if (req->status != TEGRA_DMA_REQ_INFLIGHT) {
+ spin_unlock_irqrestore(&ch->lock, irq_flags);
+ pr_debug("The dma request is not running\n");
+ return req->bytes_transferred;
+ }
+
+ status = get_channel_status(ch, req, is_stop_dma);
+ bytes_transferred = dma_active_count(ch, req, status);
+ spin_unlock_irqrestore(&ch->lock, irq_flags);
+ return bytes_transferred;
+}
+EXPORT_SYMBOL(tegra_dma_get_transfer_count);
int tegra_dma_enqueue_req(struct tegra_dma_channel *ch,
struct tegra_dma_req *req)
@@ -677,7 +724,7 @@ static void handle_continuous_dbl_dma(struct tegra_dma_channel *ch)
tegra_dma_update_hw_partial(ch, next_req);
}
req->buffer_status = TEGRA_DMA_REQ_BUF_STATUS_HALF_FULL;
- req->status = TEGRA_DMA_REQ_SUCCESS;
+ req->bytes_transferred = req->size >> 1;
/* DMA lock is NOT held when callback is called */
spin_unlock_irqrestore(&ch->lock, irq_flags);
if (likely(req->threshold))
diff --git a/arch/arm/mach-tegra/include/mach/dma.h b/arch/arm/mach-tegra/include/mach/dma.h
index d385b7a251be..cb0248cca615 100644
--- a/arch/arm/mach-tegra/include/mach/dma.h
+++ b/arch/arm/mach-tegra/include/mach/dma.h
@@ -147,6 +147,8 @@ void tegra_dma_flush(struct tegra_dma_channel *ch);
bool tegra_dma_is_req_inflight(struct tegra_dma_channel *ch,
struct tegra_dma_req *req);
+int tegra_dma_get_transfer_count(struct tegra_dma_channel *ch,
+ struct tegra_dma_req *req, bool is_stop_dma);
bool tegra_dma_is_empty(struct tegra_dma_channel *ch);
bool tegra_dma_is_stopped(struct tegra_dma_channel *ch);