/*++
linux/drivers/serial/serial_wmt.c

Copyright (c) 2008  WonderMedia Technologies, Inc.

This program is free software: you can redistribute it and/or modify it under the
terms of the GNU General Public License as published by the Free Software Foundation,
either version 2 of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
PARTICULAR PURPOSE.  See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with
this program.  If not, see <http://www.gnu.org/licenses/>.

WonderMedia Technologies, Inc.
10F, 529, Chung-Cheng Road, Hsin-Tien, Taipei 231, R.O.C.
--*/

#include <linux/module.h>
#include <linux/tty.h>
#include <linux/tty_flip.h>
#include <linux/ioport.h>
#include <linux/init.h>
#include <linux/serial.h>
#include <linux/console.h>
#include <linux/sysrq.h>
#include <linux/device.h>
#include <linux/delay.h>
#include <linux/cpufreq.h>
#include <linux/platform_device.h>
#include <asm/io.h>
#include <asm/irq.h>
#include <mach/hardware.h>
#include <mach/dma.h>
#include <linux/serial_core.h>
#include <linux/dma-mapping.h>
#include <linux/slab.h>


#define PORT_WMT 54
#if defined(CONFIG_SERIAL_WMT_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ)
#define SUPPORT_SYSRQ
#endif


#ifdef CONFIG_SERIAL_WMT_DMA
/* * DMA processing */
#define DMA_RX_REQUEST(s, cb)	wmt_request_dma(&s->rx_dmach, s->id, s->dma_rx_dev, cb, s)
#define DMA_RX_FREE(s)		{wmt_free_dma(s->rx_dmach); s->rx_dmach = NULL_DMA; }
#define DMA_RX_START(s, d, l)	wmt_start_dma(s->rx_dmach, d, 0, l)
#define DMA_RX_POS(s)		wmt_get_dma_pos(s->rx_dmach)
#define DMA_RX_STOP(s)		wmt_stop_dma(s->rx_dmach)
#define DMA_RX_CLEAR(s)		wmt_clear_dma(s->rx_dmach)
#define DMA_RX_RESET(s)		wmt_reset_dma(s->rx_dmach)
#define IS_NULLDMA(ch)		((ch) == NULL_DMA)
#define CHK_DMACH(ch)		((ch) != NULL_DMA)
#define DMA_TX_REQUEST(s, cb)	wmt_request_dma(&s->tx_dmach, s->id, s->dma_tx_dev, cb, s)
#define DMA_TX_FREE(s)		{wmt_free_dma(s->tx_dmach); s->tx_dmach = NULL_DMA; }
#define DMA_TX_START(s, d, l)	wmt_start_dma(s->tx_dmach, d, 0, l)
#define DMA_TX_POS(s)		wmt_get_dma_pos(s->tx_dmach)
#define DMA_TX_STOP(s)		wmt_stop_dma(s->tx_dmach)
#define DMA_TX_CLEAR(s)		wmt_clear_dma(s->tx_dmach)
#define DMA_TX_RESET(s)		wmt_reset_dma(s->tx_dmach)
#define NULL_DMA                ((dmach_t)(-1))
#endif



#ifdef DEBUG_SERIAL_WMT
#define DEBUG_INTR(fmt...)	printk(fmt)
#else
#define DEBUG_INTR(fmt...)	do { } while (0)
#endif


#define UART_BR_115K2 115200

/*
 * This is for saving useful I/O registers
 */

/*
 * RS232 on DB9
 * Pin No.      Name    Notes/Description
 * 1            DCD     Data Carrier Detect
 * 2            RD      Receive Data (a.k.a RxD, Rx)
 * 3            TD      Transmit Data (a.k.a TxD, Tx)
 * 4            DTR     Data Terminal Ready
 * 5            SGND    Ground
 * 6            DSR     Data Set Ready
 * 7            RTS     Request To Send
 * 8            CTS     Clear To Send
 * 9            RI      Ring Indicator
 */

/*
 * We've been assigned a range on the "Low-density serial ports" major
 *
 * dev          Major           Minor
 * ttyVT0       204             90
 * ttyS0        4               64
 */
#ifdef CONFIG_SERIAL_WMT_TTYVT
		#define SERIAL_WMT_MAJOR     204
		#define MINOR_START          90      /* Start from ttyVT0 */
		#define CALLOUT_WMT_MAJOR    205     /* for callout device */
#else
		#define SERIAL_WMT_MAJOR     4
		#define MINOR_START          64      /* Start from ttyS0 */
		#define CALLOUT_WMT_MAJOR    5       /* for callout device */
#endif

#ifdef CONFIG_UART_2_3_ENABLE
#define NR_PORTS				4
#else
#define NR_PORTS				2
#endif
#define WMT_ISR_PASS_LIMIT   256

struct wmt_port {
	struct uart_port	port;
	struct timer_list	timer;
	struct timer_list	rx_timer;
#ifdef CONFIG_SERIAL_WMT_DMA
	/* RX Buffer0 physical address */
	dma_addr_t uart_rx_dma_phy0_org;
	/* RX Buffer1 physical address */
	dma_addr_t uart_rx_dma_phy1_org;
	/* RX Buffer2 physical address */
	dma_addr_t uart_rx_dma_phy2_org;
	/* RX Buffer0 virtual address */
	char *uart_rx_dma_buf0_org;
	/* RX Buffer1 virtual address */
	char *uart_rx_dma_buf1_org;
	/* RX Buffer2 virtual address */
	char *uart_rx_dma_buf2_org;
	/* RX Buffer0 virtual address, this address is the data should be read */
	char *uart_dma_tmp_buf0;
	/* RX Buffer1 virtual address, this address is the data should be read */
	char *uart_dma_tmp_buf1;
	/* RX Buffer2 virtual address, this address is the data should be read */
	char *uart_dma_tmp_buf2;
	/* RX Buffer0 physical address, this address is the data should be read */
	dma_addr_t uart_dma_tmp_phy0;
	/* RX Buffer1 physical address, this address is the data should be read */
	dma_addr_t uart_dma_tmp_phy1;
	/* RX Buffer2 physical address, this address is the data should be read */
	dma_addr_t uart_dma_tmp_phy2;
	/* RX device identifier for DMA */
	enum dma_device_e dma_rx_dev;
	/* TX device identifier for DMA */
	enum dma_device_e dma_tx_dev;
	/* RX DMA device config */
	struct dma_device_cfg_s dma_rx_cfg;
	/* TX DMA device config */
	struct dma_device_cfg_s dma_tx_cfg;
	/* RX DMA channel number */
	dmach_t rx_dmach;
	/* TX DMA channel number */
	dmach_t tx_dmach;
	/* RX dma callback function record which buffer the DMA used */
	unsigned int buffer_used;
	/* The buffer rx_char function should used (virtual address) */
	unsigned int buffer_selected;
	/* The buffer rx_count function should used (physical address) */
	unsigned int buffer_rx_count_selected;
	/* TX Buffer0 virtual address */
	char *uart_tx_dma_buf0_org;
	/* TX Buffer0 physical address */
	dma_addr_t uart_tx_dma_phy0_org;
	/* Record TX DMA running status : DMA_TX_END, DMA_TX_CHAR, DMA_TX_XMIT */
	unsigned int uart_tx_dma_flag;
	/* TX dma transmit counter*/
	unsigned int uart_tx_count;
	/* RX dma buffer last position*/
	unsigned int last_pos;
	/* Record the number of RX dma timeout function with no data transmit*/
	unsigned int uart_rx_dma_flag;
	unsigned int dma_tx_cnt;
#endif
	unsigned int old_status;
	/* identification string */
	char *id;
	unsigned int old_urdiv;
	unsigned int old_urlcr;
	unsigned int old_urier;
	unsigned int old_urfcr;
	unsigned int old_urtod;
};

#ifdef CONFIG_SERIAL_WMT_DMA
#define DMA_TX_END 0
#define DMA_TX_CHAR 1
#define DMA_TX_XMIT 2
#endif


/*
 * WMT UART registers set structure.
 */
struct wmt_uart {
	unsigned int volatile urtdr;            /* 0x00*/
	unsigned int volatile urrdr;            /* 0x04*/
	unsigned int volatile urdiv;            /* 0x08*/
	unsigned int volatile urlcr;            /* 0x0C*/
	unsigned int volatile uricr;            /* 0x10*/
	unsigned int volatile urier;            /* 0x14*/
	unsigned int volatile urisr;            /* 0x18*/
	unsigned int volatile urusr;            /* 0x1C*/
	unsigned int volatile urfcr;            /* 0x20*/
	unsigned int volatile urfidx;           /* 0x24*/
	unsigned int volatile urbkr;            /* 0x28*/
	unsigned int volatile urtod;            /* 0x2C*/
	unsigned int volatile resv30_FFF[0x3F4];	/* 0x0030 - 0x0FFF Reserved*/
	unsigned char volatile urtxf[32];       /* 0x1000 - 0x101F*/
	unsigned char volatile urrxf[32];      /* 0x1020 - 0x103F*/
};

struct baud_info_s {
	unsigned int baud;		/* baud rate */
	unsigned int brd;		/* baud rate divisor */
	unsigned int bcv;		/* break counter value at this baud rate
							 * simply be calculated by baud * 0.004096
							 */
};
#ifdef UART_DEBUG
unsigned int *DMA_pbuf =NULL;
unsigned int *COUNT_pbuf =NULL;
unsigned int *CPU_pbuf =NULL;
unsigned int dma_write_index = 0x00;
#endif
int mtk6622_tty = -1;
extern int wmt_getsyspara(char *varname, char *varval, int *varlen);

static struct baud_info_s baud_table[] = {
	{   3600,  0x100FF,    15 },
	{   7600,  0x1007F,    30 },
	{   9600,  0x2003F,    39 },
	{  14400,  0x1003F,    59 },
	{  19200,  0x2001F,    79 },
	{  28800,  0x1001F,   118 },
	{  38400,  0x2000F,   157 },
	{  57600,  0x1000F,   236 },
	{ 115200,   0x10007,   472 },
	{ 230400,   0x10003,   944 },
	{ 460800,   0x10001,  1920 },
	{ 921600,   0x10000,  3775 },
};

#define BAUD_TABLE_SIZE                 ARRAY_SIZE(baud_table)

#ifdef CONFIG_SERIAL_WMT_DMA
#define UART_BUFFER_SIZE	(1024*16)
#endif
/*
 * Macros to put URISR and URUSR into a 32-bit status variable
 * URISR in bit[ 0:15]
 * URUSR in bit[16:31]
 */
#define URISR_TO_SM(x)                  ((x) & URISR_MASK)
#define URUSR_TO_SM(x)                  (((x) & URUSR_MASK) << 16)
#define SM_TO_URISR(x)                  ((x) & 0xffff)
#define SM_TO_URUSR(x)                  ((x) >> 16)

/*
 * Following is a trick if we're interesting to listen break signal,
 * but due to WMT UART doesn't suppout this interrupt status.
 * So I make a fake interrupt status and use URISR_FER event to implement
 * break signal detect.
 */
#ifdef CONFIG_SERIAL_WMT_BKSIG
#define SW_BKSIG                        (BIT31 | URISR_FER)
#endif
/*
 * Macros to manipulate WMT UART module.
 *
 * s = sport, o = offset, v = value
 *
 * registers offset table as follows:
 *
 * URTDR           0x0000
 * URRDR           0x0004
 * URBRD           0x0008
 * URLCR           0x000C
 * URICR           0x0010
 * URIER           0x0014
 * URISR           0x0018
 * URUSR           0x001C
 * URFCR           0x0020
 * URFIDX          0x0024
 * URBKR           0x0028
 *
 * Offset 0x002C-0x002F reserved
 *
 * URTXF           0x0030
 * URRXF           0x0040
 */
#define PORT_TO_BASE(s)                 ((s)->port.membase)
#define WMT_UART_GET(s, o)           __raw_readl((s)->port.membase + o)
#define WMT_UART_PUT(s, o, v)        __raw_writel(v, (s)->port.membase + o)
#define WMT_UART_TXFIFO(s)           (volatile unsigned char *)((s)->port.membase + URTXF)
#define WMT_UART_RXFIFO(s)           (volatile unsigned short *)((s)->port.membase + URRXF)

/*
 * This is the size of our serial port register set.
 */
#define UART_PORT_SIZE  0x1040

/*
 * This determines how often we check the modem status signals
 * for any change.  They generally aren't connected to an IRQ
 * so we have to poll them.  We also check immediately before
 * filling the TX fifo incase CTS has been dropped.
 */
#define MCTRL_TIMEOUT   (250*HZ/1000)

/*{2007/11/10 JHT Support the VT8500 Serial Port Driver Because the*/
/*                definition of URSIRT Bit[0] & Bit[3] are different.*/
/*                Before VT8500 these bit are defined as RO, in VT8500*/
/*                they are changed into the W1C. Therefore the xmit function*/
/*                for the FIFO mode should be modified as well.*/
static void wmt_tx_chars(struct wmt_port *sport);
static void wmt_rx_chars(struct wmt_port *sport, unsigned int status);
static struct wmt_port wmt_ports[NR_PORTS];

