All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v3 1/2] ARM: dts: vf610: lpuart: Add eDMA support
@ 2014-01-15  5:21 ` Yuan Yao
  0 siblings, 0 replies; 24+ messages in thread
From: Yuan Yao @ 2014-01-15  5:21 UTC (permalink / raw)
  To: gregkh, shawn.guo; +Cc: linux, arnd, linux-arm-kernel, linux-serial

Add lpuart dts node properties for eDMA support, them depend on the eDMA driver.
See VF610_EDMA_MUXID0_UART*_*X macro on eDMA driver

Signed-off-by: Yuan Yao <yao.yuan@freescale.com>
---
 arch/arm/boot/dts/vf610.dtsi | 12 ++++++++++++
 1 file changed, 12 insertions(+)

diff --git a/arch/arm/boot/dts/vf610.dtsi b/arch/arm/boot/dts/vf610.dtsi
index 365e0fa..ed6b053 100644
--- a/arch/arm/boot/dts/vf610.dtsi
+++ b/arch/arm/boot/dts/vf610.dtsi
@@ -118,6 +118,9 @@
 				interrupts = <0 61 0x00>;
 				clocks = <&clks VF610_CLK_UART0>;
 				clock-names = "ipg";
+				dma-names = "lpuart-tx","lpuart-rx";
+				dmas = <&edma0 0 VF610_EDMA_MUXID0_UART0_TX>,
+					<&edma0 0 VF610_EDMA_MUXID0_UART0_RX>;
 				status = "disabled";
 			};
 
@@ -127,6 +130,9 @@
 				interrupts = <0 62 0x04>;
 				clocks = <&clks VF610_CLK_UART1>;
 				clock-names = "ipg";
+				dma-names = "lpuart-tx","lpuart-rx";
+				dmas = <&edma0 0 VF610_EDMA_MUXID0_UART1_TX>,
+					<&edma0 0 VF610_EDMA_MUXID0_UART1_RX>;
 				status = "disabled";
 			};
 
@@ -136,6 +142,9 @@
 				interrupts = <0 63 0x04>;
 				clocks = <&clks VF610_CLK_UART2>;
 				clock-names = "ipg";
+				dma-names = "lpuart-tx","lpuart-rx";
+				dmas = <&edma0 0 VF610_EDMA_MUXID0_UART2_TX>,
+					<&edma0 0 VF610_EDMA_MUXID0_UART2_RX>;
 				status = "disabled";
 			};
 
@@ -145,6 +154,9 @@
 				interrupts = <0 64 0x04>;
 				clocks = <&clks VF610_CLK_UART3>;
 				clock-names = "ipg";
+				dma-names = "lpuart-tx","lpuart-rx";
+				dmas = <&edma0 0 VF610_EDMA_MUXID0_UART3_TX>,
+					<&edma0 0 VF610_EDMA_MUXID0_UART3_RX>;
 				status = "disabled";
 			};
 
-- 
1.8.4



^ permalink raw reply related	[flat|nested] 24+ messages in thread

* [PATCH v3 1/2] ARM: dts: vf610: lpuart: Add eDMA support
@ 2014-01-15  5:21 ` Yuan Yao
  0 siblings, 0 replies; 24+ messages in thread
From: Yuan Yao @ 2014-01-15  5:21 UTC (permalink / raw)
  To: linux-arm-kernel

Add lpuart dts node properties for eDMA support, them depend on the eDMA driver.
See VF610_EDMA_MUXID0_UART*_*X macro on eDMA driver

Signed-off-by: Yuan Yao <yao.yuan@freescale.com>
---
 arch/arm/boot/dts/vf610.dtsi | 12 ++++++++++++
 1 file changed, 12 insertions(+)

diff --git a/arch/arm/boot/dts/vf610.dtsi b/arch/arm/boot/dts/vf610.dtsi
index 365e0fa..ed6b053 100644
--- a/arch/arm/boot/dts/vf610.dtsi
+++ b/arch/arm/boot/dts/vf610.dtsi
@@ -118,6 +118,9 @@
 				interrupts = <0 61 0x00>;
 				clocks = <&clks VF610_CLK_UART0>;
 				clock-names = "ipg";
+				dma-names = "lpuart-tx","lpuart-rx";
+				dmas = <&edma0 0 VF610_EDMA_MUXID0_UART0_TX>,
+					<&edma0 0 VF610_EDMA_MUXID0_UART0_RX>;
 				status = "disabled";
 			};
 
@@ -127,6 +130,9 @@
 				interrupts = <0 62 0x04>;
 				clocks = <&clks VF610_CLK_UART1>;
 				clock-names = "ipg";
+				dma-names = "lpuart-tx","lpuart-rx";
+				dmas = <&edma0 0 VF610_EDMA_MUXID0_UART1_TX>,
+					<&edma0 0 VF610_EDMA_MUXID0_UART1_RX>;
 				status = "disabled";
 			};
 
@@ -136,6 +142,9 @@
 				interrupts = <0 63 0x04>;
 				clocks = <&clks VF610_CLK_UART2>;
 				clock-names = "ipg";
+				dma-names = "lpuart-tx","lpuart-rx";
+				dmas = <&edma0 0 VF610_EDMA_MUXID0_UART2_TX>,
+					<&edma0 0 VF610_EDMA_MUXID0_UART2_RX>;
 				status = "disabled";
 			};
 
@@ -145,6 +154,9 @@
 				interrupts = <0 64 0x04>;
 				clocks = <&clks VF610_CLK_UART3>;
 				clock-names = "ipg";
+				dma-names = "lpuart-tx","lpuart-rx";
+				dmas = <&edma0 0 VF610_EDMA_MUXID0_UART3_TX>,
+					<&edma0 0 VF610_EDMA_MUXID0_UART3_RX>;
 				status = "disabled";
 			};
 
-- 
1.8.4

^ permalink raw reply related	[flat|nested] 24+ messages in thread

* [PATCH v3 2/2] serial: fsl_lpuart: add DMA support
  2014-01-15  5:21 ` Yuan Yao
@ 2014-01-15  5:21   ` Yuan Yao
  -1 siblings, 0 replies; 24+ messages in thread
From: Yuan Yao @ 2014-01-15  5:21 UTC (permalink / raw)
  To: gregkh, shawn.guo; +Cc: linux, arnd, linux-arm-kernel, linux-serial

Add dma support for lpuart. This function depend on DMA driver. 
You can turn on it by SERIAL_FSL_LPUART_DMA=y. And It works if dts node has dma properties.

Signed-off-by: Yuan Yao <yao.yuan@freescale.com>
---
 .../devicetree/bindings/serial/fsl-lpuart.txt      |  21 +-
 drivers/tty/serial/Kconfig                         |   7 +
 drivers/tty/serial/fsl_lpuart.c                    | 457 ++++++++++++++++++++-
 3 files changed, 473 insertions(+), 12 deletions(-)

diff --git a/Documentation/devicetree/bindings/serial/fsl-lpuart.txt b/Documentation/devicetree/bindings/serial/fsl-lpuart.txt
index 6fd1dd1..7509080 100644
--- a/Documentation/devicetree/bindings/serial/fsl-lpuart.txt
+++ b/Documentation/devicetree/bindings/serial/fsl-lpuart.txt
@@ -4,11 +4,24 @@ Required properties:
 - compatible : Should be "fsl,<soc>-lpuart"
 - reg : Address and length of the register set for the device
 - interrupts : Should contain uart interrupt
+- clocks : from common clock binding: handle to uart clock
+- clock-names : from common clock binding: Shall be "ipg"
+
+Optional properties:
+- dma-names: Should contain "lpuart-tx" for transmit and "lpuart-rx" for receive channels
+- dmas: Should contain dma specifiers for transmit and receive channels
+
+Note: Optional properties for DMA support, If need this properties is must.
 
 Example:
 
 uart0: serial@40027000 {
-	       compatible = "fsl,vf610-lpuart";
-	       reg = <0x40027000 0x1000>;
-	       interrupts = <0 61 0x00>;
-       };
+		compatible = "fsl,vf610-lpuart";
+		reg = <0x40027000 0x1000>;
+		interrupts = <0 61 0x00>;
+		clocks = <&clks VF610_CLK_UART0>;
+		clock-names = "ipg";
+		dma-names = "lpuart-tx","lpuart-rx";
+		dmas = <&edma0 0 VF610_EDMA_MUXID0_UART0_TX>,
+			<&edma0 0 VF610_EDMA_MUXID0_UART0_RX>;
+	};
diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig
index a3817ab..ab3910b 100644
--- a/drivers/tty/serial/Kconfig
+++ b/drivers/tty/serial/Kconfig
@@ -1509,6 +1509,13 @@ config SERIAL_FSL_LPUART_CONSOLE
 	  If you have enabled the lpuart serial port on the Freescale SoCs,
 	  you can make it the console by answering Y to this option.
 
+config SERIAL_FSL_LPUART_DMA
+	bool "Freescale lpuart serial port dma support"
+	depends on FSL_EDMA=y && SERIAL_FSL_LPUART=y
+	help
+	  DMA support for the on-chip lpuart on some Freescale SOCs,
+	  you can turn on the dma support for lpuart by answering Y to this option.
+
 config SERIAL_ST_ASC
 	tristate "ST ASC serial port support"
 	select SERIAL_CORE
diff --git a/drivers/tty/serial/fsl_lpuart.c b/drivers/tty/serial/fsl_lpuart.c
index 67b8946..5fdcb18 100644
--- a/drivers/tty/serial/fsl_lpuart.c
+++ b/drivers/tty/serial/fsl_lpuart.c
@@ -13,14 +13,19 @@
 #define SUPPORT_SYSRQ
 #endif
 
-#include <linux/module.h>
+#include <linux/clk.h>
+#include <linux/console.h>
+#include <linux/dma-mapping.h>
+#include <linux/dmaengine.h>
+#include <linux/dmapool.h>
 #include <linux/io.h>
 #include <linux/irq.h>
-#include <linux/clk.h>
+#include <linux/module.h>
 #include <linux/of.h>
 #include <linux/of_device.h>
-#include <linux/console.h>
+#include <linux/of_dma.h>
 #include <linux/serial_core.h>
+#include <linux/slab.h>
 #include <linux/tty_flip.h>
 
 /* All registers are 8-bit width */
@@ -112,6 +117,12 @@
 #define UARTSFIFO_TXOF		0x02
 #define UARTSFIFO_RXUF		0x01
 
+#ifdef CONFIG_SERIAL_FSL_LPUART_DMA
+#define DMA_MAXBURST		16
+#define DMA_MAXBURST_MASK	(DMA_MAXBURST - 1)
+#define FSL_UART_RX_DMA_BUFFER_SIZE	64
+#endif
+
 #define DRIVER_NAME	"fsl-lpuart"
 #define DEV_NAME	"ttyLP"
 #define UART_NR		6
@@ -121,6 +132,26 @@ struct lpuart_port {
 	struct clk		*clk;
 	unsigned int		txfifo_size;
 	unsigned int		rxfifo_size;
+
+#ifdef CONFIG_SERIAL_FSL_LPUART_DMA
+	bool			lpuart_dma_use;
+	struct dma_chan		*dma_tx_chan;
+	struct dma_chan		*dma_rx_chan;
+	struct dma_async_tx_descriptor  *dma_tx_desc;
+	struct dma_async_tx_descriptor  *dma_rx_desc;
+	dma_addr_t		dma_tx_buf_bus;
+	dma_addr_t		dma_rx_buf_bus;
+	dma_cookie_t		dma_tx_cookie;
+	dma_cookie_t		dma_rx_cookie;
+	unsigned char		*dma_tx_buf_virt;
+	unsigned char		*dma_rx_buf_virt;
+	unsigned int		dma_tx_bytes;
+	unsigned int		dma_rx_bytes;
+	int			dma_tx_in_progress;
+	int			dma_rx_in_progress;
+	unsigned int		dma_rx_timeout;
+	struct timer_list	lpuart_timer;
+#endif
 };
 
 static struct of_device_id lpuart_dt_ids[] = {
@@ -131,6 +162,12 @@ static struct of_device_id lpuart_dt_ids[] = {
 };
 MODULE_DEVICE_TABLE(of, lpuart_dt_ids);
 
+#ifdef CONFIG_SERIAL_FSL_LPUART_DMA
+/* Forward declare this for the dma callbacks*/
+static void lpuart_dma_tx_complete(void *arg);
+static void lpuart_dma_rx_complete(void *arg);
+#endif
+
 static void lpuart_stop_tx(struct uart_port *port)
 {
 	unsigned char temp;
@@ -152,6 +189,212 @@ static void lpuart_enable_ms(struct uart_port *port)
 {
 }
 
+#ifdef CONFIG_SERIAL_FSL_LPUART_DMA
+static void lpuart_copy_rx_to_tty(struct lpuart_port *sport,
+		struct tty_port *tty, int count)
+{
+	int copied;
+
+	sport->port.icount.rx += count;
+
+	if (!tty) {
+		dev_err(sport->port.dev, "No tty port\n");
+		return;
+	}
+
+	dma_sync_single_for_cpu(sport->port.dev, sport->dma_rx_buf_bus,
+			FSL_UART_RX_DMA_BUFFER_SIZE, DMA_FROM_DEVICE);
+	copied = tty_insert_flip_string(tty,
+			((unsigned char *)(sport->dma_rx_buf_virt)), count);
+
+	if (copied != count) {
+		WARN_ON(1);
+		dev_err(sport->port.dev, "RxData copy to tty layer failed\n");
+	}
+
+	dma_sync_single_for_device(sport->port.dev, sport->dma_rx_buf_bus,
+			FSL_UART_RX_DMA_BUFFER_SIZE, DMA_TO_DEVICE);
+}
+
+static void lpuart_pio_tx(struct lpuart_port *sport)
+{
+	struct circ_buf *xmit = &sport->port.state->xmit;
+	unsigned long flags;
+
+	spin_lock_irqsave(&sport->port.lock, flags);
+
+	while (!uart_circ_empty(xmit) &&
+		readb(sport->port.membase + UARTTCFIFO) < sport->txfifo_size) {
+		writeb(xmit->buf[xmit->tail], sport->port.membase + UARTDR);
+		xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
+		sport->port.icount.tx++;
+	}
+
+	if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
+		uart_write_wakeup(&sport->port);
+
+	if (uart_circ_empty(xmit))
+		writeb(readb(sport->port.membase + UARTCR5) | UARTCR5_TDMAS,
+			sport->port.membase + UARTCR5);
+
+	spin_unlock_irqrestore(&sport->port.lock, flags);
+}
+
+static int lpuart_dma_tx(struct lpuart_port *sport, unsigned long count)
+{
+	struct circ_buf *xmit = &sport->port.state->xmit;
+	dma_addr_t tx_bus_addr;
+
+	dma_sync_single_for_device(sport->port.dev, sport->dma_tx_buf_bus,
+				UART_XMIT_SIZE, DMA_TO_DEVICE);
+	sport->dma_tx_bytes = count & ~(DMA_MAXBURST_MASK);
+	tx_bus_addr = sport->dma_tx_buf_bus + xmit->tail;
+	sport->dma_tx_desc = dmaengine_prep_slave_single(sport->dma_tx_chan,
+					tx_bus_addr, sport->dma_tx_bytes,
+					DMA_MEM_TO_DEV, DMA_PREP_INTERRUPT);
+
+	if (!sport->dma_tx_desc) {
+		dev_err(sport->port.dev, "Not able to get desc for tx\n");
+		return -EIO;
+	}
+
+	sport->dma_tx_desc->callback = lpuart_dma_tx_complete;
+	sport->dma_tx_desc->callback_param = sport;
+	sport->dma_tx_in_progress = 1;
+	sport->dma_tx_cookie = dmaengine_submit(sport->dma_tx_desc);
+	dma_async_issue_pending(sport->dma_tx_chan);
+
+	return 0;
+}
+
+static void lpuart_prepare_tx(struct lpuart_port *sport)
+{
+	struct circ_buf *xmit = &sport->port.state->xmit;
+	unsigned long count =  CIRC_CNT_TO_END(xmit->head,
+					xmit->tail, UART_XMIT_SIZE);
+
+	if (!count)
+		return;
+
+	if (count < DMA_MAXBURST)
+		writeb(readb(sport->port.membase + UARTCR5) & ~UARTCR5_TDMAS,
+				sport->port.membase + UARTCR5);
+	else {
+		writeb(readb(sport->port.membase + UARTCR5) | UARTCR5_TDMAS,
+				sport->port.membase + UARTCR5);
+		lpuart_dma_tx(sport, count);
+	}
+}
+
+static void lpuart_dma_tx_complete(void *arg)
+{
+	struct lpuart_port *sport = arg;
+	struct circ_buf *xmit = &sport->port.state->xmit;
+	unsigned long flags;
+
+	async_tx_ack(sport->dma_tx_desc);
+
+	spin_lock_irqsave(&sport->port.lock, flags);
+
+	xmit->tail = (xmit->tail + sport->dma_tx_bytes) & (UART_XMIT_SIZE - 1);
+	sport->dma_tx_in_progress = 0;
+
+	if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
+		uart_write_wakeup(&sport->port);
+
+	lpuart_prepare_tx(sport);
+
+	spin_unlock_irqrestore(&sport->port.lock, flags);
+}
+
+static int lpuart_dma_rx(struct lpuart_port *sport)
+{
+	dma_sync_single_for_device(sport->port.dev, sport->dma_rx_buf_bus,
+			FSL_UART_RX_DMA_BUFFER_SIZE, DMA_TO_DEVICE);
+	sport->dma_rx_desc = dmaengine_prep_slave_single(sport->dma_rx_chan,
+			sport->dma_rx_buf_bus, FSL_UART_RX_DMA_BUFFER_SIZE,
+			DMA_DEV_TO_MEM, DMA_PREP_INTERRUPT);
+
+	if (!sport->dma_rx_desc) {
+		dev_err(sport->port.dev, "Not able to get desc for rx\n");
+		return -EIO;
+	}
+
+	sport->dma_rx_desc->callback = lpuart_dma_rx_complete;
+	sport->dma_rx_desc->callback_param = sport;
+	sport->dma_rx_in_progress = 1;
+	sport->dma_rx_cookie = dmaengine_submit(sport->dma_rx_desc);
+	dma_async_issue_pending(sport->dma_rx_chan);
+
+	return 0;
+}
+
+static void lpuart_dma_rx_complete(void *arg)
+{
+	struct lpuart_port *sport = arg;
+	struct tty_port *port = &sport->port.state->port;
+	unsigned long flags;
+
+	async_tx_ack(sport->dma_rx_desc);
+
+	spin_lock_irqsave(&sport->port.lock, flags);
+
+	sport->dma_rx_in_progress = 0;
+	lpuart_copy_rx_to_tty(sport, port, FSL_UART_RX_DMA_BUFFER_SIZE);
+	tty_flip_buffer_push(port);
+	lpuart_dma_rx(sport);
+
+	spin_unlock_irqrestore(&sport->port.lock, flags);
+}
+
+static void lpuart_timer_func(unsigned long data)
+{
+	struct lpuart_port *sport = (struct lpuart_port *)data;
+	struct tty_port *port = &sport->port.state->port;
+	struct dma_tx_state state;
+	unsigned long flags;
+	unsigned char temp;
+	int count;
+
+	del_timer(&sport->lpuart_timer);
+	dmaengine_pause(sport->dma_rx_chan);
+	dmaengine_tx_status(sport->dma_rx_chan, sport->dma_rx_cookie, &state);
+	dmaengine_terminate_all(sport->dma_rx_chan);
+	count = FSL_UART_RX_DMA_BUFFER_SIZE - state.residue;
+	async_tx_ack(sport->dma_rx_desc);
+
+	spin_lock_irqsave(&sport->port.lock, flags);
+
+	sport->dma_rx_in_progress = 0;
+	lpuart_copy_rx_to_tty(sport, port, count);
+	tty_flip_buffer_push(port);
+	temp = readb(sport->port.membase + UARTCR5);
+	writeb(temp & ~UARTCR5_RDMAS, sport->port.membase + UARTCR5);
+
+	spin_unlock_irqrestore(&sport->port.lock, flags);
+}
+
+static inline void lpuart_prepare_rx(struct lpuart_port *sport)
+{
+	unsigned long flags;
+	unsigned char temp;
+
+	spin_lock_irqsave(&sport->port.lock, flags);
+
+	init_timer(&sport->lpuart_timer);
+	sport->lpuart_timer.function = lpuart_timer_func;
+	sport->lpuart_timer.data = (unsigned long)sport;
+	sport->lpuart_timer.expires = jiffies + sport->dma_rx_timeout;
+	add_timer(&sport->lpuart_timer);
+
+	lpuart_dma_rx(sport);
+	temp = readb(sport->port.membase + UARTCR5);
+	writeb(temp | UARTCR5_RDMAS, sport->port.membase + UARTCR5);
+
+	spin_unlock_irqrestore(&sport->port.lock, flags);
+}
+#endif
+
 static inline void lpuart_transmit_buffer(struct lpuart_port *sport)
 {
 	struct circ_buf *xmit = &sport->port.state->xmit;
@@ -172,14 +415,26 @@ static inline void lpuart_transmit_buffer(struct lpuart_port *sport)
 
 static void lpuart_start_tx(struct uart_port *port)
 {
-	struct lpuart_port *sport = container_of(port, struct lpuart_port, port);
+	struct lpuart_port *sport = container_of(port,
+			struct lpuart_port, port);
+	struct circ_buf *xmit = &sport->port.state->xmit;
 	unsigned char temp;
 
 	temp = readb(port->membase + UARTCR2);
 	writeb(temp | UARTCR2_TIE, port->membase + UARTCR2);
 
+#ifdef CONFIG_SERIAL_FSL_LPUART_DMA
+	if (sport->lpuart_dma_use) {
+		if (!uart_circ_empty(xmit) && !sport->dma_tx_in_progress)
+			lpuart_prepare_tx(sport);
+	} else {
+		if (readb(port->membase + UARTSR1) & UARTSR1_TDRE)
+			lpuart_transmit_buffer(sport);
+	}
+#else
 	if (readb(port->membase + UARTSR1) & UARTSR1_TDRE)
 		lpuart_transmit_buffer(sport);
+#endif
 }
 
 static irqreturn_t lpuart_txint(int irq, void *dev_id)
@@ -279,12 +534,27 @@ static irqreturn_t lpuart_int(int irq, void *dev_id)
 
 	sts = readb(sport->port.membase + UARTSR1);
 
-	if (sts & UARTSR1_RDRF)
+	if (sts & UARTSR1_RDRF) {
+#ifdef CONFIG_SERIAL_FSL_LPUART_DMA
+		if (sport->lpuart_dma_use)
+			lpuart_prepare_rx(sport);
+		else
+			lpuart_rxint(irq, dev_id);
+#else
 		lpuart_rxint(irq, dev_id);
-
+#endif
+	}
 	if (sts & UARTSR1_TDRE &&
-		!(readb(sport->port.membase + UARTCR5) & UARTCR5_TDMAS))
+		!(readb(sport->port.membase + UARTCR5) & UARTCR5_TDMAS)) {
+#ifdef CONFIG_SERIAL_FSL_LPUART_DMA
+		if (sport->lpuart_dma_use)
+			lpuart_pio_tx(sport);
+		else
+			lpuart_txint(irq, dev_id);
+#else
 		lpuart_txint(irq, dev_id);
+#endif
+	}
 
 	return IRQ_HANDLED;
 }
@@ -366,19 +636,179 @@ static void lpuart_setup_watermark(struct lpuart_port *sport)
 	writeb(UARTCFIFO_TXFLUSH | UARTCFIFO_RXFLUSH,
 			sport->port.membase + UARTCFIFO);
 
-	writeb(2, sport->port.membase + UARTTWFIFO);
+	writeb(0, sport->port.membase + UARTTWFIFO);
 	writeb(1, sport->port.membase + UARTRWFIFO);
 
 	/* Restore cr2 */
 	writeb(cr2_saved, sport->port.membase + UARTCR2);
 }
 
