linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
From: Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org>
To: vkoul@kernel.org, dan.j.williams@intel.com, afaerber@suse.de,
	robh+dt@kernel.org, gregkh@linuxfoundation.org, jslaby@suse.com
Cc: linux-serial@vger.kernel.org, dmaengine@vger.kernel.org,
	liuwei@actions-semi.com, 96boards@ucrobotics.com,
	devicetree@vger.kernel.org, daniel.thompson@linaro.org,
	amit.kucheria@linaro.org, linux-arm-kernel@lists.infradead.org,
	linux-kernel@vger.kernel.org, hzhang@ucrobotics.com,
	bdong@ucrobotics.com, manivannanece23@gmail.com,
	thomas.liau@actions-semi.com, jeff.chen@actions-semi.com,
	pn@denx.de, edgar.righi@lsitec.org.br,
	Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org>
Subject: [PATCH v2 3/3] tty: serial: Add Tx DMA support for UART in Actions Semi Owl SoCs
Date: Sat, 29 Sep 2018 13:16:37 +0530	[thread overview]
Message-ID: <20180929074637.9766-4-manivannan.sadhasivam@linaro.org> (raw)
In-Reply-To: <20180929074637.9766-1-manivannan.sadhasivam@linaro.org>

Add Tx DMA support for Actions Semi Owl SoCs. If there is no DMA
property specified in DT, it will fallback to default interrupt mode.

Signed-off-by: Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org>
---
 drivers/tty/serial/owl-uart.c | 172 +++++++++++++++++++++++++++++++++-
 1 file changed, 171 insertions(+), 1 deletion(-)

diff --git a/drivers/tty/serial/owl-uart.c b/drivers/tty/serial/owl-uart.c
index 29a6dc6a8d23..1b3016db7ae2 100644
--- a/drivers/tty/serial/owl-uart.c
+++ b/drivers/tty/serial/owl-uart.c
@@ -11,6 +11,8 @@
 #include <linux/clk.h>
 #include <linux/console.h>
 #include <linux/delay.h>
+#include <linux/dmaengine.h>
+#include <linux/dma-mapping.h>
 #include <linux/io.h>
 #include <linux/module.h>
 #include <linux/of.h>
@@ -48,6 +50,8 @@
 #define OWL_UART_CTL_RXIE		BIT(18)
 #define OWL_UART_CTL_TXIE		BIT(19)
 #define OWL_UART_CTL_LBEN		BIT(20)
+#define OWL_UART_CTL_DRCR		BIT(21)
+#define OWL_UART_CTL_DTCR		BIT(22)
 
 #define OWL_UART_STAT_RIP		BIT(0)
 #define OWL_UART_STAT_TIP		BIT(1)