/*}2007/11/10-JHT*/

enum {
	SHARE_PIN_UART = 0,
	SHARE_PIN_SPI,
};
static int wmt_uart_spi_sel = SHARE_PIN_UART;  /* 0:uart, 1:spi */

void uart_dump_reg(struct wmt_port *sport)
{
	struct wmt_uart *uart = (struct wmt_uart *)PORT_TO_BASE(sport);

	unsigned int urtdr = uart->urtdr;            /* 0x00*/
	unsigned int urrdr = uart->urrdr;            /* 0x04*/
	unsigned int urdiv = uart->urdiv;            /* 0x08*/
	unsigned int urlcr = uart->urlcr;            /* 0x0C*/
	unsigned int uricr = uart->uricr;            /* 0x10*/
	unsigned int urier = uart->urier;            /* 0x14*/
	unsigned int urisr = uart->urisr;            /* 0x18*/
	unsigned int urusr = uart->urusr;            /* 0x1C*/
	unsigned int urfcr = uart->urfcr;            /* 0x20*/
	unsigned int urfidx = uart->urfidx;           /* 0x24*/
	unsigned int urbkr = uart->urbkr;            /* 0x28*/
	unsigned int urtod = uart->urtod;            /* 0x2C*/
#if 1

	printk("urtdr=0x%.8x  urrdr=0x%.8x  urdiv=0x%.8x  urlcr=0x%.8x\n" \
		"uricr=0x%.8x  urier=0x%.8x  urisr=0x%.8x  urusr=0x%.8x\n" \
		"urfcr=0x%.8x  uridx=0x%.8x  urbkr=0x%.8x  urtod=0x%.8x\n",
			urtdr, urrdr, urdiv, urlcr,
			uricr, urier, urisr, urusr,
			urfcr, urfidx, urbkr, urtod);
#endif
}

static void wmt_mctrl_check(struct wmt_port *sport)
{
	unsigned int status, changed;

	status = sport->port.ops->get_mctrl(&sport->port);
	changed = status ^ sport->old_status;

	if (changed == 0)
		return;

	sport->old_status = status;

	if (changed & TIOCM_RI)
		sport->port.icount.rng++;
	if (changed & TIOCM_DSR)
		sport->port.icount.dsr++;
	if (changed & TIOCM_CAR)
		uart_handle_dcd_change(&sport->port, status & TIOCM_CAR);
	if (changed & TIOCM_CTS)
		uart_handle_cts_change(&sport->port, status & TIOCM_CTS);

	wake_up_interruptible(&sport->port.state->port.delta_msr_wait);
}

/*
 * This is our per-port timeout handler, for checking the
 * modem status signals.
 */
static void wmt_timeout(unsigned long data)
{
	struct wmt_port *sport = (struct wmt_port *)data;
	unsigned long flags;



	if (sport->port.state) {
		spin_lock_irqsave(&sport->port.lock, flags);
		wmt_mctrl_check(sport);
		spin_unlock_irqrestore(&sport->port.lock, flags);
		mod_timer(&sport->timer, jiffies + MCTRL_TIMEOUT);
	}
}

unsigned int rx_timeout = 1;

static void wmt_rx_timeout(unsigned long data)
{
	struct wmt_port *sport = (struct wmt_port *)data;
	struct wmt_uart *uart = (struct wmt_uart *)PORT_TO_BASE(sport);
	unsigned long flags;
	unsigned int status;
	unsigned int pos = 0;


	spin_lock_irqsave(&sport->port.lock, flags);
	pos = DMA_RX_POS(sport);
	status = URISR_TO_SM(uart->urisr) | URUSR_TO_SM(uart->urusr);
	uart->urisr |= SM_TO_URISR(status);
	spin_unlock_irqrestore(&sport->port.lock, flags);

	/* DMA didn't transmit any data */
	if (sport->last_pos == pos) {
		sport->uart_rx_dma_flag++;

		/* RX DMA didn't trasmit any data in two times,    */
		/* enable interrupt and do the last time mod timer */
		if (sport->uart_rx_dma_flag == 2) {
			sport->uart_rx_dma_flag++;
			uart->urier = URIER_ERXFAF  |
				      URIER_ERXFF   |
				      URIER_ERXTOUT |
				      URIER_EPER    |
				      URIER_EFER    |
				      URIER_ERXDOVR;
			mod_timer(&sport->rx_timer, jiffies + rx_timeout);
			return;
		} else if (sport->uart_rx_dma_flag == 3) {
			sport->uart_rx_dma_flag = 0;
			return;
		}
		mod_timer(&sport->rx_timer, jiffies + rx_timeout);
		return;
	}

	sport->uart_rx_dma_flag = 0;

	wmt_rx_chars(sport, URISR_RXFAF | URISR_RXFF | status);

	mod_timer(&sport->rx_timer, jiffies + rx_timeout);

}


/*
 * Interrupts should be disabled on entry.
 */
static void wmt_stop_tx(struct uart_port *port)
{
	struct wmt_port *sport = (struct wmt_port *)port;
	struct wmt_uart *uart = (struct wmt_uart *)PORT_TO_BASE(sport);

	#ifndef CONFIG_SERIAL_WMT_DMA
	uart->urier &= ~(URIER_ETXFAE | URIER_ETXFE);
	sport->port.read_status_mask &= ~URISR_TO_SM(URISR_TXFAE | URISR_TXFE);
	#else
	if ((unsigned int)(sport->port.membase) == UART0_BASE_ADDR) {
		uart->urier &= ~(URIER_ETXFAE | URIER_ETXFE);
		sport->port.read_status_mask &= ~URISR_TO_SM(URISR_TXFAE | URISR_TXFE);
	}
	#endif
}
/*
 * Interrupts may not be disabled on entry.
 */
static void wmt_start_tx(struct uart_port *port)
{
	struct wmt_port *sport = (struct wmt_port *)port;
	struct wmt_uart *uart = (struct wmt_uart *)PORT_TO_BASE(sport);

	/*{2007/11/10 JHT Support the VT8500 Serial Port Driver Because the
	 *                definition of URSIRT Bit[0] & Bit[3] are different.
	 *                Before VT8500 these bit are defined as RO, in VT8500
	 *                they are changed into the W1C. Therefore the xmit function
	 *                for the FIFO mode should be modified as well.
	 */
#ifdef CONFIG_SERIAL_WMT_DMA
	if ((unsigned int)(sport->port.membase) != UART0_BASE_ADDR) {
		uart->urlcr |= URLCR_DMAEN;
		uart->urier &= ~(URIER_ETXFAE | URIER_ETXFE);
	} else
		uart->urier &= ~(URIER_ETXFAE | URIER_ETXFE);
#else
	uart->urier &= ~(URIER_ETXFAE | URIER_ETXFE);
	#endif
	wmt_tx_chars(sport);
	/*}2007/11/10-JHT*/
#ifndef CONFIG_SERIAL_WMT_DMA
	sport->port.read_status_mask |= URISR_TO_SM(URISR_TXFAE | URISR_TXFE);
	uart->urier |= URIER_ETXFAE | URIER_ETXFE;
#else
	if ((unsigned int)(sport->port.membase) == UART0_BASE_ADDR) {
		sport->port.read_status_mask |= URISR_TO_SM(URISR_TXFAE | URISR_TXFE);
		uart->urier |= URIER_ETXFAE | URIER_ETXFE;
	}
	#endif
}

/*
 * Interrupts enabled
 */
static void wmt_stop_rx(struct uart_port *port)
{
/*
	struct wmt_port *sport = (struct wmt_port *)port;
	struct wmt_uart *uart = (struct wmt_uart *)PORT_TO_BASE(sport);


	uart->urier &= ~URIER_ERXFAF;
*/
}

/*
 * No modem control lines
 */
static void wmt_enable_ms(struct uart_port *port)
{
	struct wmt_port *sport = (struct wmt_port *)port;

	mod_timer(&sport->timer, jiffies);
}

#ifdef CONFIG_SERIAL_WMT_DMA
static unsigned int wmt_rx_count(struct wmt_port *sport)
{
	unsigned int rx_count = 0;
	unsigned int pos;

	pos = DMA_RX_POS(sport);

	if (pos == sport->last_pos) /*have no data to drain */
		return 0;

	if (sport->buffer_rx_count_selected == 0) {
		sport->buffer_selected = 0; /*shoud read buffer 0*/

		/*pos in the range of the buffer0*/
		if ((pos >= sport->uart_dma_tmp_phy0) &&
		    (pos <= (sport->uart_rx_dma_phy0_org + UART_BUFFER_SIZE))) {
			rx_count = (pos - (unsigned int)sport->uart_dma_tmp_phy0);
		} else if (pos == 0) {
			if (sport->uart_dma_tmp_phy0 == sport->uart_rx_dma_phy0_org)
				rx_count = 0;
			else {
				rx_count = (sport->uart_rx_dma_phy0_org + UART_BUFFER_SIZE)
					   - (unsigned int)sport->uart_dma_tmp_phy0;
			}
		} else if (((pos >= sport->uart_rx_dma_phy1_org) &&
			    (pos <= (sport->uart_rx_dma_phy1_org + UART_BUFFER_SIZE)))
			    ||
			    ((pos >= sport->uart_rx_dma_phy2_org) &&
			    (pos <= (sport->uart_rx_dma_phy2_org + UART_BUFFER_SIZE)))) {
			/* Buffer is full, dma pos in buffer 1 or 2, read all left data*/
			rx_count = (sport->uart_rx_dma_phy0_org + UART_BUFFER_SIZE) -
				   (unsigned int)sport->uart_dma_tmp_phy0;
		}
		sport->uart_dma_tmp_phy0  += rx_count; /*update tmp physical address*/
		sport->last_pos = sport->uart_dma_tmp_phy0;

		/* Buffer0 has reach to end*/
		if (sport->uart_dma_tmp_phy0 == (sport->uart_rx_dma_phy0_org + UART_BUFFER_SIZE)) {
			sport->buffer_rx_count_selected = 1;
			sport->uart_dma_tmp_phy0 = sport->uart_rx_dma_phy0_org;
		}
	} else if (sport->buffer_rx_count_selected == 1) {
		sport->buffer_selected = 1; /*shoud read buffer 1*/

		/*pos in the range of the buffer1*/
		if ((pos >= sport->uart_dma_tmp_phy1) &&
		    (pos <= (sport->uart_rx_dma_phy1_org + UART_BUFFER_SIZE))) {
			rx_count = (pos - (unsigned int)sport->uart_dma_tmp_phy1);
		} else if (pos == 0) {
			if (sport->uart_dma_tmp_phy1 == sport->uart_rx_dma_phy1_org)
				rx_count = 0;
			else {
				rx_count = (sport->uart_rx_dma_phy1_org + UART_BUFFER_SIZE) -
					   (unsigned int)sport->uart_dma_tmp_phy1;
			}
		} else if (((pos >= sport->uart_rx_dma_phy0_org) &&
			    (pos <= (sport->uart_rx_dma_phy0_org + UART_BUFFER_SIZE)))
			   ||
			   ((pos >= sport->uart_rx_dma_phy2_org) &&
			    (pos <= (sport->uart_rx_dma_phy2_org + UART_BUFFER_SIZE)))) {
			/* Buffer is full, dma pos in buffer 0 or 2, read all left data*/
			rx_count = (sport->uart_rx_dma_phy1_org + UART_BUFFER_SIZE) -
				   (unsigned int)sport->uart_dma_tmp_phy1;
		}
		sport->uart_dma_tmp_phy1  += rx_count;
		sport->last_pos = sport->uart_dma_tmp_phy1;
		/* Buffer1 has reach to end*/
		if (sport->uart_dma_tmp_phy1 == (sport->uart_rx_dma_phy1_org + UART_BUFFER_SIZE)) {
			sport->buffer_rx_count_selected = 2;
			sport->uart_dma_tmp_phy1 = sport->uart_rx_dma_phy1_org;
		}
	} else if (sport->buffer_rx_count_selected == 2) {
		sport->buffer_selected = 2; /*shoud read buffer 2*/

				/*pos in the range of the buffer1*/
		if ((pos >= sport->uart_dma_tmp_phy2) &&
		    (pos <= (sport->uart_rx_dma_phy2_org + UART_BUFFER_SIZE))) {
			rx_count = (pos - (unsigned int)sport->uart_dma_tmp_phy2);
		} else if (pos == 0) {
			if (sport->uart_dma_tmp_phy2 == sport->uart_rx_dma_phy2_org)
				rx_count = 0;
			else {
				rx_count = (sport->uart_rx_dma_phy2_org + UART_BUFFER_SIZE) -
					   (unsigned int)sport->uart_dma_tmp_phy2;
			}
		} else if (((pos >= sport->uart_rx_dma_phy0_org) &&
			    (pos <= (sport->uart_rx_dma_phy0_org + UART_BUFFER_SIZE)))
			    ||
			   ((pos >= sport->uart_rx_dma_phy1_org) &&
			    (pos <= (sport->uart_rx_dma_phy1_org + UART_BUFFER_SIZE)))) {
			/* Buffer is full, dma pos in buffer 0 or 1, read all left data*/
			rx_count = (sport->uart_rx_dma_phy2_org + UART_BUFFER_SIZE) -
				   (unsigned int)sport->uart_dma_tmp_phy2;
		}
		sport->uart_dma_tmp_phy2  += rx_count;
		sport->last_pos = sport->uart_dma_tmp_phy2;

		/* Buffer2 has reach to end*/
		if (sport->uart_dma_tmp_phy2 == (sport->uart_rx_dma_phy2_org + UART_BUFFER_SIZE)) {
			sport->buffer_rx_count_selected = 0;
			sport->uart_dma_tmp_phy2 = sport->uart_rx_dma_phy2_org;
		}
	}
	return rx_count;
}
/*
* we can use mu command to check memory content
*/
int volatile last_submit = 0x0;
void dump_rx_dma_buf(struct wmt_port *sport)
{
	unsigned int rx_count = 0;
	unsigned int pos;
	int i;
	int size=0;
	unsigned char *tmpbuf;
	pos = DMA_RX_POS(sport);
	printk("sport->last_pos:0x%x,pos:0x%x,sport->buffer_rx_count_selected:0x%x,last_submit:0x%x\n",sport->last_pos,pos,sport->buffer_rx_count_selected,last_submit);
	if ((sport->uart_rx_dma_phy0_org) <= pos && pos <= (sport->uart_rx_dma_phy0_org + UART_BUFFER_SIZE)){
		i = pos - sport->uart_rx_dma_phy0_org;
		tmpbuf = sport->uart_rx_dma_buf0_org + i;
		size = (unsigned int)(sport->uart_rx_dma_buf0_org) + UART_BUFFER_SIZE - (unsigned int)tmpbuf;
		if(size > 16)
			size = 16;
	}else if ((sport->uart_rx_dma_phy1_org)  <= pos && pos <=  (sport->uart_rx_dma_phy1_org + UART_BUFFER_SIZE)){
		i = pos - sport->uart_rx_dma_phy1_org;
		tmpbuf = sport->uart_rx_dma_buf1_org + i;
		size =(unsigned int)( sport->uart_rx_dma_buf1_org) + UART_BUFFER_SIZE - (unsigned int)tmpbuf;
		if(size > 16)
			size = 16;
	}else if ((sport->uart_rx_dma_phy2_org) <= pos && pos <=  (sport->uart_rx_dma_phy2_org + UART_BUFFER_SIZE)){
		i = pos - sport->uart_rx_dma_phy2_org;
		tmpbuf = sport->uart_rx_dma_buf2_org + i;
		size = (unsigned int)(sport->uart_rx_dma_buf2_org) + UART_BUFFER_SIZE - (unsigned int)tmpbuf;
		if(size > 16)
			size = 16;
	}

	for(i=0;i<size;i++)
		printk("0x%x ",*tmpbuf++);
	
	printk("\nsport->uart_rx_dma_phy0_org:0x%x\n",sport->uart_rx_dma_phy0_org);
	printk("sport->uart_rx_dma_phy1_org:0x%x\n",sport->uart_rx_dma_phy1_org);
	printk("sport->uart_rx_dma_phy2_org:0x%x\n",sport->uart_rx_dma_phy2_org);
}
#endif
/*
 * Inside the UART interrupt service routine dut to following
 * reason:
 *
 * URISR_RXFAF:  RX FIFO almost full (FIFO mode)
 * URISR_RXDF:   RX data register full (Register mode)
 * URISR_RXTOUT: RX timeout
 */

