From 668d6d94a7085518fc84c3809b2ba6048eea431f Mon Sep 17 00:00:00 2001 From: Laxman Dewangan Date: Wed, 21 Mar 2012 17:54:21 +0530 Subject: 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 Reviewed-on: http://git-master/r/90815 Reviewed-by: Automatic_Commit_Validation_User --- arch/arm/mach-tegra/dma.c | 84 ++++++++++++++++++++++++++++------ arch/arm/mach-tegra/include/mach/dma.h | 9 ++++ 2 files changed, 78 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; diff --git a/arch/arm/mach-tegra/include/mach/dma.h b/arch/arm/mach-tegra/include/mach/dma.h index 83840377b8b4..c6617e95c1af 100644 --- a/arch/arm/mach-tegra/include/mach/dma.h +++ b/arch/arm/mach-tegra/include/mach/dma.h @@ -189,6 +189,15 @@ bool tegra_dma_is_empty(struct tegra_dma_channel *ch); struct tegra_dma_channel *tegra_dma_allocate_channel(int mode, const char namefmt[], ...); void tegra_dma_free_channel(struct tegra_dma_channel *ch); + +/* + * tegra_dma_cancel: Stop the dma and remove all request from pending request + * queue for transfer. + * The pending list for data transfer will become empty after this callback. + * The status of each request will be marked as ABORTED. + * bytes_transferred in each requests shows the actual bytes transferred by dma. + * Callbacks will not be called when cancel the requests. +*/ int tegra_dma_cancel(struct tegra_dma_channel *ch); int __init tegra_dma_init(void); -- cgit v1.2.3