From patchwork Fri May 1 17:11:34 2015 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Yoshihiro Kaneko X-Patchwork-Id: 6311981 X-Patchwork-Delegate: geert@linux-m68k.org Return-Path: X-Original-To: patchwork-linux-sh@patchwork.kernel.org Delivered-To: patchwork-parsemail@patchwork2.web.kernel.org Received: from mail.kernel.org (mail.kernel.org [198.145.29.136]) by patchwork2.web.kernel.org (Postfix) with ESMTP id 92AA0BEEE1 for ; Fri, 1 May 2015 17:12:58 +0000 (UTC) Received: from mail.kernel.org (localhost [127.0.0.1]) by mail.kernel.org (Postfix) with ESMTP id 5A7B320435 for ; Fri, 1 May 2015 17:12:57 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id CF0EC2042B for ; Fri, 1 May 2015 17:12:55 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1751508AbbEARMz (ORCPT ); Fri, 1 May 2015 13:12:55 -0400 Received: from mail-pd0-f171.google.com ([209.85.192.171]:33002 "EHLO mail-pd0-f171.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1751054AbbEARMy (ORCPT ); Fri, 1 May 2015 13:12:54 -0400 Received: by pdbnk13 with SMTP id nk13so97452396pdb.0; Fri, 01 May 2015 10:12:53 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20120113; h=from:to:cc:subject:date:message-id; bh=03uNWtemBYvOexMxbweKaiIRIsGfWMizNOingWWNoSk=; b=tZT6JZNC1h93C072noGXULuZEa5PSJeCBZnvFpjXSPBJ7CWSj8Z6bOug2ngDdziZK+ zn6VKIfZPYd3Du99yjHYxIYs15BKmxf0WbLFubY3pqgGgVkkK5bcoF4V0E4N4Mny7L/O 0xK6cxyt21qtbb8ihG15rzD2U5nZqR56MTuJXcGcWOYZsuCJQbKQnmd91KJ8UPl9dDp6 WhxAsbcLWPcyF5YVQS5LY7PBpehGuzqm7NVNMvD8rCBRWd33/vSoWcNidojHxw74sVq7 gDAN8kxbanH4Kk+0IQBivUWNW7NVNomOI6VCLCuDilORtK4KWURJ8SjQG5qm8vra0F7b bPTA== X-Received: by 10.68.177.36 with SMTP id cn4mr19529646pbc.114.1430500373708; Fri, 01 May 2015 10:12:53 -0700 (PDT) Received: from localhost.localdomain (KD118152108246.ppp-bb.dion.ne.jp. [118.152.108.246]) by mx.google.com with ESMTPSA id fl4sm5377631pab.8.2015.05.01.10.12.51 (version=TLSv1.2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Fri, 01 May 2015 10:12:53 -0700 (PDT) From: Yoshihiro Kaneko To: linux-serial@vger.kernel.org Cc: Greg Kroah-Hartman , Simon Horman , Magnus Damm , linux-sh@vger.kernel.org Subject: [PATCH/RFC] serial: sh-sci: Add R-Car SCIF DMA receive support Date: Sat, 2 May 2015 02:11:34 +0900 Message-Id: <1430500294-23407-1-git-send-email-ykaneko0929@gmail.com> X-Mailer: git-send-email 1.9.1 Sender: linux-sh-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-sh@vger.kernel.org X-Spam-Status: No, score=-6.8 required=5.0 tests=BAYES_00, DKIM_ADSP_CUSTOM_MED, DKIM_SIGNED, FREEMAIL_FROM, RCVD_IN_DNSWL_HI, T_DKIM_INVALID, T_RP_MATCHES_RCVD, UNPARSEABLE_RELAY autolearn=ham version=3.3.1 X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on mail.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP From: Koji Matsuoka Signed-off-by: Koji Matsuoka Signed-off-by: Kazuya Mizuguchi Signed-off-by: Yoshihiro Kaneko --- This patch is based on the tty-next branch of Greg Kroah-Hartman's tty tree. This patch is the result of squashing two patches. One, of the same name, by Koji Matsuoka. And a second small fix by Kazuya Mizuguchi. Accordingly I have included both authors signed-off-by lines above. drivers/tty/serial/sh-sci.c | 142 +++++++++++++++++++++++++++++++++++--------- 1 file changed, 114 insertions(+), 28 deletions(-) diff --git a/drivers/tty/serial/sh-sci.c b/drivers/tty/serial/sh-sci.c index e7d6566..65cc72a 100644 --- a/drivers/tty/serial/sh-sci.c +++ b/drivers/tty/serial/sh-sci.c @@ -1,4 +1,8 @@ /* + * drivers/tty/serial/sh-sci.c + * + * Copyright (C) 2014 Renesas Electronics Corporation + * * SuperH on-chip serial module support. (SCI with no FIFO / with FIFO) * * Copyright (C) 2002 - 2011 Paul Mundt @@ -103,13 +107,13 @@ struct sci_port { #ifdef CONFIG_SERIAL_SH_SCI_DMA struct dma_async_tx_descriptor *desc_tx; - struct dma_async_tx_descriptor *desc_rx[2]; + struct dma_async_tx_descriptor *desc_rx[3]; dma_cookie_t cookie_tx; - dma_cookie_t cookie_rx[2]; + dma_cookie_t cookie_rx[3]; dma_cookie_t active_rx; struct scatterlist sg_tx; unsigned int sg_len_tx; - struct scatterlist sg_rx[2]; + struct scatterlist sg_rx[3]; size_t buf_len_rx; struct sh_dmae_slave param_tx; struct sh_dmae_slave param_rx; @@ -117,6 +121,8 @@ struct sci_port { struct work_struct work_rx; struct timer_list rx_timer; unsigned int rx_timeout; + int rx_flag; + int rx_release_flag; #endif struct notifier_block freq_transition; @@ -1300,6 +1306,8 @@ static int sci_dma_rx_push(struct sci_port *s, size_t count) active = 0; } else if (s->active_rx == s->cookie_rx[1]) { active = 1; + } else if (s->active_rx == s->cookie_rx[2]) { + active = 2; } else { dev_err(port->dev, "cookie %d not found!\n", s->active_rx); return 0; @@ -1332,7 +1340,14 @@ static void sci_dma_rx_complete(void *arg) spin_lock_irqsave(&port->lock, flags); - count = sci_dma_rx_push(s, s->buf_len_rx); + s->rx_flag = 1; + + if ((port->type == PORT_SCIF || port->type == PORT_HSCIF) && + s->active_rx == s->cookie_rx[0]) { + count = sci_dma_rx_push(s, 1); + async_tx_ack(s->desc_rx[0]); + } else + count = sci_dma_rx_push(s, s->buf_len_rx); mod_timer(&s->rx_timer, jiffies + s->rx_timeout); @@ -1348,13 +1363,26 @@ static void sci_rx_dma_release(struct sci_port *s, bool enable_pio) { struct dma_chan *chan = s->chan_rx; struct uart_port *port = &s->port; + size_t buf_len_rx; + int t = 100000; + + s->rx_release_flag = 1; + while (t--) { + if (!s->rx_flag) + break; + usleep_range(10, 50); + } s->chan_rx = NULL; - s->cookie_rx[0] = s->cookie_rx[1] = -EINVAL; + s->cookie_rx[0] = s->cookie_rx[1] = s->cookie_rx[2] = -EINVAL; + if (port->type == PORT_SCIF || port->type == PORT_HSCIF) + buf_len_rx = 1 + s->buf_len_rx * 2; + else + buf_len_rx = s->buf_len_rx * 2; dma_release_channel(chan); if (sg_dma_address(&s->sg_rx[0])) - dma_free_coherent(port->dev, s->buf_len_rx * 2, - sg_virt(&s->sg_rx[0]), sg_dma_address(&s->sg_rx[0])); + dma_free_coherent(port->dev, buf_len_rx, sg_virt(&s->sg_rx[0]), + sg_dma_address(&s->sg_rx[0])); if (enable_pio) sci_start_rx(port); } @@ -1374,9 +1402,15 @@ static void sci_tx_dma_release(struct sci_port *s, bool enable_pio) static void sci_submit_rx(struct sci_port *s) { struct dma_chan *chan = s->chan_rx; + struct uart_port *port = &s->port; int i; + int j; + int nr_descs = 2; - for (i = 0; i < 2; i++) { + if (port->type == PORT_SCIF || port->type == PORT_HSCIF) + nr_descs = 3; + + for (i = 0; i < nr_descs; i++) { struct scatterlist *sg = &s->sg_rx[i]; struct dma_async_tx_descriptor *desc; @@ -1392,8 +1426,10 @@ static void sci_submit_rx(struct sci_port *s) if (!desc || s->cookie_rx[i] < 0) { if (i) { - async_tx_ack(s->desc_rx[0]); - s->cookie_rx[0] = -EINVAL; + for (j = 0; j < i; j++) { + async_tx_ack(s->desc_rx[j]); + s->cookie_rx[j] = -EINVAL; + } } if (desc) { async_tx_ack(desc); @@ -1406,11 +1442,13 @@ static void sci_submit_rx(struct sci_port *s) } dev_dbg(s->port.dev, "%s(): cookie %d to #%d\n", __func__, s->cookie_rx[i], i); - } - s->active_rx = s->cookie_rx[0]; + if (i == 0) { + s->active_rx = s->cookie_rx[0]; + dma_async_issue_pending(chan); + } + } - dma_async_issue_pending(chan); } static void work_fn_rx(struct work_struct *work) @@ -1419,11 +1457,19 @@ static void work_fn_rx(struct work_struct *work) struct uart_port *port = &s->port; struct dma_async_tx_descriptor *desc; int new; + int next; + + if (s->chan_rx == NULL) { + dev_dbg(port->dev, "%s: DMA channel is released.\n", __func__); + return; + } if (s->active_rx == s->cookie_rx[0]) { new = 0; } else if (s->active_rx == s->cookie_rx[1]) { new = 1; + } else if (s->active_rx == s->cookie_rx[2]) { + new = 2; } else { dev_err(port->dev, "cookie %d not found!\n", s->active_rx); return; @@ -1450,19 +1496,34 @@ static void work_fn_rx(struct work_struct *work) if (count) tty_flip_buffer_push(&port->state->port); - sci_submit_rx(s); + if (!s->rx_release_flag) + sci_submit_rx(s); + s->rx_flag = 0; return; } - s->cookie_rx[new] = desc->tx_submit(desc); - if (s->cookie_rx[new] < 0) { - dev_warn(port->dev, "Failed submitting Rx DMA descriptor\n"); - sci_rx_dma_release(s, true); - return; + if (port->type == PORT_SCIF || port->type == PORT_HSCIF) { + if (new != 0) { + s->cookie_rx[new] = desc->tx_submit(desc); + if (s->cookie_rx[new] < 0) { + dev_warn(port->dev, "Failed submitting Rx DMA descriptor\n"); + sci_rx_dma_release(s, true); + return; + } + } + next = new % 2 + 1; + } else { + s->cookie_rx[new] = desc->tx_submit(desc); + if (s->cookie_rx[new] < 0) { + dev_warn(port->dev, "Failed submitting Rx DMA descriptor\n"); + sci_rx_dma_release(s, true); + return; + } + next = !new; } - s->active_rx = s->cookie_rx[!new]; + s->active_rx = s->cookie_rx[next]; dev_dbg(port->dev, "%s: cookie %d #%d, new active #%d\n", __func__, s->cookie_rx[new], new, s->active_rx); @@ -1666,6 +1727,8 @@ static void sci_request_dma(struct uart_port *port) if (s->cfg->dma_slave_tx <= 0 || s->cfg->dma_slave_rx <= 0) return; + s->rx_flag = 0; + s->rx_release_flag = 0; dma_cap_zero(mask); dma_cap_set(DMA_SLAVE, mask); @@ -1707,14 +1770,22 @@ static void sci_request_dma(struct uart_port *port) chan = dma_request_channel(mask, filter, param); dev_dbg(port->dev, "%s: RX: got channel %p\n", __func__, chan); if (chan) { - dma_addr_t dma[2]; - void *buf[2]; + dma_addr_t dma[3]; + void *buf[3]; + size_t sum_buf_len; + int nr_sg = 2; int i; s->chan_rx = chan; s->buf_len_rx = 2 * max(16, (int)port->fifosize); - buf[0] = dma_alloc_coherent(port->dev, s->buf_len_rx * 2, + if (port->type == PORT_SCIF || port->type == PORT_HSCIF) { + nr_sg = 3; + sum_buf_len = 1 + s->buf_len_rx * 2; + } else { + sum_buf_len = s->buf_len_rx * 2; + } + buf[0] = dma_alloc_coherent(port->dev, sum_buf_len, &dma[0], GFP_KERNEL); if (!buf[0]) { @@ -1724,14 +1795,28 @@ static void sci_request_dma(struct uart_port *port) return; } - buf[1] = buf[0] + s->buf_len_rx; - dma[1] = dma[0] + s->buf_len_rx; + if (port->type == PORT_SCIF || port->type == PORT_HSCIF) { + buf[1] = buf[0] + 1; + dma[1] = dma[0] + 1; + buf[2] = buf[1] + s->buf_len_rx; + dma[2] = dma[1] + s->buf_len_rx; + } else { + buf[1] = buf[0] + s->buf_len_rx; + dma[1] = dma[0] + s->buf_len_rx; + } - for (i = 0; i < 2; i++) { + for (i = 0; i < nr_sg; i++) { struct scatterlist *sg = &s->sg_rx[i]; + unsigned int len; + + if ((port->type == PORT_SCIF || + port->type == PORT_HSCIF) && i == 0) + len = 1; + else + len = s->buf_len_rx; sg_init_table(sg, 1); - sg_set_page(sg, virt_to_page(buf[i]), s->buf_len_rx, + sg_set_page(sg, virt_to_page(buf[i]), len, (uintptr_t)buf[i] & ~PAGE_MASK); sg_dma_address(sg) = dma[i]; } @@ -1778,7 +1863,6 @@ static int sci_startup(struct uart_port *port) spin_lock_irqsave(&port->lock, flags); sci_start_tx(port); - sci_start_rx(port); spin_unlock_irqrestore(&port->lock, flags); return 0; @@ -1796,6 +1880,8 @@ static void sci_shutdown(struct uart_port *port) sci_stop_tx(port); spin_unlock_irqrestore(&port->lock, flags); + serial_port_out(port, SCSCR, 0x00); + sci_free_dma(port); sci_free_irq(s); }