+#ifdef CONFIG_SERIAL_FSL_LPUART_DMA
+static int lpuart_dma_tx_request(struct uart_port *port)
+{
+	struct lpuart_port *sport = container_of(port,
+					struct lpuart_port, port);
+	struct dma_chan *tx_chan;
+	struct dma_slave_config dma_tx_sconfig;
+	dma_addr_t dma_bus;
+	unsigned char *dma_buf;
+	int ret;
+
+	tx_chan  = dma_request_slave_channel(sport->port.dev, "lpuart-tx");
+
+	if (!tx_chan) {
+		dev_err(sport->port.dev, "Dma tx channel request failed!\n");
+		return -ENODEV;
+	}
+
+	dma_bus = dma_map_single(tx_chan->device->dev,
+				sport->port.state->xmit.buf,
+				UART_XMIT_SIZE, DMA_TO_DEVICE);
+
+	if (dma_mapping_error(tx_chan->device->dev, dma_bus)) {
+		dev_err(sport->port.dev, "dma_map_single tx failed\n");
+		dma_release_channel(tx_chan);
+		return -ENOMEM;
+	}
+
+	dma_buf = sport->port.state->xmit.buf;
+	dma_tx_sconfig.dst_addr = sport->port.mapbase + UARTDR;
+	dma_tx_sconfig.dst_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE;
+	dma_tx_sconfig.dst_maxburst = DMA_MAXBURST;
+	dma_tx_sconfig.direction = DMA_MEM_TO_DEV;
+	ret = dmaengine_slave_config(tx_chan, &dma_tx_sconfig);
+
+	if (ret < 0) {
+		dev_err(sport->port.dev,
+				"Dma slave config failed, err = %d\n", ret);
+		dma_release_channel(tx_chan);
+		return ret;
+	}
+
+	sport->dma_tx_chan = tx_chan;
+	sport->dma_tx_buf_virt = dma_buf;
+	sport->dma_tx_buf_bus = dma_bus;
+	sport->dma_tx_in_progress = 0;
+
+	return 0;
+}
+
+static int lpuart_dma_rx_request(struct uart_port *port)
+{
+	struct lpuart_port *sport = container_of(port,
+					struct lpuart_port, port);
+	struct dma_chan *rx_chan;
+	struct dma_slave_config dma_rx_sconfig;
+	dma_addr_t dma_bus;
+	unsigned char *dma_buf;
+	int ret;
+
+	rx_chan  = dma_request_slave_channel(sport->port.dev, "lpuart-rx");
+
+	if (!rx_chan) {
+		dev_err(sport->port.dev, "Dma rx channel request failed!\n");
+		return -ENODEV;
+	}
+
+	dma_buf = devm_kzalloc(sport->port.dev,
+				FSL_UART_RX_DMA_BUFFER_SIZE, GFP_KERNEL);
+
+	if (!dma_buf) {
+		dev_err(sport->port.dev, "Dma rx alloc failed\n");
+		dma_release_channel(rx_chan);
+		return -ENOMEM;
+	}
+
+	dma_bus = dma_map_single(rx_chan->device->dev, dma_buf,
+				FSL_UART_RX_DMA_BUFFER_SIZE, DMA_FROM_DEVICE);
+
+	if (dma_mapping_error(rx_chan->device->dev, dma_bus)) {
+		dev_err(sport->port.dev, "dma_map_single rx failed\n");
+		dma_release_channel(rx_chan);
+		return -ENOMEM;
+	}
+
+	dma_rx_sconfig.src_addr = sport->port.mapbase + UARTDR;
+	dma_rx_sconfig.src_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE;
+	dma_rx_sconfig.src_maxburst = 1;
+	dma_rx_sconfig.direction = DMA_DEV_TO_MEM;
+	ret = dmaengine_slave_config(rx_chan, &dma_rx_sconfig);
+
+	if (ret < 0) {
+		dev_err(sport->port.dev,
+				"Dma slave config failed, err = %d\n", ret);
+		dma_release_channel(rx_chan);
+		return ret;
+	}
+
+	sport->dma_rx_chan = rx_chan;
+	sport->dma_rx_buf_virt = dma_buf;
+	sport->dma_rx_buf_bus = dma_bus;
+	sport->dma_rx_in_progress = 0;
+
+	sport->dma_rx_timeout = (sport->port.timeout - HZ / 50) *
+				FSL_UART_RX_DMA_BUFFER_SIZE * 3 /
+				sport->rxfifo_size / 2;
+
+	if (sport->dma_rx_timeout < msecs_to_jiffies(20))
+		sport->dma_rx_timeout = msecs_to_jiffies(20);
+
+	return 0;
+}
+
+static void lpuart_dma_tx_free(struct uart_port *port)
+{
+	struct lpuart_port *sport = container_of(port,
+					struct lpuart_port, port);
+	struct dma_chan *dma_chan;
+
+	dma_unmap_single(sport->port.dev, sport->dma_tx_buf_bus,
+			UART_XMIT_SIZE, DMA_TO_DEVICE);
+	dma_chan = sport->dma_tx_chan;
+	sport->dma_tx_chan = NULL;
+	sport->dma_tx_buf_bus = 0;
+	sport->dma_tx_buf_virt = NULL;
+	dma_release_channel(dma_chan);
+}
+
+static void lpuart_dma_rx_free(struct uart_port *port)
+{
+	struct lpuart_port *sport = container_of(port,
+					struct lpuart_port, port);
+	struct dma_chan *dma_chan;
+
+	dma_unmap_single(sport->port.dev, sport->dma_rx_buf_bus,
+			FSL_UART_RX_DMA_BUFFER_SIZE, DMA_FROM_DEVICE);
+
+	dma_chan = sport->dma_rx_chan;
+	sport->dma_rx_chan = NULL;
+	sport->dma_rx_buf_bus = 0;
+	sport->dma_rx_buf_virt = NULL;
+	dma_release_channel(dma_chan);
+}
+#endif
+
 static int lpuart_startup(struct uart_port *port)
 {
 	struct lpuart_port *sport = container_of(port, struct lpuart_port, port);
 	int ret;
 	unsigned long flags;
 	unsigned char temp;
+
+#ifdef CONFIG_SERIAL_FSL_LPUART_DMA
+	struct platform_device *pdev = to_platform_device(port->dev);
+	struct device_node *np = pdev->dev.of_node;
+
+	if (of_get_property(np, "dmas", NULL)) {
+		sport->lpuart_dma_use = true;
+		lpuart_dma_tx_request(port);
+		lpuart_dma_rx_request(port);
+		temp = readb(port->membase + UARTCR5);
+		writeb(temp | UARTCR5_TDMAS, port->membase + UARTCR5);
+	} else
+		sport->lpuart_dma_use = false;
+#endif
+
 	ret = devm_request_irq(port->dev, port->irq, lpuart_int, 0,
 				DRIVER_NAME, sport);
 	if (ret)
@@ -413,6 +843,13 @@ static void lpuart_shutdown(struct uart_port *port)
 	spin_unlock_irqrestore(&port->lock, flags);
 
 	devm_free_irq(port->dev, port->irq, sport);
+
+#ifdef CONFIG_SERIAL_FSL_LPUART_DMA
+	if (sport->lpuart_dma_use) {
+		lpuart_dma_tx_free(port);
+		lpuart_dma_rx_free(port);
+	}
+#endif
 }
 
 static void
@@ -854,6 +1291,10 @@ static int __init lpuart_serial_init(void)
 
 	pr_info("serial: Freescale lpuart driver\n");
 
+#ifdef CONFIG_SERIAL_FSL_LPUART_DMA
+	pr_info("serial: Freescale lpuart dma support\n");
+#endif
+
 	ret = uart_register_driver(&lpuart_reg);
 	if (ret)
 		return ret;
-- 
1.8.4



^ permalink raw reply related	[flat|nested] 24+ messages in thread

