diff options
author | Laxman Dewangan <ldewangan@nvidia.com> | 2011-02-22 14:46:55 +0530 |
---|---|---|
committer | Varun Colbert <vcolbert@nvidia.com> | 2011-03-03 10:39:48 -0800 |
commit | 58e9b14c5aec6590c9be6a6e5894e406aee5eef8 (patch) | |
tree | 4a0ee37824733a9897ec61509d97ebb06521ba78 | |
parent | 715a591214008391fe40fccca49ba133ac89dbf5 (diff) |
arm: tegra: dma: Api for getting transfer count
Adding api for getting the amount of data trsnaferred by dma.
Change-Id: I348b8a2f0f855165fb1bf74f0d9013faa97056e7
Reviewed-on: http://git-master/r/20377
Tested-by: Sumit Bhattacharya <sumitb@nvidia.com>
Reviewed-by: Bharat Nihalani <bnihalani@nvidia.com>
-rw-r--r-- | arch/arm/mach-tegra/dma.c | 83 | ||||
-rw-r--r-- | arch/arm/mach-tegra/include/mach/dma.h | 2 |
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); |