linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH v2 0/4] Add new features for the Spreadtrum serial controller
@ 2019-03-04  8:58 Baolin Wang
  2019-03-04  8:58 ` [PATCH v2 1/4] dt-bindings: serial: sprd: Add clocks and clocks-names properties Baolin Wang
                   ` (4 more replies)
  0 siblings, 5 replies; 9+ messages in thread
From: Baolin Wang @ 2019-03-04  8:58 UTC (permalink / raw)
  To: gregkh, jslaby, robh+dt, mark.rutland, orsonzhai, zhang.lyra
  Cc: baolin.wang, broonie, lanqing.liu, linux-serial, linux-kernel,
	devicetree

This patch set fixes the baud rate calculation formula issue, as well as
adding power management support and DMA mode support for the Spreadtrum
serial controller.

Changes from v1:
 - The patch 1 of V1 was applied, so remove the baud rate fix from this patch set.
 - Add reviewed tag from Rob for patch 1.
 - Fix the incorrect names' order in patch 3.

Lanqing Liu (4):
  dt-bindings: serial: sprd: Add clocks and clocks-names properties
  serial: sprd: Add power management for the Spreadtrum serial
    controller
  dt-bindings: serial: sprd: Add dma properties to support DMA mode
  serial: sprd: Add DMA mode support

 .../devicetree/bindings/serial/sprd-uart.txt       |   17 +-
 drivers/tty/serial/sprd_serial.c                   |  501 +++++++++++++++++++-
 2 files changed, 498 insertions(+), 20 deletions(-)

-- 
1.7.9.5


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

* [PATCH v2 1/4] dt-bindings: serial: sprd: Add clocks and clocks-names properties
  2019-03-04  8:58 [PATCH v2 0/4] Add new features for the Spreadtrum serial controller Baolin Wang
@ 2019-03-04  8:58 ` Baolin Wang
  2019-03-04  8:58 ` [PATCH v2 2/4] serial: sprd: Add power management for the Spreadtrum serial controller Baolin Wang
                   ` (3 subsequent siblings)
  4 siblings, 0 replies; 9+ messages in thread
From: Baolin Wang @ 2019-03-04  8:58 UTC (permalink / raw)
  To: gregkh, jslaby, robh+dt, mark.rutland, orsonzhai, zhang.lyra
  Cc: baolin.wang, broonie, lanqing.liu, linux-serial, linux-kernel,
	devicetree

From: Lanqing Liu <lanqing.liu@unisoc.com>

This patch adds clocks and clocks-names properties, which are used to do
power management for our UART driver.

Reviewed-by: Rob Herring <robh@kernel.org>
Signed-off-by: Lanqing Liu <lanqing.liu@unisoc.com>
Signed-off-by: Baolin Wang <baolin.wang@linaro.org>
---
 .../devicetree/bindings/serial/sprd-uart.txt       |   11 +++++++++--
 1 file changed, 9 insertions(+), 2 deletions(-)

diff --git a/Documentation/devicetree/bindings/serial/sprd-uart.txt b/Documentation/devicetree/bindings/serial/sprd-uart.txt
index cab40f0..6eb5863 100644
--- a/Documentation/devicetree/bindings/serial/sprd-uart.txt
+++ b/Documentation/devicetree/bindings/serial/sprd-uart.txt
@@ -7,7 +7,13 @@ Required properties:
 
 - reg: offset and length of the register set for the device
 - interrupts: exactly one interrupt specifier
-- clocks: phandles to input clocks.
+- clock-names: Should contain following entries:
+  "enable" for UART module enable clock,
+  "uart" for UART clock,
+  "source" for UART source (parent) clock.
+- clocks: Should contain a clock specifier for each entry in clock-names.
+  UART clock and source clock are optional properties, but enable clock
+  is required.
 
 Example:
 	uart0: serial@0 {
@@ -15,5 +21,6 @@ Example:
 			     "sprd,sc9836-uart";
 		reg = <0x0 0x100>;
 		interrupts = <GIC_SPI 2 IRQ_TYPE_LEVEL_HIGH>;
-		clocks = <&ext_26m>;
+		clock-names = "enable", "uart", "source";
+		clocks = <&clk_ap_apb_gates 9>, <&clk_uart0>, <&ext_26m>;
 	};
-- 
1.7.9.5


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

* [PATCH v2 2/4] serial: sprd: Add power management for the Spreadtrum serial controller
  2019-03-04  8:58 [PATCH v2 0/4] Add new features for the Spreadtrum serial controller Baolin Wang
  2019-03-04  8:58 ` [PATCH v2 1/4] dt-bindings: serial: sprd: Add clocks and clocks-names properties Baolin Wang