* [PATCH v3 2/2] serial: fsl_lpuart: add DMA support
@ 2014-01-15  5:21   ` Yuan Yao
  0 siblings, 0 replies; 24+ messages in thread
From: Yuan Yao @ 2014-01-15  5:21 UTC (permalink / raw)
  To: linux-arm-kernel

Add dma support for lpuart. This function depend on DMA driver. 
You can turn on it by SERIAL_FSL_LPUART_DMA=y. And It works if dts node has dma properties.

Signed-off-by: Yuan Yao <yao.yuan@freescale.com>
---
 .../devicetree/bindings/serial/fsl-lpuart.txt      |  21 +-
 drivers/tty/serial/Kconfig                         |   7 +
 drivers/tty/serial/fsl_lpuart.c                    | 457 ++++++++++++++++++++-
 3 files changed, 473 insertions(+), 12 deletions(-)

diff --git a/Documentation/devicetree/bindings/serial/fsl-lpuart.txt b/Documentation/devicetree/bindings/serial/fsl-lpuart.txt
index 6fd1dd1..7509080 100644
--- a/Documentation/devicetree/bindings/serial/fsl-lpuart.txt
+++ b/Documentation/devicetree/bindings/serial/fsl-lpuart.txt
@@ -4,11 +4,24 @@ Required properties:
 - compatible : Should be "fsl,<soc>-lpuart"
 - reg : Address and length of the register set for the device
 - interrupts : Should contain uart interrupt
+- clocks : from common clock binding: handle to uart clock
+- clock-names : from common clock binding: Shall be "ipg"
+
+Optional properties:
+- dma-names: Should contain "lpuart-tx" for transmit and "lpuart-rx" for receive channels
+- dmas: Should contain dma specifiers for transmit and receive channels
+
+Note: Optional properties for DMA support, If need this properties is must.
 
 Example:
 
 uart0: serial at 40027000 {
-	       compatible = "fsl,vf610-lpuart";
-	       reg = <0x40027000 0x1000>;
-	       interrupts = <0 61 0x00>;
-       };
+		compatible = "fsl,vf610-lpuart";
+		reg = <0x40027000 0x1000>;
+		interrupts = <0 61 0x00>;
+		clocks = <&clks VF610_CLK_UART0>;
+		clock-names = "ipg";
+		dma-names = "lpuart-tx","lpuart-rx";
+		dmas = <&edma0 0 VF610_EDMA_MUXID0_UART0_TX>,
+			<&edma0 0 VF610_EDMA_MUXID0_UART0_RX>;
+	};
diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig
index a3817ab..ab3910b 100644
--- a/drivers/tty/serial/Kconfig
+++ b/drivers/tty/serial/Kconfig
@@ -1509,6 +1509,13 @@ config SERIAL_FSL_LPUART_CONSOLE
 	  If you have enabled the lpuart serial port on the Freescale SoCs,
 	  you can make it the console by answering Y to this option.
 
+config SERIAL_FSL_LPUART_DMA
+	bool "Freescale lpuart serial port dma support"
+	depends on FSL_EDMA=y && SERIAL_FSL_LPUART=y
+	help
+	  DMA support for the on-chip lpuart on some Freescale SOCs,
+	  you can turn on the dma support for lpuart by answering Y to this option.
+
 config SERIAL_ST_ASC
 	tristate "ST ASC serial port support"
 	select SERIAL_CORE
diff --git a/drivers/tty/serial/fsl_lpuart.c b/drivers/tty/serial/fsl_lpuart.c
index 67b8946..5fdcb18 100644
--- a/drivers/tty/serial/fsl_lpuart.c
+++ b/drivers/tty/serial/fsl_lpuart.c
@@ -13,14 +13,19 @@
 #define SUPPORT_SYSRQ
 #endif
 
-#include <linux/module.h>
+#include <linux/clk.h>
+#include <linux/console.h>
+#include <linux/dma-mapping.h>
+#include <linux/dmaengine.h>
+#include <linux/dmapool.h>
 #include <linux/io.h>
 #include <linux/irq.h>
-#include <linux/clk.h>
+#include <linux/module.h>
 #include <linux/of.h>
 #include <linux/of_device.h>
-#include <linux/console.h>
+#include <linux/of_dma.h>
 #include <linux/serial_core.h>
+#include <linux/slab.h>
 #include <linux/tty_flip.h>
 
 /* All registers are 8-bit width */
@@ -112,6 +117,12 @@
 #define UARTSFIFO_TXOF		0x02
 #define UARTSFIFO_RXUF		0x01
 
+#ifdef CONFIG_SERIAL_FSL_LPUART_DMA
+#define DMA_MAXBURST		16
+#define DMA_MAXBURST_MASK	(DMA_MAXBURST - 1)
+#define FSL_UART_RX_DMA_BUFFER_SIZE	64
+#endif
+
 #define DRIVER_NAME	"fsl-lpuart"
 #define DEV_NAME	"ttyLP"
 #define UART_NR		6
@@ -121,6 +132,26 @@ struct lpuart_port {
 	struct clk		*clk;
 	unsigned int		txfifo_size;
 	unsigned int		rxfifo_size;
+
+#ifdef CONFIG_SERIAL_FSL_LPUART_DMA
+	bool			lpuart_dma_use;
+	struct dma_chan		*dma_tx_chan;
+	struct dma_chan		*dma_rx_chan;
+	struct dma_async_tx_descriptor  *dma_tx_desc;
+	struct dma_async_tx_descriptor  *dma_rx_desc;
+	dma_addr_t		dma_tx_buf_bus;
+	dma_addr_t		dma_rx_buf_bus;
+	dma_cookie_t		dma_tx_cookie;
+	dma_cookie_t		dma_rx_cookie;
+	unsigned char		*dma_tx_buf_virt;
+	unsigned char		*dma_rx_buf_virt;
+	unsigned int		dma_tx_bytes;
+	unsigned int		dma_rx_bytes;
+	int			dma_tx_in_progress;
+	int			dma_rx_in_progress;
+	unsigned int		dma_rx_timeout;
+	struct timer_list	lpuart_timer;
+#endif
 };
 
 static struct of_device_id lpuart_dt_ids[] = {
@@ -131,6 +162,12 @@ static struct of_device_id lpuart_dt_ids[] = {
 };
 MODULE_DEVICE_TABLE(of, lpuart_dt_ids);
 
+#ifdef CONFIG_SERIAL_FSL_LPUART_DMA
+/* Forward declare this for the dma callbacks*/
+static void lpuart_dma_tx_complete(void *arg);
+static void lpuart_dma_rx_complete(void *arg);
+#endif
+
 static void lpuart_stop_tx(struct uart_port *port)
 {
 	unsigned char temp;
@@ -152,6 +189,212 @@ static void lpuart_enable_ms(struct uart_port *port)
 {
 }
 
+#ifdef CONFIG_SERIAL_FSL_LPUART_DMA
+static void lpuart_copy_rx_to_tty(struct lpuart_port *sport,
+		struct tty_port *tty, int count)
+{
+	int copied;
+
+	sport->port.icount.rx += count;
+
+	if (!tty) {
+		dev_err(sport->port.dev, "No tty port\n");
+		return;
+	}
+
+	dma_sync_single_for_cpu(sport->port.dev, sport->dma_rx_buf_bus,
+			FSL_UART_RX_DMA_BUFFER_SIZE, DMA_FROM_DEVICE);
+	copied = tty_insert_flip_string(tty,
+			((unsigned char *)(sport->dma_rx_buf_virt)), count);
+
+	if (copied != count) {
+		WARN_ON(1);
+		dev_err(sport->port.dev, "RxData copy to tty layer failed\n");
+	}
+
+	dma_sync_single_for_device(sport->port.dev, sport->dma_rx_buf_bus,
+			FSL_UART_RX_DMA_BUFFER_SIZE, DMA_TO_DEVICE);
+}
+
+static void lpuart_pio_tx(struct lpuart_port *sport)
+{
+	struct circ_buf *xmit = &sport->port.state->xmit;
+	unsigned long flags;
+
+	spin_lock_irqsave(&sport->port.lock, flags);
+
+	while (!uart_circ_empty(xmit) &&
+		readb(sport->port.membase + UARTTCFIFO) < sport->txfifo_size) {
+		writeb(xmit->buf[xmit->tail], sport->port.membase + UARTDR);
+		xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
+		sport->port.icount.tx++;
+	}
+
+	if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
+		uart_write_wakeup(&sport->port);
+
+	if (uart_circ_empty(xmit))
+		writeb(readb(sport->port.membase + UARTCR5) | UARTCR5_TDMAS,
+			sport->port.membase + UARTCR5);
+
+	spin_unlock_irqrestore(&sport->port.lock, flags);
+}
+
+static int lpuart_dma_tx(struct lpuart_port *sport, unsigned long count)
+{
+	struct circ_buf *xmit = &sport->port.state->xmit;
+	dma_addr_t tx_bus_addr;
+
+	dma_sync_single_for_device(sport->port.dev, sport->dma_tx_buf_bus,
+				UART_XMIT_SIZE, DMA_TO_DEVICE);
+	sport->dma_tx_bytes = count & ~(DMA_MAXBURST_MASK);
+	tx_bus_addr = sport->dma_tx_buf_bus + xmit->tail;
+	sport->dma_tx_desc = dmaengine_prep_slave_single(sport->dma_tx_chan,
+					tx_bus_addr, sport->dma_tx_bytes,
+					DMA_MEM_TO_DEV, DMA_PREP_INTERRUPT);
+
+	if (!sport->dma_tx_desc) {
+		dev_err(sport->port.dev, "Not able to get desc for tx\n");
+		return -EIO;
+	}
+
+	sport->dma_tx_desc->callback = lpuart_dma_tx_complete;
+	sport->dma_tx_desc->callback_param = sport;
+	sport->dma_tx_in_progress = 1;
+	sport->dma_tx_cookie = dmaengine_submit(sport->dma_tx_desc);
+	dma_async_issue_pending(sport->dma_tx_chan);
+
+	return 0;
+}
+
+static void lpuart_prepare_tx(struct lpuart_port *sport)
+{
+	struct circ_buf *xmit = &sport->port.state->xmit;
+	unsigned long count =  CIRC_CNT_TO_END(xmit->head,
+					xmit->tail, UART_XMIT_SIZE);
+
+	if (!count)
+		return;
+
+	if (count < DMA_MAXBURST)
+		writeb(readb(sport->port.membase + UARTCR5) & ~UARTCR5_TDMAS,
+				sport->port.membase + UARTCR5);
+	else {
+		writeb(readb(sport->port.membase + UARTCR5) | UARTCR5_TDMAS,
+				sport->port.membase + UARTCR5);
+		lpuart_dma_tx(sport, count);
+	}
+}
+
+static void lpuart_dma_tx_complete(void *arg)
+{
+	struct lpuart_port *sport = arg;
+	struct circ_buf *xmit = &sport->port.state->xmit;
+	unsigned long flags;
+
+	async_tx_ack(sport->dma_tx_desc);
+
+	spin_lock_irqsave(&sport->port.lock, flags);
+
+	xmit->tail = (xmit->tail + sport->dma_tx_bytes) & (UART_XMIT_SIZE - 1);
+	sport->dma_tx_in_progress = 0;
+
+	if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
+		uart_write_wakeup(&sport->port);
+
+	lpuart_prepare_tx(sport);
+
+	spin_unlock_irqrestore(&sport->port.lock, flags);
+}
+
+static int lpuart_dma_rx(struct lpuart_port *sport)
+{
+	dma_sync_single_for_device(sport->port.dev, sport->dma_rx_buf_bus,
+			FSL_UART_RX_DMA_BUFFER_SIZE, DMA_TO_DEVICE);
+	sport->dma_rx_desc = dmaengine_prep_slave_single(sport->dma_rx_chan,
+			sport->dma_rx_buf_bus, FSL_UART_RX_DMA_BUFFER_SIZE,
+			DMA_DEV_TO_MEM, DMA_PREP_INTERRUPT);
+
+	if (!sport->dma_rx_desc) {
+		dev_err(sport->port.dev, "Not able to get desc for rx\n");
+		return -EIO;
+	}
+
+	sport->dma_rx_desc->callback = lpuart_dma_rx_complete;
+	sport->dma_rx_desc->callback_param = sport;
+	sport->dma_rx_in_progress = 1;
+	sport->dma_rx_cookie = dmaengine_submit(sport->dma_rx_desc);
+	dma_async_issue_pending(sport->dma_rx_chan);
+
+	return 0;
+}
+
+static void lpuart_dma_rx_complete(void *arg)
+{
+	struct lpuart_port *sport = arg;
+	struct tty_port *port = &sport->port.state->port;
+	unsigned long flags;
+
+	async_tx_ack(sport->dma_rx_desc);
+
+	spin_lock_irqsave(&sport->port.lock, flags);
+
+	sport->dma_rx_in_progress = 0;
+	lpuart_copy_rx_to_tty(sport, port, FSL_UART_RX_DMA_BUFFER_SIZE);
+	tty_flip_buffer_push(port);
+	lpuart_dma_rx(sport);
+
+	spin_unlock_irqrestore(&sport->port.lock, flags);
+}
+
+static void lpuart_timer_func(unsigned long data)
+{
+	struct lpuart_port *sport = (struct lpuart_port *)data;
+	struct tty_port *port = &sport->port.state->port;
+	struct dma_tx_state state;
+	unsigned long flags;
+	unsigned char temp;
+	int count;
+
+	del_timer(&sport->lpuart_timer);
+	dmaengine_pause(sport->dma_rx_chan);
+	dmaengine_tx_status(sport->dma_rx_chan, sport->dma_rx_cookie, &state);
+	dmaengine_terminate_all(sport->dma_rx_chan);
+	count = FSL_UART_RX_DMA_BUFFER_SIZE - state.residue;
+	async_tx_ack(sport->dma_rx_desc);
+
+	spin_lock_irqsave(&sport->port.lock, flags);
+
+	sport->dma_rx_in_progress = 0;
+	lpuart_copy_rx_to_tty(sport, port, count);
+	tty_flip_buffer_push(port);
+	temp = readb(sport->port.membase + UARTCR5);
+	writeb(temp & ~UARTCR5_RDMAS, sport->port.membase + UARTCR5);
+
+	spin_unlock_irqrestore(&sport->port.lock, flags);
+}
+
+static inline void lpuart_prepare_rx(struct lpuart_port *sport)
+{
+	unsigned long flags;
+	unsigned char temp;
+
+	spin_lock_irqsave(&sport->port.lock, flags);
+
+	init_timer(&sport->lpuart_timer);
+	sport->lpuart_timer.function = lpuart_timer_func;
+	sport->lpuart_timer.data = (unsigned long)sport;
+	sport->lpuart_timer.expires = jiffies + sport->dma_rx_timeout;
+	add_timer(&sport->lpuart_timer);
+
+	lpuart_dma_rx(sport);
+	temp = readb(sport->port.membase + UARTCR5);
+	writeb(temp | UARTCR5_RDMAS, sport->port.membase + UARTCR5);
+
+	spin_unlock_irqrestore(&sport->port.lock, flags);
+}
+#endif
+
 static inline void lpuart_transmit_buffer(struct lpuart_port *sport)
 {
 	struct circ_buf *xmit = &sport->port.state->xmit;
@@ -172,14 +415,26 @@ static inline void lpuart_transmit_buffer(struct lpuart_port *sport)
 
 static void lpuart_start_tx(struct uart_port *port)
 {
-	struct lpuart_port *sport = container_of(port, struct lpuart_port, port);
+	struct lpuart_port *sport = container_of(port,
+			struct lpuart_port, port);
+	struct circ_buf *xmit = &sport->port.state->xmit;
 	unsigned char temp;
 
 	temp = readb(port->membase + UARTCR2);
 	writeb(temp | UARTCR2_TIE, port->membase + UARTCR2);
 
+#ifdef CONFIG_SERIAL_FSL_LPUART_DMA
+	if (sport->lpuart_dma_use) {
+		if (!uart_circ_empty(xmit) && !sport->dma_tx_in_progress)
+			lpuart_prepare_tx(sport);
+	} else {
+		if (readb(port->membase + UARTSR1) & UARTSR1_TDRE)
+			lpuart_transmit_buffer(sport);
+	}
+#else
 	if (readb(port->membase + UARTSR1) & UARTSR1_TDRE)
 		lpuart_transmit_buffer(sport);
+#endif
 }
 
 static irqreturn_t lpuart_txint(int irq, void *dev_id)
@@ -279,12 +534,27 @@ static irqreturn_t lpuart_int(int irq, void *dev_id)
 
 	sts = readb(sport->port.membase + UARTSR1);
 
-	if (sts & UARTSR1_RDRF)
+	if (sts & UARTSR1_RDRF) {
+#ifdef CONFIG_SERIAL_FSL_LPUART_DMA
+		if (sport->lpuart_dma_use)
+			lpuart_prepare_rx(sport);
+		else
+			lpuart_rxint(irq, dev_id);
+#else
 		lpuart_rxint(irq, dev_id);
-
+#endif
+	}
 	if (sts & UARTSR1_TDRE &&
-		!(readb(sport->port.membase + UARTCR5) & UARTCR5_TDMAS))
+		!(readb(sport->port.membase + UARTCR5) & UARTCR5_TDMAS)) {
+#ifdef CONFIG_SERIAL_FSL_LPUART_DMA
+		if (sport->lpuart_dma_use)
+			lpuart_pio_tx(sport);
+		else
+			lpuart_txint(irq, dev_id);
+#else
 		lpuart_txint(irq, dev_id);
+#endif
+	}
 
 	return IRQ_HANDLED;
 }
@@ -366,19 +636,179 @@ static void lpuart_setup_watermark(struct lpuart_port *sport)
 	writeb(UARTCFIFO_TXFLUSH | UARTCFIFO_RXFLUSH,
 			sport->port.membase + UARTCFIFO);
 
-	writeb(2, sport->port.membase + UARTTWFIFO);
+	writeb(0, sport->port.membase + UARTTWFIFO);
 	writeb(1, sport->port.membase + UARTRWFIFO);
 
 	/* Restore cr2 */
 	writeb(cr2_saved, sport->port.membase + UARTCR2);
 }
 
+#ifdef CONFIG_SERIAL_FSL_LPUART_DMA
+static int lpuart_dma_tx_request(struct uart_port *port)
+{
+	struct lpuart_port *sport = container_of(port,
+					struct lpuart_port, port);
+	struct dma_chan *tx_chan;
+	struct dma_slave_config dma_tx_sconfig;
+	dma_addr_t dma_bus;
+	unsigned char *dma_buf;
+	int ret;
+
+	tx_chan  = dma_request_slave_channel(sport->port.dev, "lpuart-tx");
+
+	if (!tx_chan) {
+		dev_err(sport->port.dev, "Dma tx channel request failed!\n");
+		return -ENODEV;
+	}
+
+	dma_bus = dma_map_single(tx_chan->device->dev,
+				sport->port.state->xmit.buf,
+				UART_XMIT_SIZE, DMA_TO_DEVICE);
+
+	if (dma_mapping_error(tx_chan->device->dev, dma_bus)) {
+		dev_err(sport->port.dev, "dma_map_single tx failed\n");
+		dma_release_channel(tx_chan);
+		return -ENOMEM;
+	}
+
+	dma_buf = sport->port.state->xmit.buf;
+	dma_tx_sconfig.dst_addr = sport->port.mapbase + UARTDR;
+	dma_tx_sconfig.dst_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE;
+	dma_tx_sconfig.dst_maxburst = DMA_MAXBURST;
+	dma_tx_sconfig.direction = DMA_MEM_TO_DEV;
+	ret = dmaengine_slave_config(tx_chan, &dma_tx_sconfig);
+
+	if (ret < 0) {
+		dev_err(sport->port.dev,
+				"Dma slave config failed, err = %d\n", ret);
+		dma_release_channel(tx_chan);
+		return ret;
+	}
+
+	sport->dma_tx_chan = tx_chan;
+	sport->dma_tx_buf_virt = dma_buf;
+	sport->dma_tx_buf_bus = dma_bus;
+	sport->dma_tx_in_progress = 0;
+
+	return 0;
+}
+
+static int lpuart_dma_rx_request(struct uart_port *port)
+{
+	struct lpuart_port *sport = container_of(port,
+					struct lpuart_port, port);
+	struct dma_chan *rx_chan;
+	struct dma_slave_config dma_rx_sconfig;
+	dma_addr_t dma_bus;
+	unsigned char *dma_buf;
+	int ret;
+
+	rx_chan  = dma_request_slave_channel(sport->port.dev, "lpuart-rx");
+
+	if (!rx_chan) {
+		dev_err(sport->port.dev, "Dma rx channel request failed!\n");
+		return -ENODEV;
+	}
+
+	dma_buf = devm_kzalloc(sport->port.dev,
+				FSL_UART_RX_DMA_BUFFER_SIZE, GFP_KERNEL);
+
+	if (!dma_buf) {
+		dev_err(sport->port.dev, "Dma rx alloc failed\n");
+		dma_release_channel(rx_chan);
+		return -ENOMEM;
+	}
+
+	dma_bus = dma_map_single(rx_chan->device->dev, dma_buf,
+				FSL_UART_RX_DMA_BUFFER_SIZE, DMA_FROM_DEVICE);
+
+	if (dma_mapping_error(rx_chan->device->dev, dma_bus)) {
+		dev_err(sport->port.dev, "dma_map_single rx failed\n");
+		dma_release_channel(rx_chan);
+		return -ENOMEM;
+	}
+
+	dma_rx_sconfig.src_addr = sport->port.mapbase + UARTDR;
+	dma_rx_sconfig.src_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE;
+	dma_rx_sconfig.src_maxburst = 1;
+	dma_rx_sconfig.direction = DMA_DEV_TO_MEM;
+	ret = dmaengine_slave_config(rx_chan, &dma_rx_sconfig);
+
+	if (ret < 0) {
+		dev_err(sport->port.dev,
+				"Dma slave config failed, err = %d\n", ret);
+		dma_release_channel(rx_chan);
+		return ret;
+	}
+
+	sport->dma_rx_chan = rx_chan;
+	sport->dma_rx_buf_virt = dma_buf;
+	sport->dma_rx_buf_bus = dma_bus;
+	sport->dma_rx_in_progress = 0;
+
+	sport->dma_rx_timeout = (sport->port.timeout - HZ / 50) *
+				FSL_UART_RX_DMA_BUFFER_SIZE * 3 /
+				sport->rxfifo_size / 2;
+
+	if (sport->dma_rx_timeout < msecs_to_jiffies(20))
+		sport->dma_rx_timeout = msecs_to_jiffies(20);
+
+	return 0;
+}
+
+static void lpuart_dma_tx_free(struct uart_port *port)
+{
+	struct lpuart_port *sport = container_of(port,
+					struct lpuart_port, port);
+	struct dma_chan *dma_chan;
+
+	dma_unmap_single(sport->port.dev, sport->dma_tx_buf_bus,
+			UART_XMIT_SIZE, DMA_TO_DEVICE);
+	dma_chan = sport->dma_tx_chan;
+	sport->dma_tx_chan = NULL;
+	sport->dma_tx_buf_bus = 0;
+	sport->dma_tx_buf_virt = NULL;
+	dma_release_channel(dma_chan);
+}
+
+static void lpuart_dma_rx_free(struct uart_port *port)
+{
+	struct lpuart_port *sport = container_of(port,
+					struct lpuart_port, port);
+	struct dma_chan *dma_chan;
+
+	dma_unmap_single(sport->port.dev, sport->dma_rx_buf_bus,
+			FSL_UART_RX_DMA_BUFFER_SIZE, DMA_FROM_DEVICE);
+
+	dma_chan = sport->dma_rx_chan;
+	sport->dma_rx_chan = NULL;
+	sport->dma_rx_buf_bus = 0;
+	sport->dma_rx_buf_virt = NULL;
+	dma_release_channel(dma_chan);
+}
+#endif
+
 static int lpuart_startup(struct uart_port *port)
 {
 	struct lpuart_port *sport = container_of(port, struct lpuart_port, port);
 	int ret;
 	unsigned long flags;
 	unsigned char temp;
+
+#ifdef CONFIG_SERIAL_FSL_LPUART_DMA
+	struct platform_device *pdev = to_platform_device(port->dev);
+	struct device_node *np = pdev->dev.of_node;
+
+	if (of_get_property(np, "dmas", NULL)) {
+		sport->lpuart_dma_use = true;
+		lpuart_dma_tx_request(port);
+		lpuart_dma_rx_request(port);
+		temp = readb(port->membase + UARTCR5);
+		writeb(temp | UARTCR5_TDMAS, port->membase + UARTCR5);
+	} else
+		sport->lpuart_dma_use = false;
+#endif
+
 	ret = devm_request_irq(port->dev, port->irq, lpuart_int, 0,
 				DRIVER_NAME, sport);
 	if (ret)
@@ -413,6 +843,13 @@ static void lpuart_shutdown(struct uart_port *port)
 	spin_unlock_irqrestore(&port->lock, flags);
 
 	devm_free_irq(port->dev, port->irq, sport);
+
+#ifdef CONFIG_SERIAL_FSL_LPUART_DMA
+	if (sport->lpuart_dma_use) {
+		lpuart_dma_tx_free(port);
+		lpuart_dma_rx_free(port);
+	}
+#endif
 }
 
 static void
@@ -854,6 +1291,10 @@ static int __init lpuart_serial_init(void)
 
 	pr_info("serial: Freescale lpuart driver\n");
 
+#ifdef CONFIG_SERIAL_FSL_LPUART_DMA
+	pr_info("serial: Freescale lpuart dma support\n");
+#endif
+
 	ret = uart_register_driver(&lpuart_reg);
 	if (ret)
 		return ret;
-- 
1.8.4

^ permalink raw reply related	[flat|nested] 24+ messages in thread

* Re: [PATCH v3 2/2] serial: fsl_lpuart: add DMA support
  2014-01-15  5:21   ` Yuan Yao
@ 2014-01-15  9:25     ` Arnd Bergmann
  -1 siblings, 0 replies; 24+ messages in thread
From: Arnd Bergmann @ 2014-01-15  9:25 UTC (permalink / raw)
  To: Yuan Yao; +Cc: gregkh, shawn.guo, linux, linux-arm-kernel, linux-serial

On Wednesday 15 January 2014, Yuan Yao wrote:
> Add dma support for lpuart. This function depend on DMA driver. 
> You can turn on it by SERIAL_FSL_LPUART_DMA=y. And It works if dts node has dma properties.
> 
> Signed-off-by: Yuan Yao <yao.yuan@freescale.com>

Most of the changes you did look good, but I have to say I'm not a fan of
the #ifdef you added. I don't think you actually need to have the compile-time
selection, and it would be best to just drop that part.

If you have a good reason to keep it, please change the code to
more readable "if (IS_ENABLED(CONFIG_FSL_LPUART_DMA))" constructs.
If you decide to do this, you will find that you need far fewer
such conditionals because a lot of the code will automatically
get dropped by the compiler.
  
>  uart0: serial@40027000 {
> -	       compatible = "fsl,vf610-lpuart";
> -	       reg = <0x40027000 0x1000>;
> -	       interrupts = <0 61 0x00>;
> -       };
> +		compatible = "fsl,vf610-lpuart";
> +		reg = <0x40027000 0x1000>;
> +		interrupts = <0 61 0x00>;
> +		clocks = <&clks VF610_CLK_UART0>;
> +		clock-names = "ipg";
> +		dma-names = "lpuart-tx","lpuart-rx";
> +		dmas = <&edma0 0 VF610_EDMA_MUXID0_UART0_TX>,
> +			<&edma0 0 VF610_EDMA_MUXID0_UART0_RX>;
> +	};

You still haven't addressed my comment about removing the VF610_EDMA_MUXID0_UART0_TX
macros.

>  
> +#ifdef CONFIG_SERIAL_FSL_LPUART_DMA
> +#define DMA_MAXBURST		16
> +#define DMA_MAXBURST_MASK	(DMA_MAXBURST - 1)
> +#define FSL_UART_RX_DMA_BUFFER_SIZE	64
> +#endif

For this part there was no need for the #ifdef to start with.

>  #define DRIVER_NAME	"fsl-lpuart"
>  #define DEV_NAME	"ttyLP"
>  #define UART_NR		6
> @@ -121,6 +132,26 @@ struct lpuart_port {
>  	struct clk		*clk;
>  	unsigned int		txfifo_size;
>  	unsigned int		rxfifo_size;
> +
> +#ifdef CONFIG_SERIAL_FSL_LPUART_DMA
> +	bool			lpuart_dma_use;
> +	struct dma_chan		*dma_tx_chan;
> +	struct dma_chan		*dma_rx_chan;
> +	struct dma_async_tx_descriptor  *dma_tx_desc;
> +	struct dma_async_tx_descriptor  *dma_rx_desc;
> +	dma_addr_t		dma_tx_buf_bus;
> +	dma_addr_t		dma_rx_buf_bus;
> +	dma_cookie_t		dma_tx_cookie;
> +	dma_cookie_t		dma_rx_cookie;
> +	unsigned char		*dma_tx_buf_virt;
> +	unsigned char		*dma_rx_buf_virt;
> +	unsigned int		dma_tx_bytes;
> +	unsigned int		dma_rx_bytes;
> +	int			dma_tx_in_progress;
> +	int			dma_rx_in_progress;
> +	unsigned int		dma_rx_timeout;
> +	struct timer_list	lpuart_timer;
> +#endif
>  };

This part will result in a slight increase in data size even if
dma support is turned off at compile time.

>  
>  	temp = readb(port->membase + UARTCR2);
>  	writeb(temp | UARTCR2_TIE, port->membase + UARTCR2);
>  
> +#ifdef CONFIG_SERIAL_FSL_LPUART_DMA
> +	if (sport->lpuart_dma_use) {
> +		if (!uart_circ_empty(xmit) && !sport->dma_tx_in_progress)
> +			lpuart_prepare_tx(sport);
> +	} else {
> +		if (readb(port->membase + UARTSR1) & UARTSR1_TDRE)
> +			lpuart_transmit_buffer(sport);
> +	}
> +#else
>  	if (readb(port->membase + UARTSR1) & UARTSR1_TDRE)
>  		lpuart_transmit_buffer(sport);
> +#endif
>  }

So this can simply become

+	if (IS_ENABLED(CONFIG_SERIAL_FSL_LPUART_DMA) && sport->lpuart_dma_use) {
+		if (!uart_circ_empty(xmit) && !sport->dma_tx_in_progress)
+			lpuart_prepare_tx(sport);
+	} else {
+		if (readb(port->membase + UARTSR1) & UARTSR1_TDRE)
+			lpuart_trans	mit_buffer(sport);
+	}

and the compiler will silently drop the lpuart_prepare_tx() function and everything
it calls that isn't used elsewhere.


> +
> +#ifdef CONFIG_SERIAL_FSL_LPUART_DMA
> +	struct platform_device *pdev = to_platform_device(port->dev);
> +	struct device_node *np = pdev->dev.of_node;
> +
> +	if (of_get_property(np, "dmas", NULL)) {
> +		sport->lpuart_dma_use = true;
> +		lpuart_dma_tx_request(port);
> +		lpuart_dma_rx_request(port);
> +		temp = readb(port->membase + UARTCR5);
> +		writeb(temp | UARTCR5_TDMAS, port->membase + UARTCR5);
> +	} else
> +		sport->lpuart_dma_use = false;	
> +#endif

Same here.

> @@ -854,6 +1291,10 @@ static int __init lpuart_serial_init(void)
>  
>  	pr_info("serial: Freescale lpuart driver\n");
>  
> +#ifdef CONFIG_SERIAL_FSL_LPUART_DMA
> +	pr_info("serial: Freescale lpuart dma support\n");
> +#endif
> +

This message is not helpful in the long run, I'd rather see you drop the
other one as well. Note that this function is called on *all* platforms
when the driver is enabled, not just on those that actually have the uart.

	Arnd

^ permalink raw reply	[flat|nested] 24+ messages in thread

* [PATCH v3 2/2] serial: fsl_lpuart: add DMA support
@ 2014-01-15  9:25     ` Arnd Bergmann
  0 siblings, 0 replies; 24+ messages in thread