@@ -71,12 +75,21 @@ struct owl_uart_info {
 struct owl_uart_port {
 	struct uart_port port;
 	struct clk *clk;
+
+	struct dma_chan *tx_ch;
+	dma_addr_t tx_dma_buf;
+	dma_cookie_t dma_tx_cookie;
+	u32 tx_size;
+	bool tx_dma;
+	bool dma_tx_running;
 };
 
 #define to_owl_uart_port(prt) container_of(prt, struct owl_uart_port, prt)
 
 static struct owl_uart_port *owl_uart_ports[OWL_UART_PORT_NUM];
 
+static void owl_uart_dma_start_tx(struct owl_uart_port *owl_port);
+
 static inline void owl_uart_write(struct uart_port *port, u32 val, unsigned int off)
 {
 	writel(val, port->membase + off);
@@ -115,6 +128,83 @@ static unsigned int owl_uart_get_mctrl(struct uart_port *port)
 	return mctrl;
 }
 
+static void owl_uart_dma_tx_callback(void *data)
+{
+	struct owl_uart_port *owl_port = data;
+	struct uart_port *port = &owl_port->port;
+	struct circ_buf	*xmit = &port->state->xmit;
+	unsigned long flags;
+	u32 val;
+
+	dma_sync_single_for_cpu(port->dev, owl_port->tx_dma_buf,
+				UART_XMIT_SIZE, DMA_TO_DEVICE);
+
+	spin_lock_irqsave(&port->lock, flags);
+
+	owl_port->dma_tx_running = 0;
+
+	xmit->tail += owl_port->tx_size;
+	xmit->tail &= UART_XMIT_SIZE - 1;
+	port->icount.tx += owl_port->tx_size;
+
+	if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
+		uart_write_wakeup(port);
+
+	/* Disable Tx DRQ */
+	val = owl_uart_read(port, OWL_UART_CTL);
+	val &= ~OWL_UART_CTL_TXDE;
+	owl_uart_write(port, val, OWL_UART_CTL);
+
+	/* Clear pending Tx IRQ */
+	val = owl_uart_read(port, OWL_UART_STAT);
+	val |= OWL_UART_STAT_TIP;
+	owl_uart_write(port, val, OWL_UART_STAT);
+
+	if (!uart_circ_empty(xmit) && !uart_tx_stopped(port))
+		owl_uart_dma_start_tx(owl_port);
+
+	spin_unlock_irqrestore(&port->lock, flags);
+}
+
+static void owl_uart_dma_start_tx(struct owl_uart_port *owl_port)
+{
+	struct uart_port *port = &owl_port->port;
+	struct circ_buf *xmit = &port->state->xmit;
+	struct dma_async_tx_descriptor *desc;
+	u32 val;
+
+	if (uart_tx_stopped(port) || uart_circ_empty(xmit) ||
+	    owl_port->dma_tx_running)
+		return;
+
+	dma_sync_single_for_device(port->dev, owl_port->tx_dma_buf,
+				   UART_XMIT_SIZE, DMA_TO_DEVICE);
+
+	owl_port->tx_size = CIRC_CNT_TO_END(xmit->head, xmit->tail,
+					    UART_XMIT_SIZE);
+
+	desc = dmaengine_prep_slave_single(owl_port->tx_ch,
+					   owl_port->tx_dma_buf + xmit->tail,
+					   owl_port->tx_size, DMA_MEM_TO_DEV,
+					   DMA_PREP_INTERRUPT);
+	if (!desc)
+		return;
+
+	desc->callback = owl_uart_dma_tx_callback;
+	desc->callback_param = owl_port;
+
+	/* Enable Tx DRQ */
+	val = owl_uart_read(port, OWL_UART_CTL);
+	val &= ~OWL_UART_CTL_TXIE;
+	val |= OWL_UART_CTL_TXDE | OWL_UART_CTL_DTCR;
+	owl_uart_write(port, val, OWL_UART_CTL);
+
+	/* Start Tx DMA transfer */
+	owl_port->dma_tx_running = true;
+	owl_port->dma_tx_cookie = dmaengine_submit(desc);
+	dma_async_issue_pending(owl_port->tx_ch);
+}
+
 static unsigned int owl_uart_tx_empty(struct uart_port *port)
 {
 	unsigned long flags;
@@ -159,6 +249,7 @@ static void owl_uart_stop_tx(struct uart_port *port)
 
 static void owl_uart_start_tx(struct uart_port *port)
 {
+	struct owl_uart_port *owl_port = to_owl_uart_port(port);
 	u32 val;
 
 	if (uart_tx_stopped(port)) {
@@ -166,6 +257,11 @@ static void owl_uart_start_tx(struct uart_port *port)
 		return;
 	}
 
+	if (owl_port->tx_dma) {
+		owl_uart_dma_start_tx(owl_port);
+		return;
+	}
+
 	val = owl_uart_read(port, OWL_UART_STAT);
 	val |= OWL_UART_STAT_TIP;
 	owl_uart_write(port, val, OWL_UART_STAT);
@@ -273,13 +369,27 @@ static irqreturn_t owl_uart_irq(int irq, void *dev_id)
 	return IRQ_HANDLED;
 }
 
+static void owl_dma_channel_free(struct owl_uart_port *owl_port)
+{
+	dmaengine_terminate_all(owl_port->tx_ch);
+	dma_release_channel(owl_port->tx_ch);
+	dma_unmap_single(owl_port->port.dev, owl_port->tx_dma_buf,
+			 UART_XMIT_SIZE, DMA_TO_DEVICE);
+	owl_port->dma_tx_running = false;
+	owl_port->tx_ch = NULL;
+}
+
 static void owl_uart_shutdown(struct uart_port *port)
 {
-	u32 val;
+	struct owl_uart_port *owl_port = to_owl_uart_port(port);
 	unsigned long flags;
+	u32 val;
 
 	spin_lock_irqsave(&port->lock, flags);
 
+	if (owl_port->tx_dma)
+		owl_dma_channel_free(owl_port);
+
 	val = owl_uart_read(port, OWL_UART_CTL);
 	val &= ~(OWL_UART_CTL_TXIE | OWL_UART_CTL_RXIE
 		| OWL_UART_CTL_TXDE | OWL_UART_CTL_RXDE | OWL_UART_CTL_EN);
@@ -290,6 +400,62 @@ static void owl_uart_shutdown(struct uart_port *port)
 	free_irq(port->irq, port);
 }
 
+static int owl_uart_dma_tx_init(struct uart_port *port)
+{
+	struct owl_uart_port *owl_port = to_owl_uart_port(port);
+	struct device *dev = port->dev;
+	struct dma_slave_config slave_config;
+	int ret;
+
+	owl_port->tx_dma = false;
+
+	/* Request DMA TX channel */
+	owl_port->tx_ch = dma_request_slave_channel(dev, "tx");
+	if (!owl_port->tx_ch) {
+		dev_info(dev, "tx dma alloc failed\n");
+		return -ENODEV;
+	}
+
+	owl_port->tx_dma_buf = dma_map_single(dev,
+					      owl_port->port.state->xmit.buf,
+					      UART_XMIT_SIZE, DMA_TO_DEVICE);
+	if (dma_mapping_error(dev, owl_port->tx_dma_buf)) {
+		ret = -ENOMEM;
+		goto alloc_err;
+	}
+
+	/* Configure DMA channel */
+	memset(&slave_config, 0, sizeof(slave_config));
+	slave_config.direction = DMA_MEM_TO_DEV;
+	slave_config.dst_addr = port->mapbase + OWL_UART_TXDAT;
+	slave_config.dst_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE;
+
+	ret = dmaengine_slave_config(owl_port->tx_ch, &slave_config);
+	if (ret < 0) {
+		dev_err(dev, "tx dma channel config failed\n");
+		ret = -ENODEV;
+		goto map_err;
+	}
+
+	/* Use DMA buffer size as the FIFO size */
+	port->fifosize = UART_XMIT_SIZE;
+
+	/* Set DMA flag */
+	owl_port->tx_dma = true;
+	owl_port->dma_tx_running = false;
+
+	return 0;
+
+map_err:
+	dma_unmap_single(dev, owl_port->tx_dma_buf, UART_XMIT_SIZE,
+			 DMA_TO_DEVICE);
+alloc_err:
+	dma_release_channel(owl_port->tx_ch);
+	owl_port->tx_ch = NULL;
+
+	return ret;
+}
+
 static int owl_uart_startup(struct uart_port *port)
 {
 	u32 val;
@@ -301,6 +467,10 @@ static int owl_uart_startup(struct uart_port *port)
 	if (ret)
 		return ret;
 
+	ret = owl_uart_dma_tx_init(port);
+	if (!ret)
+		dev_info(port->dev, "using DMA for tx\n");
+
 	spin_lock_irqsave(&port->lock, flags);
 
 	val = owl_uart_read(port, OWL_UART_STAT);
-- 
2.17.1


      parent reply	other threads:[~2018-09-29  7:47 UTC|newest]

Thread overview: 6+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2018-09-29  7:46 [PATCH v2 0/3] Add slave DMA support for Actions Semi S900 SoC Manivannan Sadhasivam
2018-09-29  7:46 ` [PATCH v2 1/3] arm64: dts: actions: s900: Enable Tx DMA for UART5 Manivannan Sadhasivam
2018-09-30  2:04   ` kbuild test robot
2018-09-29  7:46 ` [PATCH v2 2/3] dmaengine: Add Slave and Cyclic mode support for Actions Semi Owl S900 SoC Manivannan Sadhasivam
2018-10-05 15:01   ` Vinod
2018-09-29  7:46 ` Manivannan Sadhasivam [this message]

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20180929074637.9766-4-manivannan.sadhasivam@linaro.org \
    --to=manivannan.sadhasivam@linaro.org \
    --cc=96boards@ucrobotics.com \
    --cc=afaerber@suse.de \
    --cc=amit.kucheria@linaro.org \
    --cc=bdong@ucrobotics.com \
    --cc=dan.j.williams@intel.com \
    --cc=daniel.thompson@linaro.org \
    --cc=devicetree@vger.kernel.org \
    --cc=dmaengine@vger.kernel.org \
    --cc=edgar.righi@lsitec.org.br \
    --cc=gregkh@linuxfoundation.org \
    --cc=hzhang@ucrobotics.com \
    --cc=jeff.chen@actions-semi.com \
    --cc=jslaby@suse.com \
    --cc=linux-arm-kernel@lists.infradead.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-serial@vger.kernel.org \
    --cc=liuwei@actions-semi.com \
    --cc=manivannanece23@gmail.com \
    --cc=pn@denx.de \
    --cc=robh+dt@kernel.org \
    --cc=thomas.liau@actions-semi.com \
    --cc=vkoul@kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).