@ 2019-03-04  8:58 ` Baolin Wang
  2019-03-04  8:58 ` [PATCH v2 3/4] dt-bindings: serial: sprd: Add dma properties to support DMA mode Baolin Wang
                   ` (2 subsequent siblings)
  4 siblings, 0 replies; 9+ messages in thread
From: Baolin Wang @ 2019-03-04  8:58 UTC (permalink / raw)
  To: gregkh, jslaby, robh+dt, mark.rutland, orsonzhai, zhang.lyra
  Cc: baolin.wang, broonie, lanqing.liu, linux-serial, linux-kernel,
	devicetree

From: Lanqing Liu <lanqing.liu@unisoc.com>

This patch adds power management for the Spreadtrum serial controller.

Signed-off-by: Lanqing Liu <lanqing.liu@unisoc.com>
Signed-off-by: Baolin Wang <baolin.wang@linaro.org>
---
 drivers/tty/serial/sprd_serial.c |   61 +++++++++++++++++++++++++++++++++++---
 1 file changed, 57 insertions(+), 4 deletions(-)

diff --git a/drivers/tty/serial/sprd_serial.c b/drivers/tty/serial/sprd_serial.c
index 1891a45..8f45b66 100644
--- a/drivers/tty/serial/sprd_serial.c
+++ b/drivers/tty/serial/sprd_serial.c
@@ -100,10 +100,12 @@
 #define SPRD_IMSR_TX_FIFO_EMPTY	BIT(1)
 #define SPRD_IMSR_BREAK_DETECT	BIT(7)
 #define SPRD_IMSR_TIMEOUT	BIT(13)
+#define SPRD_DEFAULT_SOURCE_CLK	26000000
 
 struct sprd_uart_port {
 	struct uart_port port;
 	char name[16];
+	struct clk *clk;
 };
 
 static struct sprd_uart_port *sprd_port[UART_NR_MAX];
@@ -491,6 +493,22 @@ static int sprd_verify_port(struct uart_port *port, struct serial_struct *ser)
 	return 0;
 }
 
+static void sprd_pm(struct uart_port *port, unsigned int state,
+		unsigned int oldstate)
+{
+	struct sprd_uart_port *sup =
+		container_of(port, struct sprd_uart_port, port);
+
+	switch (state) {
+	case UART_PM_STATE_ON:
+		clk_prepare_enable(sup->clk);
+		break;
+	case UART_PM_STATE_OFF:
+		clk_disable_unprepare(sup->clk);
+		break;
+	}
+}
+
 static const struct uart_ops serial_sprd_ops = {
 	.tx_empty = sprd_tx_empty,
 	.get_mctrl = sprd_get_mctrl,
@@ -507,6 +525,7 @@ static int sprd_verify_port(struct uart_port *port, struct serial_struct *ser)
 	.request_port = sprd_request_port,
 	.config_port = sprd_config_port,
 	.verify_port = sprd_verify_port,
+	.pm = sprd_pm,
 };
 
 #ifdef CONFIG_SERIAL_SPRD_CONSOLE
@@ -671,11 +690,45 @@ static int sprd_remove(struct platform_device *dev)
 	return 0;
 }
 
+static int sprd_clk_init(struct uart_port *uport)
+{
+	struct clk *clk_uart, *clk_parent;
+	struct sprd_uart_port *u = sprd_port[uport->line];
+
+	clk_uart = devm_clk_get(uport->dev, "uart");
+	if (IS_ERR(clk_uart)) {
+		dev_warn(uport->dev, "uart%d can't get uart clock\n",
+			 uport->line);
+		clk_uart = NULL;
+	}
+
+	clk_parent = devm_clk_get(uport->dev, "source");
+	if (IS_ERR(clk_parent)) {
+		dev_warn(uport->dev, "uart%d can't get source clock\n",
+			 uport->line);
+		clk_parent = NULL;
+	}
+
+	if (!clk_uart || clk_set_parent(clk_uart, clk_parent))
+		uport->uartclk = SPRD_DEFAULT_SOURCE_CLK;
+	else
+		uport->uartclk = clk_get_rate(clk_uart);
+
+	u->clk = devm_clk_get(uport->dev, "enable");
+	if (IS_ERR(u->clk)) {
+		if (PTR_ERR(u->clk) != -EPROBE_DEFER)
+			dev_err(uport->dev, "uart%d can't get enable clock\n",
+				uport->line);
+		return PTR_ERR(u->clk);
+	}
+
+	return 0;
+}
+
 static int sprd_probe(struct platform_device *pdev)
 {
 	struct resource *res;
 	struct uart_port *up;
-	struct clk *clk;
 	int irq;
 	int index;
 	int ret;
@@ -704,9 +757,9 @@ static int sprd_probe(struct platform_device *pdev)
 	up->ops = &serial_sprd_ops;
 	up->flags = UPF_BOOT_AUTOCONF;
 
-	clk = devm_clk_get(&pdev->dev, NULL);
-	if (!IS_ERR_OR_NULL(clk))
-		up->uartclk = clk_get_rate(clk);
+	ret = sprd_clk_init(up);
+	if (ret)
+		return ret;
 
 	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 	up->membase = devm_ioremap_resource(&pdev->dev, res);
-- 
1.7.9.5


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

* [PATCH v2 3/4] dt-bindings: serial: sprd: Add dma properties to support DMA mode
  2019-03-04  8:58 [PATCH v2 0/4] Add new features for the Spreadtrum serial controller Baolin Wang
  2019-03-04  8:58 ` [PATCH v2 1/4] dt-bindings: serial: sprd: Add clocks and clocks-names properties Baolin Wang
  2019-03-04  8:58 ` [PATCH v2 2/4] serial: sprd: Add power management for the Spreadtrum serial controller Baolin Wang
@ 2019-03-04  8:58 ` Baolin Wang
  2019-03-12 18:22   ` Rob Herring
  2019-03-04  8:58 ` [PATCH v2 4/4] serial: sprd: Add DMA mode support Baolin Wang
  2019-03-26 10:41 ` [PATCH v2 0/4] Add new features for the Spreadtrum serial controller Baolin Wang
  4 siblings, 1 reply; 9+ messages in thread
From: Baolin Wang @ 2019-03-04  8:58 UTC (permalink / raw)
  To: gregkh, jslaby, robh+dt, mark.rutland, orsonzhai, zhang.lyra
  Cc: baolin.wang, broonie, lanqing.liu, linux-serial, linux-kernel,
	devicetree

From: Lanqing Liu <lanqing.liu@unisoc.com>

This patch adds dmas and dma-names properties for the UART DMA mode.

Signed-off-by: Lanqing Liu <lanqing.liu@unisoc.com>
Signed-off-by: Baolin Wang <baolin.wang@linaro.org>
---
 .../devicetree/bindings/serial/sprd-uart.txt       |    6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/Documentation/devicetree/bindings/serial/sprd-uart.txt b/Documentation/devicetree/bindings/serial/sprd-uart.txt
index 6eb5863..9607dc6 100644
--- a/Documentation/devicetree/bindings/serial/sprd-uart.txt
+++ b/Documentation/devicetree/bindings/serial/sprd-uart.txt
@@ -15,12 +15,18 @@ Required properties:
   UART clock and source clock are optional properties, but enable clock
   is required.
 
+Optional properties:
+- dma-names: Should contain "rx" for receive and "tx" for transmit channels.
+- dmas: A list of dma specifiers, one for each entry in dma-names.
+
 Example:
 	uart0: serial@0 {
 		compatible = "sprd,sc9860-uart",
 			     "sprd,sc9836-uart";
 		reg = <0x0 0x100>;
 		interrupts = <GIC_SPI 2 IRQ_TYPE_LEVEL_HIGH>;
+		dma-names = "rx", "tx";
+		dmas = <&ap_dma 19>, <&ap_dma 20>;
 		clock-names = "enable", "uart", "source";
 		clocks = <&clk_ap_apb_gates 9>, <&clk_uart0>, <&ext_26m>;
 	};
-- 
1.7.9.5


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

* [PATCH v2 4/4] serial: sprd: Add DMA mode support
  2019-03-04  8:58 [PATCH v2 0/4] Add new features for the Spreadtrum serial controller Baolin Wang
                   ` (2 preceding siblings ...)
  2019-03-04  8:58 ` [PATCH v2 3/4] dt-bindings: serial: sprd: Add dma properties to support DMA mode Baolin Wang
@ 2019-03-04  8:58 ` Baolin Wang
  2019-03-26 10:41 ` [PATCH v2 0/4] Add new features for the Spreadtrum serial controller Baolin Wang
  4 siblings, 0 replies; 9+ messages in thread
From: Baolin Wang @ 2019-03-04  8:58 UTC (permalink / raw)
  To: gregkh, jslaby, robh+dt, mark.rutland, orsonzhai, zhang.lyra
  Cc: baolin.wang, broonie, lanqing.liu, linux-serial, linux-kernel,
	devicetree

From: Lanqing Liu <lanqing.liu@unisoc.com>

Add DMA mode support for the Spreadtrum serial controller.

Signed-off-by: Lanqing Liu <lanqing.liu@unisoc.com>
Signed-off-by: Baolin Wang <baolin.wang@linaro.org>
---
 drivers/tty/serial/sprd_serial.c |  440 ++++++++++++++++++++++++++++++++++++--
 1 file changed, 426 insertions(+), 14 deletions(-)

diff --git a/drivers/tty/serial/sprd_serial.c b/drivers/tty/serial/sprd_serial.c
index 8f45b66..6aebd77 100644
--- a/drivers/tty/serial/sprd_serial.c
+++ b/drivers/tty/serial/sprd_serial.c
@@ -10,6 +10,9 @@
 #include <linux/clk.h>
 #include <linux/console.h>
 #include <linux/delay.h>
+#include <linux/dmaengine.h>
+#include <linux/dma-mapping.h>
+#include <linux/dma/sprd-dma.h>
 #include <linux/io.h>
 #include <linux/ioport.h>
 #include <linux/kernel.h>
@@ -75,6 +78,7 @@
 
 /* control register 1 */
 #define SPRD_CTL1		0x001C
+#define SPRD_DMA_EN		BIT(15)
 #define RX_HW_FLOW_CTL_THLD	BIT(6)
 #define RX_HW_FLOW_CTL_EN	BIT(7)
 #define TX_HW_FLOW_CTL_EN	BIT(8)
@@ -86,6 +90,7 @@
 #define THLD_TX_EMPTY		0x40
 #define THLD_TX_EMPTY_SHIFT	8
 #define THLD_RX_FULL		0x40
+#define THLD_RX_FULL_MASK	GENMASK(6, 0)
 
 /* config baud rate register */
 #define SPRD_CLKD0		0x0024
@@ -102,15 +107,36 @@
 #define SPRD_IMSR_TIMEOUT	BIT(13)
 #define SPRD_DEFAULT_SOURCE_CLK	26000000
 
+#define SPRD_RX_DMA_STEP	1
+#define SPRD_RX_FIFO_FULL	1
+#define SPRD_TX_FIFO_FULL	0x20
+#define SPRD_UART_RX_SIZE	(UART_XMIT_SIZE / 4)
+
+struct sprd_uart_dma {
+	struct dma_chan *chn;
+	unsigned char *virt;
+	dma_addr_t phys_addr;
+	dma_cookie_t cookie;
+	u32 trans_len;
+	bool enable;
+};
+
 struct sprd_uart_port {
 	struct uart_port port;
 	char name[16];
 	struct clk *clk;
+	struct sprd_uart_dma tx_dma;
+	struct sprd_uart_dma rx_dma;
+	dma_addr_t pos;
+	unsigned char *rx_buf_tail;
 };
 
 static struct sprd_uart_port *sprd_port[UART_NR_MAX];
 static int sprd_ports_num;
 
+static int sprd_start_dma_rx(struct uart_port *port);
+static int sprd_tx_dma_config(struct uart_port *port);
+
 static inline unsigned int serial_in(struct uart_port *port,
 				     unsigned int offset)
 {
@@ -141,45 +167,389 @@ static void sprd_set_mctrl(struct uart_port *port, unsigned int mctrl)
 	/* nothing to do */
 }
 
-static void sprd_stop_tx(struct uart_port *port)
+static void sprd_stop_rx(struct uart_port *port)
 {
+	struct sprd_uart_port *sp =
+		container_of(port, struct sprd_uart_port, port);
 	unsigned int ien, iclr;
 
+	if (sp->rx_dma.enable)
+		dmaengine_terminate_all(sp->rx_dma.chn);
+
 	iclr = serial_in(port, SPRD_ICLR);
 	ien = serial_in(port, SPRD_IEN);
 
-	iclr |= SPRD_IEN_TX_EMPTY;
-	ien &= ~SPRD_IEN_TX_EMPTY;
+	ien &= ~(SPRD_IEN_RX_FULL | SPRD_IEN_BREAK_DETECT);
+	iclr |= SPRD_IEN_RX_FULL | SPRD_IEN_BREAK_DETECT;
 
-	serial_out(port, SPRD_ICLR, iclr);
 	serial_out(port, SPRD_IEN, ien);
+	serial_out(port, SPRD_ICLR, iclr);
 }
 
-static void sprd_start_tx(struct uart_port *port)
+static void sprd_uart_dma_enable(struct uart_port *port, bool enable)
 {
-	unsigned int ien;
+	u32 val = serial_in(port, SPRD_CTL1);
 
-	ien = serial_in(port, SPRD_IEN);
-	if (!(ien & SPRD_IEN_TX_EMPTY)) {
-		ien |= SPRD_IEN_TX_EMPTY;
-		serial_out(port, SPRD_IEN, ien);
+	if (enable)
+		val |= SPRD_DMA_EN;
+	else
+		val &= ~SPRD_DMA_EN;
+
+	serial_out(port, SPRD_CTL1, val);
+}
+
+static void sprd_stop_tx_dma(struct uart_port *port)
+{
+	struct sprd_uart_port *sp =
+		container_of(port, struct sprd_uart_port, port);
+	struct circ_buf *xmit = &port->state->xmit;
+	struct dma_tx_state state;
+	u32 trans_len;
+
+	dmaengine_pause(sp->tx_dma.chn);
+
+	dmaengine_tx_status(sp->tx_dma.chn, sp->tx_dma.cookie, &state);
+	if (state.residue) {
+		trans_len = state.residue - sp->tx_dma.phys_addr;
+		xmit->tail = (xmit->tail + trans_len) & (UART_XMIT_SIZE - 1);
+		port->icount.tx += trans_len;
+		dma_unmap_single(port->dev, sp->tx_dma.phys_addr,
+				 sp->tx_dma.trans_len, DMA_TO_DEVICE);
 	}
+
+	dmaengine_terminate_all(sp->tx_dma.chn);
+	sp->tx_dma.trans_len = 0;
 }
 
-static void sprd_stop_rx(struct uart_port *port)
+static int sprd_tx_buf_remap(struct uart_port *port)
+{
+	struct sprd_uart_port *sp =
+		container_of(port, struct sprd_uart_port, port);
+	struct circ_buf *xmit = &port->state->xmit;
+
+	sp->tx_dma.trans_len =
+		CIRC_CNT_TO_END(xmit->head, xmit->tail, UART_XMIT_SIZE);
+
+	sp->tx_dma.phys_addr = dma_map_single(port->dev,
+					      (void *)&(xmit->buf[xmit->tail]),
+					      sp->tx_dma.trans_len,
+					      DMA_TO_DEVICE);
+	return dma_mapping_error(port->dev, sp->tx_dma.phys_addr);
+}
+
+static void sprd_complete_tx_dma(void *data)
+{
+	struct uart_port *port = (struct uart_port *)data;
+	struct sprd_uart_port *sp =
+		container_of(port, struct sprd_uart_port, port);
+	struct circ_buf *xmit = &port->state->xmit;
+	unsigned long flags;
+
+	spin_lock_irqsave(&port->lock, flags);
+	dma_unmap_single(port->dev, sp->tx_dma.phys_addr,
+			 sp->tx_dma.trans_len, DMA_TO_DEVICE);
+
+	xmit->tail = (xmit->tail + sp->tx_dma.trans_len) & (UART_XMIT_SIZE - 1);
+	port->icount.tx += sp->tx_dma.trans_len;
+
+	if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
+		uart_write_wakeup(port);
+
+	if (uart_circ_empty(xmit) || sprd_tx_buf_remap(port) ||
+	    sprd_tx_dma_config(port))
+		sp->tx_dma.trans_len = 0;
+
+	spin_unlock_irqrestore(&port->lock, flags);
+}
+
+static int sprd_uart_dma_submit(struct uart_port *port,
+				struct sprd_uart_dma *ud, u32 trans_len,
+				enum dma_transfer_direction direction,
+				dma_async_tx_callback callback)
 {
+	struct dma_async_tx_descriptor *dma_des;
+	unsigned long flags;
+
+	flags = SPRD_DMA_FLAGS(SPRD_DMA_CHN_MODE_NONE,
+			       SPRD_DMA_NO_TRG,
+			       SPRD_DMA_FRAG_REQ,
+			       SPRD_DMA_TRANS_INT);
+
+	dma_des = dmaengine_prep_slave_single(ud->chn, ud->phys_addr, trans_len,
+					      direction, flags);
+	if (!dma_des)
+		return -ENODEV;
+
+	dma_des->callback = callback;
+	dma_des->callback_param = port;
+
+	ud->cookie = dmaengine_submit(dma_des);
+	if (dma_submit_error(ud->cookie))
+		return dma_submit_error(ud->cookie);
+
+	dma_async_issue_pending(ud->chn);
+
+	return 0;
+}
+
+static int sprd_tx_dma_config(struct uart_port *port)
+{
+	struct sprd_uart_port *sp =
+		container_of(port, struct sprd_uart_port, port);
+	u32 burst = sp->tx_dma.trans_len > SPRD_TX_FIFO_FULL ?
+		SPRD_TX_FIFO_FULL : sp->tx_dma.trans_len;
+	int ret;
+	struct dma_slave_config cfg = {
+		.dst_addr = port->mapbase + SPRD_TXD,
+		.src_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE,
+		.dst_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE,
+		.src_maxburst = burst,
+	};
+
+	ret = dmaengine_slave_config(sp->tx_dma.chn, &cfg);
+	if (ret < 0)
+		return ret;
+
+	return sprd_uart_dma_submit(port, &sp->tx_dma, sp->tx_dma.trans_len,
+				    DMA_MEM_TO_DEV, sprd_complete_tx_dma);
+}
+
+static void sprd_start_tx_dma(struct uart_port *port)
+{
+	struct sprd_uart_port *sp =
+		container_of(port, struct sprd_uart_port, port);
+	struct circ_buf *xmit = &port->state->xmit;
+
+	if (port->x_char) {
+		serial_out(port, SPRD_TXD, port->x_char);
+		port->icount.tx++;
+		port->x_char = 0;
+		return;
+	}
+
+	if (uart_circ_empty(xmit) || uart_tx_stopped(port)) {
+		sprd_stop_tx_dma(port);
+		return;
+	}
+
+	if (sp->tx_dma.trans_len)
+		return;
+
+	if (sprd_tx_buf_remap(port) || sprd_tx_dma_config(port))
+		sp->tx_dma.trans_len = 0;
+}
+
+static void sprd_rx_full_thld(struct uart_port *port, u32 thld)
+{
+	u32 val = serial_in(port, SPRD_CTL2);
+
+	val &= ~THLD_RX_FULL_MASK;
+	val |= thld & THLD_RX_FULL_MASK;
+	serial_out(port, SPRD_CTL2, val);
+}
+
+static int sprd_rx_alloc_buf(struct sprd_uart_port *sp)
+{
+	sp->rx_dma.virt = dma_alloc_coherent(sp->port.dev, SPRD_UART_RX_SIZE,
+					     &sp->rx_dma.phys_addr, GFP_KERNEL);
+	if (!sp->rx_dma.virt)
+		return -ENOMEM;
+
+	return 0;
+}
+
+static void sprd_rx_free_buf(struct sprd_uart_port *sp)
+{
+	if (sp->rx_dma.virt)
+		dma_free_coherent(sp->port.dev, SPRD_UART_RX_SIZE,
+				  sp->rx_dma.virt, sp->rx_dma.phys_addr);
+
+}
+
+static int sprd_rx_dma_config(struct uart_port *port, u32 burst)
+{
+	struct sprd_uart_port *sp =
+		container_of(port, struct sprd_uart_port, port);
+	struct dma_slave_config cfg = {
+		.src_addr = port->mapbase + SPRD_RXD,
+		.src_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE,
+		.dst_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE,
+		.src_maxburst = burst,
+	};
+
+	return dmaengine_slave_config(sp->rx_dma.chn, &cfg);
+}
+
+static void sprd_uart_dma_rx(struct uart_port *port)
+{
+	struct sprd_uart_port *sp =
+		container_of(port, struct sprd_uart_port, port);
+	struct tty_port *tty = &port->state->port;
+
+	port->icount.rx += sp->rx_dma.trans_len;
+	tty_insert_flip_string(tty, sp->rx_buf_tail, sp->rx_dma.trans_len);
+	tty_flip_buffer_push(tty);
+}
+
+static void sprd_uart_dma_irq(struct uart_port *port)
+{
+	struct sprd_uart_port *sp =
+		container_of(port, struct sprd_uart_port, port);
+	struct dma_tx_state state;
+	enum dma_status status;
+
+	status = dmaengine_tx_status(sp->rx_dma.chn,
+				     sp->rx_dma.cookie, &state);
+	if (status == DMA_ERROR)
+		sprd_stop_rx(port);
+
+	if (!state.residue && sp->pos == sp->rx_dma.phys_addr)
+		return;
+
+	if (!state.residue) {
+		sp->rx_dma.trans_len = SPRD_UART_RX_SIZE +
+			sp->rx_dma.phys_addr - sp->pos;
+		sp->pos = sp->rx_dma.phys_addr;
+	} else {
+		sp->rx_dma.trans_len = state.residue - sp->pos;
+		sp->pos = state.residue;
+	}
+
+	sprd_uart_dma_rx(port);
+	sp->rx_buf_tail += sp->rx_dma.trans_len;
+}
+
+static void sprd_complete_rx_dma(void *data)
+{
+	struct uart_port *port = (struct uart_port *)data;
+	struct sprd_uart_port *sp =
+		container_of(port, struct sprd_uart_port, port);
+	struct dma_tx_state state;
+	enum dma_status status;
+	unsigned long flags;
+
+	spin_lock_irqsave(&port->lock, flags);
+
+	status = dmaengine_tx_status(sp->rx_dma.chn,
+				     sp->rx_dma.cookie, &state);
+	if (status != DMA_COMPLETE) {
+		sprd_stop_rx(port);
+		spin_unlock_irqrestore(&port->lock, flags);
+		return;
+	}
+
+	if (sp->pos != sp->rx_dma.phys_addr) {
+		sp->rx_dma.trans_len =  SPRD_UART_RX_SIZE +
+			sp->rx_dma.phys_addr - sp->pos;
+		sprd_uart_dma_rx(port);
+		sp->rx_buf_tail += sp->rx_dma.trans_len;
+	}
+
+	if (sprd_start_dma_rx(port))
+		sprd_stop_rx(port);
+
+	spin_unlock_irqrestore(&port->lock, flags);
+}
+
+static int sprd_start_dma_rx(struct uart_port *port)
+{
+	struct sprd_uart_port *sp =
+		container_of(port, struct sprd_uart_port, port);
+	int ret;
+
+	if (!sp->rx_dma.enable)
+		return 0;
+
+	sp->pos = sp->rx_dma.phys_addr;
+	sp->rx_buf_tail = sp->rx_dma.virt;
+	sprd_rx_full_thld(port, SPRD_RX_FIFO_FULL);
+	ret = sprd_rx_dma_config(port, SPRD_RX_DMA_STEP);
+	if (ret)
+		return ret;
+
+	return sprd_uart_dma_submit(port, &sp->rx_dma, SPRD_UART_RX_SIZE,
+				    DMA_DEV_TO_MEM, sprd_complete_rx_dma);
+}
+
+static void sprd_release_dma(struct uart_port *port)
+{
+	struct sprd_uart_port *sp =
+		container_of(port, struct sprd_uart_port, port);
+
+	sprd_uart_dma_enable(port, false);
+
+	if (sp->rx_dma.enable)
+		dma_release_channel(sp->rx_dma.chn);
+
+	if (sp->tx_dma.enable)
+		dma_release_channel(sp->tx_dma.chn);
+
+	sp->tx_dma.enable = false;
+	sp->rx_dma.enable = false;
+}
+
+static void sprd_request_dma(struct uart_port *port)
+{
+	struct sprd_uart_port *sp =
+		container_of(port, struct sprd_uart_port, port);
+
+	sp->tx_dma.enable = true;
+	sp->rx_dma.enable = true;
+
+	sp->tx_dma.chn = dma_request_chan(port->dev, "tx");
+	if (IS_ERR(sp->tx_dma.chn)) {
+		dev_err(port->dev, "request TX DMA channel failed, ret = %ld\n",
+			PTR_ERR(sp->tx_dma.chn));
+		sp->tx_dma.enable = false;
+	}
+
+	sp->rx_dma.chn = dma_request_chan(port->dev, "rx");
+	if (IS_ERR(sp->rx_dma.chn)) {
+		dev_err(port->dev, "request RX DMA channel failed, ret = %ld\n",
+			PTR_ERR(sp->tx_dma.chn));
+		sp->rx_dma.enable = false;
+	}
+}
+
+static void sprd_stop_tx(struct uart_port *port)
+{
+	struct sprd_uart_port *sp = container_of(port, struct sprd_uart_port,
+						 port);
 	unsigned int ien, iclr;
 
+	if (sp->tx_dma.enable) {
+		sprd_stop_tx_dma(port);
+		return;
+	}
+
 	iclr = serial_in(port, SPRD_ICLR);
 	ien = serial_in(port, SPRD_IEN);
 
-	ien &= ~(SPRD_IEN_RX_FULL | SPRD_IEN_BREAK_DETECT);
-	iclr |= SPRD_IEN_RX_FULL | SPRD_IEN_BREAK_DETECT;
+	iclr |= SPRD_IEN_TX_EMPTY;
+	ien &= ~SPRD_IEN_TX_EMPTY;
 
 	serial_out(port, SPRD_IEN, ien);
 	serial_out(port, SPRD_ICLR, iclr);
 }
 
+static void sprd_start_tx(struct uart_port *port)
+{
+	struct sprd_uart_port *sp = container_of(port, struct sprd_uart_port,
+						 port);
+	unsigned int ien;
+
+	if (sp->tx_dma.enable) {
+		sprd_start_tx_dma(port);
+		return;
+	}
+
+	ien = serial_in(port, SPRD_IEN);
+	if (!(ien & SPRD_IEN_TX_EMPTY)) {
+		ien |= SPRD_IEN_TX_EMPTY;
+		serial_out(port, SPRD_IEN, ien);
+	}
+}
+
 /* The Sprd serial does not support this function. */
 static void sprd_break_ctl(struct uart_port *port, int break_state)
 {
@@ -220,9 +590,16 @@ static int handle_lsr_errors(struct uart_port *port,
 
 static inline void sprd_rx(struct uart_port *port)
 {
+	struct sprd_uart_port *sp = container_of(port, struct sprd_uart_port,
+						 port);
 	struct tty_port *tty = &port->state->port;
 	unsigned int ch, flag, lsr, max_count = SPRD_TIMEOUT;
 
+	if (sp->rx_dma.enable) {
+		sprd_uart_dma_irq(port);
+		return;
+	}
+
 	while ((serial_in(port, SPRD_STS1) & SPRD_RX_FIFO_CNT_MASK) &&
 	       max_count--) {
 		lsr = serial_in(port, SPRD_LSR);
@@ -306,6 +683,25 @@ static irqreturn_t sprd_handle_irq(int irq, void *dev_id)
 	return IRQ_HANDLED;
 }
 
+static void sprd_uart_dma_startup(struct uart_port *port,
+				  struct sprd_uart_port *sp)
+{
+	int ret;
+
+	sprd_request_dma(port);
+	if (!(sp->rx_dma.enable || sp->tx_dma.enable))
+		return;
+
+	ret = sprd_start_dma_rx(port);
+	if (ret) {
+		sp->rx_dma.enable = false;
+		dma_release_channel(sp->rx_dma.chn);
+		dev_warn(port->dev, "fail to start RX dma mode\n");
+	}
+
+	sprd_uart_dma_enable(port, true);
+}
+
 static int sprd_startup(struct uart_port *port)
 {
 	int ret = 0;
@@ -334,6 +730,9 @@ static int sprd_startup(struct uart_port *port)
 	/* allocate irq */
 	sp = container_of(port, struct sprd_uart_port, port);
 	snprintf(sp->name, sizeof(sp->name), "sprd_serial%d", port->line);
+
+	sprd_uart_dma_startup(port, sp);
+
 	ret = devm_request_irq(port->dev, port->irq, sprd_handle_irq,
 			       IRQF_SHARED, sp->name, port);
 	if (ret) {
@@ -348,7 +747,9 @@ static int sprd_startup(struct uart_port *port)
 	/* enable interrupt */
 	spin_lock_irqsave(&port->lock, flags);
 	ien = serial_in(port, SPRD_IEN);
-	ien |= SPRD_IEN_RX_FULL | SPRD_IEN_BREAK_DETECT | SPRD_IEN_TIMEOUT;
+	ien |= SPRD_IEN_BREAK_DETECT | SPRD_IEN_TIMEOUT;
+	if (!sp->rx_dma.enable)
+		ien |= SPRD_IEN_RX_FULL;
 	serial_out(port, SPRD_IEN, ien);
 	spin_unlock_irqrestore(&port->lock, flags);
 
@@ -357,6 +758,7 @@ static int sprd_startup(struct uart_port *port)
 
 static void sprd_shutdown(struct uart_port *port)
 {
+	sprd_release_dma(port);
 	serial_out(port, SPRD_IEN, 0);
 	serial_out(port, SPRD_ICLR, ~0);
 	devm_free_irq(port->dev, port->irq, port);
@@ -687,6 +1089,8 @@ static int sprd_remove(struct platform_device *dev)
 	if (!sprd_ports_num)
 		uart_unregister_driver(&sprd_uart_driver);
 
+	sprd_rx_free_buf(sup);
+
 	return 0;
 }
 
@@ -775,6 +1179,14 @@ static int sprd_probe(struct platform_device *pdev)
 	}
 	up->irq = irq;
 
+	/*
+	 * Allocate one dma buffer to prepare for receive transfer, in case
+	 * memory allocation failure at runtime.
+	 */
+	ret = sprd_rx_alloc_buf(sprd_port[index]);
+	if (ret)
+		return ret;
+
 	if (!sprd_ports_num) {
 		ret = uart_register_driver(&sprd_uart_driver);
 		if (ret < 0) {
-- 
1.7.9.5


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

* Re: [PATCH v2 3/4] dt-bindings: serial: sprd: Add dma properties to support DMA mode
  2019-03-04  8:58 ` [PATCH v2 3/4] dt-bindings: serial: sprd: Add dma properties to support DMA mode Baolin Wang
