[[PATCH] 8/9] DMA-UART-Driver-for-AST2500

Message ID 1539749466-3912-9-git-send-email-open.sudheer@gmail.com
State Not Applicable, archived
Headers show
Series
  • [[PATCH] 8/9] DMA-UART-Driver-for-AST2500
Related show

Commit Message

sudheer.v Oct. 17, 2018, 4:11 a.m.
Signed-off-by: sudheer.v <open.sudheer@gmail.com>
---
 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

Comments

Vinod Koul Oct. 17, 2018, 6:05 a.m. | #1
On 17-10-18, 09:41, sudheer.v wrote:

Please add the change log describing the driver and its features

> Signed-off-by: sudheer.v <open.sudheer@gmail.com>


> ---
>  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

why is this in serial. It is dmaengine driver so belongs to drivers/dma/
like other controllers. Please move it out and resubmit.

While doing resubmission please take some time to understand subsystem
tags to use. (hint git log <subsystem> will tell you)

Also series has [[PATCH] 8/9] whereas it should be [PATCH 8/9] please
let git generate that for you (hint git format-patch start..end does a
good job)

> @@ -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<open.sudheer@gmail.com> modified

we dont use this log in kernel. I do not see s-o-b by Shivah, that
should be added. I think he should be author and you need to list
changes you did..
Benjamin Herrenschmidt Oct. 17, 2018, 8:56 a.m. | #2
On Wed, 2018-10-17 at 11:35 +0530, Vinod wrote:
> On 17-10-18, 09:41, sudheer.v wrote:
> 
> Please add the change log describing the driver and its features
> 
> > Signed-off-by: sudheer.v <open.sudheer@gmail.com>
> 
> 
> > ---
> >  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
> 
> why is this in serial. It is dmaengine driver so belongs to drivers/dma/
> like other controllers. Please move it out and resubmit.
 
It's not a dmaengine driver. It's a serial UART driver that happens to
use a dedicated DMA engine.

It's unclear whether it should be split into two drivers, or just have
the serial driver directly use the dma engine since that engine is
dedicated in HW to only work on those UARTs and nothing else...

Cheers,
Ben.


> While doing resubmission please take some time to understand subsystem
> tags to use. (hint git log <subsystem> will tell you)
> 
> Also series has [[PATCH] 8/9] whereas it should be [PATCH 8/9] please
> let git generate that for you (hint git format-patch start..end does a
> good job)
> 
> > @@ -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<open.sudheer@gmail.com> modified
> 
> we dont use this log in kernel. I do not see s-o-b by Shivah, that
> should be added. I think he should be author and you need to list
> changes you did..
>
Vinod Koul Oct. 18, 2018, 9:55 a.m. | #3
On 17-10-18, 19:56, Benjamin Herrenschmidt wrote:
> On Wed, 2018-10-17 at 11:35 +0530, Vinod wrote:
> > On 17-10-18, 09:41, sudheer.v wrote:
> > 
> > Please add the change log describing the driver and its features
> > 
> > > Signed-off-by: sudheer.v <open.sudheer@gmail.com>
> > > ---
> > >  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
> > 
> > why is this in serial. It is dmaengine driver so belongs to drivers/dma/
> > like other controllers. Please move it out and resubmit.
>  
> It's not a dmaengine driver. It's a serial UART driver that happens to
> use a dedicated DMA engine.

Then I see no reason for it to use dmaengine APIs. The framework allows
people to share a controller for many clients, but if you have dedicated
one then you may use it directly

