summaryrefslogtreecommitdiff
path: root/arch/arm/mach-tegra/dma.c
diff options
context:
space:
mode:
authorLaxman Dewangan <ldewangan@nvidia.com>2012-03-21 17:54:21 +0530
committerSimone Willett <swillett@nvidia.com>2012-04-03 17:51:51 -0700
commit668d6d94a7085518fc84c3809b2ba6048eea431f (patch)
tree2220a74e50556343b928b4cb0673ab57b296f2dc /arch/arm/mach-tegra/dma.c
parent7ddd7f619e68d0c1fc8665e6a9b72db292ac752a (diff)
ARM: tegra: dma: Update actual bytes_transferred in dma cancel
When canceling dma, updating actual bytes transferred by dma, making all requests status to aborted and deleting from channel request queue. Change-Id: I860780814340d54465de5b2ae11a6895319f428c Signed-off-by: Laxman Dewangan <ldewangan@nvidia.com> Reviewed-on: http://git-master/r/90815 Reviewed-by: Automatic_Commit_Validation_User
Diffstat (limited to 'arch/arm/mach-tegra/dma.c')
-rw-r--r--arch/arm/mach-tegra/dma.c84
1 files changed, 69 insertions, 15 deletions
diff --git a/arch/arm/mach-tegra/dma.c b/arch/arm/mach-tegra/dma.c
index f8145844cb81..66f9c24a946c 100644
--- a/arch/arm/mach-tegra/dma.c
+++ b/arch/arm/mach-tegra/dma.c
@@ -157,6 +157,7 @@ static bool tegra_dma_update_hw_partial(struct tegra_dma_channel *ch,
static void handle_oneshot_dma(struct tegra_dma_channel *ch);
static void handle_continuous_dbl_dma(struct tegra_dma_channel *ch);
static void handle_continuous_sngl_dma(struct tegra_dma_channel *ch);
+static void handle_dma_isr_locked(struct tegra_dma_channel *ch);
void tegra_dma_flush(struct tegra_dma_channel *ch)
{
@@ -180,21 +181,6 @@ static void tegra_dma_stop(struct tegra_dma_channel *ch)
writel(status, ch->addr + APB_DMA_CHAN_STA);
}
-int tegra_dma_cancel(struct tegra_dma_channel *ch)
-{
- unsigned long irq_flags;
-
- spin_lock_irqsave(&ch->lock, irq_flags);
- while (!list_empty(&ch->list))
- list_del(ch->list.next);
-
- tegra_dma_stop(ch);
-
- spin_unlock_irqrestore(&ch->lock, irq_flags);
- return 0;
-}
-EXPORT_SYMBOL(tegra_dma_cancel);
-
static void pause_dma(bool wait_for_burst_complete)
{
spin_lock(&enable_lock);
@@ -413,6 +399,74 @@ skip_status:
}
EXPORT_SYMBOL(tegra_dma_dequeue_req);
+int tegra_dma_cancel(struct tegra_dma_channel *ch)
+{
+ struct tegra_dma_req *hreq = NULL;
+ unsigned long status;
+ unsigned long irq_flags;
+ struct tegra_dma_req *cb_req = NULL;
+ dma_callback callback = NULL;
+ struct list_head new_list;
+
+ INIT_LIST_HEAD(&new_list);
+
+ if (ch->mode & TEGRA_DMA_SHARED) {
+ pr_err("Can not abort requests from shared channel %d\n",
+ ch->id);
+ return -EPERM;
+ }
+
+ spin_lock_irqsave(&ch->lock, irq_flags);
+
+ /* If list is empty, return with error*/
+ if (list_empty(&ch->list)) {
+ spin_unlock_irqrestore(&ch->lock, irq_flags);
+ return 0;
+ }
+
+ /* Pause dma before checking the queue status */
+ pause_dma(true);
+ status = readl(ch->addr + APB_DMA_CHAN_STA);
+ if (status & STA_ISE_EOC) {
+ handle_dma_isr_locked(ch);
+ cb_req = ch->cb_req;
+ callback = ch->callback;
+ ch->cb_req = NULL;
+ ch->callback = NULL;
+ /* Read status because it may get changed */
+ status = readl(ch->addr + APB_DMA_CHAN_STA);
+ }
+
+ /* Abort head requests, stop dma and dequeue all requests */
+ if (!list_empty(&ch->list)) {
+ tegra_dma_stop(ch);
+ hreq = list_entry(ch->list.next, typeof(*hreq), node);
+ hreq->bytes_transferred +=
+ get_current_xferred_count(ch, hreq, status);
+
+ /* copy the list into new list. */
+ list_replace_init(&ch->list, &new_list);
+ }
+
+ resume_dma();
+
+ spin_unlock_irqrestore(&ch->lock, irq_flags);
+
+ /* Call callback if it is due from interrupts */
+ if (callback)
+ callback(cb_req);
+
+ /* Abort all requests on list. */
+ while (!list_empty(&new_list)) {
+ hreq = list_entry(new_list.next, typeof(*hreq), node);
+ hreq->status = -TEGRA_DMA_REQ_ERROR_ABORTED;
+ list_del(&hreq->node);
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL(tegra_dma_cancel);
+
bool tegra_dma_is_empty(struct tegra_dma_channel *ch)
{
unsigned long irq_flags;