From: Arnd Bergmann @ 2014-01-15  9:25 UTC (permalink / raw)
  To: linux-arm-kernel

On Wednesday 15 January 2014, Yuan Yao wrote:
> Add dma support for lpuart. This function depend on DMA driver. 
> You can turn on it by SERIAL_FSL_LPUART_DMA=y. And It works if dts node has dma properties.
> 
> Signed-off-by: Yuan Yao <yao.yuan@freescale.com>

Most of the changes you did look good, but I have to say I'm not a fan of
the #ifdef you added. I don't think you actually need to have the compile-time
selection, and it would be best to just drop that part.

If you have a good reason to keep it, please change the code to
more readable "if (IS_ENABLED(CONFIG_FSL_LPUART_DMA))" constructs.
If you decide to do this, you will find that you need far fewer
such conditionals because a lot of the code will automatically
get dropped by the compiler.
  
>  uart0: serial at 40027000 {
> -	       compatible = "fsl,vf610-lpuart";
> -	       reg = <0x40027000 0x1000>;
> -	       interrupts = <0 61 0x00>;
> -       };
> +		compatible = "fsl,vf610-lpuart";
> +		reg = <0x40027000 0x1000>;
> +		interrupts = <0 61 0x00>;
> +		clocks = <&clks VF610_CLK_UART0>;
> +		clock-names = "ipg";
> +		dma-names = "lpuart-tx","lpuart-rx";
> +		dmas = <&edma0 0 VF610_EDMA_MUXID0_UART0_TX>,
> +			<&edma0 0 VF610_EDMA_MUXID0_UART0_RX>;
> +	};

You still haven't addressed my comment about removing the VF610_EDMA_MUXID0_UART0_TX
macros.

>  
> +#ifdef CONFIG_SERIAL_FSL_LPUART_DMA
> +#define DMA_MAXBURST		16
> +#define DMA_MAXBURST_MASK	(DMA_MAXBURST - 1)
> +#define FSL_UART_RX_DMA_BUFFER_SIZE	64
> +#endif

For this part there was no need for the #ifdef to start with.

>  #define DRIVER_NAME	"fsl-lpuart"
>  #define DEV_NAME	"ttyLP"
>  #define UART_NR		6
> @@ -121,6 +132,26 @@ struct lpuart_port {
>  	struct clk		*clk;
>  	unsigned int		txfifo_size;
>  	unsigned int		rxfifo_size;
> +
> +#ifdef CONFIG_SERIAL_FSL_LPUART_DMA
> +	bool			lpuart_dma_use;
> +	struct dma_chan		*dma_tx_chan;
> +	struct dma_chan		*dma_rx_chan;
> +	struct dma_async_tx_descriptor  *dma_tx_desc;
> +	struct dma_async_tx_descriptor  *dma_rx_desc;
> +	dma_addr_t		dma_tx_buf_bus;
> +	dma_addr_t		dma_rx_buf_bus;
> +	dma_cookie_t		dma_tx_cookie;
> +	dma_cookie_t		dma_rx_cookie;
> +	unsigned char		*dma_tx_buf_virt;
> +	unsigned char		*dma_rx_buf_virt;
> +	unsigned int		dma_tx_bytes;
> +	unsigned int		dma_rx_bytes;
> +	int			dma_tx_in_progress;
> +	int			dma_rx_in_progress;
> +	unsigned int		dma_rx_timeout;
> +	struct timer_list	lpuart_timer;
> +#endif
>  };

This part will result in a slight increase in data size even if
dma support is turned off at compile time.

>  
>  	temp = readb(port->membase + UARTCR2);
>  	writeb(temp | UARTCR2_TIE, port->membase + UARTCR2);
>  
> +#ifdef CONFIG_SERIAL_FSL_LPUART_DMA
> +	if (sport->lpuart_dma_use) {
> +		if (!uart_circ_empty(xmit) && !sport->dma_tx_in_progress)
> +			lpuart_prepare_tx(sport);
> +	} else {
> +		if (readb(port->membase + UARTSR1) & UARTSR1_TDRE)
> +			lpuart_transmit_buffer(sport);
> +	}
> +#else
>  	if (readb(port->membase + UARTSR1) & UARTSR1_TDRE)
>  		lpuart_transmit_buffer(sport);
> +#endif
>  }

So this can simply become

+	if (IS_ENABLED(CONFIG_SERIAL_FSL_LPUART_DMA) && sport->lpuart_dma_use) {
+		if (!uart_circ_empty(xmit) && !sport->dma_tx_in_progress)
+			lpuart_prepare_tx(sport);
+	} else {
+		if (readb(port->membase + UARTSR1) & UARTSR1_TDRE)
+			lpuart_trans	mit_buffer(sport);
+	}

and the compiler will silently drop the lpuart_prepare_tx() function and everything
it calls that isn't used elsewhere.


> +
> +#ifdef CONFIG_SERIAL_FSL_LPUART_DMA
> +	struct platform_device *pdev = to_platform_device(port->dev);
> +	struct device_node *np = pdev->dev.of_node;
> +
> +	if (of_get_property(np, "dmas", NULL)) {
> +		sport->lpuart_dma_use = true;
> +		lpuart_dma_tx_request(port);
> +		lpuart_dma_rx_request(port);
> +		temp = readb(port->membase + UARTCR5);
> +		writeb(temp | UARTCR5_TDMAS, port->membase + UARTCR5);
> +	} else
> +		sport->lpuart_dma_use = false;	
> +#endif

Same here.

> @@ -854,6 +1291,10 @@ static int __init lpuart_serial_init(void)
>  
>  	pr_info("serial: Freescale lpuart driver\n");
>  
> +#ifdef CONFIG_SERIAL_FSL_LPUART_DMA
> +	pr_info("serial: Freescale lpuart dma support\n");
> +#endif
> +

This message is not helpful in the long run, I'd rather see you drop the
other one as well. Note that this function is called on *all* platforms
when the driver is enabled, not just on those that actually have the uart.

	Arnd

^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: [PATCH v3 2/2] serial: fsl_lpuart: add DMA support
  2014-01-15  5:21   ` Yuan Yao
@ 2014-01-15 11:05     ` Mark Rutland
  -1 siblings, 0 replies; 24+ messages in thread
From: Mark Rutland @ 2014-01-15 11:05 UTC (permalink / raw)
  To: Yuan Yao; +Cc: gregkh, shawn.guo, linux, linux-arm-kernel, arnd, linux-serial

On Wed, Jan 15, 2014 at 05:21:05AM +0000, Yuan Yao wrote:
> Add dma support for lpuart. This function depend on DMA driver.
> You can turn on it by SERIAL_FSL_LPUART_DMA=y. And It works if dts node has dma properties.
> 
> Signed-off-by: Yuan Yao <yao.yuan@freescale.com>
> ---
>  .../devicetree/bindings/serial/fsl-lpuart.txt      |  21 +-
>  drivers/tty/serial/Kconfig                         |   7 +
>  drivers/tty/serial/fsl_lpuart.c                    | 457 ++++++++++++++++++++-
>  3 files changed, 473 insertions(+), 12 deletions(-)
> 
> diff --git a/Documentation/devicetree/bindings/serial/fsl-lpuart.txt b/Documentation/devicetree/bindings/serial/fsl-lpuart.txt
> index 6fd1dd1..7509080 100644
> --- a/Documentation/devicetree/bindings/serial/fsl-lpuart.txt
> +++ b/Documentation/devicetree/bindings/serial/fsl-lpuart.txt
> @@ -4,11 +4,24 @@ Required properties:
>  - compatible : Should be "fsl,<soc>-lpuart"
>  - reg : Address and length of the register set for the device
>  - interrupts : Should contain uart interrupt
> +- clocks : from common clock binding: handle to uart clock
> +- clock-names : from common clock binding: Shall be "ipg"

Why are these now requried if they weren't previously? That breaks old
dts. I can't see any new code touching clocks. Was this an old but
undocumented requirement?

Could you please reword this so clocks is defined in terms of
clock-names so as to make the relationship between them clear:

- clocks: a list of phandles + clock-specifier pairs, one for each entry
  in clock-names

- clock-names: should contain:
   * "ipg" - the uart clock

> +
> +Optional properties:
> +- dma-names: Should contain "lpuart-tx" for transmit and "lpuart-rx" for receive channels
> +- dmas: Should contain dma specifiers for transmit and receive channels

Likewise could you please define dmas in terms of dma-names please. Why
not just "tx" and "rx" as other bindings use?

[...]

> +#ifdef CONFIG_SERIAL_FSL_LPUART_DMA
> +       struct platform_device *pdev = to_platform_device(port->dev);
> +       struct device_node *np = pdev->dev.of_node;
> +
> +       if (of_get_property(np, "dmas", NULL)) {
> +               sport->lpuart_dma_use = true;
> +               lpuart_dma_tx_request(port);
> +               lpuart_dma_rx_request(port);
> +               temp = readb(port->membase + UARTCR5);
> +               writeb(temp | UARTCR5_TDMAS, port->membase + UARTCR5);

Rather than reading the raw dt to find out if you have dmas, can you not
just attempt to request the dmas and if either fail give up on using
them?

> +       } else
> +               sport->lpuart_dma_use = false;