> It's unclear whether it should be split into two drivers, or just have
> the serial driver directly use the dma engine since that engine is
> dedicated in HW to only work on those UARTs and nothing else...
> 
> Cheers,
> Ben.
> 
> 
> > While doing resubmission please take some time to understand subsystem
> > tags to use. (hint git log <subsystem> will tell you)
> > 
> > Also series has [[PATCH] 8/9] whereas it should be [PATCH 8/9] please
> > let git generate that for you (hint git format-patch start..end does a
> > good job)
> > 
> > > @@ -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<open.sudheer@gmail.com> modified
> > 
> > we dont use this log in kernel. I do not see s-o-b by Shivah, that
> > should be added. I think he should be author and you need to list
> > changes you did..
> >
Benjamin Herrenschmidt Oct. 18, 2018, 11:32 p.m. | #4
On Thu, 2018-10-18 at 15:25 +0530, Vinod wrote:
> 
> > It's not a dmaengine driver. It's a serial UART driver that happens to
> > use a dedicated DMA engine.
> 
> Then I see no reason for it to use dmaengine APIs. The framework allows
> people to share a controller for many clients, but if you have dedicated
> one then you may use it directly

Well... the engine is shared by a few UARTs, they have dedicated rings
but there's a common set of regs for interrupt handling etc.

That said, I still think it could be contained within a UART driver,
there's little benefit in adding the framework overhead, esp since
these are really weak cores, any overhead will be felt.

Ben.

> > It's unclear whether it should be split into two drivers, or just have
> > the serial driver directly use the dma engine since that engine is
> > dedicated in HW to only work on those UARTs and nothing else...
> > 
> > Cheers,
> > Ben.
> > 
> > 
> > > While doing resubmission please take some time to understand subsystem
> > > tags to use. (hint git log <subsystem> will tell you)
> > > 
> > > Also series has [[PATCH] 8/9] whereas it should be [PATCH 8/9] please
> > > let git generate that for you (hint git format-patch start..end does a
> > > good job)
> > > 
> > > > @@ -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<open.sudheer@gmail.com> modified
> > > 
> > > we dont use this log in kernel. I do not see s-o-b by Shivah, that
> > > should be added. I think he should be author and you need to list
> > > changes you did..
> > > 
> 
>
sudheer.v Oct. 19, 2018, 7:11 a.m. | #5
On Fri, Oct 19, 2018 at 10:32:24AM +1100, Benjamin Herrenschmidt wrote:
> On Thu, 2018-10-18 at 15:25 +0530, Vinod wrote:
> > 
> > > It's not a dmaengine driver. It's a serial UART driver that happens to
> > > use a dedicated DMA engine.
> > 
> > Then I see no reason for it to use dmaengine APIs. The framework allows
> > people to share a controller for many clients, but if you have dedicated
> > one then you may use it directly
> 
> Well... the engine is shared by a few UARTs, they have dedicated rings
> but there's a common set of regs for interrupt handling etc.
> 
> That said, I still think it could be contained within a UART driver,
> there's little benefit in adding the framework overhead, esp since
> these are really weak cores, any overhead will be felt.
> 
> Ben.
> 
> > > It's unclear whether it should be split into two drivers, or just have
> > > the serial driver directly use the dma engine since that engine is
> > > dedicated in HW to only work on those UARTs and nothing else...
> > > 
> > > Cheers,
> > > Ben.

Initially we wanted to have  a single driver,
however we had an informal discussion with one of the maintainer 
and based on the feedback, followed the Linux DMA and UART architecture.

If this seperate DMA-engine driver adds more overhead than benifit,
we will merge them into a single UART driver and resubmitt the patches.
Vinod,
      can this dma-controller driver sit under dma subsystem?.
      or better to move it under UART framework.

