[PATCH v2 3/4] serial: 8250-mtk: add uart DMA support

2018-12-05 Thread Long Cheng
Modify uart register to support DMA function.

Signed-off-by: Long Cheng 
---
 drivers/tty/serial/8250/8250_mtk.c |  210 +++-
 1 file changed, 209 insertions(+), 1 deletion(-)

diff --git a/drivers/tty/serial/8250/8250_mtk.c 
b/drivers/tty/serial/8250/8250_mtk.c
index dd5e1ce..1da73e8 100644
--- a/drivers/tty/serial/8250/8250_mtk.c
+++ b/drivers/tty/serial/8250/8250_mtk.c
@@ -14,6 +14,10 @@
 #include 
 #include 
 #include 
+#include 
+#include 
+#include 
+#include 
 
 #include "8250.h"
 
@@ -22,12 +26,172 @@
 #define UART_MTK_SAMPLE_POINT  0x0b/* Sample point register */
 #define MTK_UART_RATE_FIX  0x0d/* UART Rate Fix Register */
 
+#define MTK_UART_DMA_EN0x13/* DMA Enable register */
+#define MTK_UART_DMA_EN_TX 0x2
+#define MTK_UART_DMA_EN_RX 0x5
+
+#define MTK_UART_TX_SIZE   UART_XMIT_SIZE
+#define MTK_UART_RX_SIZE   0x8000
+#define MTK_UART_TX_TRIGGER1
+#define MTK_UART_RX_TRIGGERMTK_UART_RX_SIZE
+
+#ifdef CONFIG_SERIAL_8250_DMA
+enum dma_rx_status {
+   DMA_RX_START = 0,
+   DMA_RX_RUNNING = 1,
+   DMA_RX_SHUTDOWN = 2,
+};
+#endif
+
 struct mtk8250_data {
int line;
+   unsigned intrx_pos;
struct clk  *uart_clk;
struct clk  *bus_clk;
+   struct uart_8250_dma*dma;
+#ifdef CONFIG_SERIAL_8250_DMA
+   enum dma_rx_status  rx_status;
+#endif
 };
 
