From patchwork Mon Oct 29 09:10:37 2012 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: "Song, Elen" X-Patchwork-Id: 1662501 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 19F353FDDA for ; Mon, 29 Oct 2012 09:15:22 +0000 (UTC) Received: from localhost ([::1] helo=merlin.infradead.org) by merlin.infradead.org with esmtp (Exim 4.76 #1 (Red Hat Linux)) id 1TSlPV-0003Yf-5G; Mon, 29 Oct 2012 09:13:29 +0000 Received: from casper.infradead.org ([2001:770:15f::2]) by merlin.infradead.org with esmtps (Exim 4.76 #1 (Red Hat Linux)) id 1TSlPP-0003Wh-TF for linux-arm-kernel@merlin.infradead.org; Mon, 29 Oct 2012 09:13:24 +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 1TSlPM-0000cS-2h for linux-arm-kernel@lists.infradead.org; Mon, 29 Oct 2012 09:13:22 +0000 Received: from penbh01.corp.atmel.com ([10.168.5.31]) by sjogate2.atmel.com (8.13.6/8.13.6) with ESMTP id q9T984HW001651; Mon, 29 Oct 2012 02:08:05 -0700 (PDT) Received: from penmb01.corp.atmel.com ([10.168.5.33]) by penbh01.corp.atmel.com with Microsoft SMTPSVC(6.0.3790.3959); Mon, 29 Oct 2012 17:12:53 +0800 Received: from shaarm01.corp.atmel.com ([10.217.6.34]) by penmb01.corp.atmel.com with Microsoft SMTPSVC(6.0.3790.3959); Mon, 29 Oct 2012 17:12:52 +0800 From: Elen Song To: nicolas.ferre@atmel.com, patrice.vilchez@atmel.com, plagnioj@jcrosoft.com Subject: [PATCH 4/6] Serial: AT91: Enable tx dma Date: Mon, 29 Oct 2012 17:10:37 +0800 Message-Id: <1351501837-13491-1-git-send-email-elen.song@atmel.com> X-Mailer: git-send-email 1.7.9.5 X-OriginalArrivalTime: 29 Oct 2012 09:12:53.0025 (UTC) FILETIME=[9365E910:01CDB5B5] X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20121029_091320_644544_3B5B6BF6 X-CRM114-Status: GOOD ( 14.89 ) X-Spam-Score: -2.5 (--) X-Spam-Report: SpamAssassin version 3.3.2 on casper.infradead.org summary: Content analysis details: (-2.5 points, 5.0 required) pts rule name description ---- ---------------------- -------------------------------------------------- -0.0 SPF_PASS SPF: sender matches SPF record -0.6 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 Tx dma will setup a single transfer, when transfer end, it will call atmel_dma_tx_complete to do finish stuff. Signed-off-by: Elen Song --- drivers/tty/serial/atmel_serial.c | 99 +++++++++++++++++++++++++++++++++++++ 1 file changed, 99 insertions(+) diff --git a/drivers/tty/serial/atmel_serial.c b/drivers/tty/serial/atmel_serial.c index c02a919..352ef4a 100644 --- a/drivers/tty/serial/atmel_serial.c +++ b/drivers/tty/serial/atmel_serial.c @@ -590,6 +590,39 @@ static void atmel_tx_chars(struct uart_port *port) UART_PUT_IER(port, atmel_port->tx_done_mask); } +static void atmel_dma_tx_complete(void *arg) +{ + struct atmel_uart_port *atmel_port = arg; + struct uart_port *port = &atmel_port->uart; + struct circ_buf *xmit = &port->state->xmit; + struct dma_chan *chan = atmel_port->chan_tx; + unsigned long flags; + + spin_lock_irqsave(&port->lock, flags); + + if (chan) + dmaengine_terminate_all(chan); + xmit->tail += sg_dma_len(&atmel_port->sg_tx); + xmit->tail &= UART_XMIT_SIZE - 1; + + port->icount.tx += sg_dma_len(&atmel_port->sg_tx); + + spin_lock_irq(&atmel_port->lock_tx); + async_tx_ack(atmel_port->desc_tx); + atmel_port->cookie_tx = -EINVAL; + atmel_port->desc_tx = NULL; + spin_unlock_irq(&atmel_port->lock_tx); + + if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + uart_write_wakeup(port); + + /* Do we really need this? */ + if (!uart_circ_empty(xmit)) + tasklet_schedule(&atmel_port->tasklet); + + spin_unlock_irqrestore(&port->lock, flags); +} + static void atmel_tx_dma_release(struct atmel_uart_port *atmel_port) { struct dma_chan *chan = atmel_port->chan_tx; @@ -602,6 +635,70 @@ static void atmel_tx_dma_release(struct atmel_uart_port *atmel_port) } } +/* + * Called from tasklet with TXRDY interrupt is disabled. + */ +static void atmel_tx_dma(struct uart_port *port) +{ + struct atmel_uart_port *atmel_port = to_atmel_uart_port(port); + struct circ_buf *xmit = &port->state->xmit; + struct dma_chan *chan = atmel_port->chan_tx; + struct dma_async_tx_descriptor *desc; + struct scatterlist *sg = &atmel_port->sg_tx; + + /* Make sure we have an idle channel */ + if (atmel_port->desc_tx != NULL) + return; + + if (!uart_circ_empty(xmit) && !uart_tx_stopped(port)) { + /* + * DMA is idle now. + * Port xmit buffer is already mapped, + * and it is one page... Just adjust + * offsets and lengths. Since it is a circular buffer, + * we have to transmit till the end, and then the rest. + * Take the port lock to get a + * consistent xmit buffer state. + */ + sg->offset = xmit->tail & (UART_XMIT_SIZE - 1); + sg_dma_address(sg) = (sg_dma_address(sg) & + ~(UART_XMIT_SIZE - 1)) + + sg->offset; + sg_dma_len(sg) = CIRC_CNT_TO_END(xmit->head, + xmit->tail, + UART_XMIT_SIZE); + BUG_ON(!sg_dma_len(sg)); + + desc = dmaengine_prep_slave_sg(chan, + sg, + atmel_port->sg_len_tx, + DMA_MEM_TO_DEV, + DMA_PREP_INTERRUPT | + DMA_CTRL_ACK); + if (!desc) { + dev_err(port->dev, "Failed to send via dma!\n"); + return; + } + + dma_sync_sg_for_device(port->dev, sg, 1, DMA_MEM_TO_DEV); + + atmel_port->desc_tx = desc; + desc->callback = atmel_dma_tx_complete; + desc->callback_param = atmel_port; + atmel_port->cookie_tx = dmaengine_submit(desc); + + dma_async_issue_pending(chan); + } else { + if (atmel_port->rs485.flags & SER_RS485_ENABLED) { + /* DMA done, stop TX, start RX for RS485 */ + atmel_start_rx(port); + } + } + + if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + uart_write_wakeup(port); +} + static bool filter(struct dma_chan *chan, void *slave) { struct at_dma_slave *sl = slave; @@ -991,6 +1088,8 @@ static void atmel_tasklet_func(unsigned long data) if (atmel_use_pdc_tx(port)) atmel_tx_pdc(port); + else if (atmel_use_dma_tx(port)) + atmel_tx_dma(port); else atmel_tx_chars(port);