#ifndef CONFIG_SERIAL_WMT_DMA
static void
wmt_rx_chars(struct wmt_port *sport,  unsigned int status)
{
	struct tty_struct *tty = sport->port.state->port.tty;
	struct wmt_uart *uart = (struct wmt_uart *)PORT_TO_BASE(sport);
	unsigned int  flg, urfidx, ignored = 0;
	char ch;

	urfidx = URFIDX_RXFIDX(uart->urfidx);

	/*
	 * Check if there is data ready to be read.
	 *
	 * Note: We only receive characters.
	 */
	while ((status & URUSR_TO_SM(URUSR_RXDRDY)) && (URFIDX_RXFIDX(uart->urfidx))) {
		ch = (uart->urrxf[0] & 0xFF);
		/*urfidx--;*/

		sport->port.icount.rx++;

		flg = TTY_NORMAL;

		/*
		 * Check interrupt status information using status[URISR_bits].
		 *
		 * Notice that the error handling code is out of
		 * the main execution path and the URISR has already
		 * been read by ISR.
		 */
		if (status & URISR_TO_SM(URISR_PER | URISR_FER | URISR_RXDOVR)) {
			if (urfidx > 1) {
				if (uart_handle_sysrq_char(&sport->port, ch))
					goto ignore_char;
				/*
				 * Pop all TTY_NORMAL data.
				 */
				goto error_return;
			} else {
				/*
				 * Now we have poped up to the data with
				 * parity error or frame error.
				 */
				goto handle_error;
			}
		}

		if (uart_handle_sysrq_char(&sport->port, ch))
			goto ignore_char;

error_return:

		uart_insert_char(&sport->port, (status & 0xFFFF), URISR_TO_SM(URISR_RXDOVR) , ch, flg);

ignore_char:
		status &= 0xffff;       /* Keep URISR field*/
		status |= URUSR_TO_SM(uart->urusr);
	}
out:
	tty_flip_buffer_push(tty);
	return;

handle_error:
	/*
	 * Update error counters.
	 */
	if (status & URISR_TO_SM(URISR_PER))
		sport->port.icount.parity++;
	else
		if (status & URISR_TO_SM(URISR_FER)) {

	#ifdef CONFIG_SERIAL_WMT_BKSIG
			/*
			 * Experimental software patch for break signal detection.
			 *
			 * When I got there is a frame error in next frame data,
			 * I check the next data to judge if it is a break signal.
			 *
			 * FIXME: Open these if Bluetooth or IrDA need this patch.
			 *        Dec.29.2004 by Harry.
			 */
			if ((ch & RX_PERMASK) == 0) {
				sport->port.icount.brk++;
				uart_handle_break(&sport->port);
			} else
				sport->port.icount.frame++;

	#else   /* Don't support break sinal detection */

			sport->port.icount.frame++;

	#endif

		}

	/*
	 * RX Over Run event
	 */
	if (status & URISR_TO_SM(URISR_RXDOVR))
		sport->port.icount.overrun++;

	if (status & sport->port.ignore_status_mask) {
		if (++ignored > 100)
			goto out;
		goto ignore_char;
	}

	/*
	 * Second, handle the events which we're interesting to listen.
	 */
	status &= sport->port.read_status_mask;

	if (status & URISR_TO_SM(URISR_PER))
		flg = TTY_PARITY;
	else
		if (status & URISR_TO_SM(URISR_FER)) {

	#ifdef CONFIG_SERIAL_WMT_BKSIG
			/* Software patch for break signal detection.
			 *
			 * When I got there is a frame error in next frame data,
			 * I check the next data to judge if it is a break signal.
			 *
			 * FIXME: Open these if Bluetooth or IrDA need this patch.
			 *        Dec.29.2004 by Harry.
			 */
			if (sport->port.read_status_mask & SW_BKSIG) {
				if ((ch & RX_PERMASK) == 0) {
					DEBUG_INTR("handling break....");
					flg = TTY_BREAK;
					/*goto error_return;*/
				} else {
					flg = TTY_FRAME;
					/*goto error_return;*/
				}
			} else {
				flg = TTY_FRAME;
				/*goto error_return;*/
			}

	#else   /* Don't support break sinal detection */

			flg = TTY_FRAME;

	#endif
		}

	if (status & URISR_TO_SM(URISR_RXDOVR)) {
		/*
		 * Overrun is special, since it's reported
		 * immediately, and doesn't affect the current
		 * character.
		 */

		ch = 0;
		flg	= TTY_OVERRUN;
	}
	#ifdef SUPPORT_SYSRQ
	sport->port.sysrq = 0;
	#endif
	goto error_return;
}
#else


static inline int put_strings_tty_io(struct tty_struct *tty,
			const unsigned char *chars, char flag, size_t size)
{
	int i;

	i = tty_insert_flip_string_fixed_flag(tty, chars, flag, size);
	tty_flip_buffer_push(tty);
	return i;
}

static void
wmt_rx_chars(struct wmt_port *sport, unsigned int status)
{
	struct tty_struct *tty = sport->port.state->port.tty;
	struct wmt_uart *uart = (struct wmt_uart *)PORT_TO_BASE(sport);
	unsigned int receive_rx_count = 0;
	unsigned long flags;

	unsigned int  flg, urfidx, ignored = 0;
	char ch;
	char *pchar = NULL;
	char rx_flag;

	urfidx = URFIDX_RXFIDX(uart->urfidx);

	/*
	 * Check if there is data ready to be read.
	 *
	 * Note: We only receive characters.
	 */

	if ((unsigned int)(sport->port.membase) != UART0_BASE_ADDR) {

		spin_lock_irqsave(&sport->port.lock, flags);
		receive_rx_count = wmt_rx_count(sport);
		spin_unlock_irqrestore(&sport->port.lock, flags);

		rx_flag = TTY_NORMAL;

		if (status & URISR_TO_SM(URISR_PER))
			rx_flag = TTY_PARITY;

		if (status & URISR_TO_SM(URISR_FER))
			rx_flag = TTY_FRAME;

		if (receive_rx_count > 0) {
			if (sport->buffer_selected == 0) {
				pchar = sport->uart_dma_tmp_buf0;
				sport->uart_dma_tmp_buf0 += receive_rx_count;
				put_strings_tty_io(tty, pchar, rx_flag, receive_rx_count);

				if (sport->uart_dma_tmp_buf0 ==
				  (sport->uart_rx_dma_buf0_org + UART_BUFFER_SIZE)) {
					sport->uart_dma_tmp_buf0 = sport->uart_rx_dma_buf0_org;
				}

			} else if (sport->buffer_selected == 1) {
				pchar = sport->uart_dma_tmp_buf1;
				sport->uart_dma_tmp_buf1 += receive_rx_count;
				put_strings_tty_io(tty, pchar, rx_flag, receive_rx_count);

				if (sport->uart_dma_tmp_buf1 ==
				  (sport->uart_rx_dma_buf1_org + UART_BUFFER_SIZE)) {
					sport->uart_dma_tmp_buf1 = sport->uart_rx_dma_buf1_org;
				}
			} else if (sport->buffer_selected == 2) {
				pchar = sport->uart_dma_tmp_buf2;
				sport->uart_dma_tmp_buf2 += receive_rx_count ;
				put_strings_tty_io(tty, pchar, rx_flag, receive_rx_count);

				if (sport->uart_dma_tmp_buf2 ==
				   (sport->uart_rx_dma_buf2_org + UART_BUFFER_SIZE)) {
					sport->uart_dma_tmp_buf2 = sport->uart_rx_dma_buf2_org;
				}
			}
			sport->port.icount.rx += receive_rx_count;

			if (rx_flag == TTY_PARITY)
				sport->port.icount.parity += receive_rx_count;

			if (rx_flag == TTY_FRAME)
				sport->port.icount.frame += receive_rx_count;

			receive_rx_count = 0;
		}
		tty_flip_buffer_push(tty);
		return;

	} else {

		while ((status & URUSR_TO_SM(URUSR_RXDRDY)) && (URFIDX_RXFIDX(uart->urfidx))) {

			ch = (unsigned int)(uart->urrxf[0] & 0xFF);
			sport->port.icount.rx++;

			flg = TTY_NORMAL;

			/*
			 * Check interrupt status information using status[URISR_bits].
			 *
			 * Notice that the error handling code is out of
			 * the main execution path and the URISR has already
			 * been read by ISR.
			 */
			if (status & URISR_TO_SM(URISR_PER | URISR_FER | URISR_RXDOVR)) {
				if (urfidx > 1) {
					if (uart_handle_sysrq_char(&sport->port, ch))
						goto ignore_char2;
					/*
					 * Pop all TTY_NORMAL data.
					 */
					goto error_return2;
				} else {
					/*
					 * Now we have poped up to the data with
					 * parity error or frame error.
					 */
					goto handle_error2;
				}
			}

			if (uart_handle_sysrq_char(&sport->port, ch))
				goto ignore_char2;

error_return2:
			uart_insert_char(&sport->port, (status & 0xFFFF),
				URISR_TO_SM(URISR_RXDOVR) , ch, flg);
ignore_char2:
			status &= 0xffff;       /* Keep URISR field*/
			status |= URUSR_TO_SM(uart->urusr);
		}
out2:
		tty_flip_buffer_push(tty);
		return;

handle_error2:
		/*
		 * Update error counters.
		 */
		if (status & URISR_TO_SM(URISR_PER))
			sport->port.icount.parity++;
		else {
			if (status & URISR_TO_SM(URISR_FER)) {
#ifdef CONFIG_SERIAL_WMT_BKSIG
				/*
				 * Experimental software patch for break signal detection.
				 *
				 * When I got there is a frame error in next frame data,
				 * I check the next data to judge if it is a break signal.
				 *
				 * FIXME: Open these if Bluetooth or IrDA need this patch.
				 *        Dec.29.2004 by Harry.
				 */
				if ((ch & RX_PERMASK) == 0) {
					sport->port.icount.brk++;
					uart_handle_break(&sport->port);
				} else
					sport->port.icount.frame++;

#else   /* Don't support break sinal detection */
				sport->port.icount.frame++;
#endif
			}
		}
		/*
		 * RX Over Run event
		 */
		if (status & URISR_TO_SM(URISR_RXDOVR))
			sport->port.icount.overrun++;

		if (status & sport->port.ignore_status_mask) {
			if (++ignored > 100)
				goto out2;
			goto ignore_char2;
		}

		/*
		 * Second, handle the events which we're interesting to listen.
		 */
		status &= sport->port.read_status_mask;

		if (status & URISR_TO_SM(URISR_PER))
			flg = TTY_PARITY;
		else
			if (status & URISR_TO_SM(URISR_FER)) {

#ifdef CONFIG_SERIAL_WMT_BKSIG
				/* Software patch for break signal detection.
				 *
				 * When I got there is a frame error in next frame data,
				 * I check the next data to judge if it is a break signal.
				 *
				 * FIXME: Open these if Bluetooth or IrDA need this patch.
				 *        Dec.29.2004 by Harry.
				 */
				if (sport->port.read_status_mask & SW_BKSIG) {
					if ((ch & RX_PERMASK) == 0) {
						DEBUG_INTR("handling break....");
						flg = TTY_BREAK;
						/*goto error_return;*/
					} else {
						flg = TTY_FRAME;
						/*goto error_return;*/
					}
				} else {
					flg = TTY_FRAME;
					/*goto error_return;*/
				}
#else   /* Don't support break sinal detection */
				flg = TTY_FRAME;
#endif
		}

		if (status & URISR_TO_SM(URISR_RXDOVR)) {
			/*
			 * Overrun is special, since it's reported
			 * immediately, and doesn't affect the current
			 * character.
			 */

			ch = 0;
			flg	= TTY_OVERRUN;
		}
		#ifdef SUPPORT_SYSRQ
		sport->port.sysrq = 0;
		#endif
		goto error_return2;
	}

}
#endif
/*
 * Inside the UART interrupt service routine dut to following
 * reason:
 *
 * URISR_TXFAE: TX FIFO almost empty (FIFO mode)
 * URISR_TXFE:  TX FIFO empty(FIFO mode)
 */