+#ifdef CONFIG_SERIAL_8250_DMA
+static void mtk8250_rx_dma(struct uart_8250_port *up);
+
+static void mtk8250_dma_rx_complete(void *param)
+{
+   struct uart_8250_port *up = param;
+   struct uart_8250_dma *dma = up->dma;
+   struct mtk8250_data *data = up->port.private_data;
+   struct tty_port *tty_port = >port.state->port;
+   struct dma_tx_state state;
+   unsigned char *ptr;
+   int copied;
+
+   dma_sync_single_for_cpu(dma->rxchan->device->dev, dma->rx_addr,
+   dma->rx_size, DMA_FROM_DEVICE);
+
+   dmaengine_tx_status(dma->rxchan, dma->rx_cookie, );
+
+   if (data->rx_status == DMA_RX_SHUTDOWN)
+   return;
+
+   if ((data->rx_pos + state.residue) <= dma->rx_size) {
+   ptr = (unsigned char *)(data->rx_pos + dma->rx_buf);
+   copied = tty_insert_flip_string(tty_port, ptr, state.residue);
+   } else {
+   ptr = (unsigned char *)(data->rx_pos + dma->rx_buf);
+   copied = tty_insert_flip_string(tty_port, ptr,
+   dma->rx_size - data->rx_pos);
+   ptr = (unsigned char *)(dma->rx_buf);
+   copied += tty_insert_flip_string(tty_port, ptr,
+   data->rx_pos + state.residue - dma->rx_size);
+   }
+   up->port.icount.rx += copied;
+
+   tty_flip_buffer_push(tty_port);
+
+   mtk8250_rx_dma(up);
+}
+
+static void mtk8250_rx_dma(struct uart_8250_port *up)
+{
+   struct uart_8250_dma *dma = up->dma;
+   struct mtk8250_data *data = up->port.private_data;
+   struct dma_async_tx_descriptor  *desc;
+   struct dma_tx_state  state;
+
+   desc = dmaengine_prep_slave_single(dma->rxchan, dma->rx_addr,
+  dma->rx_size, DMA_DEV_TO_MEM,
+  DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
+   if (!desc) {
+   pr_err("failed to prepare rx slave single\n");
+   return;
+   }
+
+   desc->callback = mtk8250_dma_rx_complete;
+   desc->callback_param = up;
+
+   dma->rx_cookie = dmaengine_submit(desc);
+
+   dmaengine_tx_status(dma->rxchan, dma->rx_cookie, );
+   data->rx_pos = state.residue;
+
+   dma_sync_single_for_device(dma->rxchan->device->dev, dma->rx_addr,
+  dma->rx_size, DMA_FROM_DEVICE);
+
+   dma_async_issue_pending(dma->rxchan);
+}
+
+static void mtk8250_dma_enable(struct uart_8250_port *up)
+{
+   struct uart_8250_dma *dma = up->dma;
+   struct mtk8250_data *data = up->port.private_data;
+   int lcr = serial_in(up, UART_LCR);
+
+   if (data->rx_status != DMA_RX_START)
+   return;
+
+   dma->rxconf.direction   = DMA_DEV_TO_MEM;
+   dma->rxconf.src_addr_width  = dma->rx_size / 1024;
+   dma->rxconf.src_addr= dma->rx_addr;
+
+   dma->txconf.direction   = DMA_MEM_TO_DEV;
+   dma->txconf.dst_addr_width  = MTK_UART_TX_SIZE / 1024;
+   dma->txconf.dst_addr= dma->tx_addr;
+
+   serial_out(up, UART_FCR, UART_FCR_ENABLE_FIFO | UART_FCR_CLEAR_RCVR |
+   UART_FCR_CLEAR_XMIT);
+   serial_out(up, MTK_UART_DMA_EN,
+  MTK_UART_DMA_EN_RX | MTK_UART_DMA_EN_TX);
+
+   serial_out(up, UART_LCR, UART_LCR_CONF_MODE_B);
+   serial_out(up, UART_EFR, UART_EFR_ECB);
+   serial_out(up, UART_LCR, lcr);
+
+   if 

[PATCH v2 3/4] serial: 8250-mtk: add uart DMA support

2018-12-05 Thread Long Cheng
Modify uart register to support DMA function.

Signed-off-by: Long Cheng 
---
 drivers/tty/serial/8250/8250_mtk.c |  210 +++-
 1 file changed, 209 insertions(+), 1 deletion(-)

diff --git a/drivers/tty/serial/8250/8250_mtk.c 
b/drivers/tty/serial/8250/8250_mtk.c
index dd5e1ce..1da73e8 100644
--- a/drivers/tty/serial/8250/8250_mtk.c
+++ b/drivers/tty/serial/8250/8250_mtk.c
@@ -14,6 +14,10 @@
 #include 
 #include 
 #include 
+#include 
+#include 
+#include 
+#include 
 
 #include "8250.h"
 
@@ -22,12 +26,172 @@
 #define UART_MTK_SAMPLE_POINT  0x0b/* Sample point register */
 #define MTK_UART_RATE_FIX  0x0d/* UART Rate Fix Register */
 
+#define MTK_UART_DMA_EN0x13/* DMA Enable register */
+#define MTK_UART_DMA_EN_TX 0x2
+#define MTK_UART_DMA_EN_RX 0x5
+
+#define MTK_UART_TX_SIZE   UART_XMIT_SIZE
+#define MTK_UART_RX_SIZE   0x8000
+#define MTK_UART_TX_TRIGGER1
+#define MTK_UART_RX_TRIGGERMTK_UART_RX_SIZE
+
+#ifdef CONFIG_SERIAL_8250_DMA
+enum dma_rx_status {
+   DMA_RX_START = 0,
+   DMA_RX_RUNNING = 1,
+   DMA_RX_SHUTDOWN = 2,
+};
+#endif
+
 struct mtk8250_data {
int line;
+   unsigned intrx_pos;
struct clk  *uart_clk;
struct clk  *bus_clk;
+   struct uart_8250_dma*dma;
+#ifdef CONFIG_SERIAL_8250_DMA
+   enum dma_rx_status  rx_status;
+#endif
 };
 
+#ifdef CONFIG_SERIAL_8250_DMA
+static void mtk8250_rx_dma(struct uart_8250_port *up);
+
+static void mtk8250_dma_rx_complete(void *param)
+{
+   struct uart_8250_port *up = param;
+   struct uart_8250_dma *dma = up->dma;
+   struct mtk8250_data *data = up->port.private_data;
+   struct tty_port *tty_port = >port.state->port;
+   struct dma_tx_state state;
+   unsigned char *ptr;
+   int copied;
+
+   dma_sync_single_for_cpu(dma->rxchan->device->dev, dma->rx_addr,
+   dma->rx_size, DMA_FROM_DEVICE);
+
+   dmaengine_tx_status(dma->rxchan, dma->rx_cookie, );
+
+   if (data->rx_status == DMA_RX_SHUTDOWN)
+   return;
+
+   if ((data->rx_pos + state.residue) <= dma->rx_size) {
+   ptr = (unsigned char *)(data->rx_pos + dma->rx_buf);
+   copied = tty_insert_flip_string(tty_port, ptr, state.residue);
+   } else {
+   ptr = (unsigned char *)(data->rx_pos + dma->rx_buf);
+   copied = tty_insert_flip_string(tty_port, ptr,
+   dma->rx_size - data->rx_pos);
+   ptr = (unsigned char *)(dma->rx_buf);
+   copied += tty_insert_flip_string(tty_port, ptr,
+   data->rx_pos + state.residue - dma->rx_size);
+   }
+   up->port.icount.rx += copied;
+
+   tty_flip_buffer_push(tty_port);
+
+   mtk8250_rx_dma(up);
+}
+
+static void mtk8250_rx_dma(struct uart_8250_port *up)
+{
+   struct uart_8250_dma *dma = up->dma;
+   struct mtk8250_data *data = up->port.private_data;
+   struct dma_async_tx_descriptor  *desc;
+   struct dma_tx_state  state;
+
+   desc = dmaengine_prep_slave_single(dma->rxchan, dma->rx_addr,
+  dma->rx_size, DMA_DEV_TO_MEM,
+  DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
+   if (!desc) {
+   pr_err("failed to prepare rx slave single\n");
+   return;
+   }
+
+   desc->callback = mtk8250_dma_rx_complete;
+   desc->callback_param = up;
+
+   dma->rx_cookie = dmaengine_submit(desc);
+
+   dmaengine_tx_status(dma->rxchan, dma->rx_cookie, );
+   data->rx_pos = state.residue;
+
+   dma_sync_single_for_device(dma->rxchan->device->dev, dma->rx_addr,
+  dma->rx_size, DMA_FROM_DEVICE);
+
+   dma_async_issue_pending(dma->rxchan);
+}
+
+static void mtk8250_dma_enable(struct uart_8250_port *up)
+{
+   struct uart_8250_dma *dma = up->dma;
+   struct mtk8250_data *data = up->port.private_data;
+   int lcr = serial_in(up, UART_LCR);
+
+   if (data->rx_status != DMA_RX_START)
+   return;
+
+   dma->rxconf.direction   = DMA_DEV_TO_MEM;
+   dma->rxconf.src_addr_width  = dma->rx_size / 1024;
+   dma->rxconf.src_addr= dma->rx_addr;
+
+   dma->txconf.direction   = DMA_MEM_TO_DEV;
+   dma->txconf.dst_addr_width  = MTK_UART_TX_SIZE / 1024;
+   dma->txconf.dst_addr= dma->tx_addr;
+
+   serial_out(up, UART_FCR, UART_FCR_ENABLE_FIFO | UART_FCR_CLEAR_RCVR |
+   UART_FCR_CLEAR_XMIT);
+   serial_out(up, MTK_UART_DMA_EN,
+  MTK_UART_DMA_EN_RX | MTK_UART_DMA_EN_TX);
+
+   serial_out(up, UART_LCR, UART_LCR_CONF_MODE_B);
+   serial_out(up, UART_EFR, UART_EFR_ECB);
+   serial_out(up, UART_LCR, lcr);
+
+   if