Thank you.
-- Sudheer
Vinod Koul Oct. 20, 2018, 4:26 p.m. | #6
On 19-10-18, 12:41, sudheer.v wrote:
> On Fri, Oct 19, 2018 at 10:32:24AM +1100, Benjamin Herrenschmidt wrote:
> > On Thu, 2018-10-18 at 15:25 +0530, Vinod wrote:
> > > 
> > > > It's not a dmaengine driver. It's a serial UART driver that happens to
> > > > use a dedicated DMA engine.
> > > 
> > > Then I see no reason for it to use dmaengine APIs. The framework allows
> > > people to share a controller for many clients, but if you have dedicated
> > > one then you may use it directly
> > 
> > Well... the engine is shared by a few UARTs, they have dedicated rings
> > but there's a common set of regs for interrupt handling etc.
> > 
> > That said, I still think it could be contained within a UART driver,
> > there's little benefit in adding the framework overhead, esp since
> > these are really weak cores, any overhead will be felt.
> > 
> > Ben.
> > 
> > > > It's unclear whether it should be split into two drivers, or just have
> > > > the serial driver directly use the dma engine since that engine is
> > > > dedicated in HW to only work on those UARTs and nothing else...
> > > > 
> > > > Cheers,
> > > > Ben.
> 
> Initially we wanted to have  a single driver,
> however we had an informal discussion with one of the maintainer 
> and based on the feedback, followed the Linux DMA and UART architecture.
> 
> If this seperate DMA-engine driver adds more overhead than benifit,
> we will merge them into a single UART driver and resubmitt the patches.
> Vinod,
>       can this dma-controller driver sit under dma subsystem?.
>       or better to move it under UART framework.


My advise would be to see what you can do with the DMA IP block. If this
can/would be used in different places then it would make sense to do a
dmaengine driver and solve the problem for everyone.

If this is always going to be hidden behind serial then maybe it makes
sense to be inside serial driver and not use dmaengine APIs

If you decide to prefer the former case, please move it to dmaengine and
resubmit :)

HTH
sudheer.v Oct. 26, 2018, 7:07 a.m. | #7
On Sat, Oct 20, 2018 at 09:56:24PM +0530, Vinod wrote:
> On 19-10-18, 12:41, sudheer.v wrote:
> > On Fri, Oct 19, 2018 at 10:32:24AM +1100, Benjamin Herrenschmidt wrote:
> > > On Thu, 2018-10-18 at 15:25 +0530, Vinod wrote:
> > > > 
> > > > > It's not a dmaengine driver. It's a serial UART driver that happens to
> > > > > use a dedicated DMA engine.
> > > > 
> > > > Then I see no reason for it to use dmaengine APIs. The framework allows
> > > > people to share a controller for many clients, but if you have dedicated
> > > > one then you may use it directly
> > > 
> > > Well... the engine is shared by a few UARTs, they have dedicated rings
> > > but there's a common set of regs for interrupt handling etc.
> > > 
> > > That said, I still think it could be contained within a UART driver,
> > > there's little benefit in adding the framework overhead, esp since
> > > these are really weak cores, any overhead will be felt.
> > > 
> > > Ben.
> > > 
> > > > > It's unclear whether it should be split into two drivers, or just have
> > > > > the serial driver directly use the dma engine since that engine is
> > > > > dedicated in HW to only work on those UARTs and nothing else...
> > > > > 
> > > > > Cheers,
> > > > > Ben.
> > 
> > Initially we wanted to have  a single driver,
> > however we had an informal discussion with one of the maintainer 
> > and based on the feedback, followed the Linux DMA and UART architecture.
> > 
> > If this seperate DMA-engine driver adds more overhead than benifit,
> > we will merge them into a single UART driver and resubmitt the patches.
> > Vinod,
> >       can this dma-controller driver sit under dma subsystem?.
> >       or better to move it under UART framework.
> 
> 
> My advise would be to see what you can do with the DMA IP block. If this
> can/would be used in different places then it would make sense to do a
> dmaengine driver and solve the problem for everyone.
> 
> If this is always going to be hidden behind serial then maybe it makes
> sense to be inside serial driver and not use dmaengine APIs
> 
> If you decide to prefer the former case, please move it to dmaengine and
> resubmit :)
> 
> HTH
> -- 
> ~Vinod

Hi All,
 As the DMA engine is dedicated only to UART,we have decided 
 to rewrite the driver so that no code will come under 
 drivers/dma.
 
 I will resubmitt the patches after merging dma controller
 code and uart driver code.
Regards
-sudheer

Patch

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<open.sudheer@gmail.com> 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);