[patch 1/5] AST2500 DMA UART driver

sudheer.v open.sudheer at gmail.com
Wed Jun 5 22:23:11 AEST 2019


From: sudheer Kumar veliseti <sudheer.open at gmail.com>

Signed-off-by: sudheer veliseti <sudheer.open at gmail.com>
---
 .../tty/serial/8250/8250_ast2500_uart_dma.c   | 1928 +++++++++++++++++
 1 file changed, 1928 insertions(+)
 create mode 100644 drivers/tty/serial/8250/8250_ast2500_uart_dma.c

diff --git a/drivers/tty/serial/8250/8250_ast2500_uart_dma.c b/drivers/tty/serial/8250/8250_ast2500_uart_dma.c
new file mode 100644
index 000000000000..6f3adcac9190
--- /dev/null
+++ b/drivers/tty/serial/8250/8250_ast2500_uart_dma.c
@@ -0,0 +1,1928 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ *  DMA UART Driver for ASPEED BMC chip: AST2500
+ *
+ *  Copyright (C) 2019 sudheer Kumar veliseti, Aspeed technology Inc.
+ *  <open.sudheer at gmail.com>
+ *
+ */
+#include <linux/io.h>
+#include <linux/irq.h>
+#include <linux/clk.h>
+#include <linux/console.h>
+#include <linux/delay.h>
+#include <linux/dma-mapping.h>
+#include <linux/dma-mapping.h>
+#include <linux/init.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/ioport.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/mutex.h>
+#include <linux/nmi.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_device.h>
+#include <linux/of_device.h>
+#include <linux/of_irq.h>
+#include <linux/of_irq.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/serial.h>
+#include <linux/serial_8250.h>
+#include <linux/serial_core.h>
+#include <linux/serial_reg.h>
+#include <linux/slab.h>
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
+
+#include "8250.h"
+
+#define DMA_BUFF_SIZE 0x1000      // 4096
+#define SDMA_RX_BUFF_SIZE 0x10000 // 65536
+
+#define SDDMA_RX_FIX 1
+/* enum ast_uart_chan_op
+ * operation codes passed to the DMA code by the user, and also used
+ * to inform the current channel owner of any changes to the system state
+ */
+
+enum ast_uart_chan_op {
+	AST_UART_DMAOP_TRIGGER,
+	AST_UART_DMAOP_STOP,
+	AST_UART_DMAOP_PAUSE,
+};
+
+/* ast_uart_dma_cbfn_t *  * buffer callback routine type */
+typedef void (*ast_uart_dma_cbfn_t)(void *dev_id, u16 len);
+
+struct ast_sdma_info {
+	u8 ch_no;
+	u8 direction;
+	u8 enable;
+	void *priv;
+	char *sdma_virt_addr;
+	dma_addr_t dma_phy_addr;
+	/* cdriver callbacks */
+	ast_uart_dma_cbfn_t callback_fn; /* buffer done callback */
+};
+
+#define AST_UART_SDMA_CH 12
+
+struct ast_sdma_ch {
+	struct ast_sdma_info tx_dma_info[AST_UART_SDMA_CH];
+	struct ast_sdma_info rx_dma_info[AST_UART_SDMA_CH];
+};
+
+struct ast_sdma {
+	void __iomem *reg_base;
+	int dma_irq;
+	struct ast_sdma_ch *dma_ch;
+	struct regmap *map;
+};
+
+
+
+#define UART_TX_SDMA_EN 0x00
+#define UART_RX_SDMA_EN 0x04
+#define UART_SDMA_CONF 0x08
+#define UART_SDMA_TIMER 0x0C
+#define UART_TX_SDMA_REST 0x20
+#define UART_RX_SDMA_REST 0x24
+#define UART_TX_SDMA_IER 0x30
+#define UART_TX_SDMA_ISR 0x34
+#define UART_RX_SDMA_IER 0x38
+#define UART_RX_SDMA_ISR 0x3C
+#define UART_TX_R_POINT(x) (0x40 + (x * 0x20))
+#define UART_TX_W_POINT(x) (0x44 + (x * 0x20))
+#define UART_TX_SDMA_ADDR(x) (0x48 + (x * 0x20))
+#define UART_RX_R_POINT(x) (0x50 + (x * 0x20))
+#define UART_RX_W_POINT(x) (0x54 + (x * 0x20))
+#define UART_RX_SDMA_ADDR(x) (0x58 + (x * 0x20))
+
+/* UART_TX_SDMA_EN-0x00 : UART TX DMA Enable */
+/* UART_RX_SDMA_EN-0x04 : UART RX DMA Enable */
+#define SDMA_CH_EN(x) (0x1 << (x))
+
+/* UART_SDMA_CONF - 0x08 : Misc, Buffer size  */
+#define SDMA_TX_BUFF_SIZE_MASK (0x3)
+#define SDMA_SET_TX_BUFF_SIZE(x) (x)
+#define SDMA_BUFF_SIZE_1KB (0x0)
+#define SDMA_BUFF_SIZE_4KB (0x1)
+#define SDMA_BUFF_SIZE_16KB (0x2)
+#define SDMA_BUFF_SIZE_64KB (0x3)
+#define SDMA_RX_BUFF_SIZE_MASK (0x3 << 2)
+#define SDMA_SET_RX_BUFF_SIZE(x) (x << 2)
+#define SDMA_TIMEOUT_DIS (0x1 << 4)
+
+/* UART_SDMA_TIMER-0x0C :  UART DMA time out timer */
+
+/* UART_TX_SDMA_IER			0x30	*/
+/* UART_TX_SDMA_ISR			0x34	*/
+
+#define UART_SDMA11_INT (1 << 11)
+#define UART_SDMA10_INT (1 << 10)
+#define UART_SDMA9_INT (1 << 9)
+#define UART_SDMA8_INT (1 << 8)
+#define UART_SDMA7_INT (1 << 7)
+#define UART_SDMA6_INT (1 << 6)
+#define UART_SDMA5_INT (1 << 5)
+#define UART_SDMA4_INT (1 << 4)
+#define UART_SDMA3_INT (1 << 3)
+#define UART_SDMA2_INT (1 << 2)
+#define UART_SDMA1_INT (1 << 1)
+#define UART_SDMA0_INT (1 << 0)
+
+
+#define CONFIG_UART_DMA_DEBUG
+
+#ifdef CONFIG_UART_DMA_DEBUG
+#define UART_DBG(fmt, args...) pr_debug("%s() " fmt, __func__, ## args)
+#else
+#define UART_DBG(fmt, args...)
+#endif
+
+#define  CONFIG_UART_TX_DMA_DEBUG 1
+
+#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.
+ */
+#ifdef CONFIG_UART_DMA_DEBUG
+#define DEBUG_AUTOCONF(fmt...) UART_DBG(fmt)
+#else
+#define DEBUG_AUTOCONF(fmt...)          \
+	do {                            \
+	} while (0)
+#endif
+
+#ifdef CONFIG_UART_DMA_DEBUG
+#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_priv_data {
+
+	unsigned short line; //index of uart port
+	struct uart_8250_port *up;
+	u8 dma_ch; // dma channel number
+	struct circ_buf rx_dma_buf;
+	struct circ_buf tx_dma_buf;
+	dma_addr_t dma_rx_addr; /* Mapped ADMA descr. table */
+	dma_addr_t dma_tx_addr; /* Mapped ADMA descr. table */
+#ifdef SDDMA_RX_FIX
+	struct tasklet_struct rx_tasklet;
+#else
+	struct timer_list rx_timer;
+#endif
+	struct tasklet_struct tx_tasklet;
+	spinlock_t lock;
+	int tx_done;
+	int tx_count;
+};
+
+
+static inline struct uart_8250_port *
+to_uart_8250_port(struct uart_port *uart) {
+	return container_of(uart, struct uart_8250_port, port);
+}
+
+struct irq_info {
+	spinlock_t lock;
+	struct uart_8250_port *up;
+};
+
+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)
+
+// SDMA - software Layer : ast-uart-sdma.c
+
+#define AST_UART_SDMA_DEBUG 1
+
+#ifdef AST_UART_SDMA_DEBUG
+#define SDMADBUG(fmt, args...)  pr_debug("%s() " fmt, __func__, ## args)
+#else
+#define SDMADBUG(fmt, args...)
+#endif
+
+static inline void ast_uart_sdma_write(struct ast_sdma *sdma,
+						u32 val, u32 reg)
+{
+	// SDMADBUG("uart dma write : val: %x , reg : %x\n",val,reg);
+	writel(val, sdma->reg_base + reg);
+}
+
+static inline u32 ast_uart_sdma_read(struct ast_sdma *sdma, u32 reg)
+{
+	return readl(sdma->reg_base + reg);
+}
+
+struct ast_sdma ast_uart_sdma;
+
+int ast_uart_rx_sdma_enqueue(u8 ch, dma_addr_t rx_buff)
+{
+	unsigned long flags;
+	struct ast_sdma *sdma = &ast_uart_sdma;
+
+	SDMADBUG("ch = %d, rx buff = %x\n", ch, rx_buff);
+
+	local_irq_save(flags);
+	ast_uart_sdma_write(sdma, rx_buff, UART_RX_SDMA_ADDR(ch));
+	local_irq_restore(flags);
+
+	return 0;
+}
+
+int ast_uart_tx_sdma_enqueue(u8 ch, dma_addr_t tx_buff)
+{
+	unsigned long flags;
+	struct ast_sdma *sdma = &ast_uart_sdma;
+
+	SDMADBUG("ch = %d, tx buff = %x\n", ch, tx_buff);
+
+	local_irq_save(flags);
+	ast_uart_sdma_write(sdma, tx_buff, UART_TX_SDMA_ADDR(ch));
+	local_irq_restore(flags);
+
+	return 0;
+}
+
+int ast_uart_rx_sdma_ctrl(u8 ch, enum ast_uart_chan_op op)
+{
+	unsigned long flags;
+	struct ast_sdma *sdma = &ast_uart_sdma;
+	struct ast_sdma_info *dma_ch = &(sdma->dma_ch->rx_dma_info[ch]);
+
+	SDMADBUG("RX DMA CTRL [ch %d]\n", ch);
+
+	local_irq_save(flags);
+
+	switch (op) {
+	case AST_UART_DMAOP_TRIGGER:
+	SDMADBUG("Trigger\n");
+	dma_ch->enable = 1;
+#ifdef SDDMA_RX_FIX
+#else
+	ast_uart_set_sdma_time_out(0xffff);
+#endif
+	// set enable
+	ast_uart_sdma_write(sdma,
+			ast_uart_sdma_read(sdma, UART_RX_SDMA_EN) | (0x1 << ch),
+			UART_RX_SDMA_EN);
+	break;
+	case AST_UART_DMAOP_STOP:
+	// disable engine
+	SDMADBUG("STOP\n");
+	dma_ch->enable = 0;
+	ast_uart_sdma_write(sdma, ast_uart_sdma_read(sdma, UART_RX_SDMA_EN) &
+				  ~(0x1 << ch),
+			UART_RX_SDMA_EN);
+	// set reset
+	ast_uart_sdma_write(sdma, ast_uart_sdma_read(sdma, UART_RX_SDMA_REST) |
+				  (0x1 << ch),
+			UART_RX_SDMA_REST);
+	ast_uart_sdma_write(sdma, ast_uart_sdma_read(sdma, UART_RX_SDMA_REST) &
+				  ~(0x1 << ch),
+			UART_RX_SDMA_REST);
+
+	ast_uart_sdma_write(sdma, 0, UART_RX_R_POINT(ch));
+	ast_uart_sdma_write(sdma, dma_ch->dma_phy_addr, UART_RX_SDMA_ADDR(ch));
+	break;
+	case AST_UART_DMAOP_PAUSE:
+	// disable engine
+	dma_ch->enable = 0;
+	ast_uart_sdma_write(sdma, ast_uart_sdma_read(sdma, UART_RX_SDMA_EN) &
+				  ~(0x1 << ch),
+			UART_RX_SDMA_EN);
+	break;
+	}
+
+	local_irq_restore(flags);
+	return 0;
+}
+
+int ast_uart_tx_sdma_ctrl(u8 ch, enum ast_uart_chan_op op)
+{
+	unsigned long flags;
+	struct ast_sdma *sdma = &ast_uart_sdma;
+	struct ast_sdma_info *dma_ch = &(sdma->dma_ch->tx_dma_info[ch]);
+
+	SDMADBUG("TX DMA CTRL [ch %d]\n", ch);
+
+	local_irq_save(flags);
+
+	switch (op) {
+	case AST_UART_DMAOP_TRIGGER:
+	SDMADBUG("TRIGGER : Enable\n");
+	dma_ch->enable = 1;
+	// set enable
+	ast_uart_sdma_write(sdma,
+			ast_uart_sdma_read(sdma, UART_TX_SDMA_EN) | (0x1 << ch),
+			UART_TX_SDMA_EN);
+	break;
+	case AST_UART_DMAOP_STOP:
+	SDMADBUG("STOP : DISABLE & RESET\n");
+	dma_ch->enable = 0;
+	// disable engine
+	ast_uart_sdma_write(sdma, ast_uart_sdma_read(sdma, UART_TX_SDMA_EN) &
+				  ~(0x1 << ch),
+			UART_TX_SDMA_EN);
+	// set reset
+	ast_uart_sdma_write(sdma, ast_uart_sdma_read(sdma, UART_TX_SDMA_REST) |
+				  (0x1 << ch),
+			UART_TX_SDMA_REST);
+	ast_uart_sdma_write(sdma, ast_uart_sdma_read(sdma, UART_TX_SDMA_REST) &
+				  ~(0x1 << ch),
+			UART_TX_SDMA_REST);
+
+	ast_uart_sdma_write(sdma, 0, UART_TX_W_POINT(ch));
+	break;
+	case AST_UART_DMAOP_PAUSE:
+	SDMADBUG("PAUSE : DISABLE\n");
+	dma_ch->enable = 0;
+	// disable engine
+	ast_uart_sdma_write(sdma, ast_uart_sdma_read(sdma, UART_TX_SDMA_EN) &
+				  ~(0x1 << ch),
+			UART_TX_SDMA_EN);
+	}
+
+	local_irq_restore(flags);
+	return 0;
+}
+
+u32 ast_uart_get_tx_sdma_pt(u8 ch)
+{
+	struct ast_sdma *sdma = &ast_uart_sdma;
+
+	return ast_uart_sdma_read(sdma, UART_TX_R_POINT(ch));
+}
+
+int ast_uart_tx_sdma_update(u8 ch, u16 point)
+{
+	unsigned long flags;
+	struct ast_sdma *sdma = &ast_uart_sdma;
+
+	SDMADBUG("TX DMA CTRL [ch %d] point %d\n", ch, point);
+	local_irq_save(flags);
+	ast_uart_sdma_write(sdma, point, UART_TX_W_POINT(ch));
+	local_irq_restore(flags);
+	return 0;
+}
+
+int ast_uart_tx_sdma_request(u8 ch, ast_uart_dma_cbfn_t rtn, void *id)
+{
+	unsigned long flags;
+	struct ast_sdma *sdma = &ast_uart_sdma;
+	struct ast_sdma_info *dma_ch = &(sdma->dma_ch->tx_dma_info[ch]);
+
+	SDMADBUG("TX DMA REQUEST ch = %d\n", ch);
+
+	local_irq_save(flags);
+
+	if (dma_ch->enable) {
+		local_irq_restore(flags);
+		return -EBUSY;
+	}
+	dma_ch->priv = id;
+	dma_ch->callback_fn = rtn;
+
+	// DMA IRQ En
+	ast_uart_sdma_write(sdma,
+		      ast_uart_sdma_read(sdma, UART_TX_SDMA_IER) | (1 << ch),
+		      UART_TX_SDMA_IER);
+
+	local_irq_restore(flags);
+
+	return 0;
+}
+
+int ast_uart_rx_sdma_update(u8 ch, u16 point)
+{
+	unsigned long flags;
+	struct ast_sdma *sdma = &ast_uart_sdma;
+
+	SDMADBUG("RX DMA CTRL [ch %d] point %x\n", ch, point);
+
+	local_irq_save(flags);
+	ast_uart_sdma_write(sdma, point, UART_RX_R_POINT(ch));
+	local_irq_restore(flags);
+	return 0;
+}
+
+#ifdef SDDMA_RX_FIX
+char *ast_uart_rx_sdma_request(u8 ch, ast_uart_dma_cbfn_t rtn, void *id)
+{
+	unsigned long flags;
+	struct ast_sdma *sdma = &ast_uart_sdma;
+	struct ast_sdma_info *dma_ch = &(sdma->dma_ch->rx_dma_info[ch]);
+
+	SDMADBUG("RX DMA REQUEST ch = %d\n", ch);
+
+	local_irq_save(flags);
+
+	if (dma_ch->enable) {
+		local_irq_restore(flags);
+		return 0;
+	}
+	dma_ch->priv = id;
+
+	dma_ch->callback_fn = rtn;
+
+	// DMA IRQ En
+	ast_uart_sdma_write(sdma,
+		      ast_uart_sdma_read(sdma, UART_RX_SDMA_IER) | (1 << ch),
+		      UART_RX_SDMA_IER);
+
+	local_irq_restore(flags);
+
+	return dma_ch->sdma_virt_addr;
+}
+
+#else
+char *ast_uart_rx_sdma_request(u8 ch, void *id)
+{
+	unsigned long flags;
+	struct ast_sdma *sdma = &ast_uart_sdma;
+	struct ast_sdma_info *dma_ch = &(sdma->dma_ch->rx_dma_info[ch]);
+
+	SDMADBUG("RX DMA REQUEST ch = %d\n", ch);
+
+	local_irq_save(flags);
+
+	if (dma_ch->enable) {
+		local_irq_restore(flags);
+		return -EBUSY;
+	}
+	dma_ch->priv = id;
+
+	local_irq_restore(flags);
+	return dma_ch->sdma_virt_addr;
+}
+#endif
+
+u16 ast_uart_get_rx_sdma_pt(u8 ch)
+{
+	struct ast_sdma *sdma = &ast_uart_sdma;
+
+	return ast_uart_sdma_read(sdma, UART_RX_W_POINT(ch));
+}
+
+void ast_uart_set_sdma_time_out(u16 val)
+{
+	struct ast_sdma *sdma = &ast_uart_sdma;
+
+	ast_uart_sdma_write(sdma, val, UART_SDMA_TIMER);
+}
+
+static inline void ast_sdma_bufffdone(struct ast_sdma_info *sdma_ch)
+{
+	u32 len;
+	struct ast_sdma *sdma = &ast_uart_sdma;
+
+	if (sdma_ch->enable == 0) {
+		SDMADBUG("sdma Please check ch_no %x %s!!!!!\n",
+			sdma_ch->ch_no, sdma_ch->direction ? "TX" : "RX");
+		if (sdma_ch->direction) {
+			ast_uart_sdma_write(sdma,
+				ast_uart_sdma_read(sdma, UART_TX_SDMA_EN)
+				& ~(0x1 << sdma_ch->ch_no), UART_TX_SDMA_EN);
+		} else {
+			ast_uart_sdma_write(sdma,
+				ast_uart_sdma_read(sdma, UART_RX_SDMA_EN) &
+				~(0x1 << sdma_ch->ch_no), UART_RX_SDMA_EN);
+			ast_uart_rx_sdma_update(sdma_ch->ch_no,
+				ast_uart_get_rx_sdma_pt(sdma_ch->ch_no));
+			SDMADBUG("OFFSET : UART_RX_SDMA_EN = %x\n ",
+				ast_uart_sdma_read(sdma, UART_RX_SDMA_EN));
+		}
+		return;
+	}
+
+	if (sdma_ch->direction) {
+		len = ast_uart_sdma_read(sdma, UART_TX_R_POINT(sdma_ch->ch_no));
+		SDMADBUG("tx rp %x , wp %x\n",
+		ast_uart_sdma_read(sdma, UART_TX_R_POINT(sdma_ch->ch_no)),
+		ast_uart_sdma_read(sdma, UART_TX_W_POINT(sdma_ch->ch_no))
+		);
+	} else {
+		SDMADBUG("rx rp %x , wp %x\n",
+		ast_uart_sdma_read(sdma, UART_RX_R_POINT(sdma_ch->ch_no)),
+		ast_uart_sdma_read(sdma, UART_RX_W_POINT(sdma_ch->ch_no))
+		);
+		len = ast_uart_sdma_read(sdma, UART_RX_W_POINT(sdma_ch->ch_no));
+	}
+
+	SDMADBUG("<dma dwn>: ch[%d] : %s ,len : %d\n", sdma_ch->ch_no,
+				sdma_ch->direction ? "tx" : "rx", len);
+
+	if (sdma_ch->callback_fn != NULL)
+		(sdma_ch->callback_fn)(sdma_ch->priv, len);
+}
+
+static irqreturn_t ast_uart_sdma_irq(int irq, void *dev_id)
+{
+	struct ast_sdma *sdma = (struct ast_sdma *)dev_id;
+
+	u32 tx_sts = ast_uart_sdma_read(sdma, UART_TX_SDMA_ISR);
+	u32 rx_sts = ast_uart_sdma_read(sdma, UART_RX_SDMA_ISR);
+
+	SDMADBUG("tx sts : %x, rx sts : %x\n", tx_sts, rx_sts);
+
+	if ((tx_sts == 0) && (rx_sts == 0)) {
+		SDMADBUG("SDMA IRQ ERROR !!!\n");
+		return IRQ_HANDLED;
+	}
+
+	if (rx_sts & UART_SDMA0_INT) {
+		ast_uart_sdma_write(sdma, UART_SDMA0_INT, UART_RX_SDMA_ISR);
+		ast_sdma_bufffdone(&(sdma->dma_ch->rx_dma_info[0]));
+	} else if (rx_sts & UART_SDMA1_INT) {
+		ast_uart_sdma_write(sdma, UART_SDMA1_INT, UART_RX_SDMA_ISR);
+		ast_sdma_bufffdone(&(sdma->dma_ch->rx_dma_info[1]));
+	} else if (rx_sts & UART_SDMA2_INT) {
+		ast_uart_sdma_write(sdma, UART_SDMA2_INT, UART_RX_SDMA_ISR);
+		ast_sdma_bufffdone(&(sdma->dma_ch->rx_dma_info[2]));
+	} else if (rx_sts & UART_SDMA3_INT) {
+		ast_uart_sdma_write(sdma, UART_SDMA3_INT, UART_RX_SDMA_ISR);
+		ast_sdma_bufffdone(&(sdma->dma_ch->rx_dma_info[3]));
+	} else if (rx_sts & UART_SDMA4_INT) {
+		ast_uart_sdma_write(sdma, UART_SDMA4_INT, UART_RX_SDMA_ISR);
+		ast_sdma_bufffdone(&(sdma->dma_ch->rx_dma_info[4]));
+	} else if (rx_sts & UART_SDMA5_INT) {
+		ast_uart_sdma_write(sdma, UART_SDMA5_INT, UART_RX_SDMA_ISR);
+		ast_sdma_bufffdone(&(sdma->dma_ch->rx_dma_info[5]));
+	} else if (rx_sts & UART_SDMA6_INT) {
+		ast_uart_sdma_write(sdma, UART_SDMA6_INT, UART_RX_SDMA_ISR);
+		ast_sdma_bufffdone(&(sdma->dma_ch->rx_dma_info[6]));
+	} else if (rx_sts & UART_SDMA7_INT) {
+		ast_uart_sdma_write(sdma, UART_SDMA7_INT, UART_RX_SDMA_ISR);
+		ast_sdma_bufffdone(&(sdma->dma_ch->rx_dma_info[7]));
+	} else if (rx_sts & UART_SDMA8_INT) {
+		ast_uart_sdma_write(sdma, UART_SDMA8_INT, UART_RX_SDMA_ISR);
+		ast_sdma_bufffdone(&(sdma->dma_ch->rx_dma_info[8]));
+	} else if (rx_sts & UART_SDMA9_INT) {
+		ast_uart_sdma_write(sdma, UART_SDMA9_INT, UART_RX_SDMA_ISR);
+		ast_sdma_bufffdone(&(sdma->dma_ch->rx_dma_info[9]));
+	} else if (rx_sts & UART_SDMA10_INT) {
+		ast_uart_sdma_write(sdma, UART_SDMA10_INT, UART_RX_SDMA_ISR);
+		ast_sdma_bufffdone(&(sdma->dma_ch->rx_dma_info[10]));
+	} else if (rx_sts & UART_SDMA11_INT) {
+		ast_uart_sdma_write(sdma, UART_SDMA11_INT, UART_RX_SDMA_ISR);
+		ast_sdma_bufffdone(&(sdma->dma_ch->rx_dma_info[11]));
+	} else {
+
+	}
+
+	if (tx_sts & UART_SDMA0_INT) {
+		ast_uart_sdma_write(sdma, UART_SDMA0_INT, UART_TX_SDMA_ISR);
+		ast_sdma_bufffdone(&(sdma->dma_ch->tx_dma_info[0]));
+	} else if (tx_sts & UART_SDMA1_INT) {
+		ast_uart_sdma_write(sdma, UART_SDMA1_INT, UART_TX_SDMA_ISR);
+		ast_sdma_bufffdone(&(sdma->dma_ch->tx_dma_info[1]));
+	} else if (tx_sts & UART_SDMA2_INT) {
+		ast_uart_sdma_write(sdma, UART_SDMA2_INT, UART_TX_SDMA_ISR);
+		ast_sdma_bufffdone(&(sdma->dma_ch->tx_dma_info[2]));
+	} else if (tx_sts & UART_SDMA3_INT) {
+		ast_uart_sdma_write(sdma, UART_SDMA3_INT, UART_TX_SDMA_ISR);
+		ast_sdma_bufffdone(&(sdma->dma_ch->tx_dma_info[3]));
+	} else if (tx_sts & UART_SDMA4_INT) {
+		ast_uart_sdma_write(sdma, UART_SDMA4_INT, UART_TX_SDMA_ISR);
+		ast_sdma_bufffdone(&(sdma->dma_ch->tx_dma_info[4]));
+	} else if (tx_sts & UART_SDMA5_INT) {
+		ast_uart_sdma_write(sdma, UART_SDMA5_INT, UART_TX_SDMA_ISR);
+		ast_sdma_bufffdone(&(sdma->dma_ch->tx_dma_info[5]));
+	} else if (tx_sts & UART_SDMA6_INT) {
+		ast_uart_sdma_write(sdma, UART_SDMA6_INT, UART_TX_SDMA_ISR);
+		ast_sdma_bufffdone(&(sdma->dma_ch->tx_dma_info[6]));
+	} else if (tx_sts & UART_SDMA7_INT) {
+		ast_uart_sdma_write(sdma, UART_SDMA7_INT, UART_TX_SDMA_ISR);
+		ast_sdma_bufffdone(&(sdma->dma_ch->tx_dma_info[7]));
+	} else if (tx_sts & UART_SDMA8_INT) {
+		ast_uart_sdma_write(sdma, UART_SDMA8_INT, UART_TX_SDMA_ISR);
+		ast_sdma_bufffdone(&(sdma->dma_ch->tx_dma_info[8]));
+	} else if (tx_sts & UART_SDMA9_INT) {
+		ast_uart_sdma_write(sdma, UART_SDMA9_INT, UART_TX_SDMA_ISR);
+		ast_sdma_bufffdone(&(sdma->dma_ch->tx_dma_info[9]));
+	} else if (tx_sts & UART_SDMA10_INT) {
+		ast_uart_sdma_write(sdma, UART_SDMA10_INT, UART_TX_SDMA_ISR);
+		ast_sdma_bufffdone(&(sdma->dma_ch->tx_dma_info[10]));
+	} else if (tx_sts & UART_SDMA11_INT) {
+		ast_uart_sdma_write(sdma, UART_SDMA11_INT, UART_TX_SDMA_ISR);
+		ast_sdma_bufffdone(&(sdma->dma_ch->tx_dma_info[11]));
+	} else {
+	}
+
+	return IRQ_HANDLED;
+}
+
+static int ast_uart_sdma_probe(void)
+{
+	int i;
+	struct device_node *node;
+	int ret;
+	struct ast_sdma *sdma = &ast_uart_sdma;
+	char *rx_dma_virt_addr;
+	dma_addr_t rx_dma_phy_addr;
+
+	sdma->dma_ch = kzalloc(sizeof(struct ast_sdma_ch), GFP_KERNEL);
+	if (!sdma->dma_ch)
+		return -ENOMEM;
+
+	// sdma memory mapping
+	node = of_find_compatible_node(NULL, NULL, "aspeed,ast-uart-sdma");
+	if (!node)
+		return -ENODEV;
+
+	sdma->reg_base = of_iomap(node, 0);
+	if (IS_ERR(sdma->reg_base))
+		return PTR_ERR(sdma->map);
+	rx_dma_virt_addr = dma_alloc_coherent(NULL,
+	SDMA_RX_BUFF_SIZE * AST_UART_SDMA_CH, &rx_dma_phy_addr, GFP_KERNEL);
+
+	if (!rx_dma_virt_addr)
+		SDMADBUG(" rx_dma_virt_addr Errr : unable top alloc\n");
+
+	for (i = 0; i < AST_UART_SDMA_CH; i++) {
+		// TX ------------------------
+		sdma->dma_ch->tx_dma_info[i].enable = 0;
+		sdma->dma_ch->tx_dma_info[i].ch_no = i;
+		sdma->dma_ch->tx_dma_info[i].direction = 1;
+		ast_uart_sdma_write(sdma, 0, UART_TX_W_POINT(i));
+		// RX ------------------------
+		sdma->dma_ch->rx_dma_info[i].enable = 0;
+		sdma->dma_ch->rx_dma_info[i].ch_no = i;
+		sdma->dma_ch->rx_dma_info[i].direction = 0;
+		sdma->dma_ch->rx_dma_info[i].sdma_virt_addr =
+		rx_dma_virt_addr + (SDMA_RX_BUFF_SIZE * i);
+		sdma->dma_ch->rx_dma_info[i].dma_phy_addr =
+		rx_dma_phy_addr + (SDMA_RX_BUFF_SIZE * i);
+		ast_uart_sdma_write(sdma,
+			sdma->dma_ch->rx_dma_info[i].dma_phy_addr,
+			UART_RX_SDMA_ADDR(i));
+		ast_uart_sdma_write(sdma, 0, UART_RX_R_POINT(i));
+	}
+
+	ast_uart_sdma_write(sdma, 0xffffffff, UART_TX_SDMA_REST);
+	ast_uart_sdma_write(sdma, 0x0, UART_TX_SDMA_REST);
+
+	ast_uart_sdma_write(sdma, 0xffffffff, UART_RX_SDMA_REST);
+	ast_uart_sdma_write(sdma, 0x0, UART_RX_SDMA_REST);
+
+	ast_uart_sdma_write(sdma, 0, UART_TX_SDMA_EN);
+	ast_uart_sdma_write(sdma, 0, UART_RX_SDMA_EN);
+
+#ifdef SDDMA_RX_FIX
+	ast_uart_sdma_write(sdma, 0x200, UART_SDMA_TIMER);
+#else
+	ast_uart_sdma_write(sdma, 0xffff, UART_SDMA_TIMER);
+#endif
+
+	// TX
+	ast_uart_sdma_write(sdma, 0xfff, UART_TX_SDMA_ISR);
+	ast_uart_sdma_write(sdma, 0, UART_TX_SDMA_IER);
+
+	// RX
+	ast_uart_sdma_write(sdma, 0xfff, UART_RX_SDMA_ISR);
+	ast_uart_sdma_write(sdma, 0, UART_RX_SDMA_IER);
+
+	sdma->dma_irq = of_irq_get(node, 0);
+	ret = request_irq(sdma->dma_irq, ast_uart_sdma_irq, 0,
+						"sdma-intr", sdma);
+	if (ret) {
+		SDMADBUG("Unable to get UART SDMA IRQ %x\n", ret);
+		return -ENODEV;
+	}
+
+	ast_uart_sdma_write(sdma, SDMA_SET_TX_BUFF_SIZE(SDMA_BUFF_SIZE_4KB) |
+				SDMA_SET_RX_BUFF_SIZE(SDMA_BUFF_SIZE_64KB),
+		      UART_SDMA_CONF);
+	return 0;
+}
+
+// END of SDMA Layer
+
+// UART Driver Layer
+
+static unsigned int ast_serial_in(struct uart_8250_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 uart_8250_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 uart_8250_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 uart_8250_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_sdma_tasklet_func(unsigned long data)
+{
+
+	struct ast_uart_priv_data *priv = (struct ast_uart_priv_data *)data;
+	struct uart_8250_port *up = priv->up;
+	struct circ_buf *xmit = NULL;
+	u32 tx_pt;
+
+
+	if (!up)
+		return;
+	xmit = &up->port.state->xmit;
+	spin_lock(&up->port.lock);
+	priv->tx_count = CIRC_CNT(xmit->head, xmit->tail, UART_XMIT_SIZE);
+	dma_sync_single_for_device(up->port.dev, priv->dma_tx_addr,
+					UART_XMIT_SIZE, DMA_TO_DEVICE);
+	tx_pt = ast_uart_get_tx_sdma_pt(priv->dma_ch);
+
+	if (tx_pt > xmit->head)	{
+		if ((tx_pt & 0xfffc) == 0)
+			ast_uart_tx_sdma_update(priv->dma_ch, 0xffff);
+		else
+			ast_uart_tx_sdma_update(priv->dma_ch, 0);
+	} else {
+		ast_uart_tx_sdma_update(priv->dma_ch, xmit->head);
+	}
+	ast_uart_tx_sdma_update(priv->dma_ch, xmit->head);
+	spin_unlock(&up->port.lock);
+}
+
+static void ast_uart_tx_buffdone(void *dev_id, u16 len)
+{
+
+	struct ast_uart_priv_data *priv = (struct ast_uart_priv_data *)dev_id;
+	struct uart_8250_port *up = priv->up;
+	struct circ_buf *xmit;
+
+	if (!up)
+		return;
+	xmit = &(up->port.state->xmit);
+
+	UART_TX_DBG("line[%d] : tx len = % d\n", priv->line, len);
+	spin_lock(&up->port.lock);
+	xmit->tail = len;
+	UART_TX_DBG(" line[%d], xmit->head = %d, xmit->tail = % d\n",
+			priv->line, xmit->head, xmit->tail);
+
+	if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
+		uart_write_wakeup(&up->port);
+
+	if (xmit->head != xmit->tail)
+		tasklet_schedule(&priv->tx_tasklet);
+
+	spin_unlock(&up->port.lock);
+}
+
+#ifdef SDDMA_RX_FIX
+static void ast_uart_rx_sdma_tasklet_func(unsigned long data)
+{
+	struct ast_uart_priv_data *priv = (struct ast_uart_priv_data *)data;
+	struct circ_buf *rx_ring = &priv->rx_dma_buf;
+	struct tty_port *ttyport;
+	int count;
+	int copy = 0;
+	struct uart_8250_port *up = priv->up;
+
+	if (!up)
+		return;
+
+	ttyport = &up->port.state->port;
+
+	UART_DBG("line[%d], rx_ring->head = % d, rx_ring->tail = % d\n",
+			 up->port.line, rx_ring->head, rx_ring->tail);
+	spin_lock(&up->port.lock);
+	if (rx_ring->head > rx_ring->tail) {
+		count = rx_ring->head - rx_ring->tail;
+		copy = tty_insert_flip_string(ttyport,
+				rx_ring->buf + rx_ring->tail, count);
+	} else if (rx_ring->head < rx_ring->tail) {
+		count = SDMA_RX_BUFF_SIZE - rx_ring->tail;
+		copy = tty_insert_flip_string(ttyport,
+					rx_ring->buf + rx_ring->tail, count);
+	} else {
+		count = 0;
+	}
+
+	if (copy != count)
+		UART_DBG(" !!!!!!!!ERROR 111\n");
+	if (count) {
+		rx_ring->tail += count;
+		rx_ring->tail &= (SDMA_RX_BUFF_SIZE - 1);
+		up->port.icount.rx += count;
+		tty_flip_buffer_push(ttyport);
+		ast_uart_rx_sdma_update(priv->dma_ch, rx_ring->tail);
+	}
+	spin_unlock(&up->port.lock);
+}
+
+static void ast_uart_rx_buffdone(void *dev_id, u16 len)
+{
+	struct ast_uart_priv_data *priv = (struct ast_uart_priv_data *)dev_id;
+	struct circ_buf *rx_ring = &priv->rx_dma_buf;
+	struct uart_8250_port *up = priv->up;
+
+	if (!up)
+		return;
+	UART_DBG("line[%d], head = %d,len:%d\n",
+			 priv->line, priv->rx_dma_buf.head, len);
+	spin_lock(&up->port.lock);
+	rx_ring->head = len;
+	spin_unlock(&up->port.lock);
+	tasklet_schedule(&priv->rx_tasklet);
+}
+
+#else
+static void ast_uart_rx_timer_func(unsigned long data)
+{
+	struct ast_uart_priv_data *priv = (struct ast_uart_priv_data *)data;
+	struct uart_8250_port *up = priv->up;
+	struct tty_port *ttyport;
+	struct circ_buf *rx_ring;
+	struct tty_struct *tty;
+	char flag;
+	int count;
+	int copy;
+
+
+	if (!up)
+		return;
+	ttyport = &up->port.state->port;
+	rx_ring = &up->rx_dma_buf;
+	tty = up->port.state->port.tty;
+
+	UART_DBG("line[%d], rx_ring->head = % d, rx_ring->tail = % d\n",
+				up->port.line, rx_ring->head, rx_ring->tail);
+	rx_ring->head = ast_uart_get_rx_sdma_pt(priv->dma_ch);
+	del_timer(&up->rx_timer);
+
+	if (rx_ring->head > rx_ring->tail) {
+		ast_uart_set_sdma_time_out(0xffff);
+		count = rx_ring->head - rx_ring->tail;
+		copy = tty_insert_flip_string(ttyport,
+				 rx_ring->buf + rx_ring->tail, count);
+	} else if (rx_ring->head < rx_ring->tail) {
+		ast_uart_set_sdma_time_out(0xffff);
+		count = SDMA_RX_BUFF_SIZE - rx_ring->tail;
+		copy = tty_insert_flip_string(ttyport,
+				 rx_ring->buf + rx_ring->tail, count);
+	} else {
+		count = 0;
+		// UART_DBG("@@--%s-- ch = 0x%x\n", __func__, ch);
+	}
+
+	if (copy != count)
+		UART_DBG(" !!!!!!!!ERROR 111\n");
+		rx_ring->tail += count;
+		rx_ring->tail &= (SDMA_RX_BUFF_SIZE - 1);
+
+	if (count) {
+		//UART_DBG("\n count = % d\n", count);
+		up->port.icount.rx += count;
+		spin_lock(&up->port.lock);
+		tty_flip_buffer_push(ttyport);
+		spin_unlock(&up->port.lock);
+		//UART_DBG("update rx_ring->tail % x\n", rx_ring->tail);
+		ast_uart_rx_sdma_update(priv->dma_ch, rx_ring->tail);
+		priv->workaround = 1;
+	} else {
+		if (priv->workaround) {
+			priv->workaround++;
+			if (priv->workaround > 1)
+				ast_uart_set_sdma_time_out(0);
+			else
+				ast_uart_set_sdma_time_out(0xffff);
+		}
+	}
+	add_timer(&up->rx_timer);
+}
+#endif
+
+/*
+ * FIFO support.
+ */
+static inline void ast25xx_uart_clear_fifos(struct uart_8250_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 uart_8250_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%04lx, 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.
+	 */
+	ast25xx_uart_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 uart_8250_port *p)
+{
+	if (p->ier & UART_IER_THRI) {
+		p->ier &= ~UART_IER_THRI;
+		ast_serial_out(p, UART_IER, p->ier);
+	}
+}
+
+static void ast25xx_uart_stop_tx(struct uart_port *port)
+{
+	struct uart_8250_port *up = to_uart_8250_port(port);
+
+	UART_TX_DBG("line[%d]\n", up->port.line);
+	__stop_tx(up);
+}
+
+static void transmit_chars(struct uart_8250_port *up);
+
+static void ast25xx_uart_start_tx(struct uart_port *port)
+{
+	struct uart_8250_port *up = to_uart_8250_port(port);
+	struct ast_uart_priv_data *priv = up->port.private_data;
+
+	UART_TX_DBG("line[%d]\n", port->line);
+	tasklet_schedule(&priv->tx_tasklet);
+}
+
+static void ast25xx_uart_stop_rx(struct uart_port *port)
+{
+	struct uart_8250_port *up = to_uart_8250_port(port);
+
+	UART_DBG("line[%d]\n", port->line);
+	up->ier &= ~UART_IER_RLSI;
+	up->port.read_status_mask &= ~UART_LSR_DR;
+	ast_serial_out(up, UART_IER, up->ier);
+}
+
+static void ast25xx_uart_enable_ms(struct uart_port *port)
+{
+	struct uart_8250_port *up = to_uart_8250_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 uart_8250_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)) {
+		ast25xx_uart_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 uart_8250_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 ast25xx_uart_handle_port(struct uart_8250_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 uart_8250_port *up;
+		unsigned int iir;
+
+		up = (struct uart_8250_port *)(i->up);
+
+		iir = ast_serial_in(up, UART_IIR);
+		if (!(iir & UART_IIR_NO_INT)) {
+			ast25xx_uart_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 irq%d\n", irq);
+			break;
+		}
+	} while (end);
+
+	spin_unlock(&i->lock);
+
+	DEBUG_INTR("end.\n");
+
+	return IRQ_RETVAL(handled);
+}
+
+static unsigned int ast25xx_uart_tx_empty(struct uart_port *port)
+{
+	struct uart_8250_port *up = to_uart_8250_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 ast25xx_uart_get_mctrl(struct uart_port *port)
+{
+	struct uart_8250_port *up = to_uart_8250_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 ast25xx_uart_set_mctrl(struct uart_port *port, unsigned int mctrl)
+{
+	struct uart_8250_port *up = to_uart_8250_port(port);
+	unsigned char mcr = 0;
+
+	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 ast25xx_uart_break_ctl(struct uart_port *port, int break_state)
+{
+	struct uart_8250_port *up = to_uart_8250_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 ast25xx_uart_startup(struct uart_port *port)
+{
+	struct uart_8250_port *up = to_uart_8250_port(port);
+	// TX DMA
+	struct circ_buf *xmit = &up->port.state->xmit;
+	struct ast_uart_priv_data *priv = up->port.private_data;
+	unsigned long flags;
+	unsigned char lsr, iir;
+	int retval;
+	int irq_flags = up->port.flags & UPF_SHARE_IRQ ? IRQF_SHARED : 0;
+
+	UART_DBG("%s\n", __func__);
+
+	priv->up = up;
+
+	UART_DBG("line[%d]\n", port->line);
+	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())
+	 */
+	ast25xx_uart_clear_fifos(up);
+	/*
+	 * 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;
+
+	ast25xx_uart_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
+	priv->rx_dma_buf.head = 0;
+	priv->rx_dma_buf.tail = 0;
+	up->port.icount.rx = 0;
+
+	priv->tx_done = 1;
+	priv->tx_count = 0;
+
+	priv->rx_dma_buf.head = 0;
+	priv->rx_dma_buf.tail = 0;
+#ifdef SDDMA_RX_FIX
+#else
+	priv->workaround = 0;
+#endif
+	// UART_DBG("Sending trigger for % x\n", priv->dma_ch);
+	ast_uart_rx_sdma_ctrl(priv->dma_ch, AST_UART_DMAOP_STOP);
+	ast_uart_rx_sdma_ctrl(priv->dma_ch, AST_UART_DMAOP_TRIGGER);
+#ifdef SDDMA_RX_FIX
+#else
+	add_timer(&priv->rx_timer);
+#endif
+	priv->tx_dma_buf.head = 0;
+	priv->tx_dma_buf.tail = 0;
+	priv->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;
+
+	priv->dma_tx_addr = dma_map_single(port->dev, priv->tx_dma_buf.buf,
+				   UART_XMIT_SIZE, DMA_TO_DEVICE);
+
+	ast_uart_tx_sdma_ctrl(priv->dma_ch, AST_UART_DMAOP_STOP);
+	ast_uart_tx_sdma_enqueue(priv->dma_ch, priv->dma_tx_addr);
+	ast_uart_tx_sdma_update(priv->dma_ch, 0);
+	ast_uart_tx_sdma_ctrl(priv->dma_ch, AST_UART_DMAOP_TRIGGER);
+	return 0;
+}
+
+static void ast25xx_uart_shutdown(struct uart_port *port)
+{
+	struct uart_8250_port *up = to_uart_8250_port(port);
+	struct ast_uart_priv_data *priv = up->port.private_data;
+	unsigned long flags;
+
+	UART_DBG("line[%d]\n", port->line);
+	priv->up = NULL;
+
+	up->ier = 0;
+	serial_outp(up, UART_IER, 0);
+
+	spin_lock_irqsave(&up->port.lock, flags);
+	up->port.mctrl &= ~TIOCM_OUT2;
+
+	ast25xx_uart_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);
+	ast25xx_uart_clear_fifos(up);
+
+	(void)ast_serial_in(up, UART_RX);
+
+	ast_uart_rx_sdma_ctrl(priv->dma_ch, AST_UART_DMAOP_PAUSE);
+	ast_uart_tx_sdma_ctrl(priv->dma_ch, AST_UART_DMAOP_PAUSE);
+#ifdef SDDMA_RX_FIX
+#else
+	del_timer_sync(&up->rx_timer);
+#endif
+
+	// Tx buffer will free by serial_core.c
+	free_irq(up->port.irq, ast_uart_irq);
+}
+
+static unsigned int ast25xx_uart_get_divisor(struct uart_port *port,
+					   unsigned int baud)
+{
+	unsigned int quot;
+
+	quot = uart_get_divisor(port, baud);
+
+	return quot;
+}
+
+static void ast25xx_uart_set_termios(struct uart_port *port,
+				   struct ktermios *termios,
+				   struct ktermios *old)
+{
+	struct uart_8250_port *up = to_uart_8250_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 = ast25xx_uart_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 */
+	ast25xx_uart_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);
+}
+
+/*
+ * Resource handling.
+ */
+static int ast25xx_uart_request_std_resource(struct uart_8250_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 ast25xx_uart_release_std_resource(struct uart_8250_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 ast25xx_uart_release_port(struct uart_port *port)
+{
+	struct uart_8250_port *up = to_uart_8250_port(port);
+
+	ast25xx_uart_release_std_resource(up);
+}
+
+static int ast25xx_uart_request_port(struct uart_port *port)
+{
+	struct uart_8250_port *up = to_uart_8250_port(port);
+	int ret;
+
+	ret = ast25xx_uart_request_std_resource(up);
+	if (ret == 0)
+		ast25xx_uart_release_std_resource(up);
+
+	return ret;
+}
+
+static void ast25xx_uart_config_port(struct uart_port *port, int flags)
+{
+	struct uart_8250_port *up = to_uart_8250_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 = ast25xx_uart_request_std_resource(up);
+	if (ret < 0)
+		return;
+
+	if (flags & UART_CONFIG_TYPE)
+		autoconfig(up);
+
+	if (up->port.type == PORT_UNKNOWN)
+		ast25xx_uart_release_std_resource(up);
+}
+
+static int ast25xx_uart_verify_port(struct uart_port *port,
+				  struct serial_struct *ser)
+{
+	return 0;
+}
+
+static const char *ast25xx_uart_type(struct uart_port *port)
+{
+	int type = port->type;
+
+	if (type >= ARRAY_SIZE(uart_config))
+		type = 0;
+	return uart_config[type].name;
+}
+
+
+
+static unsigned int ast25xx_uart_serial_in(struct uart_port *port, int offset)
+{
+	offset = offset << port->regshift;
+	return readb(port->membase + offset);
+
+}
+
+
+static void ast25xx_uart_serial_out(struct uart_port *port,
+						 int offset, int value)
+{
+	offset =  offset << port->regshift;
+	writeb(value, port->membase + offset);
+}
+
+static const struct uart_ops ast25xx_uart_pops = {
+	.tx_empty = ast25xx_uart_tx_empty,
+	.set_mctrl = ast25xx_uart_set_mctrl,
+	.get_mctrl = ast25xx_uart_get_mctrl,
+	.stop_tx = ast25xx_uart_stop_tx,
+	.start_tx = ast25xx_uart_start_tx,
+	.stop_rx = ast25xx_uart_stop_rx,
+	.enable_ms = ast25xx_uart_enable_ms,
+	.break_ctl = ast25xx_uart_break_ctl,
+	.startup = ast25xx_uart_startup,
+	.shutdown = ast25xx_uart_shutdown,
+	.set_termios = ast25xx_uart_set_termios,
+	.type = ast25xx_uart_type,
+	.release_port = ast25xx_uart_release_port,
+	.request_port = ast25xx_uart_request_port,
+	.config_port = ast25xx_uart_config_port,
+	.verify_port = ast25xx_uart_verify_port,
+};
+
+
+
+/*
+ * 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 ast25xx_uart_probe(struct platform_device *pdev)
+{
+	struct device_node *np = pdev->dev.of_node;
+	struct ast_uart_priv_data *priv;
+	struct uart_8250_port port_8250;
+	struct uart_8250_port *up;
+	int ret;
+	u32 read, dma_channel = 0;
+	struct resource *res;
+
+	if (UART_XMIT_SIZE > DMA_BUFF_SIZE)
+		UART_DBG("UART_XMIT_SIZE > DMA_BUFF_SIZE : Please Check\n");
+
+	priv = (struct ast_uart_priv_data *)devm_kzalloc(&pdev->dev,
+			sizeof(struct ast_uart_priv_data), GFP_KERNEL);
+	if (priv == NULL)
+		return -ENOMEM;
+
+	up = &port_8250;
+	memset(up, 0, sizeof(struct uart_8250_port));
+	up->port.flags = UPF_IOREMAP;
+
+	res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+	if (res == NULL) {
+		dev_err(&pdev->dev, "IRQ resource not found");
+		return -ENODEV;
+	}
+	up->port.irq = res->start;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!res) {
+		dev_err(&pdev->dev, "Register base not found");
+		return -ENODEV;
+	}
+	up->port.mapbase = res->start;
+
+	ret = ast25xx_uart_request_std_resource(up);
+	if (ret) {
+		dev_err(&pdev->dev, "ioremap_nocache Failed");
+		return ret;
+	}
+
+	clk = devm_clk_get(&pdev->dev, NULL);
+	if (IS_ERR(clk))
+		dev_err(&pdev->dev, "missing controller clock");
+
+	ret = clk_prepare_enable(clk);
+	if (ret)
+		dev_err(&pdev->dev, "failed to enable DMA UART Clk");
+
+	up->port.uartclk = clk_get_rate(clk);
+
+	if (of_property_read_u32(np, "reg-shift", &read) == 0)
+		up->port.regshift = read;
+	if (of_property_read_u32(np, "dma-channel", &read) == 0) {
+		dma_channel = read;
+		priv->dma_ch = dma_channel;
+	}
+	up->port.iotype = UPIO_MEM;
+	up->port.flags |= UPF_BOOT_AUTOCONF | UPF_SKIP_TEST;
+	up->port.dev        = &pdev->dev;
+	if (share_irqs)
+		up->port.flags |= UPF_SHARE_IRQ;
+	up->port.fifosize = uart_config[up->port.type].fifo_size;
+	up->port.type = PORT_16550;
+	up->port.iotype = UPIO_MEM;
+	up->port.flags =  UPF_FIXED_TYPE;
+	up->port.startup = ast25xx_uart_startup;
+	up->port.shutdown = ast25xx_uart_shutdown;
+	up->port.set_termios = ast25xx_uart_set_termios;
+	up->port.set_mctrl = ast25xx_uart_set_mctrl;
+	up->port.serial_in = ast25xx_uart_serial_in;
+	up->port.serial_out = ast25xx_uart_serial_out;
+	up->capabilities = uart_config[up->port.type].flags;
+	up->tx_loadsz = uart_config[up->port.type].tx_loadsz;
+	up->capabilities |= UART_CAP_FIFO;
+
+	up->port.private_data = priv;
+
+	ret = serial8250_register_8250_port(up);
+	if (ret < 0) {
+		dev_err(&pdev->dev,
+		"unable to registr port (IO%lx MEM%llx IRQ%d):%d\n",
+			up->port.iobase, (unsigned long long)up->port.mapbase,
+							 up->port.irq, ret);
+		return ret;
+	}
+	priv->line = ret;
+
+	tasklet_init(&priv->tx_tasklet, ast_uart_tx_sdma_tasklet_func,
+							 (unsigned long)priv);
+#ifdef SDDMA_RX_FIX
+	tasklet_init(&priv->rx_tasklet, ast_uart_rx_sdma_tasklet_func,
+							 (unsigned long)priv);
+#else
+	uart->rx_timer.data = (unsigned long)port;
+	uart->rx_timer.expires = jiffies + (HZ);
+	uart->rx_timer.function = ast_uart_rx_timer_func;
+	init_timer(&priv->rx_timer);
+#endif
+
+//DMA request
+#ifdef SDDMA_RX_FIX
+	priv->rx_dma_buf.buf =
+	ast_uart_rx_sdma_request(priv->dma_ch, ast_uart_rx_buffdone,
+			      priv);
+	if (priv->rx_dma_buf.buf < 0) {
+		UART_DBG("Error : failed to get rx dma channel[%d]\n",
+						 priv->dma_ch);
+		return -EBUSY;
+}
+#else
+	priv->rx_dma_buf.buf = ast_uart_rx_sdma_request(
+	priv->dma_ch, priv);
+	if (priv->rx_dma_buf.buf < 0) {
+		UART_DBG("Error : failed to get rx dma channel[%d]\n",
+						 priv->dma_ch);
+		return -EBUSY;
+	}
+#endif
+	if (ast_uart_tx_sdma_request(
+			priv->dma_ch, ast_uart_tx_buffdone, priv) < 0) {
+		UART_DBG("Error : failed to get tx dma channel[%d]\n",
+						 priv->dma_ch);
+		return -EBUSY;
+	}
+
+
+	platform_set_drvdata(pdev, priv);
+	return 0;
+}
+
+/*
+ * Remove serial ports registered against a platform device.
+ */
+static int ast25xx_uart_remove(struct platform_device *pdev)
+{
+	struct ast_uart_priv_data *priv;
+
+	priv = platform_get_drvdata(pdev);
+	serial8250_unregister_port(priv->line);
+	return 0;
+}
+
+static int ast25xx_uart_suspend(struct platform_device *pdev,
+			      pm_message_t state)
+{
+	struct ast_uart_priv_data *priv;
+
+	priv = platform_get_drvdata(pdev);
+	serial8250_suspend_port(priv->line);
+	return 0;
+}
+
+static int ast25xx_uart_resume(struct platform_device *pdev)
+{
+	struct ast_uart_priv_data *priv;
+
+	priv = platform_get_drvdata(pdev);
+	serial8250_resume_port(priv->line);
+	return 0;
+}
+
+static const struct of_device_id ast_serial_dt_ids[] = {
+	{ .compatible = "aspeed,ast-sdma-uart", },
+	{ /* sentinel */ }
+};
+
+static struct platform_driver ast25xx_uart_driver = {
+	.probe = ast25xx_uart_probe,
+	.remove = ast25xx_uart_remove,
+	.suspend = ast25xx_uart_suspend,
+	.resume = ast25xx_uart_resume,
+	.driver = {
+	    .name = "ast-uart-dma",
+	    .of_match_table = of_match_ptr(ast_serial_dt_ids),
+	},
+};
+
+static int __init ast_uart_init(void)
+{
+	int ret;
+
+	if (nr_uarts > UART_DMA_NR)
+		nr_uarts = UART_DMA_NR;
+
+	ret = ast_uart_sdma_probe();
+	if (ret) {
+		UART_DBG("ast_uart_sdma_probe Failed ret = %d\n", ret);
+		goto out;
+	}
+	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 = platform_driver_register(&ast25xx_uart_driver);
+	if (ret == 0)
+		goto out;
+
+out:
+	return ret;
+}
+
+static void __exit ast_uart_exit(void)
+{
+
+	platform_driver_unregister(&ast25xx_uart_driver);
+
+}
+module_init(ast_uart_init);
+module_exit(ast_uart_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("AST DMA serial driver");
+MODULE_ALIAS_CHARDEV_MAJOR(TTY_MAJOR);
-- 
2.17.1



More information about the Linux-aspeed mailing list