Nit: if you have brackets on one half of an if-else, they should be on
the other half too (even if i's a single line).

Cheers,
Mark.

^ permalink raw reply	[flat|nested] 24+ messages in thread

* [PATCH v3 2/2] serial: fsl_lpuart: add DMA support
@ 2014-01-15 11:05     ` Mark Rutland
  0 siblings, 0 replies; 24+ messages in thread
From: Mark Rutland @ 2014-01-15 11:05 UTC (permalink / raw)
  To: linux-arm-kernel

On Wed, Jan 15, 2014 at 05:21:05AM +0000, Yuan Yao wrote:
> Add dma support for lpuart. This function depend on DMA driver.
> You can turn on it by SERIAL_FSL_LPUART_DMA=y. And It works if dts node has dma properties.
> 
> Signed-off-by: Yuan Yao <yao.yuan@freescale.com>
> ---
>  .../devicetree/bindings/serial/fsl-lpuart.txt      |  21 +-
>  drivers/tty/serial/Kconfig                         |   7 +
>  drivers/tty/serial/fsl_lpuart.c                    | 457 ++++++++++++++++++++-
>  3 files changed, 473 insertions(+), 12 deletions(-)
> 
> diff --git a/Documentation/devicetree/bindings/serial/fsl-lpuart.txt b/Documentation/devicetree/bindings/serial/fsl-lpuart.txt
> index 6fd1dd1..7509080 100644
> --- a/Documentation/devicetree/bindings/serial/fsl-lpuart.txt
> +++ b/Documentation/devicetree/bindings/serial/fsl-lpuart.txt
> @@ -4,11 +4,24 @@ Required properties:
>  - compatible : Should be "fsl,<soc>-lpuart"
>  - reg : Address and length of the register set for the device
>  - interrupts : Should contain uart interrupt
> +- clocks : from common clock binding: handle to uart clock
> +- clock-names : from common clock binding: Shall be "ipg"

Why are these now requried if they weren't previously? That breaks old
dts. I can't see any new code touching clocks. Was this an old but
undocumented requirement?

Could you please reword this so clocks is defined in terms of
clock-names so as to make the relationship between them clear:

- clocks: a list of phandles + clock-specifier pairs, one for each entry
  in clock-names

- clock-names: should contain:
   * "ipg" - the uart clock

> +
> +Optional properties:
> +- dma-names: Should contain "lpuart-tx" for transmit and "lpuart-rx" for receive channels
> +- dmas: Should contain dma specifiers for transmit and receive channels

Likewise could you please define dmas in terms of dma-names please. Why
not just "tx" and "rx" as other bindings use?

[...]

> +#ifdef CONFIG_SERIAL_FSL_LPUART_DMA
> +       struct platform_device *pdev = to_platform_device(port->dev);
> +       struct device_node *np = pdev->dev.of_node;
> +
> +       if (of_get_property(np, "dmas", NULL)) {
> +               sport->lpuart_dma_use = true;
> +               lpuart_dma_tx_request(port);
> +               lpuart_dma_rx_request(port);
> +               temp = readb(port->membase + UARTCR5);
> +               writeb(temp | UARTCR5_TDMAS, port->membase + UARTCR5);

Rather than reading the raw dt to find out if you have dmas, can you not
just attempt to request the dmas and if either fail give up on using
them?

> +       } else
> +               sport->lpuart_dma_use = false;

Nit: if you have brackets on one half of an if-else, they should be on
the other half too (even if i's a single line).

Cheers,
Mark.

^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: [PATCH v3 2/2] serial: fsl_lpuart: add DMA support
  2014-01-15 11:05     ` Mark Rutland
@ 2014-01-15 11:17       ` Arnd Bergmann
  -1 siblings, 0 replies; 24+ messages in thread
From: Arnd Bergmann @ 2014-01-15 11:17 UTC (permalink / raw)
  To: linux-arm-kernel
  Cc: Mark Rutland, linux, gregkh, Yuan Yao, linux-serial, shawn.guo

On Wednesday 15 January 2014 11:05:37 Mark Rutland wrote:
> On Wed, Jan 15, 2014 at 05:21:05AM +0000, Yuan Yao wrote:
> > --- a/Documentation/devicetree/bindings/serial/fsl-lpuart.txt
> > +++ b/Documentation/devicetree/bindings/serial/fsl-lpuart.txt
> > @@ -4,11 +4,24 @@ Required properties:
> >  - compatible : Should be "fsl,<soc>-lpuart"
> >  - reg : Address and length of the register set for the device
> >  - interrupts : Should contain uart interrupt
> > +- clocks : from common clock binding: handle to uart clock
> > +- clock-names : from common clock binding: Shall be "ipg"
> 
> Why are these now requried if they weren't previously? That breaks old
> dts. I can't see any new code touching clocks. Was this an old but
> undocumented requirement?

It was previously required but not documented. I asked Yuan Yao to
add the text to the binding along with the new "dmas" addition.

It would have been cleaner to do this as a separate patch, but at
the very least it should be mentioned in the changelog.

	Arnd

^ permalink raw reply	[flat|nested] 24+ messages in thread

* [PATCH v3 2/2] serial: fsl_lpuart: add DMA support
@ 2014-01-15 11:17       ` Arnd Bergmann
  0 siblings, 0 replies; 24+ messages in thread
From: Arnd Bergmann @ 2014-01-15 11:17 UTC (permalink / raw)
  To: linux-arm-kernel

On Wednesday 15 January 2014 11:05:37 Mark Rutland wrote:
> On Wed, Jan 15, 2014 at 05:21:05AM +0000, Yuan Yao wrote:
> > --- a/Documentation/devicetree/bindings/serial/fsl-lpuart.txt
> > +++ b/Documentation/devicetree/bindings/serial/fsl-lpuart.txt
> > @@ -4,11 +4,24 @@ Required properties:
> >  - compatible : Should be "fsl,<soc>-lpuart"
> >  - reg : Address and length of the register set for the device
> >  - interrupts : Should contain uart interrupt
> > +- clocks : from common clock binding: handle to uart clock
> > +- clock-names : from common clock binding: Shall be "ipg"
> 
> Why are these now requried if they weren't previously? That breaks old
> dts. I can't see any new code touching clocks. Was this an old but
> undocumented requirement?

It was previously required but not documented. I asked Yuan Yao to
add the text to the binding along with the new "dmas" addition.

It would have been cleaner to do this as a separate patch, but at
the very least it should be mentioned in the changelog.

	Arnd

^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: [PATCH v3 2/2] serial: fsl_lpuart: add DMA support
  2014-01-15 11:17       ` Arnd Bergmann
@ 2014-01-15 13:50         ` Mark Rutland
  -1 siblings, 0 replies; 24+ messages in thread
From: Mark Rutland @ 2014-01-15 13:50 UTC (permalink / raw)
  To: Arnd Bergmann
  Cc: linux-arm-kernel, Yuan Yao, linux, gregkh, linux-serial, shawn.guo

On Wed, Jan 15, 2014 at 11:17:07AM +0000, Arnd Bergmann wrote:
> On Wednesday 15 January 2014 11:05:37 Mark Rutland wrote:
> > On Wed, Jan 15, 2014 at 05:21:05AM +0000, Yuan Yao wrote:
> > > --- a/Documentation/devicetree/bindings/serial/fsl-lpuart.txt
> > > +++ b/Documentation/devicetree/bindings/serial/fsl-lpuart.txt
> > > @@ -4,11 +4,24 @@ Required properties:
> > >  - compatible : Should be "fsl,<soc>-lpuart"
> > >  - reg : Address and length of the register set for the device
> > >  - interrupts : Should contain uart interrupt
> > > +- clocks : from common clock binding: handle to uart clock
> > > +- clock-names : from common clock binding: Shall be "ipg"
> > 
> > Why are these now requried if they weren't previously? That breaks old
> > dts. I can't see any new code touching clocks. Was this an old but
> > undocumented requirement?
> 
> It was previously required but not documented. I asked Yuan Yao to
> add the text to the binding along with the new "dmas" addition.

Ok.

> 
> It would have been cleaner to do this as a separate patch, but at
> the very least it should be mentioned in the changelog.

Having it in the changelog would be nice.

Cheers,
Mark.

^ permalink raw reply	[flat|nested] 24+ messages in thread

* [PATCH v3 2/2] serial: fsl_lpuart: add DMA support
@ 2014-01-15 13:50         ` Mark Rutland
  0 siblings, 0 replies; 24+ messages in thread
From: Mark Rutland @ 2014-01-15 13:50 UTC (permalink / raw)
  To: linux-arm-kernel

On Wed, Jan 15, 2014 at 11:17:07AM +0000, Arnd Bergmann wrote:
> On Wednesday 15 January 2014 11:05:37 Mark Rutland wrote:
> > On Wed, Jan 15, 2014 at 05:21:05AM +0000, Yuan Yao wrote:
> > > --- a/Documentation/devicetree/bindings/serial/fsl-lpuart.txt
> > > +++ b/Documentation/devicetree/bindings/serial/fsl-lpuart.txt
> > > @@ -4,11 +4,24 @@ Required properties:
> > >  - compatible : Should be "fsl,<soc>-lpuart"
> > >  - reg : Address and length of the register set for the device
> > >  - interrupts : Should contain uart interrupt
> > > +- clocks : from common clock binding: handle to uart clock
> > > +- clock-names : from common clock binding: Shall be "ipg"
> > 
> > Why are these now requried if they weren't previously? That breaks old
> > dts. I can't see any new code touching clocks. Was this an old but
> > undocumented requirement?
> 
> It was previously required but not documented. I asked Yuan Yao to
> add the text to the binding along with the new "dmas" addition.

Ok.

> 
> It would have been cleaner to do this as a separate patch, but at
> the very least it should be mentioned in the changelog.

Having it in the changelog would be nice.

Cheers,
Mark.

^ permalink raw reply	[flat|nested] 24+ messages in thread

* RE: [PATCH v3 2/2] serial: fsl_lpuart: add DMA support
  2014-01-15  9:25     ` Arnd Bergmann
@ 2014-01-16  6:02       ` Yao Yuan
  -1 siblings, 0 replies; 24+ messages in thread
From: Yao Yuan @ 2014-01-16  6:02 UTC (permalink / raw)
  To: Arnd Bergmann; +Cc: gregkh, shawn.guo, linux, linux-arm-kernel, linux-serial

Hi, Arnd
Thanks for your suggestion.

> -----Original Message-----
> From: linux-serial-owner@vger.kernel.org [mailto:linux-serial-
> owner@vger.kernel.org] On Behalf Of Arnd Bergmann
> Sent: Wednesday, January 15, 2014 5:26 PM
> To: Yuan Yao-B46683
> Cc: gregkh@linuxfoundation.org; shawn.guo@linaro.org;
> linux@arm.linux.org.uk; linux-arm-kernel@lists.infradead.org; linux-
> serial@vger.kernel.org
> Subject: Re: [PATCH v3 2/2] serial: fsl_lpuart: add DMA support
> 
> On Wednesday 15 January 2014, Yuan Yao wrote:
> > Add dma support for lpuart. This function depend on DMA driver.
> > You can turn on it by SERIAL_FSL_LPUART_DMA=y. And It works if dts node
> has dma properties.
> >
> > Signed-off-by: Yuan Yao <yao.yuan@freescale.com>
> 
> Most of the changes you did look good, but I have to say I'm not a fan of
> the #ifdef you added. I don't think you actually need to have the
> compile-time selection, and it would be best to just drop that part.
> 
> If you have a good reason to keep it, please change the code to more
> readable "if (IS_ENABLED(CONFIG_FSL_LPUART_DMA))" constructs.
> If you decide to do this, you will find that you need far fewer such
> conditionals because a lot of the code will automatically get dropped by
> the compiler.
> 

Thank you! I even not know the IS_ENABLED macro.
At the beginning I just think this can save the size of code. 
But now I think it's not very necessary.
I tend to cancel it.

> >  uart0: serial@40027000 {
> > -	       compatible = "fsl,vf610-lpuart";
> > -	       reg = <0x40027000 0x1000>;
> > -	       interrupts = <0 61 0x00>;
> > -       };
> > +		compatible = "fsl,vf610-lpuart";
> > +		reg = <0x40027000 0x1000>;
> > +		interrupts = <0 61 0x00>;
> > +		clocks = <&clks VF610_CLK_UART0>;
> > +		clock-names = "ipg";
> > +		dma-names = "lpuart-tx","lpuart-rx";
> > +		dmas = <&edma0 0 VF610_EDMA_MUXID0_UART0_TX>,
> > +			<&edma0 0 VF610_EDMA_MUXID0_UART0_RX>;
> > +	};
> 
> You still haven't addressed my comment about removing the
> VF610_EDMA_MUXID0_UART0_TX macros.
>

Very sorry for forget it. At that time the VF610_EDMA_MUXID0_UART0_TX is because of eDMA driver request.
But now it also cancel the macro. So this is my fault. I will fix it next.

> >
> > +#ifdef CONFIG_SERIAL_FSL_LPUART_DMA
> > +#define DMA_MAXBURST		16
> > +#define DMA_MAXBURST_MASK	(DMA_MAXBURST - 1)
> > +#define FSL_UART_RX_DMA_BUFFER_SIZE	64
> > +#endif
> 
> For this part there was no need for the #ifdef to start with.
> 
> >  #define DRIVER_NAME	"fsl-lpuart"
> >  #define DEV_NAME	"ttyLP"
> >  #define UART_NR		6
> > @@ -121,6 +132,26 @@ struct lpuart_port {
> >  	struct clk		*clk;
> >  	unsigned int		txfifo_size;
> >  	unsigned int		rxfifo_size;
> > +
> > +#ifdef CONFIG_SERIAL_FSL_LPUART_DMA
> > +	bool			lpuart_dma_use;
> > +	struct dma_chan		*dma_tx_chan;
> > +	struct dma_chan		*dma_rx_chan;
> > +	struct dma_async_tx_descriptor  *dma_tx_desc;
> > +	struct dma_async_tx_descriptor  *dma_rx_desc;
> > +	dma_addr_t		dma_tx_buf_bus;
> > +	dma_addr_t		dma_rx_buf_bus;
> > +	dma_cookie_t		dma_tx_cookie;
> > +	dma_cookie_t		dma_rx_cookie;
> > +	unsigned char		*dma_tx_buf_virt;
> > +	unsigned char		*dma_rx_buf_virt;
> > +	unsigned int		dma_tx_bytes;
> > +	unsigned int		dma_rx_bytes;
> > +	int			dma_tx_in_progress;
> > +	int			dma_rx_in_progress;
> > +	unsigned int		dma_rx_timeout;
> > +	struct timer_list	lpuart_timer;
> > +#endif
> >  };
> 
> This part will result in a slight increase in data size even if dma
> support is turned off at compile time.
> 

Yes, I will fix it.

> >
> >  	temp = readb(port->membase + UARTCR2);
> >  	writeb(temp | UARTCR2_TIE, port->membase + UARTCR2);
> >
> > +#ifdef CONFIG_SERIAL_FSL_LPUART_DMA
> > +	if (sport->lpuart_dma_use) {
> > +		if (!uart_circ_empty(xmit) && !sport->dma_tx_in_progress)
> > +			lpuart_prepare_tx(sport);
> > +	} else {
> > +		if (readb(port->membase + UARTSR1) & UARTSR1_TDRE)
> > +			lpuart_transmit_buffer(sport);
> > +	}
> > +#else
> >  	if (readb(port->membase + UARTSR1) & UARTSR1_TDRE)
> >  		lpuart_transmit_buffer(sport);
> > +#endif
> >  }
> 
> So this can simply become
> 
> +	if (IS_ENABLED(CONFIG_SERIAL_FSL_LPUART_DMA) && sport-
> >lpuart_dma_use) {
> +		if (!uart_circ_empty(xmit) && !sport->dma_tx_in_progress)
> +			lpuart_prepare_tx(sport);
> +	} else {
> +		if (readb(port->membase + UARTSR1) & UARTSR1_TDRE)
> +			lpuart_trans	mit_buffer(sport);
> +	}
> 
> and the compiler will silently drop the lpuart_prepare_tx() function and
> everything it calls that isn't used elsewhere.
> 

It's wonderful. 

> 
> > +
> > +#ifdef CONFIG_SERIAL_FSL_LPUART_DMA
> > +	struct platform_device *pdev = to_platform_device(port->dev);
> > +	struct device_node *np = pdev->dev.of_node;
> > +
> > +	if (of_get_property(np, "dmas", NULL)) {
> > +		sport->lpuart_dma_use = true;
> > +		lpuart_dma_tx_request(port);
> > +		lpuart_dma_rx_request(port);
> > +		temp = readb(port->membase + UARTCR5);
> > +		writeb(temp | UARTCR5_TDMAS, port->membase + UARTCR5);
> > +	} else
> > +		sport->lpuart_dma_use = false;
> > +#endif
> 
> Same here.
> 
> > @@ -854,6 +1291,10 @@ static int __init lpuart_serial_init(void)
> >
> >  	pr_info("serial: Freescale lpuart driver\n");
> >
> > +#ifdef CONFIG_SERIAL_FSL_LPUART_DMA
> > +	pr_info("serial: Freescale lpuart dma support\n"); #endif
> > +
> 
> This message is not helpful in the long run, I'd rather see you drop the
> other one as well. Note that this function is called on *all* platforms
> when the driver is enabled, not just on those that actually have the uart.
> 
> 	Arnd
> --
> To unsubscribe from this list: send the line "unsubscribe linux-serial"
> in the body of a message to majordomo@vger.kernel.org More majordomo info
> at  http://vger.kernel.org/majordomo-info.html
> 


^ permalink raw reply	[flat|nested] 24+ messages in thread

* [PATCH v3 2/2] serial: fsl_lpuart: add DMA support
@ 2014-01-16  6:02       ` Yao Yuan
  0 siblings, 0 replies; 24+ messages in thread
From: Yao Yuan @ 2014-01-16  6:02 UTC (permalink / raw)
  To: linux-arm-kernel

Hi, Arnd
Thanks for your suggestion.

> -----Original Message-----
> From: linux-serial-owner at vger.kernel.org [mailto:linux-serial-
> owner at vger.kernel.org] On Behalf Of Arnd Bergmann
> Sent: Wednesday, January 15, 2014 5:26 PM
> To: Yuan Yao-B46683
> Cc: gregkh at linuxfoundation.org; shawn.guo at linaro.org;
> linux at arm.linux.org.uk; linux-arm-kernel at lists.infradead.org; linux-
> serial at vger.kernel.org
> Subject: Re: [PATCH v3 2/2] serial: fsl_lpuart: add DMA support
> 
> On Wednesday 15 January 2014, Yuan Yao wrote:
> > Add dma support for lpuart. This function depend on DMA driver.
> > You can turn on it by SERIAL_FSL_LPUART_DMA=y. And It works if dts node
> has dma properties.
> >
> > Signed-off-by: Yuan Yao <yao.yuan@freescale.com>
> 
> Most of the changes you did look good, but I have to say I'm not a fan of
> the #ifdef you added. I don't think you actually need to have the
> compile-time selection, and it would be best to just drop that part.
> 
> If you have a good reason to keep it, please change the code to more
> readable "if (IS_ENABLED(CONFIG_FSL_LPUART_DMA))" constructs.
> If you decide to do this, you will find that you need far fewer such
> conditionals because a lot of the code will automatically get dropped by
> the compiler.
> 

Thank you! I even not know the IS_ENABLED macro.
At the beginning I just think this can save the size of code. 
But now I think it's not very necessary.
I tend to cancel it.

> >  uart0: serial at 40027000 {
> > -	       compatible = "fsl,vf610-lpuart";
> > -	       reg = <0x40027000 0x1000>;
> > -	       interrupts = <0 61 0x00>;
> > -       };
> > +		compatible = "fsl,vf610-lpuart";
> > +		reg = <0x40027000 0x1000>;
> > +		interrupts = <0 61 0x00>;
> > +		clocks = <&clks VF610_CLK_UART0>;
> > +		clock-names = "ipg";
> > +		dma-names = "lpuart-tx","lpuart-rx";
> > +		dmas = <&edma0 0 VF610_EDMA_MUXID0_UART0_TX>,
> > +			<&edma0 0 VF610_EDMA_MUXID0_UART0_RX>;
> > +	};
> 
> You still haven't addressed my comment about removing the
> VF610_EDMA_MUXID0_UART0_TX macros.
>

Very sorry for forget it. At that time the VF610_EDMA_MUXID0_UART0_TX is because of eDMA driver request.
But now it also cancel the macro. So this is my fault. I will fix it next.

> >
> > +#ifdef CONFIG_SERIAL_FSL_LPUART_DMA
> > +#define DMA_MAXBURST		16
> > +#define DMA_MAXBURST_MASK	(DMA_MAXBURST - 1)
> > +#define FSL_UART_RX_DMA_BUFFER_SIZE	64
> > +#endif
> 
> For this part there was no need for the #ifdef to start with.
> 
> >  #define DRIVER_NAME	"fsl-lpuart"
> >  #define DEV_NAME	"ttyLP"
> >  #define UART_NR		6
> > @@ -121,6 +132,26 @@ struct lpuart_port {
> >  	struct clk		*clk;
> >  	unsigned int		txfifo_size;
> >  	unsigned int		rxfifo_size;
> > +
> > +#ifdef CONFIG_SERIAL_FSL_LPUART_DMA
> > +	bool			lpuart_dma_use;
> > +	struct dma_chan		*dma_tx_chan;
> > +	struct dma_chan		*dma_rx_chan;
> > +	struct dma_async_tx_descriptor  *dma_tx_desc;
> > +	struct dma_async_tx_descriptor  *dma_rx_desc;
> > +	dma_addr_t		dma_tx_buf_bus;
> > +	dma_addr_t		dma_rx_buf_bus;
> > +	dma_cookie_t		dma_tx_cookie;
> > +	dma_cookie_t		dma_rx_cookie;
> > +	unsigned char		*dma_tx_buf_virt;
> > +	unsigned char		*dma_rx_buf_virt;
> > +	unsigned int		dma_tx_bytes;
> > +	unsigned int		dma_rx_bytes;
> > +	int			dma_tx_in_progress;
> > +	int			dma_rx_in_progress;
> > +	unsigned int		dma_rx_timeout;
> > +	struct timer_list	lpuart_timer;
> > +#endif
> >  };
> 
> This part will result in a slight increase in data size even if dma
> support is turned off at compile time.
> 

Yes, I will fix it.

> >
> >  	temp = readb(port->membase + UARTCR2);
> >  	writeb(temp | UARTCR2_TIE, port->membase + UARTCR2);
> >
> > +#ifdef CONFIG_SERIAL_FSL_LPUART_DMA
> > +	if (sport->lpuart_dma_use) {
> > +		if (!uart_circ_empty(xmit) && !sport->dma_tx_in_progress)
> > +			lpuart_prepare_tx(sport);
> > +	} else {
> > +		if (readb(port->membase + UARTSR1) & UARTSR1_TDRE)
> > +			lpuart_transmit_buffer(sport);
> > +	}
> > +#else
> >  	if (readb(port->membase + UARTSR1) & UARTSR1_TDRE)
> >  		lpuart_transmit_buffer(sport);
> > +#endif
> >  }
> 
> So this can simply become
> 
> +	if (IS_ENABLED(CONFIG_SERIAL_FSL_LPUART_DMA) && sport-
> >lpuart_dma_use) {
> +		if (!uart_circ_empty(xmit) && !sport->dma_tx_in_progress)
> +			lpuart_prepare_tx(sport);
> +	} else {
> +		if (readb(port->membase + UARTSR1) & UARTSR1_TDRE)
> +			lpuart_trans	mit_buffer(sport);
> +	}
> 
> and the compiler will silently drop the lpuart_prepare_tx() function and
> everything it calls that isn't used elsewhere.
> 

It's wonderful. 

> 
> > +
> > +#ifdef CONFIG_SERIAL_FSL_LPUART_DMA
> > +	struct platform_device *pdev = to_platform_device(port->dev);
> > +	struct device_node *np = pdev->dev.of_node;
> > +
> > +	if (of_get_property(np, "dmas", NULL)) {
> > +		sport->lpuart_dma_use = true;
> > +		lpuart_dma_tx_request(port);
> > +		lpuart_dma_rx_request(port);
> > +		temp = readb(port->membase + UARTCR5);
> > +		writeb(temp | UARTCR5_TDMAS, port->membase + UARTCR5);
> > +	} else
> > +		sport->lpuart_dma_use = false;
> > +#endif
> 
> Same here.
> 
> > @@ -854,6 +1291,10 @@ static int __init lpuart_serial_init(void)
> >
> >  	pr_info("serial: Freescale lpuart driver\n");
> >
> > +#ifdef CONFIG_SERIAL_FSL_LPUART_DMA
> > +	pr_info("serial: Freescale lpuart dma support\n"); #endif
> > +
> 
> This message is not helpful in the long run, I'd rather see you drop the
> other one as well. Note that this function is called on *all* platforms
> when the driver is enabled, not just on those that actually have the uart.
> 
> 	Arnd
> --
> To unsubscribe from this list: send the line "unsubscribe linux-serial"
> in the body of a message to majordomo at vger.kernel.org More majordomo info
> at  http://vger.kernel.org/majordomo-info.html
> 

^ permalink raw reply	[flat|nested] 24+ messages in thread

* RE: [PATCH v3 2/2] serial: fsl_lpuart: add DMA support
  2014-01-15 11:05     ` Mark Rutland
@ 2014-01-16  6:54       ` Yao Yuan
  -1 siblings, 0 replies; 24+ messages in thread
From: Yao Yuan @ 2014-01-16  6:54 UTC (permalink / raw)
  To: Mark Rutland
  Cc: gregkh, shawn.guo, linux, linux-arm-kernel, arnd, linux-serial

Hi, mark