#ifndef CONFIG_SERIAL_WMT_DMA
static void wmt_tx_chars(struct wmt_port *sport)
{
	struct circ_buf *xmit = &sport->port.state->xmit;
	struct wmt_uart *uart = (struct wmt_uart *)PORT_TO_BASE(sport);

	if (sport->port.x_char) {
		/*
		 * Fill character to the TX FIFO entry.
		 */
		uart->urtxf[0] = sport->port.x_char;
		sport->port.icount.tx++;
		sport->port.x_char = 0;
		return;
	}

	/*Check the modem control lines before transmitting anything.*/
	wmt_mctrl_check(sport);

	if (uart_circ_empty(xmit) || uart_tx_stopped(&sport->port)) {
		wmt_stop_tx(&sport->port);
		return;
	}

	/*{2007/11/10 JHT Support the WMT Serial Port Driver Because the
	 *                definition of URSIRT Bit[0] & Bit[3] are different.
	 *                Before WMT these bit are defined as RO, in WMT
	 *                they are changed into the W1C. Therefore the xmit function
	 *                for the FIFO mode should be modified as well.
	 */
	while ((uart->urfidx & 0x1F) < 16) {
		if (uart_circ_empty(xmit))
			break;

		if (uart->urusr & URUSR_TXDBSY)
			continue;
		uart->urtxf[0] = xmit->buf[xmit->tail];
		xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
		sport->port.icount.tx++;
	}
	/*}2007/11/10-JHT*/

	if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
		uart_write_wakeup(&sport->port);

	if (uart_circ_empty(xmit))
		wmt_stop_tx(&sport->port);
}
#else

static void wmt_tx_chars(struct wmt_port *sport)
{
	struct circ_buf *xmit = &sport->port.state->xmit;
	int head = xmit->head;
	int tail = xmit->tail;
	struct wmt_uart *uart = (struct wmt_uart *)PORT_TO_BASE(sport);
	char *dma_buf = sport->uart_tx_dma_buf0_org;
	unsigned int tx_count;

	if (sport->port.x_char) {
		/*
		 * Fill character to the TX FIFO entry.
		 */
		if ((unsigned int)(sport->port.membase) != UART0_BASE_ADDR) {
			/*Dma is still running*/
			if (sport->uart_tx_dma_flag != DMA_TX_END)
				return;

			*dma_buf = sport->port.x_char;
			sport->uart_tx_dma_flag = DMA_TX_CHAR;
			DMA_TX_START(sport, sport->uart_tx_dma_phy0_org, 1);
		} else {
			uart->urtxf[0] = sport->port.x_char;
			sport->port.icount.tx++;
			sport->port.x_char = 0;
		}
		return;
	}
	/*Check the modem control lines before transmitting anything.*/
	wmt_mctrl_check(sport);

	if (uart_circ_empty(xmit) || uart_tx_stopped(&sport->port)) {
		wmt_stop_tx(&sport->port);
		return;
	}

	/*{2007/11/10 JHT Support the WMT Serial Port Driver Because the
	 *                definition of URSIRT Bit[0] & Bit[3] are different.
	 *                Before WMT these bit are defined as RO, in WMT
	 *                they are changed into the W1C. Therefore the xmit function
	 *                for the FIFO mode should be modified as well.
	 */


	if ((unsigned int)(sport->port.membase) != UART0_BASE_ADDR) {
        while((uart->urusr  & URUSR_TXON));
		if(sport->port.line == mtk6622_tty){
			while ((uart->urusr & URUSR_TXDBSY));
		}

		if (sport->uart_tx_dma_flag == DMA_TX_END) {
			sport->uart_tx_dma_flag = DMA_TX_XMIT;
			tx_count = 0;
			head = xmit->head;
			tail = xmit->tail;
			sport->uart_tx_count = 0;
			while (head != tail) {
				*dma_buf = xmit->buf[tail];
				tail = (tail + 1) & (UART_XMIT_SIZE - 1);
				dma_buf++;
				tx_count++;
				sport->uart_tx_count++;

				if (tx_count == UART_BUFFER_SIZE)
					break;
			}
			DMA_TX_START(sport, sport->uart_tx_dma_phy0_org, tx_count);
		}
	} else {
		while ((uart->urfidx & 0x1F) < 16) {
			if (uart_circ_empty(xmit))
				break;

			if (uart->urusr & URUSR_TXDBSY)
				continue;
			uart->urtxf[0] = xmit->buf[xmit->tail];
			xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
			sport->port.icount.tx++;
		}


		if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
			uart_write_wakeup(&sport->port);

		if (uart_circ_empty(xmit))
			wmt_stop_tx(&sport->port);
	}

}
#endif

static irqreturn_t wmt_int(int irq, void *dev_id)
{
	struct wmt_port *sport = dev_id;
	struct wmt_uart *uart = (struct wmt_uart *)PORT_TO_BASE(sport);
	unsigned int status, pass_counter = 0;
	unsigned long flags;

	spin_lock_irqsave(&sport->port.lock, flags);
	/*
	 * Put interrupts status information to status bit[0:15]
	 * Put UART status register to status bit[16:31].
	 */

	status = URISR_TO_SM(uart->urisr) | URUSR_TO_SM(uart->urusr);
	uart->urisr |= SM_TO_URISR(status);

	do {
		/*
		 * First, we handle RX events.
		 *
		 * RX FIFO Almost Full.         (URUSR_RXFAF)
		 * RX Timeout.                  (URISR_RXTOUT)
		 * Frame error                  (URISR_FER)
		 *
		 * Note that also allow URISR_FER and URISR_PER event to do rx.
		 */

		if (status & URISR_TO_SM(URISR_RXFAF | URISR_RXFF | URISR_RXTOUT |\
								 URISR_PER | URISR_FER)) {

			if ((unsigned int)(sport->port.membase) == UART0_BASE_ADDR)
				wmt_rx_chars(sport,  status);

			if ((unsigned int)(sport->port.membase) != UART0_BASE_ADDR) {
				uart->urier = 0;	/*close uart rx interruption */
				uart->urisr |= SM_TO_URISR(status);	/* clear uart rx int status */
				sport->uart_rx_dma_flag = 0;
				mod_timer(&sport->rx_timer, jiffies + rx_timeout);
				break;
			}
		}
		/*
		 * Second, we handle TX events.
		 *
		 * If there comes a TX FIFO Almost event, try to fill TX FIFO.
		 */

#ifndef CONFIG_SERIAL_WMT_DMA
		wmt_tx_chars(sport);
#else
		if ((unsigned int)(sport->port.membase) == UART0_BASE_ADDR)
			wmt_tx_chars(sport);
#endif
		if (pass_counter++ > WMT_ISR_PASS_LIMIT)
			break;

		/*
		 * Update UART interrupt status and general status information.
		 */
		status = (URISR_TO_SM(uart->urisr) | URUSR_TO_SM(uart->urusr));
		uart->urisr |= SM_TO_URISR(status);

		/*
		 * Inside the loop, we handle events that we're interesting.
		 */
		status &= sport->port.read_status_mask;

		/*
		 * Continue loop while following condition:
		 *
		 * TX FIFO Almost Empty.        (URISR_TXFAE)
		 * RX FIFO Almost Full.         (URISR_RXFAF)
		 * RX Receive Time Out.         (URISR_RXTOUT)
		 */
	} while (status & (URISR_TXFE   |
			   URISR_TXFAE  |
			   URISR_RXFAF  |
			   URISR_RXFF   |
			   URISR_RXTOUT |
			   URISR_RXDOVR));

	spin_unlock_irqrestore(&sport->port.lock, flags);
	return IRQ_HANDLED;
}

/* wmt_tx_empty()
 *
 * Return TIOCSER_TEMT when transmitter is not busy.
 */
static unsigned int wmt_tx_empty(struct uart_port *port)
{
	struct wmt_port *sport = (struct wmt_port *)port;
	struct wmt_uart *uart = (struct wmt_uart *)PORT_TO_BASE(sport);

	return (uart->urusr & URUSR_TXDBSY) ? 0 : TIOCSER_TEMT;
}

/* wmt_get_mctrl()
 *
 * Returns the current state of modem control inputs.
 *
 * Note: Only support CTS now.
 */
static u_int wmt_get_mctrl(struct uart_port *port)
{
	u_int ret = TIOCM_DSR | TIOCM_CAR;
	struct wmt_port *sport = (struct wmt_port *)port;
	struct wmt_uart *uart = (struct wmt_uart *)PORT_TO_BASE(sport);

	ret |= (uart->urusr & URUSR_CTS) ? TIOCM_CTS : 0;

	return ret;
}

/* wmt_set_mctrl()
 *
 * This function sets the modem control lines for port described
 * by 'port' to the state described by mctrl. More detail please
 * refer to Documentation/serial/driver.
 *
 * Note: Only support RTS now.
 */
static void wmt_set_mctrl(struct uart_port *port, u_int mctrl)
{
	struct wmt_port *sport = (struct wmt_port *)port;
	struct wmt_uart *uart = (struct wmt_uart *)PORT_TO_BASE(sport);

	if (mctrl & TIOCM_RTS)
		uart->urlcr |= URLCR_RTS;
	else
		uart->urlcr &= ~URLCR_RTS;

}

/*
 * Interrupts always disabled.
 */
static void wmt_break_ctl(struct uart_port *port, int break_state)
{
	struct wmt_port *sport = (struct wmt_port *)port;
	struct wmt_uart *uart = (struct wmt_uart *)PORT_TO_BASE(sport);
	unsigned long flags;

	spin_lock_irqsave(&sport->port.lock, flags);

	if (break_state == -1) {
		int i;
		unsigned int urbrd = URBRD_BRD(uart->urdiv);

		/*
		 * This looks something tricky.
		 * Anyway, we need to get current baud rate divisor,
		 * search bcv in baud_table[], program it into
		 * URBKR, then generate break signal.
		 */
		for (i = 0; i < BAUD_TABLE_SIZE; i++) {
			if ((baud_table[i].brd & URBRD_BRDMASK) == urbrd)
				break;
		}

		if (i < BAUD_TABLE_SIZE) {
			uart->urbkr = URBKR_BCV(baud_table[i].bcv);
			uart->urlcr |= URLCR_BKINIT;
		}
	}

	spin_unlock_irqrestore(&sport->port.lock, flags);
}