@ 2019-03-12 18:22   ` Rob Herring
  0 siblings, 0 replies; 9+ messages in thread
From: Rob Herring @ 2019-03-12 18:22 UTC (permalink / raw)
  To: Baolin Wang
  Cc: gregkh, jslaby, robh+dt, mark.rutland, orsonzhai, zhang.lyra,
	baolin.wang, broonie, lanqing.liu, linux-serial, linux-kernel,
	devicetree

On Mon,  4 Mar 2019 16:58:23 +0800, Baolin Wang wrote:
> From: Lanqing Liu <lanqing.liu@unisoc.com>
> 
> This patch adds dmas and dma-names properties for the UART DMA mode.
> 
> Signed-off-by: Lanqing Liu <lanqing.liu@unisoc.com>
> Signed-off-by: Baolin Wang <baolin.wang@linaro.org>
> ---
>  .../devicetree/bindings/serial/sprd-uart.txt       |    6 ++++++
>  1 file changed, 6 insertions(+)
> 

Reviewed-by: Rob Herring <robh@kernel.org>

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

* Re: [PATCH v2 0/4] Add new features for the Spreadtrum serial controller
  2019-03-04  8:58 [PATCH v2 0/4] Add new features for the Spreadtrum serial controller Baolin Wang
                   ` (3 preceding siblings ...)
  2019-03-04  8:58 ` [PATCH v2 4/4] serial: sprd: Add DMA mode support Baolin Wang
@ 2019-03-26 10:41 ` Baolin Wang
  2019-03-27  0:59   ` Greg KH
  4 siblings, 1 reply; 9+ messages in thread
From: Baolin Wang @ 2019-03-26 10:41 UTC (permalink / raw)
  To: Greg KH, jslaby, Rob Herring, Mark Rutland, Orson Zhai, Chunyan Zhang
  Cc: Mark Brown, lanqing.liu, linux-serial, LKML, DTML

Hi Greg,

On Mon, 4 Mar 2019 at 16:59, Baolin Wang <baolin.wang@linaro.org> wrote:
>
> This patch set fixes the baud rate calculation formula issue, as well as
> adding power management support and DMA mode support for the Spreadtrum
> serial controller.
>
> Changes from v1:
>  - The patch 1 of V1 was applied, so remove the baud rate fix from this patch set.
>  - Add reviewed tag from Rob for patch 1.
>  - Fix the incorrect names' order in patch 3.

Could you apply this patch set if no objection from you? Thanks.

-- 
Baolin Wang
Best Regards

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

* Re: [PATCH v2 0/4] Add new features for the Spreadtrum serial controller
  2019-03-26 10:41 ` [PATCH v2 0/4] Add new features for the Spreadtrum serial controller Baolin Wang