> -----Original Message-----
> > +#ifdef CONFIG_SERIAL_FSL_LPUART_DMA
> > +       struct platform_device *pdev = to_platform_device(port->dev);
> > +       struct device_node *np = pdev->dev.of_node;
> > +
> > +       if (of_get_property(np, "dmas", NULL)) {
> > +               sport->lpuart_dma_use = true;
> > +               lpuart_dma_tx_request(port);
> > +               lpuart_dma_rx_request(port);
> > +               temp = readb(port->membase + UARTCR5);
> > +               writeb(temp | UARTCR5_TDMAS, port->membase + UARTCR5);
> 
> Rather than reading the raw dt to find out if you have dmas, can you not
> just attempt to request the dmas and if either fail give up on using them?

Yes, the dma request function can also confirm it. But maybe it's better that add the judge
as a dma entrance first?
I think if the dmas is be written it means dma want be support. At this time, rather than 
silently change to no dma model, we may throw the error when some errors happened.
But dma request failed may have many other reasons. Also the judge will just run only once, 
it will not waste of performance.
If the dmas is not be written, we don't need to do anything about dma.

^ permalink raw reply	[flat|nested] 24+ messages in thread

* [PATCH v3 2/2] serial: fsl_lpuart: add DMA support
@ 2014-01-16  6:54       ` Yao Yuan
  0 siblings, 0 replies; 24+ messages in thread
From: Yao Yuan @ 2014-01-16  6:54 UTC (permalink / raw)
  To: linux-arm-kernel

Hi, mark

> -----Original Message-----
> > +#ifdef CONFIG_SERIAL_FSL_LPUART_DMA
> > +       struct platform_device *pdev = to_platform_device(port->dev);
> > +       struct device_node *np = pdev->dev.of_node;
> > +
> > +       if (of_get_property(np, "dmas", NULL)) {
> > +               sport->lpuart_dma_use = true;
> > +               lpuart_dma_tx_request(port);
> > +               lpuart_dma_rx_request(port);
> > +               temp = readb(port->membase + UARTCR5);
> > +               writeb(temp | UARTCR5_TDMAS, port->membase + UARTCR5);
> 
> Rather than reading the raw dt to find out if you have dmas, can you not
> just attempt to request the dmas and if either fail give up on using them?

Yes, the dma request function can also confirm it. But maybe it's better that add the judge
as a dma entrance first?
I think if the dmas is be written it means dma want be support. At this time, rather than 
silently change to no dma model, we may throw the error when some errors happened.
But dma request failed may have many other reasons. Also the judge will just run only once, 
it will not waste of performance.
If the dmas is not be written, we don't need to do anything about dma.

^ permalink raw reply	[flat|nested] 24+ messages in thread

* RE: [PATCH v3 2/2] serial: fsl_lpuart: add DMA support
  2014-01-15  9:25     ` Arnd Bergmann
@ 2014-01-16  8:19       ` Yao Yuan
  -1 siblings, 0 replies; 24+ messages in thread
From: Yao Yuan @ 2014-01-16  8:19 UTC (permalink / raw)
  To: Arnd Bergmann; +Cc: gregkh, shawn.guo, linux, linux-arm-kernel, linux-serial

Hi, Arnd

> -----Original Message-----
> >  #define DRIVER_NAME	"fsl-lpuart"
> >  #define DEV_NAME	"ttyLP"
> >  #define UART_NR		6
> > @@ -121,6 +132,26 @@ struct lpuart_port {
> >  	struct clk		*clk;
> >  	unsigned int		txfifo_size;
> >  	unsigned int		rxfifo_size;
> > +
> > +#ifdef CONFIG_SERIAL_FSL_LPUART_DMA
> > +	bool			lpuart_dma_use;
> > +	struct dma_chan		*dma_tx_chan;
> > +	struct dma_chan		*dma_rx_chan;
> > +	struct dma_async_tx_descriptor  *dma_tx_desc;
> > +	struct dma_async_tx_descriptor  *dma_rx_desc;
> > +	dma_addr_t		dma_tx_buf_bus;
> > +	dma_addr_t		dma_rx_buf_bus;
> > +	dma_cookie_t		dma_tx_cookie;
> > +	dma_cookie_t		dma_rx_cookie;
> > +	unsigned char		*dma_tx_buf_virt;
> > +	unsigned char		*dma_rx_buf_virt;
> > +	unsigned int		dma_tx_bytes;
> > +	unsigned int		dma_rx_bytes;
> > +	int			dma_tx_in_progress;
> > +	int			dma_rx_in_progress;
> > +	unsigned int		dma_rx_timeout;
> > +	struct timer_list	lpuart_timer;
> > +#endif
> >  };
> 
> This part will result in a slight increase in data size even if dma
> support is turned off at compile time.
> 

Yes, someone also want me define a independent struct for dma and allocate the memory when dma be use.
But I think this way will add a more memory access time, like a->b->c and a->c. And the variable 
about dma is just a few bytes but used frequently. So I think I'd better define the variable about dma in lpuart_port struct.


^ permalink raw reply	[flat|nested] 24+ messages in thread

* [PATCH v3 2/2] serial: fsl_lpuart: add DMA support
@ 2014-01-16  8:19       ` Yao Yuan
  0 siblings, 0 replies; 24+ messages in thread
From: Yao Yuan @ 2014-01-16  8:19 UTC (permalink / raw)
  To: linux-arm-kernel

Hi, Arnd

> -----Original Message-----
> >  #define DRIVER_NAME	"fsl-lpuart"
> >  #define DEV_NAME	"ttyLP"
> >  #define UART_NR		6
> > @@ -121,6 +132,26 @@ struct lpuart_port {
> >  	struct clk		*clk;
> >  	unsigned int		txfifo_size;
> >  	unsigned int		rxfifo_size;
> > +
> > +#ifdef CONFIG_SERIAL_FSL_LPUART_DMA
> > +	bool			lpuart_dma_use;
> > +	struct dma_chan		*dma_tx_chan;
> > +	struct dma_chan		*dma_rx_chan;
> > +	struct dma_async_tx_descriptor  *dma_tx_desc;
> > +	struct dma_async_tx_descriptor  *dma_rx_desc;
> > +	dma_addr_t		dma_tx_buf_bus;
> > +	dma_addr_t		dma_rx_buf_bus;
> > +	dma_cookie_t		dma_tx_cookie;
> > +	dma_cookie_t		dma_rx_cookie;
> > +	unsigned char		*dma_tx_buf_virt;
> > +	unsigned char		*dma_rx_buf_virt;
> > +	unsigned int		dma_tx_bytes;
> > +	unsigned int		dma_rx_bytes;
> > +	int			dma_tx_in_progress;
> > +	int			dma_rx_in_progress;
> > +	unsigned int		dma_rx_timeout;
> > +	struct timer_list	lpuart_timer;
> > +#endif
> >  };
> 
> This part will result in a slight increase in data size even if dma
> support is turned off at compile time.
> 

Yes, someone also want me define a independent struct for dma and allocate the memory when dma be use.
But I think this way will add a more memory access time, like a->b->c and a->c. And the variable 
about dma is just a few bytes but used frequently. So I think I'd better define the variable about dma in lpuart_port struct.

^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: [PATCH v3 2/2] serial: fsl_lpuart: add DMA support
  2014-01-16  8:19       ` Yao Yuan
@ 2014-01-16 10:47         ` Arnd Bergmann
  -1 siblings, 0 replies; 24+ messages in thread
From: Arnd Bergmann @ 2014-01-16 10:47 UTC (permalink / raw)
  To: Yao Yuan; +Cc: gregkh, shawn.guo, linux, linux-arm-kernel, linux-serial

On Thursday 16 January 2014, Yao Yuan wrote:
> > This part will result in a slight increase in data size even if dma
> > support is turned off at compile time.
> > 
> 
> Yes, someone also want me define a independent struct for dma and allocate the memory when dma be use.
> But I think this way will add a more memory access time, like a->b->c and a->c. And the variable 
> about dma is just a few bytes but used frequently. So I think I'd better define the variable about dma in lpuart_port struct.

I agree. Just leave it unconditionally in here to make the code simpler.

	Arnd

^ permalink raw reply	[flat|nested] 24+ messages in thread

* [PATCH v3 2/2] serial: fsl_lpuart: add DMA support
@ 2014-01-16 10:47         ` Arnd Bergmann
  0 siblings, 0 replies; 24+ messages in thread
From: Arnd Bergmann @ 2014-01-16 10:47 UTC (permalink / raw)
  To: linux-arm-kernel

On Thursday 16 January 2014, Yao Yuan wrote:
> > This part will result in a slight increase in data size even if dma
> > support is turned off at compile time.
> > 
> 
> Yes, someone also want me define a independent struct for dma and allocate the memory when dma be use.
> But I think this way will add a more memory access time, like a->b->c and a->c. And the variable 
> about dma is just a few bytes but used frequently. So I think I'd better define the variable about dma in lpuart_port struct.

I agree. Just leave it unconditionally in here to make the code simpler.

	Arnd

^ permalink raw reply	[flat|nested] 24+ messages in thread

* RE: [PATCH v3 2/2] serial: fsl_lpuart: add DMA support
  2014-01-16  6:54       ` Yao Yuan
@ 2014-01-22  3:41         ` Yao Yuan
  -1 siblings, 0 replies; 24+ messages in thread
From: Yao Yuan @ 2014-01-22  3:41 UTC (permalink / raw)
  To: Mark Rutland
  Cc: gregkh, shawn.guo, linux, linux-arm-kernel, arnd, linux-serial

Hi, Mark

Think twice.
Yes, you are right. "of_get_property" is not necessary here.

Thanks for your suggestion.

> -----Original Message-----
> From: linux-serial-owner@vger.kernel.org [mailto:linux-serial-
> owner@vger.kernel.org] On Behalf Of Yao Yuan
> Sent: Thursday, January 16, 2014 2:54 PM
> To: Mark Rutland
> Cc: gregkh@linuxfoundation.org; shawn.guo@linaro.org;
> linux@arm.linux.org.uk; linux-arm-kernel@lists.infradead.org;
> arnd@arndb.de; linux-serial@vger.kernel.org
> Subject: RE: [PATCH v3 2/2] serial: fsl_lpuart: add DMA support
> 
> Hi, mark
> 
> > -----Original Message-----
> > > +#ifdef CONFIG_SERIAL_FSL_LPUART_DMA
> > > +       struct platform_device *pdev = to_platform_device(port->dev);
> > > +       struct device_node *np = pdev->dev.of_node;
> > > +
> > > +       if (of_get_property(np, "dmas", NULL)) {
> > > +               sport->lpuart_dma_use = true;
> > > +               lpuart_dma_tx_request(port);
> > > +               lpuart_dma_rx_request(port);
> > > +               temp = readb(port->membase + UARTCR5);
> > > +               writeb(temp | UARTCR5_TDMAS, port->membase +
> > > + UARTCR5);
> >
> > Rather than reading the raw dt to find out if you have dmas, can you
> > not just attempt to request the dmas and if either fail give up on
> using them?
> 
> Yes, the dma request function can also confirm it. But maybe it's better
> that add the judge as a dma entrance first?
> I think if the dmas is be written it means dma want be support. At this
> time, rather than silently change to no dma model, we may throw the error
> when some errors happened.
> But dma request failed may have many other reasons. Also the judge will
> just run only once, it will not waste of performance.
> If the dmas is not be written, we don't need to do anything about dma.
> \x13  칻\x1c & ~ & \x18  +-  ݶ\x17  w  ˛   m b  lz   )   w*jg   \x1e     ݢj/   z ޖ  2 ޙ
> & )ߡ a  \x7f  \x1e G   h \x0f j:+v   w ٥

^ permalink raw reply	[flat|nested] 24+ messages in thread

* [PATCH v3 2/2] serial: fsl_lpuart: add DMA support
@ 2014-01-22  3:41         ` Yao Yuan
  0 siblings, 0 replies; 24+ messages in thread
From: Yao Yuan @ 2014-01-22  3:41 UTC (permalink / raw)
  To: linux-arm-kernel

Hi, Mark

Think twice.
Yes, you are right. "of_get_property" is not necessary here.

Thanks for your suggestion.

> -----Original Message-----
> From: linux-serial-owner at vger.kernel.org [mailto:linux-serial-
> owner at vger.kernel.org] On Behalf Of Yao Yuan
> Sent: Thursday, January 16, 2014 2:54 PM
> To: Mark Rutland
> Cc: gregkh at linuxfoundation.org; shawn.guo at linaro.org;
> linux at arm.linux.org.uk; linux-arm-kernel at lists.infradead.org;
> arnd at arndb.de; linux-serial at vger.kernel.org
> Subject: RE: [PATCH v3 2/2] serial: fsl_lpuart: add DMA support
> 
> Hi, mark
> 
> > -----Original Message-----
> > > +#ifdef CONFIG_SERIAL_FSL_LPUART_DMA
> > > +       struct platform_device *pdev = to_platform_device(port->dev);
> > > +       struct device_node *np = pdev->dev.of_node;
> > > +
> > > +       if (of_get_property(np, "dmas", NULL)) {
> > > +               sport->lpuart_dma_use = true;
> > > +               lpuart_dma_tx_request(port);
> > > +               lpuart_dma_rx_request(port);
> > > +               temp = readb(port->membase + UARTCR5);
> > > +               writeb(temp | UARTCR5_TDMAS, port->membase +
> > > + UARTCR5);
> >
> > Rather than reading the raw dt to find out if you have dmas, can you
> > not just attempt to request the dmas and if either fail give up on
> using them?
> 
> Yes, the dma request function can also confirm it. But maybe it's better
> that add the judge as a dma entrance first?
> I think if the dmas is be written it means dma want be support. At this
> time, rather than silently change to no dma model, we may throw the error
> when some errors happened.
> But dma request failed may have many other reasons. Also the judge will
> just run only once, it will not waste of performance.
> If the dmas is not be written, we don't need to do anything about dma.
> \x13  ?
 & ~ & \x18  +-  ?\x17  w  ?   m b  lz   )   w*jg   
     ?j/   z ?  2 ?
> & )? a  \x7f  
 G   h \x0f j:+v   w ?

^ permalink raw reply	[flat|nested] 24+ messages in thread

* [PATCH v3 2/2] serial: fsl_lpuart: add DMA support
  2014-01-15  5:24 [PATCH v3 0/2] " Yuan Yao