static char *wmt_uartname[] = {
	"uart0",
	"uart1",
#ifdef CONFIG_UART_2_3_ENABLE
	"uart2",
	"uart3",
#endif
};

#ifdef CONFIG_SERIAL_WMT_DMA
static void uart_dma_callback_rx(void *data)
{
	struct wmt_port	*sport = data;
#ifdef UART_DEBUG	
	static int out_range =0x00;
	if(dma_write_index >=4096){
		printk("out of 4096\n");
		dma_write_index = 0x00;
		out_range++;
	}
#endif
	if (sport->buffer_used == 0) {
		sport->buffer_used = 1;
		DMA_RX_START(sport, sport->uart_rx_dma_phy0_org, UART_BUFFER_SIZE);
	} else if (sport->buffer_used == 1) {
		sport->buffer_used = 2;
		DMA_RX_START(sport, sport->uart_rx_dma_phy1_org, UART_BUFFER_SIZE);
	} else if (sport->buffer_used == 2) {
		sport->buffer_used = 0;
		DMA_RX_START(sport, sport->uart_rx_dma_phy2_org, UART_BUFFER_SIZE);
	}
#ifdef UART_DEBUG		
	DMA_pbuf[dma_write_index] = sport->buffer_used + (out_range<<24);
	//printk("0x%x dma rx DMA_pbuf[%d]:0x%x\n",(unsigned int)DMA_pbuf,dma_write_index,DMA_pbuf[dma_write_index]);
	if(sport->buffer_rx_count_selected == 0){
		COUNT_pbuf[dma_write_index] = sport->uart_dma_tmp_phy0;
	}else if(sport->buffer_rx_count_selected == 1)	{
		COUNT_pbuf[dma_write_index] = sport->uart_dma_tmp_phy1;
	}else if(sport->buffer_rx_count_selected == 2)	{
		COUNT_pbuf[dma_write_index] = sport->uart_dma_tmp_phy2;
	}

	if(sport->buffer_selected == 0){
		CPU_pbuf[dma_write_index] = sport->uart_dma_tmp_buf0;
	}else if(sport->buffer_selected == 1){
		CPU_pbuf[dma_write_index] = sport->uart_dma_tmp_buf1;
	}else if(sport->buffer_selected == 2){
		CPU_pbuf[dma_write_index] = sport->uart_dma_tmp_buf2;
	}
	dma_write_index++;
#endif	

}
#ifdef UART_DEBUG
void print_dma_count_cpu_buf_pos(struct wmt_port *sport)
{
	int i;
	for(i=0;i<UART_BUFFER_SIZE;i++){
		//printk("0x%x DMA_pbuf[%d]:0x%x\n",DMA_pbuf,i,DMA_pbuf[i]);
		if(DMA_pbuf[i] == 0x5a5a5a5a)
			break;
		printk("dma buf index:0x%x ",DMA_pbuf[i]);
		if((sport->uart_rx_dma_phy0_org <= COUNT_pbuf[i]) && (COUNT_pbuf[i] <= (sport->uart_rx_dma_phy0_org+UART_BUFFER_SIZE))){
			printk("count buf index:0x00 offset:0x%x ",COUNT_pbuf[i]-sport->uart_rx_dma_phy0_org);
		}else if((sport->uart_rx_dma_phy1_org <= COUNT_pbuf[i]) && (COUNT_pbuf[i] <= (sport->uart_rx_dma_phy1_org+UART_BUFFER_SIZE))){
			printk("count buf index:0x01 offset:0x%x ",COUNT_pbuf[i]-sport->uart_rx_dma_phy1_org);
		}if((sport->uart_rx_dma_phy2_org <= COUNT_pbuf[i]) && (COUNT_pbuf[i] <= (sport->uart_rx_dma_phy2_org+UART_BUFFER_SIZE))){
			printk("count buf index:0x02 offset:0x%x ",COUNT_pbuf[i]-sport->uart_rx_dma_phy2_org);
		}

		if((sport->uart_rx_dma_buf0_org <= CPU_pbuf[i]) && (CPU_pbuf[i] <= (sport->uart_rx_dma_buf0_org+UART_BUFFER_SIZE))){
			printk("cpu buf index:0x00 offset:0x%x\n",CPU_pbuf[i]-(unsigned int)sport->uart_rx_dma_buf0_org);
		}else if((sport->uart_rx_dma_buf1_org <= CPU_pbuf[i]) && (CPU_pbuf[i] <= (sport->uart_rx_dma_buf1_org+UART_BUFFER_SIZE))){
			printk("cpu buf index:0x01 offset:0x%x\n",CPU_pbuf[i]-(unsigned int)sport->uart_rx_dma_buf1_org);
		}if((sport->uart_rx_dma_buf2_org <= CPU_pbuf[i]) && (CPU_pbuf[i] <= (sport->uart_rx_dma_buf2_org+UART_BUFFER_SIZE))){
			printk("cpu buf index:0x02 offset:0x%x\n",CPU_pbuf[i]-(unsigned int)sport->uart_rx_dma_buf2_org);
		}
		printk("\n");
	}
}
#endif
static void uart_dma_callback_tx(void *data)
{
	struct wmt_port	*sport = data;
	unsigned long flags;
	sport->dma_tx_cnt++;
	spin_lock_irqsave(&sport->port.lock, flags);

	if (sport->uart_tx_dma_flag == DMA_TX_CHAR) {
		sport->port.icount.tx++;
		sport->port.x_char = 0;
	} else {
		sport->port.state->xmit.tail =
			(sport->port.state->xmit.tail + sport->uart_tx_count) & (UART_XMIT_SIZE - 1);
		sport->port.icount.tx += sport->uart_tx_count;
	}
	sport->uart_tx_dma_flag = DMA_TX_END;

	wmt_tx_chars(sport);

	if (uart_circ_chars_pending(&sport->port.state->xmit) < WAKEUP_CHARS)
		uart_write_wakeup(&sport->port);

	if (uart_circ_empty(&sport->port.state->xmit))
		wmt_stop_tx(&sport->port);

	spin_unlock_irqrestore(&sport->port.lock, flags);

}
#endif

wmt_uart1_pre_init(void)
{
	GPIO_CTRL_GP18_UART_BYTE_VAL &= ~(BIT4 | BIT5);
	auto_pll_divisor(DEV_UART1, CLK_ENABLE, 0, 0);
	printk("wmt_uart1_pre_init\n");
}

wmt_uart1_post_deinit(void)
{
	GPIO_CTRL_GP18_UART_BYTE_VAL |= (BIT4 | BIT5);
	auto_pll_divisor(DEV_UART1, CLK_DISABLE, 0, 0);
	printk("wmt_uart1_post_deinit\n");
}

static int wmt_startup(struct uart_port *port)
{
	struct wmt_port *sport = (struct wmt_port *)port;
	struct wmt_uart *uart = (struct wmt_uart *)PORT_TO_BASE(sport);
	char *uartname = NULL;
	int retval;
	int i;
	unsigned long flags;

	switch (sport->port.irq) {

	case IRQ_UART0:
		uartname = wmt_uartname[0];
/*#ifdef CONFIG_SERIAL_WMT_DMA
		sport->port.dma_rx_dev = UART_0_RX_DMA_REQ;
		sport->port.dma_tx_dev = UART_0_TX_DMA_REQ;
		sport->id			= "uart0";
		sport->port.dma_rx_cfg = dma_device_cfg_table[UART_0_RX_DMA_REQ];
		sport->port.dma_tx_cfg = dma_device_cfg_table[UART_0_TX_DMA_REQ];
#endif*/
		break;

	case IRQ_UART1:
		wmt_uart1_pre_init();//added by rubbitxiao
		uartname = wmt_uartname[1];
#ifdef CONFIG_SERIAL_WMT_DMA
		sport->dma_rx_dev = UART_1_RX_DMA_REQ;
		sport->dma_tx_dev = UART_1_TX_DMA_REQ;
		sport->id = "uart1";
		sport->dma_rx_cfg = dma_device_cfg_table[UART_1_RX_DMA_REQ];
		sport->dma_tx_cfg = dma_device_cfg_table[UART_1_TX_DMA_REQ];
#endif
		break;

#ifdef CONFIG_UART_2_3_ENABLE
	case IRQ_UART2:
		uartname = wmt_uartname[2];
#ifdef CONFIG_SERIAL_WMT_DMA
		sport->dma_rx_dev = UART_2_RX_DMA_REQ;
		sport->dma_tx_dev = UART_2_TX_DMA_REQ;
		sport->id = "uart2";
		sport->dma_rx_cfg = dma_device_cfg_table[UART_2_RX_DMA_REQ];
		sport->dma_tx_cfg = dma_device_cfg_table[UART_2_TX_DMA_REQ];
#endif
		break;
	case IRQ_UART3:
		uartname = wmt_uartname[3];
#ifdef CONFIG_SERIAL_WMT_DMA
		sport->dma_rx_dev = UART_3_RX_DMA_REQ;
		sport->dma_tx_dev = UART_3_TX_DMA_REQ;
		sport->id = "uart3";
		sport->dma_rx_cfg = dma_device_cfg_table[UART_3_RX_DMA_REQ];
		sport->dma_tx_cfg = dma_device_cfg_table[UART_3_TX_DMA_REQ];
#endif
		break;
#endif

	}
#ifdef CONFIG_SERIAL_WMT_DMA
	if ((unsigned int)(sport->port.membase) != UART0_BASE_ADDR) {
		memset(sport->uart_rx_dma_buf0_org, 0x0, UART_BUFFER_SIZE);
		memset(sport->uart_rx_dma_buf1_org, 0x0, UART_BUFFER_SIZE);
		memset(sport->uart_rx_dma_buf2_org, 0x0, UART_BUFFER_SIZE);
		memset(sport->uart_tx_dma_buf0_org, 0x0, UART_BUFFER_SIZE);
		sport->uart_dma_tmp_buf0 = sport->uart_rx_dma_buf0_org;
		sport->uart_dma_tmp_phy0 = sport->uart_rx_dma_phy0_org;
		sport->uart_dma_tmp_buf1 = sport->uart_rx_dma_buf1_org;
		sport->uart_dma_tmp_buf2 = sport->uart_rx_dma_buf2_org;
		sport->uart_dma_tmp_phy1 = sport->uart_rx_dma_phy1_org;
		sport->uart_dma_tmp_phy2 = sport->uart_rx_dma_phy2_org;
		sport->buffer_used = 0;   /*to record which buf DMA hardware used*/
		sport->buffer_selected = 0; /* to record which buf software is used to put data to kernel*/
		sport->buffer_rx_count_selected = 0; /* to record which buf rx_count is used*/
		sport->rx_dmach = NULL_DMA;
		sport->tx_dmach = NULL_DMA;
		sport->last_pos = 0;
		sport->uart_tx_dma_flag = DMA_TX_END;
		sport->uart_tx_count = 0;
		sport->dma_tx_cnt = 0x00;
		init_timer(&sport->rx_timer);
		sport->rx_timer.function = wmt_rx_timeout;
		sport->rx_timer.data = (unsigned long)sport;
		DMA_RX_REQUEST(sport, uart_dma_callback_rx);
		DMA_TX_REQUEST(sport, uart_dma_callback_tx);
		wmt_setup_dma(sport->rx_dmach, sport->dma_rx_cfg);
		wmt_setup_dma(sport->tx_dmach, sport->dma_tx_cfg);
		DMA_RX_START(sport, sport->uart_rx_dma_phy0_org, UART_BUFFER_SIZE);
		DMA_RX_START(sport, sport->uart_rx_dma_phy1_org, UART_BUFFER_SIZE);
		DMA_RX_START(sport, sport->uart_rx_dma_phy2_org, UART_BUFFER_SIZE);
#ifdef UART_DEBUG
		DMA_pbuf  = kmalloc(UART_BUFFER_SIZE*sizeof(unsigned int*), GFP_KERNEL);
		COUNT_pbuf  = kmalloc(UART_BUFFER_SIZE*sizeof(unsigned int*), GFP_KERNEL);
		CPU_pbuf  = kmalloc(UART_BUFFER_SIZE*sizeof(unsigned int*), GFP_KERNEL);
		if(!DMA_pbuf || !COUNT_pbuf || !CPU_pbuf){
			printk("kmalloc buf for debug buf failed\n");
			return;
		}else{
			memset(DMA_pbuf,0x5a,UART_BUFFER_SIZE*sizeof(unsigned int*));
			memset(COUNT_pbuf,0x00,UART_BUFFER_SIZE*sizeof(unsigned int*));
			memset(CPU_pbuf,0x00,UART_BUFFER_SIZE*sizeof(unsigned int*));			
		}
		dma_write_index =0x00;
#endif
		{
			char uboot_buf[256];
			int varlen = sizeof(uboot_buf);
			if(wmt_getsyspara("wmt.bt.tty",uboot_buf,&varlen) == 0) 
			{
			  	sscanf(uboot_buf,"%d",&mtk6622_tty);
				printk("mtk6622_tty:%d\n",mtk6622_tty);
				if(1<=mtk6622_tty && mtk6622_tty <=3){
					printk("wmt.bt.tty is correct\n");
				}else{
					printk("wmt.bt.tty is illegal\n");
					mtk6622_tty = -1;
				}
			}else{
				printk("have not set uboot variant:wmt.bt.tty\n");
			}
		}
	}
#endif
	/*
	 * Allocate the IRQ
	 */
	retval = request_irq(sport->port.irq, wmt_int, 0, uartname, sport);
	if (retval)
		return retval;

	/*
	 * Setup the UART clock divisor
	 */
	for (i = 0; i < BAUD_TABLE_SIZE; i++) {
		if (baud_table[i].baud == 115200)
			break;
	}
	spin_lock_irqsave(&sport->port.lock, flags);

	uart->urdiv = baud_table[i].brd;

	/* Disable TX,RX*/
	uart->urlcr = 0;
	/* Disable all interrupt*/
	uart->urier = 0;

	/*Reset TX,RX Fifo*/
	uart->urfcr = URFCR_TXFRST | URFCR_RXFRST;

	while (uart->urfcr)
		;

	/* Disable Fifo*/
	uart->urfcr &= ~(URFCR_FIFOEN);

	uart->urlcr |=  (URLCR_DLEN & ~URLCR_STBLEN & ~URLCR_PTYEN);
#ifdef CONFIG_SERIAL_WMT_DMA
	if ((unsigned int)(sport->port.membase) != UART0_BASE_ADDR) {
		uart->urfcr = URFCR_FIFOEN | URFCR_TXFLV(8) | URFCR_RXFLV(1) | URFCR_TRAIL;
		uart->urtod = 0x0a;
	} else
		uart->urfcr = URFCR_FIFOEN | URFCR_TXFLV(8) | URFCR_RXFLV(4);
#else
	/* Enable Fifo, Tx 16 , Rx 16*/
	uart->urfcr = URFCR_FIFOEN | URFCR_TXFLV(8) | URFCR_RXFLV(4);
#endif
	/* Enable Fifo, Tx 8 , Rx 8*/
#ifdef CONFIG_SERIAL_WMT_DMA
	if ((unsigned int)(sport->port.membase) != UART0_BASE_ADDR) {
		uart->urlcr |= URLCR_RXEN | URLCR_TXEN | URLCR_DMAEN | URLCR_RCTSSW;
		uart->urier = URIER_ERXFAF | URIER_ERXFF | URIER_ERXTOUT |
			      URIER_EPER | URIER_EFER | URIER_ERXDOVR;
	} else {
		uart->urlcr |= URLCR_RXEN | URLCR_TXEN | URLCR_RCTSSW;
		uart->urier = URIER_ERXFAF | URIER_ERXFF | URIER_ERXTOUT |
			      URIER_EPER | URIER_EFER | URIER_ERXDOVR;
	}
#else
	uart->urlcr |= URLCR_RXEN | URLCR_TXEN | URLCR_RCTSSW;
	uart->urier = URIER_ERXFAF | URIER_ERXFF | URIER_ERXTOUT | URIER_EPER |
		      URIER_EFER | URIER_ERXDOVR;

#endif
	/*
	 * Enable RX FIFO almost full, timeout, and overrun interrupts.
	 */

	/*
	 * Enable modem status interrupts
	 */

	wmt_enable_ms(&sport->port);
	spin_unlock_irqrestore(&sport->port.lock, flags);

	return 0;
}