@ 2019-03-27  0:59   ` Greg KH
  2019-03-27  1:59     ` Baolin Wang
  0 siblings, 1 reply; 9+ messages in thread
From: Greg KH @ 2019-03-27  0:59 UTC (permalink / raw)
  To: Baolin Wang
  Cc: jslaby, Rob Herring, Mark Rutland, Orson Zhai, Chunyan Zhang,
	Mark Brown, lanqing.liu, linux-serial, LKML, DTML

On Tue, Mar 26, 2019 at 06:41:31PM +0800, Baolin Wang wrote:
> Hi Greg,
> 
> On Mon, 4 Mar 2019 at 16:59, Baolin Wang <baolin.wang@linaro.org> wrote:
> >
> > This patch set fixes the baud rate calculation formula issue, as well as
> > adding power management support and DMA mode support for the Spreadtrum
> > serial controller.
> >
> > Changes from v1:
> >  - The patch 1 of V1 was applied, so remove the baud rate fix from this patch set.
> >  - Add reviewed tag from Rob for patch 1.
> >  - Fix the incorrect names' order in patch 3.
> 
> Could you apply this patch set if no objection from you? Thanks.

It's in my to-review queue, sorry, will get to it soon...

greg k-h

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

* Re: [PATCH v2 0/4] Add new features for the Spreadtrum serial controller
  2019-03-27  0:59   ` Greg KH
@ 2019-03-27  1:59     ` Baolin Wang
  0 siblings, 0 replies; 9+ messages in thread