@ 2014-01-15  5:24   ` Yuan Yao
  0 siblings, 0 replies; 24+ messages in thread
From: Yuan Yao @ 2014-01-15  5:24 UTC (permalink / raw)
  To: gregkh, shawn.guo; +Cc: linux, arnd, linux-arm-kernel, linux-serial

Add dma support for lpuart. This function depend on DMA driver. 
You can turn on it by SERIAL_FSL_LPUART_DMA=y. And It works if dts node has dma properties.

Signed-off-by: Yuan Yao <yao.yuan@freescale.com>
---
 .../devicetree/bindings/serial/fsl-lpuart.txt      |  21 +-
 drivers/tty/serial/Kconfig                         |   7 +
 drivers/tty/serial/fsl_lpuart.c                    | 457 ++++++++++++++++++++-
 3 files changed, 473 insertions(+), 12 deletions(-)

diff --git a/Documentation/devicetree/bindings/serial/fsl-lpuart.txt b/Documentation/devicetree/bindings/serial/fsl-lpuart.txt
index 6fd1dd1..7509080 100644
--- a/Documentation/devicetree/bindings/serial/fsl-lpuart.txt
+++ b/Documentation/devicetree/bindings/serial/fsl-lpuart.txt
@@ -4,11 +4,24 @@ Required properties:
 - compatible : Should be "fsl,<soc>-lpuart"
 - reg : Address and length of the register set for the device
 - interrupts : Should contain uart interrupt
+- clocks : from common clock binding: handle to uart clock
+- clock-names : from common clock binding: Shall be "ipg"
+
+Optional properties:
+- dma-names: Should contain "lpuart-tx" for transmit and "lpuart-rx" for receive channels
+- dmas: Should contain dma specifiers for transmit and receive channels
+
+Note: Optional properties for DMA support, If need this properties is must.
 
 Example:
 
 uart0: serial@40027000 {
-	       compatible = "fsl,vf610-lpuart";
-	       reg = <0x40027000 0x1000>;
-	       interrupts = <0 61 0x00>;
-       };
+		compatible = "fsl,vf610-lpuart";
+		reg = <0x40027000 0x1000>;
+		interrupts = <0 61 0x00>;
+		clocks = <&clks VF610_CLK_UART0>;
+		clock-names = "ipg";
+		dma-names = "lpuart-tx","lpuart-rx";
+		dmas = <&edma0 0 VF610_EDMA_MUXID0_UART0_TX>,
+			<&edma0 0 VF610_EDMA_MUXID0_UART0_RX>;
+	};
diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig
index a3817ab..ab3910b 100644
--- a/drivers/tty/serial/Kconfig
+++ b/drivers/tty/serial/Kconfig
@@ -1509,6 +1509,13 @@ config SERIAL_FSL_LPUART_CONSOLE
 	  If you have enabled the lpuart serial port on the Freescale SoCs,
 	  you can make it the console by answering Y to this option.
 
+config SERIAL_FSL_LPUART_DMA
+	bool "Freescale lpuart serial port dma support"
+	depends on FSL_EDMA=y && SERIAL_FSL_LPUART=y
+	help
+	  DMA support for the on-chip lpuart on some Freescale SOCs,
+	  you can turn on the dma support for lpuart by answering Y to this option.
+
 config SERIAL_ST_ASC
 	tristate "ST ASC serial port support"
 	select SERIAL_CORE
diff --git a/drivers/tty/serial/fsl_lpuart.c b/drivers/tty/serial/fsl_lpuart.c
index 67b8946..5fdcb18 100644
--- a/drivers/tty/serial/fsl_lpuart.c
+++ b/drivers/tty/serial/fsl_lpuart.c
@@ -13,14 +13,19 @@
 #define SUPPORT_SYSRQ
 #endif
 
-#include <linux/module.h>
+#include <linux/clk.h>
+#include <linux/console.h>
+#include <linux/dma-mapping.h>
+#include <linux/dmaengine.h>
+#include <linux/dmapool.h>
 #include <linux/io.h>
 #include <linux/irq.h>
-#include <linux/clk.h>
+#include <linux/module.h>
 #include <linux/of.h>
 #include <linux/of_device.h>
-#include <linux/console.h>
+#include <linux/of_dma.h>
 #include <linux/serial_core.h>
+#include <linux/slab.h>
 #include <linux/tty_flip.h>
 
 /* All registers are 8-bit width */
@@ -112,6 +117,12 @@
 #define UARTSFIFO_TXOF		0x02
 #define UARTSFIFO_RXUF		0x01
 
+#ifdef CONFIG_SERIAL_FSL_LPUART_DMA
+#define DMA_MAXBURST		16
+#define DMA_MAXBURST_MASK	(DMA_MAXBURST - 1)
+#define FSL_UART_RX_DMA_BUFFER_SIZE	64
+#endif
+
 #define DRIVER_NAME	"fsl-lpuart"
 #define DEV_NAME	"ttyLP"
 #define UART_NR		6
@@ -121,6 +132,26 @@ struct lpuart_port {
 	struct clk		*clk;
 	unsigned int		txfifo_size;
 	unsigned int		rxfifo_size;
+
+#ifdef CONFIG_SERIAL_FSL_LPUART_DMA
+	bool			lpuart_dma_use;
+	struct dma_chan		*dma_tx_chan;
+	struct dma_chan		*dma_rx_chan;
+	struct dma_async_tx_descriptor  *dma_tx_desc;
+	struct dma_async_tx_descriptor  *dma_rx_desc;
+	dma_addr_t		dma_tx_buf_bus;
+	dma_addr_t		dma_rx_buf_bus;
+	dma_cookie_t		dma_tx_cookie;
+	dma_cookie_t		dma_rx_cookie;
+	unsigned char		*dma_tx_buf_virt;
+	unsigned char		*dma_rx_buf_virt;
+	unsigned int		dma_tx_bytes;
+	unsigned int		dma_rx_bytes;
+	int			dma_tx_in_progress;
+	int			dma_rx_in_progress;
+	unsigned int		dma_rx_timeout;
+	struct timer_list	lpuart_timer;
+#endif
 };
 
 static struct of_device_id lpuart_dt_ids[] = {
@@ -131,6 +162,12 @@ static struct of_device_id lpuart_dt_ids[] = {
 };
 MODULE_DEVICE_TABLE(of, lpuart_dt_ids);
 
+#ifdef CONFIG_SERIAL_FSL_LPUART_DMA
+/* Forward declare this for the dma callbacks*/
+static void lpuart_dma_tx_complete(void *arg);
+static void lpuart_dma_rx_complete(void *arg);
+#endif
+
 static void lpuart_stop_tx(struct uart_port *port)
 {
 	unsigned char temp;
@@ -152,6 +189,212 @@ static void lpuart_enable_ms(struct uart_port *port)
 {
 }
 
+#ifdef CONFIG_SERIAL_FSL_LPUART_DMA
+static void lpuart_copy_rx_to_tty(struct lpuart_port *sport,
+		struct tty_port *tty, int count)
+{
+	int copied;
+
+	sport->port.icount.rx += count;
+
+	if (!tty) {
+		dev_err(sport->port.dev, "No tty port\n");
+		return;
+	}
+
+	dma_sync_single_for_cpu(sport->port.dev, sport->dma_rx_buf_bus,
+			FSL_UART_RX_DMA_BUFFER_SIZE, DMA_FROM_DEVICE);
+	copied = tty_insert_flip_string(tty,
+			((unsigned char *)(sport->dma_rx_buf_virt)), count);
+
+	if (copied != count) {
+		WARN_ON(1);
+		dev_err(sport->port.dev, "RxData copy to tty layer failed\n");
+	}
+
+	dma_sync_single_for_device(sport->port.dev, sport->dma_rx_buf_bus,
+			FSL_UART_RX_DMA_BUFFER_SIZE, DMA_TO_DEVICE);
+}
+
+static void lpuart_pio_tx(struct lpuart_port *sport)
+{
+	struct circ_buf *xmit = &sport->port.state->xmit;
+	unsigned long flags;
+
+	spin_lock_irqsave(&sport->port.lock, flags);
+
+	while (!uart_circ_empty(xmit) &&
+		readb(sport->port.membase + UARTTCFIFO) < sport->txfifo_size) {
+		writeb(xmit->buf[xmit->tail], sport->port.membase + UARTDR);
+		xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
+		sport->port.icount.tx++;
+	}
+
+	if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
+		uart_write_wakeup(&sport->port);
+
+	if (uart_circ_empty(xmit))
+		writeb(readb(sport->port.membase + UARTCR5) | UARTCR5_TDMAS,
+			sport->port.membase + UARTCR5);
+
+	spin_unlock_irqrestore(&sport->port.lock, flags);
+}
+
+static int lpuart_dma_tx(struct lpuart_port *sport, unsigned long count)
+{
+	struct circ_buf *xmit = &sport->port.state->xmit;
+	dma_addr_t tx_bus_addr;
+
+	dma_sync_single_for_device(sport->port.dev, sport->dma_tx_buf_bus,
+				UART_XMIT_SIZE, DMA_TO_DEVICE);
+	sport->dma_tx_bytes = count & ~(DMA_MAXBURST_MASK);
+	tx_bus_addr = sport->dma_tx_buf_bus + xmit->tail;
+	sport->dma_tx_desc = dmaengine_prep_slave_single(sport->dma_tx_chan,
+					tx_bus_addr, sport->dma_tx_bytes,
+					DMA_MEM_TO_DEV, DMA_PREP_INTERRUPT);
+
+	if (!sport->dma_tx_desc) {
+		dev_err(sport->port.dev, "Not able to get desc for tx\n");
+		return -EIO;
+	}
+
+	sport->dma_tx_desc->callback = lpuart_dma_tx_complete;
+	sport->dma_tx_desc->callback_param = sport;
+	sport->dma_tx_in_progress = 1;
+	sport->dma_tx_cookie = dmaengine_submit(sport->dma_tx_desc);
+	dma_async_issue_pending(sport->dma_tx_chan);
+
+	return 0;
+}
+
+static void lpuart_prepare_tx(struct lpuart_port *sport)
+{
+	struct circ_buf *xmit = &sport->port.state->xmit;
+	unsigned long count =  CIRC_CNT_TO_END(xmit->head,
+					xmit->tail, UART_XMIT_SIZE);
+
+	if (!count)
+		return;
+
+	if (count < DMA_MAXBURST)
+		writeb(readb(sport->port.membase + UARTCR5) & ~UARTCR5_TDMAS,
+				sport->port.membase + UARTCR5);
+	else {
+		writeb(readb(sport->port.membase + UARTCR5) | UARTCR5_TDMAS,
+				sport->port.membase + UARTCR5);
+		lpuart_dma_tx(sport, count);
+	}
+}
+
+static void lpuart_dma_tx_complete(void *arg)
+{
+	struct lpuart_port *sport = arg;
+	struct circ_buf *xmit = &sport->port.state->xmit;
+	unsigned long flags;
+
+	async_tx_ack(sport->dma_tx_desc);
+
+	spin_lock_irqsave(&sport->port.lock, flags);
+
+	xmit->tail = (xmit->tail + sport->dma_tx_bytes) & (UART_XMIT_SIZE - 1);
+	sport->dma_tx_in_progress = 0;
+
+	if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
+		uart_write_wakeup(&sport->port);
+
+	lpuart_prepare_tx(sport);
+
+	spin_unlock_irqrestore(&sport->port.lock, flags);
+}
+
+static int lpuart_dma_rx(struct lpuart_port *sport)
+{
+	dma_sync_single_for_device(sport->port.dev, sport->dma_rx_buf_bus,
+			FSL_UART_RX_DMA_BUFFER_SIZE, DMA_TO_DEVICE);
+	sport->dma_rx_desc = dmaengine_prep_slave_single(sport->dma_rx_chan,
+			sport->dma_rx_buf_bus, FSL_UART_RX_DMA_BUFFER_SIZE,
+			DMA_DEV_TO_MEM, DMA_PREP_INTERRUPT);
+
+	if (!sport->dma_rx_desc) {
+		dev_err(sport->port.dev, "Not able to get desc for rx\n");
+		return -EIO;
+	}
+
+	sport->dma_rx_desc->callback = lpuart_dma_rx_complete;
+	sport->dma_rx_desc->callback_param = sport;
+	sport->dma_rx_in_progress = 1;
+	sport->dma_rx_cookie = dmaengine_submit(sport->dma_rx_desc);
+	dma_async_issue_pending(sport->dma_rx_chan);
+
+	return 0;
+}
+
+static void lpuart_dma_rx_complete(void *arg)
+{
+	struct lpuart_port *sport = arg;
+	struct tty_port *port = &sport->port.state->port;
+	unsigned long flags;
+
+	async_tx_ack(sport->dma_rx_desc);
+
+	spin_lock_irqsave(&sport->port.lock, flags);
+
+	sport->dma_rx_in_progress = 0;
+	lpuart_copy_rx_to_tty(sport, port, FSL_UART_RX_DMA_BUFFER_SIZE);
+	tty_flip_buffer_push(port);
+	lpuart_dma_rx(sport);
+
+	spin_unlock_irqrestore(&sport->port.lock, flags);
+}
+
+static void lpuart_timer_func(unsigned long data)
+{
+	struct lpuart_port *sport = (struct lpuart_port *)data;
+	struct tty_port *port = &sport->port.state->port;
+	struct dma_tx_state state;
+	unsigned long flags;
+	unsigned char temp;
+	int count;
+
+	del_timer(&sport->lpuart_timer);
+	dmaengine_pause(sport->dma_rx_chan);
+	dmaengine_tx_status(sport->dma_rx_chan, sport->dma_rx_cookie, &state);
+	dmaengine_terminate_all(sport->dma_rx_chan);
+	count = FSL_UART_RX_DMA_BUFFER_SIZE - state.residue;
+	async_tx_ack(sport->dma_rx_desc);
+
+	spin_lock_irqsave(&sport->port.lock, flags);
+
+	sport->dma_rx_in_progress = 0;
+	lpuart_copy_rx_to_tty(sport, port, count);
+	tty_flip_buffer_push(port);
+	temp = readb(sport->port.membase + UARTCR5);
+	writeb(temp & ~UARTCR5_RDMAS, sport->port.membase + UARTCR5);
+
+	spin_unlock_irqrestore(&sport->port.lock, flags);
+}
+
+static inline void lpuart_prepare_rx(struct lpuart_port *sport)
+{
+	unsigned long flags;
+	unsigned char temp;
+
+	spin_lock_irqsave(&sport->port.lock, flags);
+
+	init_timer(&sport->lpuart_timer);
+	sport->lpuart_timer.function = lpuart_timer_func;
+	sport->lpuart_timer.data = (unsigned long)sport;
+	sport->lpuart_timer.expires = jiffies + sport->dma_rx_timeout;
+	add_timer(&sport->lpuart_timer);
+
+	lpuart_dma_rx(sport);
+	temp = readb(sport->port.membase + UARTCR5);
+	writeb(temp | UARTCR5_RDMAS, sport->port.membase + UARTCR5);
+
+	spin_unlock_irqrestore(&sport->port.lock, flags);
+}
+#endif
+
 static inline void lpuart_transmit_buffer(struct lpuart_port *sport)
 {
 	struct circ_buf *xmit = &sport->port.state->xmit;
@@ -172,14 +415,26 @@ static inline void lpuart_transmit_buffer(struct lpuart_port *sport)
 
 static void lpuart_start_tx(struct uart_port *port)
 {
-	struct lpuart_port *sport = container_of(port, struct lpuart_port, port);
+	struct lpuart_port *sport = container_of(port,
+			struct lpuart_port, port);
+	struct circ_buf *xmit = &sport->port.state->xmit;
 	unsigned char temp;
 
 	temp = readb(port->membase + UARTCR2);
 	writeb(temp | UARTCR2_TIE, port->membase + UARTCR2);
 
+#ifdef CONFIG_SERIAL_FSL_LPUART_DMA
+	if (sport->lpuart_dma_use) {
+		if (!uart_circ_empty(xmit) && !sport->dma_tx_in_progress)
+			lpuart_prepare_tx(sport);
+	} else {
+		if (readb(port->membase + UARTSR1) & UARTSR1_TDRE)
+			lpuart_transmit_buffer(sport);
+	}
+#else
 	if (readb(port->membase + UARTSR1) & UARTSR1_TDRE)
 		lpuart_transmit_buffer(sport);
+#endif
 }
 
 static irqreturn_t lpuart_txint(int irq, void *dev_id)
@@ -279,12 +534,27 @@ static irqreturn_t lpuart_int(int irq, void *dev_id)
 
 	sts = readb(sport->port.membase + UARTSR1);
 
-	if (sts & UARTSR1_RDRF)
+	if (sts & UARTSR1_RDRF) {
+#ifdef CONFIG_SERIAL_FSL_LPUART_DMA
+		if (sport->lpuart_dma_use)
+			lpuart_prepare_rx(sport);
+		else
+			lpuart_rxint(irq, dev_id);
+#else
 		lpuart_rxint(irq, dev_id);
-
+#endif
+	}
 	if (sts & UARTSR1_TDRE &&
-		!(readb(sport->port.membase + UARTCR5) & UARTCR5_TDMAS))
+		!(readb(sport->port.membase + UARTCR5) & UARTCR5_TDMAS)) {
+#ifdef CONFIG_SERIAL_FSL_LPUART_DMA
+		if (sport->lpuart_dma_use)
+			lpuart_pio_tx(sport);
+		else
+			lpuart_txint(irq, dev_id);
+#else
 		lpuart_txint(irq, dev_id);
+#endif
+	}
 
 	return IRQ_HANDLED;
 }
@@ -366,19 +636,179 @@ static void lpuart_setup_watermark(struct lpuart_port *sport)
 	writeb(UARTCFIFO_TXFLUSH | UARTCFIFO_RXFLUSH,
 			sport->port.membase + UARTCFIFO);
 
-	writeb(2, sport->port.membase + UARTTWFIFO);
+	writeb(0, sport->port.membase + UARTTWFIFO);
 	writeb(1, sport->port.membase + UARTRWFIFO);
 
 	/* Restore cr2 */
 	writeb(cr2_saved, sport->port.membase + UARTCR2);
 }
 
+#ifdef CONFIG_SERIAL_FSL_LPUART_DMA
+static int lpuart_dma_tx_request(struct uart_port *port)
+{
+	struct lpuart_port *sport = container_of(port,
+					struct lpuart_port, port);
+	struct dma_chan *tx_chan;
+	struct dma_slave_config dma_tx_sconfig;
+	dma_addr_t dma_bus;
+	unsigned char *dma_buf;
+	int ret;
+
+	tx_chan  = dma_request_slave_channel(sport->port.dev, "lpuart-tx");
+
+	if (!tx_chan) {
+		dev_err(sport->port.dev, "Dma tx channel request failed!\n");
+		return -ENODEV;
+	}
+
+	dma_bus = dma_map_single(tx_chan->device->dev,
+				sport->port.state->xmit.buf,
+				UART_XMIT_SIZE, DMA_TO_DEVICE);
+
+	if (dma_mapping_error(tx_chan->device->dev, dma_bus)) {
+		dev_err(sport->port.dev, "dma_map_single tx failed\n");
+		dma_release_channel(tx_chan);
+		return -ENOMEM;
+	}
+
+	dma_buf = sport->port.state->xmit.buf;
+	dma_tx_sconfig.dst_addr = sport->port.mapbase + UARTDR;
+	dma_tx_sconfig.dst_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE;
+	dma_tx_sconfig.dst_maxburst = DMA_MAXBURST;
+	dma_tx_sconfig.direction = DMA_MEM_TO_DEV;
+	ret = dmaengine_slave_config(tx_chan, &dma_tx_sconfig);
+
+	if (ret < 0) {
+		dev_err(sport->port.dev,
+				"Dma slave config failed, err = %d\n", ret);
+		dma_release_channel(tx_chan);
+		return ret;
+	}
+
+	sport->dma_tx_chan = tx_chan;
+	sport->dma_tx_buf_virt = dma_buf;
+	sport->dma_tx_buf_bus = dma_bus;
+	sport->dma_tx_in_progress = 0;
+
+	return 0;
+}
+
+static int lpuart_dma_rx_request(struct uart_port *port)
+{
+	struct lpuart_port *sport = container_of(port,
+					struct lpuart_port, port);
+	struct dma_chan *rx_chan;
+	struct dma_slave_config dma_rx_sconfig;
+	dma_addr_t dma_bus;
+	unsigned char *dma_buf;
+	int ret;
+
+	rx_chan  = dma_request_slave_channel(sport->port.dev, "lpuart-rx");
+
+	if (!rx_chan) {
+		dev_err(sport->port.dev, "Dma rx channel request failed!\n");
+		return -ENODEV;
+	}
+
+	dma_buf = devm_kzalloc(sport->port.dev,
+				FSL_UART_RX_DMA_BUFFER_SIZE, GFP_KERNEL);
+
+	if (!dma_buf) {
+		dev_err(sport->port.dev, "Dma rx alloc failed\n");
+		dma_release_channel(rx_chan);
+		return -ENOMEM;
+	}
+
+	dma_bus = dma_map_single(rx_chan->device->dev, dma_buf,
+				FSL_UART_RX_DMA_BUFFER_SIZE, DMA_FROM_DEVICE);
+
+	if (dma_mapping_error(rx_chan->device->dev, dma_bus)) {
+		dev_err(sport->port.dev, "dma_map_single rx failed\n");
+		dma_release_channel(rx_chan);
+		return -ENOMEM;
+	}
+
+	dma_rx_sconfig.src_addr = sport->port.mapbase + UARTDR;
+	dma_rx_sconfig.src_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE;
+	dma_rx_sconfig.src_maxburst = 1;
+	dma_rx_sconfig.direction = DMA_DEV_TO_MEM;
+	ret = dmaengine_slave_config(rx_chan, &dma_rx_sconfig);
+
+	if (ret < 0) {
+		dev_err(sport->port.dev,
+				"Dma slave config failed, err = %d\n", ret);
+		dma_release_channel(rx_chan);
+		return ret;
+	}
+
+	sport->dma_rx_chan = rx_chan;
+	sport->dma_rx_buf_virt = dma_buf;
+	sport->dma_rx_buf_bus = dma_bus;
+	sport->dma_rx_in_progress = 0;
+
+	sport->dma_rx_timeout = (sport->port.timeout - HZ / 50) *
+				FSL_UART_RX_DMA_BUFFER_SIZE * 3 /
+				sport->rxfifo_size / 2;
+
+	if (sport->dma_rx_timeout < msecs_to_jiffies(20))
+		sport->dma_rx_timeout = msecs_to_jiffies(20);
+
+	return 0;
+}
+
+static void lpuart_dma_tx_free(struct uart_port *port)
+{
+	struct lpuart_port *sport = container_of(port,
+					struct lpuart_port, port);
+	struct dma_chan *dma_chan;
+
+	dma_unmap_single(sport->port.dev, sport->dma_tx_buf_bus,
+			UART_XMIT_SIZE, DMA_TO_DEVICE);
+	dma_chan = sport->dma_tx_chan;
+	sport->dma_tx_chan = NULL;
+	sport->dma_tx_buf_bus = 0;
+	sport->dma_tx_buf_virt = NULL;
+	dma_release_channel(dma_chan);
+}
+
+static void lpuart_dma_rx_free(struct uart_port *port)
+{
+	struct lpuart_port *sport = container_of(port,
+					struct lpuart_port, port);
+	struct dma_chan *dma_chan;
+
+	dma_unmap_single(sport->port.dev, sport->dma_rx_buf_bus,
+			FSL_UART_RX_DMA_BUFFER_SIZE, DMA_FROM_DEVICE);
+
+	dma_chan = sport->dma_rx_chan;
+	sport->dma_rx_chan = NULL;
+	sport->dma_rx_buf_bus = 0;
+	sport->dma_rx_buf_virt = NULL;
+	dma_release_channel(dma_chan);
+}
+#endif
+
 static int lpuart_startup(struct uart_port *port)
 {
 	struct lpuart_port *sport = container_of(port, struct lpuart_port, port);
 	int ret;
 	unsigned long flags;
 	unsigned char temp;
+
+#ifdef CONFIG_SERIAL_FSL_LPUART_DMA
+	struct platform_device *pdev = to_platform_device(port->dev);
+	struct device_node *np = pdev->dev.of_node;
+
+	if (of_get_property(np, "dmas", NULL)) {
+		sport->lpuart_dma_use = true;
+		lpuart_dma_tx_request(port);
+		lpuart_dma_rx_request(port);
+		temp = readb(port->membase + UARTCR5);
+		writeb(temp | UARTCR5_TDMAS, port->membase + UARTCR5);
+	} else
+		sport->lpuart_dma_use = false;
+#endif
+
 	ret = devm_request_irq(port->dev, port->irq, lpuart_int, 0,
 				DRIVER_NAME, sport);
 	if (ret)
@@ -413,6 +843,13 @@ static void lpuart_shutdown(struct uart_port *port)
 	spin_unlock_irqrestore(&port->lock, flags);
 
 	devm_free_irq(port->dev, port->irq, sport);
+
+#ifdef CONFIG_SERIAL_FSL_LPUART_DMA
+	if (sport->lpuart_dma_use) {
+		lpuart_dma_tx_free(port);
+		lpuart_dma_rx_free(port);
+	}
+#endif
 }
 
 static void
@@ -854,6 +1291,10 @@ static int __init lpuart_serial_init(void)
 
 	pr_info("serial: Freescale lpuart driver\n");
 
+#ifdef CONFIG_SERIAL_FSL_LPUART_DMA
+	pr_info("serial: Freescale lpuart dma support\n");
+#endif
+
 	ret = uart_register_driver(&lpuart_reg);
 	if (ret)
 		return ret;
-- 
1.8.4



^ permalink raw reply related	[flat|nested] 24+ messages in thread

* [PATCH v3 2/2] serial: fsl_lpuart: add DMA support
@ 2014-01-15  5:24   ` Yuan Yao
  0 siblings, 0 replies; 24+ messages in thread
From: Yuan Yao @ 2014-01-15  5:24 UTC (permalink / raw)
  To: linux-arm-kernel

Add dma support for lpuart. This function depend on DMA driver. 
You can turn on it by SERIAL_FSL_LPUART_DMA=y. And It works if dts node has dma properties.

Signed-off-by: Yuan Yao <yao.yuan@freescale.com>
---
 .../devicetree/bindings/serial/fsl-lpuart.txt      |  21 +-
 drivers/tty/serial/Kconfig                         |   7 +
 drivers/tty/serial/fsl_lpuart.c                    | 457 ++++++++++++++++++++-
 3 files changed, 473 insertions(+), 12 deletions(-)

diff --git a/Documentation/devicetree/bindings/serial/fsl-lpuart.txt b/Documentation/devicetree/bindings/serial/fsl-lpuart.txt
index 6fd1dd1..7509080 100644
--- a/Documentation/devicetree/bindings/serial/fsl-lpuart.txt
+++ b/Documentation/devicetree/bindings/serial/fsl-lpuart.txt
@@ -4,11 +4,24 @@ Required properties:
 - compatible : Should be "fsl,<soc>-lpuart"
 - reg : Address and length of the register set for the device
 - interrupts : Should contain uart interrupt
+- clocks : from common clock binding: handle to uart clock
+- clock-names : from common clock binding: Shall be "ipg"
+
+Optional properties:
+- dma-names: Should contain "lpuart-tx" for transmit and "lpuart-rx" for receive channels
+- dmas: Should contain dma specifiers for transmit and receive channels
+
+Note: Optional properties for DMA support, If need this properties is must.
 
 Example:
 
 uart0: serial at 40027000 {
-	       compatible = "fsl,vf610-lpuart";
-	       reg = <0x40027000 0x1000>;
-	       interrupts = <0 61 0x00>;
-       };
+		compatible = "fsl,vf610-lpuart";
+		reg = <0x40027000 0x1000>;
+		interrupts = <0 61 0x00>;
+		clocks = <&clks VF610_CLK_UART0>;
+		clock-names = "ipg";
+		dma-names = "lpuart-tx","lpuart-rx";
+		dmas = <&edma0 0 VF610_EDMA_MUXID0_UART0_TX>,
+			<&edma0 0 VF610_EDMA_MUXID0_UART0_RX>;
+	};
diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig
index a3817ab..ab3910b 100644
--- a/drivers/tty/serial/Kconfig
+++ b/drivers/tty/serial/Kconfig
@@ -1509,6 +1509,13 @@ config SERIAL_FSL_LPUART_CONSOLE
 	  If you have enabled the lpuart serial port on the Freescale SoCs,
 	  you can make it the console by answering Y to this option.
 
+config SERIAL_FSL_LPUART_DMA
+	bool "Freescale lpuart serial port dma support"
+	depends on FSL_EDMA=y && SERIAL_FSL_LPUART=y
+	help
+	  DMA support for the on-chip lpuart on some Freescale SOCs,
+	  you can turn on the dma support for lpuart by answering Y to this option.
+
 config SERIAL_ST_ASC
 	tristate "ST ASC serial port support"
 	select SERIAL_CORE
diff --git a/drivers/tty/serial/fsl_lpuart.c b/drivers/tty/serial/fsl_lpuart.c
index 67b8946..5fdcb18 100644
--- a/drivers/tty/serial/fsl_lpuart.c
+++ b/drivers/tty/serial/fsl_lpuart.c
@@ -13,14 +13,19 @@
 #define SUPPORT_SYSRQ
 #endif
 
-#include <linux/module.h>
+#include <linux/clk.h>
+#include <linux/console.h>
+#include <linux/dma-mapping.h>
+#include <linux/dmaengine.h>
+#include <linux/dmapool.h>
 #include <linux/io.h>
 #include <linux/irq.h>
-#include <linux/clk.h>
+#include <linux/module.h>
 #include <linux/of.h>
 #include <linux/of_device.h>
-#include <linux/console.h>
+#include <linux/of_dma.h>
 #include <linux/serial_core.h>
+#include <linux/slab.h>
 #include <linux/tty_flip.h>
 
 /* All registers are 8-bit width */
@@ -112,6 +117,12 @@
 #define UARTSFIFO_TXOF		0x02
 #define UARTSFIFO_RXUF		0x01
 
+#ifdef CONFIG_SERIAL_FSL_LPUART_DMA
+#define DMA_MAXBURST		16
+#define DMA_MAXBURST_MASK	(DMA_MAXBURST - 1)
+#define FSL_UART_RX_DMA_BUFFER_SIZE	64
+#endif
+
 #define DRIVER_NAME	"fsl-lpuart"
 #define DEV_NAME	"ttyLP"
 #define UART_NR		6
