Signed-off-by: sudheer.v <[email protected]>
---
 drivers/tty/serial/8250/8250_aspeed_uart_dma.c | 1594 ++++++++++++++++++++++++
 1 file changed, 1594 insertions(+)
 create mode 100644 drivers/tty/serial/8250/8250_aspeed_uart_dma.c

diff --git a/drivers/tty/serial/8250/8250_aspeed_uart_dma.c 
b/drivers/tty/serial/8250/8250_aspeed_uart_dma.c
new file mode 100644
index 0000000..e1019a8
--- /dev/null
+++ b/drivers/tty/serial/8250/8250_aspeed_uart_dma.c
@@ -0,0 +1,1594 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ *   drivers/tty/serial/8250/8250_aspeed_uart_dma.c
+ *    1. 2018/07/01 Shivah Shankar created
+ *    2. 2018/08/25 sudheer.veliseti<[email protected]> modified
+ *
+ */
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/ioport.h>
+#include <linux/init.h>
+#include <linux/console.h>
+#include<linux/slab.h>
+#include <linux/delay.h>
+#include <linux/platform_device.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
+#include <linux/serial_reg.h>
+#include <linux/serial_core.h>
+#include <linux/serial.h>
+#include <linux/serial_8250.h>
+#include <linux/nmi.h>
+#include <linux/mutex.h>
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <asm/irq.h>
+
+#include "8250.h"
+#include <linux/dma-mapping.h>
+#define SDMA_RX_BUFF_SIZE                               0x10000 //65536
+#define DMA_BUFF_SIZE                           0x1000  //4096
+
+
+
+
+#undef UART_XMIT_SIZE
+#define UART_XMIT_SIZE 0x1000
+#define UART_RX_SIZE   0x10000
+
+#ifdef UART_DMA_DEBUG
+       #define UART_DBG(fmt, args...) pr_debug("%s() " fmt, __func__, ## args)
+#else
+       #define UART_DBG(fmt, args...)
+#endif
+
+#ifdef CONFIG_UART_TX_DMA_DEBUG
+       #define UART_TX_DBG(fmt, args...) pr_debug("%s()"fmt, __func__, ## args)
+#else
+       #define UART_TX_DBG(fmt, args...)
+#endif
+
+/*
+ * Configuration:
+ *   share_irqs - whether we pass IRQF_SHARED to request_irq().  This option
+ *                is unsafe when used on edge-triggered interrupts.
+ */
+static unsigned int share_irqs = SERIAL8250_SHARE_IRQS;
+
+static unsigned int nr_uarts = CONFIG_AST_RUNTIME_DMA_UARTS;
+
+/*
+ * Debugging.
+ */
+#if 0
+#define DEBUG_AUTOCONF(fmt...) UART_DBG(fmt)
+#else
+#define DEBUG_AUTOCONF(fmt...) do { } while (0)
+#endif
+
+#if 0
+#define DEBUG_INTR(fmt...)     UART_DBG(fmt)
+#else
+#define DEBUG_INTR(fmt...)     do { } while (0)
+#endif
+
+#define PASS_LIMIT     256
+
+#include <asm/serial.h>
+
+
+#define UART_DMA_NR            CONFIG_AST_NR_DMA_UARTS
+
+struct ast_uart_port {
+       struct uart_port        port;
+       struct platform_device  *pdev;
+       unsigned short          capabilities;   /* port capabilities */
+       unsigned short          bugs;           /* port bugs */
+       unsigned int            tx_loadsz;      /* transmit fifo load size */
+       unsigned char           acr;
+       unsigned char           ier;
+       unsigned char           lcr;
+       unsigned char           mcr;
+       unsigned char           mcr_mask;       /* mask of user bits */
+       unsigned char           mcr_force;      /* mask of forced bits */
+       unsigned int            channel_no;
+       struct scatterlist      rx_sgl;
+       struct dma_chan         *rx_dma_chan;
+       struct circ_buf         rx_dma_buf;
+       dma_addr_t              dma_rx_addr;
+       u8                      rx_in_progress;
+       struct dma_async_tx_descriptor          *rx_dma_desc;
+       dma_cookie_t            rx_cookie;
+       unsigned int            rx_bytes_requested;
+       unsigned int            rx_bytes_transferred;
+       struct tasklet_struct   rx_tasklet;
+       struct scatterlist      tx_sgl;
+       struct dma_chan         *tx_dma_chan;
+       struct circ_buf         tx_dma_buf;
+       dma_addr_t              dma_tx_addr;
+       u8                      tx_in_progress;
+       struct dma_async_tx_descriptor          *tx_dma_desc;
+       dma_cookie_t            tx_cookie;
+       unsigned int            tx_bytes_requested;
+       unsigned int            tx_bytes_transferred;
+       struct tasklet_struct   tx_tasklet;
+       spinlock_t lock;
+       int                     tx_done;
+       int                     tx_count;
+       /*
+        * Some bits in registers are cleared on a read, so they must
+        * be saved whenever the register is read but the bits will not
+        * be immediately processed.
+        */
+#define LSR_SAVE_FLAGS UART_LSR_BRK_ERROR_BITS
+       unsigned char           lsr_saved_flags;
+#define MSR_SAVE_FLAGS UART_MSR_ANY_DELTA
+       unsigned char           msr_saved_flags;
+
+       /*
+        * We provide a per-port pm hook.
+        */
+       void                    (*pm)(struct uart_port *port,
+                                     unsigned int state, unsigned int old);
+};
+
+static struct ast_uart_port ast_uart_ports[UART_DMA_NR];
+
+static int ast_dma_channel_setup(struct ast_uart_port *up);
+static inline struct ast_uart_port *
+to_ast_dma_uart_port(struct uart_port *uart)
+{
+       return container_of(uart, struct ast_uart_port, port);
+}
+
+struct irq_info {
+       spinlock_t              lock;
+       struct ast_uart_port *up;
+};
+
+static void ast_dma_channel_teardown(struct ast_uart_port *s);
+static struct irq_info ast_uart_irq[1];
+static DEFINE_MUTEX(ast_uart_mutex);
+
+/*
+ * Here we define the default xmit fifo size used for each type of UART.
+ */
+static const struct serial8250_config uart_config[] = {
+       [PORT_UNKNOWN] = {
+               .name           = "unknown",
+               .fifo_size      = 1,
+               .tx_loadsz      = 1,
+       },
+       [PORT_8250] = {
+               .name           = "8250",
+               .fifo_size      = 1,
+               .tx_loadsz      = 1,
+       },
+       [PORT_16450] = {
+               .name           = "16450",
+               .fifo_size      = 1,
+               .tx_loadsz      = 1,
+       },
+       [PORT_16550] = {
+               .name           = "16550",
+               .fifo_size      = 1,
+               .tx_loadsz      = 1,
+       },
+       [PORT_16550A] = {
+               .name           = "16550A",
+               .fifo_size      = 16,
+               .tx_loadsz      = 16,
+               .fcr            = UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_10
+                                                       | UART_FCR_DMA_SELECT,
+               .flags          = UART_CAP_FIFO,
+       },
+};
+
+/* sane hardware needs no mapping */
+#define map_8250_in_reg(up, offset) (offset)
+#define map_8250_out_reg(up, offset) (offset)
+
+static void ast_uart_unregister_port(int line);
+static int ast_uart_register_port(struct uart_port *port,
+                                       unsigned int channel_no);
+
+static unsigned int ast_serial_in(struct ast_uart_port *up, int offset)
+{
+       offset = map_8250_in_reg(up, offset) << up->port.regshift;
+
+               return readb(up->port.membase + offset);
+}
+
+static void
+ast_serial_out(struct ast_uart_port *up, int offset, int value)
+{
+       /* Save the offset before it's remapped */
+       offset = map_8250_out_reg(up, offset) << up->port.regshift;
+
+               writeb(value, up->port.membase + offset);
+}
+
+
+/*
+ * We used to support using pause I/O for certain machines.  We
+ * haven't supported this for a while, but just in case it's badly
+ * needed for certain old 386 machines, I've left these #define's
+ * in....
+ */
+#define serial_inp(up, offset)         ast_serial_in(up, offset)
+#define serial_outp(up, offset, value) ast_serial_out(up, offset, value)
+
+/* Uart divisor latch read */
+static inline int _serial_dl_read(struct ast_uart_port *up)
+{
+       return serial_inp(up, UART_DLL) | serial_inp(up, UART_DLM) << 8;
+}
+
+/* Uart divisor latch write */
+static inline void _serial_dl_write(struct ast_uart_port *up, int value)
+{
+       serial_outp(up, UART_DLL, value & 0xff);
+       serial_outp(up, UART_DLM, value >> 8 & 0xff);
+}
+
+#define serial_dl_read(up) _serial_dl_read(up)
+#define serial_dl_write(up, value) _serial_dl_write(up, value)
+
+static void ast_uart_tx_dma_complete(void *args);
+
+static int ast_uart_start_tx_dma(struct ast_uart_port *up,
+               unsigned long count)
+{
+       struct circ_buf *xmit = &up->port.state->xmit;
+       dma_addr_t tx_phys_addr;
+
+       UART_DBG("Entered %s count is %d\n", __func__, count);
+       UART_DBG("up->tx_cookie = %d\n", up->tx_cookie);
+       if (up->tx_cookie == 0) {
+               dma_sync_single_for_device(up->port.dev, up->dma_tx_addr,
+                                               UART_XMIT_SIZE, DMA_TO_DEVICE);
+       tx_phys_addr = up->dma_tx_addr + xmit->tail;
+       UART_DBG("Transmit address is %x actual is %x\n", tx_phys_addr,
+                                                       up->dma_tx_addr);
+       up->tx_dma_desc = dmaengine_prep_slave_single(up->tx_dma_chan,
+               tx_phys_addr, count, DMA_MEM_TO_DEV, DMA_PREP_INTERRUPT);
+       if (!up->tx_dma_desc) {
+               dev_err(up->port.dev, "Not able to get desc for Tx\n");
+               return -EIO;
+       }
+
+       up->tx_dma_desc->callback = ast_uart_tx_dma_complete;
+       up->tx_dma_desc->callback_param = up;
+       up->tx_in_progress = 1;
+       up->tx_bytes_requested = count;
+       up->tx_bytes_transferred = 0;
+       up->tx_cookie = dmaengine_submit(up->tx_dma_desc);
+       }
+       dma_async_issue_pending(up->tx_dma_chan);
+       return 0;
+}
+static void ast_uart_start_next_tx(struct ast_uart_port *up)
+{
+       unsigned long count;
+       struct circ_buf *xmit = &up->port.state->xmit;
+
+       count = CIRC_CNT_TO_END(xmit->head, xmit->tail, UART_XMIT_SIZE);
+       if (count)
+               ast_uart_start_tx_dma(up, count);
+}
+static void ast_uart_tx_sdma_tasklet_func(unsigned long data)
+{
+       struct ast_uart_port *up = ((struct ast_uart_port *)data);
+       struct circ_buf *xmit = &up->port.state->xmit;
+       unsigned long flags = 0;
+
+       UART_DBG("In %s bytes to send is %d\n", __func__, CIRC_CNT(xmit->head,
+       spin_lock_irqsave(&up->port.lock, flags);
+                                           xmit->tail, UART_XMIT_SIZE));
+       if (!uart_circ_empty(xmit) && !up->tx_in_progress) {
+               UART_DBG("Calling ast_uart_start_next_tx\n");
+               ast_uart_start_next_tx(up);
+       }
+       spin_unlock_irqrestore(&up->port.lock, flags);
+}
+
+static void ast_uart_tx_dma_complete(void *args)
+{
+       struct ast_uart_port *up = args;
+       struct circ_buf *xmit = &up->port.state->xmit;
+       struct dma_tx_state state;
+       unsigned long flags;
+       unsigned int count;
+       enum dma_status status;
+
+       status = dmaengine_tx_status(up->tx_dma_chan, up->tx_cookie, &state);
+       UART_DBG("%s:state.residue=%d\n", __func__, state.residue);
+       UART_DBG("up->tx_bytes_requested=%d up->tx_bytes_transferred=%d\n",
+                           up->tx_bytes_requested, up->tx_bytes_transferred);
+       if (status == DMA_COMPLETE) {
+               up->tx_cookie = 0;
+               count = up->tx_bytes_requested - up->tx_bytes_transferred;
+               UART_DBG("DMA_COMPLETE:bytes till end of Buffer=%d\n", count);
+       } else{
+               count = up->tx_bytes_requested - state.residue;
+               up->tx_bytes_transferred += count;
+               UART_DBG("DMA_not_COMPLETE: count=%d\n", count);
+       }
+       UART_DBG("up->tx_bytes_requested=%d up->tx_bytes_transferred=%d\n",
+                           up->tx_bytes_requested, up->tx_bytes_transferred);
+       UART_DBG("xmit->head=%d and xmit->tail=%d\n", xmit->head, xmit->tail);
+       async_tx_ack(up->tx_dma_desc);
+       spin_lock_irqsave(&up->port.lock, flags);
+       xmit->tail = (xmit->tail + count) & (UART_XMIT_SIZE - 1);
+       up->tx_in_progress = 0;
+       UART_DBG("updated xmit->head=%d and xmit->tail=%d\n",
+                                           xmit->head, xmit->tail);
+       if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
+               uart_write_wakeup(&up->port);
+       ast_uart_start_next_tx(up);
+       spin_unlock_irqrestore(&up->port.lock, flags);
+}
+
+static void ast_uart_rx_sdma_tasklet_func(unsigned long data);
+static void ast_uart_rx_dma_complete(void *args)
+{
+       struct ast_uart_port *up = args;
+       struct dma_tx_state state;
+       struct circ_buf *rx_ring = &up->rx_dma_buf;
+       unsigned int count;
+       unsigned int temp = 0;
+       enum dma_status status;
+
+       UART_DBG("line [%d],head = %d, len : %d\n",
+                               up->port.line, up->rx_dma_buf.head, count);
+       status = dmaengine_tx_status(up->rx_dma_chan, up->rx_cookie, &state);
+       UART_DBG("Freespace in buffer=%d\n", state.residue);
+       UART_DBG("up->rx_bytes_requested=%d up->rx_bytes_transferred=%d\n",
+                           up->rx_bytes_requested, up->rx_bytes_transferred);
+       if (status == DMA_COMPLETE) {
+               up->rx_cookie = 0;
+               count = up->rx_bytes_requested - up->rx_bytes_transferred;
+               up->rx_in_progress = 0;
+               UART_DBG("DMA_COMPLETE:bytes till end of Buffer=%d\n", count);
+       } else{
+               temp = up->rx_bytes_requested - state.residue;
+               count = temp - up->rx_bytes_transferred;
+               up->rx_bytes_transferred = temp;
+               UART_DBG("DMA_not_COMPLETE:fill index =%d\n", temp, count);
+               UART_DBG("bytes to be rxed in current lap=%d\n", count);
+               UART_DBG("rx_bytes_transfred=%d\n", up->rx_bytes_transferred);
+       }
+
+       UART_DBG("rx_ring->head=%d rx_ring->tail=%d\n",
+                                   rx_ring->head, rx_ring->tail);
+       rx_ring->head = (rx_ring->head + count) & (UART_RX_SIZE - 1);
+       UART_DBG("updated rx_ring->head=%d rx_ring->tail=%d\n",
+                                       rx_ring->head, rx_ring->tail);
+       ast_uart_rx_sdma_tasklet_func((unsigned long)up);
+}
+
+static int ast_uart_start_rx_dma(struct ast_uart_port *up,
+                                       unsigned long count)
+{
+       struct circ_buf *rx_ring = &up->rx_dma_buf;
+       dma_addr_t rx_phys_addr;
+
+       UART_DBG("%s:up->rx_dma_chan= %d\n", __func__, up->rx_dma_chan);
+       UART_DBG("up->rx_cookie = %d\n", up->rx_cookie);
+       if (up->rx_cookie == 0) {
+               dma_sync_single_for_device(up->port.dev, up->dma_rx_addr,
+                                       UART_RX_SIZE, DMA_FROM_DEVICE);
+               rx_phys_addr = up->dma_rx_addr + rx_ring->tail;
+               up->rx_dma_desc = dmaengine_prep_slave_single(up->rx_dma_chan,
+               rx_phys_addr, UART_RX_SIZE, DMA_DEV_TO_MEM, DMA_PREP_INTERRUPT);
+               if (!up->rx_dma_desc) {
+                       dev_err(up->port.dev, "Not able to get desc for Rx\n");
+                       return -EIO;
+               }
+               up->rx_dma_desc->callback = ast_uart_rx_dma_complete;
+               up->rx_dma_desc->callback_param = up;
+               up->rx_in_progress = 1;
+               up->rx_bytes_requested = UART_RX_SIZE;
+               up->rx_bytes_transferred = 0;
+               up->rx_cookie = dmaengine_submit(up->rx_dma_desc);
+       }
+       dma_async_issue_pending(up->rx_dma_chan);
+       return 0;
+}
+
+static void ast_uart_rx_sdma_tasklet_func(unsigned long data)
+{
+       struct ast_uart_port *up = ((struct ast_uart_port *)data);
+       struct tty_port *port = &up->port.state->port;
+       struct circ_buf *rx_ring = &up->rx_dma_buf;
+       unsigned long flags;
+       int count;
+       int copy = 0;
+
+       UART_DBG("line [%d], rx_ring->head = %d, rx_ring->tail = %d\n",
+                           up->port.line, rx_ring->head, rx_ring->tail);
+       spin_lock_irqsave(&up->port.lock, flags);
+       if (rx_ring->head > rx_ring->tail) {
+               count = rx_ring->head - rx_ring->tail;
+               UART_DBG("^^^^ count=%d rx_ring->head=%d rx_ring->tail=%d\n",
+                                       count, rx_ring->head, rx_ring->tail);
+               copy = tty_insert_flip_string(port,
+                                       rx_ring->buf + rx_ring->tail, count);
+       } else if (rx_ring->head < rx_ring->tail) {
+               count = SDMA_RX_BUFF_SIZE - rx_ring->tail;
+               UART_DBG("rollovr:count=%d rx_ring->head=%d rx_ring->tail=%d\n",
+                                   count, rx_ring->head, rx_ring->tail);
+               copy = tty_insert_flip_string(port,
+                                       rx_ring->buf + rx_ring->tail, count);
+       } else {
+               count = 0;
+       }
+       if (copy != count)
+               UART_DBG("!!!!!!!! ERROR 111\n");
+       if (count) {
+               UART_DBG("count = %d\n", count);
+               rx_ring->tail += count;
+               rx_ring->tail &= (SDMA_RX_BUFF_SIZE - 1);
+               up->port.icount.rx += count;
+               tty_flip_buffer_push(port);
+               spin_unlock_irqrestore(&up->port.lock, flags);
+               ast_uart_start_rx_dma(up, count);
+               spin_lock_irqsave(&up->port.lock, flags);
+       }
+       spin_unlock_irqrestore(&up->port.lock, flags);
+}
+
+/*
+ * FIFO support.
+ */
+static inline void serial8250_clear_fifos(struct ast_uart_port *p)
+{
+       serial_outp(p, UART_FCR, UART_FCR_ENABLE_FIFO);
+       serial_outp(p, UART_FCR, UART_FCR_ENABLE_FIFO |
+                      UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT);
+       serial_outp(p, UART_FCR, 0);
+}
+
+/*
+ * This routine is called by rs_init() to initialize a specific serial
+ * port.
+ */
+static void autoconfig(struct ast_uart_port *up)
+{
+       unsigned long flags;
+
+       UART_DBG("line [%d]\n", up->port.line);
+       if (!up->port.iobase && !up->port.mapbase && !up->port.membase)
+               return;
+
+       DEBUG_AUTOCONF("ttyDMA%d: autoconf (0x%04x, 0x%p): ",
+                       up->port.line, up->port.iobase, up->port.membase);
+
+       spin_lock_irqsave(&up->port.lock, flags);
+
+       up->capabilities = 0;
+       up->bugs = 0;
+
+       up->port.type = PORT_16550A;
+       up->capabilities |= UART_CAP_FIFO;
+
+       up->port.fifosize = uart_config[up->port.type].fifo_size;
+       up->capabilities = uart_config[up->port.type].flags;
+       up->tx_loadsz = uart_config[up->port.type].tx_loadsz;
+
+       if (up->port.type == PORT_UNKNOWN)
+               goto out;
+
+       /*
+        * Reset the UART.
+        */
+       serial8250_clear_fifos(up);
+       ast_serial_in(up, UART_RX);
+       serial_outp(up, UART_IER, 0);
+
+ out:
+       spin_unlock_irqrestore(&up->port.lock, flags);
+       DEBUG_AUTOCONF("type=%s\n", uart_config[up->port.type].name);
+}
+
+
+static inline void __stop_tx(struct ast_uart_port *p)
+{
+       if (p->ier & UART_IER_THRI) {
+               p->ier &= ~UART_IER_THRI;
+               ast_serial_out(p, UART_IER, p->ier);
+       }
+}
+
+static void serial8250_stop_tx(struct uart_port *port)
+{
+       struct ast_uart_port *up = to_ast_dma_uart_port(port);
+       struct circ_buf *xmit = &up->port.state->xmit;
+       struct dma_tx_state state;
+       unsigned int count;
+
+       __stop_tx(up);
+       if (!up->tx_in_progress)
+               return;
+       dmaengine_terminate_all(up->tx_dma_chan);
+       dmaengine_tx_status(up->tx_dma_chan, up->tx_cookie, &state);
+       count = up->tx_bytes_requested - state.residue;
+       async_tx_ack(up->tx_dma_desc);
+       xmit->tail = (xmit->tail + count) & (UART_XMIT_SIZE - 1);
+       up->tx_in_progress = 0;
+}
+
+static void transmit_chars(struct ast_uart_port *up);
+
+static void serial8250_start_tx(struct uart_port *port)
+{
+       struct ast_uart_port *up = to_ast_dma_uart_port(port);
+       struct circ_buf *xmit = &up->port.state->xmit;
+
+       UART_DBG("\n%s:line %d", __func__, port->line);
+       UART_TX_DBG("line [%d]\n", port->line);
+       if (!uart_circ_empty(xmit) && !up->tx_in_progress) {
+               UART_DBG("Calling ast_uart_start_next_tx\n");
+               ast_uart_start_next_tx(up);
+       }
+}
+
+static void serial8250_stop_rx(struct uart_port *port)
+{
+       struct ast_uart_port *up = to_ast_dma_uart_port(port);
+       struct dma_tx_state state;
+
+       up->ier &= ~UART_IER_RLSI;
+       up->port.read_status_mask &= ~UART_LSR_DR;
+       ast_serial_out(up, UART_IER, up->ier);
+       if (!up->rx_in_progress)
+               return;
+       dmaengine_terminate_all(up->rx_dma_chan);
+       dmaengine_tx_status(up->rx_dma_chan, up->rx_cookie, &state);
+       up->rx_in_progress = 0;
+       up->rx_bytes_transferred = 0;
+}
+
+static void serial8250_enable_ms(struct uart_port *port)
+{
+       struct ast_uart_port *up = to_ast_dma_uart_port(port);
+
+       UART_DBG("line [%d]\n", port->line);
+       up->ier |= UART_IER_MSI;
+       ast_serial_out(up, UART_IER, up->ier);
+}
+
+static void transmit_chars(struct ast_uart_port *up)
+{
+       struct circ_buf *xmit = &up->port.state->xmit;
+       int count;
+
+       if (up->port.x_char) {
+               serial_outp(up, UART_TX, up->port.x_char);
+               up->port.icount.tx++;
+               up->port.x_char = 0;
+               return;
+       }
+       if (uart_tx_stopped(&up->port)) {
+               serial8250_stop_tx(&up->port);
+               return;
+       }
+       if (uart_circ_empty(xmit)) {
+               __stop_tx(up);
+               return;
+       }
+
+       count = up->tx_loadsz;
+       do {
+               ast_serial_out(up, UART_TX, xmit->buf[xmit->tail]);
+               xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
+               up->port.icount.tx++;
+               if (uart_circ_empty(xmit))
+                       break;
+       } while (--count > 0);
+
+       if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
+               uart_write_wakeup(&up->port);
+
+       if (uart_circ_empty(xmit))
+               __stop_tx(up);
+}
+
+static unsigned int check_modem_status(struct ast_uart_port *up)
+{
+       unsigned int status = ast_serial_in(up, UART_MSR);
+
+       UART_DBG("line [%d]\n", up->port.line);
+       status |= up->msr_saved_flags;
+       up->msr_saved_flags = 0;
+       if (status & UART_MSR_ANY_DELTA && up->ier & UART_IER_MSI &&
+                                           up->port.state != NULL) {
+               if (status & UART_MSR_TERI)
+                       up->port.icount.rng++;
+               if (status & UART_MSR_DDSR)
+                       up->port.icount.dsr++;
+               if (status & UART_MSR_DDCD)
+                       uart_handle_dcd_change(&up->port,
+                                                       status & UART_MSR_DCD);
+               if (status & UART_MSR_DCTS)
+                       uart_handle_cts_change(&up->port,
+                                                       status & UART_MSR_CTS);
+               wake_up_interruptible(&up->port.state->port.delta_msr_wait);
+       }
+       return status;
+}
+
+/*
+ * This handles the interrupt from one port.
+ */
+static inline void
+serial8250_handle_port(struct ast_uart_port *up)
+{
+       unsigned int status;
+       unsigned long flags;
+
+       spin_lock_irqsave(&up->port.lock, flags);
+
+       status = serial_inp(up, UART_LSR);
+
+       DEBUG_INTR("status = %x...", status);
+
+       check_modem_status(up);
+       if (status & UART_LSR_THRE)
+               transmit_chars(up);
+
+       spin_unlock_irqrestore(&up->port.lock, flags);
+}
+
+/*
+ * This is the serial driver's interrupt routine.
+ */
+static irqreturn_t ast_uart_interrupt(int irq, void *dev_id)
+{
+       struct irq_info *i = dev_id;
+       int pass_counter = 0, handled = 0, end = 0;
+
+       DEBUG_INTR("(%d) ", irq);
+       spin_lock(&i->lock);
+       do {
+               struct ast_uart_port *up;
+               unsigned int iir;
+
+               up = (struct ast_uart_port *)(i->up);
+               iir = ast_serial_in(up, UART_IIR);
+               if (!(iir & UART_IIR_NO_INT)) {
+                       serial8250_handle_port(up);
+                       handled = 1;
+               } else
+                       end = 1;
+
+               if (pass_counter++ > PASS_LIMIT) {
+                       /* If we hit this, we're dead. */
+                       UART_DBG(KERN_ERR
+                         "ast-uart-dma:too much work for irqi%d", irq);
+                       break;
+               }
+       } while (end);
+
+       spin_unlock(&i->lock);
+
+       DEBUG_INTR("end.\n");
+
+       return IRQ_RETVAL(handled);
+}
+
+static unsigned int serial8250_tx_empty(struct uart_port *port)
+{
+       struct ast_uart_port *up = to_ast_dma_uart_port(port);
+       unsigned long flags;
+       unsigned int lsr;
+
+       UART_TX_DBG("line [%d]\n", up->port.line);
+
+       spin_lock_irqsave(&up->port.lock, flags);
+       lsr = ast_serial_in(up, UART_LSR);
+       up->lsr_saved_flags |= lsr & LSR_SAVE_FLAGS;
+       spin_unlock_irqrestore(&up->port.lock, flags);
+
+       return lsr & UART_LSR_TEMT ? TIOCSER_TEMT : 0;
+}
+
+static unsigned int serial8250_get_mctrl(struct uart_port *port)
+{
+       struct ast_uart_port *up = to_ast_dma_uart_port(port);
+       unsigned int status;
+       unsigned int ret;
+
+       status = check_modem_status(up);
+
+       ret = 0;
+       if (status & UART_MSR_DCD)
+               ret |= TIOCM_CAR;
+       if (status & UART_MSR_RI)
+               ret |= TIOCM_RNG;
+       if (status & UART_MSR_DSR)
+               ret |= TIOCM_DSR;
+       if (status & UART_MSR_CTS)
+               ret |= TIOCM_CTS;
+       return ret;
+}
+
+static void serial8250_set_mctrl(struct uart_port *port, unsigned int mctrl)
+{
+       struct ast_uart_port *up = to_ast_dma_uart_port(port);
+       unsigned char mcr = 0;
+       //UART_DBG("serial8250_set_mctrl %x\n",mctrl);
+       //TODO .... Issue for fix ......
+       mctrl = 0;
+
+       if (mctrl & TIOCM_RTS)
+               mcr |= UART_MCR_RTS;
+       if (mctrl & TIOCM_DTR)
+               mcr |= UART_MCR_DTR;
+       if (mctrl & TIOCM_OUT1)
+               mcr |= UART_MCR_OUT1;
+       if (mctrl & TIOCM_OUT2)
+               mcr |= UART_MCR_OUT2;
+       if (mctrl & TIOCM_LOOP)
+               mcr |= UART_MCR_LOOP;
+
+       mcr = (mcr & up->mcr_mask) | up->mcr_force | up->mcr;
+
+       ast_serial_out(up, UART_MCR, mcr);
+}
+
+static void serial8250_break_ctl(struct uart_port *port, int break_state)
+{
+       struct ast_uart_port *up = to_ast_dma_uart_port(port);
+       unsigned long flags;
+
+       spin_lock_irqsave(&up->port.lock, flags);
+       if (break_state == -1)
+               up->lcr |= UART_LCR_SBC;
+       else
+               up->lcr &= ~UART_LCR_SBC;
+       ast_serial_out(up, UART_LCR, up->lcr);
+       spin_unlock_irqrestore(&up->port.lock, flags);
+}
+
+static int serial8250_startup(struct uart_port *port)
+{
+       struct ast_uart_port *up = to_ast_dma_uart_port(port);
+       //TX DMA
+       struct circ_buf *xmit = &up->port.state->xmit;
+       unsigned long flags;
+       unsigned char lsr, iir;
+       int retval;
+       struct dma_slave_config dma_sconfig;
+       int irq_flags = up->port.flags & UPF_SHARE_IRQ ? IRQF_SHARED : 0;
+
+       up->capabilities = uart_config[up->port.type].flags;
+       up->mcr = 0;
+       /*
+        * Clear the FIFO buffers and disable them.
+        * (they will be reenabled in set_termios())
+        */
+       serial8250_clear_fifos(up);
+       UART_DBG("1: line [%d]\n", port->line);
+
+       /*
+        * Clear the interrupt registers.
+        */
+       (void) serial_inp(up, UART_LSR);
+       (void) serial_inp(up, UART_RX);
+       (void) serial_inp(up, UART_IIR);
+       (void) serial_inp(up, UART_MSR);
+
+       ast_uart_irq[0].up = up;
+       retval = request_irq(up->port.irq, ast_uart_interrupt,
+                                irq_flags, "ast-uart-dma", ast_uart_irq);
+       if (retval)
+               return retval;
+
+       /*
+        * Now, initialize the UART
+        */
+       serial_outp(up, UART_LCR, UART_LCR_WLEN8);
+
+       spin_lock_irqsave(&up->port.lock, flags);
+       up->port.mctrl |= TIOCM_OUT2;
+
+       serial8250_set_mctrl(&up->port, up->port.mctrl);
+
+       /*
+        * Do a quick test to see if we receive an
+        * interrupt when we enable the TX irq.
+        */
+       serial_outp(up, UART_IER, UART_IER_THRI);
+       lsr = ast_serial_in(up, UART_LSR);
+       iir = ast_serial_in(up, UART_IIR);
+       serial_outp(up, UART_IER, 0);
+
+       if (lsr & UART_LSR_TEMT && iir & UART_IIR_NO_INT) {
+               if (!(up->bugs & UART_BUG_TXEN)) {
+                       up->bugs |= UART_BUG_TXEN;
+                       UART_DBG("ttyDMA%d - enabling bad tx status\n",
+                                port->line);
+               }
+       } else {
+               up->bugs &= ~UART_BUG_TXEN;
+       }
+
+       spin_unlock_irqrestore(&up->port.lock, flags);
+
+       /*
+        * Clear the interrupt registers again for luck, and clear the
+        * saved flags to avoid getting false values from polling
+        * routines or the previous session.
+        */
+       serial_inp(up, UART_LSR);
+       serial_inp(up, UART_RX);
+       serial_inp(up, UART_IIR);
+       serial_inp(up, UART_MSR);
+       up->lsr_saved_flags = 0;
+       up->msr_saved_flags = 0;
+
+       //RX DMA
+       up->rx_dma_buf.head = 0;
+       up->rx_dma_buf.tail = 0;
+       up->port.icount.rx = 0;
+       ast_dma_channel_setup(up);
+       up->rx_dma_buf.buf = dma_alloc_coherent(port->dev, UART_RX_SIZE,
+                                       &up->dma_rx_addr, GFP_KERNEL);
+       if (!up->rx_dma_buf.buf)
+               ast_dma_channel_teardown(up);
+#if 1
+       memset(&dma_sconfig, 0, sizeof(struct dma_slave_config));
+       dma_sconfig.dst_addr = up->dma_rx_addr;
+       dma_sconfig.dst_port_window_size = UART_RX_SIZE;
+       dma_sconfig.dst_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE;
+       dma_sconfig.dst_maxburst = 4;
+       dma_sconfig.slave_id = up->channel_no;
+
+       dmaengine_slave_config(up->rx_dma_chan, &dma_sconfig);
+
+       //ast_uart_start_rx_dma(up, UART_RX_SIZE);
+       dma_sync_single_for_device(up->port.dev, up->dma_rx_addr,
+                       UART_RX_SIZE, DMA_FROM_DEVICE);
+
+       up->rx_dma_desc = dmaengine_prep_slave_single(up->rx_dma_chan,
+                       up->dma_rx_addr, UART_RX_SIZE, DMA_DEV_TO_MEM,
+                       DMA_PREP_INTERRUPT);
+       if (!up->rx_dma_desc) {
+               dev_err(up->port.dev, "Not able to get desc for Rx\n");
+               return -EIO;
+       }
+       up->rx_dma_desc->callback = ast_uart_rx_dma_complete;
+       up->rx_dma_desc->callback_param = up;
+       up->rx_in_progress = 1;
+       up->rx_bytes_requested = UART_RX_SIZE;
+       up->rx_cookie = dmaengine_submit(up->rx_dma_desc);
+#endif
+
+       memset(&dma_sconfig, 0, sizeof(struct dma_slave_config));
+
+       up->tx_done = 1;
+       up->tx_count = 0;
+       up->tx_dma_buf.head = 0;
+       up->tx_dma_buf.tail = 0;
+       up->tx_dma_buf.buf = xmit->buf;
+       UART_DBG("head:0x%x tail:0x%x\n", xmit->head, xmit->tail);
+       xmit->head = 0;
+       xmit->tail = 0;
+
+       up->dma_tx_addr = dma_map_single(port->dev,
+                                      up->tx_dma_buf.buf,
+                                      UART_XMIT_SIZE,
+                                      DMA_TO_DEVICE);
+#if 1
+       dma_sconfig.src_addr = up->dma_tx_addr;
+       dma_sconfig.src_port_window_size = UART_XMIT_SIZE;
+       dma_sconfig.src_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE;
+       dma_sconfig.src_maxburst = 4;
+       dma_sconfig.slave_id = up->channel_no;
+       dmaengine_slave_config(up->tx_dma_chan, &dma_sconfig);
+#endif
+       //STOP and TRIGGER is done in SDMA driver
+       return 0;
+}
+
+static void serial8250_shutdown(struct uart_port *port)
+{
+       struct ast_uart_port *up = to_ast_dma_uart_port(port);
+       unsigned long flags;
+
+       up->ier = 0;
+       serial_outp(up, UART_IER, 0);
+
+       spin_lock_irqsave(&up->port.lock, flags);
+       up->port.mctrl &= ~TIOCM_OUT2;
+
+       serial8250_set_mctrl(&up->port, up->port.mctrl);
+       spin_unlock_irqrestore(&up->port.lock, flags);
+
+       /*
+        * Disable break condition and FIFOs
+        */
+       ast_serial_out(up, UART_LCR, serial_inp(up, UART_LCR) & ~UART_LCR_SBC);
+       serial8250_clear_fifos(up);
+
+       (void) ast_serial_in(up, UART_RX);
+
+       up->rx_in_progress = 0;
+       up->tx_in_progress = 0;
+       dma_release_channel(up->rx_dma_chan);
+       dma_release_channel(up->tx_dma_chan);
+       dma_free_coherent(port->dev, UART_RX_SIZE,
+                                       up->rx_dma_buf.buf, up->dma_rx_addr);
+       dma_unmap_single(port->dev, up->dma_tx_addr,
+                                       UART_XMIT_SIZE, DMA_TO_DEVICE);
+       up->rx_dma_chan = NULL;
+       up->tx_dma_chan = NULL;
+       up->dma_rx_addr = 0;
+       up->dma_rx_addr = 0;
+       //Tx buffer will free by serial_core.c
+       free_irq(up->port.irq, ast_uart_irq);
+}
+
+static unsigned int serial8250_get_divisor(struct uart_port *port,
+                                                       unsigned int baud)
+{
+       unsigned int quot;
+
+       quot = uart_get_divisor(port, baud);
+       return quot;
+}
+
+static void
+serial8250_set_termios(struct uart_port *port, struct ktermios *termios,
+                      struct ktermios *old)
+{
+       struct ast_uart_port *up = to_ast_dma_uart_port(port);
+       unsigned char cval, fcr = 0;
+       unsigned long flags;
+       unsigned int baud, quot;
+
+       switch (termios->c_cflag & CSIZE) {
+       case CS5:
+               cval = UART_LCR_WLEN5;
+               break;
+       case CS6:
+               cval = UART_LCR_WLEN6;
+               break;
+       case CS7:
+               cval = UART_LCR_WLEN7;
+               break;
+       default:
+       case CS8:
+               cval = UART_LCR_WLEN8;
+               break;
+       }
+
+       if (termios->c_cflag & CSTOPB)
+               cval |= UART_LCR_STOP;
+       if (termios->c_cflag & PARENB)
+               cval |= UART_LCR_PARITY;
+       if (!(termios->c_cflag & PARODD))
+               cval |= UART_LCR_EPAR;
+#ifdef CMSPAR
+       if (termios->c_cflag & CMSPAR)
+               cval |= UART_LCR_SPAR;
+#endif
+
+       /*
+        * Ask the core to calculate the divisor for us.
+        */
+       baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk/16);
+       quot = serial8250_get_divisor(port, baud);
+
+       if (up->capabilities & UART_CAP_FIFO && up->port.fifosize > 1) {
+               if (baud < 2400)
+                       fcr = UART_FCR_ENABLE_FIFO | UART_FCR_TRIGGER_1;
+               else
+                       fcr = uart_config[up->port.type].fcr;
+       }
+
+       /*
+        * Ok, we're now changing the port state.  Do it with
+        * interrupts disabled.
+        */
+       spin_lock_irqsave(&up->port.lock, flags);
+
+       /*
+        * Update the per-port timeout.
+        */
+       uart_update_timeout(port, termios->c_cflag, baud);
+
+       up->port.read_status_mask = UART_LSR_OE | UART_LSR_THRE | UART_LSR_DR;
+       if (termios->c_iflag & INPCK)
+               up->port.read_status_mask |= UART_LSR_FE | UART_LSR_PE;
+       if (termios->c_iflag & (BRKINT | PARMRK))
+               up->port.read_status_mask |= UART_LSR_BI;
+
+       /*
+        * Characteres to ignore
+        */
+       up->port.ignore_status_mask = 0;
+       if (termios->c_iflag & IGNPAR)
+               up->port.ignore_status_mask |= UART_LSR_PE | UART_LSR_FE;
+       if (termios->c_iflag & IGNBRK) {
+               up->port.ignore_status_mask |= UART_LSR_BI;
+               /*
+                * If we're ignoring parity and break indicators,
+                * ignore overruns too (for real raw support).
+                */
+               if (termios->c_iflag & IGNPAR)
+                       up->port.ignore_status_mask |= UART_LSR_OE;
+       }
+
+       /*
+        * ignore all characters if CREAD is not set
+        */
+       if ((termios->c_cflag & CREAD) == 0)
+               up->port.ignore_status_mask |= UART_LSR_DR;
+
+       /*
+        * CTS flow control flag and modem status interrupts
+        */
+       up->ier &= ~UART_IER_MSI;
+       if (UART_ENABLE_MS(&up->port, termios->c_cflag))
+               up->ier |= UART_IER_MSI;
+
+       ast_serial_out(up, UART_IER, up->ier);
+
+
+       serial_outp(up, UART_LCR, cval | UART_LCR_DLAB);/* set DLAB */
+
+       serial_dl_write(up, quot);
+
+       /*
+        * LCR DLAB must be set to enable 64-byte FIFO mode. If the FCR
+        * is written without DLAB set, this mode will be disabled.
+        */
+
+       serial_outp(up, UART_LCR, cval);                /* reset DLAB */
+       up->lcr = cval;                                 /* Save LCR */
+               if (fcr & UART_FCR_ENABLE_FIFO) {
+                       /* emulated UARTs (Lucent Venus 167x) need two steps */
+                       serial_outp(up, UART_FCR, UART_FCR_ENABLE_FIFO);
+               }
+               serial_outp(up, UART_FCR, fcr);         /* set fcr */
+       serial8250_set_mctrl(&up->port, up->port.mctrl);
+       spin_unlock_irqrestore(&up->port.lock, flags);
+       /* Don't rewrite B0 */
+       if (tty_termios_baud_rate(termios))
+               tty_termios_encode_baud_rate(termios, baud, baud);
+}
+
+static void
+serial8250_pm(struct uart_port *port, unsigned int state,
+             unsigned int oldstate)
+{
+       struct ast_uart_port *p = (struct ast_uart_port *)port;
+
+       if (p->pm)
+               p->pm(port, state, oldstate);
+}
+
+/*
+ * Resource handling.
+ */
+static int serial8250_request_std_resource(struct ast_uart_port *up)
+{
+       unsigned int size = 8 << up->port.regshift;
+       int ret = 0;
+
+       if (!up->port.mapbase)
+               return ret;
+
+       if (!request_mem_region(up->port.mapbase, size, "ast-uart-dma")) {
+               ret = -EBUSY;
+               return ret;
+       }
+
+       if (up->port.flags & UPF_IOREMAP) {
+               up->port.membase = ioremap_nocache(up->port.mapbase, size);
+               if (!up->port.membase) {
+                       release_mem_region(up->port.mapbase, size);
+                       ret = -ENOMEM;
+                       return ret;
+               }
+       }
+       return ret;
+}
+
+static void serial8250_release_std_resource(struct ast_uart_port *up)
+{
+       unsigned int size = 8 << up->port.regshift;
+
+               if (!up->port.mapbase)
+                       return;
+
+               if (up->port.flags & UPF_IOREMAP) {
+                       iounmap(up->port.membase);
+                       up->port.membase = NULL;
+               }
+
+               release_mem_region(up->port.mapbase, size);
+}
+
+
+static void serial8250_release_port(struct uart_port *port)
+{
+       struct ast_uart_port *up = (struct ast_uart_port *)port;
+
+       serial8250_release_std_resource(up);
+}
+
+static int serial8250_request_port(struct uart_port *port)
+{
+       struct ast_uart_port *up = (struct ast_uart_port *)port;
+       int ret = 0;
+
+       ret = serial8250_request_std_resource(up);
+       if (ret == 0)
+               serial8250_release_std_resource(up);
+
+       return ret;
+}
+
+static void serial8250_config_port(struct uart_port *port, int flags)
+{
+       struct ast_uart_port *up = (struct ast_uart_port *)port;
+       int ret;
+
+       /*
+        * Find the region that we can probe for.  This in turn
+        * tells us whether we can probe for the type of port.
+        */
+       ret = serial8250_request_std_resource(up);
+       if (ret < 0)
+               return;
+
+       if (flags & UART_CONFIG_TYPE)
+               autoconfig(up);
+
+       if (up->port.type == PORT_UNKNOWN)
+               serial8250_release_std_resource(up);
+}
+
+static int
+serial8250_verify_port(struct uart_port *port, struct serial_struct *ser)
+{
+       return 0;
+}
+
+static const char *
+serial8250_type(struct uart_port *port)
+{
+       int type = port->type;
+
+       if (type >= ARRAY_SIZE(uart_config))
+               type = 0;
+       return uart_config[type].name;
+}
+
+static const struct uart_ops serial8250_pops = {
+       .tx_empty       = serial8250_tx_empty,
+       .set_mctrl      = serial8250_set_mctrl,
+       .get_mctrl      = serial8250_get_mctrl,
+       .stop_tx        = serial8250_stop_tx,
+       .start_tx       = serial8250_start_tx,
+       .stop_rx        = serial8250_stop_rx,
+       .enable_ms      = serial8250_enable_ms,
+       .break_ctl      = serial8250_break_ctl,
+       .startup        = serial8250_startup,
+       .shutdown       = serial8250_shutdown,
+       .set_termios    = serial8250_set_termios,
+       .pm             = serial8250_pm,
+       .type           = serial8250_type,
+       .release_port   = serial8250_release_port,
+       .request_port   = serial8250_request_port,
+       .config_port    = serial8250_config_port,
+       .verify_port    = serial8250_verify_port,
+};
+
+static void __init serial8250_isa_init_ports(void)
+{
+       static int first = 1;
+       int i;
+
+       if (!first)
+               return;
+       first = 0;
+
+       for (i = 0; i < nr_uarts; i++) {
+               struct ast_uart_port *up = &ast_uart_ports[i];
+
+               up->port.line = i;
+               spin_lock_init(&up->port.lock);
+
+               /*
+                * ALPHA_KLUDGE_MCR needs to be killed.
+                */
+               up->mcr_mask = ~ALPHA_KLUDGE_MCR;
+               up->mcr_force = ALPHA_KLUDGE_MCR;
+
+               up->port.ops = &serial8250_pops;
+       }
+
+}
+
+static void __init
+serial8250_register_ports(struct uart_driver *drv, struct device *dev)
+{
+       int i;
+       struct ast_uart_port *up = NULL;
+
+       serial8250_isa_init_ports();
+       for (i = 0; i < nr_uarts; i++) {
+               up = &ast_uart_ports[i];
+               up->port.dev = dev;
+               uart_add_one_port(drv, &up->port);
+       }
+}
+
+#define SERIAL8250_CONSOLE     NULL
+
+static struct uart_driver serial8250_reg = {
+       .owner                  = THIS_MODULE,
+       .driver_name            = "ast-uart-dma",
+       .dev_name               = "ttyDMA",
+#if 0
+       .major                  = TTY_MAJOR,
+       .minor                  = 64,
+#else
+       .major                  = 204, // like atmel_serial
+       .minor                  = 155,
+#endif
+       .nr                     = UART_DMA_NR,
+       .cons                   = SERIAL8250_CONSOLE,
+};
+
+static void ast_dma_channel_teardown(struct ast_uart_port *s)
+{
+       UART_DBG("Teardown called\n");
+       if (s->tx_dma_chan) {
+               dma_release_channel(s->tx_dma_chan);
+               s->tx_dma_chan = NULL;
+       }
+       if (s->rx_dma_chan) {
+               dma_release_channel(s->rx_dma_chan);
+               s->rx_dma_chan = NULL;
+       }
+
+}
+static int ast_dma_channel_setup(struct ast_uart_port *up)
+{
+       up->rx_dma_chan = dma_request_slave_channel(up->port.dev, "rx");
+       UART_DBG("Entered %s ptr is %p\n", __func__, up->rx_dma_chan);
+       if (!up->rx_dma_chan)
+               goto err_out;
+
+       up->tx_dma_chan = dma_request_slave_channel(up->port.dev, "tx");
+       UART_DBG("Entered %s ptr is %p\n", __func__, up->tx_dma_chan);
+       if (!up->tx_dma_chan)
+               goto err_out;
+
+       return 0;
+err_out:
+       ast_dma_channel_teardown(up);
+       return -EINVAL;
+}
+
+/*
+ * Register a set of serial devices attached to a platform device.  The
+ * list is terminated with a zero flags entry, which means we expect
+ * all entries to have at least UPF_BOOT_AUTOCONF set.
+ */
+struct clk *clk;
+static int serial8250_probe(struct platform_device *dev)
+{
+       struct device_node *np = dev->dev.of_node;
+       struct uart_port port;
+       int ret;
+       u32 read = 0;
+       struct resource *res = 0;
+
+       if (UART_XMIT_SIZE > DMA_BUFF_SIZE)
+               UART_DBG("UART_XMIT_SIZE > DMA_BUFF_SIZE : Please Check\n");
+       memset(&port, 0, sizeof(struct uart_port));
+
+       port.irq = platform_get_irq(dev, 0);
+       if (port.irq < 0) {
+               dev_err(&dev->dev, "cannot get irq\n");
+               return port.irq;
+       }
+       res = platform_get_resource(dev, IORESOURCE_MEM, 0);
+       if (!res) {
+               dev_err(&dev->dev, "Register base not found");
+               return -ENODEV;
+       }
+       port.mapbase = res->start;
+       clk = devm_clk_get(&dev->dev, NULL);
+       if (IS_ERR(clk))
+               dev_err(&dev->dev, "missing controller clock");
+
+       ret = clk_prepare_enable(clk);
+       if (ret) {
+               dev_err(&dev->dev, "failed to enable DMA UART Clk\n");
+               return ret;
+       }
+       port.uartclk = clk_get_rate(clk);
+
+       if (of_property_read_u32(np, "reg-shift", &read) == 0)
+               port.regshift = read;
+       port.iotype = UPIO_MEM;
+       port.flags = UPF_IOREMAP | UPF_BOOT_AUTOCONF | UPF_SKIP_TEST;
+       port.dev = &dev->dev;
+       if (share_irqs)
+               port.flags |= UPF_SHARE_IRQ;
+       ret = ast_uart_register_port(&port, read);
+       if (ret < 0) {
+               dev_err(&dev->dev,
+               "Fail:register_port at index %d(IO%lx MEM%llx IRQ%d): %d\n",
+               read, port.iobase, (unsigned long long)port.mapbase,
+                                                               port.irq, ret);
+       }
+       return ret;
+}
+
+/*
+ * Remove serial ports registered against a platform device.
+ */
+static int serial8250_remove(struct platform_device *dev)
+{
+       int i;
+
+       for (i = 0; i < nr_uarts; i++) {
+               struct ast_uart_port *up = &ast_uart_ports[i];
+
+               if (up->port.dev == &dev->dev)
+                       ast_uart_unregister_port(i);
+               ast_dma_channel_teardown(up);
+       }
+       return 0;
+}
+
+static int serial8250_suspend(struct platform_device *dev, pm_message_t state)
+{
+       int i;
+
+       for (i = 0; i < UART_DMA_NR; i++) {
+               struct ast_uart_port *up = &ast_uart_ports[i];
+
+               if (up->port.type != PORT_UNKNOWN && up->port.dev == &dev->dev)
+                       uart_suspend_port(&serial8250_reg, &up->port);
+       }
+
+       return 0;
+}
+
+static int serial8250_resume(struct platform_device *dev)
+{
+       int i;
+
+       for (i = 0; i < UART_DMA_NR; i++) {
+               struct ast_uart_port *up = &ast_uart_ports[i];
+
+               if (up->port.type != PORT_UNKNOWN && up->port.dev == &dev->dev)
+                       serial8250_resume_port(i);
+       }
+
+       return 0;
+}
+
+static const struct of_device_id ast_serial_dt_ids[] = {
+        { .compatible = "aspeed,ast-sdma-uart", },
+        { /* sentinel */ }
+};
+
+static struct platform_driver serial8250_ast_dma_driver = {
+       .probe          = serial8250_probe,
+       .remove         = serial8250_remove,
+       .suspend        = serial8250_suspend,
+       .resume         = serial8250_resume,
+       .driver         = {
+               .name   = "ast-uart-dma",
+               .of_match_table     = of_match_ptr(ast_serial_dt_ids),
+       },
+};
+
+/*
+ * This "device" covers _all_ ISA 8250-compatible serial devices listed
+ * in the table in include/asm/serial.h
+ */
+static struct platform_device *serial8250_isa_devs;
+
+/*
+ * serial8250_register_port and serial8250_unregister_port allows for
+ * 16x50 serial ports to be configured at run-time, to support PCMCIA
+ * modems and PCI multiport cards.
+ */
+
+static struct ast_uart_port *
+               serial8250_find_match_or_unused(struct uart_port *port)
+{
+       int i;
+
+       /*
+        * First, find a port entry which matches.
+        */
+       for (i = 0; i < nr_uarts; i++) {
+               if (uart_match_port(&ast_uart_ports[i].port, port))
+                       return &ast_uart_ports[i];
+       }
+       /*
+        * We didn't find a matching entry, so look for the first
+        * free entry.  We look for one which hasn't been previously
+        * used (indicated by zero iobase).
+        */
+       for (i = 0; i < nr_uarts; i++)
+               if (ast_uart_ports[i].port.type == PORT_UNKNOWN &&
+                   ast_uart_ports[i].port.iobase == 0)
+                       return &ast_uart_ports[i];
+       /*
+        * That also failed.  Last resort is to find any entry which
+        * doesn't have a real port associated with it.
+        */
+       for (i = 0; i < nr_uarts; i++)
+               if (ast_uart_ports[i].port.type == PORT_UNKNOWN)
+                       return &ast_uart_ports[i];
+
+       return NULL;
+}
+
+/**
+ *     serial8250_register_port - register a serial port
+ *     @port: serial port template
+ *
+ *     Configure the serial port specified by the request. If the
+ *     port exists and is in use, it is hung up and unregistered
+ *     first.
+ *
+ *     The port is then probed and if necessary the IRQ is autodetected
+ *     If this fails an error is returned.
+ *
+ *     On success the port is ready to use and the line number is returned.
+ */
+static int ast_uart_register_port(struct uart_port *port,
+                                       unsigned int channel_no)
+{
+       struct ast_uart_port *uart;
+       int ret = -ENOSPC;
+
+       if (port->uartclk == 0)
+               return -EINVAL;
+
+       mutex_lock(&ast_uart_mutex);
+
+       uart = serial8250_find_match_or_unused(port);
+       if (uart) {
+               uart_remove_one_port(&serial8250_reg, &uart->port);
+               uart->port.iobase       = port->iobase;
+               uart->port.membase      = port->membase;
+               uart->port.irq          = port->irq;
+               uart->port.uartclk      = port->uartclk;
+               uart->port.fifosize     = port->fifosize;
+               uart->port.regshift     = port->regshift;
+               uart->port.iotype       = port->iotype;
+               uart->port.flags        = port->flags | UPF_BOOT_AUTOCONF;
+               uart->port.mapbase      = port->mapbase;
+               uart->port.private_data = uart;
+               if (port->dev) {
+                       UART_DBG("Writing dev\n");
+                       uart->port.dev = port->dev;
+               }
+               ret = uart_add_one_port(&serial8250_reg, &uart->port);
+               if (ret != 0) {
+                       UART_DBG("uart_add_one_port: Failed for port=%p",
+                                                               &uart->port);
+                       return ret;
+               }
+               uart->channel_no = channel_no;
+               spin_lock_init(&uart->lock);
+
+               tasklet_init(&uart->tx_tasklet, ast_uart_tx_sdma_tasklet_func,
+                               (unsigned long)uart);
+               tasklet_init(&uart->rx_tasklet, ast_uart_rx_sdma_tasklet_func,
+                               (unsigned long)uart);
+       }
+
+       mutex_unlock(&ast_uart_mutex);
+       return ret;
+}
+EXPORT_SYMBOL(ast_uart_register_port);
+
+/**
+ *     serial8250_unregister_port - remove a 16x50 serial port at runtime
+ *     @line: serial line number
+ *
+ *     Remove one serial port.  This may not be called from interrupt
+ *     context.  We hand the port back to the our control.
+ */
+static void ast_uart_unregister_port(int line)
+{
+       struct ast_uart_port *uart = &ast_uart_ports[line];
+
+       mutex_lock(&ast_uart_mutex);
+       uart_remove_one_port(&serial8250_reg, &uart->port);
+       if (serial8250_isa_devs) {
+               uart->port.flags &= ~UPF_BOOT_AUTOCONF;
+               uart->port.type = PORT_UNKNOWN;
+               uart->port.dev = &serial8250_isa_devs->dev;
+               uart_add_one_port(&serial8250_reg, &uart->port);
+       } else {
+               uart->port.dev = NULL;
+       }
+       mutex_unlock(&ast_uart_mutex);
+}
+EXPORT_SYMBOL(ast_uart_unregister_port);
+
+static int __init ast_uart_init(void)
+{
+       int ret;
+
+       if (nr_uarts > UART_DMA_NR)
+               nr_uarts = UART_DMA_NR;
+
+       UART_DBG(KERN_INFO
+       "ast-uart-dma: UART driver with DMA %d ports, IRQ sharing %sabled\n",
+                                       nr_uarts, share_irqs ? "en" : "dis");
+       spin_lock_init(&ast_uart_irq[0].lock);
+
+       ret = uart_register_driver(&serial8250_reg);
+       if (ret)
+               goto out;
+
+       serial8250_isa_devs = platform_device_alloc("ast-uart-dma",
+                                                   PLAT8250_DEV_LEGACY);
+       if (!serial8250_isa_devs) {
+               ret = -ENOMEM;
+               goto unreg_uart_drv;
+       }
+
+       ret = platform_device_add(serial8250_isa_devs);
+       if (ret)
+               goto put_dev;
+
+       serial8250_register_ports(&serial8250_reg, &serial8250_isa_devs->dev);
+
+       ret = platform_driver_register(&serial8250_ast_dma_driver);
+       if (ret == 0)
+               goto out;
+
+       platform_device_del(serial8250_isa_devs);
+ put_dev:
+       platform_device_put(serial8250_isa_devs);
+ unreg_uart_drv:
+       uart_unregister_driver(&serial8250_reg);
+ out:
+       return ret;
+}
+
+static void __exit ast_uart_exit(void)
+{
+       struct platform_device *isa_dev = serial8250_isa_devs;
+
+       /*
+        * This tells serial8250_unregister_port() not to re-register
+        * the ports (thereby making serial8250_ast_dma_driver permanently
+        * in use.)
+        */
+       serial8250_isa_devs = NULL;
+
+       platform_driver_unregister(&serial8250_ast_dma_driver);
+       platform_device_unregister(isa_dev);
+
+       uart_unregister_driver(&serial8250_reg);
+}
+
+late_initcall(ast_uart_init);
+module_exit(ast_uart_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("AST DMA serial driver");
+MODULE_ALIAS_CHARDEV_MAJOR(TTY_MAJOR);
-- 
1.9.1

Reply via email to