static void wmt_shutdown(struct uart_port *port)
{
	struct wmt_port *sport = (struct wmt_port *)port;
	struct wmt_uart *uart = (struct wmt_uart *)PORT_TO_BASE(sport);
	unsigned long flags;
	//added begin by rubbitxiao
	spin_lock_irqsave(&sport->port.lock, flags);
	/* Disable TX,RX*/
	uart->urlcr = 0;
	/* Disable all interrupt*/
	uart->urier = 0;
	/*Reset TX,RX Fifo*/
	uart->urfcr = URFCR_TXFRST | URFCR_RXFRST;
	while (uart->urfcr)
		;
	/* Disable Fifo*/
	uart->urfcr &= ~(URFCR_FIFOEN);
	uart->urlcr |=  (URLCR_DLEN & ~URLCR_STBLEN & ~URLCR_PTYEN);
	spin_unlock_irqrestore(&sport->port.lock, flags);
	//added end by rubbitxiao
	/*
	 * Stop our timer.
	 */
	del_timer_sync(&sport->timer);

	/*
	 * Free the allocated interrupt
	 */
	free_irq(sport->port.irq, sport);

	/*
	 * Disable all interrupts, port and break condition.
	 */
	//spin_lock_irqsave(&sport->port.lock, flags);
	//uart->urier &= ~(URIER_ETXFE | URIER_ETXFAE | URIER_ERXFF | URIER_ERXFAF);
	//spin_unlock_irqrestore(&sport->port.lock, flags);

#ifdef CONFIG_SERIAL_WMT_DMA
	if ((unsigned int)(sport->port.membase) != UART0_BASE_ADDR) {
		del_timer_sync(&sport->rx_timer);
		DMA_RX_STOP(sport);
		DMA_RX_CLEAR(sport);
		DMA_RX_FREE(sport);
		while (sport->uart_tx_dma_flag != DMA_TX_END)
			msleep(1);

		DMA_TX_STOP(sport);
		DMA_TX_CLEAR(sport);
		DMA_TX_FREE(sport);
	}
#endif
	if ((unsigned int)(sport->port.membase) == UART1_BASE_ADDR)
		wmt_uart1_post_deinit();
}

/* wmt_uart_pm()
 *
 * Switch on/off uart in powersave mode.
 *
 * Hint: Identify port by irq number.
 */
static void wmt_uart_pm(struct uart_port *port, u_int state, u_int oldstate)
{
	return;
}

static void
wmt_set_termios(struct uart_port *port, struct ktermios *termios, struct ktermios *old)
{
	struct wmt_port *sport = (struct wmt_port *)port;
	struct wmt_uart *uart = (struct wmt_uart *)PORT_TO_BASE(sport);
	unsigned long flags;
	unsigned int new_urlcr, old_urlcr, old_urier, tmp_urisr, baud;
	unsigned int old_csize = old ? old->c_cflag & CSIZE : CS8;
	int i;

	/*
	 * If we don't support modem control lines, don't allow
	 * these to be set.
	 */
	if (0) {
		termios->c_cflag &= ~(HUPCL | CRTSCTS | CMSPAR);
		termios->c_cflag |= CLOCAL;
	}
	/*
	 * Only support CS7 and CS8.
	 */
	while ((termios->c_cflag & CSIZE) != CS7 && (termios->c_cflag & CSIZE) != CS8) {
		termios->c_cflag &= ~CSIZE;
		termios->c_cflag |= old_csize;
		old_csize = CS8;
	}

	if ((termios->c_cflag & CSIZE) == CS8)
		new_urlcr = URLCR_DLEN;
	else
		new_urlcr = 0;

	if (termios->c_cflag & CRTSCTS)
		new_urlcr &= ~URLCR_RCTSSW;
	else
		new_urlcr |= URLCR_RCTSSW;

	if (termios->c_cflag & CSTOPB)
		new_urlcr |= URLCR_STBLEN;

	if (termios->c_cflag & PARENB) {
		/*
		 * Enable parity.
		 */
		new_urlcr |= URLCR_PTYEN;

		/*
		 * Parity mode select.
		 */
		if (termios->c_cflag & PARODD)
			new_urlcr |= URLCR_PTYMODE;
	}

	/*
	 * Ask the core to get baud rate, but we need to
	 * calculate quot by ourself.
	 */
	baud = uart_get_baud_rate(port, termios, old, 9600, 921600);

	/*
	 * We need to calculate quot by ourself.
	 *
	 * FIXME: Be careful, following result is not an
	 *        interger quotient, fix it if need.
	 */
	/*quot = port->uartclk / (13 * baud);*/

	spin_lock_irqsave(&sport->port.lock, flags);

	/*
	 * Mask out other interesting to listen expect TX FIFO almost empty event.
	 */
	sport->port.read_status_mask &= URISR_TO_SM(URISR_TXFAE | URISR_TXFE);

	/*
	 * We're also interested in receiving RX FIFO events.
	 */
	sport->port.read_status_mask |= URISR_TO_SM(URISR_RXDOVR | URISR_RXFAF | URISR_RXFF);

	/*
	 * Check if we need to enable frame and parity error events
	 * to be passed to the TTY layer.
	 */
	if (termios->c_iflag & INPCK)
		sport->port.read_status_mask |= URISR_TO_SM(URISR_FER | URISR_PER);

#ifdef CONFIG_SERIAL_WMT_BKSIG
	/*
	 * check if we need to enable break events to be passed to the TTY layer.
	 */
	if (termios->c_iflag & (BRKINT | PARMRK))
		/*
		 * WMT UART doesn't support break signal detection interrupt.
		 *
		 * I try to implement this using URISR_FER.
		 */
		sport->port.read_status_mask |= SW_BKSIG;
#endif
	/*
	 * Characters to ignore
	 */
		sport->port.ignore_status_mask = 0;

	if (termios->c_iflag & IGNPAR)
		sport->port.ignore_status_mask |= URISR_TO_SM(URISR_FER | URISR_PER);

	if (termios->c_iflag & IGNBRK) {
#ifdef CONFIG_SERIAL_WMT_BKSIG
		/*
		 * WMT UART doesn't support break signal detection interrupt.
		 *
		 * I try to implement this using URISR_FER.
		 */
		sport->port.ignore_status_mask |= BIT31;/*FIXME*/
#endif

		/*
		 * If we're ignoring parity and break indicators,
		 * ignore overruns too (for real raw support).
		 */
		if (termios->c_iflag & IGNPAR)
			sport->port.ignore_status_mask |= URISR_TO_SM(URISR_RXDOVR);
	}

	del_timer_sync(&sport->timer);

	/*
	 * Update the per-port timeout.
	 */
	 uart_update_timeout(port, termios->c_cflag, baud);

	/*
	 * Disable FIFO request interrupts and drain transmitter
	 */
	old_urlcr = uart->urlcr;
	old_urier = uart->urier;
	uart->urier = old_urier & ~(URIER_ETXFAE | URIER_ERXFAF);

	/*
	 * Two step polling, first step polling the remaining
	 * entries in TX FIFO. This step make it safe to drain
	 * out all of remaining data in FIFO.
	 */
	while (URFIDX_TXFIDX(uart->urfidx))
		barrier();

	/*
	 * Second step to make sure the last one data has been sent.
	 */
	while (uart->urusr & URUSR_TXDBSY)
		barrier();

	/*
	 * Disable this UART port.
	 */
	uart->urier = 0;

	/*
	 * Set the parity, stop bits and data size
	 */
	uart->urlcr = new_urlcr;

	/*
	 * Set baud rate
	 */

	for (i = 0; i < BAUD_TABLE_SIZE; i++) {
		if (baud_table[i].baud == baud)
			break;
	}
	uart->urdiv = baud_table[i].brd;

	/*
	 * Read to clean any pending pulse interrupts.
	 */
	tmp_urisr = uart->urisr;

	/*
	 * Restore FIFO interrupt, TXEN bit, RXEN bit settings.
	 */
	uart->urier = old_urier;
#ifdef CONFIG_SERIAL_WMT_DMA
	uart->urlcr |= old_urlcr & (URLCR_TXEN | URLCR_RXEN | URLCR_DMAEN);
#else
	uart->urlcr |= old_urlcr & (URLCR_TXEN | URLCR_RXEN);
#endif

	if (UART_ENABLE_MS(&sport->port, termios->c_cflag))
		wmt_enable_ms(&sport->port);

	spin_unlock_irqrestore(&sport->port.lock, flags);
}

static const char *wmt_type(struct uart_port *port)
{
	struct wmt_port *sport = (struct wmt_port *)port;

	return (sport->port.type == PORT_WMT) ? "wmt serial" : NULL;
}

/*
 * Release the memory region(s) being used by 'port'.
 */
static void wmt_release_port(struct uart_port *port)
{
	struct wmt_port *sport = (struct wmt_port *)port;

	release_mem_region(sport->port.mapbase, UART_PORT_SIZE);
}

/*
 * Request the memory region(s) being used by 'port'.
 */
