lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-Id: <20180901164215.3683-4-manivannan.sadhasivam@linaro.org>
Date:   Sat,  1 Sep 2018 22:12:15 +0530
From:   Manivannan Sadhasivam <manivannan.sadhasivam@...aro.org>
To:     vkoul@...nel.org, dan.j.williams@...el.com, afaerber@...e.de,
        robh+dt@...nel.org, gregkh@...uxfoundation.org, jslaby@...e.com
Cc:     linux-serial@...r.kernel.org, dmaengine@...r.kernel.org,
        liuwei@...ions-semi.com, 96boards@...obotics.com,
        devicetree@...r.kernel.org, daniel.thompson@...aro.org,
        amit.kucheria@...aro.org, linux-arm-kernel@...ts.infradead.org,
        linux-kernel@...r.kernel.org, hzhang@...obotics.com,
        bdong@...obotics.com, manivannanece23@...il.com,
        thomas.liau@...ions-semi.com, jeff.chen@...ions-semi.com,
        pn@...x.de, edgar.righi@...tec.org.br,
        Manivannan Sadhasivam <manivannan.sadhasivam@...aro.org>
Subject: [PATCH 3/3] tty: serial: Add Tx DMA support for UART in Actions Semi Owl SoCs

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@...aro.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

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