From patchwork Tue Oct 16 06:03:05 2012 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Huang Shijie X-Patchwork-Id: 1598921 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 B83143FD4F for ; Tue, 16 Oct 2012 06:38:10 +0000 (UTC) Received: from localhost ([::1] helo=merlin.infradead.org) by merlin.infradead.org with esmtp (Exim 4.76 #1 (Red Hat Linux)) id 1TO0lL-0001K7-H4; Tue, 16 Oct 2012 06:36:23 +0000 Received: from ch1ehsobe003.messaging.microsoft.com ([216.32.181.183] helo=ch1outboundpool.messaging.microsoft.com) by merlin.infradead.org with esmtps (Exim 4.76 #1 (Red Hat Linux)) id 1TO0kn-0001Br-7P for linux-arm-kernel@lists.infradead.org; Tue, 16 Oct 2012 06:35:53 +0000 Received: from mail170-ch1-R.bigfish.com (10.43.68.235) by CH1EHSOBE012.bigfish.com (10.43.70.62) with Microsoft SMTP Server id 14.1.225.23; Tue, 16 Oct 2012 06:35:41 +0000 Received: from mail170-ch1 (localhost [127.0.0.1]) by mail170-ch1-R.bigfish.com (Postfix) with ESMTP id 7C2544C0109; Tue, 16 Oct 2012 06:35:41 +0000 (UTC) X-Forefront-Antispam-Report: CIP:70.37.183.190; KIP:(null); UIP:(null); IPV:NLI; H:mail.freescale.net; RD:none; EFVD:NLI X-SpamScore: 3 X-BigFish: VS3(zzzz1202h1d1ah1d2ah1082kzz8275bhz2dh2a8h668h839hd24he5bhf0ah107ah1288h12a5h12a9h12bdh12e5h1354h137ah139eh13b6h1441h1155h) Received: from mail170-ch1 (localhost.localdomain [127.0.0.1]) by mail170-ch1 (MessageSwitch) id 1350369339209876_30335; Tue, 16 Oct 2012 06:35:39 +0000 (UTC) Received: from CH1EHSMHS032.bigfish.com (snatpool2.int.messaging.microsoft.com [10.43.68.237]) by mail170-ch1.bigfish.com (Postfix) with ESMTP id 3101D4800A3; Tue, 16 Oct 2012 06:35:39 +0000 (UTC) Received: from mail.freescale.net (70.37.183.190) by CH1EHSMHS032.bigfish.com (10.43.70.32) with Microsoft SMTP Server (TLS) id 14.1.225.23; Tue, 16 Oct 2012 06:35:38 +0000 Received: from az84smr01.freescale.net (10.64.34.197) by 039-SN1MMR1-001.039d.mgd.msft.net (10.84.1.13) with Microsoft SMTP Server (TLS) id 14.2.309.3; Tue, 16 Oct 2012 06:35:37 +0000 Received: from localhost.localdomain (shlinux2.ap.freescale.net [10.192.224.44]) by az84smr01.freescale.net (8.14.3/8.14.0) with ESMTP id q9G6ZJEN023050; Mon, 15 Oct 2012 23:35:35 -0700 From: Huang Shijie To: Subject: [PATCH 2/3] serial: mxs-auart: add the DMA support for mx28 Date: Tue, 16 Oct 2012 14:03:05 +0800 Message-ID: <1350367386-7742-3-git-send-email-b32955@freescale.com> X-Mailer: git-send-email 1.7.0.4 In-Reply-To: <1350367386-7742-1-git-send-email-b32955@freescale.com> References: <1350367386-7742-1-git-send-email-b32955@freescale.com> MIME-Version: 1.0 X-OriginatorOrg: freescale.com X-Spam-Note: CRM114 invocation failed X-Spam-Score: -2.6 (--) X-Spam-Report: SpamAssassin version 3.3.2 on merlin.infradead.org summary: Content analysis details: (-2.6 points) pts rule name description ---- ---------------------- -------------------------------------------------- -0.7 RCVD_IN_DNSWL_LOW RBL: Sender listed at http://www.dnswl.org/, low trust [216.32.181.183 listed in list.dnswl.org] -1.9 BAYES_00 BODY: Bayes spam probability is 0 to 1% [score: 0.0000] Cc: linux@arm.linux.org.uk, vinod.koul@intel.com, lauri.hintsala@bluegiga.com, Huang Shijie , linux-serial@vger.kernel.org, shawn.guo@linaro.org, linux-arm-kernel@lists.infradead.org, alan@linux.intel.com 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: , Sender: linux-arm-kernel-bounces@lists.infradead.org Errors-To: linux-arm-kernel-bounces+patchwork-linux-arm=patchwork.kernel.org@lists.infradead.org Only we meet the following conditions, we can enable the DMA support for auart: (1) We enable the DMA support in the dts file, such as arch/arm/boot/dts/imx28.dtsi. (2) We enable the hardware flow control. (3) We use the mx28, not the mx23. Due to hardware bug(see errata: 2836), we can not add the DMA support to mx23. Signed-off-by: Huang Shijie --- .../bindings/tty/serial/fsl-mxs-auart.txt | 7 + drivers/tty/serial/mxs-auart.c | 307 +++++++++++++++++++- 2 files changed, 311 insertions(+), 3 deletions(-) diff --git a/Documentation/devicetree/bindings/tty/serial/fsl-mxs-auart.txt b/Documentation/devicetree/bindings/tty/serial/fsl-mxs-auart.txt index a154bf1..67e54b4 100644 --- a/Documentation/devicetree/bindings/tty/serial/fsl-mxs-auart.txt +++ b/Documentation/devicetree/bindings/tty/serial/fsl-mxs-auart.txt @@ -6,11 +6,18 @@ Required properties: - reg : Address and length of the register set for the device - interrupts : Should contain the auart interrupt numbers +Optional properties: +- fsl,auart-dma-channel : The DMA channels, the first is for RX, the other + is for TX. +- fsl,auart-enable-dma : Enable the DMA support for the auart. + Example: auart0: serial@8006a000 { compatible = "fsl,imx28-auart"; reg = <0x8006a000 0x2000>; interrupts = <112 70 71>; + fsl,auart-dma-channel = <8 9>; + fsl,auart-enable-dma; }; Note: Each auart port should have an alias correctly numbered in "aliases" diff --git a/drivers/tty/serial/mxs-auart.c b/drivers/tty/serial/mxs-auart.c index cd9ec1d..2271330 100644 --- a/drivers/tty/serial/mxs-auart.c +++ b/drivers/tty/serial/mxs-auart.c @@ -34,6 +34,8 @@ #include #include #include +#include +#include #include @@ -76,7 +78,15 @@ #define AUART_CTRL0_SFTRST (1 << 31) #define AUART_CTRL0_CLKGATE (1 << 30) +#define AUART_CTRL0_RXTO_ENABLE (1 << 27) +#define AUART_CTRL0_RXTIMEOUT(v) (((v) & 0x7ff) << 16) +#define AUART_CTRL0_XFER_COUNT(v) ((v) & 0xffff) +#define AUART_CTRL1_XFER_COUNT(v) ((v) & 0xffff) + +#define AUART_CTRL2_DMAONERR (1 << 26) +#define AUART_CTRL2_TXDMAE (1 << 25) +#define AUART_CTRL2_RXDMAE (1 << 24) #define AUART_CTRL2_CTSEN (1 << 15) #define AUART_CTRL2_RTSEN (1 << 14) #define AUART_CTRL2_RTS (1 << 11) @@ -116,12 +126,15 @@ #define AUART_STAT_BERR (1 << 18) #define AUART_STAT_PERR (1 << 17) #define AUART_STAT_FERR (1 << 16) +#define AUART_STAT_RXCOUNT_MASK 0xffff static struct uart_driver auart_driver; struct mxs_auart_port { struct uart_port port; +#define MXS_AUART_DMA_CONFIG 0x1 +#define MXS_AUART_DMA_ENABLED 0x2 unsigned int flags; unsigned int ctrl; @@ -130,16 +143,116 @@ struct mxs_auart_port { struct clk *clk; struct device *dev; struct platform_device *pdev; + + /* for DMA */ + struct mxs_dma_data dma_data; + int dma_channel_rx, dma_channel_tx; + int dma_irq_rx, dma_irq_tx; + int dma_channel; + + struct scatterlist tx_sgl; + struct dma_chan *tx_dma_chan; + void *tx_dma_buf; + + struct scatterlist rx_sgl; + struct dma_chan *rx_dma_chan; + void *rx_dma_buf; }; +static inline bool auart_dma_enabled(struct mxs_auart_port *s) +{ + return s->flags & MXS_AUART_DMA_ENABLED; +} + static void mxs_auart_stop_tx(struct uart_port *u); #define to_auart_port(u) container_of(u, struct mxs_auart_port, port) +static inline void mxs_auart_tx_chars(struct mxs_auart_port *s); + +static void dma_tx_callback(void *param) +{ + struct mxs_auart_port *s = param; + struct circ_buf *xmit = &s->port.state->xmit; + + dma_unmap_sg(s->dev, &s->tx_sgl, 1, DMA_TO_DEVICE); + + /* wake up the possible processes. */ + if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + uart_write_wakeup(&s->port); + + mxs_auart_tx_chars(s); +} + +static int mxs_auart_dma_tx(struct mxs_auart_port *s, int size) +{ + struct dma_async_tx_descriptor *desc; + struct scatterlist *sgl = &s->tx_sgl; + struct dma_chan *channel = s->tx_dma_chan; + u32 pio[1]; + + /* [1] : send PIO. Note, the first pio word is CTRL1. */ + pio[0] = AUART_CTRL1_XFER_COUNT(size); + desc = dmaengine_prep_slave_sg(channel, (struct scatterlist *)pio, + 1, DMA_TRANS_NONE, 0); + if (!desc) { + dev_err(s->dev, "step 1 error\n"); + return -EINVAL; + } + + /* [2] : set DMA buffer. */ + sg_init_one(sgl, s->tx_dma_buf, size); + dma_map_sg(s->dev, sgl, 1, DMA_TO_DEVICE); + desc = dmaengine_prep_slave_sg(channel, sgl, + 1, DMA_MEM_TO_DEV, DMA_PREP_INTERRUPT | DMA_CTRL_ACK); + if (!desc) { + dev_err(s->dev, "step 2 error\n"); + return -EINVAL; + } + + /* [3] : submit the DMA */ + desc->callback = dma_tx_callback; + desc->callback_param = s; + dmaengine_submit(desc); + dma_async_issue_pending(channel); + return 0; +} + static inline void mxs_auart_tx_chars(struct mxs_auart_port *s) { struct circ_buf *xmit = &s->port.state->xmit; + if (auart_dma_enabled(s)) { + int i = 0; + int size; + void *buffer = s->tx_dma_buf; + enum dma_status status; + + /* Check whether there is pending DMA operations. */ + status = dmaengine_tx_status(s->tx_dma_chan, 0, NULL); + if (status != DMA_SUCCESS) + return; + + while (!uart_circ_empty(xmit) && !uart_tx_stopped(&s->port)) { + size = min_t(u32, UART_XMIT_SIZE - i, + CIRC_CNT_TO_END(xmit->head, + xmit->tail, + UART_XMIT_SIZE)); + memcpy(buffer + i, xmit->buf + xmit->tail, size); + xmit->tail = (xmit->tail + size) & (UART_XMIT_SIZE - 1); + + i += size; + if (i >= UART_XMIT_SIZE) + break; + } + + if (uart_tx_stopped(&s->port)) + mxs_auart_stop_tx(&s->port); + if (i) + mxs_auart_dma_tx(s, i); + return; + } + while (!(readl(s->port.membase + AUART_STAT) & AUART_STAT_TXFF)) { if (s->port.x_char) { @@ -293,10 +406,155 @@ static u32 mxs_auart_get_mctrl(struct uart_port *u) return mctrl; } +static bool mxs_auart_dma_filter(struct dma_chan *chan, void *param) +{ + struct mxs_auart_port *s = param; + + if (!mxs_dma_is_apbx(chan)) + return false; + + if (s->dma_channel == chan->chan_id) { + chan->private = &s->dma_data; + return true; + } + return false; +} + +static int mxs_auart_dma_prep_rx(struct mxs_auart_port *s); +static void dma_rx_callback(void *arg) +{ + struct mxs_auart_port *s = (struct mxs_auart_port *) arg; + struct tty_struct *tty = s->port.state->port.tty; + int count; + u32 stat; + + stat = readl(s->port.membase + AUART_STAT); + stat &= ~(AUART_STAT_OERR | AUART_STAT_BERR | + AUART_STAT_PERR | AUART_STAT_FERR); + + count = stat & AUART_STAT_RXCOUNT_MASK; + tty_insert_flip_string(tty, s->rx_dma_buf, count); + + writel(stat, s->port.membase + AUART_STAT); + tty_flip_buffer_push(tty); + + /* start the next DMA for RX. */ + mxs_auart_dma_prep_rx(s); +} + +static int mxs_auart_dma_prep_rx(struct mxs_auart_port *s) +{ + struct dma_async_tx_descriptor *desc; + struct scatterlist *sgl = &s->rx_sgl; + struct dma_chan *channel = s->rx_dma_chan; + u32 pio[1]; + + /* [1] : send PIO */ + pio[0] = AUART_CTRL0_RXTO_ENABLE + | AUART_CTRL0_RXTIMEOUT(0x80) + | AUART_CTRL0_XFER_COUNT(UART_XMIT_SIZE); + desc = dmaengine_prep_slave_sg(channel, (struct scatterlist *)pio, + 1, DMA_TRANS_NONE, 0); + if (!desc) { + dev_err(s->dev, "step 1 error\n"); + return -EINVAL; + } + + /* [2] : send DMA request */ + sg_init_one(sgl, s->rx_dma_buf, UART_XMIT_SIZE); + dma_map_sg(s->dev, sgl, 1, DMA_FROM_DEVICE); + desc = dmaengine_prep_slave_sg(channel, sgl, 1, DMA_DEV_TO_MEM, + DMA_PREP_INTERRUPT | DMA_CTRL_ACK); + if (!desc) { + dev_err(s->dev, "step 2 error\n"); + return -1; + } + + /* [3] : submit the DMA, but do not issue it. */ + desc->callback = dma_rx_callback; + desc->callback_param = s; + dmaengine_submit(desc); + dma_async_issue_pending(channel); + return 0; +} + +static void mxs_auart_dma_exit_channel(struct mxs_auart_port *s) +{ + if (s->tx_dma_chan) { + dma_release_channel(s->tx_dma_chan); + s->tx_dma_chan = NULL; + } + if (s->rx_dma_chan) { + dma_release_channel(s->rx_dma_chan); + s->rx_dma_chan = NULL; + } + + kfree(s->tx_dma_buf); + kfree(s->rx_dma_buf); + s->tx_dma_buf = NULL; + s->rx_dma_buf = NULL; +} + +static void mxs_auart_dma_exit(struct mxs_auart_port *s) +{ + + writel(AUART_CTRL2_TXDMAE | AUART_CTRL2_RXDMAE | AUART_CTRL2_DMAONERR, + s->port.membase + AUART_CTRL2_CLR); + + mxs_auart_dma_exit_channel(s); + s->flags &= ~MXS_AUART_DMA_ENABLED; +} + +static int mxs_auart_dma_init(struct mxs_auart_port *s) +{ + dma_cap_mask_t mask; + + if (auart_dma_enabled(s)) + return 0; + + /* We do not get the right DMA channels. */ + if (s->dma_channel_rx == -1 || s->dma_channel_rx == -1) + return -EINVAL; + + /* init for RX */ + dma_cap_zero(mask); + dma_cap_set(DMA_SLAVE, mask); + s->dma_channel = s->dma_channel_rx; + s->dma_data.chan_irq = s->dma_irq_rx; + s->rx_dma_chan = dma_request_channel(mask, mxs_auart_dma_filter, s); + if (!s->rx_dma_chan) + goto err_out; + s->rx_dma_buf = kzalloc(UART_XMIT_SIZE, GFP_KERNEL | GFP_DMA); + if (!s->rx_dma_buf) + goto err_out; + + /* init for TX */ + s->dma_channel = s->dma_channel_tx; + s->dma_data.chan_irq = s->dma_irq_tx; + s->tx_dma_chan = dma_request_channel(mask, mxs_auart_dma_filter, s); + if (!s->tx_dma_chan) + goto err_out; + s->tx_dma_buf = kzalloc(UART_XMIT_SIZE, GFP_KERNEL | GFP_DMA); + if (!s->tx_dma_buf) + goto err_out; + + /* set the flags */ + s->flags |= MXS_AUART_DMA_ENABLED; + dev_dbg(s->dev, "enabled the DMA support."); + + return 0; + +err_out: + mxs_auart_dma_exit_channel(s); + return -EINVAL; + +} + static void mxs_auart_settermios(struct uart_port *u, struct ktermios *termios, struct ktermios *old) { + struct mxs_auart_port *s = to_auart_port(u); u32 bm, ctrl, ctrl2, div; unsigned int cflag, baud; @@ -368,10 +626,23 @@ static void mxs_auart_settermios(struct uart_port *u, ctrl |= AUART_LINECTRL_STP2; /* figure out the hardware flow control settings */ - if (cflag & CRTSCTS) + if (cflag & CRTSCTS) { + /* + * The DMA has a bug(see errata:2836) in mx23. + * So we can not implement the DMA for auart in mx23, + * we can only implement the DMA support for auart + * in mx28. + */ + if (!AUART_IS_MX23(s) && (s->flags & MXS_AUART_DMA_CONFIG)) { + if (!mxs_auart_dma_init(s)) + /* enable DMA tranfer */ + ctrl2 |= AUART_CTRL2_TXDMAE | AUART_CTRL2_RXDMAE + | AUART_CTRL2_DMAONERR; + } ctrl2 |= AUART_CTRL2_CTSEN | AUART_CTRL2_RTSEN; - else + } else { ctrl2 &= ~(AUART_CTRL2_CTSEN | AUART_CTRL2_RTSEN); + } /* set baud rate */ baud = uart_get_baud_rate(u, termios, old, 0, u->uartclk); @@ -383,6 +654,17 @@ static void mxs_auart_settermios(struct uart_port *u, writel(ctrl2, u->membase + AUART_CTRL2); uart_update_timeout(u, termios->c_cflag, baud); + + /* prepare for the DMA RX. */ + if (auart_dma_enabled(s)) { + if (!mxs_auart_dma_prep_rx(s)) { + /* Disable the normal RX interrupt. */ + writel(AUART_INTR_RXIEN, u->membase + AUART_INTR_CLR); + } else { + mxs_auart_dma_exit(s); + dev_err(s->dev, "We can not start up the DMA.\n"); + } + } } static irqreturn_t mxs_auart_irq_handle(int irq, void *context) @@ -461,6 +743,9 @@ static void mxs_auart_shutdown(struct uart_port *u) { struct mxs_auart_port *s = to_auart_port(u); + if (auart_dma_enabled(s)) + mxs_auart_dma_exit(s); + writel(AUART_CTRL2_UARTEN, u->membase + AUART_CTRL2_CLR); writel(AUART_INTR_RXIEN | AUART_INTR_RTIEN | AUART_INTR_CTSMIEN, @@ -707,6 +992,7 @@ static int serial_mxs_probe_dt(struct mxs_auart_port *s, struct platform_device *pdev) { struct device_node *np = pdev->dev.of_node; + u32 dma_channel[2]; int ret; if (!np) @@ -722,6 +1008,22 @@ static int serial_mxs_probe_dt(struct mxs_auart_port *s, pdev->id_entry = of_match_device(mxs_auart_dt_ids, &pdev->dev)->data; + ret = of_property_read_u32_array(np, "fsl,auart-dma-channel", + dma_channel, 2); + if (ret == 0) { + s->dma_channel_rx = dma_channel[0]; + s->dma_channel_tx = dma_channel[1]; + } else { + s->dma_channel_rx = -1; + s->dma_channel_tx = -1; + } + + s->dma_irq_rx = platform_get_irq(pdev, 1); + s->dma_irq_tx = platform_get_irq(pdev, 2); + + if (of_property_read_bool(np, "fsl,auart-enable-dma")) + s->flags |= MXS_AUART_DMA_CONFIG; + return 0; } @@ -772,7 +1074,6 @@ static int __devinit mxs_auart_probe(struct platform_device *pdev) s->port.type = PORT_IMX; s->port.dev = s->dev = get_device(&pdev->dev); - s->flags = 0; s->ctrl = 0; s->pdev = pdev;