From: Baolin Wang @ 2019-03-27  1:59 UTC (permalink / raw)
  To: Greg KH
  Cc: jslaby, Rob Herring, Mark Rutland, Orson Zhai, Chunyan Zhang,
	Mark Brown, lanqing.liu, linux-serial, LKML, DTML

On Wed, 27 Mar 2019 at 08:59, Greg KH <gregkh@linuxfoundation.org> wrote:
>
> On Tue, Mar 26, 2019 at 06:41:31PM +0800, Baolin Wang wrote:
> > Hi Greg,
> >
> > On Mon, 4 Mar 2019 at 16:59, Baolin Wang <baolin.wang@linaro.org> wrote:
> > >
> > > This patch set fixes the baud rate calculation formula issue, as well as
> > > adding power management support and DMA mode support for the Spreadtrum
> > > serial controller.
> > >
> > > Changes from v1:
> > >  - The patch 1 of V1 was applied, so remove the baud rate fix from this patch set.
> > >  - Add reviewed tag from Rob for patch 1.
> > >  - Fix the incorrect names' order in patch 3.
> >
> > Could you apply this patch set if no objection from you? Thanks.
>
> It's in my to-review queue, sorry, will get to it soon...

Thanks :)

--
Baolin Wang
Best Regards

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

end of thread, other threads:[~2019-03-27  2:00 UTC | newest]

Thread overview: 9+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2019-03-04  8:58 [PATCH v2 0/4] Add new features for the Spreadtrum serial controller Baolin Wang
2019-03-04  8:58 ` [PATCH v2 1/4] dt-bindings: serial: sprd: Add clocks and clocks-names properties Baolin Wang
2019-03-04  8:58 ` [PATCH v2 2/4] serial: sprd: Add power management for the Spreadtrum serial controller Baolin Wang
2019-03-04  8:58 ` [PATCH v2 3/4] dt-bindings: serial: sprd: Add dma properties to support DMA mode Baolin Wang
2019-03-12 18:22   ` Rob Herring
2019-03-04  8:58 ` [PATCH v2 4/4] serial: sprd: Add DMA mode support Baolin Wang
2019-03-26 10:41 ` [PATCH v2 0/4] Add new features for the Spreadtrum serial controller Baolin Wang
2019-03-27  0:59   ` Greg KH
2019-03-27  1:59     ` Baolin Wang

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).