@@ -121,6 +132,26 @@ struct lpuart_port {
 	struct clk		*clk;
 	unsigned int		txfifo_size;
 	unsigned int		rxfifo_size;
+
+#ifdef CONFIG_SERIAL_FSL_LPUART_DMA
+	bool			lpuart_dma_use;
+	struct dma_chan		*dma_tx_chan;
+	struct dma_chan		*dma_rx_chan;
+	struct dma_async_tx_descriptor  *dma_tx_desc;
+	struct dma_async_tx_descriptor  *dma_rx_desc;
+	dma_addr_t		dma_tx_buf_bus;
+	dma_addr_t		dma_rx_buf_bus;
+	dma_cookie_t		dma_tx_cookie;
+	dma_cookie_t		dma_rx_cookie;
+	unsigned char		*dma_tx_buf_virt;
+	unsigned char		*dma_rx_buf_virt;
+	unsigned int		dma_tx_bytes;
+	unsigned int		dma_rx_bytes;
+	int			dma_tx_in_progress;
+	int			dma_rx_in_progress;
+	unsigned int		dma_rx_timeout;
+	struct timer_list	lpuart_timer;
+#endif
 };
 
 static struct of_device_id lpuart_dt_ids[] = {
@@ -131,6 +162,12 @@ static struct of_device_id lpuart_dt_ids[] = {
 };
 MODULE_DEVICE_TABLE(of, lpuart_dt_ids);
 
+#ifdef CONFIG_SERIAL_FSL_LPUART_DMA
+/* Forward declare this for the dma callbacks*/
+static void lpuart_dma_tx_complete(void *arg);
+static void lpuart_dma_rx_complete(void *arg);
+#endif
+
 static void lpuart_stop_tx(struct uart_port *port)
 {
 	unsigned char temp;
@@ -152,6 +189,212 @@ static void lpuart_enable_ms(struct uart_port *port)
 {
 }
 
+#ifdef CONFIG_SERIAL_FSL_LPUART_DMA
+static void lpuart_copy_rx_to_tty(struct lpuart_port *sport,
+		struct tty_port *tty, int count)
+{
+	int copied;
+
+	sport->port.icount.rx += count;
+
+	if (!tty) {
+		dev_err(sport->port.dev, "No tty port\n");
+		return;
+	}
+
+	dma_sync_single_for_cpu(sport->port.dev, sport->dma_rx_buf_bus,
+			FSL_UART_RX_DMA_BUFFER_SIZE, DMA_FROM_DEVICE);
+	copied = tty_insert_flip_string(tty,
+			((unsigned char *)(sport->dma_rx_buf_virt)), count);
+
+	if (copied != count) {
+		WARN_ON(1);
+		dev_err(sport->port.dev, "RxData copy to tty layer failed\n");
+	}
+
+	dma_sync_single_for_device(sport->port.dev, sport->dma_rx_buf_bus,
+			FSL_UART_RX_DMA_BUFFER_SIZE, DMA_TO_DEVICE);
+}
+
+static void lpuart_pio_tx(struct lpuart_port *sport)
+{
+	struct circ_buf *xmit = &sport->port.state->xmit;
+	unsigned long flags;
+
+	spin_lock_irqsave(&sport->port.lock, flags);
+
+	while (!uart_circ_empty(xmit) &&
+		readb(sport->port.membase + UARTTCFIFO) < sport->txfifo_size) {
+		writeb(xmit->buf[xmit->tail], sport->port.membase + UARTDR);
+		xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
+		sport->port.icount.tx++;
+	}
+
+	if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
+		uart_write_wakeup(&sport->port);
+
+	if (uart_circ_empty(xmit))
+		writeb(readb(sport->port.membase + UARTCR5) | UARTCR5_TDMAS,
+			sport->port.membase + UARTCR5);
+
+	spin_unlock_irqrestore(&sport->port.lock, flags);
+}
+
+static int lpuart_dma_tx(struct lpuart_port *sport, unsigned long count)
+{
+	struct circ_buf *xmit = &sport->port.state->xmit;
+	dma_addr_t tx_bus_addr;
+
+	dma_sync_single_for_device(sport->port.dev, sport->dma_tx_buf_bus,
+				UART_XMIT_SIZE, DMA_TO_DEVICE);
+	sport->dma_tx_bytes = count & ~(DMA_MAXBURST_MASK);
+	tx_bus_addr = sport->dma_tx_buf_bus + xmit->tail;
+	sport->dma_tx_desc = dmaengine_prep_slave_single(sport->dma_tx_chan,
+					tx_bus_addr, sport->dma_tx_bytes,
+					DMA_MEM_TO_DEV, DMA_PREP_INTERRUPT);
+
+	if (!sport->dma_tx_desc) {
+		dev_err(sport->port.dev, "Not able to get desc for tx\n");
+		return -EIO;
+	}
+
+	sport->dma_tx_desc->callback = lpuart_dma_tx_complete;
+	sport->dma_tx_desc->callback_param = sport;
+	sport->dma_tx_in_progress = 1;
+	sport->dma_tx_cookie = dmaengine_submit(sport->dma_tx_desc);
+	dma_async_issue_pending(sport->dma_tx_chan);
+
+	return 0;
+}
+
+static void lpuart_prepare_tx(struct lpuart_port *sport)
+{
+	struct circ_buf *xmit = &sport->port.state->xmit;
+	unsigned long count =  CIRC_CNT_TO_END(xmit->head,
+					xmit->tail, UART_XMIT_SIZE);
+
+	if (!count)
+		return;
+
+	if (count < DMA_MAXBURST)
+		writeb(readb(sport->port.membase + UARTCR5) & ~UARTCR5_TDMAS,
+				sport->port.membase + UARTCR5);
+	else {
+		writeb(readb(sport->port.membase + UARTCR5) | UARTCR5_TDMAS,
+				sport->port.membase + UARTCR5);
+		lpuart_dma_tx(sport, count);
+	}
+}
+
+static void lpuart_dma_tx_complete(void *arg)
+{
+	struct lpuart_port *sport = arg;
+	struct circ_buf *xmit = &sport->port.state->xmit;
+	unsigned long flags;
+
+	async_tx_ack(sport->dma_tx_desc);
+
+	spin_lock_irqsave(&sport->port.lock, flags);
+
+	xmit->tail = (xmit->tail + sport->dma_tx_bytes) & (UART_XMIT_SIZE - 1);
+	sport->dma_tx_in_progress = 0;
+
+	if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
+		uart_write_wakeup(&sport->port);
+
+	lpuart_prepare_tx(sport);
+
+	spin_unlock_irqrestore(&sport->port.lock, flags);
+}
+
+static int lpuart_dma_rx(struct lpuart_port *sport)
+{
+	dma_sync_single_for_device(sport->port.dev, sport->dma_rx_buf_bus,
+			FSL_UART_RX_DMA_BUFFER_SIZE, DMA_TO_DEVICE);
+	sport->dma_rx_desc = dmaengine_prep_slave_single(sport->dma_rx_chan,
+			sport->dma_rx_buf_bus, FSL_UART_RX_DMA_BUFFER_SIZE,
+			DMA_DEV_TO_MEM, DMA_PREP_INTERRUPT);
+
+	if (!sport->dma_rx_desc) {
+		dev_err(sport->port.dev, "Not able to get desc for rx\n");
+		return -EIO;
+	}
+
+	sport->dma_rx_desc->callback = lpuart_dma_rx_complete;
+	sport->dma_rx_desc->callback_param = sport;
+	sport->dma_rx_in_progress = 1;
+	sport->dma_rx_cookie = dmaengine_submit(sport->dma_rx_desc);
+	dma_async_issue_pending(sport->dma_rx_chan);
+
+	return 0;
+}
+
+static void lpuart_dma_rx_complete(void *arg)
+{
+	struct lpuart_port *sport = arg;
+	struct tty_port *port = &sport->port.state->port;
+	unsigned long flags;
+
+	async_tx_ack(sport->dma_rx_desc);
+
+	spin_lock_irqsave(&sport->port.lock, flags);
+
+	sport->dma_rx_in_progress = 0;
+	lpuart_copy_rx_to_tty(sport, port, FSL_UART_RX_DMA_BUFFER_SIZE);
+	tty_flip_buffer_push(port);
+	lpuart_dma_rx(sport);
+
+	spin_unlock_irqrestore(&sport->port.lock, flags);
+}
+
+static void lpuart_timer_func(unsigned long data)
+{
+	struct lpuart_port *sport = (struct lpuart_port *)data;
+	struct tty_port *port = &sport->port.state->port;
+	struct dma_tx_state state;
+	unsigned long flags;
+	unsigned char temp;
+	int count;
+
+	del_timer(&sport->lpuart_timer);
+	dmaengine_pause(sport->dma_rx_chan);
+	dmaengine_tx_status(sport->dma_rx_chan, sport->dma_rx_cookie, &state);
+	dmaengine_terminate_all(sport->dma_rx_chan);
+	count = FSL_UART_RX_DMA_BUFFER_SIZE - state.residue;
+	async_tx_ack(sport->dma_rx_desc);
+
+	spin_lock_irqsave(&sport->port.lock, flags);
+
+	sport->dma_rx_in_progress = 0;
+	lpuart_copy_rx_to_tty(sport, port, count);
+	tty_flip_buffer_push(port);
+	temp = readb(sport->port.membase + UARTCR5);
+	writeb(temp & ~UARTCR5_RDMAS, sport->port.membase + UARTCR5);
+
+	spin_unlock_irqrestore(&sport->port.lock, flags);
+}
+
+static inline void lpuart_prepare_rx(struct lpuart_port *sport)
+{
+	unsigned long flags;
+	unsigned char temp;
+
+	spin_lock_irqsave(&sport->port.lock, flags);
+
+	init_timer(&sport->lpuart_timer);
+	sport->lpuart_timer.function = lpuart_timer_func;
+	sport->lpuart_timer.data = (unsigned long)sport;
+	sport->lpuart_timer.expires = jiffies + sport->dma_rx_timeout;
+	add_timer(&sport->lpuart_timer);
+
+	lpuart_dma_rx(sport);
+	temp = readb(sport->port.membase + UARTCR5);
+	writeb(temp | UARTCR5_RDMAS, sport->port.membase + UARTCR5);
+
+	spin_unlock_irqrestore(&sport->port.lock, flags);
+}
+#endif
+
 static inline void lpuart_transmit_buffer(struct lpuart_port *sport)
 {
 	struct circ_buf *xmit = &sport->port.state->xmit;
@@ -172,14 +415,26 @@ static inline void lpuart_transmit_buffer(struct lpuart_port *sport)
 
 static void lpuart_start_tx(struct uart_port *port)
 {
-	struct lpuart_port *sport = container_of(port, struct lpuart_port, port);
+	struct lpuart_port *sport = container_of(port,
+			struct lpuart_port, port);
+	struct circ_buf *xmit = &sport->port.state->xmit;
 	unsigned char temp;
 
 	temp = readb(port->membase + UARTCR2);
 	writeb(temp | UARTCR2_TIE, port->membase + UARTCR2);
 
+#ifdef CONFIG_SERIAL_FSL_LPUART_DMA
+	if (sport->lpuart_dma_use) {
+		if (!uart_circ_empty(xmit) && !sport->dma_tx_in_progress)
+			lpuart_prepare_tx(sport);
+	} else {
+		if (readb(port->membase + UARTSR1) & UARTSR1_TDRE)
+			lpuart_transmit_buffer(sport);
+	}
+#else
 	if (readb(port->membase + UARTSR1) & UARTSR1_TDRE)
 		lpuart_transmit_buffer(sport);
+#endif
 }
 
 static irqreturn_t lpuart_txint(int irq, void *dev_id)
@@ -279,12 +534,27 @@ static irqreturn_t lpuart_int(int irq, void *dev_id)
 
 	sts = readb(sport->port.membase + UARTSR1);
 
-	if (sts & UARTSR1_RDRF)
+	if (sts & UARTSR1_RDRF) {
+#ifdef CONFIG_SERIAL_FSL_LPUART_DMA
+		if (sport->lpuart_dma_use)
+			lpuart_prepare_rx(sport);
+		else
+			lpuart_rxint(irq, dev_id);
+#else
 		lpuart_rxint(irq, dev_id);
-
+#endif
+	}
 	if (sts & UARTSR1_TDRE &&
-		!(readb(sport->port.membase + UARTCR5) & UARTCR5_TDMAS))
+		!(readb(sport->port.membase + UARTCR5) & UARTCR5_TDMAS)) {
+#ifdef CONFIG_SERIAL_FSL_LPUART_DMA
+		if (sport->lpuart_dma_use)
+			lpuart_pio_tx(sport);
+		else
+			lpuart_txint(irq, dev_id);
+#else
 		lpuart_txint(irq, dev_id);
+#endif
+	}
 
 	return IRQ_HANDLED;
 }
@@ -366,19 +636,179 @@ static void lpuart_setup_watermark(struct lpuart_port *sport)
 	writeb(UARTCFIFO_TXFLUSH | UARTCFIFO_RXFLUSH,
 			sport->port.membase + UARTCFIFO);
 
-	writeb(2, sport->port.membase + UARTTWFIFO);
+	writeb(0, sport->port.membase + UARTTWFIFO);
 	writeb(1, sport->port.membase + UARTRWFIFO);
 
 	/* Restore cr2 */
 	writeb(cr2_saved, sport->port.membase + UARTCR2);
 }
 
+#ifdef CONFIG_SERIAL_FSL_LPUART_DMA
+static int lpuart_dma_tx_request(struct uart_port *port)
+{
+	struct lpuart_port *sport = container_of(port,
+					struct lpuart_port, port);
+	struct dma_chan *tx_chan;
+	struct dma_slave_config dma_tx_sconfig;
+	dma_addr_t dma_bus;
+	unsigned char *dma_buf;
+	int ret;
+
+	tx_chan  = dma_request_slave_channel(sport->port.dev, "lpuart-tx");
+
+	if (!tx_chan) {
+		dev_err(sport->port.dev, "Dma tx channel request failed!\n");
+		return -ENODEV;
+	}
+
+	dma_bus = dma_map_single(tx_chan->device->dev,
+				sport->port.state->xmit.buf,
+				UART_XMIT_SIZE, DMA_TO_DEVICE);
+
+	if (dma_mapping_error(tx_chan->device->dev, dma_bus)) {
+		dev_err(sport->port.dev, "dma_map_single tx failed\n");
+		dma_release_channel(tx_chan);
+		return -ENOMEM;
+	}
+
+	dma_buf = sport->port.state->xmit.buf;
+	dma_tx_sconfig.dst_addr = sport->port.mapbase + UARTDR;
+	dma_tx_sconfig.dst_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE;
+	dma_tx_sconfig.dst_maxburst = DMA_MAXBURST;
+	dma_tx_sconfig.direction = DMA_MEM_TO_DEV;
+	ret = dmaengine_slave_config(tx_chan, &dma_tx_sconfig);
+
+	if (ret < 0) {
+		dev_err(sport->port.dev,
+				"Dma slave config failed, err = %d\n", ret);
+		dma_release_channel(tx_chan);
+		return ret;
+	}
+
+	sport->dma_tx_chan = tx_chan;
+	sport->dma_tx_buf_virt = dma_buf;
+	sport->dma_tx_buf_bus = dma_bus;
+	sport->dma_tx_in_progress = 0;
+
+	return 0;
+}
+
+static int lpuart_dma_rx_request(struct uart_port *port)
+{
+	struct lpuart_port *sport = container_of(port,
+					struct lpuart_port, port);
+	struct dma_chan *rx_chan;
+	struct dma_slave_config dma_rx_sconfig;
+	dma_addr_t dma_bus;
+	unsigned char *dma_buf;
+	int ret;
+
+	rx_chan  = dma_request_slave_channel(sport->port.dev, "lpuart-rx");
+
+	if (!rx_chan) {
+		dev_err(sport->port.dev, "Dma rx channel request failed!\n");
+		return -ENODEV;
+	}
+
+	dma_buf = devm_kzalloc(sport->port.dev,
+				FSL_UART_RX_DMA_BUFFER_SIZE, GFP_KERNEL);
+
+	if (!dma_buf) {
+		dev_err(sport->port.dev, "Dma rx alloc failed\n");
+		dma_release_channel(rx_chan);
+		return -ENOMEM;
+	}
+
+	dma_bus = dma_map_single(rx_chan->device->dev, dma_buf,
+				FSL_UART_RX_DMA_BUFFER_SIZE, DMA_FROM_DEVICE);
+
+	if (dma_mapping_error(rx_chan->device->dev, dma_bus)) {
+		dev_err(sport->port.dev, "dma_map_single rx failed\n");
+		dma_release_channel(rx_chan);
+		return -ENOMEM;
+	}
+
+	dma_rx_sconfig.src_addr = sport->port.mapbase + UARTDR;
+	dma_rx_sconfig.src_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE;
+	dma_rx_sconfig.src_maxburst = 1;
+	dma_rx_sconfig.direction = DMA_DEV_TO_MEM;
+	ret = dmaengine_slave_config(rx_chan, &dma_rx_sconfig);
+
+	if (ret < 0) {
+		dev_err(sport->port.dev,
+				"Dma slave config failed, err = %d\n", ret);
+		dma_release_channel(rx_chan);
+		return ret;
+	}
+
+	sport->dma_rx_chan = rx_chan;
+	sport->dma_rx_buf_virt = dma_buf;
+	sport->dma_rx_buf_bus = dma_bus;
+	sport->dma_rx_in_progress = 0;
+
+	sport->dma_rx_timeout = (sport->port.timeout - HZ / 50) *
+				FSL_UART_RX_DMA_BUFFER_SIZE * 3 /
+				sport->rxfifo_size / 2;
+
+	if (sport->dma_rx_timeout < msecs_to_jiffies(20))
+		sport->dma_rx_timeout = msecs_to_jiffies(20);
+
+	return 0;
+}
+
+static void lpuart_dma_tx_free(struct uart_port *port)
+{
+	struct lpuart_port *sport = container_of(port,
+					struct lpuart_port, port);
+	struct dma_chan *dma_chan;
+
+	dma_unmap_single(sport->port.dev, sport->dma_tx_buf_bus,
+			UART_XMIT_SIZE, DMA_TO_DEVICE);
+	dma_chan = sport->dma_tx_chan;
+	sport->dma_tx_chan = NULL;
+	sport->dma_tx_buf_bus = 0;
+	sport->dma_tx_buf_virt = NULL;
+	dma_release_channel(dma_chan);
+}
+
+static void lpuart_dma_rx_free(struct uart_port *port)
+{
+	struct lpuart_port *sport = container_of(port,
+					struct lpuart_port, port);
+	struct dma_chan *dma_chan;
+
+	dma_unmap_single(sport->port.dev, sport->dma_rx_buf_bus,
+			FSL_UART_RX_DMA_BUFFER_SIZE, DMA_FROM_DEVICE);
+
+	dma_chan = sport->dma_rx_chan;
+	sport->dma_rx_chan = NULL;
+	sport->dma_rx_buf_bus = 0;
+	sport->dma_rx_buf_virt = NULL;
+	dma_release_channel(dma_chan);
+}
+#endif
+
 static int lpuart_startup(struct uart_port *port)
 {
 	struct lpuart_port *sport = container_of(port, struct lpuart_port, port);
 	int ret;
 	unsigned long flags;
 	unsigned char temp;
+
+#ifdef CONFIG_SERIAL_FSL_LPUART_DMA
+	struct platform_device *pdev = to_platform_device(port->dev);
+	struct device_node *np = pdev->dev.of_node;
+
+	if (of_get_property(np, "dmas", NULL)) {
+		sport->lpuart_dma_use = true;
+		lpuart_dma_tx_request(port);
+		lpuart_dma_rx_request(port);
+		temp = readb(port->membase + UARTCR5);
+		writeb(temp | UARTCR5_TDMAS, port->membase + UARTCR5);
+	} else
+		sport->lpuart_dma_use = false;
+#endif
+
 	ret = devm_request_irq(port->dev, port->irq, lpuart_int, 0,
 				DRIVER_NAME, sport);
 	if (ret)
@@ -413,6 +843,13 @@ static void lpuart_shutdown(struct uart_port *port)
 	spin_unlock_irqrestore(&port->lock, flags);
 
 	devm_free_irq(port->dev, port->irq, sport);
+
+#ifdef CONFIG_SERIAL_FSL_LPUART_DMA
+	if (sport->lpuart_dma_use) {
+		lpuart_dma_tx_free(port);
+		lpuart_dma_rx_free(port);
+	}
+#endif
 }
 
 static void
@@ -854,6 +1291,10 @@ static int __init lpuart_serial_init(void)
 
 	pr_info("serial: Freescale lpuart driver\n");
 
+#ifdef CONFIG_SERIAL_FSL_LPUART_DMA
+	pr_info("serial: Freescale lpuart dma support\n");
+#endif
+
 	ret = uart_register_driver(&lpuart_reg);
 	if (ret)
 		return ret;
-- 
1.8.4

^ permalink raw reply related	[flat|nested] 24+ messages in thread

end of thread, other threads:[~2014-01-22  3:41 UTC | newest]

Thread overview: 24+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2014-01-15  5:21 [PATCH v3 1/2] ARM: dts: vf610: lpuart: Add eDMA support Yuan Yao
2014-01-15  5:21 ` Yuan Yao
2014-01-15  5:21 ` [PATCH v3 2/2] serial: fsl_lpuart: add DMA support Yuan Yao
2014-01-15  5:21   ` Yuan Yao
2014-01-15  9:25   ` Arnd Bergmann
2014-01-15  9:25     ` Arnd Bergmann
2014-01-16  6:02     ` Yao Yuan
2014-01-16  6:02       ` Yao Yuan
2014-01-16  8:19     ` Yao Yuan
2014-01-16  8:19       ` Yao Yuan
2014-01-16 10:47       ` Arnd Bergmann
2014-01-16 10:47         ` Arnd Bergmann
2014-01-15 11:05   ` Mark Rutland
2014-01-15 11:05     ` Mark Rutland
2014-01-15 11:17     ` Arnd Bergmann
2014-01-15 11:17       ` Arnd Bergmann
2014-01-15 13:50       ` Mark Rutland
2014-01-15 13:50         ` Mark Rutland
2014-01-16  6:54     ` Yao Yuan
2014-01-16  6:54       ` Yao Yuan
2014-01-22  3:41       ` Yao Yuan
2014-01-22  3:41         ` Yao Yuan
2014-01-15  5:24 [PATCH v3 0/2] " Yuan Yao
2014-01-15  5:24 ` [PATCH v3 2/2] " Yuan Yao
2014-01-15  5:24   ` Yuan Yao

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.