From patchwork Fri Oct 26 07:32:26 2012 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: "Song, Elen" X-Patchwork-Id: 1650061 Return-Path: X-Original-To: patchwork-linux-arm@patchwork.kernel.org Delivered-To: patchwork-process-083081@patchwork1.kernel.org Received: from merlin.infradead.org (merlin.infradead.org [205.233.59.134]) by patchwork1.kernel.org (Postfix) with ESMTP id 4D1E53FD4E for ; Fri, 26 Oct 2012 07:37:50 +0000 (UTC) Received: from localhost ([::1] helo=merlin.infradead.org) by merlin.infradead.org with esmtp (Exim 4.76 #1 (Red Hat Linux)) id 1TReSp-00063Q-Hc; Fri, 26 Oct 2012 07:36:19 +0000 Received: from casper.infradead.org ([2001:770:15f::2]) by merlin.infradead.org with esmtps (Exim 4.76 #1 (Red Hat Linux)) id 1TReSn-00063A-Cj for linux-arm-kernel@merlin.infradead.org; Fri, 26 Oct 2012 07:36:17 +0000 Received: from newsmtp5.atmel.com ([204.2.163.5] helo=sjogate2.atmel.com) by casper.infradead.org with esmtp (Exim 4.76 #1 (Red Hat Linux)) id 1TReSh-0004zp-Kf for linux-arm-kernel@lists.infradead.org; Fri, 26 Oct 2012 07:36:16 +0000 Received: from penbh01.corp.atmel.com ([10.168.5.31]) by sjogate2.atmel.com (8.13.6/8.13.6) with ESMTP id q9Q7TV4X028843; Fri, 26 Oct 2012 00:29:32 -0700 (PDT) Received: from penmb01.corp.atmel.com ([10.168.5.33]) by penbh01.corp.atmel.com with Microsoft SMTPSVC(6.0.3790.3959); Fri, 26 Oct 2012 15:34:34 +0800 Received: from shaarm01.corp.atmel.com ([10.217.6.34]) by penmb01.corp.atmel.com with Microsoft SMTPSVC(6.0.3790.3959); Fri, 26 Oct 2012 15:34:32 +0800 From: Elen Song To: nicolas.ferre@atmel.com, plagnioj@jcrosoft.com Subject: [PATCH v2] DMA: AT91: Get residual bytes in dma buffer Date: Fri, 26 Oct 2012 15:32:26 +0800 Message-Id: <1351236746-4721-1-git-send-email-elen.song@atmel.com> X-Mailer: git-send-email 1.7.9.5 X-OriginalArrivalTime: 26 Oct 2012 07:34:32.0246 (UTC) FILETIME=[57053D60:01CDB34C] X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20121026_083612_417947_383585E2 X-CRM114-Status: GOOD ( 22.17 ) X-Spam-Score: -2.6 (--) X-Spam-Report: SpamAssassin version 3.3.2 on casper.infradead.org summary: Content analysis details: (-2.6 points, 5.0 required) pts rule name description ---- ---------------------- -------------------------------------------------- -0.0 SPF_PASS SPF: sender matches SPF record -0.7 RP_MATCHES_RCVD Envelope sender domain matches handover relay domain -1.9 BAYES_00 BODY: Bayes spam probability is 0 to 1% [score: 0.0000] Cc: jm.lin@atmel.com, linux@arm.linux.org.uk, elen.song@atmel.com, linux-arm-kernel@lists.infradead.org X-BeenThere: linux-arm-kernel@lists.infradead.org X-Mailman-Version: 2.1.14 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Sender: linux-arm-kernel-bounces@lists.infradead.org Errors-To: linux-arm-kernel-bounces+patchwork-linux-arm=patchwork.kernel.org@lists.infradead.org Add support for returning the residue for a particular descriptor by reading the BTSIZE in CTRLA register. - If the cookie has completed successfully, the residue will be zero. - If the cookie is in progress or the channel is paused, it will be the number of bytes yet to be transferred. - If the cookie is queued, it will be the number of bytes in the descriptor. - If get residue error, the cookie will be turn into error status, and dma transfer will be terminated. Signed-off-by: Elen Song --- Change compare to v1: 1. Do not need to use DMA_CTRL_ACK flag to know descriptor transfer complete. 2. Pause dma before read residue. 3. Change comment. drivers/dma/at_hdmac.c | 137 +++++++++++++++++++++++++++++++++++++++---- drivers/dma/at_hdmac_regs.h | 5 ++ 2 files changed, 131 insertions(+), 11 deletions(-) diff --git a/drivers/dma/at_hdmac.c b/drivers/dma/at_hdmac.c index 13a02f4..c771e83 100644 --- a/drivers/dma/at_hdmac.c +++ b/drivers/dma/at_hdmac.c @@ -229,6 +229,101 @@ static void atc_dostart(struct at_dma_chan *atchan, struct at_desc *first) vdbg_dump_regs(atchan); } +/* + * atc_get_current_descriptors - + * locate the descriptor which equal to physical address in DSCR + * @atchan: the channel we want to start + * @dscr_addr: physical descriptor address in DSCR + */ +static struct at_desc *atc_get_current_descriptors(struct at_dma_chan *atchan, + u32 dscr_addr) +{ + struct at_desc *desc, *_desc, *child, *desc_cur = NULL; + + list_for_each_entry_safe(desc, _desc, &atchan->active_list, desc_node) { + + if (desc->lli.dscr == dscr_addr) { + desc_cur = desc; + break; + } + + list_for_each_entry(child, &desc->tx_list, desc_node) { + + if (child->lli.dscr == dscr_addr) { + desc_cur = child; + break; + } + } + } + + return desc_cur; +} + +/* + * atc_get_bytes_left - + * Get the number of bytes residue in dma buffer, + * the channel should be paused when calling this + * @atchan: the channel we want to start + */ +static int atc_get_bytes_left(struct at_dma_chan *atchan) +{ + struct at_desc *desc_first; + int left = 0, count = 0; + u32 dscr_addr; + + /* First descriptor embedds the transaction length */ + desc_first = atc_first_active(atchan); + if (!desc_first) + return -EINVAL; + /* + * Initialize necessary values in the first time. + * We use desc_cur to save current descriptor, + * save_len record residual dma buffer length. + */ + if (!atchan->desc_cur && + !atchan->save_len) { + atchan->desc_cur = desc_first; + atchan->save_len = desc_first->len; + } + /* + * This happens when current descriptor transfer complete. + * When transfer terminated, we reset the necessary values. + * Otherwise, we calculate the remain buffer size. + */ + if (test_and_clear_bit(ATC_IS_BTC, &atchan->status)) { + dscr_addr = channel_readl(atchan, DSCR); + /* End of a single or cyclic transfer */ + if (dscr_addr == 0 || dscr_addr == desc_first->lli.dscr) { + atchan->desc_cur = NULL; + atchan->save_len = 0; + /* + * Current descriptor transfer done and switch to next. + * The residual buffer size should + * reduce current descriptor length. + */ + } else { + atchan->desc_cur = atc_get_current_descriptors(atchan, + dscr_addr); + if (!atchan->desc_cur) + return -EINVAL; + atchan->save_len -= (atchan->desc_cur->lli.ctrla + & ATC_BTSIZE_MAX); + } + + left = atchan->save_len; + } + + /* + * Get residual bytes when current descriptor transfer in progress. + */ + if (atchan->desc_cur) { + count = channel_readl(atchan, CTRLA) & ATC_BTSIZE_MAX; + left = atchan->save_len - count; + } + + return left; +} + /** * atc_chain_complete - finish work for one transaction chain * @atchan: channel we work on @@ -494,6 +589,8 @@ static irqreturn_t at_dma_interrupt(int irq, void *dev_id) /* Give information to tasklet */ set_bit(ATC_IS_ERROR, &atchan->status); } + if (pending & AT_DMA_BTC(i)) + set_bit(ATC_IS_BTC, &atchan->status); tasklet_schedule(&atchan->tasklet); ret = IRQ_HANDLED; } @@ -1034,32 +1131,50 @@ atc_tx_status(struct dma_chan *chan, dma_cookie_t last_complete; unsigned long flags; enum dma_status ret; - - spin_lock_irqsave(&atchan->lock, flags); + int bytes = 0; ret = dma_cookie_status(chan, cookie, txstate); - if (ret != DMA_SUCCESS) { - atc_cleanup_descriptors(atchan); - - ret = dma_cookie_status(chan, cookie, txstate); + /* Residue is zero while transactions complete */ + if (ret == DMA_SUCCESS) { + dma_set_residue(txstate, 0); + return ret; } + /* + * There's no point calculating the residue if there's + * no txstate to store the value. + */ + if (!txstate) + goto err_dma; - last_complete = chan->completed_cookie; - last_used = chan->cookie; + /* Channel should be paused before get residue */ + if (!atc_chan_is_paused(atchan)) + atc_control(chan, DMA_PAUSE, 0); + + spin_lock_irqsave(&atchan->lock, flags); + + /* Get number of bytes left in the active transactions */ + bytes = atc_get_bytes_left(atchan); spin_unlock_irqrestore(&atchan->lock, flags); - if (ret != DMA_SUCCESS) - dma_set_residue(txstate, atc_first_active(atchan)->len); + if (bytes < 0) + goto err_dma; + else + dma_set_residue(txstate, bytes); if (atc_chan_is_paused(atchan)) - ret = DMA_PAUSED; + atc_control(chan, DMA_RESUME, 0); dev_vdbg(chan2dev(chan), "tx_status %d: cookie = %d (d%d, u%d)\n", ret, cookie, last_complete ? last_complete : 0, last_used ? last_used : 0); return ret; + +err_dma: + atc_control(chan, DMA_TERMINATE_ALL, 0); + ret = DMA_ERROR; + return ret; } /** diff --git a/drivers/dma/at_hdmac_regs.h b/drivers/dma/at_hdmac_regs.h index 116e4ad..b1861cb 100644 --- a/drivers/dma/at_hdmac_regs.h +++ b/drivers/dma/at_hdmac_regs.h @@ -211,6 +211,7 @@ txd_to_at_desc(struct dma_async_tx_descriptor *txd) enum atc_status { ATC_IS_ERROR = 0, ATC_IS_PAUSED = 1, + ATC_IS_BTC = 2, ATC_IS_CYCLIC = 24, }; @@ -226,12 +227,14 @@ enum atc_status { * @save_cfg: configuration register that is saved on suspend/resume cycle * @save_dscr: for cyclic operations, preserve next descriptor address in * the cyclic list on suspend/resume cycle + * @save_len: to record dma buffer length * @dma_sconfig: configuration for slave transfers, passed via DMA_SLAVE_CONFIG * @lock: serializes enqueue/dequeue operations to descriptors lists * @active_list: list of descriptors dmaengine is being running on * @queue: list of descriptors ready to be submitted to engine * @free_list: list of descriptors usable by the channel * @descs_allocated: records the actual size of the descriptor pool + * @desc_cur: current descriptor */ struct at_dma_chan { struct dma_chan chan_common; @@ -242,6 +245,7 @@ struct at_dma_chan { struct tasklet_struct tasklet; u32 save_cfg; u32 save_dscr; + u32 save_len; struct dma_slave_config dma_sconfig; spinlock_t lock; @@ -251,6 +255,7 @@ struct at_dma_chan { struct list_head queue; struct list_head free_list; unsigned int descs_allocated; + struct at_desc *desc_cur; }; #define channel_readl(atchan, name) \