2019-03-04 08:59:57

by Baolin Wang

[permalink] [raw]
Subject: [PATCH v2 0/4] Add new features for the Spreadtrum serial controller

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



2019-03-04 08:59:59

by Baolin Wang

[permalink] [raw]
Subject: [PATCH v2 1/4] dt-bindings: serial: sprd: Add clocks and clocks-names properties

From: Lanqing Liu <[email protected]>

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

Reviewed-by: Rob Herring <[email protected]>
Signed-off-by: Lanqing Liu <[email protected]>
Signed-off-by: Baolin Wang <[email protected]>
---
.../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


2019-03-04 09:00:11

by Baolin Wang

[permalink] [raw]
Subject: [PATCH v2 2/4] serial: sprd: Add power management for the Spreadtrum serial controller

From: Lanqing Liu <[email protected]>

This patch adds power management for the Spreadtrum serial controller.

Signed-off-by: Lanqing Liu <[email protected]>
Signed-off-by: Baolin Wang <[email protected]>
---
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


2019-03-04 09:00:17

by Baolin Wang

[permalink] [raw]
Subject: [PATCH v2 3/4] dt-bindings: serial: sprd: Add dma properties to support DMA mode

From: Lanqing Liu <[email protected]>

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

Signed-off-by: Lanqing Liu <[email protected]>
Signed-off-by: Baolin Wang <[email protected]>
---
.../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


2019-03-04 09:02:03

by Baolin Wang

[permalink] [raw]
Subject: [PATCH v2 4/4] serial: sprd: Add DMA mode support

From: Lanqing Liu <[email protected]>

Add DMA mode support for the Spreadtrum serial controller.

Signed-off-by: Lanqing Liu <[email protected]>
Signed-off-by: Baolin Wang <[email protected]>
---
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


2019-03-12 18:22:53

by Rob Herring (Arm)

[permalink] [raw]
Subject: Re: [PATCH v2 3/4] dt-bindings: serial: sprd: Add dma properties to support DMA mode

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

Reviewed-by: Rob Herring <[email protected]>

2019-03-26 10:42:55

by Baolin Wang

[permalink] [raw]
Subject: Re: [PATCH v2 0/4] Add new features for the Spreadtrum serial controller

Hi Greg,

On Mon, 4 Mar 2019 at 16:59, Baolin Wang <[email protected]> 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

2019-03-27 01:00:50

by Greg Kroah-Hartman

[permalink] [raw]
Subject: Re: [PATCH v2 0/4] Add new features for the Spreadtrum serial controller

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 <[email protected]> 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

2019-03-27 02:00:58

by Baolin Wang

[permalink] [raw]
Subject: Re: [PATCH v2 0/4] Add new features for the Spreadtrum serial controller

On Wed, 27 Mar 2019 at 08:59, Greg KH <[email protected]> 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 <[email protected]> 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