static int wmt_request_port(struct uart_port *port)
{
	struct wmt_port *sport = (struct wmt_port *)port;

	return request_mem_region(sport->port.mapbase,
							  UART_PORT_SIZE,
							  "uart") != NULL ? 0 : -EBUSY;
}

/*
 * Configure/autoconfigure the port.
 */
static void wmt_config_port(struct uart_port *port, int flags)
{
	struct wmt_port *sport = (struct wmt_port *)port;

	if (flags & UART_CONFIG_TYPE && wmt_request_port(&sport->port) == 0)
		sport->port.type = PORT_WMT;
}

/*
 * Verify the new serial_struct (for TIOCSSERIAL).
 * The only change we allow are to the flags and type, and
 * even then only between PORT_WMT and PORT_UNKNOWN
 */
static int wmt_verify_port(struct uart_port *port, struct serial_struct *ser)
{
	struct wmt_port *sport = (struct wmt_port *)port;
	int ret = 0;

	if (ser->type != PORT_UNKNOWN && ser->type != PORT_WMT)
		ret = -EINVAL;
	if (sport->port.irq != ser->irq)
		ret = -EINVAL;
	if (ser->io_type != SERIAL_IO_MEM)
		ret = -EINVAL;
	if (sport->port.uartclk / 16 != ser->baud_base)
		ret = -EINVAL;

	if ((void *)sport->port.mapbase != ser->iomem_base)
		ret = -EINVAL;
	if (sport->port.iobase != ser->port)
		ret = -EINVAL;
	if (ser->hub6 != 0)
		ret = -EINVAL;
	return ret;
}

static struct uart_ops wmt_pops = {
	.tx_empty	= wmt_tx_empty,
	.set_mctrl	= wmt_set_mctrl,
	.get_mctrl	= wmt_get_mctrl,
	.stop_tx	= wmt_stop_tx,
	.start_tx	= wmt_start_tx,
	.stop_rx	= wmt_stop_rx,
	.enable_ms	= wmt_enable_ms,
	.break_ctl	= wmt_break_ctl,
	.startup	= wmt_startup,
	.shutdown	= wmt_shutdown,
	.pm		= wmt_uart_pm,
	.set_termios	= wmt_set_termios,
	.type		= wmt_type,
	.release_port	= wmt_release_port,
	.request_port	= wmt_request_port,
	.config_port	= wmt_config_port,
	.verify_port	= wmt_verify_port,
};

static int parse_spi1_param(void)
{
	char buf[64];
	size_t l = sizeof(buf);
	int uart_spi_sel = 0;

	if (wmt_getsyspara("wmt.spi1.param", buf, &l) == 0) {
		sscanf(buf, "%d", &uart_spi_sel);
	}
	return uart_spi_sel;
}

/* Setup the WMT serial ports.  Note that we don't include the IrDA
 * port here since we have our own SIR/FIR driver (see drivers/net/irda)
 *
 * Note also that we support "console=ttyVTx" where "x" is either 0 to 2.
 * Which serial port this ends up being depends on the machine you're
 * running this kernel on.
 */
static void wmt_init_ports(void)
{
	static int first = 1;
	int i;

	if (!first)
		return;

	first = 0;

	wmt_uart_spi_sel = parse_spi1_param();

	for (i = 0; i < NR_PORTS; i++) {
		wmt_ports[i].port.uartclk    = 24000000;
		wmt_ports[i].port.ops        = &wmt_pops;
		wmt_ports[i].port.fifosize   = 16;
		wmt_ports[i].port.line       = i;
		wmt_ports[i].port.iotype     = SERIAL_IO_MEM;
		init_timer(&wmt_ports[i].timer);
		wmt_ports[i].timer.function  = wmt_timeout;
		wmt_ports[i].timer.data      = (unsigned long)&wmt_ports[i];
	}

	/*
	 * Make sure all UARTs are not configured as GPIO function.
	 *
	 * This step may be redundant due to bootloader has already
	 * done this for us.
	 */


	/* Switch the Uart's pin from default GPIO into uart function pins for UART0 ~ UART3 */
	GPIO_CTRL_GP18_UART_BYTE_VAL &= ~(BIT0 | BIT1 | BIT2 | BIT3 |
					  BIT4 | BIT5 | BIT6 | BIT7); /*UART0 UART1*/
#ifdef CONFIG_UART_2_3_ENABLE
	GPIO_CTRL_GP18_UART_BYTE_VAL &= ~(BIT2 | BIT3 | BIT6 | BIT7); /*UART2 UART3*/
#endif

	/*Set Uart0 and Uart1, Uart2 and Uart3  pin share*/
	PIN_SHARING_SEL_4BYTE_VAL  &= ~(BIT9 | BIT8);
	if (wmt_uart_spi_sel == SHARE_PIN_UART)
		PIN_SHARING_SEL_4BYTE_VAL  &= ~(BIT10);

#ifdef CONFIG_UART_2_3_ENABLE
	//kevin modify    uart0,uart1(hw flow control),uart2
    PIN_SHARING_SEL_4BYTE_VAL |= (BIT8);
#endif

	auto_pll_divisor(DEV_UART0, CLK_ENABLE, 0, 0);
	auto_pll_divisor(DEV_UART1, CLK_ENABLE, 0, 0);
#ifdef CONFIG_UART_2_3_ENABLE
	auto_pll_divisor(DEV_UART2, CLK_ENABLE, 0, 0);
	auto_pll_divisor(DEV_UART3, CLK_ENABLE, 0, 0);
#endif
}

void __init wmt_register_uart_fns(struct wmt_port_fns *fns)
{
	if (fns->get_mctrl)
		wmt_pops.get_mctrl = fns->get_mctrl;
	if (fns->set_mctrl)
		wmt_pops.set_mctrl = fns->set_mctrl;

	wmt_pops.pm = fns->pm;
	wmt_pops.set_wake = fns->set_wake;
}

void __init wmt_register_uart(int idx, int port)
{
	if (idx >= NR_PORTS) {
		printk(KERN_ERR "%s: bad index number %d\n", __func__, idx);
		return;
	}

	switch (port) {
	case 0:
		wmt_ports[idx].port.membase = (void *)(REG32_PTR(UART0_BASE_ADDR));
		wmt_ports[idx].port.mapbase = UART0_BASE_ADDR;
		wmt_ports[idx].port.irq     = IRQ_UART0;
		wmt_ports[idx].port.flags   = ASYNC_BOOT_AUTOCONF;
		break;
	case 1:
		wmt_ports[idx].port.membase = (void *)(REG32_PTR(UART1_BASE_ADDR));
		wmt_ports[idx].port.mapbase = UART1_BASE_ADDR;
		wmt_ports[idx].port.irq     = IRQ_UART1;
		wmt_ports[idx].port.flags   = ASYNC_BOOT_AUTOCONF;
		break;
#ifdef CONFIG_UART_2_3_ENABLE
	case 2:
		wmt_ports[idx].port.membase = (void *)(REG32_PTR(UART2_BASE_ADDR));
		wmt_ports[idx].port.mapbase = UART2_BASE_ADDR;
		wmt_ports[idx].port.irq     = IRQ_UART2;
		wmt_ports[idx].port.flags   = ASYNC_BOOT_AUTOCONF;
		break;
	case 3:
		wmt_ports[idx].port.membase = (void *)(REG32_PTR(UART3_BASE_ADDR));
		wmt_ports[idx].port.mapbase = UART3_BASE_ADDR;
		wmt_ports[idx].port.irq     = IRQ_UART3;
		wmt_ports[idx].port.flags   = ASYNC_BOOT_AUTOCONF;
		break;
#endif

	default:
		printk(KERN_ERR "%s: bad port number %d\n", __func__, port);
	}
}

#ifdef CONFIG_SERIAL_WMT_CONSOLE

/*
 * Interrupts are disabled on entering
 *
 * Note: We do console writing with UART register mode.
 */

static void wmt_console_write(struct console *co, const char *s, u_int count)
{
	struct wmt_port *sport = &wmt_ports[co->index];
	struct wmt_uart *uart = (struct wmt_uart *)PORT_TO_BASE(sport);
	unsigned int i, old_urlcr, old_urier;
	/*{JHT*/
	unsigned int old_urfcr;
	unsigned long flags;

	spin_lock_irqsave(&sport->port.lock, flags);
	/*}JHT*/
	/*
	 * First, save URLCR and URIER.
	 */
	old_urlcr = uart->urlcr;
	old_urier = uart->urier;
	/*{JHT*/
	old_urfcr = uart->urfcr;
	/*}JHT*/

	/*
	 * Second, switch to register mode with follows method:
	 *
	 * Disable FIFO threshold interrupts, and enable transmitter.
	 */
	uart->urier &= ~(URIER_ETXFAE | URIER_ERXFAF);
	uart->urlcr |= URLCR_TXEN;
	/*{JHT*/
	uart->urfcr &= ~URFCR_FIFOEN;
	/*}JHT*/
	/*
	 * Now, do each character
	 */
	for (i = 0; i < count; i++) {
		/*
		 * Polling until free for transmitting.
		 */
		while (uart->urusr & URUSR_TXDBSY)
			;

		uart->urtdr = (unsigned int)s[i];

		/*
		 * Do CR if there comes a LF.
		 */
		if (s[i] == '\n') {
			/*
			 * Polling until free for transmitting.
			 */
			while (uart->urusr & URUSR_TXDBSY)
				;

			uart->urtdr = (unsigned int)'\r';
		}
	}

	/*
	 * Finally, wait for transmitting done and restore URLCR and URIER.
	 */
	while (uart->urusr & URUSR_TXDBSY)
		;

	uart->urlcr = old_urlcr;
	uart->urier = old_urier;
	/*{JHT*/
	uart->urfcr = old_urfcr;
	/*}JHT*/
	spin_unlock_irqrestore(&sport->port.lock, flags);
}

/*
 * If the port was already initialised (eg, by a boot loader), try to determine
 * the current setup.
 */
static void __init wmt_console_get_options(struct wmt_port *sport, int *baud, int *parity, int *bits)
{
	int i;
	struct wmt_uart *uart = (struct wmt_uart *)PORT_TO_BASE(sport);

	if ((uart->urlcr & (URLCR_RXEN | URLCR_TXEN)) == (URLCR_RXEN | URLCR_TXEN)) {
		/*
		 * Port was enabled.
		 */
		unsigned quot;

		*parity = 'n';
		/*
		 * Check parity mode, 0:evev 1:odd
		 */
		if (uart->urlcr & URLCR_PTYEN) {
			if (uart->urlcr & URLCR_PTYMODE)
				*parity = 'o';
			else
				*parity = 'e';
		}

		/*
		 * Check data length, 0:7-bit 1:8-bit
		 */
		if (uart->urlcr & URLCR_DLEN)
			*bits = 8;
		else
			*bits = 7;

		/*
		 * Get baud rate divisor.
		 */
		quot = (uart->urdiv & URBRD_BRDMASK);
		/*
		 * FIXME: I didn't trace the console driver want me
		 * report baud rate whether actual baud rate or ideal
		 * target baud rate, current I report baud as actual
		 * one, if it need value as target baud rate, just
		 * creat an array to fix it, Dec.23 by Harry.
		 */

		for (i = 0; i < BAUD_TABLE_SIZE; i++) {
			if ((baud_table[i].brd & URBRD_BRDMASK) == quot) {
				*baud = baud_table[i].baud;
				break;
			}
		}

		/*
		 * If this condition is true, something might be wrong.
		 * I reprot the actual baud rate temporary.
		 * Check the printk information then fix it.
		 */
		if (i >= BAUD_TABLE_SIZE)
			*baud = sport->port.uartclk / (13 * (quot + 1));
	}
}

#ifndef CONFIG_WMT_DEFAULT_BAUDRATE
#define CONFIG_WMT_DEFAULT_BAUDRATE  115200
#endif

static int __init
wmt_console_setup(struct console *co, char *options)
{
	struct wmt_port *sport;
	int bits = 8;
	int parity = 'n';
	int flow = 'n';
	int baud = CONFIG_WMT_DEFAULT_BAUDRATE;

	/*
	 * Check whether an invalid uart number has been specified, and
	 * if so, search for the first available port that does have
	 * console support.
	*/
	if (co->index == -1 || co->index >= NR_PORTS)
		co->index = 0;

	sport = &wmt_ports[co->index];

	if (options)
		uart_parse_options(options, &baud, &parity, &bits, &flow);
	else
		wmt_console_get_options(sport, &baud, &parity, &bits);

	return uart_set_options(&sport->port, co, baud, parity, bits, flow);
}

static struct uart_driver wmt_reg;

static struct console wmt_console = {

#ifdef CONFIG_SERIAL_WMT_TTYVT
	.name	= "ttyVT",
#else
	.name	= "ttyS",
#endif
	.write	= wmt_console_write,
	.device	= uart_console_device,
	.setup	= wmt_console_setup,
	.flags	= CON_PRINTBUFFER,
	.index	= -1,
	.data	= &wmt_reg,
};

static int __init wmt_rs_console_init(void)
{
	wmt_init_ports();
	register_console(&wmt_console);
	return 0;
}

console_initcall(wmt_rs_console_init);

#define WMT_CONSOLE  (&wmt_console)

#else   /* CONFIG_SERIAL_WMT_CONSOLE */

#define WMT_CONSOLE  NULL

#endif

static struct uart_driver wmt_reg = {
	.owner          = THIS_MODULE,

#ifdef CONFIG_SERIAL_WMT_TTYVT
	.driver_name    = "ttyVT",
	.dev_name       = "ttyVT",
#else
	.driver_name    = "ttyS",
	.dev_name       = "ttyS",
#endif
	.major          = SERIAL_WMT_MAJOR,
	.minor          = MINOR_START,
	.nr             = NR_PORTS,
	.cons           = WMT_CONSOLE,
};


void wmt_serial_set_reg(void)
{
	*(volatile unsigned int *) (UART0_BASE_ADDR + 0x00000008) = BRD_115200BPS;
	*(volatile unsigned int *) (UART0_BASE_ADDR + 0x0000000c) = URLCR_TXEN |
								    URLCR_RXEN |
								    URLCR_DLEN |
								    URLCR_RCTSSW;

	*(volatile unsigned int *) (UART0_BASE_ADDR + 0x00000014) = URIER_ERXFAF  |
								    URIER_ERXFF   |
								    URIER_ERXTOUT |
								    URIER_EPER    |
								    URIER_EFER    |
								    URIER_ERXDOVR;

	*(volatile unsigned int *) (UART0_BASE_ADDR + 0x00000020) = URFCR_FIFOEN   |
								    URFCR_TXFLV(8) |
								    URFCR_RXFLV(8);
}

static int wmt_serial_suspend(struct platform_device *pdev, pm_message_t state)
{
	struct device *dev = &pdev->dev;
	struct wmt_port *sport = dev_get_drvdata(dev);
	struct wmt_uart *uart = (struct wmt_uart *)PORT_TO_BASE(sport);
	unsigned long flags;

	if (sport) {
		if (sport->port.irq != IRQ_UART0)
			uart_suspend_port(&wmt_reg, &sport->port);
	}
	if (!sport)
		return 0;

	spin_lock_irqsave(&sport->port.lock, flags);

	/* save host register */
	sport->old_urdiv = uart->urdiv;
	sport->old_urlcr = uart->urlcr;
	sport->old_urier = uart->urier;
	sport->old_urfcr = uart->urfcr;
	sport->old_urtod = uart->urtod;
	
	uart->urier = 0;
	spin_unlock_irqrestore(&sport->port.lock, flags);

	switch (sport->port.irq) {
	case IRQ_UART0:
		/*auto_pll_divisor(DEV_UART0, CLK_DISABLE, 0, 0);*/
		break;
	case IRQ_UART1:
		auto_pll_divisor(DEV_UART1, CLK_DISABLE, 0, 0);
		break;
#ifdef CONFIG_UART_2_3_ENABLE
	case IRQ_UART2:
		auto_pll_divisor(DEV_UART2, CLK_DISABLE, 0, 0);
		break;

	case IRQ_UART3:
		auto_pll_divisor(DEV_UART3, CLK_DISABLE, 0, 0);
		break;
#endif
	default:
		break;

	}
	return 0;
}

static int wmt_serial_resume(struct platform_device *pdev)
{
	struct device *dev = &pdev->dev;
	struct wmt_port *sport = dev_get_drvdata(dev);
	struct wmt_uart *uart = (struct wmt_uart *)PORT_TO_BASE(sport);
	unsigned long flags;

	if (!sport)
		return 0;

	GPIO_CTRL_GP18_UART_BYTE_VAL &= ~(BIT0 | BIT1 | BIT2 | BIT3 |
					  BIT4 | BIT5 | BIT6 | BIT7); /*UART0 UART1*/
#ifdef CONFIG_UART_2_3_ENABLE
	GPIO_CTRL_GP18_UART_BYTE_VAL &= ~(BIT2 | BIT3 | BIT6 | BIT7); /*UART2 UART3*/
#endif

	/*Set Uart0 and Uart1, Uart2 and Uart3  pin share*/
	PIN_SHARING_SEL_4BYTE_VAL  &= ~(BIT9 | BIT8);
	if (wmt_uart_spi_sel == SHARE_PIN_UART)
		PIN_SHARING_SEL_4BYTE_VAL  &= ~(BIT10);

#ifdef CONFIG_UART_2_3_ENABLE
	//kevin modify,    uart0,uart1(hw flow control),uart2
    PIN_SHARING_SEL_4BYTE_VAL |= (BIT8);
#endif

	switch (sport->port.irq) {
	case IRQ_UART0:
		auto_pll_divisor(DEV_UART0, CLK_ENABLE, 0, 0);
		break;
	case IRQ_UART1:
		auto_pll_divisor(DEV_UART1, CLK_ENABLE, 0, 0);
		break;
#ifdef CONFIG_UART_2_3_ENABLE
	case IRQ_UART2:
		auto_pll_divisor(DEV_UART2, CLK_ENABLE, 0, 0);
		break;
	case IRQ_UART3:
		auto_pll_divisor(DEV_UART3, CLK_ENABLE, 0, 0);
		break;
#endif
	default:
		break;
	}
	
	if (sport->port.irq != IRQ_UART0) {
		/* Disable TX,RX */
		uart->urlcr = 0;
		/* Disable all interrupt */
		uart->urier = 0;
		/* Clear all interrupt */
		uart->urisr = 0xffffffff;
	
		/* Disable Fifo */
		uart->urfcr &= ~(URFCR_FIFOEN);

		/* Reset TX,RX Fifo */
		uart->urfcr = URFCR_TXFRST | URFCR_RXFRST;

		while (uart->urfcr)
			;
	}
	
	spin_lock_irqsave(&sport->port.lock, flags);
	/*store back the interrupt enable status*/
	uart->urdiv = sport->old_urdiv;
	uart->urfcr = sport->old_urfcr;
	uart->urtod = sport->old_urtod;
	uart->urier = sport->old_urier;
	uart->urlcr = sport->old_urlcr;
	spin_unlock_irqrestore(&sport->port.lock, flags);

	if (sport) {
		if (sport->port.irq != IRQ_UART0)
			uart_resume_port(&wmt_reg, &sport->port);

	}

	return 0;
}
#if 1
void print_dma_position(struct wmt_port *sport)
{
	unsigned int *rx_dma = (unsigned int *)(0xfe001904 + (0x20*sport->rx_dmach));
	printk("address 0x%p = 0x%x(value)\n",rx_dma,*rx_dma);
}

void print_dma_buf_pointer(struct wmt_port *sport)
{
	printk("buf0:0x%p;  buf1:0x%p;   buf2:0x%p\n",sport->uart_dma_tmp_buf0,sport->uart_dma_tmp_buf1,sport->uart_dma_tmp_buf2);
	printk("phy0:0x%x; phy1:0x%x;  phy2:0x%x\n",sport->uart_dma_tmp_phy0,sport->uart_dma_tmp_phy1,sport->uart_dma_tmp_phy2);
}
void dump_uart_info(void)
{
     unsigned long flags;
     struct wmt_port * p_wmt_port = &wmt_ports[1];
     uart_dump_reg(p_wmt_port);
     printk("sport1->port.icount.rx0:0x%x,uart_tx_dma_phy0_org:0x%x\n",p_wmt_port->port.icount.rx,p_wmt_port->uart_tx_dma_phy0_org);
     print_dma_position(p_wmt_port);

     //spin_lock_irqsave(&(p_wmt_port->port.lock), flags);
     wmt_rx_chars(p_wmt_port,URISR_RXFAF | URISR_RXFF);
     //spin_unlock_irqrestore(&(p_wmt_port->port.lock), flags);

     print_dma_position(p_wmt_port);
     printk("sport1->port.icount.rx1:0x%x,uart_tx_dma_phy0_org:0x%x\n",p_wmt_port->port.icount.rx,p_wmt_port->uart_tx_dma_phy0_org);
     printk("uart rxxx dma channel register:\n");
     wmt_dump_dma_regs(p_wmt_port->rx_dmach);
     printk("uart tttx dma channel register:\n");
     wmt_dump_dma_regs(p_wmt_port->tx_dmach);
     printk("buf pointer position\n");
     print_dma_buf_pointer(p_wmt_port);
     printk("#######################################\n");
     dump_rx_dma_buf(p_wmt_port);
#ifdef UART_DEBUG	 
     printk("##########################################\n");
     print_dma_count_cpu_buf_pos(p_wmt_port);
#endif
    printk("#############debug dma tx failed begin#############\n");
    printk("sport->port.icount.tx:0x%x,sport->uart_tx_count:0x%x\n",p_wmt_port->port.icount.tx,
	p_wmt_port->uart_tx_count);
    printk("dma_tx_cnt:0x%x\n",p_wmt_port->dma_tx_cnt);
    printk("uart_tx_dma_flag:0x%x\n",p_wmt_port->uart_tx_dma_flag);
	printk("uart_tx_stopped:%d\n",uart_tx_stopped(&p_wmt_port->port));
	if(uart_tx_stopped(&p_wmt_port->port))
	{
		printk("stopped:%d\n",(&p_wmt_port->port)->state->port.tty->stopped);
		printk("hw_stopped:%d\n",(&p_wmt_port->port)->state->port.tty->hw_stopped);
	}
    printk("#############debug dma tx failed end#############\n");	

}
EXPORT_SYMBOL(dump_uart_info);
#endif
static int wmt_serial_probe(struct platform_device *pdev)
{
	struct device *dev = &pdev->dev;
	struct resource *res = pdev->resource;
	int i;

	for (i = 0; i < pdev->num_resources; i++, res++)
		if (res->flags & IORESOURCE_MEM)
			break;

	if (i < pdev->num_resources) {
		for (i = 0; i < NR_PORTS; i++) {
			if (wmt_ports[i].port.mapbase != res->start)
					continue;

			wmt_ports[i].port.dev = dev;
			uart_add_one_port(&wmt_reg, &wmt_ports[i].port);
			dev_set_drvdata(dev, &wmt_ports[i]);
			if (i >= 1) {
				wmt_ports[i].uart_rx_dma_buf0_org =
					dma_alloc_coherent(NULL,
							   UART_BUFFER_SIZE,
							   &wmt_ports[i].uart_rx_dma_phy0_org,
							   GFP_KERNEL);
				wmt_ports[i].uart_rx_dma_buf1_org =
					dma_alloc_coherent(NULL,
							   UART_BUFFER_SIZE,
							   &wmt_ports[i].uart_rx_dma_phy1_org,
							   GFP_KERNEL);
				wmt_ports[i].uart_rx_dma_buf2_org =
					dma_alloc_coherent(NULL,
							   UART_BUFFER_SIZE,
							   &wmt_ports[i].uart_rx_dma_phy2_org,
							   GFP_KERNEL);
				wmt_ports[i].uart_tx_dma_buf0_org =
					dma_alloc_coherent(NULL,
							   UART_BUFFER_SIZE,
							   &wmt_ports[i].uart_tx_dma_phy0_org,
							   GFP_KERNEL);
			}
			break;
		}
	}
	return 0;
}

static int wmt_serial_remove(struct platform_device *pdev)
{
	struct device *dev = &pdev->dev;
	struct wmt_port *sport = dev_get_drvdata(dev);

	dev_set_drvdata(dev, NULL);

	if (sport)
		uart_remove_one_port(&wmt_reg, &sport->port);

	return 0;
}

static struct platform_driver wmt_serial_driver = {
	.driver.name    = "uart",
	.probe          = wmt_serial_probe,
	.remove         = wmt_serial_remove,
	.suspend        = wmt_serial_suspend,
	.resume         = wmt_serial_resume,
};

static int __init wmt_serial_init(void)
{
	int ret;

	wmt_init_ports();
	ret = uart_register_driver(&wmt_reg);

	if (ret == 0) {
		ret = platform_driver_register(&wmt_serial_driver);
		if (ret)
			uart_unregister_driver(&wmt_reg);
	}
#ifndef CONFIG_SKIP_DRIVER_MSG
	printk(KERN_INFO "WMT Serial driver initialized: %s\n",
			(ret == 0) ? "ok" : "failed");
#endif
	return ret;
}

static void __exit wmt_serial_exit(void)
{
	platform_driver_unregister(&wmt_serial_driver);
	uart_unregister_driver(&wmt_reg);
}

module_init(wmt_serial_init);
module_exit(wmt_serial_exit);

MODULE_AUTHOR("WonderMedia Technologies, Inc.");
MODULE_DESCRIPTION("WMT [generic serial port] driver");
MODULE_LICENSE("GPL");