zte's code,first commit

Change-Id: I9a04da59e459a9bc0d67f101f700d9d7dc8d681b
diff --git a/ap/os/linux/linux-3.4.x/drivers/tty/serial/21285.c b/ap/os/linux/linux-3.4.x/drivers/tty/serial/21285.c
new file mode 100644
index 0000000..a44345a
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/tty/serial/21285.c
@@ -0,0 +1,512 @@
+/*
+ * Driver for the serial port on the 21285 StrongArm-110 core logic chip.
+ *
+ * Based on drivers/char/serial.c
+ */
+#include <linux/module.h>
+#include <linux/tty.h>
+#include <linux/ioport.h>
+#include <linux/init.h>
+#include <linux/console.h>
+#include <linux/device.h>
+#include <linux/tty_flip.h>
+#include <linux/serial_core.h>
+#include <linux/serial.h>
+#include <linux/io.h>
+
+#include <asm/irq.h>
+#include <asm/mach-types.h>
+#include <asm/system_info.h>
+#include <asm/hardware/dec21285.h>
+#include <mach/hardware.h>
+
+#define BAUD_BASE		(mem_fclk_21285/64)
+
+#define SERIAL_21285_NAME	"ttyFB"
+#define SERIAL_21285_MAJOR	204
+#define SERIAL_21285_MINOR	4
+
+#define RXSTAT_DUMMY_READ	0x80000000
+#define RXSTAT_FRAME		(1 << 0)
+#define RXSTAT_PARITY		(1 << 1)
+#define RXSTAT_OVERRUN		(1 << 2)
+#define RXSTAT_ANYERR		(RXSTAT_FRAME|RXSTAT_PARITY|RXSTAT_OVERRUN)
+
+#define H_UBRLCR_BREAK		(1 << 0)
+#define H_UBRLCR_PARENB		(1 << 1)
+#define H_UBRLCR_PAREVN		(1 << 2)
+#define H_UBRLCR_STOPB		(1 << 3)
+#define H_UBRLCR_FIFO		(1 << 4)
+
+static const char serial21285_name[] = "Footbridge UART";
+
+#define tx_enabled(port)	((port)->unused[0])
+#define rx_enabled(port)	((port)->unused[1])
+
+/*
+ * The documented expression for selecting the divisor is:
+ *  BAUD_BASE / baud - 1
+ * However, typically BAUD_BASE is not divisible by baud, so
+ * we want to select the divisor that gives us the minimum
+ * error.  Therefore, we want:
+ *  int(BAUD_BASE / baud - 0.5) ->
+ *  int(BAUD_BASE / baud - (baud >> 1) / baud) ->
+ *  int((BAUD_BASE - (baud >> 1)) / baud)
+ */
+
+static void serial21285_stop_tx(struct uart_port *port)
+{
+	if (tx_enabled(port)) {
+		disable_irq_nosync(IRQ_CONTX);
+		tx_enabled(port) = 0;
+	}
+}
+
+static void serial21285_start_tx(struct uart_port *port)
+{
+	if (!tx_enabled(port)) {
+		enable_irq(IRQ_CONTX);
+		tx_enabled(port) = 1;
+	}
+}
+
+static void serial21285_stop_rx(struct uart_port *port)
+{
+	if (rx_enabled(port)) {
+		disable_irq_nosync(IRQ_CONRX);
+		rx_enabled(port) = 0;
+	}
+}
+
+static void serial21285_enable_ms(struct uart_port *port)
+{
+}
+
+static irqreturn_t serial21285_rx_chars(int irq, void *dev_id)
+{
+	struct uart_port *port = dev_id;
+	struct tty_struct *tty = port->state->port.tty;
+	unsigned int status, ch, flag, rxs, max_count = 256;
+
+	status = *CSR_UARTFLG;
+	while (!(status & 0x10) && max_count--) {
+		ch = *CSR_UARTDR;
+		flag = TTY_NORMAL;
+		port->icount.rx++;
+
+		rxs = *CSR_RXSTAT | RXSTAT_DUMMY_READ;
+		if (unlikely(rxs & RXSTAT_ANYERR)) {
+			if (rxs & RXSTAT_PARITY)
+				port->icount.parity++;
+			else if (rxs & RXSTAT_FRAME)
+				port->icount.frame++;
+			if (rxs & RXSTAT_OVERRUN)
+				port->icount.overrun++;
+
+			rxs &= port->read_status_mask;
+
+			if (rxs & RXSTAT_PARITY)
+				flag = TTY_PARITY;
+			else if (rxs & RXSTAT_FRAME)
+				flag = TTY_FRAME;
+		}
+
+		uart_insert_char(port, rxs, RXSTAT_OVERRUN, ch, flag);
+
+		status = *CSR_UARTFLG;
+	}
+	tty_flip_buffer_push(tty);
+
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t serial21285_tx_chars(int irq, void *dev_id)
+{
+	struct uart_port *port = dev_id;
+	struct circ_buf *xmit = &port->state->xmit;
+	int count = 256;
+
+	if (port->x_char) {
+		*CSR_UARTDR = port->x_char;
+		port->icount.tx++;
+		port->x_char = 0;
+		goto out;
+	}
+	if (uart_circ_empty(xmit) || uart_tx_stopped(port)) {
+		serial21285_stop_tx(port);
+		goto out;
+	}
+
+	do {
+		*CSR_UARTDR = xmit->buf[xmit->tail];
+		xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
+		port->icount.tx++;
+		if (uart_circ_empty(xmit))
+			break;
+	} while (--count > 0 && !(*CSR_UARTFLG & 0x20));
+
+	if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
+		uart_write_wakeup(port);
+
+	if (uart_circ_empty(xmit))
+		serial21285_stop_tx(port);
+
+ out:
+	return IRQ_HANDLED;
+}
+
+static unsigned int serial21285_tx_empty(struct uart_port *port)
+{
+	return (*CSR_UARTFLG & 8) ? 0 : TIOCSER_TEMT;
+}
+
+/* no modem control lines */
+static unsigned int serial21285_get_mctrl(struct uart_port *port)
+{
+	return TIOCM_CAR | TIOCM_DSR | TIOCM_CTS;
+}
+
+static void serial21285_set_mctrl(struct uart_port *port, unsigned int mctrl)
+{
+}
+
+static void serial21285_break_ctl(struct uart_port *port, int break_state)
+{
+	unsigned long flags;
+	unsigned int h_lcr;
+
+	spin_lock_irqsave(&port->lock, flags);
+	h_lcr = *CSR_H_UBRLCR;
+	if (break_state)
+		h_lcr |= H_UBRLCR_BREAK;
+	else
+		h_lcr &= ~H_UBRLCR_BREAK;
+	*CSR_H_UBRLCR = h_lcr;
+	spin_unlock_irqrestore(&port->lock, flags);
+}
+
+static int serial21285_startup(struct uart_port *port)
+{
+	int ret;
+
+	tx_enabled(port) = 1;
+	rx_enabled(port) = 1;
+
+	ret = request_irq(IRQ_CONRX, serial21285_rx_chars, 0,
+			  serial21285_name, port);
+	if (ret == 0) {
+		ret = request_irq(IRQ_CONTX, serial21285_tx_chars, 0,
+				  serial21285_name, port);
+		if (ret)
+			free_irq(IRQ_CONRX, port);
+	}
+
+	return ret;
+}
+
+static void serial21285_shutdown(struct uart_port *port)
+{
+	free_irq(IRQ_CONTX, port);
+	free_irq(IRQ_CONRX, port);
+}
+
+static void
+serial21285_set_termios(struct uart_port *port, struct ktermios *termios,
+			struct ktermios *old)
+{
+	unsigned long flags;
+	unsigned int baud, quot, h_lcr, b;
+
+	/*
+	 * We don't support modem control lines.
+	 */
+	termios->c_cflag &= ~(HUPCL | CRTSCTS | CMSPAR);
+	termios->c_cflag |= CLOCAL;
+
+	/*
+	 * We don't support BREAK character recognition.
+	 */
+	termios->c_iflag &= ~(IGNBRK | BRKINT);
+
+	/*
+	 * Ask the core to calculate the divisor for us.
+	 */
+	baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk/16); 
+	quot = uart_get_divisor(port, baud);
+	b = port->uartclk / (16 * quot);
+	tty_termios_encode_baud_rate(termios, b, b);
+
+	switch (termios->c_cflag & CSIZE) {
+	case CS5:
+		h_lcr = 0x00;
+		break;
+	case CS6:
+		h_lcr = 0x20;
+		break;
+	case CS7:
+		h_lcr = 0x40;
+		break;
+	default: /* CS8 */
+		h_lcr = 0x60;
+		break;
+	}
+
+	if (termios->c_cflag & CSTOPB)
+		h_lcr |= H_UBRLCR_STOPB;
+	if (termios->c_cflag & PARENB) {
+		h_lcr |= H_UBRLCR_PARENB;
+		if (!(termios->c_cflag & PARODD))
+			h_lcr |= H_UBRLCR_PAREVN;
+	}
+
+	if (port->fifosize)
+		h_lcr |= H_UBRLCR_FIFO;
+
+	spin_lock_irqsave(&port->lock, flags);
+
+	/*
+	 * Update the per-port timeout.
+	 */
+	uart_update_timeout(port, termios->c_cflag, baud);
+
+	/*
+	 * Which character status flags are we interested in?
+	 */
+	port->read_status_mask = RXSTAT_OVERRUN;
+	if (termios->c_iflag & INPCK)
+		port->read_status_mask |= RXSTAT_FRAME | RXSTAT_PARITY;
+
+	/*
+	 * Which character status flags should we ignore?
+	 */
+	port->ignore_status_mask = 0;
+	if (termios->c_iflag & IGNPAR)
+		port->ignore_status_mask |= RXSTAT_FRAME | RXSTAT_PARITY;
+	if (termios->c_iflag & IGNBRK && termios->c_iflag & IGNPAR)
+		port->ignore_status_mask |= RXSTAT_OVERRUN;
+
+	/*
+	 * Ignore all characters if CREAD is not set.
+	 */
+	if ((termios->c_cflag & CREAD) == 0)
+		port->ignore_status_mask |= RXSTAT_DUMMY_READ;
+
+	quot -= 1;
+
+	*CSR_UARTCON = 0;
+	*CSR_L_UBRLCR = quot & 0xff;
+	*CSR_M_UBRLCR = (quot >> 8) & 0x0f;
+	*CSR_H_UBRLCR = h_lcr;
+	*CSR_UARTCON = 1;
+
+	spin_unlock_irqrestore(&port->lock, flags);
+}
+
+static const char *serial21285_type(struct uart_port *port)
+{
+	return port->type == PORT_21285 ? "DC21285" : NULL;
+}
+
+static void serial21285_release_port(struct uart_port *port)
+{
+	release_mem_region(port->mapbase, 32);
+}
+
+static int serial21285_request_port(struct uart_port *port)
+{
+	return request_mem_region(port->mapbase, 32, serial21285_name)
+			 != NULL ? 0 : -EBUSY;
+}
+
+static void serial21285_config_port(struct uart_port *port, int flags)
+{
+	if (flags & UART_CONFIG_TYPE && serial21285_request_port(port) == 0)
+		port->type = PORT_21285;
+}
+
+/*
+ * verify the new serial_struct (for TIOCSSERIAL).
+ */
+static int serial21285_verify_port(struct uart_port *port, struct serial_struct *ser)
+{
+	int ret = 0;
+	if (ser->type != PORT_UNKNOWN && ser->type != PORT_21285)
+		ret = -EINVAL;
+	if (ser->irq <= 0)
+		ret = -EINVAL;
+	if (ser->baud_base != port->uartclk / 16)
+		ret = -EINVAL;
+	return ret;
+}
+
+static struct uart_ops serial21285_ops = {
+	.tx_empty	= serial21285_tx_empty,
+	.get_mctrl	= serial21285_get_mctrl,
+	.set_mctrl	= serial21285_set_mctrl,
+	.stop_tx	= serial21285_stop_tx,
+	.start_tx	= serial21285_start_tx,
+	.stop_rx	= serial21285_stop_rx,
+	.enable_ms	= serial21285_enable_ms,
+	.break_ctl	= serial21285_break_ctl,
+	.startup	= serial21285_startup,
+	.shutdown	= serial21285_shutdown,
+	.set_termios	= serial21285_set_termios,
+	.type		= serial21285_type,
+	.release_port	= serial21285_release_port,
+	.request_port	= serial21285_request_port,
+	.config_port	= serial21285_config_port,
+	.verify_port	= serial21285_verify_port,
+};
+
+static struct uart_port serial21285_port = {
+	.mapbase	= 0x42000160,
+	.iotype		= UPIO_MEM,
+	.irq		= 0,
+	.fifosize	= 16,
+	.ops		= &serial21285_ops,
+	.flags		= UPF_BOOT_AUTOCONF,
+};
+
+static void serial21285_setup_ports(void)
+{
+	serial21285_port.uartclk = mem_fclk_21285 / 4;
+}
+
+#ifdef CONFIG_SERIAL_21285_CONSOLE
+static void serial21285_console_putchar(struct uart_port *port, int ch)
+{
+	while (*CSR_UARTFLG & 0x20)
+		barrier();
+	*CSR_UARTDR = ch;
+}
+
+static void
+serial21285_console_write(struct console *co, const char *s,
+			  unsigned int count)
+{
+	uart_console_write(&serial21285_port, s, count, serial21285_console_putchar);
+}
+
+static void __init
+serial21285_get_options(struct uart_port *port, int *baud,
+			int *parity, int *bits)
+{
+	if (*CSR_UARTCON == 1) {
+		unsigned int tmp;
+
+		tmp = *CSR_H_UBRLCR;
+		switch (tmp & 0x60) {
+		case 0x00:
+			*bits = 5;
+			break;
+		case 0x20:
+			*bits = 6;
+			break;
+		case 0x40:
+			*bits = 7;
+			break;
+		default:
+		case 0x60:
+			*bits = 8;
+			break;
+		}
+
+		if (tmp & H_UBRLCR_PARENB) {
+			*parity = 'o';
+			if (tmp & H_UBRLCR_PAREVN)
+				*parity = 'e';
+		}
+
+		tmp = *CSR_L_UBRLCR | (*CSR_M_UBRLCR << 8);
+
+		*baud = port->uartclk / (16 * (tmp + 1));
+	}
+}
+
+static int __init serial21285_console_setup(struct console *co, char *options)
+{
+	struct uart_port *port = &serial21285_port;
+	int baud = 9600;
+	int bits = 8;
+	int parity = 'n';
+	int flow = 'n';
+
+	if (machine_is_personal_server())
+		baud = 57600;
+
+	/*
+	 * Check whether an invalid uart number has been specified, and
+	 * if so, search for the first available port that does have
+	 * console support.
+	 */
+	if (options)
+		uart_parse_options(options, &baud, &parity, &bits, &flow);
+	else
+		serial21285_get_options(port, &baud, &parity, &bits);
+
+	return uart_set_options(port, co, baud, parity, bits, flow);
+}
+
+static struct uart_driver serial21285_reg;
+
+static struct console serial21285_console =
+{
+	.name		= SERIAL_21285_NAME,
+	.write		= serial21285_console_write,
+	.device		= uart_console_device,
+	.setup		= serial21285_console_setup,
+	.flags		= CON_PRINTBUFFER,
+	.index		= -1,
+	.data		= &serial21285_reg,
+};
+
+static int __init rs285_console_init(void)
+{
+	serial21285_setup_ports();
+	register_console(&serial21285_console);
+	return 0;
+}
+console_initcall(rs285_console_init);
+
+#define SERIAL_21285_CONSOLE	&serial21285_console
+#else
+#define SERIAL_21285_CONSOLE	NULL
+#endif
+
+static struct uart_driver serial21285_reg = {
+	.owner			= THIS_MODULE,
+	.driver_name		= "ttyFB",
+	.dev_name		= "ttyFB",
+	.major			= SERIAL_21285_MAJOR,
+	.minor			= SERIAL_21285_MINOR,
+	.nr			= 1,
+	.cons			= SERIAL_21285_CONSOLE,
+};
+
+static int __init serial21285_init(void)
+{
+	int ret;
+
+	printk(KERN_INFO "Serial: 21285 driver\n");
+
+	serial21285_setup_ports();
+
+	ret = uart_register_driver(&serial21285_reg);
+	if (ret == 0)
+		uart_add_one_port(&serial21285_reg, &serial21285_port);
+
+	return ret;
+}
+
+static void __exit serial21285_exit(void)
+{
+	uart_remove_one_port(&serial21285_reg, &serial21285_port);
+	uart_unregister_driver(&serial21285_reg);
+}
+
+module_init(serial21285_init);
+module_exit(serial21285_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Intel Footbridge (21285) serial driver");
+MODULE_ALIAS_CHARDEV(SERIAL_21285_MAJOR, SERIAL_21285_MINOR);
diff --git a/ap/os/linux/linux-3.4.x/drivers/tty/serial/68328serial.c b/ap/os/linux/linux-3.4.x/drivers/tty/serial/68328serial.c
new file mode 100644
index 0000000..5ce7825
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/tty/serial/68328serial.c
@@ -0,0 +1,1407 @@
+/* 68328serial.c: Serial port driver for 68328 microcontroller
+ *
+ * Copyright (C) 1995       David S. Miller    <davem@caip.rutgers.edu>
+ * Copyright (C) 1998       Kenneth Albanowski <kjahds@kjahds.com>
+ * Copyright (C) 1998, 1999 D. Jeff Dionne     <jeff@uclinux.org>
+ * Copyright (C) 1999       Vladimir Gurevich  <vgurevic@cisco.com>
+ * Copyright (C) 2002-2003  David McCullough   <davidm@snapgear.com>
+ * Copyright (C) 2002       Greg Ungerer       <gerg@snapgear.com>
+ *
+ * VZ Support/Fixes             Evan Stawnyczy <e@lineo.ca>
+ * Multiple UART support        Daniel Potts <danielp@cse.unsw.edu.au>
+ * Power management support     Daniel Potts <danielp@cse.unsw.edu.au>
+ * VZ Second Serial Port enable Phil Wilshire
+ * 2.4/2.5 port                 David McCullough
+ */
+
+#include <asm/dbg.h>
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <linux/signal.h>
+#include <linux/sched.h>
+#include <linux/timer.h>
+#include <linux/interrupt.h>
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
+#include <linux/major.h>
+#include <linux/string.h>
+#include <linux/fcntl.h>
+#include <linux/mm.h>
+#include <linux/kernel.h>
+#include <linux/console.h>
+#include <linux/reboot.h>
+#include <linux/keyboard.h>
+#include <linux/init.h>
+#include <linux/pm.h>
+#include <linux/bitops.h>
+#include <linux/delay.h>
+#include <linux/gfp.h>
+
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/delay.h>
+#include <asm/uaccess.h>
+
+/* (es) */
+/* note: perhaps we can murge these files, so that you can just
+ * 	 define 1 of them, and they can sort that out for themselves
+ */
+#if defined(CONFIG_M68EZ328)
+#include <asm/MC68EZ328.h>
+#else
+#if defined(CONFIG_M68VZ328)
+#include <asm/MC68VZ328.h>
+#else
+#include <asm/MC68328.h>
+#endif /* CONFIG_M68VZ328 */
+#endif /* CONFIG_M68EZ328 */
+
+#include "68328serial.h"
+
+/* Turn off usage of real serial interrupt code, to "support" Copilot */
+#ifdef CONFIG_XCOPILOT_BUGS
+#undef USE_INTS
+#else
+#define USE_INTS
+#endif
+
+static struct m68k_serial m68k_soft[NR_PORTS];
+
+static unsigned int uart_irqs[NR_PORTS] = UART_IRQ_DEFNS;
+
+/* multiple ports are contiguous in memory */
+m68328_uart *uart_addr = (m68328_uart *)USTCNT_ADDR;
+
+struct tty_struct m68k_ttys;
+struct m68k_serial *m68k_consinfo = 0;
+
+#define M68K_CLOCK (16667000) /* FIXME: 16MHz is likely wrong */
+
+struct tty_driver *serial_driver;
+
+/* number of characters left in xmit buffer before we ask for more */
+#define WAKEUP_CHARS 256
+
+/* Debugging... DEBUG_INTR is bad to use when one of the zs
+ * lines is your console ;(
+ */
+#undef SERIAL_DEBUG_INTR
+#undef SERIAL_DEBUG_OPEN
+#undef SERIAL_DEBUG_FLOW
+
+#define RS_ISR_PASS_LIMIT 256
+
+static void change_speed(struct m68k_serial *info);
+
+/*
+ *	Setup for console. Argument comes from the boot command line.
+ */
+
+/* note: this is messy, but it works, again, perhaps defined somewhere else?*/
+#ifdef CONFIG_M68VZ328
+#define CONSOLE_BAUD_RATE	19200
+#define DEFAULT_CBAUD		B19200
+#endif
+
+
+#ifndef CONSOLE_BAUD_RATE
+#define	CONSOLE_BAUD_RATE	9600
+#define	DEFAULT_CBAUD		B9600
+#endif
+
+
+static int m68328_console_initted = 0;
+static int m68328_console_baud    = CONSOLE_BAUD_RATE;
+static int m68328_console_cbaud   = DEFAULT_CBAUD;
+
+
+static inline int serial_paranoia_check(struct m68k_serial *info,
+					char *name, const char *routine)
+{
+#ifdef SERIAL_PARANOIA_CHECK
+	static const char *badmagic =
+		"Warning: bad magic number for serial struct %s in %s\n";
+	static const char *badinfo =
+		"Warning: null m68k_serial for %s in %s\n";
+
+	if (!info) {
+		printk(badinfo, name, routine);
+		return 1;
+	}
+	if (info->magic != SERIAL_MAGIC) {
+		printk(badmagic, name, routine);
+		return 1;
+	}
+#endif
+	return 0;
+}
+
+/*
+ * This is used to figure out the divisor speeds and the timeouts
+ */
+static int baud_table[] = {
+	0, 50, 75, 110, 134, 150, 200, 300, 600, 1200, 1800, 2400, 4800,
+	9600, 19200, 38400, 57600, 115200, 0 };
+
+/* Sets or clears DTR/RTS on the requested line */
+static inline void m68k_rtsdtr(struct m68k_serial *ss, int set)
+{
+	if (set) {
+		/* set the RTS/CTS line */
+	} else {
+		/* clear it */
+	}
+	return;
+}
+
+/* Utility routines */
+static inline int get_baud(struct m68k_serial *ss)
+{
+	unsigned long result = 115200;
+	unsigned short int baud = uart_addr[ss->line].ubaud;
+	if (GET_FIELD(baud, UBAUD_PRESCALER) == 0x38) result = 38400;
+	result >>= GET_FIELD(baud, UBAUD_DIVIDE);
+
+	return result;
+}
+
+/*
+ * ------------------------------------------------------------
+ * rs_stop() and rs_start()
+ *
+ * This routines are called before setting or resetting tty->stopped.
+ * They enable or disable transmitter interrupts, as necessary.
+ * ------------------------------------------------------------
+ */
+static void rs_stop(struct tty_struct *tty)
+{
+	struct m68k_serial *info = (struct m68k_serial *)tty->driver_data;
+	m68328_uart *uart = &uart_addr[info->line];
+	unsigned long flags;
+
+	if (serial_paranoia_check(info, tty->name, "rs_stop"))
+		return;
+	
+	local_irq_save(flags);
+	uart->ustcnt &= ~USTCNT_TXEN;
+	local_irq_restore(flags);
+}
+
+static int rs_put_char(char ch)
+{
+        int flags, loops = 0;
+
+        local_irq_save(flags);
+
+	while (!(UTX & UTX_TX_AVAIL) && (loops < 1000)) {
+        	loops++;
+        	udelay(5);
+        }
+
+	UTX_TXDATA = ch;
+        udelay(5);
+        local_irq_restore(flags);
+        return 1;
+}
+
+static void rs_start(struct tty_struct *tty)
+{
+	struct m68k_serial *info = (struct m68k_serial *)tty->driver_data;
+	m68328_uart *uart = &uart_addr[info->line];
+	unsigned long flags;
+	
+	if (serial_paranoia_check(info, tty->name, "rs_start"))
+		return;
+	
+	local_irq_save(flags);
+	if (info->xmit_cnt && info->xmit_buf && !(uart->ustcnt & USTCNT_TXEN)) {
+#ifdef USE_INTS
+		uart->ustcnt |= USTCNT_TXEN | USTCNT_TX_INTR_MASK;
+#else
+		uart->ustcnt |= USTCNT_TXEN;
+#endif
+	}
+	local_irq_restore(flags);
+}
+
+/* Drop into either the boot monitor or kadb upon receiving a break
+ * from keyboard/console input.
+ */
+static void batten_down_hatches(void)
+{
+	/* Drop into the debugger */
+}
+
+static void status_handle(struct m68k_serial *info, unsigned short status)
+{
+	/* If this is console input and this is a
+	 * 'break asserted' status change interrupt
+	 * see if we can drop into the debugger
+	 */
+	if((status & URX_BREAK) && info->break_abort)
+		batten_down_hatches();
+	return;
+}
+
+static void receive_chars(struct m68k_serial *info, unsigned short rx)
+{
+	struct tty_struct *tty = info->tty;
+	m68328_uart *uart = &uart_addr[info->line];
+	unsigned char ch, flag;
+
+	/*
+	 * This do { } while() loop will get ALL chars out of Rx FIFO 
+         */
+#ifndef CONFIG_XCOPILOT_BUGS
+	do {
+#endif	
+		ch = GET_FIELD(rx, URX_RXDATA);
+	
+		if(info->is_cons) {
+			if(URX_BREAK & rx) { /* whee, break received */
+				status_handle(info, rx);
+				return;
+#ifdef CONFIG_MAGIC_SYSRQ
+			} else if (ch == 0x10) { /* ^P */
+				show_state();
+				show_free_areas(0);
+				show_buffers();
+/*				show_net_buffers(); */
+				return;
+			} else if (ch == 0x12) { /* ^R */
+				emergency_restart();
+				return;
+#endif /* CONFIG_MAGIC_SYSRQ */
+			}
+		}
+
+		if(!tty)
+			goto clear_and_exit;
+		
+		flag = TTY_NORMAL;
+
+		if(rx & URX_PARITY_ERROR) {
+			flag = TTY_PARITY;
+			status_handle(info, rx);
+		} else if(rx & URX_OVRUN) {
+			flag = TTY_OVERRUN;
+			status_handle(info, rx);
+		} else if(rx & URX_FRAME_ERROR) {
+			flag = TTY_FRAME;
+			status_handle(info, rx);
+		}
+		tty_insert_flip_char(tty, ch, flag);
+#ifndef CONFIG_XCOPILOT_BUGS
+	} while((rx = uart->urx.w) & URX_DATA_READY);
+#endif
+
+	tty_schedule_flip(tty);
+
+clear_and_exit:
+	return;
+}
+
+static void transmit_chars(struct m68k_serial *info)
+{
+	m68328_uart *uart = &uart_addr[info->line];
+
+	if (info->x_char) {
+		/* Send next char */
+		uart->utx.b.txdata = info->x_char;
+		info->x_char = 0;
+		goto clear_and_return;
+	}
+
+	if((info->xmit_cnt <= 0) || info->tty->stopped) {
+		/* That's peculiar... TX ints off */
+		uart->ustcnt &= ~USTCNT_TX_INTR_MASK;
+		goto clear_and_return;
+	}
+
+	/* Send char */
+	uart->utx.b.txdata = info->xmit_buf[info->xmit_tail++];
+	info->xmit_tail = info->xmit_tail & (SERIAL_XMIT_SIZE-1);
+	info->xmit_cnt--;
+
+	if(info->xmit_cnt <= 0) {
+		/* All done for now... TX ints off */
+		uart->ustcnt &= ~USTCNT_TX_INTR_MASK;
+		goto clear_and_return;
+	}
+
+clear_and_return:
+	/* Clear interrupt (should be auto)*/
+	return;
+}
+
+/*
+ * This is the serial driver's generic interrupt routine
+ */
+irqreturn_t rs_interrupt(int irq, void *dev_id)
+{
+	struct m68k_serial *info = dev_id;
+	m68328_uart *uart;
+	unsigned short rx;
+	unsigned short tx;
+
+	uart = &uart_addr[info->line];
+	rx = uart->urx.w;
+
+#ifdef USE_INTS
+	tx = uart->utx.w;
+
+	if (rx & URX_DATA_READY) receive_chars(info, rx);
+	if (tx & UTX_TX_AVAIL)   transmit_chars(info);
+#else
+	receive_chars(info, rx);		
+#endif
+	return IRQ_HANDLED;
+}
+
+static int startup(struct m68k_serial * info)
+{
+	m68328_uart *uart = &uart_addr[info->line];
+	unsigned long flags;
+	
+	if (info->flags & S_INITIALIZED)
+		return 0;
+
+	if (!info->xmit_buf) {
+		info->xmit_buf = (unsigned char *) __get_free_page(GFP_KERNEL);
+		if (!info->xmit_buf)
+			return -ENOMEM;
+	}
+
+	local_irq_save(flags);
+
+	/*
+	 * Clear the FIFO buffers and disable them
+	 * (they will be reenabled in change_speed())
+	 */
+
+	uart->ustcnt = USTCNT_UEN;
+	info->xmit_fifo_size = 1;
+	uart->ustcnt = USTCNT_UEN | USTCNT_RXEN | USTCNT_TXEN;
+	(void)uart->urx.w;
+
+	/*
+	 * Finally, enable sequencing and interrupts
+	 */
+#ifdef USE_INTS
+	uart->ustcnt = USTCNT_UEN | USTCNT_RXEN | 
+                 USTCNT_RX_INTR_MASK | USTCNT_TX_INTR_MASK;
+#else
+	uart->ustcnt = USTCNT_UEN | USTCNT_RXEN | USTCNT_RX_INTR_MASK;
+#endif
+
+	if (info->tty)
+		clear_bit(TTY_IO_ERROR, &info->tty->flags);
+	info->xmit_cnt = info->xmit_head = info->xmit_tail = 0;
+
+	/*
+	 * and set the speed of the serial port
+	 */
+
+	change_speed(info);
+
+	info->flags |= S_INITIALIZED;
+	local_irq_restore(flags);
+	return 0;
+}
+
+/*
+ * This routine will shutdown a serial port; interrupts are disabled, and
+ * DTR is dropped if the hangup on close termio flag is on.
+ */
+static void shutdown(struct m68k_serial * info)
+{
+	m68328_uart *uart = &uart_addr[info->line];
+	unsigned long	flags;
+
+	uart->ustcnt = 0; /* All off! */
+	if (!(info->flags & S_INITIALIZED))
+		return;
+
+	local_irq_save(flags);
+	
+	if (info->xmit_buf) {
+		free_page((unsigned long) info->xmit_buf);
+		info->xmit_buf = 0;
+	}
+
+	if (info->tty)
+		set_bit(TTY_IO_ERROR, &info->tty->flags);
+	
+	info->flags &= ~S_INITIALIZED;
+	local_irq_restore(flags);
+}
+
+struct {
+	int divisor, prescale;
+}
+#ifndef CONFIG_M68VZ328
+ hw_baud_table[18] = {
+	{0,0}, /* 0 */
+	{0,0}, /* 50 */
+	{0,0}, /* 75 */
+	{0,0}, /* 110 */
+	{0,0}, /* 134 */
+	{0,0}, /* 150 */
+	{0,0}, /* 200 */
+	{7,0x26}, /* 300 */
+	{6,0x26}, /* 600 */
+	{5,0x26}, /* 1200 */
+	{0,0}, /* 1800 */
+	{4,0x26}, /* 2400 */
+	{3,0x26}, /* 4800 */
+	{2,0x26}, /* 9600 */
+	{1,0x26}, /* 19200 */
+	{0,0x26}, /* 38400 */
+	{1,0x38}, /* 57600 */
+	{0,0x38}, /* 115200 */
+};
+#else
+ hw_baud_table[18] = {
+                 {0,0}, /* 0 */
+                 {0,0}, /* 50 */
+                 {0,0}, /* 75 */
+                 {0,0}, /* 110 */
+                 {0,0}, /* 134 */
+                 {0,0}, /* 150 */
+                 {0,0}, /* 200 */
+                 {0,0}, /* 300 */
+                 {7,0x26}, /* 600 */
+                 {6,0x26}, /* 1200 */
+                 {0,0}, /* 1800 */
+                 {5,0x26}, /* 2400 */
+                 {4,0x26}, /* 4800 */
+                 {3,0x26}, /* 9600 */
+                 {2,0x26}, /* 19200 */
+                 {1,0x26}, /* 38400 */
+                 {0,0x26}, /* 57600 */
+                 {1,0x38}, /* 115200 */
+}; 
+#endif
+/* rate = 1036800 / ((65 - prescale) * (1<<divider)) */
+
+/*
+ * This routine is called to set the UART divisor registers to match
+ * the specified baud rate for a serial port.
+ */
+static void change_speed(struct m68k_serial *info)
+{
+	m68328_uart *uart = &uart_addr[info->line];
+	unsigned short port;
+	unsigned short ustcnt;
+	unsigned cflag;
+	int	i;
+
+	if (!info->tty || !info->tty->termios)
+		return;
+	cflag = info->tty->termios->c_cflag;
+	if (!(port = info->port))
+		return;
+
+	ustcnt = uart->ustcnt;
+	uart->ustcnt = ustcnt & ~USTCNT_TXEN;
+
+	i = cflag & CBAUD;
+        if (i & CBAUDEX) {
+                i = (i & ~CBAUDEX) + B38400;
+        }
+
+	info->baud = baud_table[i];
+	uart->ubaud = PUT_FIELD(UBAUD_DIVIDE,    hw_baud_table[i].divisor) | 
+		PUT_FIELD(UBAUD_PRESCALER, hw_baud_table[i].prescale);
+
+	ustcnt &= ~(USTCNT_PARITYEN | USTCNT_ODD_EVEN | USTCNT_STOP | USTCNT_8_7);
+	
+	if ((cflag & CSIZE) == CS8)
+		ustcnt |= USTCNT_8_7;
+		
+	if (cflag & CSTOPB)
+		ustcnt |= USTCNT_STOP;
+
+	if (cflag & PARENB)
+		ustcnt |= USTCNT_PARITYEN;
+	if (cflag & PARODD)
+		ustcnt |= USTCNT_ODD_EVEN;
+	
+#ifdef CONFIG_SERIAL_68328_RTS_CTS
+	if (cflag & CRTSCTS) {
+		uart->utx.w &= ~ UTX_NOCTS;
+	} else {
+		uart->utx.w |= UTX_NOCTS;
+	}
+#endif
+
+	ustcnt |= USTCNT_TXEN;
+	
+	uart->ustcnt = ustcnt;
+	return;
+}
+
+/*
+ * Fair output driver allows a process to speak.
+ */
+static void rs_fair_output(void)
+{
+	int left;		/* Output no more than that */
+	unsigned long flags;
+	struct m68k_serial *info = &m68k_soft[0];
+	char c;
+
+	if (info == 0) return;
+	if (info->xmit_buf == 0) return;
+
+	local_irq_save(flags);
+	left = info->xmit_cnt;
+	while (left != 0) {
+		c = info->xmit_buf[info->xmit_tail];
+		info->xmit_tail = (info->xmit_tail+1) & (SERIAL_XMIT_SIZE-1);
+		info->xmit_cnt--;
+		local_irq_restore(flags);
+
+		rs_put_char(c);
+
+		local_irq_save(flags);
+		left = min(info->xmit_cnt, left-1);
+	}
+
+	/* Last character is being transmitted now (hopefully). */
+	udelay(5);
+
+	local_irq_restore(flags);
+	return;
+}
+
+/*
+ * m68k_console_print is registered for printk.
+ */
+void console_print_68328(const char *p)
+{
+	char c;
+	
+	while((c=*(p++)) != 0) {
+		if(c == '\n')
+			rs_put_char('\r');
+		rs_put_char(c);
+	}
+
+	/* Comment this if you want to have a strict interrupt-driven output */
+	rs_fair_output();
+
+	return;
+}
+
+static void rs_set_ldisc(struct tty_struct *tty)
+{
+	struct m68k_serial *info = (struct m68k_serial *)tty->driver_data;
+
+	if (serial_paranoia_check(info, tty->name, "rs_set_ldisc"))
+		return;
+
+	info->is_cons = (tty->termios->c_line == N_TTY);
+	
+	printk("ttyS%d console mode %s\n", info->line, info->is_cons ? "on" : "off");
+}
+
+static void rs_flush_chars(struct tty_struct *tty)
+{
+	struct m68k_serial *info = (struct m68k_serial *)tty->driver_data;
+	m68328_uart *uart = &uart_addr[info->line];
+	unsigned long flags;
+
+	if (serial_paranoia_check(info, tty->name, "rs_flush_chars"))
+		return;
+#ifndef USE_INTS
+	for(;;) {
+#endif
+
+	/* Enable transmitter */
+	local_irq_save(flags);
+
+	if (info->xmit_cnt <= 0 || tty->stopped || tty->hw_stopped ||
+			!info->xmit_buf) {
+		local_irq_restore(flags);
+		return;
+	}
+
+#ifdef USE_INTS
+	uart->ustcnt |= USTCNT_TXEN | USTCNT_TX_INTR_MASK;
+#else
+	uart->ustcnt |= USTCNT_TXEN;
+#endif
+
+#ifdef USE_INTS
+	if (uart->utx.w & UTX_TX_AVAIL) {
+#else
+	if (1) {
+#endif
+		/* Send char */
+		uart->utx.b.txdata = info->xmit_buf[info->xmit_tail++];
+		info->xmit_tail = info->xmit_tail & (SERIAL_XMIT_SIZE-1);
+		info->xmit_cnt--;
+	}
+
+#ifndef USE_INTS
+	while (!(uart->utx.w & UTX_TX_AVAIL)) udelay(5);
+	}
+#endif
+	local_irq_restore(flags);
+}
+
+extern void console_printn(const char * b, int count);
+
+static int rs_write(struct tty_struct * tty,
+		    const unsigned char *buf, int count)
+{
+	int	c, total = 0;
+	struct m68k_serial *info = (struct m68k_serial *)tty->driver_data;
+	m68328_uart *uart = &uart_addr[info->line];
+	unsigned long flags;
+
+	if (serial_paranoia_check(info, tty->name, "rs_write"))
+		return 0;
+
+	if (!tty || !info->xmit_buf)
+		return 0;
+
+	local_save_flags(flags);
+	while (1) {
+		local_irq_disable();		
+		c = min_t(int, count, min(SERIAL_XMIT_SIZE - info->xmit_cnt - 1,
+				   SERIAL_XMIT_SIZE - info->xmit_head));
+		local_irq_restore(flags);
+
+		if (c <= 0)
+			break;
+
+		memcpy(info->xmit_buf + info->xmit_head, buf, c);
+
+		local_irq_disable();
+		info->xmit_head = (info->xmit_head + c) & (SERIAL_XMIT_SIZE-1);
+		info->xmit_cnt += c;
+		local_irq_restore(flags);
+		buf += c;
+		count -= c;
+		total += c;
+	}
+
+	if (info->xmit_cnt && !tty->stopped && !tty->hw_stopped) {
+		/* Enable transmitter */
+		local_irq_disable();		
+#ifndef USE_INTS
+		while(info->xmit_cnt) {
+#endif
+
+		uart->ustcnt |= USTCNT_TXEN;
+#ifdef USE_INTS
+		uart->ustcnt |= USTCNT_TX_INTR_MASK;
+#else
+		while (!(uart->utx.w & UTX_TX_AVAIL)) udelay(5);
+#endif
+		if (uart->utx.w & UTX_TX_AVAIL) {
+			uart->utx.b.txdata = info->xmit_buf[info->xmit_tail++];
+			info->xmit_tail = info->xmit_tail & (SERIAL_XMIT_SIZE-1);
+			info->xmit_cnt--;
+		}
+
+#ifndef USE_INTS
+		}
+#endif
+		local_irq_restore(flags);
+	}
+
+	return total;
+}
+
+static int rs_write_room(struct tty_struct *tty)
+{
+	struct m68k_serial *info = (struct m68k_serial *)tty->driver_data;
+	int	ret;
+				
+	if (serial_paranoia_check(info, tty->name, "rs_write_room"))
+		return 0;
+	ret = SERIAL_XMIT_SIZE - info->xmit_cnt - 1;
+	if (ret < 0)
+		ret = 0;
+	return ret;
+}
+
+static int rs_chars_in_buffer(struct tty_struct *tty)
+{
+	struct m68k_serial *info = (struct m68k_serial *)tty->driver_data;
+				
+	if (serial_paranoia_check(info, tty->name, "rs_chars_in_buffer"))
+		return 0;
+	return info->xmit_cnt;
+}
+
+static void rs_flush_buffer(struct tty_struct *tty)
+{
+	struct m68k_serial *info = (struct m68k_serial *)tty->driver_data;
+	unsigned long flags;
+				
+	if (serial_paranoia_check(info, tty->name, "rs_flush_buffer"))
+		return;
+	local_irq_save(flags);
+	info->xmit_cnt = info->xmit_head = info->xmit_tail = 0;
+	local_irq_restore(flags);
+	tty_wakeup(tty);
+}
+
+/*
+ * ------------------------------------------------------------
+ * rs_throttle()
+ * 
+ * This routine is called by the upper-layer tty layer to signal that
+ * incoming characters should be throttled.
+ * ------------------------------------------------------------
+ */
+static void rs_throttle(struct tty_struct * tty)
+{
+	struct m68k_serial *info = (struct m68k_serial *)tty->driver_data;
+
+	if (serial_paranoia_check(info, tty->name, "rs_throttle"))
+		return;
+	
+	if (I_IXOFF(tty))
+		info->x_char = STOP_CHAR(tty);
+
+	/* Turn off RTS line (do this atomic) */
+}
+
+static void rs_unthrottle(struct tty_struct * tty)
+{
+	struct m68k_serial *info = (struct m68k_serial *)tty->driver_data;
+
+	if (serial_paranoia_check(info, tty->name, "rs_unthrottle"))
+		return;
+	
+	if (I_IXOFF(tty)) {
+		if (info->x_char)
+			info->x_char = 0;
+		else
+			info->x_char = START_CHAR(tty);
+	}
+
+	/* Assert RTS line (do this atomic) */
+}
+
+/*
+ * ------------------------------------------------------------
+ * rs_ioctl() and friends
+ * ------------------------------------------------------------
+ */
+
+static int get_serial_info(struct m68k_serial * info,
+			   struct serial_struct * retinfo)
+{
+	struct serial_struct tmp;
+  
+	if (!retinfo)
+		return -EFAULT;
+	memset(&tmp, 0, sizeof(tmp));
+	tmp.type = info->type;
+	tmp.line = info->line;
+	tmp.port = info->port;
+	tmp.irq = info->irq;
+	tmp.flags = info->flags;
+	tmp.baud_base = info->baud_base;
+	tmp.close_delay = info->close_delay;
+	tmp.closing_wait = info->closing_wait;
+	tmp.custom_divisor = info->custom_divisor;
+	if (copy_to_user(retinfo, &tmp, sizeof(*retinfo)))
+		return -EFAULT;
+
+	return 0;
+}
+
+static int set_serial_info(struct m68k_serial * info,
+			   struct serial_struct * new_info)
+{
+	struct serial_struct new_serial;
+	struct m68k_serial old_info;
+	int 			retval = 0;
+
+	if (!new_info)
+		return -EFAULT;
+	if (copy_from_user(&new_serial, new_info, sizeof(new_serial)))
+		return -EFAULT;
+	old_info = *info;
+
+	if (!capable(CAP_SYS_ADMIN)) {
+		if ((new_serial.baud_base != info->baud_base) ||
+		    (new_serial.type != info->type) ||
+		    (new_serial.close_delay != info->close_delay) ||
+		    ((new_serial.flags & ~S_USR_MASK) !=
+		     (info->flags & ~S_USR_MASK)))
+			return -EPERM;
+		info->flags = ((info->flags & ~S_USR_MASK) |
+			       (new_serial.flags & S_USR_MASK));
+		info->custom_divisor = new_serial.custom_divisor;
+		goto check_and_exit;
+	}
+
+	if (info->count > 1)
+		return -EBUSY;
+
+	/*
+	 * OK, past this point, all the error checking has been done.
+	 * At this point, we start making changes.....
+	 */
+
+	info->baud_base = new_serial.baud_base;
+	info->flags = ((info->flags & ~S_FLAGS) |
+			(new_serial.flags & S_FLAGS));
+	info->type = new_serial.type;
+	info->close_delay = new_serial.close_delay;
+	info->closing_wait = new_serial.closing_wait;
+
+check_and_exit:
+	retval = startup(info);
+	return retval;
+}
+
+/*
+ * get_lsr_info - get line status register info
+ *
+ * Purpose: Let user call ioctl() to get info when the UART physically
+ * 	    is emptied.  On bus types like RS485, the transmitter must
+ * 	    release the bus after transmitting. This must be done when
+ * 	    the transmit shift register is empty, not be done when the
+ * 	    transmit holding register is empty.  This functionality
+ * 	    allows an RS485 driver to be written in user space. 
+ */
+static int get_lsr_info(struct m68k_serial * info, unsigned int *value)
+{
+#ifdef CONFIG_SERIAL_68328_RTS_CTS
+	m68328_uart *uart = &uart_addr[info->line];
+#endif
+	unsigned char status;
+	unsigned long flags;
+
+	local_irq_save(flags);
+#ifdef CONFIG_SERIAL_68328_RTS_CTS
+	status = (uart->utx.w & UTX_CTS_STAT) ? 1 : 0;
+#else
+	status = 0;
+#endif
+	local_irq_restore(flags);
+	return put_user(status, value);
+}
+
+/*
+ * This routine sends a break character out the serial port.
+ */
+static void send_break(struct m68k_serial * info, unsigned int duration)
+{
+	m68328_uart *uart = &uart_addr[info->line];
+        unsigned long flags;
+        if (!info->port)
+                return;
+        local_irq_save(flags);
+#ifdef USE_INTS	
+	uart->utx.w |= UTX_SEND_BREAK;
+	msleep_interruptible(duration);
+	uart->utx.w &= ~UTX_SEND_BREAK;
+#endif		
+        local_irq_restore(flags);
+}
+
+static int rs_ioctl(struct tty_struct *tty,
+		    unsigned int cmd, unsigned long arg)
+{
+	struct m68k_serial * info = (struct m68k_serial *)tty->driver_data;
+	int retval;
+
+	if (serial_paranoia_check(info, tty->name, "rs_ioctl"))
+		return -ENODEV;
+
+	if ((cmd != TIOCGSERIAL) && (cmd != TIOCSSERIAL) &&
+	    (cmd != TIOCSERCONFIG) && (cmd != TIOCSERGWILD)  &&
+	    (cmd != TIOCSERSWILD) && (cmd != TIOCSERGSTRUCT)) {
+		if (tty->flags & (1 << TTY_IO_ERROR))
+		    return -EIO;
+	}
+	
+	switch (cmd) {
+		case TCSBRK:	/* SVID version: non-zero arg --> no break */
+			retval = tty_check_change(tty);
+			if (retval)
+				return retval;
+			tty_wait_until_sent(tty, 0);
+			if (!arg)
+				send_break(info, 250);	/* 1/4 second */
+			return 0;
+		case TCSBRKP:	/* support for POSIX tcsendbreak() */
+			retval = tty_check_change(tty);
+			if (retval)
+				return retval;
+			tty_wait_until_sent(tty, 0);
+			send_break(info, arg ? arg*(100) : 250);
+			return 0;
+		case TIOCGSERIAL:
+			return get_serial_info(info,
+				       (struct serial_struct *) arg);
+		case TIOCSSERIAL:
+			return set_serial_info(info,
+					       (struct serial_struct *) arg);
+		case TIOCSERGETLSR: /* Get line status register */
+			return get_lsr_info(info, (unsigned int *) arg);
+		case TIOCSERGSTRUCT:
+			if (copy_to_user((struct m68k_serial *) arg,
+				    info, sizeof(struct m68k_serial)))
+				return -EFAULT;
+			return 0;
+		default:
+			return -ENOIOCTLCMD;
+		}
+	return 0;
+}
+
+static void rs_set_termios(struct tty_struct *tty, struct ktermios *old_termios)
+{
+	struct m68k_serial *info = (struct m68k_serial *)tty->driver_data;
+
+	change_speed(info);
+
+	if ((old_termios->c_cflag & CRTSCTS) &&
+	    !(tty->termios->c_cflag & CRTSCTS)) {
+		tty->hw_stopped = 0;
+		rs_start(tty);
+	}
+	
+}
+
+/*
+ * ------------------------------------------------------------
+ * rs_close()
+ * 
+ * This routine is called when the serial port gets closed.  First, we
+ * wait for the last remaining data to be sent.  Then, we unlink its
+ * S structure from the interrupt chain if necessary, and we free
+ * that IRQ if nothing is left in the chain.
+ * ------------------------------------------------------------
+ */
+static void rs_close(struct tty_struct *tty, struct file * filp)
+{
+	struct m68k_serial * info = (struct m68k_serial *)tty->driver_data;
+	m68328_uart *uart = &uart_addr[info->line];
+	unsigned long flags;
+
+	if (!info || serial_paranoia_check(info, tty->name, "rs_close"))
+		return;
+	
+	local_irq_save(flags);
+	
+	if (tty_hung_up_p(filp)) {
+		local_irq_restore(flags);
+		return;
+	}
+	
+	if ((tty->count == 1) && (info->count != 1)) {
+		/*
+		 * Uh, oh.  tty->count is 1, which means that the tty
+		 * structure will be freed.  Info->count should always
+		 * be one in these conditions.  If it's greater than
+		 * one, we've got real problems, since it means the
+		 * serial port won't be shutdown.
+		 */
+		printk("rs_close: bad serial port count; tty->count is 1, "
+		       "info->count is %d\n", info->count);
+		info->count = 1;
+	}
+	if (--info->count < 0) {
+		printk("rs_close: bad serial port count for ttyS%d: %d\n",
+		       info->line, info->count);
+		info->count = 0;
+	}
+	if (info->count) {
+		local_irq_restore(flags);
+		return;
+	}
+	info->flags |= S_CLOSING;
+	/*
+	 * Now we wait for the transmit buffer to clear; and we notify 
+	 * the line discipline to only process XON/XOFF characters.
+	 */
+	tty->closing = 1;
+	if (info->closing_wait != S_CLOSING_WAIT_NONE)
+		tty_wait_until_sent(tty, info->closing_wait);
+	/*
+	 * At this point we stop accepting input.  To do this, we
+	 * disable the receive line status interrupts, and tell the
+	 * interrupt driver to stop checking the data ready bit in the
+	 * line status register.
+	 */
+
+	uart->ustcnt &= ~USTCNT_RXEN;
+	uart->ustcnt &= ~(USTCNT_RXEN | USTCNT_RX_INTR_MASK);
+
+	shutdown(info);
+	rs_flush_buffer(tty);
+		
+	tty_ldisc_flush(tty);
+	tty->closing = 0;
+	info->event = 0;
+	info->tty = NULL;
+#warning "This is not and has never been valid so fix it"	
+#if 0
+	if (tty->ldisc.num != ldiscs[N_TTY].num) {
+		if (tty->ldisc.close)
+			(tty->ldisc.close)(tty);
+		tty->ldisc = ldiscs[N_TTY];
+		tty->termios->c_line = N_TTY;
+		if (tty->ldisc.open)
+			(tty->ldisc.open)(tty);
+	}
+#endif	
+	if (info->blocked_open) {
+		if (info->close_delay) {
+			msleep_interruptible(jiffies_to_msecs(info->close_delay));
+		}
+		wake_up_interruptible(&info->open_wait);
+	}
+	info->flags &= ~(S_NORMAL_ACTIVE|S_CLOSING);
+	wake_up_interruptible(&info->close_wait);
+	local_irq_restore(flags);
+}
+
+/*
+ * rs_hangup() --- called by tty_hangup() when a hangup is signaled.
+ */
+void rs_hangup(struct tty_struct *tty)
+{
+	struct m68k_serial * info = (struct m68k_serial *)tty->driver_data;
+	
+	if (serial_paranoia_check(info, tty->name, "rs_hangup"))
+		return;
+	
+	rs_flush_buffer(tty);
+	shutdown(info);
+	info->event = 0;
+	info->count = 0;
+	info->flags &= ~S_NORMAL_ACTIVE;
+	info->tty = NULL;
+	wake_up_interruptible(&info->open_wait);
+}
+
+/*
+ * ------------------------------------------------------------
+ * rs_open() and friends
+ * ------------------------------------------------------------
+ */
+static int block_til_ready(struct tty_struct *tty, struct file * filp,
+			   struct m68k_serial *info)
+{
+	DECLARE_WAITQUEUE(wait, current);
+	int		retval;
+	int		do_clocal = 0;
+
+	/*
+	 * If the device is in the middle of being closed, then block
+	 * until it's done, and then try again.
+	 */
+	if (info->flags & S_CLOSING) {
+		interruptible_sleep_on(&info->close_wait);
+#ifdef SERIAL_DO_RESTART
+		if (info->flags & S_HUP_NOTIFY)
+			return -EAGAIN;
+		else
+			return -ERESTARTSYS;
+#else
+		return -EAGAIN;
+#endif
+	}
+	
+	/*
+	 * If non-blocking mode is set, or the port is not enabled,
+	 * then make the check up front and then exit.
+	 */
+	if ((filp->f_flags & O_NONBLOCK) ||
+	    (tty->flags & (1 << TTY_IO_ERROR))) {
+		info->flags |= S_NORMAL_ACTIVE;
+		return 0;
+	}
+
+	if (tty->termios->c_cflag & CLOCAL)
+		do_clocal = 1;
+
+	/*
+	 * Block waiting for the carrier detect and the line to become
+	 * free (i.e., not in use by the callout).  While we are in
+	 * this loop, info->count is dropped by one, so that
+	 * rs_close() knows when to free things.  We restore it upon
+	 * exit, either normal or abnormal.
+	 */
+	retval = 0;
+	add_wait_queue(&info->open_wait, &wait);
+
+	info->count--;
+	info->blocked_open++;
+	while (1) {
+		local_irq_disable();
+		m68k_rtsdtr(info, 1);
+		local_irq_enable();
+		current->state = TASK_INTERRUPTIBLE;
+		if (tty_hung_up_p(filp) ||
+		    !(info->flags & S_INITIALIZED)) {
+#ifdef SERIAL_DO_RESTART
+			if (info->flags & S_HUP_NOTIFY)
+				retval = -EAGAIN;
+			else
+				retval = -ERESTARTSYS;	
+#else
+			retval = -EAGAIN;
+#endif
+			break;
+		}
+		if (!(info->flags & S_CLOSING) && do_clocal)
+			break;
+                if (signal_pending(current)) {
+			retval = -ERESTARTSYS;
+			break;
+		}
+		tty_unlock();
+		schedule();
+		tty_lock();
+	}
+	current->state = TASK_RUNNING;
+	remove_wait_queue(&info->open_wait, &wait);
+	if (!tty_hung_up_p(filp))
+		info->count++;
+	info->blocked_open--;
+
+	if (retval)
+		return retval;
+	info->flags |= S_NORMAL_ACTIVE;
+	return 0;
+}	
+
+/*
+ * This routine is called whenever a serial port is opened.  It
+ * enables interrupts for a serial port, linking in its S structure into
+ * the IRQ chain.   It also performs the serial-specific
+ * initialization for the tty structure.
+ */
+int rs_open(struct tty_struct *tty, struct file * filp)
+{
+	struct m68k_serial	*info;
+	int retval;
+
+	info = &m68k_soft[tty->index];
+
+	if (serial_paranoia_check(info, tty->name, "rs_open"))
+		return -ENODEV;
+
+	info->count++;
+	tty->driver_data = info;
+	info->tty = tty;
+
+	/*
+	 * Start up serial port
+	 */
+	retval = startup(info);
+	if (retval)
+		return retval;
+
+	return block_til_ready(tty, filp, info);
+}
+
+/* Finally, routines used to initialize the serial driver. */
+
+static void show_serial_version(void)
+{
+	printk("MC68328 serial driver version 1.00\n");
+}
+
+static const struct tty_operations rs_ops = {
+	.open = rs_open,
+	.close = rs_close,
+	.write = rs_write,
+	.flush_chars = rs_flush_chars,
+	.write_room = rs_write_room,
+	.chars_in_buffer = rs_chars_in_buffer,
+	.flush_buffer = rs_flush_buffer,
+	.ioctl = rs_ioctl,
+	.throttle = rs_throttle,
+	.unthrottle = rs_unthrottle,
+	.set_termios = rs_set_termios,
+	.stop = rs_stop,
+	.start = rs_start,
+	.hangup = rs_hangup,
+	.set_ldisc = rs_set_ldisc,
+};
+
+/* rs_init inits the driver */
+static int __init
+rs68328_init(void)
+{
+	int flags, i;
+	struct m68k_serial *info;
+
+	serial_driver = alloc_tty_driver(NR_PORTS);
+	if (!serial_driver)
+		return -ENOMEM;
+
+	show_serial_version();
+
+	/* Initialize the tty_driver structure */
+	/* SPARC: Not all of this is exactly right for us. */
+	
+	serial_driver->name = "ttyS";
+	serial_driver->major = TTY_MAJOR;
+	serial_driver->minor_start = 64;
+	serial_driver->type = TTY_DRIVER_TYPE_SERIAL;
+	serial_driver->subtype = SERIAL_TYPE_NORMAL;
+	serial_driver->init_termios = tty_std_termios;
+	serial_driver->init_termios.c_cflag = 
+			m68328_console_cbaud | CS8 | CREAD | HUPCL | CLOCAL;
+	serial_driver->flags = TTY_DRIVER_REAL_RAW;
+	tty_set_operations(serial_driver, &rs_ops);
+
+	if (tty_register_driver(serial_driver)) {
+		put_tty_driver(serial_driver);
+		printk(KERN_ERR "Couldn't register serial driver\n");
+		return -ENOMEM;
+	}
+
+	local_irq_save(flags);
+
+	for(i=0;i<NR_PORTS;i++) {
+
+	    info = &m68k_soft[i];
+	    info->magic = SERIAL_MAGIC;
+	    info->port = (int) &uart_addr[i];
+	    info->tty = NULL;
+	    info->irq = uart_irqs[i];
+	    info->custom_divisor = 16;
+	    info->close_delay = 50;
+	    info->closing_wait = 3000;
+	    info->x_char = 0;
+	    info->event = 0;
+	    info->count = 0;
+	    info->blocked_open = 0;
+	    init_waitqueue_head(&info->open_wait);
+	    init_waitqueue_head(&info->close_wait);
+	    info->line = i;
+	    info->is_cons = 1; /* Means shortcuts work */
+	    
+	    printk("%s%d at 0x%08x (irq = %d)", serial_driver->name, info->line, 
+		   info->port, info->irq);
+	    printk(" is a builtin MC68328 UART\n");
+	    
+#ifdef CONFIG_M68VZ328
+		if (i > 0 )
+			PJSEL &= 0xCF;  /* PSW enable second port output */
+#endif
+
+	    if (request_irq(uart_irqs[i],
+			    rs_interrupt,
+			    0,
+			    "M68328_UART", info))
+                panic("Unable to attach 68328 serial interrupt\n");
+	}
+	local_irq_restore(flags);
+	return 0;
+}
+
+module_init(rs68328_init);
+
+
+
+static void m68328_set_baud(void)
+{
+	unsigned short ustcnt;
+	int	i;
+
+	ustcnt = USTCNT;
+	USTCNT = ustcnt & ~USTCNT_TXEN;
+
+again:
+	for (i = 0; i < ARRAY_SIZE(baud_table); i++)
+		if (baud_table[i] == m68328_console_baud)
+			break;
+	if (i >= ARRAY_SIZE(baud_table)) {
+		m68328_console_baud = 9600;
+		goto again;
+	}
+
+	UBAUD = PUT_FIELD(UBAUD_DIVIDE,    hw_baud_table[i].divisor) | 
+		PUT_FIELD(UBAUD_PRESCALER, hw_baud_table[i].prescale);
+	ustcnt &= ~(USTCNT_PARITYEN | USTCNT_ODD_EVEN | USTCNT_STOP | USTCNT_8_7);
+	ustcnt |= USTCNT_8_7;
+	ustcnt |= USTCNT_TXEN;
+	USTCNT = ustcnt;
+	m68328_console_initted = 1;
+	return;
+}
+
+
+int m68328_console_setup(struct console *cp, char *arg)
+{
+	int		i, n = CONSOLE_BAUD_RATE;
+
+	if (!cp)
+		return(-1);
+
+	if (arg)
+		n = simple_strtoul(arg,NULL,0);
+
+	for (i = 0; i < ARRAY_SIZE(baud_table); i++)
+		if (baud_table[i] == n)
+			break;
+	if (i < ARRAY_SIZE(baud_table)) {
+		m68328_console_baud = n;
+		m68328_console_cbaud = 0;
+		if (i > 15) {
+			m68328_console_cbaud |= CBAUDEX;
+			i -= 15;
+		}
+		m68328_console_cbaud |= i;
+	}
+
+	m68328_set_baud(); /* make sure baud rate changes */
+	return(0);
+}
+
+
+static struct tty_driver *m68328_console_device(struct console *c, int *index)
+{
+	*index = c->index;
+	return serial_driver;
+}
+
+
+void m68328_console_write (struct console *co, const char *str,
+			   unsigned int count)
+{
+	if (!m68328_console_initted)
+		m68328_set_baud();
+    while (count--) {
+        if (*str == '\n')
+           rs_put_char('\r');
+        rs_put_char( *str++ );
+    }
+}
+
+
+static struct console m68328_driver = {
+	.name		= "ttyS",
+	.write		= m68328_console_write,
+	.device		= m68328_console_device,
+	.setup		= m68328_console_setup,
+	.flags		= CON_PRINTBUFFER,
+	.index		= -1,
+};
+
+
+static int __init m68328_console_init(void)
+{
+	register_console(&m68328_driver);
+	return 0;
+}
+
+console_initcall(m68328_console_init);
diff --git a/ap/os/linux/linux-3.4.x/drivers/tty/serial/68328serial.h b/ap/os/linux/linux-3.4.x/drivers/tty/serial/68328serial.h
new file mode 100644
index 0000000..3d2faab
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/tty/serial/68328serial.h
@@ -0,0 +1,186 @@
+/* 68328serial.h: Definitions for the mc68328 serial driver.
+ *
+ * Copyright (C) 1995       David S. Miller    <davem@caip.rutgers.edu>
+ * Copyright (C) 1998       Kenneth Albanowski <kjahds@kjahds.com>
+ * Copyright (C) 1998, 1999 D. Jeff Dionne     <jeff@uclinux.org>
+ * Copyright (C) 1999       Vladimir Gurevich  <vgurevic@cisco.com>
+ *
+ * VZ Support/Fixes             Evan Stawnyczy <e@lineo.ca>
+ */
+
+#ifndef _MC683XX_SERIAL_H
+#define _MC683XX_SERIAL_H
+
+
+struct serial_struct {
+	int	type;
+	int	line;
+	int	port;
+	int	irq;
+	int	flags;
+	int	xmit_fifo_size;
+	int	custom_divisor;
+	int	baud_base;
+	unsigned short	close_delay;
+	char	reserved_char[2];
+	int	hub6;  /* FIXME: We don't have AT&T Hub6 boards! */
+	unsigned short	closing_wait; /* time to wait before closing */
+	unsigned short	closing_wait2; /* no longer used... */
+	int	reserved[4];
+};
+
+/*
+ * For the close wait times, 0 means wait forever for serial port to
+ * flush its output.  65535 means don't wait at all.
+ */
+#define S_CLOSING_WAIT_INF	0
+#define S_CLOSING_WAIT_NONE	65535
+
+/*
+ * Definitions for S_struct (and serial_struct) flags field
+ */
+#define S_HUP_NOTIFY 0x0001 /* Notify getty on hangups and closes 
+				   on the callout port */
+#define S_FOURPORT  0x0002	/* Set OU1, OUT2 per AST Fourport settings */
+#define S_SAK	0x0004	/* Secure Attention Key (Orange book) */
+#define S_SPLIT_TERMIOS 0x0008 /* Separate termios for dialin/callout */
+
+#define S_SPD_MASK	0x0030
+#define S_SPD_HI	0x0010	/* Use 56000 instead of 38400 bps */
+
+#define S_SPD_VHI	0x0020  /* Use 115200 instead of 38400 bps */
+#define S_SPD_CUST	0x0030  /* Use user-specified divisor */
+
+#define S_SKIP_TEST	0x0040 /* Skip UART test during autoconfiguration */
+#define S_AUTO_IRQ  0x0080 /* Do automatic IRQ during autoconfiguration */
+#define S_SESSION_LOCKOUT 0x0100 /* Lock out cua opens based on session */
+#define S_PGRP_LOCKOUT    0x0200 /* Lock out cua opens based on pgrp */
+#define S_CALLOUT_NOHUP   0x0400 /* Don't do hangups for cua device */
+
+#define S_FLAGS	0x0FFF	/* Possible legal S flags */
+#define S_USR_MASK 0x0430	/* Legal flags that non-privileged
+				 * users can set or reset */
+
+/* Internal flags used only by kernel/chr_drv/serial.c */
+#define S_INITIALIZED	0x80000000 /* Serial port was initialized */
+#define S_CALLOUT_ACTIVE	0x40000000 /* Call out device is active */
+#define S_NORMAL_ACTIVE	0x20000000 /* Normal device is active */
+#define S_BOOT_AUTOCONF	0x10000000 /* Autoconfigure port on bootup */
+#define S_CLOSING		0x08000000 /* Serial port is closing */
+#define S_CTS_FLOW		0x04000000 /* Do CTS flow control */
+#define S_CHECK_CD		0x02000000 /* i.e., CLOCAL */
+
+/* Software state per channel */
+
+#ifdef __KERNEL__
+
+/*
+ * I believe this is the optimal setting that reduces the number of interrupts.
+ * At high speeds the output might become a little "bursted" (use USTCNT_TXHE
+ * if that bothers you), but in most cases it will not, since we try to 
+ * transmit characters every time rs_interrupt is called. Thus, quite often
+ * you'll see that a receive interrupt occures before the transmit one.
+ *                                  -- Vladimir Gurevich
+ */
+#define USTCNT_TX_INTR_MASK (USTCNT_TXEE)
+
+/*
+ * 68328 and 68EZ328 UARTS are a little bit different. EZ328 has special
+ * "Old data interrupt" which occures whenever the data stay in the FIFO
+ * longer than 30 bits time. This allows us to use FIFO without compromising
+ * latency. '328 does not have this feature and without the real  328-based
+ * board I would assume that RXRE is the safest setting.
+ *
+ * For EZ328 I use RXHE (Half empty) interrupt to reduce the number of
+ * interrupts. RXFE (receive queue full) causes the system to lose data
+ * at least at 115200 baud
+ *
+ * If your board is busy doing other stuff, you might consider to use
+ * RXRE (data ready intrrupt) instead.
+ *
+ * The other option is to make these INTR masks run-time configurable, so
+ * that people can dynamically adapt them according to the current usage.
+ *                                  -- Vladimir Gurevich
+ */
+
+/* (es) */
+#if defined(CONFIG_M68EZ328) || defined(CONFIG_M68VZ328)
+#define USTCNT_RX_INTR_MASK (USTCNT_RXHE | USTCNT_ODEN)
+#elif defined(CONFIG_M68328)
+#define USTCNT_RX_INTR_MASK (USTCNT_RXRE)
+#else
+#error Please, define the Rx interrupt events for your CPU
+#endif
+/* (/es) */
+
+/*
+ * This is our internal structure for each serial port's state.
+ * 
+ * Many fields are paralleled by the structure used by the serial_struct
+ * structure.
+ *
+ * For definitions of the flags field, see tty.h
+ */
+
+struct m68k_serial {
+	char soft_carrier;  /* Use soft carrier on this channel */
+	char break_abort;   /* Is serial console in, so process brk/abrt */
+	char is_cons;       /* Is this our console. */
+
+	/* We need to know the current clock divisor
+	 * to read the bps rate the chip has currently
+	 * loaded.
+	 */
+	unsigned char clk_divisor;  /* May be 1, 16, 32, or 64 */
+	int baud;
+	int			magic;
+	int			baud_base;
+	int			port;
+	int			irq;
+	int			flags; 		/* defined in tty.h */
+	int			type; 		/* UART type */
+	struct tty_struct 	*tty;
+	int			read_status_mask;
+	int			ignore_status_mask;
+	int			timeout;
+	int			xmit_fifo_size;
+	int			custom_divisor;
+	int			x_char;	/* xon/xoff character */
+	int			close_delay;
+	unsigned short		closing_wait;
+	unsigned short		closing_wait2;
+	unsigned long		event;
+	unsigned long		last_active;
+	int			line;
+	int			count;	    /* # of fd on device */
+	int			blocked_open; /* # of blocked opens */
+	unsigned char 		*xmit_buf;
+	int			xmit_head;
+	int			xmit_tail;
+	int			xmit_cnt;
+	wait_queue_head_t	open_wait;
+	wait_queue_head_t	close_wait;
+};
+
+
+#define SERIAL_MAGIC 0x5301
+
+/*
+ * The size of the serial xmit buffer is 1 page, or 4096 bytes
+ */
+#define SERIAL_XMIT_SIZE 4096
+
+/*
+ * Events are used to schedule things to happen at timer-interrupt
+ * time, instead of at rs interrupt time.
+ */
+#define RS_EVENT_WRITE_WAKEUP	0
+
+/* 
+ * Define the number of ports supported and their irqs.
+ */
+#define NR_PORTS 1
+#define UART_IRQ_DEFNS {UART_IRQ_NUM}
+
+#endif /* __KERNEL__ */
+#endif /* !(_MC683XX_SERIAL_H) */
diff --git a/ap/os/linux/linux-3.4.x/drivers/tty/serial/8250/8250.c b/ap/os/linux/linux-3.4.x/drivers/tty/serial/8250/8250.c
new file mode 100644
index 0000000..2d5c0cb
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/tty/serial/8250/8250.c
@@ -0,0 +1,3385 @@
+/*
+ *  Driver for 8250/16550-type serial ports
+ *
+ *  Based on drivers/char/serial.c, by Linus Torvalds, Theodore Ts'o.
+ *
+ *  Copyright (C) 2001 Russell King.
+ *
+ * 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.
+ *
+ * A note about mapbase / membase
+ *
+ *  mapbase is the physical address of the IO port.
+ *  membase is an 'ioremapped' cookie.
+ */
+
+#if defined(CONFIG_SERIAL_8250_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ)
+#define SUPPORT_SYSRQ
+#endif
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/ioport.h>
+#include <linux/init.h>
+#include <linux/console.h>
+#include <linux/sysrq.h>
+#include <linux/delay.h>
+#include <linux/platform_device.h>
+#include <linux/tty.h>
+#include <linux/ratelimit.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/slab.h>
+#include <linux/kdb.h>
+#ifdef CONFIG_SPARC
+#include <linux/sunserialcore.h>
+#endif
+
+#include <asm/io.h>
+#include <asm/irq.h>
+
+#include "8250.h"
+
+/*
+ * 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_SERIAL_8250_RUNTIME_UARTS;
+
+static struct uart_driver serial8250_reg;
+
+static int serial_index(struct uart_port *port)
+{
+	return (serial8250_reg.minor - 64) + port->line;
+}
+
+static unsigned int skip_txen_test; /* force skip of txen test at init time */
+
+/*
+ * Debugging.
+ */
+#if 0
+#define DEBUG_AUTOCONF(fmt...)	printk(fmt)
+#else
+#define DEBUG_AUTOCONF(fmt...)	do { } while (0)
+#endif
+
+#if 0
+#define DEBUG_INTR(fmt...)	printk(fmt)
+#else
+#define DEBUG_INTR(fmt...)	do { } while (0)
+#endif
+
+/*
+ * On -rt we can have a more delays, and legitimately
+ * so - so don't drop work spuriously and spam the
+ * syslog:
+ */
+#ifdef CONFIG_PREEMPT_RT_FULL
+# define PASS_LIMIT	1000000
+#else
+# define PASS_LIMIT	512
+#endif
+
+#define BOTH_EMPTY 	(UART_LSR_TEMT | UART_LSR_THRE)
+
+
+#ifdef CONFIG_SERIAL_8250_DETECT_IRQ
+#define CONFIG_SERIAL_DETECT_IRQ 1
+#endif
+#ifdef CONFIG_SERIAL_8250_MANY_PORTS
+#define CONFIG_SERIAL_MANY_PORTS 1
+#endif
+
+/*
+ * HUB6 is always on.  This will be removed once the header
+ * files have been cleaned.
+ */
+#define CONFIG_HUB6 1
+
+#include <asm/serial.h>
+/*
+ * SERIAL_PORT_DFNS tells us about built-in ports that have no
+ * standard enumeration mechanism.   Platforms that can find all
+ * serial ports via mechanisms like ACPI or PCI need not supply it.
+ */
+#ifndef SERIAL_PORT_DFNS
+#define SERIAL_PORT_DFNS
+#endif
+
+static const struct old_serial_port old_serial_port[] = {
+	SERIAL_PORT_DFNS /* defined in asm/serial.h */
+};
+
+#define UART_NR	CONFIG_SERIAL_8250_NR_UARTS
+
+#ifdef CONFIG_SERIAL_8250_RSA
+
+#define PORT_RSA_MAX 4
+static unsigned long probe_rsa[PORT_RSA_MAX];
+static unsigned int probe_rsa_count;
+#endif /* CONFIG_SERIAL_8250_RSA  */
+
+struct irq_info {
+	struct			hlist_node node;
+	int			irq;
+	spinlock_t		lock;	/* Protects list not the hash */
+	struct list_head	*head;
+};
+
+#define NR_IRQ_HASH		32	/* Can be adjusted later */
+static struct hlist_head irq_lists[NR_IRQ_HASH];
+static DEFINE_MUTEX(hash_mutex);	/* Used to walk the hash */
+
+/*
+ * 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,
+		.flags		= UART_CAP_FIFO,
+	},
+	[PORT_CIRRUS] = {
+		.name		= "Cirrus",
+		.fifo_size	= 1,
+		.tx_loadsz	= 1,
+	},
+	[PORT_16650] = {
+		.name		= "ST16650",
+		.fifo_size	= 1,
+		.tx_loadsz	= 1,
+		.flags		= UART_CAP_FIFO | UART_CAP_EFR | UART_CAP_SLEEP,
+	},
+	[PORT_16650V2] = {
+		.name		= "ST16650V2",
+		.fifo_size	= 32,
+		.tx_loadsz	= 16,
+		.fcr		= UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_01 |
+				  UART_FCR_T_TRIG_00,
+		.flags		= UART_CAP_FIFO | UART_CAP_EFR | UART_CAP_SLEEP,
+	},
+	[PORT_16750] = {
+		.name		= "TI16750",
+		.fifo_size	= 64,
+		.tx_loadsz	= 64,
+		.fcr		= UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_10 |
+				  UART_FCR7_64BYTE,
+		.flags		= UART_CAP_FIFO | UART_CAP_SLEEP | UART_CAP_AFE,
+	},
+	[PORT_STARTECH] = {
+		.name		= "Startech",
+		.fifo_size	= 1,
+		.tx_loadsz	= 1,
+	},
+	[PORT_16C950] = {
+		.name		= "16C950/954",
+		.fifo_size	= 128,
+		.tx_loadsz	= 128,
+		.fcr		= UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_10,
+		/* UART_CAP_EFR breaks billionon CF bluetooth card. */
+		.flags		= UART_CAP_FIFO | UART_CAP_SLEEP,
+	},
+	[PORT_16654] = {
+		.name		= "ST16654",
+		.fifo_size	= 64,
+		.tx_loadsz	= 32,
+		.fcr		= UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_01 |
+				  UART_FCR_T_TRIG_10,
+		.flags		= UART_CAP_FIFO | UART_CAP_EFR | UART_CAP_SLEEP,
+	},
+	[PORT_16850] = {
+		.name		= "XR16850",
+		.fifo_size	= 128,
+		.tx_loadsz	= 128,
+		.fcr		= UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_10,
+		.flags		= UART_CAP_FIFO | UART_CAP_EFR | UART_CAP_SLEEP,
+	},
+	[PORT_RSA] = {
+		.name		= "RSA",
+		.fifo_size	= 2048,
+		.tx_loadsz	= 2048,
+		.fcr		= UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_11,
+		.flags		= UART_CAP_FIFO,
+	},
+	[PORT_NS16550A] = {
+		.name		= "NS16550A",
+		.fifo_size	= 16,
+		.tx_loadsz	= 16,
+		.fcr		= UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_10,
+		.flags		= UART_CAP_FIFO | UART_NATSEMI,
+	},
+	[PORT_XSCALE] = {
+		.name		= "XScale",
+		.fifo_size	= 32,
+		.tx_loadsz	= 32,
+		.fcr		= UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_10,
+		.flags		= UART_CAP_FIFO | UART_CAP_UUE | UART_CAP_RTOIE,
+	},
+	[PORT_RM9000] = {
+		.name		= "RM9000",
+		.fifo_size	= 16,
+		.tx_loadsz	= 16,
+		.fcr		= UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_10,
+		.flags		= UART_CAP_FIFO,
+	},
+	[PORT_OCTEON] = {
+		.name		= "OCTEON",
+		.fifo_size	= 64,
+		.tx_loadsz	= 64,
+		.fcr		= UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_10,
+		.flags		= UART_CAP_FIFO,
+	},
+	[PORT_AR7] = {
+		.name		= "AR7",
+		.fifo_size	= 16,
+		.tx_loadsz	= 16,
+		.fcr		= UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_00,
+		.flags		= UART_CAP_FIFO | UART_CAP_AFE,
+	},
+	[PORT_U6_16550A] = {
+		.name		= "U6_16550A",
+		.fifo_size	= 64,
+		.tx_loadsz	= 64,
+		.fcr		= UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_10,
+		.flags		= UART_CAP_FIFO | UART_CAP_AFE,
+	},
+	[PORT_TEGRA] = {
+		.name		= "Tegra",
+		.fifo_size	= 32,
+		.tx_loadsz	= 8,
+		.fcr		= UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_01 |
+				  UART_FCR_T_TRIG_01,
+		.flags		= UART_CAP_FIFO | UART_CAP_RTOIE,
+	},
+	[PORT_XR17D15X] = {
+		.name		= "XR17D15X",
+		.fifo_size	= 64,
+		.tx_loadsz	= 64,
+		.fcr		= UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_10,
+		.flags		= UART_CAP_FIFO | UART_CAP_AFE | UART_CAP_EFR,
+	},
+	[PORT_BRCM_TRUMANAGE] = {
+		.name		= "TruManage",
+		.fifo_size	= 1,
+		.tx_loadsz	= 1024,
+		.flags		= UART_CAP_HFIFO,
+	},
+	[PORT_ALTR_16550_F32] = {
+		.name		= "Altera 16550 FIFO32",
+		.fifo_size	= 32,
+		.tx_loadsz	= 32,
+		.fcr		= UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_10,
+		.flags		= UART_CAP_FIFO | UART_CAP_AFE,
+	},
+	[PORT_ALTR_16550_F64] = {
+		.name		= "Altera 16550 FIFO64",
+		.fifo_size	= 64,
+		.tx_loadsz	= 64,
+		.fcr		= UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_10,
+		.flags		= UART_CAP_FIFO | UART_CAP_AFE,
+	},
+	[PORT_ALTR_16550_F128] = {
+		.name		= "Altera 16550 FIFO128",
+		.fifo_size	= 128,
+		.tx_loadsz	= 128,
+		.fcr		= UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_10,
+		.flags		= UART_CAP_FIFO | UART_CAP_AFE,
+	},
+};
+
+#if defined(CONFIG_MIPS_ALCHEMY)
+
+/* Au1x00 UART hardware has a weird register layout */
+static const u8 au_io_in_map[] = {
+	[UART_RX]  = 0,
+	[UART_IER] = 2,
+	[UART_IIR] = 3,
+	[UART_LCR] = 5,
+	[UART_MCR] = 6,
+	[UART_LSR] = 7,
+	[UART_MSR] = 8,
+};
+
+static const u8 au_io_out_map[] = {
+	[UART_TX]  = 1,
+	[UART_IER] = 2,
+	[UART_FCR] = 4,
+	[UART_LCR] = 5,
+	[UART_MCR] = 6,
+};
+
+/* sane hardware needs no mapping */
+static inline int map_8250_in_reg(struct uart_port *p, int offset)
+{
+	if (p->iotype != UPIO_AU)
+		return offset;
+	return au_io_in_map[offset];
+}
+
+static inline int map_8250_out_reg(struct uart_port *p, int offset)
+{
+	if (p->iotype != UPIO_AU)
+		return offset;
+	return au_io_out_map[offset];
+}
+
+#elif defined(CONFIG_SERIAL_8250_RM9K)
+
+static const u8
+	regmap_in[8] = {
+		[UART_RX]	= 0x00,
+		[UART_IER]	= 0x0c,
+		[UART_IIR]	= 0x14,
+		[UART_LCR]	= 0x1c,
+		[UART_MCR]	= 0x20,
+		[UART_LSR]	= 0x24,
+		[UART_MSR]	= 0x28,
+		[UART_SCR]	= 0x2c
+	},
+	regmap_out[8] = {
+		[UART_TX] 	= 0x04,
+		[UART_IER]	= 0x0c,
+		[UART_FCR]	= 0x18,
+		[UART_LCR]	= 0x1c,
+		[UART_MCR]	= 0x20,
+		[UART_LSR]	= 0x24,
+		[UART_MSR]	= 0x28,
+		[UART_SCR]	= 0x2c
+	};
+
+static inline int map_8250_in_reg(struct uart_port *p, int offset)
+{
+	if (p->iotype != UPIO_RM9000)
+		return offset;
+	return regmap_in[offset];
+}
+
+static inline int map_8250_out_reg(struct uart_port *p, int offset)
+{
+	if (p->iotype != UPIO_RM9000)
+		return offset;
+	return regmap_out[offset];
+}
+
+#else
+
+/* sane hardware needs no mapping */
+#define map_8250_in_reg(up, offset) (offset)
+#define map_8250_out_reg(up, offset) (offset)
+
+#endif
+
+static unsigned int hub6_serial_in(struct uart_port *p, int offset)
+{
+	offset = map_8250_in_reg(p, offset) << p->regshift;
+	outb(p->hub6 - 1 + offset, p->iobase);
+	return inb(p->iobase + 1);
+}
+
+static void hub6_serial_out(struct uart_port *p, int offset, int value)
+{
+	offset = map_8250_out_reg(p, offset) << p->regshift;
+	outb(p->hub6 - 1 + offset, p->iobase);
+	outb(value, p->iobase + 1);
+}
+
+static unsigned int mem_serial_in(struct uart_port *p, int offset)
+{
+	offset = map_8250_in_reg(p, offset) << p->regshift;
+	return readb(p->membase + offset);
+}
+
+static void mem_serial_out(struct uart_port *p, int offset, int value)
+{
+	offset = map_8250_out_reg(p, offset) << p->regshift;
+	writeb(value, p->membase + offset);
+}
+
+static void mem32_serial_out(struct uart_port *p, int offset, int value)
+{
+	offset = map_8250_out_reg(p, offset) << p->regshift;
+	writel(value, p->membase + offset);
+}
+
+static unsigned int mem32_serial_in(struct uart_port *p, int offset)
+{
+	offset = map_8250_in_reg(p, offset) << p->regshift;
+	return readl(p->membase + offset);
+}
+
+static unsigned int au_serial_in(struct uart_port *p, int offset)
+{
+	offset = map_8250_in_reg(p, offset) << p->regshift;
+	return __raw_readl(p->membase + offset);
+}
+
+static void au_serial_out(struct uart_port *p, int offset, int value)
+{
+	offset = map_8250_out_reg(p, offset) << p->regshift;
+	__raw_writel(value, p->membase + offset);
+}
+
+static unsigned int io_serial_in(struct uart_port *p, int offset)
+{
+	offset = map_8250_in_reg(p, offset) << p->regshift;
+	return inb(p->iobase + offset);
+}
+
+static void io_serial_out(struct uart_port *p, int offset, int value)
+{
+	offset = map_8250_out_reg(p, offset) << p->regshift;
+	outb(value, p->iobase + offset);
+}
+
+static int serial8250_default_handle_irq(struct uart_port *port);
+
+static void set_io_from_upio(struct uart_port *p)
+{
+	struct uart_8250_port *up =
+		container_of(p, struct uart_8250_port, port);
+	switch (p->iotype) {
+	case UPIO_HUB6:
+		p->serial_in = hub6_serial_in;
+		p->serial_out = hub6_serial_out;
+		break;
+
+	case UPIO_MEM:
+		p->serial_in = mem_serial_in;
+		p->serial_out = mem_serial_out;
+		break;
+
+	case UPIO_RM9000:
+	case UPIO_MEM32:
+		p->serial_in = mem32_serial_in;
+		p->serial_out = mem32_serial_out;
+		break;
+
+	case UPIO_AU:
+		p->serial_in = au_serial_in;
+		p->serial_out = au_serial_out;
+		break;
+
+	default:
+		p->serial_in = io_serial_in;
+		p->serial_out = io_serial_out;
+		break;
+	}
+	/* Remember loaded iotype */
+	up->cur_iotype = p->iotype;
+	p->handle_irq = serial8250_default_handle_irq;
+}
+
+static void
+serial_port_out_sync(struct uart_port *p, int offset, int value)
+{
+	switch (p->iotype) {
+	case UPIO_MEM:
+	case UPIO_MEM32:
+	case UPIO_AU:
+		p->serial_out(p, offset, value);
+		p->serial_in(p, UART_LCR);	/* safe, no side-effects */
+		break;
+	default:
+		p->serial_out(p, offset, value);
+	}
+}
+
+/* Uart divisor latch read */
+static inline int _serial_dl_read(struct uart_8250_port *up)
+{
+	return serial_in(up, UART_DLL) | serial_in(up, UART_DLM) << 8;
+}
+
+/* Uart divisor latch write */
+static inline void _serial_dl_write(struct uart_8250_port *up, int value)
+{
+	serial_out(up, UART_DLL, value & 0xff);
+	serial_out(up, UART_DLM, value >> 8 & 0xff);
+}
+
+#if defined(CONFIG_MIPS_ALCHEMY)
+/* Au1x00 haven't got a standard divisor latch */
+static int serial_dl_read(struct uart_8250_port *up)
+{
+	if (up->port.iotype == UPIO_AU)
+		return __raw_readl(up->port.membase + 0x28);
+	else
+		return _serial_dl_read(up);
+}
+
+static void serial_dl_write(struct uart_8250_port *up, int value)
+{
+	if (up->port.iotype == UPIO_AU)
+		__raw_writel(value, up->port.membase + 0x28);
+	else
+		_serial_dl_write(up, value);
+}
+#elif defined(CONFIG_SERIAL_8250_RM9K)
+static int serial_dl_read(struct uart_8250_port *up)
+{
+	return	(up->port.iotype == UPIO_RM9000) ?
+		(((__raw_readl(up->port.membase + 0x10) << 8) |
+		(__raw_readl(up->port.membase + 0x08) & 0xff)) & 0xffff) :
+		_serial_dl_read(up);
+}
+
+static void serial_dl_write(struct uart_8250_port *up, int value)
+{
+	if (up->port.iotype == UPIO_RM9000) {
+		__raw_writel(value, up->port.membase + 0x08);
+		__raw_writel(value >> 8, up->port.membase + 0x10);
+	} else {
+		_serial_dl_write(up, value);
+	}
+}
+#else
+#define serial_dl_read(up) _serial_dl_read(up)
+#define serial_dl_write(up, value) _serial_dl_write(up, value)
+#endif
+
+/*
+ * For the 16C950
+ */
+static void serial_icr_write(struct uart_8250_port *up, int offset, int value)
+{
+	serial_out(up, UART_SCR, offset);
+	serial_out(up, UART_ICR, value);
+}
+
+static unsigned int serial_icr_read(struct uart_8250_port *up, int offset)
+{
+	unsigned int value;
+
+	serial_icr_write(up, UART_ACR, up->acr | UART_ACR_ICRRD);
+	serial_out(up, UART_SCR, offset);
+	value = serial_in(up, UART_ICR);
+	serial_icr_write(up, UART_ACR, up->acr);
+
+	return value;
+}
+
+/*
+ * FIFO support.
+ */
+static void serial8250_clear_fifos(struct uart_8250_port *p)
+{
+	if (p->capabilities & UART_CAP_FIFO) {
+		serial_out(p, UART_FCR, UART_FCR_ENABLE_FIFO);
+		serial_out(p, UART_FCR, UART_FCR_ENABLE_FIFO |
+			       UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT);
+		serial_out(p, UART_FCR, 0);
+	}
+}
+
+/*
+ * IER sleep support.  UARTs which have EFRs need the "extended
+ * capability" bit enabled.  Note that on XR16C850s, we need to
+ * reset LCR to write to IER.
+ */
+static void serial8250_set_sleep(struct uart_8250_port *p, int sleep)
+{
+	if (p->capabilities & UART_CAP_SLEEP) {
+		if (p->capabilities & UART_CAP_EFR) {
+			serial_out(p, UART_LCR, UART_LCR_CONF_MODE_B);
+			serial_out(p, UART_EFR, UART_EFR_ECB);
+			serial_out(p, UART_LCR, 0);
+		}
+		serial_out(p, UART_IER, sleep ? UART_IERX_SLEEP : 0);
+		if (p->capabilities & UART_CAP_EFR) {
+			serial_out(p, UART_LCR, UART_LCR_CONF_MODE_B);
+			serial_out(p, UART_EFR, 0);
+			serial_out(p, UART_LCR, 0);
+		}
+	}
+}
+
+#ifdef CONFIG_SERIAL_8250_RSA
+/*
+ * Attempts to turn on the RSA FIFO.  Returns zero on failure.
+ * We set the port uart clock rate if we succeed.
+ */
+static int __enable_rsa(struct uart_8250_port *up)
+{
+	unsigned char mode;
+	int result;
+
+	mode = serial_in(up, UART_RSA_MSR);
+	result = mode & UART_RSA_MSR_FIFO;
+
+	if (!result) {
+		serial_out(up, UART_RSA_MSR, mode | UART_RSA_MSR_FIFO);
+		mode = serial_in(up, UART_RSA_MSR);
+		result = mode & UART_RSA_MSR_FIFO;
+	}
+
+	if (result)
+		up->port.uartclk = SERIAL_RSA_BAUD_BASE * 16;
+
+	return result;
+}
+
+static void enable_rsa(struct uart_8250_port *up)
+{
+	if (up->port.type == PORT_RSA) {
+		if (up->port.uartclk != SERIAL_RSA_BAUD_BASE * 16) {
+			spin_lock_irq(&up->port.lock);
+			__enable_rsa(up);
+			spin_unlock_irq(&up->port.lock);
+		}
+		if (up->port.uartclk == SERIAL_RSA_BAUD_BASE * 16)
+			serial_out(up, UART_RSA_FRR, 0);
+	}
+}
+
+/*
+ * Attempts to turn off the RSA FIFO.  Returns zero on failure.
+ * It is unknown why interrupts were disabled in here.  However,
+ * the caller is expected to preserve this behaviour by grabbing
+ * the spinlock before calling this function.
+ */
+static void disable_rsa(struct uart_8250_port *up)
+{
+	unsigned char mode;
+	int result;
+
+	if (up->port.type == PORT_RSA &&
+	    up->port.uartclk == SERIAL_RSA_BAUD_BASE * 16) {
+		spin_lock_irq(&up->port.lock);
+
+		mode = serial_in(up, UART_RSA_MSR);
+		result = !(mode & UART_RSA_MSR_FIFO);
+
+		if (!result) {
+			serial_out(up, UART_RSA_MSR, mode & ~UART_RSA_MSR_FIFO);
+			mode = serial_in(up, UART_RSA_MSR);
+			result = !(mode & UART_RSA_MSR_FIFO);
+		}
+
+		if (result)
+			up->port.uartclk = SERIAL_RSA_BAUD_BASE_LO * 16;
+		spin_unlock_irq(&up->port.lock);
+	}
+}
+#endif /* CONFIG_SERIAL_8250_RSA */
+
+/*
+ * This is a quickie test to see how big the FIFO is.
+ * It doesn't work at all the time, more's the pity.
+ */
+static int size_fifo(struct uart_8250_port *up)
+{
+	unsigned char old_fcr, old_mcr, old_lcr;
+	unsigned short old_dl;
+	int count;
+
+	old_lcr = serial_in(up, UART_LCR);
+	serial_out(up, UART_LCR, 0);
+	old_fcr = serial_in(up, UART_FCR);
+	old_mcr = serial_in(up, UART_MCR);
+	serial_out(up, UART_FCR, UART_FCR_ENABLE_FIFO |
+		    UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT);
+	serial_out(up, UART_MCR, UART_MCR_LOOP);
+	serial_out(up, UART_LCR, UART_LCR_CONF_MODE_A);
+	old_dl = serial_dl_read(up);
+	serial_dl_write(up, 0x0001);
+	serial_out(up, UART_LCR, 0x03);
+	for (count = 0; count < 256; count++)
+		serial_out(up, UART_TX, count);
+	mdelay(20);/* FIXME - schedule_timeout */
+	for (count = 0; (serial_in(up, UART_LSR) & UART_LSR_DR) &&
+	     (count < 256); count++)
+		serial_in(up, UART_RX);
+	serial_out(up, UART_FCR, old_fcr);
+	serial_out(up, UART_MCR, old_mcr);
+	serial_out(up, UART_LCR, UART_LCR_CONF_MODE_A);
+	serial_dl_write(up, old_dl);
+	serial_out(up, UART_LCR, old_lcr);
+
+	return count;
+}
+
+/*
+ * Read UART ID using the divisor method - set DLL and DLM to zero
+ * and the revision will be in DLL and device type in DLM.  We
+ * preserve the device state across this.
+ */
+static unsigned int autoconfig_read_divisor_id(struct uart_8250_port *p)
+{
+	unsigned char old_dll, old_dlm, old_lcr;
+	unsigned int id;
+
+	old_lcr = serial_in(p, UART_LCR);
+	serial_out(p, UART_LCR, UART_LCR_CONF_MODE_A);
+
+	old_dll = serial_in(p, UART_DLL);
+	old_dlm = serial_in(p, UART_DLM);
+
+	serial_out(p, UART_DLL, 0);
+	serial_out(p, UART_DLM, 0);
+
+	id = serial_in(p, UART_DLL) | serial_in(p, UART_DLM) << 8;
+
+	serial_out(p, UART_DLL, old_dll);
+	serial_out(p, UART_DLM, old_dlm);
+	serial_out(p, UART_LCR, old_lcr);
+
+	return id;
+}
+
+/*
+ * This is a helper routine to autodetect StarTech/Exar/Oxsemi UART's.
+ * When this function is called we know it is at least a StarTech
+ * 16650 V2, but it might be one of several StarTech UARTs, or one of
+ * its clones.  (We treat the broken original StarTech 16650 V1 as a
+ * 16550, and why not?  Startech doesn't seem to even acknowledge its
+ * existence.)
+ *
+ * What evil have men's minds wrought...
+ */
+static void autoconfig_has_efr(struct uart_8250_port *up)
+{
+	unsigned int id1, id2, id3, rev;
+
+	/*
+	 * Everything with an EFR has SLEEP
+	 */
+	up->capabilities |= UART_CAP_EFR | UART_CAP_SLEEP;
+
+	/*
+	 * First we check to see if it's an Oxford Semiconductor UART.
+	 *
+	 * If we have to do this here because some non-National
+	 * Semiconductor clone chips lock up if you try writing to the
+	 * LSR register (which serial_icr_read does)
+	 */
+
+	/*
+	 * Check for Oxford Semiconductor 16C950.
+	 *
+	 * EFR [4] must be set else this test fails.
+	 *
+	 * This shouldn't be necessary, but Mike Hudson (Exoray@isys.ca)
+	 * claims that it's needed for 952 dual UART's (which are not
+	 * recommended for new designs).
+	 */
+	up->acr = 0;
+	serial_out(up, UART_LCR, UART_LCR_CONF_MODE_B);
+	serial_out(up, UART_EFR, UART_EFR_ECB);
+	serial_out(up, UART_LCR, 0x00);
+	id1 = serial_icr_read(up, UART_ID1);
+	id2 = serial_icr_read(up, UART_ID2);
+	id3 = serial_icr_read(up, UART_ID3);
+	rev = serial_icr_read(up, UART_REV);
+
+	DEBUG_AUTOCONF("950id=%02x:%02x:%02x:%02x ", id1, id2, id3, rev);
+
+	if (id1 == 0x16 && id2 == 0xC9 &&
+	    (id3 == 0x50 || id3 == 0x52 || id3 == 0x54)) {
+		up->port.type = PORT_16C950;
+
+		/*
+		 * Enable work around for the Oxford Semiconductor 952 rev B
+		 * chip which causes it to seriously miscalculate baud rates
+		 * when DLL is 0.
+		 */
+		if (id3 == 0x52 && rev == 0x01)
+			up->bugs |= UART_BUG_QUOT;
+		return;
+	}
+
+	/*
+	 * We check for a XR16C850 by setting DLL and DLM to 0, and then
+	 * reading back DLL and DLM.  The chip type depends on the DLM
+	 * value read back:
+	 *  0x10 - XR16C850 and the DLL contains the chip revision.
+	 *  0x12 - XR16C2850.
+	 *  0x14 - XR16C854.
+	 */
+	id1 = autoconfig_read_divisor_id(up);
+	DEBUG_AUTOCONF("850id=%04x ", id1);
+
+	id2 = id1 >> 8;
+	if (id2 == 0x10 || id2 == 0x12 || id2 == 0x14) {
+		up->port.type = PORT_16850;
+		return;
+	}
+
+	/*
+	 * It wasn't an XR16C850.
+	 *
+	 * We distinguish between the '654 and the '650 by counting
+	 * how many bytes are in the FIFO.  I'm using this for now,
+	 * since that's the technique that was sent to me in the
+	 * serial driver update, but I'm not convinced this works.
+	 * I've had problems doing this in the past.  -TYT
+	 */
+	if (size_fifo(up) == 64)
+		up->port.type = PORT_16654;
+	else
+		up->port.type = PORT_16650V2;
+}
+
+/*
+ * We detected a chip without a FIFO.  Only two fall into
+ * this category - the original 8250 and the 16450.  The
+ * 16450 has a scratch register (accessible with LCR=0)
+ */
+static void autoconfig_8250(struct uart_8250_port *up)
+{
+	unsigned char scratch, status1, status2;
+
+	up->port.type = PORT_8250;
+
+	scratch = serial_in(up, UART_SCR);
+	serial_out(up, UART_SCR, 0xa5);
+	status1 = serial_in(up, UART_SCR);
+	serial_out(up, UART_SCR, 0x5a);
+	status2 = serial_in(up, UART_SCR);
+	serial_out(up, UART_SCR, scratch);
+
+	if (status1 == 0xa5 && status2 == 0x5a)
+		up->port.type = PORT_16450;
+}
+
+static int broken_efr(struct uart_8250_port *up)
+{
+	/*
+	 * Exar ST16C2550 "A2" devices incorrectly detect as
+	 * having an EFR, and report an ID of 0x0201.  See
+	 * http://linux.derkeiler.com/Mailing-Lists/Kernel/2004-11/4812.html 
+	 */
+	if (autoconfig_read_divisor_id(up) == 0x0201 && size_fifo(up) == 16)
+		return 1;
+
+	return 0;
+}
+
+static inline int ns16550a_goto_highspeed(struct uart_8250_port *up)
+{
+	unsigned char status;
+
+	status = serial_in(up, 0x04); /* EXCR2 */
+#define PRESL(x) ((x) & 0x30)
+	if (PRESL(status) == 0x10) {
+		/* already in high speed mode */
+		return 0;
+	} else {
+		status &= ~0xB0; /* Disable LOCK, mask out PRESL[01] */
+		status |= 0x10;  /* 1.625 divisor for baud_base --> 921600 */
+		serial_out(up, 0x04, status);
+	}
+	return 1;
+}
+
+/*
+ * We know that the chip has FIFOs.  Does it have an EFR?  The
+ * EFR is located in the same register position as the IIR and
+ * we know the top two bits of the IIR are currently set.  The
+ * EFR should contain zero.  Try to read the EFR.
+ */
+static void autoconfig_16550a(struct uart_8250_port *up)
+{
+	unsigned char status1, status2;
+	unsigned int iersave;
+
+	up->port.type = PORT_16550A;
+	up->capabilities |= UART_CAP_FIFO;
+
+	/*
+	 * Check for presence of the EFR when DLAB is set.
+	 * Only ST16C650V1 UARTs pass this test.
+	 */
+	serial_out(up, UART_LCR, UART_LCR_CONF_MODE_A);
+	if (serial_in(up, UART_EFR) == 0) {
+		serial_out(up, UART_EFR, 0xA8);
+		if (serial_in(up, UART_EFR) != 0) {
+			DEBUG_AUTOCONF("EFRv1 ");
+			up->port.type = PORT_16650;
+			up->capabilities |= UART_CAP_EFR | UART_CAP_SLEEP;
+		} else {
+			DEBUG_AUTOCONF("Motorola 8xxx DUART ");
+		}
+		serial_out(up, UART_EFR, 0);
+		return;
+	}
+
+	/*
+	 * Maybe it requires 0xbf to be written to the LCR.
+	 * (other ST16C650V2 UARTs, TI16C752A, etc)
+	 */
+	serial_out(up, UART_LCR, UART_LCR_CONF_MODE_B);
+	if (serial_in(up, UART_EFR) == 0 && !broken_efr(up)) {
+		DEBUG_AUTOCONF("EFRv2 ");
+		autoconfig_has_efr(up);
+		return;
+	}
+
+	/*
+	 * Check for a National Semiconductor SuperIO chip.
+	 * Attempt to switch to bank 2, read the value of the LOOP bit
+	 * from EXCR1. Switch back to bank 0, change it in MCR. Then
+	 * switch back to bank 2, read it from EXCR1 again and check
+	 * it's changed. If so, set baud_base in EXCR2 to 921600. -- dwmw2
+	 */
+	serial_out(up, UART_LCR, 0);
+	status1 = serial_in(up, UART_MCR);
+	serial_out(up, UART_LCR, 0xE0);
+	status2 = serial_in(up, 0x02); /* EXCR1 */
+
+	if (!((status2 ^ status1) & UART_MCR_LOOP)) {
+		serial_out(up, UART_LCR, 0);
+		serial_out(up, UART_MCR, status1 ^ UART_MCR_LOOP);
+		serial_out(up, UART_LCR, 0xE0);
+		status2 = serial_in(up, 0x02); /* EXCR1 */
+		serial_out(up, UART_LCR, 0);
+		serial_out(up, UART_MCR, status1);
+
+		if ((status2 ^ status1) & UART_MCR_LOOP) {
+			unsigned short quot;
+
+			serial_out(up, UART_LCR, 0xE0);
+
+			quot = serial_dl_read(up);
+			quot <<= 3;
+
+			if (ns16550a_goto_highspeed(up))
+				serial_dl_write(up, quot);
+
+			serial_out(up, UART_LCR, 0);
+
+			up->port.uartclk = 921600*16;
+			up->port.type = PORT_NS16550A;
+			up->capabilities |= UART_NATSEMI;
+			return;
+		}
+	}
+
+	/*
+	 * No EFR.  Try to detect a TI16750, which only sets bit 5 of
+	 * the IIR when 64 byte FIFO mode is enabled when DLAB is set.
+	 * Try setting it with and without DLAB set.  Cheap clones
+	 * set bit 5 without DLAB set.
+	 */
+	serial_out(up, UART_LCR, 0);
+	serial_out(up, UART_FCR, UART_FCR_ENABLE_FIFO | UART_FCR7_64BYTE);
+	status1 = serial_in(up, UART_IIR) >> 5;
+	serial_out(up, UART_FCR, UART_FCR_ENABLE_FIFO);
+	serial_out(up, UART_LCR, UART_LCR_CONF_MODE_A);
+	serial_out(up, UART_FCR, UART_FCR_ENABLE_FIFO | UART_FCR7_64BYTE);
+	status2 = serial_in(up, UART_IIR) >> 5;
+	serial_out(up, UART_FCR, UART_FCR_ENABLE_FIFO);
+	serial_out(up, UART_LCR, 0);
+
+	DEBUG_AUTOCONF("iir1=%d iir2=%d ", status1, status2);
+
+	if (status1 == 6 && status2 == 7) {
+		up->port.type = PORT_16750;
+		up->capabilities |= UART_CAP_AFE | UART_CAP_SLEEP;
+		return;
+	}
+
+	/*
+	 * Try writing and reading the UART_IER_UUE bit (b6).
+	 * If it works, this is probably one of the Xscale platform's
+	 * internal UARTs.
+	 * We're going to explicitly set the UUE bit to 0 before
+	 * trying to write and read a 1 just to make sure it's not
+	 * already a 1 and maybe locked there before we even start start.
+	 */
+	iersave = serial_in(up, UART_IER);
+	serial_out(up, UART_IER, iersave & ~UART_IER_UUE);
+	if (!(serial_in(up, UART_IER) & UART_IER_UUE)) {
+		/*
+		 * OK it's in a known zero state, try writing and reading
+		 * without disturbing the current state of the other bits.
+		 */
+		serial_out(up, UART_IER, iersave | UART_IER_UUE);
+		if (serial_in(up, UART_IER) & UART_IER_UUE) {
+			/*
+			 * It's an Xscale.
+			 * We'll leave the UART_IER_UUE bit set to 1 (enabled).
+			 */
+			DEBUG_AUTOCONF("Xscale ");
+			up->port.type = PORT_XSCALE;
+			up->capabilities |= UART_CAP_UUE | UART_CAP_RTOIE;
+			return;
+		}
+	} else {
+		/*
+		 * If we got here we couldn't force the IER_UUE bit to 0.
+		 * Log it and continue.
+		 */
+		DEBUG_AUTOCONF("Couldn't force IER_UUE to 0 ");
+	}
+	serial_out(up, UART_IER, iersave);
+
+	/*
+	 * Exar uarts have EFR in a weird location
+	 */
+	if (up->port.flags & UPF_EXAR_EFR) {
+		up->port.type = PORT_XR17D15X;
+		up->capabilities |= UART_CAP_AFE | UART_CAP_EFR;
+	}
+
+	/*
+	 * We distinguish between 16550A and U6 16550A by counting
+	 * how many bytes are in the FIFO.
+	 */
+	if (up->port.type == PORT_16550A && size_fifo(up) == 64) {
+		up->port.type = PORT_U6_16550A;
+		up->capabilities |= UART_CAP_AFE;
+	}
+}
+
+/*
+ * This routine is called by rs_init() to initialize a specific serial
+ * port.  It determines what type of UART chip this serial port is
+ * using: 8250, 16450, 16550, 16550A.  The important question is
+ * whether or not this UART is a 16550A or not, since this will
+ * determine whether or not we can use its FIFO features or not.
+ */
+static void autoconfig(struct uart_8250_port *up, unsigned int probeflags)
+{
+	unsigned char status1, scratch, scratch2, scratch3;
+	unsigned char save_lcr, save_mcr;
+	struct uart_port *port = &up->port;
+	unsigned long flags;
+
+	if (!port->iobase && !port->mapbase && !port->membase)
+		return;
+
+	DEBUG_AUTOCONF("ttyS%d: autoconf (0x%04lx, 0x%p): ",
+		       serial_index(port), port->iobase, port->membase);
+
+	/*
+	 * We really do need global IRQs disabled here - we're going to
+	 * be frobbing the chips IRQ enable register to see if it exists.
+	 */
+	spin_lock_irqsave(&port->lock, flags);
+
+	up->capabilities = 0;
+	up->bugs = 0;
+
+	if (!(port->flags & UPF_BUGGY_UART)) {
+		/*
+		 * Do a simple existence test first; if we fail this,
+		 * there's no point trying anything else.
+		 *
+		 * 0x80 is used as a nonsense port to prevent against
+		 * false positives due to ISA bus float.  The
+		 * assumption is that 0x80 is a non-existent port;
+		 * which should be safe since include/asm/io.h also
+		 * makes this assumption.
+		 *
+		 * Note: this is safe as long as MCR bit 4 is clear
+		 * and the device is in "PC" mode.
+		 */
+		scratch = serial_in(up, UART_IER);
+		serial_out(up, UART_IER, 0);
+#ifdef __i386__
+		outb(0xff, 0x080);
+#endif
+		/*
+		 * Mask out IER[7:4] bits for test as some UARTs (e.g. TL
+		 * 16C754B) allow only to modify them if an EFR bit is set.
+		 */
+		scratch2 = serial_in(up, UART_IER) & 0x0f;
+		serial_out(up, UART_IER, 0x0F);
+#ifdef __i386__
+		outb(0, 0x080);
+#endif
+		scratch3 = serial_in(up, UART_IER) & 0x0f;
+		serial_out(up, UART_IER, scratch);
+		if (scratch2 != 0 || scratch3 != 0x0F) {
+			/*
+			 * We failed; there's nothing here
+			 */
+			DEBUG_AUTOCONF("IER test failed (%02x, %02x) ",
+				       scratch2, scratch3);
+			goto out;
+		}
+	}
+
+	save_mcr = serial_in(up, UART_MCR);
+	save_lcr = serial_in(up, UART_LCR);
+
+	/*
+	 * Check to see if a UART is really there.  Certain broken
+	 * internal modems based on the Rockwell chipset fail this
+	 * test, because they apparently don't implement the loopback
+	 * test mode.  So this test is skipped on the COM 1 through
+	 * COM 4 ports.  This *should* be safe, since no board
+	 * manufacturer would be stupid enough to design a board
+	 * that conflicts with COM 1-4 --- we hope!
+	 */
+	if (!(port->flags & UPF_SKIP_TEST)) {
+		serial_out(up, UART_MCR, UART_MCR_LOOP | 0x0A);
+		status1 = serial_in(up, UART_MSR) & 0xF0;
+		serial_out(up, UART_MCR, save_mcr);
+		if (status1 != 0x90) {
+			DEBUG_AUTOCONF("LOOP test failed (%02x) ",
+				       status1);
+			goto out;
+		}
+	}
+
+	/*
+	 * We're pretty sure there's a port here.  Lets find out what
+	 * type of port it is.  The IIR top two bits allows us to find
+	 * out if it's 8250 or 16450, 16550, 16550A or later.  This
+	 * determines what we test for next.
+	 *
+	 * We also initialise the EFR (if any) to zero for later.  The
+	 * EFR occupies the same register location as the FCR and IIR.
+	 */
+	serial_out(up, UART_LCR, UART_LCR_CONF_MODE_B);
+	serial_out(up, UART_EFR, 0);
+	serial_out(up, UART_LCR, 0);
+
+	serial_out(up, UART_FCR, UART_FCR_ENABLE_FIFO);
+	scratch = serial_in(up, UART_IIR) >> 6;
+
+	DEBUG_AUTOCONF("iir=%d ", scratch);
+
+	switch (scratch) {
+	case 0:
+		autoconfig_8250(up);
+		break;
+	case 1:
+		port->type = PORT_UNKNOWN;
+		break;
+	case 2:
+		port->type = PORT_16550;
+		break;
+	case 3:
+		autoconfig_16550a(up);
+		break;
+	}
+
+#ifdef CONFIG_SERIAL_8250_RSA
+	/*
+	 * Only probe for RSA ports if we got the region.
+	 */
+	if (port->type == PORT_16550A && probeflags & PROBE_RSA) {
+		int i;
+
+		for (i = 0 ; i < probe_rsa_count; ++i) {
+			if (probe_rsa[i] == port->iobase && __enable_rsa(up)) {
+				port->type = PORT_RSA;
+				break;
+			}
+		}
+	}
+#endif
+
+	serial_out(up, UART_LCR, save_lcr);
+
+	if (up->capabilities != uart_config[port->type].flags) {
+		printk(KERN_WARNING
+		       "ttyS%d: detected caps %08x should be %08x\n",
+		       serial_index(port), up->capabilities,
+		       uart_config[port->type].flags);
+	}
+
+	port->fifosize = uart_config[up->port.type].fifo_size;
+	up->capabilities = uart_config[port->type].flags;
+	up->tx_loadsz = uart_config[port->type].tx_loadsz;
+
+	if (port->type == PORT_UNKNOWN)
+		goto out;
+
+	/*
+	 * Reset the UART.
+	 */
+#ifdef CONFIG_SERIAL_8250_RSA
+	if (port->type == PORT_RSA)
+		serial_out(up, UART_RSA_FRR, 0);
+#endif
+	serial_out(up, UART_MCR, save_mcr);
+	serial8250_clear_fifos(up);
+	serial_in(up, UART_RX);
+	if (up->capabilities & UART_CAP_UUE)
+		serial_out(up, UART_IER, UART_IER_UUE);
+	else
+		serial_out(up, UART_IER, 0);
+
+ out:
+	spin_unlock_irqrestore(&port->lock, flags);
+	DEBUG_AUTOCONF("type=%s\n", uart_config[port->type].name);
+}
+
+static void autoconfig_irq(struct uart_8250_port *up)
+{
+	struct uart_port *port = &up->port;
+	unsigned char save_mcr, save_ier;
+	unsigned char save_ICP = 0;
+	unsigned int ICP = 0;
+	unsigned long irqs;
+	int irq;
+
+	if (port->flags & UPF_FOURPORT) {
+		ICP = (port->iobase & 0xfe0) | 0x1f;
+		save_ICP = inb_p(ICP);
+		outb_p(0x80, ICP);
+		inb_p(ICP);
+	}
+
+	/* forget possible initially masked and pending IRQ */
+	probe_irq_off(probe_irq_on());
+	save_mcr = serial_in(up, UART_MCR);
+	save_ier = serial_in(up, UART_IER);
+	serial_out(up, UART_MCR, UART_MCR_OUT1 | UART_MCR_OUT2);
+
+	irqs = probe_irq_on();
+	serial_out(up, UART_MCR, 0);
+	udelay(10);
+	if (port->flags & UPF_FOURPORT) {
+		serial_out(up, UART_MCR,
+			    UART_MCR_DTR | UART_MCR_RTS);
+	} else {
+		serial_out(up, UART_MCR,
+			    UART_MCR_DTR | UART_MCR_RTS | UART_MCR_OUT2);
+	}
+	serial_out(up, UART_IER, 0x0f);	/* enable all intrs */
+	serial_in(up, UART_LSR);
+	serial_in(up, UART_RX);
+	serial_in(up, UART_IIR);
+	serial_in(up, UART_MSR);
+	serial_out(up, UART_TX, 0xFF);
+	udelay(20);
+	irq = probe_irq_off(irqs);
+
+	serial_out(up, UART_MCR, save_mcr);
+	serial_out(up, UART_IER, save_ier);
+
+	if (port->flags & UPF_FOURPORT)
+		outb_p(save_ICP, ICP);
+
+	port->irq = (irq > 0) ? irq : 0;
+}
+
+static inline void __stop_tx(struct uart_8250_port *p)
+{
+	if (p->ier & UART_IER_THRI) {
+		p->ier &= ~UART_IER_THRI;
+		serial_out(p, UART_IER, p->ier);
+	}
+}
+
+static void serial8250_stop_tx(struct uart_port *port)
+{
+	struct uart_8250_port *up =
+		container_of(port, struct uart_8250_port, port);
+
+	__stop_tx(up);
+
+	/*
+	 * We really want to stop the transmitter from sending.
+	 */
+	if (port->type == PORT_16C950) {
+		up->acr |= UART_ACR_TXDIS;
+		serial_icr_write(up, UART_ACR, up->acr);
+	}
+}
+
+static void serial8250_start_tx(struct uart_port *port)
+{
+	struct uart_8250_port *up =
+		container_of(port, struct uart_8250_port, port);
+
+	if (!(up->ier & UART_IER_THRI)) {
+		up->ier |= UART_IER_THRI;
+		serial_port_out(port, UART_IER, up->ier);
+
+		if (up->bugs & UART_BUG_TXEN) {
+			unsigned char lsr;
+			lsr = serial_in(up, UART_LSR);
+			up->lsr_saved_flags |= lsr & LSR_SAVE_FLAGS;
+			if ((port->type == PORT_RM9000) ?
+				(lsr & UART_LSR_THRE) :
+				(lsr & UART_LSR_TEMT))
+				serial8250_tx_chars(up);
+		}
+	}
+
+	/*
+	 * Re-enable the transmitter if we disabled it.
+	 */
+	if (port->type == PORT_16C950 && up->acr & UART_ACR_TXDIS) {
+		up->acr &= ~UART_ACR_TXDIS;
+		serial_icr_write(up, UART_ACR, up->acr);
+	}
+}
+
+static void serial8250_stop_rx(struct uart_port *port)
+{
+	struct uart_8250_port *up =
+		container_of(port, struct uart_8250_port, port);
+
+	up->ier &= ~UART_IER_RLSI;
+	up->port.read_status_mask &= ~UART_LSR_DR;
+	serial_port_out(port, UART_IER, up->ier);
+}
+
+static void serial8250_enable_ms(struct uart_port *port)
+{
+	struct uart_8250_port *up =
+		container_of(port, struct uart_8250_port, port);
+
+	/* no MSR capabilities */
+	if (up->bugs & UART_BUG_NOMSR)
+		return;
+
+	up->ier |= UART_IER_MSI;
+	serial_port_out(port, UART_IER, up->ier);
+}
+
+/*
+ * Clear the Tegra rx fifo after a break
+ *
+ * FIXME: This needs to become a port specific callback once we have a
+ * framework for this
+ */
+static void clear_rx_fifo(struct uart_8250_port *up)
+{
+	unsigned int status, tmout = 10000;
+	do {
+		status = serial_in(up, UART_LSR);
+		if (status & (UART_LSR_FIFOE | UART_LSR_BRK_ERROR_BITS))
+			status = serial_in(up, UART_RX);
+		else
+			break;
+		if (--tmout == 0)
+			break;
+		udelay(1);
+	} while (1);
+}
+
+/*
+ * serial8250_rx_chars: processes according to the passed in LSR
+ * value, and returns the remaining LSR bits not handled
+ * by this Rx routine.
+ */
+unsigned char
+serial8250_rx_chars(struct uart_8250_port *up, unsigned char lsr)
+{
+	struct uart_port *port = &up->port;
+	struct tty_struct *tty = port->state->port.tty;
+	unsigned char ch;
+	int max_count = 256;
+	char flag;
+
+	do {
+		if (likely(lsr & UART_LSR_DR))
+			ch = serial_in(up, UART_RX);
+		else
+			/*
+			 * Intel 82571 has a Serial Over Lan device that will
+			 * set UART_LSR_BI without setting UART_LSR_DR when
+			 * it receives a break. To avoid reading from the
+			 * receive buffer without UART_LSR_DR bit set, we
+			 * just force the read character to be 0
+			 */
+			ch = 0;
+
+		flag = TTY_NORMAL;
+		port->icount.rx++;
+
+		lsr |= up->lsr_saved_flags;
+		up->lsr_saved_flags = 0;
+
+		if (unlikely(lsr & UART_LSR_BRK_ERROR_BITS)) {
+			/*
+			 * For statistics only
+			 */
+			if (lsr & UART_LSR_BI) {
+				lsr &= ~(UART_LSR_FE | UART_LSR_PE);
+				port->icount.brk++;
+				/*
+				 * If tegra port then clear the rx fifo to
+				 * accept another break/character.
+				 */
+				if (port->type == PORT_TEGRA)
+					clear_rx_fifo(up);
+
+				/*
+				 * We do the SysRQ and SAK checking
+				 * here because otherwise the break
+				 * may get masked by ignore_status_mask
+				 * or read_status_mask.
+				 */
+				if (uart_handle_break(port))
+					goto ignore_char;
+			} else if (lsr & UART_LSR_PE)
+				port->icount.parity++;
+			else if (lsr & UART_LSR_FE)
+				port->icount.frame++;
+			if (lsr & UART_LSR_OE)
+				port->icount.overrun++;
+
+			/*
+			 * Mask off conditions which should be ignored.
+			 */
+			lsr &= port->read_status_mask;
+
+			if (lsr & UART_LSR_BI) {
+				DEBUG_INTR("handling break....");
+				flag = TTY_BREAK;
+			} else if (lsr & UART_LSR_PE)
+				flag = TTY_PARITY;
+			else if (lsr & UART_LSR_FE)
+				flag = TTY_FRAME;
+		}
+		if (uart_handle_sysrq_char(port, ch))
+			goto ignore_char;
+
+		uart_insert_char(port, lsr, UART_LSR_OE, ch, flag);
+
+ignore_char:
+		lsr = serial_in(up, UART_LSR);
+	} while ((lsr & (UART_LSR_DR | UART_LSR_BI)) && (max_count-- > 0));
+	spin_unlock(&port->lock);
+	tty_flip_buffer_push(tty);
+	spin_lock(&port->lock);
+	return lsr;
+}
+EXPORT_SYMBOL_GPL(serial8250_rx_chars);
+
+void serial8250_tx_chars(struct uart_8250_port *up)
+{
+	struct uart_port *port = &up->port;
+	struct circ_buf *xmit = &port->state->xmit;
+	int count;
+
+	if (port->x_char) {
+		serial_out(up, UART_TX, port->x_char);
+		port->icount.tx++;
+		port->x_char = 0;
+		return;
+	}
+	if (uart_tx_stopped(port)) {
+		serial8250_stop_tx(port);
+		return;
+	}
+	if (uart_circ_empty(xmit)) {
+		__stop_tx(up);
+		return;
+	}
+
+	count = up->tx_loadsz;
+	do {
+		serial_out(up, UART_TX, xmit->buf[xmit->tail]);
+		xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
+		port->icount.tx++;
+		if (uart_circ_empty(xmit))
+			break;
+		if (up->capabilities & UART_CAP_HFIFO) {
+			if ((serial_port_in(port, UART_LSR) & BOTH_EMPTY) !=
+			    BOTH_EMPTY)
+				break;
+		}
+	} while (--count > 0);
+
+	if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
+		uart_write_wakeup(port);
+
+	DEBUG_INTR("THRE...");
+
+	if (uart_circ_empty(xmit))
+		__stop_tx(up);
+}
+EXPORT_SYMBOL_GPL(serial8250_tx_chars);
+
+unsigned int serial8250_modem_status(struct uart_8250_port *up)
+{
+	struct uart_port *port = &up->port;
+	unsigned int status = serial_in(up, UART_MSR);
+
+	status |= up->msr_saved_flags;
+	up->msr_saved_flags = 0;
+	if (status & UART_MSR_ANY_DELTA && up->ier & UART_IER_MSI &&
+	    port->state != NULL) {
+		if (status & UART_MSR_TERI)
+			port->icount.rng++;
+		if (status & UART_MSR_DDSR)
+			port->icount.dsr++;
+		if (status & UART_MSR_DDCD)
+			uart_handle_dcd_change(port, status & UART_MSR_DCD);
+		if (status & UART_MSR_DCTS)
+			uart_handle_cts_change(port, status & UART_MSR_CTS);
+
+		wake_up_interruptible(&port->state->port.delta_msr_wait);
+	}
+
+	return status;
+}
+EXPORT_SYMBOL_GPL(serial8250_modem_status);
+
+/*
+ * This handles the interrupt from one port.
+ */
+int serial8250_handle_irq(struct uart_port *port, unsigned int iir)
+{
+	unsigned char status;
+	unsigned long flags;
+	struct uart_8250_port *up =
+		container_of(port, struct uart_8250_port, port);
+
+	if (iir & UART_IIR_NO_INT)
+		return 0;
+
+	spin_lock_irqsave(&port->lock, flags);
+
+	status = serial_port_in(port, UART_LSR);
+
+	DEBUG_INTR("status = %x...", status);
+
+	if (status & (UART_LSR_DR | UART_LSR_BI))
+		status = serial8250_rx_chars(up, status);
+	serial8250_modem_status(up);
+	if (status & UART_LSR_THRE)
+		serial8250_tx_chars(up);
+
+	spin_unlock_irqrestore(&port->lock, flags);
+	return 1;
+}
+EXPORT_SYMBOL_GPL(serial8250_handle_irq);
+
+static int serial8250_default_handle_irq(struct uart_port *port)
+{
+	unsigned int iir = serial_port_in(port, UART_IIR);
+
+	return serial8250_handle_irq(port, iir);
+}
+
+/*
+ * This is the serial driver's interrupt routine.
+ *
+ * Arjan thinks the old way was overly complex, so it got simplified.
+ * Alan disagrees, saying that need the complexity to handle the weird
+ * nature of ISA shared interrupts.  (This is a special exception.)
+ *
+ * In order to handle ISA shared interrupts properly, we need to check
+ * that all ports have been serviced, and therefore the ISA interrupt
+ * line has been de-asserted.
+ *
+ * This means we need to loop through all ports. checking that they
+ * don't have an interrupt pending.
+ */
+static irqreturn_t serial8250_interrupt(int irq, void *dev_id)
+{
+	struct irq_info *i = dev_id;
+	struct list_head *l, *end = NULL;
+	int pass_counter = 0, handled = 0;
+
+	DEBUG_INTR("serial8250_interrupt(%d)...", irq);
+
+	spin_lock(&i->lock);
+
+	l = i->head;
+	do {
+		struct uart_8250_port *up;
+		struct uart_port *port;
+
+		up = list_entry(l, struct uart_8250_port, list);
+		port = &up->port;
+
+		if (port->handle_irq(port)) {
+			handled = 1;
+			end = NULL;
+		} else if (end == NULL)
+			end = l;
+
+		l = l->next;
+
+		if (l == i->head && pass_counter++ > PASS_LIMIT) {
+			/* If we hit this, we're dead. */
+			printk_ratelimited(KERN_ERR
+				"serial8250: too much work for irq%d\n", irq);
+			break;
+		}
+	} while (l != end);
+
+	spin_unlock(&i->lock);
+
+	DEBUG_INTR("end.\n");
+
+	return IRQ_RETVAL(handled);
+}
+
+/*
+ * To support ISA shared interrupts, we need to have one interrupt
+ * handler that ensures that the IRQ line has been deasserted
+ * before returning.  Failing to do this will result in the IRQ
+ * line being stuck active, and, since ISA irqs are edge triggered,
+ * no more IRQs will be seen.
+ */
+static void serial_do_unlink(struct irq_info *i, struct uart_8250_port *up)
+{
+	spin_lock_irq(&i->lock);
+
+	if (!list_empty(i->head)) {
+		if (i->head == &up->list)
+			i->head = i->head->next;
+		list_del(&up->list);
+	} else {
+		BUG_ON(i->head != &up->list);
+		i->head = NULL;
+	}
+	spin_unlock_irq(&i->lock);
+	/* List empty so throw away the hash node */
+	if (i->head == NULL) {
+		hlist_del(&i->node);
+		kfree(i);
+	}
+}
+
+static int serial_link_irq_chain(struct uart_8250_port *up)
+{
+	struct hlist_head *h;
+	struct hlist_node *n;
+	struct irq_info *i;
+	int ret, irq_flags = up->port.flags & UPF_SHARE_IRQ ? IRQF_SHARED : 0;
+
+	mutex_lock(&hash_mutex);
+
+	h = &irq_lists[up->port.irq % NR_IRQ_HASH];
+
+	hlist_for_each(n, h) {
+		i = hlist_entry(n, struct irq_info, node);
+		if (i->irq == up->port.irq)
+			break;
+	}
+
+	if (n == NULL) {
+		i = kzalloc(sizeof(struct irq_info), GFP_KERNEL);
+		if (i == NULL) {
+			mutex_unlock(&hash_mutex);
+			return -ENOMEM;
+		}
+		spin_lock_init(&i->lock);
+		i->irq = up->port.irq;
+		hlist_add_head(&i->node, h);
+	}
+	mutex_unlock(&hash_mutex);
+
+	spin_lock_irq(&i->lock);
+
+	if (i->head) {
+		list_add(&up->list, i->head);
+		spin_unlock_irq(&i->lock);
+
+		ret = 0;
+	} else {
+		INIT_LIST_HEAD(&up->list);
+		i->head = &up->list;
+		spin_unlock_irq(&i->lock);
+		irq_flags |= up->port.irqflags;
+		ret = request_irq(up->port.irq, serial8250_interrupt,
+				  irq_flags, "serial", i);
+		if (ret < 0)
+			serial_do_unlink(i, up);
+	}
+
+	return ret;
+}
+
+static void serial_unlink_irq_chain(struct uart_8250_port *up)
+{
+	struct irq_info *i;
+	struct hlist_node *n;
+	struct hlist_head *h;
+
+	mutex_lock(&hash_mutex);
+
+	h = &irq_lists[up->port.irq % NR_IRQ_HASH];
+
+	hlist_for_each(n, h) {
+		i = hlist_entry(n, struct irq_info, node);
+		if (i->irq == up->port.irq)
+			break;
+	}
+
+	BUG_ON(n == NULL);
+	BUG_ON(i->head == NULL);
+
+	if (list_empty(i->head))
+		free_irq(up->port.irq, i);
+
+	serial_do_unlink(i, up);
+	mutex_unlock(&hash_mutex);
+}
+
+/*
+ * This function is used to handle ports that do not have an
+ * interrupt.  This doesn't work very well for 16450's, but gives
+ * barely passable results for a 16550A.  (Although at the expense
+ * of much CPU overhead).
+ */
+static void serial8250_timeout(unsigned long data)
+{
+	struct uart_8250_port *up = (struct uart_8250_port *)data;
+
+	up->port.handle_irq(&up->port);
+	mod_timer(&up->timer, jiffies + uart_poll_timeout(&up->port));
+}
+
+static void serial8250_backup_timeout(unsigned long data)
+{
+	struct uart_8250_port *up = (struct uart_8250_port *)data;
+	unsigned int iir, ier = 0, lsr;
+	unsigned long flags;
+
+	spin_lock_irqsave(&up->port.lock, flags);
+
+	/*
+	 * Must disable interrupts or else we risk racing with the interrupt
+	 * based handler.
+	 */
+	if (up->port.irq) {
+		ier = serial_in(up, UART_IER);
+		serial_out(up, UART_IER, 0);
+	}
+
+	iir = serial_in(up, UART_IIR);
+
+	/*
+	 * This should be a safe test for anyone who doesn't trust the
+	 * IIR bits on their UART, but it's specifically designed for
+	 * the "Diva" UART used on the management processor on many HP
+	 * ia64 and parisc boxes.
+	 */
+	lsr = serial_in(up, UART_LSR);
+	up->lsr_saved_flags |= lsr & LSR_SAVE_FLAGS;
+	if ((iir & UART_IIR_NO_INT) && (up->ier & UART_IER_THRI) &&
+	    (!uart_circ_empty(&up->port.state->xmit) || up->port.x_char) &&
+	    (lsr & UART_LSR_THRE)) {
+		iir &= ~(UART_IIR_ID | UART_IIR_NO_INT);
+		iir |= UART_IIR_THRI;
+	}
+
+	if (!(iir & UART_IIR_NO_INT))
+		serial8250_tx_chars(up);
+
+	if (up->port.irq)
+		serial_out(up, UART_IER, ier);
+
+	spin_unlock_irqrestore(&up->port.lock, flags);
+
+	/* Standard timer interval plus 0.2s to keep the port running */
+	mod_timer(&up->timer,
+		jiffies + uart_poll_timeout(&up->port) + HZ / 5);
+}
+
+static unsigned int serial8250_tx_empty(struct uart_port *port)
+{
+	struct uart_8250_port *up =
+		container_of(port, struct uart_8250_port, port);
+	unsigned long flags;
+	unsigned int lsr;
+
+	spin_lock_irqsave(&port->lock, flags);
+	lsr = serial_port_in(port, UART_LSR);
+	up->lsr_saved_flags |= lsr & LSR_SAVE_FLAGS;
+	spin_unlock_irqrestore(&port->lock, flags);
+
+	return (lsr & BOTH_EMPTY) == BOTH_EMPTY ? TIOCSER_TEMT : 0;
+}
+
+static unsigned int serial8250_get_mctrl(struct uart_port *port)
+{
+	struct uart_8250_port *up =
+		container_of(port, struct uart_8250_port, port);
+	unsigned int status;
+	unsigned int ret;
+
+	status = serial8250_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 uart_8250_port *up =
+		container_of(port, struct uart_8250_port, port);
+	unsigned char mcr = 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;
+
+	serial_port_out(port, UART_MCR, mcr);
+}
+
+static void serial8250_break_ctl(struct uart_port *port, int break_state)
+{
+	struct uart_8250_port *up =
+		container_of(port, struct uart_8250_port, port);
+	unsigned long flags;
+
+	spin_lock_irqsave(&port->lock, flags);
+	if (break_state == -1)
+		up->lcr |= UART_LCR_SBC;
+	else
+		up->lcr &= ~UART_LCR_SBC;
+	serial_port_out(port, UART_LCR, up->lcr);
+	spin_unlock_irqrestore(&port->lock, flags);
+}
+
+/*
+ *	Wait for transmitter & holding register to empty
+ */
+static void wait_for_xmitr(struct uart_8250_port *up, int bits)
+{
+	unsigned int status, tmout = 10000;
+
+	/* Wait up to 10ms for the character(s) to be sent. */
+	for (;;) {
+		status = serial_in(up, UART_LSR);
+
+		up->lsr_saved_flags |= status & LSR_SAVE_FLAGS;
+
+		if ((status & bits) == bits)
+			break;
+		if (--tmout == 0)
+			break;
+		udelay(1);
+	}
+
+	/* Wait up to 1s for flow control if necessary */
+	if (up->port.flags & UPF_CONS_FLOW) {
+		unsigned int tmout;
+		for (tmout = 1000000; tmout; tmout--) {
+			unsigned int msr = serial_in(up, UART_MSR);
+			up->msr_saved_flags |= msr & MSR_SAVE_FLAGS;
+			if (msr & UART_MSR_CTS)
+				break;
+			udelay(1);
+			touch_nmi_watchdog();
+		}
+	}
+}
+
+#ifdef CONFIG_CONSOLE_POLL
+/*
+ * Console polling routines for writing and reading from the uart while
+ * in an interrupt or debug context.
+ */
+
+static int serial8250_get_poll_char(struct uart_port *port)
+{
+	unsigned char lsr = serial_port_in(port, UART_LSR);
+
+	if (!(lsr & UART_LSR_DR))
+		return NO_POLL_CHAR;
+
+	return serial_port_in(port, UART_RX);
+}
+
+
+static void serial8250_put_poll_char(struct uart_port *port,
+			 unsigned char c)
+{
+	unsigned int ier;
+	struct uart_8250_port *up =
+		container_of(port, struct uart_8250_port, port);
+
+	/*
+	 *	First save the IER then disable the interrupts
+	 */
+	ier = serial_port_in(port, UART_IER);
+	if (up->capabilities & UART_CAP_UUE)
+		serial_port_out(port, UART_IER, UART_IER_UUE);
+	else
+		serial_port_out(port, UART_IER, 0);
+
+	wait_for_xmitr(up, BOTH_EMPTY);
+	/*
+	 *	Send the character out.
+	 *	If a LF, also do CR...
+	 */
+	serial_port_out(port, UART_TX, c);
+	if (c == 10) {
+		wait_for_xmitr(up, BOTH_EMPTY);
+		serial_port_out(port, UART_TX, 13);
+	}
+
+	/*
+	 *	Finally, wait for transmitter to become empty
+	 *	and restore the IER
+	 */
+	wait_for_xmitr(up, BOTH_EMPTY);
+	serial_port_out(port, UART_IER, ier);
+}
+
+#endif /* CONFIG_CONSOLE_POLL */
+
+static int serial8250_startup(struct uart_port *port)
+{
+	struct uart_8250_port *up =
+		container_of(port, struct uart_8250_port, port);
+	unsigned long flags;
+	unsigned char lsr, iir;
+	int retval;
+
+	port->fifosize = uart_config[up->port.type].fifo_size;
+	up->tx_loadsz = uart_config[up->port.type].tx_loadsz;
+	up->capabilities = uart_config[up->port.type].flags;
+	up->mcr = 0;
+
+	if (port->iotype != up->cur_iotype)
+		set_io_from_upio(port);
+
+	if (port->type == PORT_16C950) {
+		/* Wake up and initialize UART */
+		up->acr = 0;
+		serial_port_out(port, UART_LCR, UART_LCR_CONF_MODE_B);
+		serial_port_out(port, UART_EFR, UART_EFR_ECB);
+		serial_port_out(port, UART_IER, 0);
+		serial_port_out(port, UART_LCR, 0);
+		serial_icr_write(up, UART_CSR, 0); /* Reset the UART */
+		serial_port_out(port, UART_LCR, UART_LCR_CONF_MODE_B);
+		serial_port_out(port, UART_EFR, UART_EFR_ECB);
+		serial_port_out(port, UART_LCR, 0);
+	}
+
+#ifdef CONFIG_SERIAL_8250_RSA
+	/*
+	 * If this is an RSA port, see if we can kick it up to the
+	 * higher speed clock.
+	 */
+	enable_rsa(up);
+#endif
+
+	/*
+	 * Clear the FIFO buffers and disable them.
+	 * (they will be reenabled in set_termios())
+	 */
+	serial8250_clear_fifos(up);
+
+	/*
+	 * Clear the interrupt registers.
+	 */
+	serial_port_in(port, UART_LSR);
+	serial_port_in(port, UART_RX);
+	serial_port_in(port, UART_IIR);
+	serial_port_in(port, UART_MSR);
+
+	/*
+	 * At this point, there's no way the LSR could still be 0xff;
+	 * if it is, then bail out, because there's likely no UART
+	 * here.
+	 */
+	if (!(port->flags & UPF_BUGGY_UART) &&
+	    (serial_port_in(port, UART_LSR) == 0xff)) {
+		printk_ratelimited(KERN_INFO "ttyS%d: LSR safety check engaged!\n",
+				   serial_index(port));
+		return -ENODEV;
+	}
+
+	/*
+	 * For a XR16C850, we need to set the trigger levels
+	 */
+	if (port->type == PORT_16850) {
+		unsigned char fctr;
+
+		serial_out(up, UART_LCR, UART_LCR_CONF_MODE_B);
+
+		fctr = serial_in(up, UART_FCTR) & ~(UART_FCTR_RX|UART_FCTR_TX);
+		serial_port_out(port, UART_FCTR,
+				fctr | UART_FCTR_TRGD | UART_FCTR_RX);
+		serial_port_out(port, UART_TRG, UART_TRG_96);
+		serial_port_out(port, UART_FCTR,
+				fctr | UART_FCTR_TRGD | UART_FCTR_TX);
+		serial_port_out(port, UART_TRG, UART_TRG_96);
+
+		serial_port_out(port, UART_LCR, 0);
+	}
+
+	if (port->irq) {
+		unsigned char iir1;
+		/*
+		 * Test for UARTs that do not reassert THRE when the
+		 * transmitter is idle and the interrupt has already
+		 * been cleared.  Real 16550s should always reassert
+		 * this interrupt whenever the transmitter is idle and
+		 * the interrupt is enabled.  Delays are necessary to
+		 * allow register changes to become visible.
+		 */
+		spin_lock_irqsave(&port->lock, flags);
+		if (up->port.irqflags & IRQF_SHARED)
+			disable_irq_nosync(port->irq);
+
+		wait_for_xmitr(up, UART_LSR_THRE);
+		serial_port_out_sync(port, UART_IER, UART_IER_THRI);
+		udelay(1); /* allow THRE to set */
+		iir1 = serial_port_in(port, UART_IIR);
+		serial_port_out(port, UART_IER, 0);
+		serial_port_out_sync(port, UART_IER, UART_IER_THRI);
+		udelay(1); /* allow a working UART time to re-assert THRE */
+		iir = serial_port_in(port, UART_IIR);
+		serial_port_out(port, UART_IER, 0);
+
+		if (port->irqflags & IRQF_SHARED)
+			enable_irq(port->irq);
+		spin_unlock_irqrestore(&port->lock, flags);
+
+		/*
+		 * If the interrupt is not reasserted, or we otherwise
+		 * don't trust the iir, setup a timer to kick the UART
+		 * on a regular basis.
+		 */
+		if ((!(iir1 & UART_IIR_NO_INT) && (iir & UART_IIR_NO_INT)) ||
+		    up->port.flags & UPF_BUG_THRE) {
+			up->bugs |= UART_BUG_THRE;
+			pr_debug("ttyS%d - using backup timer\n",
+				 serial_index(port));
+		}
+	}
+
+	/*
+	 * The above check will only give an accurate result the first time
+	 * the port is opened so this value needs to be preserved.
+	 */
+	if (up->bugs & UART_BUG_THRE) {
+		up->timer.function = serial8250_backup_timeout;
+		up->timer.data = (unsigned long)up;
+		mod_timer(&up->timer, jiffies +
+			uart_poll_timeout(port) + HZ / 5);
+	}
+
+	/*
+	 * If the "interrupt" for this port doesn't correspond with any
+	 * hardware interrupt, we use a timer-based system.  The original
+	 * driver used to do this with IRQ0.
+	 */
+	if (!port->irq) {
+		up->timer.data = (unsigned long)up;
+		mod_timer(&up->timer, jiffies + uart_poll_timeout(port));
+	} else {
+		retval = serial_link_irq_chain(up);
+		if (retval)
+			return retval;
+	}
+
+	/*
+	 * Now, initialize the UART
+	 */
+	serial_port_out(port, UART_LCR, UART_LCR_WLEN8);
+
+	spin_lock_irqsave(&port->lock, flags);
+	if (up->port.flags & UPF_FOURPORT) {
+		if (!up->port.irq)
+			up->port.mctrl |= TIOCM_OUT1;
+	} else
+		/*
+		 * Most PC uarts need OUT2 raised to enable interrupts.
+		 */
+		if (port->irq)
+			up->port.mctrl |= TIOCM_OUT2;
+
+	serial8250_set_mctrl(port, port->mctrl);
+
+	/* Serial over Lan (SoL) hack:
+	   Intel 8257x Gigabit ethernet chips have a
+	   16550 emulation, to be used for Serial Over Lan.
+	   Those chips take a longer time than a normal
+	   serial device to signalize that a transmission
+	   data was queued. Due to that, the above test generally
+	   fails. One solution would be to delay the reading of
+	   iir. However, this is not reliable, since the timeout
+	   is variable. So, let's just don't test if we receive
+	   TX irq. This way, we'll never enable UART_BUG_TXEN.
+	 */
+	if (skip_txen_test || up->port.flags & UPF_NO_TXEN_TEST)
+		goto dont_test_tx_en;
+
+	/*
+	 * Do a quick test to see if we receive an
+	 * interrupt when we enable the TX irq.
+	 */
+	serial_port_out(port, UART_IER, UART_IER_THRI);
+	lsr = serial_port_in(port, UART_LSR);
+	iir = serial_port_in(port, UART_IIR);
+	serial_port_out(port, UART_IER, 0);
+
+	if (lsr & UART_LSR_TEMT && iir & UART_IIR_NO_INT) {
+		if (!(up->bugs & UART_BUG_TXEN)) {
+			up->bugs |= UART_BUG_TXEN;
+			pr_debug("ttyS%d - enabling bad tx status workarounds\n",
+				 serial_index(port));
+		}
+	} else {
+		up->bugs &= ~UART_BUG_TXEN;
+	}
+
+dont_test_tx_en:
+	spin_unlock_irqrestore(&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_port_in(port, UART_LSR);
+	serial_port_in(port, UART_RX);
+	serial_port_in(port, UART_IIR);
+	serial_port_in(port, UART_MSR);
+	up->lsr_saved_flags = 0;
+	up->msr_saved_flags = 0;
+
+	/*
+	 * Finally, enable interrupts.  Note: Modem status interrupts
+	 * are set via set_termios(), which will be occurring imminently
+	 * anyway, so we don't enable them here.
+	 */
+	up->ier = UART_IER_RLSI | UART_IER_RDI;
+	serial_port_out(port, UART_IER, up->ier);
+
+	if (port->flags & UPF_FOURPORT) {
+		unsigned int icp;
+		/*
+		 * Enable interrupts on the AST Fourport board
+		 */
+		icp = (port->iobase & 0xfe0) | 0x01f;
+		outb_p(0x80, icp);
+		inb_p(icp);
+	}
+
+	return 0;
+}
+
+static void serial8250_shutdown(struct uart_port *port)
+{
+	struct uart_8250_port *up =
+		container_of(port, struct uart_8250_port, port);
+	unsigned long flags;
+
+	/*
+	 * Disable interrupts from this port
+	 */
+	up->ier = 0;
+	serial_port_out(port, UART_IER, 0);
+
+	spin_lock_irqsave(&port->lock, flags);
+	if (port->flags & UPF_FOURPORT) {
+		/* reset interrupts on the AST Fourport board */
+		inb((port->iobase & 0xfe0) | 0x1f);
+		port->mctrl |= TIOCM_OUT1;
+	} else
+		port->mctrl &= ~TIOCM_OUT2;
+
+	serial8250_set_mctrl(port, port->mctrl);
+	spin_unlock_irqrestore(&port->lock, flags);
+
+	/*
+	 * Disable break condition and FIFOs
+	 */
+	serial_port_out(port, UART_LCR,
+			serial_port_in(port, UART_LCR) & ~UART_LCR_SBC);
+	serial8250_clear_fifos(up);
+
+#ifdef CONFIG_SERIAL_8250_RSA
+	/*
+	 * Reset the RSA board back to 115kbps compat mode.
+	 */
+	disable_rsa(up);
+#endif
+
+	/*
+	 * Read data port to reset things, and then unlink from
+	 * the IRQ chain.
+	 */
+	serial_port_in(port, UART_RX);
+
+	del_timer_sync(&up->timer);
+	up->timer.function = serial8250_timeout;
+	if (port->irq)
+		serial_unlink_irq_chain(up);
+}
+
+static unsigned int serial8250_get_divisor(struct uart_port *port, unsigned int baud)
+{
+	unsigned int quot;
+
+	/*
+	 * Handle magic divisors for baud rates above baud_base on
+	 * SMSC SuperIO chips.
+	 */
+	if ((port->flags & UPF_MAGIC_MULTIPLIER) &&
+	    baud == (port->uartclk/4))
+		quot = 0x8001;
+	else if ((port->flags & UPF_MAGIC_MULTIPLIER) &&
+		 baud == (port->uartclk/8))
+		quot = 0x8002;
+	else
+		quot = uart_get_divisor(port, baud);
+
+	return quot;
+}
+
+void
+serial8250_do_set_termios(struct uart_port *port, struct ktermios *termios,
+		          struct ktermios *old)
+{
+	struct uart_8250_port *up =
+		container_of(port, struct uart_8250_port, port);
+	unsigned char cval, fcr = 0;
+	unsigned long flags;
+	unsigned int baud, quot;
+
+	switch (termios->c_cflag & CSIZE) {
+	case CS5:
+		cval = UART_LCR_WLEN5;
+		break;
+	case CS6:
+		cval = UART_LCR_WLEN6;
+		break;
+	case CS7:
+		cval = UART_LCR_WLEN7;
+		break;
+	default:
+	case CS8:
+		cval = UART_LCR_WLEN8;
+		break;
+	}
+
+	if (termios->c_cflag & CSTOPB)
+		cval |= UART_LCR_STOP;
+	if (termios->c_cflag & PARENB)
+		cval |= UART_LCR_PARITY;
+	if (!(termios->c_cflag & PARODD))
+		cval |= UART_LCR_EPAR;
+#ifdef CMSPAR
+	if (termios->c_cflag & CMSPAR)
+		cval |= UART_LCR_SPAR;
+#endif
+
+	/*
+	 * Ask the core to calculate the divisor for us.
+	 */
+	baud = uart_get_baud_rate(port, termios, old,
+				  port->uartclk / 16 / 0xffff,
+				  port->uartclk / 16);
+	quot = serial8250_get_divisor(port, baud);
+
+	/*
+	 * Oxford Semi 952 rev B workaround
+	 */
+	if (up->bugs & UART_BUG_QUOT && (quot & 0xff) == 0)
+		quot++;
+
+	if (up->capabilities & UART_CAP_FIFO && port->fifosize > 1) {
+		fcr = uart_config[port->type].fcr;
+		if (baud < 2400) {
+			fcr &= ~UART_FCR_TRIGGER_MASK;
+			fcr |= UART_FCR_TRIGGER_1;
+		}
+	}
+
+	/*
+	 * MCR-based auto flow control.  When AFE is enabled, RTS will be
+	 * deasserted when the receive FIFO contains more characters than
+	 * the trigger, or the MCR RTS bit is cleared.  In the case where
+	 * the remote UART is not using CTS auto flow control, we must
+	 * have sufficient FIFO entries for the latency of the remote
+	 * UART to respond.  IOW, at least 32 bytes of FIFO.
+	 */
+	if (up->capabilities & UART_CAP_AFE && port->fifosize >= 32) {
+		up->mcr &= ~UART_MCR_AFE;
+		if (termios->c_cflag & CRTSCTS)
+			up->mcr |= UART_MCR_AFE;
+	}
+
+	/*
+	 * Ok, we're now changing the port state.  Do it with
+	 * interrupts disabled.
+	 */
+	spin_lock_irqsave(&port->lock, flags);
+
+	/*
+	 * Update the per-port timeout.
+	 */
+	uart_update_timeout(port, termios->c_cflag, baud);
+
+	port->read_status_mask = UART_LSR_OE | UART_LSR_THRE | UART_LSR_DR;
+	if (termios->c_iflag & INPCK)
+		port->read_status_mask |= UART_LSR_FE | UART_LSR_PE;
+	if (termios->c_iflag & (BRKINT | PARMRK))
+		port->read_status_mask |= UART_LSR_BI;
+
+	/*
+	 * Characteres to ignore
+	 */
+	port->ignore_status_mask = 0;
+	if (termios->c_iflag & IGNPAR)
+		port->ignore_status_mask |= UART_LSR_PE | UART_LSR_FE;
+	if (termios->c_iflag & IGNBRK) {
+		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)
+			port->ignore_status_mask |= UART_LSR_OE;
+	}
+
+	/*
+	 * ignore all characters if CREAD is not set
+	 */
+	if ((termios->c_cflag & CREAD) == 0)
+		port->ignore_status_mask |= UART_LSR_DR;
+
+	/*
+	 * CTS flow control flag and modem status interrupts
+	 */
+	up->ier &= ~UART_IER_MSI;
+	if (!(up->bugs & UART_BUG_NOMSR) &&
+			UART_ENABLE_MS(&up->port, termios->c_cflag))
+		up->ier |= UART_IER_MSI;
+	if (up->capabilities & UART_CAP_UUE)
+		up->ier |= UART_IER_UUE;
+	if (up->capabilities & UART_CAP_RTOIE)
+		up->ier |= UART_IER_RTOIE;
+
+	serial_port_out(port, UART_IER, up->ier);
+
+	if (up->capabilities & UART_CAP_EFR) {
+		unsigned char efr = 0;
+		/*
+		 * TI16C752/Startech hardware flow control.  FIXME:
+		 * - TI16C752 requires control thresholds to be set.
+		 * - UART_MCR_RTS is ineffective if auto-RTS mode is enabled.
+		 */
+		if (termios->c_cflag & CRTSCTS)
+			efr |= UART_EFR_CTS;
+
+		serial_port_out(port, UART_LCR, UART_LCR_CONF_MODE_B);
+		if (port->flags & UPF_EXAR_EFR)
+			serial_port_out(port, UART_XR_EFR, efr);
+		else
+			serial_port_out(port, UART_EFR, efr);
+	}
+
+#ifdef CONFIG_ARCH_OMAP
+	/* Workaround to enable 115200 baud on OMAP1510 internal ports */
+	if (cpu_is_omap1510() && is_omap_port(up)) {
+		if (baud == 115200) {
+			quot = 1;
+			serial_port_out(port, UART_OMAP_OSC_12M_SEL, 1);
+		} else
+			serial_port_out(port, UART_OMAP_OSC_12M_SEL, 0);
+	}
+#endif
+
+	/*
+	 * For NatSemi, switch to bank 2 not bank 1, to avoid resetting EXCR2,
+	 * otherwise just set DLAB
+	 */
+	if (up->capabilities & UART_NATSEMI)
+		serial_port_out(port, UART_LCR, 0xe0);
+	else
+		serial_port_out(port, UART_LCR, cval | UART_LCR_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.
+	 */
+	if (port->type == PORT_16750)
+		serial_port_out(port, UART_FCR, fcr);
+
+	serial_port_out(port, UART_LCR, cval);		/* reset DLAB */
+	up->lcr = cval;					/* Save LCR */
+	if (port->type != PORT_16750) {
+		/* emulated UARTs (Lucent Venus 167x) need two steps */
+		if (fcr & UART_FCR_ENABLE_FIFO)
+			serial_port_out(port, UART_FCR, UART_FCR_ENABLE_FIFO);
+		serial_port_out(port, UART_FCR, fcr);		/* set fcr */
+	}
+	serial8250_set_mctrl(port, port->mctrl);
+	spin_unlock_irqrestore(&port->lock, flags);
+	/* Don't rewrite B0 */
+	if (tty_termios_baud_rate(termios))
+		tty_termios_encode_baud_rate(termios, baud, baud);
+}
+EXPORT_SYMBOL(serial8250_do_set_termios);
+
+static void
+serial8250_set_termios(struct uart_port *port, struct ktermios *termios,
+		       struct ktermios *old)
+{
+	if (port->set_termios)
+		port->set_termios(port, termios, old);
+	else
+		serial8250_do_set_termios(port, termios, old);
+}
+
+static void
+serial8250_set_ldisc(struct uart_port *port, int new)
+{
+	if (new == N_PPS) {
+		port->flags |= UPF_HARDPPS_CD;
+		serial8250_enable_ms(port);
+	} else
+		port->flags &= ~UPF_HARDPPS_CD;
+}
+
+
+void serial8250_do_pm(struct uart_port *port, unsigned int state,
+		      unsigned int oldstate)
+{
+	struct uart_8250_port *p =
+		container_of(port, struct uart_8250_port, port);
+
+	serial8250_set_sleep(p, state != 0);
+}
+EXPORT_SYMBOL(serial8250_do_pm);
+
+static void
+serial8250_pm(struct uart_port *port, unsigned int state,
+	      unsigned int oldstate)
+{
+	if (port->pm)
+		port->pm(port, state, oldstate);
+	else
+		serial8250_do_pm(port, state, oldstate);
+}
+
+static unsigned int serial8250_port_size(struct uart_8250_port *pt)
+{
+	if (pt->port.iotype == UPIO_AU)
+		return 0x1000;
+#ifdef CONFIG_ARCH_OMAP
+	if (is_omap_port(pt))
+		return 0x16 << pt->port.regshift;
+#endif
+	return 8 << pt->port.regshift;
+}
+
+/*
+ * Resource handling.
+ */
+static int serial8250_request_std_resource(struct uart_8250_port *up)
+{
+	unsigned int size = serial8250_port_size(up);
+	struct uart_port *port = &up->port;
+	int ret = 0;
+
+	switch (port->iotype) {
+	case UPIO_AU:
+	case UPIO_TSI:
+	case UPIO_MEM32:
+	case UPIO_MEM:
+		if (!port->mapbase)
+			break;
+
+		if (!request_mem_region(port->mapbase, size, "serial")) {
+			ret = -EBUSY;
+			break;
+		}
+
+		if (port->flags & UPF_IOREMAP) {
+			port->membase = ioremap_nocache(port->mapbase, size);
+			if (!port->membase) {
+				release_mem_region(port->mapbase, size);
+				ret = -ENOMEM;
+			}
+		}
+		break;
+
+	case UPIO_HUB6:
+	case UPIO_PORT:
+		if (!request_region(port->iobase, size, "serial"))
+			ret = -EBUSY;
+		break;
+	}
+	return ret;
+}
+
+static void serial8250_release_std_resource(struct uart_8250_port *up)
+{
+	unsigned int size = serial8250_port_size(up);
+	struct uart_port *port = &up->port;
+
+	switch (port->iotype) {
+	case UPIO_AU:
+	case UPIO_TSI:
+	case UPIO_MEM32:
+	case UPIO_MEM:
+		if (!port->mapbase)
+			break;
+
+		if (port->flags & UPF_IOREMAP) {
+			iounmap(port->membase);
+			port->membase = NULL;
+		}
+
+		release_mem_region(port->mapbase, size);
+		break;
+
+	case UPIO_HUB6:
+	case UPIO_PORT:
+		release_region(port->iobase, size);
+		break;
+	}
+}
+
+static int serial8250_request_rsa_resource(struct uart_8250_port *up)
+{
+	unsigned long start = UART_RSA_BASE << up->port.regshift;
+	unsigned int size = 8 << up->port.regshift;
+	struct uart_port *port = &up->port;
+	int ret = -EINVAL;
+
+	switch (port->iotype) {
+	case UPIO_HUB6:
+	case UPIO_PORT:
+		start += port->iobase;
+		if (request_region(start, size, "serial-rsa"))
+			ret = 0;
+		else
+			ret = -EBUSY;
+		break;
+	}
+
+	return ret;
+}
+
+static void serial8250_release_rsa_resource(struct uart_8250_port *up)
+{
+	unsigned long offset = UART_RSA_BASE << up->port.regshift;
+	unsigned int size = 8 << up->port.regshift;
+	struct uart_port *port = &up->port;
+
+	switch (port->iotype) {
+	case UPIO_HUB6:
+	case UPIO_PORT:
+		release_region(port->iobase + offset, size);
+		break;
+	}
+}
+
+static void serial8250_release_port(struct uart_port *port)
+{
+	struct uart_8250_port *up =
+		container_of(port, struct uart_8250_port, port);
+
+	serial8250_release_std_resource(up);
+	if (port->type == PORT_RSA)
+		serial8250_release_rsa_resource(up);
+}
+
+static int serial8250_request_port(struct uart_port *port)
+{
+	struct uart_8250_port *up =
+		container_of(port, struct uart_8250_port, port);
+	int ret = 0;
+
+	ret = serial8250_request_std_resource(up);
+	if (ret == 0 && port->type == PORT_RSA) {
+		ret = serial8250_request_rsa_resource(up);
+		if (ret < 0)
+			serial8250_release_std_resource(up);
+	}
+
+	return ret;
+}
+
+static void serial8250_config_port(struct uart_port *port, int flags)
+{
+	struct uart_8250_port *up =
+		container_of(port, struct uart_8250_port, port);
+	int probeflags = PROBE_ANY;
+	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;
+
+	ret = serial8250_request_rsa_resource(up);
+	if (ret < 0)
+		probeflags &= ~PROBE_RSA;
+
+	if (port->iotype != up->cur_iotype)
+		set_io_from_upio(port);
+
+	if (flags & UART_CONFIG_TYPE)
+		autoconfig(up, probeflags);
+
+	/* if access method is AU, it is a 16550 with a quirk */
+	if (port->type == PORT_16550A && port->iotype == UPIO_AU)
+		up->bugs |= UART_BUG_NOMSR;
+
+	if (port->type != PORT_UNKNOWN && flags & UART_CONFIG_IRQ)
+		autoconfig_irq(up);
+
+	if (port->type != PORT_RSA && probeflags & PROBE_RSA)
+		serial8250_release_rsa_resource(up);
+	if (port->type == PORT_UNKNOWN)
+		serial8250_release_std_resource(up);
+}
+
+static int
+serial8250_verify_port(struct uart_port *port, struct serial_struct *ser)
+{
+	if (ser->irq >= nr_irqs || ser->irq < 0 ||
+	    ser->baud_base < 9600 || ser->type < PORT_UNKNOWN ||
+	    ser->type >= ARRAY_SIZE(uart_config) || ser->type == PORT_CIRRUS ||
+	    ser->type == PORT_STARTECH || uart_config[ser->type].name == NULL)
+		return -EINVAL;
+	return 0;
+}
+
+static const char *
+serial8250_type(struct uart_port *port)
+{
+	int type = port->type;
+
+	if (type >= ARRAY_SIZE(uart_config) || uart_config[type].name == NULL)
+		type = 0;
+	return uart_config[type].name;
+}
+
+static 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,
+	.set_ldisc	= serial8250_set_ldisc,
+	.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,
+#ifdef CONFIG_CONSOLE_POLL
+	.poll_get_char = serial8250_get_poll_char,
+	.poll_put_char = serial8250_put_poll_char,
+#endif
+};
+
+static struct uart_8250_port serial8250_ports[UART_NR];
+
+static void (*serial8250_isa_config)(int port, struct uart_port *up,
+	unsigned short *capabilities);
+
+void serial8250_set_isa_configurator(
+	void (*v)(int port, struct uart_port *up, unsigned short *capabilities))
+{
+	serial8250_isa_config = v;
+}
+EXPORT_SYMBOL(serial8250_set_isa_configurator);
+
+static void __init serial8250_isa_init_ports(void)
+{
+	struct uart_8250_port *up;
+	static int first = 1;
+	int i, irqflag = 0;
+
+	if (!first)
+		return;
+	first = 0;
+
+	for (i = 0; i < nr_uarts; i++) {
+		struct uart_8250_port *up = &serial8250_ports[i];
+		struct uart_port *port = &up->port;
+
+		port->line = i;
+		spin_lock_init(&port->lock);
+
+		init_timer(&up->timer);
+		up->timer.function = serial8250_timeout;
+
+		/*
+		 * ALPHA_KLUDGE_MCR needs to be killed.
+		 */
+		up->mcr_mask = ~ALPHA_KLUDGE_MCR;
+		up->mcr_force = ALPHA_KLUDGE_MCR;
+
+		port->ops = &serial8250_pops;
+	}
+
+	if (share_irqs)
+		irqflag = IRQF_SHARED;
+
+	for (i = 0, up = serial8250_ports;
+	     i < ARRAY_SIZE(old_serial_port) && i < nr_uarts;
+	     i++, up++) {
+		struct uart_port *port = &up->port;
+
+		port->iobase   = old_serial_port[i].port;
+		port->irq      = irq_canonicalize(old_serial_port[i].irq);
+		port->irqflags = old_serial_port[i].irqflags;
+		port->uartclk  = old_serial_port[i].baud_base * 16;
+		port->flags    = old_serial_port[i].flags;
+		port->hub6     = old_serial_port[i].hub6;
+		port->membase  = old_serial_port[i].iomem_base;
+		port->iotype   = old_serial_port[i].io_type;
+		port->regshift = old_serial_port[i].iomem_reg_shift;
+		set_io_from_upio(port);
+		port->irqflags |= irqflag;
+		if (serial8250_isa_config != NULL)
+			serial8250_isa_config(i, &up->port, &up->capabilities);
+
+	}
+}
+
+static void
+serial8250_init_fixed_type_port(struct uart_8250_port *up, unsigned int type)
+{
+	up->port.type = type;
+	up->port.fifosize = uart_config[type].fifo_size;
+	up->capabilities = uart_config[type].flags;
+	up->tx_loadsz = uart_config[type].tx_loadsz;
+}
+
+static void __init
+serial8250_register_ports(struct uart_driver *drv, struct device *dev)
+{
+	int i;
+
+	for (i = 0; i < nr_uarts; i++) {
+		struct uart_8250_port *up = &serial8250_ports[i];
+		up->cur_iotype = 0xFF;
+	}
+
+	serial8250_isa_init_ports();
+
+	for (i = 0; i < nr_uarts; i++) {
+		struct uart_8250_port *up = &serial8250_ports[i];
+
+		up->port.dev = dev;
+
+		if (up->port.flags & UPF_FIXED_TYPE)
+			serial8250_init_fixed_type_port(up, up->port.type);
+
+		uart_add_one_port(drv, &up->port);
+	}
+}
+
+#ifdef CONFIG_SERIAL_8250_CONSOLE
+
+static void serial8250_console_putchar(struct uart_port *port, int ch)
+{
+	struct uart_8250_port *up =
+		container_of(port, struct uart_8250_port, port);
+
+	wait_for_xmitr(up, UART_LSR_THRE);
+	serial_port_out(port, UART_TX, ch);
+}
+
+/*
+ *	Print a string to the serial port trying not to disturb
+ *	any possible real use of the port...
+ *
+ *	The console_lock must be held when we get here.
+ */
+static void
+serial8250_console_write(struct console *co, const char *s, unsigned int count)
+{
+	struct uart_8250_port *up = &serial8250_ports[co->index];
+	struct uart_port *port = &up->port;
+	unsigned long flags;
+	unsigned int ier;
+	int locked = 1;
+
+	touch_nmi_watchdog();
+
+	if (port->sysrq || oops_in_progress || in_kdb_printk())
+		locked = spin_trylock_irqsave(&port->lock, flags);
+	else
+		spin_lock_irqsave(&port->lock, flags);
+
+	/*
+	 *	First save the IER then disable the interrupts
+	 */
+	ier = serial_port_in(port, UART_IER);
+
+	if (up->capabilities & UART_CAP_UUE)
+		serial_port_out(port, UART_IER, UART_IER_UUE);
+	else
+		serial_port_out(port, UART_IER, 0);
+
+	uart_console_write(port, s, count, serial8250_console_putchar);
+
+	/*
+	 *	Finally, wait for transmitter to become empty
+	 *	and restore the IER
+	 */
+	wait_for_xmitr(up, BOTH_EMPTY);
+	serial_port_out(port, UART_IER, ier);
+
+	/*
+	 *	The receive handling will happen properly because the
+	 *	receive ready bit will still be set; it is not cleared
+	 *	on read.  However, modem control will not, we must
+	 *	call it if we have saved something in the saved flags
+	 *	while processing with interrupts off.
+	 */
+	if (up->msr_saved_flags)
+		serial8250_modem_status(up);
+
+	if (locked)
+		spin_unlock_irqrestore(&port->lock, flags);
+}
+
+static int __init serial8250_console_setup(struct console *co, char *options)
+{
+	struct uart_port *port;
+	int baud = 9600;
+	int bits = 8;
+	int parity = 'n';
+	int flow = 'n';
+
+	/*
+	 * 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 >= nr_uarts)
+		co->index = 0;
+	port = &serial8250_ports[co->index].port;
+	if (!port->iobase && !port->membase)
+		return -ENODEV;
+
+	if (options)
+		uart_parse_options(options, &baud, &parity, &bits, &flow);
+
+	return uart_set_options(port, co, baud, parity, bits, flow);
+}
+
+static int serial8250_console_early_setup(void)
+{
+	return serial8250_find_port_for_earlycon();
+}
+
+static struct console serial8250_console = {
+	.name		= "ttyS",
+	.write		= serial8250_console_write,
+	.device		= uart_console_device,
+	.setup		= serial8250_console_setup,
+	.early_setup	= serial8250_console_early_setup,
+	.flags		= CON_PRINTBUFFER | CON_ANYTIME,
+	.index		= -1,
+	.data		= &serial8250_reg,
+};
+
+static int __init serial8250_console_init(void)
+{
+	if (nr_uarts > UART_NR)
+		nr_uarts = UART_NR;
+
+	serial8250_isa_init_ports();
+	register_console(&serial8250_console);
+	return 0;
+}
+console_initcall(serial8250_console_init);
+
+int serial8250_find_port(struct uart_port *p)
+{
+	int line;
+	struct uart_port *port;
+
+	for (line = 0; line < nr_uarts; line++) {
+		port = &serial8250_ports[line].port;
+		if (uart_match_port(p, port))
+			return line;
+	}
+	return -ENODEV;
+}
+
+#define SERIAL8250_CONSOLE	&serial8250_console
+#else
+#define SERIAL8250_CONSOLE	NULL
+#endif
+
+static struct uart_driver serial8250_reg = {
+	.owner			= THIS_MODULE,
+	.driver_name		= "serial",
+	.dev_name		= "ttyS",
+	.major			= TTY_MAJOR,
+	.minor			= 64,
+	.cons			= SERIAL8250_CONSOLE,
+};
+
+/*
+ * early_serial_setup - early registration for 8250 ports
+ *
+ * Setup an 8250 port structure prior to console initialisation.  Use
+ * after console initialisation will cause undefined behaviour.
+ */
+int __init early_serial_setup(struct uart_port *port)
+{
+	struct uart_port *p;
+
+	if (port->line >= ARRAY_SIZE(serial8250_ports))
+		return -ENODEV;
+
+	serial8250_isa_init_ports();
+	p = &serial8250_ports[port->line].port;
+	p->iobase       = port->iobase;
+	p->membase      = port->membase;
+	p->irq          = port->irq;
+	p->irqflags     = port->irqflags;
+	p->uartclk      = port->uartclk;
+	p->fifosize     = port->fifosize;
+	p->regshift     = port->regshift;
+	p->iotype       = port->iotype;
+	p->flags        = port->flags;
+	p->mapbase      = port->mapbase;
+	p->private_data = port->private_data;
+	p->type		= port->type;
+	p->line		= port->line;
+
+	set_io_from_upio(p);
+	if (port->serial_in)
+		p->serial_in = port->serial_in;
+	if (port->serial_out)
+		p->serial_out = port->serial_out;
+	if (port->handle_irq)
+		p->handle_irq = port->handle_irq;
+	else
+		p->handle_irq = serial8250_default_handle_irq;
+
+	return 0;
+}
+
+/**
+ *	serial8250_suspend_port - suspend one serial port
+ *	@line:  serial line number
+ *
+ *	Suspend one serial port.
+ */
+void serial8250_suspend_port(int line)
+{
+	uart_suspend_port(&serial8250_reg, &serial8250_ports[line].port);
+}
+
+/**
+ *	serial8250_resume_port - resume one serial port
+ *	@line:  serial line number
+ *
+ *	Resume one serial port.
+ */
+void serial8250_resume_port(int line)
+{
+	struct uart_8250_port *up = &serial8250_ports[line];
+	struct uart_port *port = &up->port;
+
+	if (up->capabilities & UART_NATSEMI) {
+		/* Ensure it's still in high speed mode */
+		serial_port_out(port, UART_LCR, 0xE0);
+
+		ns16550a_goto_highspeed(up);
+
+		serial_port_out(port, UART_LCR, 0);
+		port->uartclk = 921600*16;
+	}
+	uart_resume_port(&serial8250_reg, port);
+}
+
+/*
+ * Register a set of serial devices attached to a platform device.  The
+ * list is terminated with a zero flags entry, which means we expect
+ * all entries to have at least UPF_BOOT_AUTOCONF set.
+ */
+static int __devinit serial8250_probe(struct platform_device *dev)
+{
+	struct plat_serial8250_port *p = dev->dev.platform_data;
+	struct uart_port port;
+	int ret, i, irqflag = 0;
+
+	memset(&port, 0, sizeof(struct uart_port));
+
+	if (share_irqs)
+		irqflag = IRQF_SHARED;
+
+	for (i = 0; p && p->flags != 0; p++, i++) {
+		port.iobase		= p->iobase;
+		port.membase		= p->membase;
+		port.irq		= p->irq;
+		port.irqflags		= p->irqflags;
+		port.uartclk		= p->uartclk;
+		port.regshift		= p->regshift;
+		port.iotype		= p->iotype;
+		port.flags		= p->flags;
+		port.mapbase		= p->mapbase;
+		port.hub6		= p->hub6;
+		port.private_data	= p->private_data;
+		port.type		= p->type;
+		port.serial_in		= p->serial_in;
+		port.serial_out		= p->serial_out;
+		port.handle_irq		= p->handle_irq;
+		port.set_termios	= p->set_termios;
+		port.pm			= p->pm;
+		port.dev		= &dev->dev;
+		port.irqflags		|= irqflag;
+		ret = serial8250_register_port(&port);
+		if (ret < 0) {
+			dev_err(&dev->dev, "unable to register port at index %d "
+				"(IO%lx MEM%llx IRQ%d): %d\n", i,
+				p->iobase, (unsigned long long)p->mapbase,
+				p->irq, ret);
+		}
+	}
+	return 0;
+}
+
+/*
+ * Remove serial ports registered against a platform device.
+ */
+static int __devexit serial8250_remove(struct platform_device *dev)
+{
+	int i;
+
+	for (i = 0; i < nr_uarts; i++) {
+		struct uart_8250_port *up = &serial8250_ports[i];
+
+		if (up->port.dev == &dev->dev)
+			serial8250_unregister_port(i);
+	}
+	return 0;
+}
+
+static int serial8250_suspend(struct platform_device *dev, pm_message_t state)
+{
+	int i;
+
+	for (i = 0; i < UART_NR; i++) {
+		struct uart_8250_port *up = &serial8250_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_NR; i++) {
+		struct uart_8250_port *up = &serial8250_ports[i];
+
+		if (up->port.type != PORT_UNKNOWN && up->port.dev == &dev->dev)
+			serial8250_resume_port(i);
+	}
+
+	return 0;
+}
+
+static struct platform_driver serial8250_isa_driver = {
+	.probe		= serial8250_probe,
+	.remove		= __devexit_p(serial8250_remove),
+	.suspend	= serial8250_suspend,
+	.resume		= serial8250_resume,
+	.driver		= {
+		.name	= "serial8250",
+		.owner	= THIS_MODULE,
+	},
+};
+
+/*
+ * 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 DEFINE_MUTEX(serial_mutex);
+
+static struct uart_8250_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(&serial8250_ports[i].port, port))
+			return &serial8250_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 (serial8250_ports[i].port.type == PORT_UNKNOWN &&
+		    serial8250_ports[i].port.iobase == 0)
+			return &serial8250_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 (serial8250_ports[i].port.type == PORT_UNKNOWN)
+			return &serial8250_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.
+ */
+int serial8250_register_port(struct uart_port *port)
+{
+	struct uart_8250_port *uart;
+	int ret = -ENOSPC;
+
+	if (port->uartclk == 0)
+		return -EINVAL;
+
+	mutex_lock(&serial_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.irqflags     = port->irqflags;
+		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 = port->private_data;
+		if (port->dev)
+			uart->port.dev = port->dev;
+
+		if (port->flags & UPF_FIXED_TYPE)
+			serial8250_init_fixed_type_port(uart, port->type);
+
+		set_io_from_upio(&uart->port);
+		/* Possibly override default I/O functions.  */
+		if (port->serial_in)
+			uart->port.serial_in = port->serial_in;
+		if (port->serial_out)
+			uart->port.serial_out = port->serial_out;
+		if (port->handle_irq)
+			uart->port.handle_irq = port->handle_irq;
+		/*  Possibly override set_termios call */
+		if (port->set_termios)
+			uart->port.set_termios = port->set_termios;
+		if (port->pm)
+			uart->port.pm = port->pm;
+
+		if (serial8250_isa_config != NULL)
+			serial8250_isa_config(0, &uart->port,
+					&uart->capabilities);
+
+		ret = uart_add_one_port(&serial8250_reg, &uart->port);
+		if (ret == 0)
+			ret = uart->port.line;
+	}
+	mutex_unlock(&serial_mutex);
+
+	return ret;
+}
+EXPORT_SYMBOL(serial8250_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.
+ */
+void serial8250_unregister_port(int line)
+{
+	struct uart_8250_port *uart = &serial8250_ports[line];
+
+	mutex_lock(&serial_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->capabilities = uart_config[uart->port.type].flags;
+		uart_add_one_port(&serial8250_reg, &uart->port);
+	} else {
+		uart->port.dev = NULL;
+	}
+	mutex_unlock(&serial_mutex);
+}
+EXPORT_SYMBOL(serial8250_unregister_port);
+
+static int __init serial8250_init(void)
+{
+	int ret;
+
+	if (nr_uarts > UART_NR)
+		nr_uarts = UART_NR;
+
+	printk(KERN_INFO "Serial: 8250/16550 driver, "
+		"%d ports, IRQ sharing %sabled\n", nr_uarts,
+		share_irqs ? "en" : "dis");
+
+#ifdef CONFIG_SPARC
+	ret = sunserial_register_minors(&serial8250_reg, UART_NR);
+#else
+	serial8250_reg.nr = UART_NR;
+	ret = uart_register_driver(&serial8250_reg);
+#endif
+	if (ret)
+		goto out;
+
+	serial8250_isa_devs = platform_device_alloc("serial8250",
+						    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_isa_driver);
+	if (ret == 0)
+		goto out;
+
+	platform_device_del(serial8250_isa_devs);
+put_dev:
+	platform_device_put(serial8250_isa_devs);
+unreg_uart_drv:
+#ifdef CONFIG_SPARC
+	sunserial_unregister_minors(&serial8250_reg, UART_NR);
+#else
+	uart_unregister_driver(&serial8250_reg);
+#endif
+out:
+	return ret;
+}
+
+static void __exit serial8250_exit(void)
+{
+	struct platform_device *isa_dev = serial8250_isa_devs;
+
+	/*
+	 * This tells serial8250_unregister_port() not to re-register
+	 * the ports (thereby making serial8250_isa_driver permanently
+	 * in use.)
+	 */
+	serial8250_isa_devs = NULL;
+
+	platform_driver_unregister(&serial8250_isa_driver);
+	platform_device_unregister(isa_dev);
+
+#ifdef CONFIG_SPARC
+	sunserial_unregister_minors(&serial8250_reg, UART_NR);
+#else
+	uart_unregister_driver(&serial8250_reg);
+#endif
+}
+
+module_init(serial8250_init);
+module_exit(serial8250_exit);
+
+EXPORT_SYMBOL(serial8250_suspend_port);
+EXPORT_SYMBOL(serial8250_resume_port);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Generic 8250/16x50 serial driver");
+
+module_param(share_irqs, uint, 0644);
+MODULE_PARM_DESC(share_irqs, "Share IRQs with other non-8250/16x50 devices"
+	" (unsafe)");
+
+module_param(nr_uarts, uint, 0644);
+MODULE_PARM_DESC(nr_uarts, "Maximum number of UARTs supported. (1-" __MODULE_STRING(CONFIG_SERIAL_8250_NR_UARTS) ")");
+
+module_param(skip_txen_test, uint, 0644);
+MODULE_PARM_DESC(skip_txen_test, "Skip checking for the TXEN bug at init time");
+
+#ifdef CONFIG_SERIAL_8250_RSA
+module_param_array(probe_rsa, ulong, &probe_rsa_count, 0444);
+MODULE_PARM_DESC(probe_rsa, "Probe I/O ports for RSA");
+#endif
+MODULE_ALIAS_CHARDEV_MAJOR(TTY_MAJOR);
diff --git a/ap/os/linux/linux-3.4.x/drivers/tty/serial/8250/8250.h b/ap/os/linux/linux-3.4.x/drivers/tty/serial/8250/8250.h
new file mode 100644
index 0000000..b37147c
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/tty/serial/8250/8250.h
@@ -0,0 +1,116 @@
+/*
+ *  Driver for 8250/16550-type serial ports
+ *
+ *  Based on drivers/char/serial.c, by Linus Torvalds, Theodore Ts'o.
+ *
+ *  Copyright (C) 2001 Russell King.
+ *
+ * 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.
+ */
+
+#include <linux/serial_8250.h>
+
+struct uart_8250_port {
+	struct uart_port	port;
+	struct timer_list	timer;		/* "no irq" timer */
+	struct list_head	list;		/* ports on this IRQ */
+	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 char		cur_iotype;	/* Running I/O type */
+
+	/*
+	 * 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;
+};
+
+struct old_serial_port {
+	unsigned int uart;
+	unsigned int baud_base;
+	unsigned int port;
+	unsigned int irq;
+	unsigned int flags;
+	unsigned char hub6;
+	unsigned char io_type;
+	unsigned char *iomem_base;
+	unsigned short iomem_reg_shift;
+	unsigned long irqflags;
+};
+
+/*
+ * This replaces serial_uart_config in include/linux/serial.h
+ */
+struct serial8250_config {
+	const char	*name;
+	unsigned short	fifo_size;
+	unsigned short	tx_loadsz;
+	unsigned char	fcr;
+	unsigned int	flags;
+};
+
+#define UART_CAP_FIFO	(1 << 8)	/* UART has FIFO */
+#define UART_CAP_EFR	(1 << 9)	/* UART has EFR */
+#define UART_CAP_SLEEP	(1 << 10)	/* UART has IER sleep */
+#define UART_CAP_AFE	(1 << 11)	/* MCR-based hw flow control */
+#define UART_CAP_UUE	(1 << 12)	/* UART needs IER bit 6 set (Xscale) */
+#define UART_CAP_RTOIE	(1 << 13)	/* UART needs IER bit 4 set (Xscale, Tegra) */
+#define UART_CAP_HFIFO	(1 << 14)	/* UART has a "hidden" FIFO */
+
+#define UART_BUG_QUOT	(1 << 0)	/* UART has buggy quot LSB */
+#define UART_BUG_TXEN	(1 << 1)	/* UART has buggy TX IIR status */
+#define UART_BUG_NOMSR	(1 << 2)	/* UART has buggy MSR status bits (Au1x00) */
+#define UART_BUG_THRE	(1 << 3)	/* UART has buggy THRE reassertion */
+
+#define PROBE_RSA	(1 << 0)
+#define PROBE_ANY	(~0)
+
+#define HIGH_BITS_OFFSET ((sizeof(long)-sizeof(int))*8)
+
+#ifdef CONFIG_SERIAL_8250_SHARE_IRQ
+#define SERIAL8250_SHARE_IRQS 1
+#else
+#define SERIAL8250_SHARE_IRQS 0
+#endif
+
+static inline int serial_in(struct uart_8250_port *up, int offset)
+{
+	return up->port.serial_in(&up->port, offset);
+}
+
+static inline void serial_out(struct uart_8250_port *up, int offset, int value)
+{
+	up->port.serial_out(&up->port, offset, value);
+}
+
+#if defined(__alpha__) && !defined(CONFIG_PCI)
+/*
+ * Digital did something really horribly wrong with the OUT1 and OUT2
+ * lines on at least some ALPHA's.  The failure mode is that if either
+ * is cleared, the machine locks up with endless interrupts.
+ */
+#define ALPHA_KLUDGE_MCR  (UART_MCR_OUT2 | UART_MCR_OUT1)
+#elif defined(CONFIG_SBC8560)
+/*
+ * WindRiver did something similarly broken on their SBC8560 board. The
+ * UART tristates its IRQ output while OUT2 is clear, but they pulled
+ * the interrupt line _up_ instead of down, so if we register the IRQ
+ * while the UART is in that state, we die in an IRQ storm. */
+#define ALPHA_KLUDGE_MCR (UART_MCR_OUT2)
+#else
+#define ALPHA_KLUDGE_MCR 0
+#endif
diff --git a/ap/os/linux/linux-3.4.x/drivers/tty/serial/8250/8250_accent.c b/ap/os/linux/linux-3.4.x/drivers/tty/serial/8250/8250_accent.c
new file mode 100644
index 0000000..34b51c6
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/tty/serial/8250/8250_accent.c
@@ -0,0 +1,45 @@
+/*
+ *  Copyright (C) 2005 Russell King.
+ *  Data taken from include/asm-i386/serial.h
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/serial_8250.h>
+
+#define PORT(_base,_irq)				\
+	{						\
+		.iobase		= _base,		\
+		.irq		= _irq,			\
+		.uartclk	= 1843200,		\
+		.iotype		= UPIO_PORT,		\
+		.flags		= UPF_BOOT_AUTOCONF,	\
+	}
+
+static struct plat_serial8250_port accent_data[] = {
+	PORT(0x330, 4),
+	PORT(0x338, 4),
+	{ },
+};
+
+static struct platform_device accent_device = {
+	.name			= "serial8250",
+	.id			= PLAT8250_DEV_ACCENT,
+	.dev			= {
+		.platform_data	= accent_data,
+	},
+};
+
+static int __init accent_init(void)
+{
+	return platform_device_register(&accent_device);
+}
+
+module_init(accent_init);
+
+MODULE_AUTHOR("Russell King");
+MODULE_DESCRIPTION("8250 serial probe module for Accent Async cards");
+MODULE_LICENSE("GPL");
diff --git a/ap/os/linux/linux-3.4.x/drivers/tty/serial/8250/8250_acorn.c b/ap/os/linux/linux-3.4.x/drivers/tty/serial/8250/8250_acorn.c
new file mode 100644
index 0000000..b0ce8c5
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/tty/serial/8250/8250_acorn.c
@@ -0,0 +1,141 @@
+/*
+ *  linux/drivers/serial/acorn.c
+ *
+ *  Copyright (C) 1996-2003 Russell King.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/tty.h>
+#include <linux/serial_core.h>
+#include <linux/errno.h>
+#include <linux/ioport.h>
+#include <linux/slab.h>
+#include <linux/device.h>
+#include <linux/init.h>
+
+#include <asm/io.h>
+#include <asm/ecard.h>
+#include <asm/string.h>
+
+#include "8250.h"
+
+#define MAX_PORTS	3
+
+struct serial_card_type {
+	unsigned int	num_ports;
+	unsigned int	uartclk;
+	unsigned int	type;
+	unsigned int	offset[MAX_PORTS];
+};
+
+struct serial_card_info {
+	unsigned int	num_ports;
+	int		ports[MAX_PORTS];
+	void __iomem *vaddr;
+};
+
+static int __devinit
+serial_card_probe(struct expansion_card *ec, const struct ecard_id *id)
+{
+	struct serial_card_info *info;
+	struct serial_card_type *type = id->data;
+	struct uart_port port;
+	unsigned long bus_addr;
+	unsigned int i;
+
+	info = kzalloc(sizeof(struct serial_card_info), GFP_KERNEL);
+	if (!info)
+		return -ENOMEM;
+
+	info->num_ports = type->num_ports;
+
+	bus_addr = ecard_resource_start(ec, type->type);
+	info->vaddr = ecardm_iomap(ec, type->type, 0, 0);
+	if (!info->vaddr) {
+		kfree(info);
+		return -ENOMEM;
+	}
+
+	ecard_set_drvdata(ec, info);
+
+	memset(&port, 0, sizeof(struct uart_port));
+	port.irq	= ec->irq;
+	port.flags	= UPF_BOOT_AUTOCONF | UPF_SHARE_IRQ;
+	port.uartclk	= type->uartclk;
+	port.iotype	= UPIO_MEM;
+	port.regshift	= 2;
+	port.dev	= &ec->dev;
+
+	for (i = 0; i < info->num_ports; i ++) {
+		port.membase = info->vaddr + type->offset[i];
+		port.mapbase = bus_addr + type->offset[i];
+
+		info->ports[i] = serial8250_register_port(&port);
+	}
+
+	return 0;
+}
+
+static void __devexit serial_card_remove(struct expansion_card *ec)
+{
+	struct serial_card_info *info = ecard_get_drvdata(ec);
+	int i;
+
+	ecard_set_drvdata(ec, NULL);
+
+	for (i = 0; i < info->num_ports; i++)
+		if (info->ports[i] > 0)
+			serial8250_unregister_port(info->ports[i]);
+
+	kfree(info);
+}
+
+static struct serial_card_type atomwide_type = {
+	.num_ports	= 3,
+	.uartclk	= 7372800,
+	.type		= ECARD_RES_IOCSLOW,
+	.offset		= { 0x2800, 0x2400, 0x2000 },
+};
+
+static struct serial_card_type serport_type = {
+	.num_ports	= 2,
+	.uartclk	= 3686400,
+	.type		= ECARD_RES_IOCSLOW,
+	.offset		= { 0x2000, 0x2020 },
+};
+
+static const struct ecard_id serial_cids[] = {
+	{ MANU_ATOMWIDE,	PROD_ATOMWIDE_3PSERIAL,	&atomwide_type	},
+	{ MANU_SERPORT,		PROD_SERPORT_DSPORT,	&serport_type	},
+	{ 0xffff, 0xffff }
+};
+
+static struct ecard_driver serial_card_driver = {
+	.probe		= serial_card_probe,
+	.remove 	= __devexit_p(serial_card_remove),
+	.id_table	= serial_cids,
+	.drv = {
+		.name	= "8250_acorn",
+	},
+};
+
+static int __init serial_card_init(void)
+{
+	return ecard_register_driver(&serial_card_driver);
+}
+
+static void __exit serial_card_exit(void)
+{
+	ecard_remove_driver(&serial_card_driver);
+}
+
+MODULE_AUTHOR("Russell King");
+MODULE_DESCRIPTION("Acorn 8250-compatible serial port expansion card driver");
+MODULE_LICENSE("GPL");
+
+module_init(serial_card_init);
+module_exit(serial_card_exit);
diff --git a/ap/os/linux/linux-3.4.x/drivers/tty/serial/8250/8250_boca.c b/ap/os/linux/linux-3.4.x/drivers/tty/serial/8250/8250_boca.c
new file mode 100644
index 0000000..d125dc1
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/tty/serial/8250/8250_boca.c
@@ -0,0 +1,59 @@
+/*
+ *  Copyright (C) 2005 Russell King.
+ *  Data taken from include/asm-i386/serial.h
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/serial_8250.h>
+
+#define PORT(_base,_irq)				\
+	{						\
+		.iobase		= _base,		\
+		.irq		= _irq,			\
+		.uartclk	= 1843200,		\
+		.iotype		= UPIO_PORT,		\
+		.flags		= UPF_BOOT_AUTOCONF,	\
+	}
+
+static struct plat_serial8250_port boca_data[] = {
+	PORT(0x100, 12),
+	PORT(0x108, 12),
+	PORT(0x110, 12),
+	PORT(0x118, 12),
+	PORT(0x120, 12),
+	PORT(0x128, 12),
+	PORT(0x130, 12),
+	PORT(0x138, 12),
+	PORT(0x140, 12),
+	PORT(0x148, 12),
+	PORT(0x150, 12),
+	PORT(0x158, 12),
+	PORT(0x160, 12),
+	PORT(0x168, 12),
+	PORT(0x170, 12),
+	PORT(0x178, 12),
+	{ },
+};
+
+static struct platform_device boca_device = {
+	.name			= "serial8250",
+	.id			= PLAT8250_DEV_BOCA,
+	.dev			= {
+		.platform_data	= boca_data,
+	},
+};
+
+static int __init boca_init(void)
+{
+	return platform_device_register(&boca_device);
+}
+
+module_init(boca_init);
+
+MODULE_AUTHOR("Russell King");
+MODULE_DESCRIPTION("8250 serial probe module for Boca cards");
+MODULE_LICENSE("GPL");
diff --git a/ap/os/linux/linux-3.4.x/drivers/tty/serial/8250/8250_dw.c b/ap/os/linux/linux-3.4.x/drivers/tty/serial/8250/8250_dw.c
new file mode 100644
index 0000000..b6dc908
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/tty/serial/8250/8250_dw.c
@@ -0,0 +1,184 @@
+/*
+ * Synopsys DesignWare 8250 driver.
+ *
+ * Copyright 2011 Picochip, Jamie Iles.
+ *
+ * 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.
+ *
+ * The Synopsys DesignWare 8250 has an extra feature whereby it detects if the
+ * LCR is written whilst busy.  If it is, then a busy detect interrupt is
+ * raised, the LCR needs to be rewritten and the uart status register read.
+ */
+#include <linux/device.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/serial_8250.h>
+#include <linux/serial_core.h>
+#include <linux/serial_reg.h>
+#include <linux/of.h>
+#include <linux/of_irq.h>
+#include <linux/of_platform.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+
+struct dw8250_data {
+	int	last_lcr;
+	int	line;
+};
+
+static void dw8250_serial_out(struct uart_port *p, int offset, int value)
+{
+	struct dw8250_data *d = p->private_data;
+
+	if (offset == UART_LCR)
+		d->last_lcr = value;
+
+	offset <<= p->regshift;
+	writeb(value, p->membase + offset);
+}
+
+static unsigned int dw8250_serial_in(struct uart_port *p, int offset)
+{
+	offset <<= p->regshift;
+
+	return readb(p->membase + offset);
+}
+
+static void dw8250_serial_out32(struct uart_port *p, int offset, int value)
+{
+	struct dw8250_data *d = p->private_data;
+
+	if (offset == UART_LCR)
+		d->last_lcr = value;
+
+	offset <<= p->regshift;
+	writel(value, p->membase + offset);
+}
+
+static unsigned int dw8250_serial_in32(struct uart_port *p, int offset)
+{
+	offset <<= p->regshift;
+
+	return readl(p->membase + offset);
+}
+
+/* Offset for the DesignWare's UART Status Register. */
+#define UART_USR	0x1f
+
+static int dw8250_handle_irq(struct uart_port *p)
+{
+	struct dw8250_data *d = p->private_data;
+	unsigned int iir = p->serial_in(p, UART_IIR);
+
+	if (serial8250_handle_irq(p, iir)) {
+		return 1;
+	} else if ((iir & UART_IIR_BUSY) == UART_IIR_BUSY) {
+		/* Clear the USR and write the LCR again. */
+		(void)p->serial_in(p, UART_USR);
+		p->serial_out(p, UART_LCR, d->last_lcr);
+
+		return 1;
+	}
+
+	return 0;
+}
+
+static int __devinit dw8250_probe(struct platform_device *pdev)
+{
+	struct uart_port port = {};
+	struct resource *regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	struct resource *irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+	struct device_node *np = pdev->dev.of_node;
+	u32 val;
+	struct dw8250_data *data;
+
+	if (!regs || !irq) {
+		dev_err(&pdev->dev, "no registers/irq defined\n");
+		return -EINVAL;
+	}
+
+	data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
+	if (!data)
+		return -ENOMEM;
+	port.private_data = data;
+
+	spin_lock_init(&port.lock);
+	port.mapbase = regs->start;
+	port.irq = irq->start;
+	port.handle_irq = dw8250_handle_irq;
+	port.type = PORT_8250;
+	port.flags = UPF_SHARE_IRQ | UPF_BOOT_AUTOCONF | UPF_IOREMAP |
+		UPF_FIXED_PORT | UPF_FIXED_TYPE;
+	port.dev = &pdev->dev;
+
+	port.iotype = UPIO_MEM;
+	port.serial_in = dw8250_serial_in;
+	port.serial_out = dw8250_serial_out;
+	if (!of_property_read_u32(np, "reg-io-width", &val)) {
+		switch (val) {
+		case 1:
+			break;
+		case 4:
+			port.iotype = UPIO_MEM32;
+			port.serial_in = dw8250_serial_in32;
+			port.serial_out = dw8250_serial_out32;
+			break;
+		default:
+			dev_err(&pdev->dev, "unsupported reg-io-width (%u)\n",
+				val);
+			return -EINVAL;
+		}
+	}
+
+	if (!of_property_read_u32(np, "reg-shift", &val))
+		port.regshift = val;
+
+	if (of_property_read_u32(np, "clock-frequency", &val)) {
+		dev_err(&pdev->dev, "no clock-frequency property set\n");
+		return -EINVAL;
+	}
+	port.uartclk = val;
+
+	data->line = serial8250_register_port(&port);
+	if (data->line < 0)
+		return data->line;
+
+	platform_set_drvdata(pdev, data);
+
+	return 0;
+}
+
+static int __devexit dw8250_remove(struct platform_device *pdev)
+{
+	struct dw8250_data *data = platform_get_drvdata(pdev);
+
+	serial8250_unregister_port(data->line);
+
+	return 0;
+}
+
+static const struct of_device_id dw8250_match[] = {
+	{ .compatible = "snps,dw-apb-uart" },
+	{ /* Sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, dw8250_match);
+
+static struct platform_driver dw8250_platform_driver = {
+	.driver = {
+		.name		= "dw-apb-uart",
+		.owner		= THIS_MODULE,
+		.of_match_table	= dw8250_match,
+	},
+	.probe			= dw8250_probe,
+	.remove			= __devexit_p(dw8250_remove),
+};
+
+module_platform_driver(dw8250_platform_driver);
+
+MODULE_AUTHOR("Jamie Iles");
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Synopsys DesignWare 8250 serial port driver");
diff --git a/ap/os/linux/linux-3.4.x/drivers/tty/serial/8250/8250_early.c b/ap/os/linux/linux-3.4.x/drivers/tty/serial/8250/8250_early.c
new file mode 100644
index 0000000..eaafb98
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/tty/serial/8250/8250_early.c
@@ -0,0 +1,287 @@
+/*
+ * Early serial console for 8250/16550 devices
+ *
+ * (c) Copyright 2004 Hewlett-Packard Development Company, L.P.
+ *	Bjorn Helgaas <bjorn.helgaas@hp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Based on the 8250.c serial driver, Copyright (C) 2001 Russell King,
+ * and on early_printk.c by Andi Kleen.
+ *
+ * This is for use before the serial driver has initialized, in
+ * particular, before the UARTs have been discovered and named.
+ * Instead of specifying the console device as, e.g., "ttyS0",
+ * we locate the device directly by its MMIO or I/O port address.
+ *
+ * The user can specify the device directly, e.g.,
+ *	earlycon=uart8250,io,0x3f8,9600n8
+ *	earlycon=uart8250,mmio,0xff5e0000,115200n8
+ *	earlycon=uart8250,mmio32,0xff5e0000,115200n8
+ * or
+ *	console=uart8250,io,0x3f8,9600n8
+ *	console=uart8250,mmio,0xff5e0000,115200n8
+ *	console=uart8250,mmio32,0xff5e0000,115200n8
+ */
+
+#include <linux/tty.h>
+#include <linux/init.h>
+#include <linux/console.h>
+#include <linux/serial_core.h>
+#include <linux/serial_reg.h>
+#include <linux/serial.h>
+#include <linux/serial_8250.h>
+#include <asm/io.h>
+#include <asm/serial.h>
+#ifdef CONFIG_FIX_EARLYCON_MEM
+#include <asm/pgtable.h>
+#include <asm/fixmap.h>
+#endif
+
+struct early_serial8250_device {
+	struct uart_port port;
+	char options[16];		/* e.g., 115200n8 */
+	unsigned int baud;
+};
+
+static struct early_serial8250_device early_device;
+
+static unsigned int __init serial_in(struct uart_port *port, int offset)
+{
+	switch (port->iotype) {
+	case UPIO_MEM:
+		return readb(port->membase + offset);
+	case UPIO_MEM32:
+		return readl(port->membase + (offset << 2));
+	case UPIO_PORT:
+		return inb(port->iobase + offset);
+	default:
+		return 0;
+	}
+}
+
+static void __init serial_out(struct uart_port *port, int offset, int value)
+{
+	switch (port->iotype) {
+	case UPIO_MEM:
+		writeb(value, port->membase + offset);
+		break;
+	case UPIO_MEM32:
+		writel(value, port->membase + (offset << 2));
+		break;
+	case UPIO_PORT:
+		outb(value, port->iobase + offset);
+		break;
+	}
+}
+
+#define BOTH_EMPTY (UART_LSR_TEMT | UART_LSR_THRE)
+
+static void __init wait_for_xmitr(struct uart_port *port)
+{
+	unsigned int status;
+
+	for (;;) {
+		status = serial_in(port, UART_LSR);
+		if ((status & BOTH_EMPTY) == BOTH_EMPTY)
+			return;
+		cpu_relax();
+	}
+}
+
+static void __init serial_putc(struct uart_port *port, int c)
+{
+	wait_for_xmitr(port);
+	serial_out(port, UART_TX, c);
+}
+
+static void __init early_serial8250_write(struct console *console,
+					const char *s, unsigned int count)
+{
+	struct uart_port *port = &early_device.port;
+	unsigned int ier;
+
+	/* Save the IER and disable interrupts */
+	ier = serial_in(port, UART_IER);
+	serial_out(port, UART_IER, 0);
+
+	uart_console_write(port, s, count, serial_putc);
+
+	/* Wait for transmitter to become empty and restore the IER */
+	wait_for_xmitr(port);
+	serial_out(port, UART_IER, ier);
+}
+
+static unsigned int __init probe_baud(struct uart_port *port)
+{
+	unsigned char lcr, dll, dlm;
+	unsigned int quot;
+
+	lcr = serial_in(port, UART_LCR);
+	serial_out(port, UART_LCR, lcr | UART_LCR_DLAB);
+	dll = serial_in(port, UART_DLL);
+	dlm = serial_in(port, UART_DLM);
+	serial_out(port, UART_LCR, lcr);
+
+	quot = (dlm << 8) | dll;
+	return (port->uartclk / 16) / quot;
+}
+
+static void __init init_port(struct early_serial8250_device *device)
+{
+	struct uart_port *port = &device->port;
+	unsigned int divisor;
+	unsigned char c;
+
+	serial_out(port, UART_LCR, 0x3);	/* 8n1 */
+	serial_out(port, UART_IER, 0);		/* no interrupt */
+	serial_out(port, UART_FCR, 0);		/* no fifo */
+	serial_out(port, UART_MCR, 0x3);	/* DTR + RTS */
+
+	divisor = port->uartclk / (16 * device->baud);
+	c = serial_in(port, UART_LCR);
+	serial_out(port, UART_LCR, c | UART_LCR_DLAB);
+	serial_out(port, UART_DLL, divisor & 0xff);
+	serial_out(port, UART_DLM, (divisor >> 8) & 0xff);
+	serial_out(port, UART_LCR, c & ~UART_LCR_DLAB);
+}
+
+static int __init parse_options(struct early_serial8250_device *device,
+								char *options)
+{
+	struct uart_port *port = &device->port;
+	int mmio, mmio32, length;
+
+	if (!options)
+		return -ENODEV;
+
+	port->uartclk = BASE_BAUD * 16;
+
+	mmio = !strncmp(options, "mmio,", 5);
+	mmio32 = !strncmp(options, "mmio32,", 7);
+	if (mmio || mmio32) {
+		port->iotype = (mmio ? UPIO_MEM : UPIO_MEM32);
+		port->mapbase = simple_strtoul(options + (mmio ? 5 : 7),
+					       &options, 0);
+		if (mmio32)
+			port->regshift = 2;
+#ifdef CONFIG_FIX_EARLYCON_MEM
+		set_fixmap_nocache(FIX_EARLYCON_MEM_BASE,
+					port->mapbase & PAGE_MASK);
+		port->membase =
+			(void __iomem *)__fix_to_virt(FIX_EARLYCON_MEM_BASE);
+		port->membase += port->mapbase & ~PAGE_MASK;
+#else
+		port->membase = ioremap_nocache(port->mapbase, 64);
+		if (!port->membase) {
+			printk(KERN_ERR "%s: Couldn't ioremap 0x%llx\n",
+				__func__,
+			       (unsigned long long) port->mapbase);
+			return -ENOMEM;
+		}
+#endif
+	} else if (!strncmp(options, "io,", 3)) {
+		port->iotype = UPIO_PORT;
+		port->iobase = simple_strtoul(options + 3, &options, 0);
+		mmio = 0;
+	} else
+		return -EINVAL;
+
+	options = strchr(options, ',');
+	if (options) {
+		options++;
+		device->baud = simple_strtoul(options, NULL, 0);
+		length = min(strcspn(options, " "), sizeof(device->options));
+		strncpy(device->options, options, length);
+	} else {
+		device->baud = probe_baud(port);
+		snprintf(device->options, sizeof(device->options), "%u",
+			device->baud);
+	}
+
+	if (mmio || mmio32)
+		printk(KERN_INFO
+		       "Early serial console at MMIO%s 0x%llx (options '%s')\n",
+			mmio32 ? "32" : "",
+			(unsigned long long)port->mapbase,
+			device->options);
+	else
+		printk(KERN_INFO
+		      "Early serial console at I/O port 0x%lx (options '%s')\n",
+			port->iobase,
+			device->options);
+
+	return 0;
+}
+
+static struct console early_serial8250_console __initdata = {
+	.name	= "uart",
+	.write	= early_serial8250_write,
+	.flags	= CON_PRINTBUFFER | CON_BOOT,
+	.index	= -1,
+};
+
+static int __init early_serial8250_setup(char *options)
+{
+	struct early_serial8250_device *device = &early_device;
+	int err;
+
+	if (device->port.membase || device->port.iobase)
+		return 0;
+
+	err = parse_options(device, options);
+	if (err < 0)
+		return err;
+
+	init_port(device);
+	return 0;
+}
+
+int __init setup_early_serial8250_console(char *cmdline)
+{
+	char *options;
+	int err;
+
+	options = strstr(cmdline, "uart8250,");
+	if (!options) {
+		options = strstr(cmdline, "uart,");
+		if (!options)
+			return 0;
+	}
+
+	options = strchr(cmdline, ',') + 1;
+	err = early_serial8250_setup(options);
+	if (err < 0)
+		return err;
+
+	register_console(&early_serial8250_console);
+
+	return 0;
+}
+
+int serial8250_find_port_for_earlycon(void)
+{
+	struct early_serial8250_device *device = &early_device;
+	struct uart_port *port = &device->port;
+	int line;
+	int ret;
+
+	if (!device->port.membase && !device->port.iobase)
+		return -ENODEV;
+
+	line = serial8250_find_port(port);
+	if (line < 0)
+		return -ENODEV;
+
+	ret = update_console_cmdline("uart", 8250,
+			     "ttyS", line, device->options);
+	if (ret < 0)
+		ret = update_console_cmdline("uart", 0,
+				     "ttyS", line, device->options);
+
+	return ret;
+}
+
+early_param("earlycon", setup_early_serial8250_console);
diff --git a/ap/os/linux/linux-3.4.x/drivers/tty/serial/8250/8250_exar_st16c554.c b/ap/os/linux/linux-3.4.x/drivers/tty/serial/8250/8250_exar_st16c554.c
new file mode 100644
index 0000000..bf53aab
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/tty/serial/8250/8250_exar_st16c554.c
@@ -0,0 +1,50 @@
+/*
+ *  Written by Paul B Schroeder < pschroeder "at" uplogix "dot" com >
+ *  Based on 8250_boca.
+ *
+ *  Copyright (C) 2005 Russell King.
+ *  Data taken from include/asm-i386/serial.h
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/serial_8250.h>
+
+#define PORT(_base,_irq)				\
+	{						\
+		.iobase		= _base,		\
+		.irq		= _irq,			\
+		.uartclk	= 1843200,		\
+		.iotype		= UPIO_PORT,		\
+		.flags		= UPF_BOOT_AUTOCONF,	\
+	}
+
+static struct plat_serial8250_port exar_data[] = {
+	PORT(0x100, 5),
+	PORT(0x108, 5),
+	PORT(0x110, 5),
+	PORT(0x118, 5),
+	{ },
+};
+
+static struct platform_device exar_device = {
+	.name			= "serial8250",
+	.id			= PLAT8250_DEV_EXAR_ST16C554,
+	.dev			= {
+		.platform_data	= exar_data,
+	},
+};
+
+static int __init exar_init(void)
+{
+	return platform_device_register(&exar_device);
+}
+
+module_init(exar_init);
+
+MODULE_AUTHOR("Paul B Schroeder");
+MODULE_DESCRIPTION("8250 serial probe module for Exar cards");
+MODULE_LICENSE("GPL");
diff --git a/ap/os/linux/linux-3.4.x/drivers/tty/serial/8250/8250_fourport.c b/ap/os/linux/linux-3.4.x/drivers/tty/serial/8250/8250_fourport.c
new file mode 100644
index 0000000..be15826
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/tty/serial/8250/8250_fourport.c
@@ -0,0 +1,51 @@
+/*
+ *  Copyright (C) 2005 Russell King.
+ *  Data taken from include/asm-i386/serial.h
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/serial_8250.h>
+
+#define PORT(_base,_irq)						\
+	{								\
+		.iobase		= _base,				\
+		.irq		= _irq,					\
+		.uartclk	= 1843200,				\
+		.iotype		= UPIO_PORT,				\
+		.flags		= UPF_BOOT_AUTOCONF | UPF_FOURPORT,	\
+	}
+
+static struct plat_serial8250_port fourport_data[] = {
+	PORT(0x1a0, 9),
+	PORT(0x1a8, 9),
+	PORT(0x1b0, 9),
+	PORT(0x1b8, 9),
+	PORT(0x2a0, 5),
+	PORT(0x2a8, 5),
+	PORT(0x2b0, 5),
+	PORT(0x2b8, 5),
+	{ },
+};
+
+static struct platform_device fourport_device = {
+	.name			= "serial8250",
+	.id			= PLAT8250_DEV_FOURPORT,
+	.dev			= {
+		.platform_data	= fourport_data,
+	},
+};
+
+static int __init fourport_init(void)
+{
+	return platform_device_register(&fourport_device);
+}
+
+module_init(fourport_init);
+
+MODULE_AUTHOR("Russell King");
+MODULE_DESCRIPTION("8250 serial probe module for AST Fourport cards");
+MODULE_LICENSE("GPL");
diff --git a/ap/os/linux/linux-3.4.x/drivers/tty/serial/8250/8250_fsl.c b/ap/os/linux/linux-3.4.x/drivers/tty/serial/8250/8250_fsl.c
new file mode 100644
index 0000000..f4d3c47
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/tty/serial/8250/8250_fsl.c
@@ -0,0 +1,63 @@
+#include <linux/serial_reg.h>
+#include <linux/serial_8250.h>
+
+#include "8250.h"
+
+/*
+ * Freescale 16550 UART "driver", Copyright (C) 2011 Paul Gortmaker.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This isn't a full driver; it just provides an alternate IRQ
+ * handler to deal with an errata.  Everything else is just
+ * using the bog standard 8250 support.
+ *
+ * We follow code flow of serial8250_default_handle_irq() but add
+ * a check for a break and insert a dummy read on the Rx for the
+ * immediately following IRQ event.
+ *
+ * We re-use the already existing "bug handling" lsr_saved_flags
+ * field to carry the "what we just did" information from the one
+ * IRQ event to the next one.
+ */
+
+int fsl8250_handle_irq(struct uart_port *port)
+{
+	unsigned char lsr, orig_lsr;
+	unsigned long flags;
+	unsigned int iir;
+	struct uart_8250_port *up =
+		container_of(port, struct uart_8250_port, port);
+
+	spin_lock_irqsave(&up->port.lock, flags);
+
+	iir = port->serial_in(port, UART_IIR);
+	if (iir & UART_IIR_NO_INT) {
+		spin_unlock_irqrestore(&up->port.lock, flags);
+		return 0;
+	}
+
+	/* This is the WAR; if last event was BRK, then read and return */
+	if (unlikely(up->lsr_saved_flags & UART_LSR_BI)) {
+		up->lsr_saved_flags &= ~UART_LSR_BI;
+		port->serial_in(port, UART_RX);
+		spin_unlock_irqrestore(&up->port.lock, flags);
+		return 1;
+	}
+
+	lsr = orig_lsr = up->port.serial_in(&up->port, UART_LSR);
+
+	if (lsr & (UART_LSR_DR | UART_LSR_BI))
+		lsr = serial8250_rx_chars(up, lsr);
+
+	serial8250_modem_status(up);
+
+	if (lsr & UART_LSR_THRE)
+		serial8250_tx_chars(up);
+
+	up->lsr_saved_flags = orig_lsr;
+	spin_unlock_irqrestore(&up->port.lock, flags);
+	return 1;
+}
diff --git a/ap/os/linux/linux-3.4.x/drivers/tty/serial/8250/8250_gsc.c b/ap/os/linux/linux-3.4.x/drivers/tty/serial/8250/8250_gsc.c
new file mode 100644
index 0000000..d8c0ffb
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/tty/serial/8250/8250_gsc.c
@@ -0,0 +1,122 @@
+/*
+ *	Serial Device Initialisation for Lasi/Asp/Wax/Dino
+ *
+ *	(c) Copyright Matthew Wilcox <willy@debian.org> 2001-2002
+ *
+ *	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.
+ */
+
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/ioport.h>
+#include <linux/module.h>
+#include <linux/serial_core.h>
+#include <linux/signal.h>
+#include <linux/types.h>
+
+#include <asm/hardware.h>
+#include <asm/parisc-device.h>
+#include <asm/io.h>
+
+#include "8250.h"
+
+static int __init serial_init_chip(struct parisc_device *dev)
+{
+	struct uart_port port;
+	unsigned long address;
+	int err;
+
+	if (!dev->irq) {
+		/* We find some unattached serial ports by walking native
+		 * busses.  These should be silently ignored.  Otherwise,
+		 * what we have here is a missing parent device, so tell
+		 * the user what they're missing.
+		 */
+		if (parisc_parent(dev)->id.hw_type != HPHW_IOA)
+			printk(KERN_INFO
+				"Serial: device 0x%llx not configured.\n"
+				"Enable support for Wax, Lasi, Asp or Dino.\n",
+				(unsigned long long)dev->hpa.start);
+		return -ENODEV;
+	}
+
+	address = dev->hpa.start;
+	if (dev->id.sversion != 0x8d)
+		address += 0x800;
+
+	memset(&port, 0, sizeof(port));
+	port.iotype	= UPIO_MEM;
+	/* 7.272727MHz on Lasi.  Assumed the same for Dino, Wax and Timi. */
+	port.uartclk	= 7272727;
+	port.mapbase	= address;
+	port.membase	= ioremap_nocache(address, 16);
+	port.irq	= dev->irq;
+	port.flags	= UPF_BOOT_AUTOCONF;
+	port.dev	= &dev->dev;
+
+	err = serial8250_register_port(&port);
+	if (err < 0) {
+		printk(KERN_WARNING
+			"serial8250_register_port returned error %d\n", err);
+		iounmap(port.membase);
+		return err;
+	}
+
+	return 0;
+}
+
+static struct parisc_device_id serial_tbl[] = {
+	{ HPHW_FIO, HVERSION_REV_ANY_ID, HVERSION_ANY_ID, 0x00075 },
+	{ HPHW_FIO, HVERSION_REV_ANY_ID, HVERSION_ANY_ID, 0x0008c },
+	{ HPHW_FIO, HVERSION_REV_ANY_ID, HVERSION_ANY_ID, 0x0008d },
+	{ 0 }
+};
+
+/* Hack.  Some machines have SERIAL_0 attached to Lasi and SERIAL_1
+ * attached to Dino.  Unfortunately, Dino appears before Lasi in the device
+ * tree.  To ensure that ttyS0 == SERIAL_0, we register two drivers; one
+ * which only knows about Lasi and then a second which will find all the
+ * other serial ports.  HPUX ignores this problem.
+ */
+static struct parisc_device_id lasi_tbl[] = {
+	{ HPHW_FIO, HVERSION_REV_ANY_ID, 0x03B, 0x0008C }, /* C1xx/C1xxL */
+	{ HPHW_FIO, HVERSION_REV_ANY_ID, 0x03C, 0x0008C }, /* B132L */
+	{ HPHW_FIO, HVERSION_REV_ANY_ID, 0x03D, 0x0008C }, /* B160L */
+	{ HPHW_FIO, HVERSION_REV_ANY_ID, 0x03E, 0x0008C }, /* B132L+ */
+	{ HPHW_FIO, HVERSION_REV_ANY_ID, 0x03F, 0x0008C }, /* B180L+ */
+	{ HPHW_FIO, HVERSION_REV_ANY_ID, 0x046, 0x0008C }, /* Rocky2 120 */
+	{ HPHW_FIO, HVERSION_REV_ANY_ID, 0x047, 0x0008C }, /* Rocky2 150 */
+	{ HPHW_FIO, HVERSION_REV_ANY_ID, 0x04E, 0x0008C }, /* Kiji L2 132 */
+	{ HPHW_FIO, HVERSION_REV_ANY_ID, 0x056, 0x0008C }, /* Raven+ */
+	{ 0 }
+};
+
+
+MODULE_DEVICE_TABLE(parisc, serial_tbl);
+
+static struct parisc_driver lasi_driver = {
+	.name		= "serial_1",
+	.id_table	= lasi_tbl,
+	.probe		= serial_init_chip,
+};
+
+static struct parisc_driver serial_driver = {
+	.name		= "serial",
+	.id_table	= serial_tbl,
+	.probe		= serial_init_chip,
+};
+
+static int __init probe_serial_gsc(void)
+{
+	register_parisc_driver(&lasi_driver);
+	register_parisc_driver(&serial_driver);
+	return 0;
+}
+
+module_init(probe_serial_gsc);
+
+MODULE_LICENSE("GPL");
diff --git a/ap/os/linux/linux-3.4.x/drivers/tty/serial/8250/8250_hp300.c b/ap/os/linux/linux-3.4.x/drivers/tty/serial/8250/8250_hp300.c
new file mode 100644
index 0000000..c13438c
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/tty/serial/8250/8250_hp300.c
@@ -0,0 +1,327 @@
+/*
+ * Driver for the 98626/98644/internal serial interface on hp300/hp400
+ * (based on the National Semiconductor INS8250/NS16550AF/WD16C552 UARTs)
+ *
+ * Ported from 2.2 and modified to use the normal 8250 driver
+ * by Kars de Jong <jongk@linux-m68k.org>, May 2004.
+ */
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/string.h>
+#include <linux/kernel.h>
+#include <linux/serial.h>
+#include <linux/serial_core.h>
+#include <linux/serial_8250.h>
+#include <linux/delay.h>
+#include <linux/dio.h>
+#include <linux/console.h>
+#include <linux/slab.h>
+#include <asm/io.h>
+
+#include "8250.h"
+
+#if !defined(CONFIG_HPDCA) && !defined(CONFIG_HPAPCI)
+#warning CONFIG_8250 defined but neither CONFIG_HPDCA nor CONFIG_HPAPCI defined, are you sure?
+#endif
+
+#ifdef CONFIG_HPAPCI
+struct hp300_port
+{
+	struct hp300_port *next;	/* next port */
+	int line;			/* line (tty) number */
+};
+
+static struct hp300_port *hp300_ports;
+#endif
+
+#ifdef CONFIG_HPDCA
+
+static int __devinit hpdca_init_one(struct dio_dev *d,
+					const struct dio_device_id *ent);
+static void __devexit hpdca_remove_one(struct dio_dev *d);
+
+static struct dio_device_id hpdca_dio_tbl[] = {
+	{ DIO_ID_DCA0 },
+	{ DIO_ID_DCA0REM },
+	{ DIO_ID_DCA1 },
+	{ DIO_ID_DCA1REM },
+	{ 0 }
+};
+
+static struct dio_driver hpdca_driver = {
+	.name      = "hpdca",
+	.id_table  = hpdca_dio_tbl,
+	.probe     = hpdca_init_one,
+	.remove    = __devexit_p(hpdca_remove_one),
+};
+
+#endif
+
+static unsigned int num_ports;
+
+extern int hp300_uart_scode;
+
+/* Offset to UART registers from base of DCA */
+#define UART_OFFSET	17
+
+#define DCA_ID		0x01	/* ID (read), reset (write) */
+#define DCA_IC		0x03	/* Interrupt control        */
+
+/* Interrupt control */
+#define DCA_IC_IE	0x80	/* Master interrupt enable  */
+
+#define HPDCA_BAUD_BASE 153600
+
+/* Base address of the Frodo part */
+#define FRODO_BASE	(0x41c000)
+
+/*
+ * Where we find the 8250-like APCI ports, and how far apart they are.
+ */
+#define FRODO_APCIBASE		0x0
+#define FRODO_APCISPACE		0x20
+#define FRODO_APCI_OFFSET(x)	(FRODO_APCIBASE + ((x) * FRODO_APCISPACE))
+
+#define HPAPCI_BAUD_BASE 500400
+
+#ifdef CONFIG_SERIAL_8250_CONSOLE
+/*
+ * Parse the bootinfo to find descriptions for headless console and
+ * debug serial ports and register them with the 8250 driver.
+ * This function should be called before serial_console_init() is called
+ * to make sure the serial console will be available for use. IA-64 kernel
+ * calls this function from setup_arch() after the EFI and ACPI tables have
+ * been parsed.
+ */
+int __init hp300_setup_serial_console(void)
+{
+	int scode;
+	struct uart_port port;
+
+	memset(&port, 0, sizeof(port));
+
+	if (hp300_uart_scode < 0 || hp300_uart_scode > DIO_SCMAX)
+		return 0;
+
+	if (DIO_SCINHOLE(hp300_uart_scode))
+		return 0;
+
+	scode = hp300_uart_scode;
+
+	/* Memory mapped I/O */
+	port.iotype = UPIO_MEM;
+	port.flags = UPF_SKIP_TEST | UPF_SHARE_IRQ | UPF_BOOT_AUTOCONF;
+	port.type = PORT_UNKNOWN;
+
+	/* Check for APCI console */
+	if (scode == 256) {
+#ifdef CONFIG_HPAPCI
+		printk(KERN_INFO "Serial console is HP APCI 1\n");
+
+		port.uartclk = HPAPCI_BAUD_BASE * 16;
+		port.mapbase = (FRODO_BASE + FRODO_APCI_OFFSET(1));
+		port.membase = (char *)(port.mapbase + DIO_VIRADDRBASE);
+		port.regshift = 2;
+		add_preferred_console("ttyS", port.line, "9600n8");
+#else
+		printk(KERN_WARNING "Serial console is APCI but support is disabled (CONFIG_HPAPCI)!\n");
+		return 0;
+#endif
+	} else {
+#ifdef CONFIG_HPDCA
+		unsigned long pa = dio_scodetophysaddr(scode);
+		if (!pa)
+			return 0;
+
+		printk(KERN_INFO "Serial console is HP DCA at select code %d\n", scode);
+
+		port.uartclk = HPDCA_BAUD_BASE * 16;
+		port.mapbase = (pa + UART_OFFSET);
+		port.membase = (char *)(port.mapbase + DIO_VIRADDRBASE);
+		port.regshift = 1;
+		port.irq = DIO_IPL(pa + DIO_VIRADDRBASE);
+
+		/* Enable board-interrupts */
+		out_8(pa + DIO_VIRADDRBASE + DCA_IC, DCA_IC_IE);
+
+		if (DIO_ID(pa + DIO_VIRADDRBASE) & 0x80)
+			add_preferred_console("ttyS", port.line, "9600n8");
+#else
+		printk(KERN_WARNING "Serial console is DCA but support is disabled (CONFIG_HPDCA)!\n");
+		return 0;
+#endif
+	}
+
+	if (early_serial_setup(&port) < 0)
+		printk(KERN_WARNING "hp300_setup_serial_console(): early_serial_setup() failed.\n");
+	return 0;
+}
+#endif /* CONFIG_SERIAL_8250_CONSOLE */
+
+#ifdef CONFIG_HPDCA
+static int __devinit hpdca_init_one(struct dio_dev *d,
+				const struct dio_device_id *ent)
+{
+	struct uart_port port;
+	int line;
+
+#ifdef CONFIG_SERIAL_8250_CONSOLE
+	if (hp300_uart_scode == d->scode) {
+		/* Already got it. */
+		return 0;
+	}
+#endif
+	memset(&port, 0, sizeof(struct uart_port));
+
+	/* Memory mapped I/O */
+	port.iotype = UPIO_MEM;
+	port.flags = UPF_SKIP_TEST | UPF_SHARE_IRQ | UPF_BOOT_AUTOCONF;
+	port.irq = d->ipl;
+	port.uartclk = HPDCA_BAUD_BASE * 16;
+	port.mapbase = (d->resource.start + UART_OFFSET);
+	port.membase = (char *)(port.mapbase + DIO_VIRADDRBASE);
+	port.regshift = 1;
+	port.dev = &d->dev;
+	line = serial8250_register_port(&port);
+
+	if (line < 0) {
+		printk(KERN_NOTICE "8250_hp300: register_serial() DCA scode %d"
+		       " irq %d failed\n", d->scode, port.irq);
+		return -ENOMEM;
+	}
+
+	/* Enable board-interrupts */
+	out_8(d->resource.start + DIO_VIRADDRBASE + DCA_IC, DCA_IC_IE);
+	dio_set_drvdata(d, (void *)line);
+
+	/* Reset the DCA */
+	out_8(d->resource.start + DIO_VIRADDRBASE + DCA_ID, 0xff);
+	udelay(100);
+
+	num_ports++;
+
+	return 0;
+}
+#endif
+
+static int __init hp300_8250_init(void)
+{
+	static int called;
+#ifdef CONFIG_HPAPCI
+	int line;
+	unsigned long base;
+	struct uart_port uport;
+	struct hp300_port *port;
+	int i;
+#endif
+	if (called)
+		return -ENODEV;
+	called = 1;
+
+	if (!MACH_IS_HP300)
+		return -ENODEV;
+
+#ifdef CONFIG_HPDCA
+	dio_register_driver(&hpdca_driver);
+#endif
+#ifdef CONFIG_HPAPCI
+	if (hp300_model < HP_400) {
+		if (!num_ports)
+			return -ENODEV;
+		return 0;
+	}
+	/* These models have the Frodo chip.
+	 * Port 0 is reserved for the Apollo Domain keyboard.
+	 * Port 1 is either the console or the DCA.
+	 */
+	for (i = 1; i < 4; i++) {
+		/* Port 1 is the console on a 425e, on other machines it's
+		 * mapped to DCA.
+		 */
+#ifdef CONFIG_SERIAL_8250_CONSOLE
+		if (i == 1)
+			continue;
+#endif
+
+		/* Create new serial device */
+		port = kmalloc(sizeof(struct hp300_port), GFP_KERNEL);
+		if (!port)
+			return -ENOMEM;
+
+		memset(&uport, 0, sizeof(struct uart_port));
+
+		base = (FRODO_BASE + FRODO_APCI_OFFSET(i));
+
+		/* Memory mapped I/O */
+		uport.iotype = UPIO_MEM;
+		uport.flags = UPF_SKIP_TEST | UPF_SHARE_IRQ \
+			      | UPF_BOOT_AUTOCONF;
+		/* XXX - no interrupt support yet */
+		uport.irq = 0;
+		uport.uartclk = HPAPCI_BAUD_BASE * 16;
+		uport.mapbase = base;
+		uport.membase = (char *)(base + DIO_VIRADDRBASE);
+		uport.regshift = 2;
+
+		line = serial8250_register_port(&uport);
+
+		if (line < 0) {
+			printk(KERN_NOTICE "8250_hp300: register_serial() APCI"
+			       " %d irq %d failed\n", i, uport.irq);
+			kfree(port);
+			continue;
+		}
+
+		port->line = line;
+		port->next = hp300_ports;
+		hp300_ports = port;
+
+		num_ports++;
+	}
+#endif
+
+	/* Any boards found? */
+	if (!num_ports)
+		return -ENODEV;
+
+	return 0;
+}
+
+#ifdef CONFIG_HPDCA
+static void __devexit hpdca_remove_one(struct dio_dev *d)
+{
+	int line;
+
+	line = (int) dio_get_drvdata(d);
+	if (d->resource.start) {
+		/* Disable board-interrupts */
+		out_8(d->resource.start + DIO_VIRADDRBASE + DCA_IC, 0);
+	}
+	serial8250_unregister_port(line);
+}
+#endif
+
+static void __exit hp300_8250_exit(void)
+{
+#ifdef CONFIG_HPAPCI
+	struct hp300_port *port, *to_free;
+
+	for (port = hp300_ports; port; ) {
+		serial8250_unregister_port(port->line);
+		to_free = port;
+		port = port->next;
+		kfree(to_free);
+	}
+
+	hp300_ports = NULL;
+#endif
+#ifdef CONFIG_HPDCA
+	dio_unregister_driver(&hpdca_driver);
+#endif
+}
+
+module_init(hp300_8250_init);
+module_exit(hp300_8250_exit);
+MODULE_DESCRIPTION("HP DCA/APCI serial driver");
+MODULE_AUTHOR("Kars de Jong <jongk@linux-m68k.org>");
+MODULE_LICENSE("GPL");
diff --git a/ap/os/linux/linux-3.4.x/drivers/tty/serial/8250/8250_hub6.c b/ap/os/linux/linux-3.4.x/drivers/tty/serial/8250/8250_hub6.c
new file mode 100644
index 0000000..a5c778e
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/tty/serial/8250/8250_hub6.c
@@ -0,0 +1,56 @@
+/*
+ *  Copyright (C) 2005 Russell King.
+ *  Data taken from include/asm-i386/serial.h
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/serial_8250.h>
+
+#define HUB6(card,port)							\
+	{								\
+		.iobase		= 0x302,				\
+		.irq		= 3,					\
+		.uartclk	= 1843200,				\
+		.iotype		= UPIO_HUB6,				\
+		.flags		= UPF_BOOT_AUTOCONF,			\
+		.hub6		= (card) << 6 | (port) << 3 | 1,	\
+	}
+
+static struct plat_serial8250_port hub6_data[] = {
+	HUB6(0, 0),
+	HUB6(0, 1),
+	HUB6(0, 2),
+	HUB6(0, 3),
+	HUB6(0, 4),
+	HUB6(0, 5),
+	HUB6(1, 0),
+	HUB6(1, 1),
+	HUB6(1, 2),
+	HUB6(1, 3),
+	HUB6(1, 4),
+	HUB6(1, 5),
+	{ },
+};
+
+static struct platform_device hub6_device = {
+	.name			= "serial8250",
+	.id			= PLAT8250_DEV_HUB6,
+	.dev			= {
+		.platform_data	= hub6_data,
+	},
+};
+
+static int __init hub6_init(void)
+{
+	return platform_device_register(&hub6_device);
+}
+
+module_init(hub6_init);
+
+MODULE_AUTHOR("Russell King");
+MODULE_DESCRIPTION("8250 serial probe module for Hub6 cards");
+MODULE_LICENSE("GPL");
diff --git a/ap/os/linux/linux-3.4.x/drivers/tty/serial/8250/8250_mca.c b/ap/os/linux/linux-3.4.x/drivers/tty/serial/8250/8250_mca.c
new file mode 100644
index 0000000..d20abf0
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/tty/serial/8250/8250_mca.c
@@ -0,0 +1,61 @@
+/*
+ *  Copyright (C) 2005 Russell King.
+ *  Data taken from include/asm-i386/serial.h
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/mca.h>
+#include <linux/serial_8250.h>
+
+/*
+ * FIXME: Should we be doing AUTO_IRQ here?
+ */
+#ifdef CONFIG_SERIAL_8250_DETECT_IRQ
+#define MCA_FLAGS	UPF_BOOT_AUTOCONF | UPF_SKIP_TEST | UPF_AUTO_IRQ
+#else
+#define MCA_FLAGS	UPF_BOOT_AUTOCONF | UPF_SKIP_TEST
+#endif
+
+#define PORT(_base,_irq)			\
+	{					\
+		.iobase		= _base,	\
+		.irq		= _irq,		\
+		.uartclk	= 1843200,	\
+		.iotype		= UPIO_PORT,	\
+		.flags		= MCA_FLAGS,	\
+	}
+
+static struct plat_serial8250_port mca_data[] = {
+	PORT(0x3220, 3),
+	PORT(0x3228, 3),
+	PORT(0x4220, 3),
+	PORT(0x4228, 3),
+	PORT(0x5220, 3),
+	PORT(0x5228, 3),
+	{ },
+};
+
+static struct platform_device mca_device = {
+	.name			= "serial8250",
+	.id			= PLAT8250_DEV_MCA,
+	.dev			= {
+		.platform_data	= mca_data,
+	},
+};
+
+static int __init mca_init(void)
+{
+	if (!MCA_bus)
+		return -ENODEV;
+	return platform_device_register(&mca_device);
+}
+
+module_init(mca_init);
+
+MODULE_AUTHOR("Russell King");
+MODULE_DESCRIPTION("8250 serial probe module for MCA ports");
+MODULE_LICENSE("GPL");
diff --git a/ap/os/linux/linux-3.4.x/drivers/tty/serial/8250/8250_pci.c b/ap/os/linux/linux-3.4.x/drivers/tty/serial/8250/8250_pci.c
new file mode 100644
index 0000000..e4aada5
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/tty/serial/8250/8250_pci.c
@@ -0,0 +1,4292 @@
+/*
+ *  Probe module for 8250/16550-type PCI serial ports.
+ *
+ *  Based on drivers/char/serial.c, by Linus Torvalds, Theodore Ts'o.
+ *
+ *  Copyright (C) 2001 Russell King, All Rights Reserved.
+ *
+ * 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.
+ */
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/pci.h>
+#include <linux/string.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/tty.h>
+#include <linux/serial_core.h>
+#include <linux/8250_pci.h>
+#include <linux/bitops.h>
+
+#include <asm/byteorder.h>
+#include <asm/io.h>
+
+#include "8250.h"
+
+#undef SERIAL_DEBUG_PCI
+
+/*
+ * init function returns:
+ *  > 0 - number of ports
+ *  = 0 - use board->num_ports
+ *  < 0 - error
+ */
+struct pci_serial_quirk {
+	u32	vendor;
+	u32	device;
+	u32	subvendor;
+	u32	subdevice;
+	int	(*probe)(struct pci_dev *dev);
+	int	(*init)(struct pci_dev *dev);
+	int	(*setup)(struct serial_private *,
+			 const struct pciserial_board *,
+			 struct uart_port *, int);
+	void	(*exit)(struct pci_dev *dev);
+};
+
+#define PCI_NUM_BAR_RESOURCES	6
+
+struct serial_private {
+	struct pci_dev		*dev;
+	unsigned int		nr;
+	void __iomem		*remapped_bar[PCI_NUM_BAR_RESOURCES];
+	struct pci_serial_quirk	*quirk;
+	int			line[0];
+};
+
+static int pci_default_setup(struct serial_private*,
+	  const struct pciserial_board*, struct uart_port*, int);
+
+static void moan_device(const char *str, struct pci_dev *dev)
+{
+	printk(KERN_WARNING
+	       "%s: %s\n"
+	       "Please send the output of lspci -vv, this\n"
+	       "message (0x%04x,0x%04x,0x%04x,0x%04x), the\n"
+	       "manufacturer and name of serial board or\n"
+	       "modem board to <linux-serial@vger.kernel.org>.\n",
+	       pci_name(dev), str, dev->vendor, dev->device,
+	       dev->subsystem_vendor, dev->subsystem_device);
+}
+
+static int
+setup_port(struct serial_private *priv, struct uart_port *port,
+	   int bar, int offset, int regshift)
+{
+	struct pci_dev *dev = priv->dev;
+	unsigned long base, len;
+
+	if (bar >= PCI_NUM_BAR_RESOURCES)
+		return -EINVAL;
+
+	base = pci_resource_start(dev, bar);
+
+	if (pci_resource_flags(dev, bar) & IORESOURCE_MEM) {
+		len =  pci_resource_len(dev, bar);
+
+		if (!priv->remapped_bar[bar])
+			priv->remapped_bar[bar] = ioremap_nocache(base, len);
+		if (!priv->remapped_bar[bar])
+			return -ENOMEM;
+
+		port->iotype = UPIO_MEM;
+		port->iobase = 0;
+		port->mapbase = base + offset;
+		port->membase = priv->remapped_bar[bar] + offset;
+		port->regshift = regshift;
+	} else {
+		port->iotype = UPIO_PORT;
+		port->iobase = base + offset;
+		port->mapbase = 0;
+		port->membase = NULL;
+		port->regshift = 0;
+	}
+	return 0;
+}
+
+/*
+ * ADDI-DATA GmbH communication cards <info@addi-data.com>
+ */
+static int addidata_apci7800_setup(struct serial_private *priv,
+				const struct pciserial_board *board,
+				struct uart_port *port, int idx)
+{
+	unsigned int bar = 0, offset = board->first_offset;
+	bar = FL_GET_BASE(board->flags);
+
+	if (idx < 2) {
+		offset += idx * board->uart_offset;
+	} else if ((idx >= 2) && (idx < 4)) {
+		bar += 1;
+		offset += ((idx - 2) * board->uart_offset);
+	} else if ((idx >= 4) && (idx < 6)) {
+		bar += 2;
+		offset += ((idx - 4) * board->uart_offset);
+	} else if (idx >= 6) {
+		bar += 3;
+		offset += ((idx - 6) * board->uart_offset);
+	}
+
+	return setup_port(priv, port, bar, offset, board->reg_shift);
+}
+
+/*
+ * AFAVLAB uses a different mixture of BARs and offsets
+ * Not that ugly ;) -- HW
+ */
+static int
+afavlab_setup(struct serial_private *priv, const struct pciserial_board *board,
+	      struct uart_port *port, int idx)
+{
+	unsigned int bar, offset = board->first_offset;
+
+	bar = FL_GET_BASE(board->flags);
+	if (idx < 4)
+		bar += idx;
+	else {
+		bar = 4;
+		offset += (idx - 4) * board->uart_offset;
+	}
+
+	return setup_port(priv, port, bar, offset, board->reg_shift);
+}
+
+/*
+ * HP's Remote Management Console.  The Diva chip came in several
+ * different versions.  N-class, L2000 and A500 have two Diva chips, each
+ * with 3 UARTs (the third UART on the second chip is unused).  Superdome
+ * and Keystone have one Diva chip with 3 UARTs.  Some later machines have
+ * one Diva chip, but it has been expanded to 5 UARTs.
+ */
+static int pci_hp_diva_init(struct pci_dev *dev)
+{
+	int rc = 0;
+
+	switch (dev->subsystem_device) {
+	case PCI_DEVICE_ID_HP_DIVA_TOSCA1:
+	case PCI_DEVICE_ID_HP_DIVA_HALFDOME:
+	case PCI_DEVICE_ID_HP_DIVA_KEYSTONE:
+	case PCI_DEVICE_ID_HP_DIVA_EVEREST:
+		rc = 3;
+		break;
+	case PCI_DEVICE_ID_HP_DIVA_TOSCA2:
+		rc = 2;
+		break;
+	case PCI_DEVICE_ID_HP_DIVA_MAESTRO:
+		rc = 4;
+		break;
+	case PCI_DEVICE_ID_HP_DIVA_POWERBAR:
+	case PCI_DEVICE_ID_HP_DIVA_HURRICANE:
+		rc = 1;
+		break;
+	}
+
+	return rc;
+}
+
+/*
+ * HP's Diva chip puts the 4th/5th serial port further out, and
+ * some serial ports are supposed to be hidden on certain models.
+ */
+static int
+pci_hp_diva_setup(struct serial_private *priv,
+		const struct pciserial_board *board,
+		struct uart_port *port, int idx)
+{
+	unsigned int offset = board->first_offset;
+	unsigned int bar = FL_GET_BASE(board->flags);
+
+	switch (priv->dev->subsystem_device) {
+	case PCI_DEVICE_ID_HP_DIVA_MAESTRO:
+		if (idx == 3)
+			idx++;
+		break;
+	case PCI_DEVICE_ID_HP_DIVA_EVEREST:
+		if (idx > 0)
+			idx++;
+		if (idx > 2)
+			idx++;
+		break;
+	}
+	if (idx > 2)
+		offset = 0x18;
+
+	offset += idx * board->uart_offset;
+
+	return setup_port(priv, port, bar, offset, board->reg_shift);
+}
+
+/*
+ * Added for EKF Intel i960 serial boards
+ */
+static int pci_inteli960ni_init(struct pci_dev *dev)
+{
+	unsigned long oldval;
+
+	if (!(dev->subsystem_device & 0x1000))
+		return -ENODEV;
+
+	/* is firmware started? */
+	pci_read_config_dword(dev, 0x44, (void *)&oldval);
+	if (oldval == 0x00001000L) { /* RESET value */
+		printk(KERN_DEBUG "Local i960 firmware missing");
+		return -ENODEV;
+	}
+	return 0;
+}
+
+/*
+ * Some PCI serial cards using the PLX 9050 PCI interface chip require
+ * that the card interrupt be explicitly enabled or disabled.  This
+ * seems to be mainly needed on card using the PLX which also use I/O
+ * mapped memory.
+ */
+static int pci_plx9050_init(struct pci_dev *dev)
+{
+	u8 irq_config;
+	void __iomem *p;
+
+	if ((pci_resource_flags(dev, 0) & IORESOURCE_MEM) == 0) {
+		moan_device("no memory in bar 0", dev);
+		return 0;
+	}
+
+	irq_config = 0x41;
+	if (dev->vendor == PCI_VENDOR_ID_PANACOM ||
+	    dev->subsystem_vendor == PCI_SUBVENDOR_ID_EXSYS)
+		irq_config = 0x43;
+
+	if ((dev->vendor == PCI_VENDOR_ID_PLX) &&
+	    (dev->device == PCI_DEVICE_ID_PLX_ROMULUS))
+		/*
+		 * As the megawolf cards have the int pins active
+		 * high, and have 2 UART chips, both ints must be
+		 * enabled on the 9050. Also, the UARTS are set in
+		 * 16450 mode by default, so we have to enable the
+		 * 16C950 'enhanced' mode so that we can use the
+		 * deep FIFOs
+		 */
+		irq_config = 0x5b;
+	/*
+	 * enable/disable interrupts
+	 */
+	p = ioremap_nocache(pci_resource_start(dev, 0), 0x80);
+	if (p == NULL)
+		return -ENOMEM;
+	writel(irq_config, p + 0x4c);
+
+	/*
+	 * Read the register back to ensure that it took effect.
+	 */
+	readl(p + 0x4c);
+	iounmap(p);
+
+	return 0;
+}
+
+static void __devexit pci_plx9050_exit(struct pci_dev *dev)
+{
+	u8 __iomem *p;
+
+	if ((pci_resource_flags(dev, 0) & IORESOURCE_MEM) == 0)
+		return;
+
+	/*
+	 * disable interrupts
+	 */
+	p = ioremap_nocache(pci_resource_start(dev, 0), 0x80);
+	if (p != NULL) {
+		writel(0, p + 0x4c);
+
+		/*
+		 * Read the register back to ensure that it took effect.
+		 */
+		readl(p + 0x4c);
+		iounmap(p);
+	}
+}
+
+#define NI8420_INT_ENABLE_REG	0x38
+#define NI8420_INT_ENABLE_BIT	0x2000
+
+static void __devexit pci_ni8420_exit(struct pci_dev *dev)
+{
+	void __iomem *p;
+	unsigned long base, len;
+	unsigned int bar = 0;
+
+	if ((pci_resource_flags(dev, bar) & IORESOURCE_MEM) == 0) {
+		moan_device("no memory in bar", dev);
+		return;
+	}
+
+	base = pci_resource_start(dev, bar);
+	len =  pci_resource_len(dev, bar);
+	p = ioremap_nocache(base, len);
+	if (p == NULL)
+		return;
+
+	/* Disable the CPU Interrupt */
+	writel(readl(p + NI8420_INT_ENABLE_REG) & ~(NI8420_INT_ENABLE_BIT),
+	       p + NI8420_INT_ENABLE_REG);
+	iounmap(p);
+}
+
+
+/* MITE registers */
+#define MITE_IOWBSR1	0xc4
+#define MITE_IOWCR1	0xf4
+#define MITE_LCIMR1	0x08
+#define MITE_LCIMR2	0x10
+
+#define MITE_LCIMR2_CLR_CPU_IE	(1 << 30)
+
+static void __devexit pci_ni8430_exit(struct pci_dev *dev)
+{
+	void __iomem *p;
+	unsigned long base, len;
+	unsigned int bar = 0;
+
+	if ((pci_resource_flags(dev, bar) & IORESOURCE_MEM) == 0) {
+		moan_device("no memory in bar", dev);
+		return;
+	}
+
+	base = pci_resource_start(dev, bar);
+	len =  pci_resource_len(dev, bar);
+	p = ioremap_nocache(base, len);
+	if (p == NULL)
+		return;
+
+	/* Disable the CPU Interrupt */
+	writel(MITE_LCIMR2_CLR_CPU_IE, p + MITE_LCIMR2);
+	iounmap(p);
+}
+
+/* SBS Technologies Inc. PMC-OCTPRO and P-OCTAL cards */
+static int
+sbs_setup(struct serial_private *priv, const struct pciserial_board *board,
+		struct uart_port *port, int idx)
+{
+	unsigned int bar, offset = board->first_offset;
+
+	bar = 0;
+
+	if (idx < 4) {
+		/* first four channels map to 0, 0x100, 0x200, 0x300 */
+		offset += idx * board->uart_offset;
+	} else if (idx < 8) {
+		/* last four channels map to 0x1000, 0x1100, 0x1200, 0x1300 */
+		offset += idx * board->uart_offset + 0xC00;
+	} else /* we have only 8 ports on PMC-OCTALPRO */
+		return 1;
+
+	return setup_port(priv, port, bar, offset, board->reg_shift);
+}
+
+/*
+* This does initialization for PMC OCTALPRO cards:
+* maps the device memory, resets the UARTs (needed, bc
+* if the module is removed and inserted again, the card
+* is in the sleep mode) and enables global interrupt.
+*/
+
+/* global control register offset for SBS PMC-OctalPro */
+#define OCT_REG_CR_OFF		0x500
+
+static int sbs_init(struct pci_dev *dev)
+{
+	u8 __iomem *p;
+
+	p = pci_ioremap_bar(dev, 0);
+
+	if (p == NULL)
+		return -ENOMEM;
+	/* Set bit-4 Control Register (UART RESET) in to reset the uarts */
+	writeb(0x10, p + OCT_REG_CR_OFF);
+	udelay(50);
+	writeb(0x0, p + OCT_REG_CR_OFF);
+
+	/* Set bit-2 (INTENABLE) of Control Register */
+	writeb(0x4, p + OCT_REG_CR_OFF);
+	iounmap(p);
+
+	return 0;
+}
+
+/*
+ * Disables the global interrupt of PMC-OctalPro
+ */
+
+static void __devexit sbs_exit(struct pci_dev *dev)
+{
+	u8 __iomem *p;
+
+	p = pci_ioremap_bar(dev, 0);
+	/* FIXME: What if resource_len < OCT_REG_CR_OFF */
+	if (p != NULL)
+		writeb(0, p + OCT_REG_CR_OFF);
+	iounmap(p);
+}
+
+/*
+ * SIIG serial cards have an PCI interface chip which also controls
+ * the UART clocking frequency. Each UART can be clocked independently
+ * (except cards equipped with 4 UARTs) and initial clocking settings
+ * are stored in the EEPROM chip. It can cause problems because this
+ * version of serial driver doesn't support differently clocked UART's
+ * on single PCI card. To prevent this, initialization functions set
+ * high frequency clocking for all UART's on given card. It is safe (I
+ * hope) because it doesn't touch EEPROM settings to prevent conflicts
+ * with other OSes (like M$ DOS).
+ *
+ *  SIIG support added by Andrey Panin <pazke@donpac.ru>, 10/1999
+ *
+ * There is two family of SIIG serial cards with different PCI
+ * interface chip and different configuration methods:
+ *     - 10x cards have control registers in IO and/or memory space;
+ *     - 20x cards have control registers in standard PCI configuration space.
+ *
+ * Note: all 10x cards have PCI device ids 0x10..
+ *       all 20x cards have PCI device ids 0x20..
+ *
+ * There are also Quartet Serial cards which use Oxford Semiconductor
+ * 16954 quad UART PCI chip clocked by 18.432 MHz quartz.
+ *
+ * Note: some SIIG cards are probed by the parport_serial object.
+ */
+
+#define PCI_DEVICE_ID_SIIG_1S_10x (PCI_DEVICE_ID_SIIG_1S_10x_550 & 0xfffc)
+#define PCI_DEVICE_ID_SIIG_2S_10x (PCI_DEVICE_ID_SIIG_2S_10x_550 & 0xfff8)
+
+static int pci_siig10x_init(struct pci_dev *dev)
+{
+	u16 data;
+	void __iomem *p;
+
+	switch (dev->device & 0xfff8) {
+	case PCI_DEVICE_ID_SIIG_1S_10x:	/* 1S */
+		data = 0xffdf;
+		break;
+	case PCI_DEVICE_ID_SIIG_2S_10x:	/* 2S, 2S1P */
+		data = 0xf7ff;
+		break;
+	default:			/* 1S1P, 4S */
+		data = 0xfffb;
+		break;
+	}
+
+	p = ioremap_nocache(pci_resource_start(dev, 0), 0x80);
+	if (p == NULL)
+		return -ENOMEM;
+
+	writew(readw(p + 0x28) & data, p + 0x28);
+	readw(p + 0x28);
+	iounmap(p);
+	return 0;
+}
+
+#define PCI_DEVICE_ID_SIIG_2S_20x (PCI_DEVICE_ID_SIIG_2S_20x_550 & 0xfffc)
+#define PCI_DEVICE_ID_SIIG_2S1P_20x (PCI_DEVICE_ID_SIIG_2S1P_20x_550 & 0xfffc)
+
+static int pci_siig20x_init(struct pci_dev *dev)
+{
+	u8 data;
+
+	/* Change clock frequency for the first UART. */
+	pci_read_config_byte(dev, 0x6f, &data);
+	pci_write_config_byte(dev, 0x6f, data & 0xef);
+
+	/* If this card has 2 UART, we have to do the same with second UART. */
+	if (((dev->device & 0xfffc) == PCI_DEVICE_ID_SIIG_2S_20x) ||
+	    ((dev->device & 0xfffc) == PCI_DEVICE_ID_SIIG_2S1P_20x)) {
+		pci_read_config_byte(dev, 0x73, &data);
+		pci_write_config_byte(dev, 0x73, data & 0xef);
+	}
+	return 0;
+}
+
+static int pci_siig_init(struct pci_dev *dev)
+{
+	unsigned int type = dev->device & 0xff00;
+
+	if (type == 0x1000)
+		return pci_siig10x_init(dev);
+	else if (type == 0x2000)
+		return pci_siig20x_init(dev);
+
+	moan_device("Unknown SIIG card", dev);
+	return -ENODEV;
+}
+
+static int pci_siig_setup(struct serial_private *priv,
+			  const struct pciserial_board *board,
+			  struct uart_port *port, int idx)
+{
+	unsigned int bar = FL_GET_BASE(board->flags) + idx, offset = 0;
+
+	if (idx > 3) {
+		bar = 4;
+		offset = (idx - 4) * 8;
+	}
+
+	return setup_port(priv, port, bar, offset, 0);
+}
+
+/*
+ * Timedia has an explosion of boards, and to avoid the PCI table from
+ * growing *huge*, we use this function to collapse some 70 entries
+ * in the PCI table into one, for sanity's and compactness's sake.
+ */
+static const unsigned short timedia_single_port[] = {
+	0x4025, 0x4027, 0x4028, 0x5025, 0x5027, 0
+};
+
+static const unsigned short timedia_dual_port[] = {
+	0x0002, 0x4036, 0x4037, 0x4038, 0x4078, 0x4079, 0x4085,
+	0x4088, 0x4089, 0x5037, 0x5078, 0x5079, 0x5085, 0x6079,
+	0x7079, 0x8079, 0x8137, 0x8138, 0x8237, 0x8238, 0x9079,
+	0x9137, 0x9138, 0x9237, 0x9238, 0xA079, 0xB079, 0xC079,
+	0xD079, 0
+};
+
+static const unsigned short timedia_quad_port[] = {
+	0x4055, 0x4056, 0x4095, 0x4096, 0x5056, 0x8156, 0x8157,
+	0x8256, 0x8257, 0x9056, 0x9156, 0x9157, 0x9158, 0x9159,
+	0x9256, 0x9257, 0xA056, 0xA157, 0xA158, 0xA159, 0xB056,
+	0xB157, 0
+};
+
+static const unsigned short timedia_eight_port[] = {
+	0x4065, 0x4066, 0x5065, 0x5066, 0x8166, 0x9066, 0x9166,
+	0x9167, 0x9168, 0xA066, 0xA167, 0xA168, 0
+};
+
+static const struct timedia_struct {
+	int num;
+	const unsigned short *ids;
+} timedia_data[] = {
+	{ 1, timedia_single_port },
+	{ 2, timedia_dual_port },
+	{ 4, timedia_quad_port },
+	{ 8, timedia_eight_port }
+};
+
+/*
+ * There are nearly 70 different Timedia/SUNIX PCI serial devices.  Instead of
+ * listing them individually, this driver merely grabs them all with
+ * PCI_ANY_ID.  Some of these devices, however, also feature a parallel port,
+ * and should be left free to be claimed by parport_serial instead.
+ */
+static int pci_timedia_probe(struct pci_dev *dev)
+{
+	/*
+	 * Check the third digit of the subdevice ID
+	 * (0,2,3,5,6: serial only -- 7,8,9: serial + parallel)
+	 */
+	if ((dev->subsystem_device & 0x00f0) >= 0x70) {
+		dev_info(&dev->dev,
+			"ignoring Timedia subdevice %04x for parport_serial\n",
+			dev->subsystem_device);
+		return -ENODEV;
+	}
+
+	return 0;
+}
+
+static int pci_timedia_init(struct pci_dev *dev)
+{
+	const unsigned short *ids;
+	int i, j;
+
+	for (i = 0; i < ARRAY_SIZE(timedia_data); i++) {
+		ids = timedia_data[i].ids;
+		for (j = 0; ids[j]; j++)
+			if (dev->subsystem_device == ids[j])
+				return timedia_data[i].num;
+	}
+	return 0;
+}
+
+/*
+ * Timedia/SUNIX uses a mixture of BARs and offsets
+ * Ugh, this is ugly as all hell --- TYT
+ */
+static int
+pci_timedia_setup(struct serial_private *priv,
+		  const struct pciserial_board *board,
+		  struct uart_port *port, int idx)
+{
+	unsigned int bar = 0, offset = board->first_offset;
+
+	switch (idx) {
+	case 0:
+		bar = 0;
+		break;
+	case 1:
+		offset = board->uart_offset;
+		bar = 0;
+		break;
+	case 2:
+		bar = 1;
+		break;
+	case 3:
+		offset = board->uart_offset;
+		/* FALLTHROUGH */
+	case 4: /* BAR 2 */
+	case 5: /* BAR 3 */
+	case 6: /* BAR 4 */
+	case 7: /* BAR 5 */
+		bar = idx - 2;
+	}
+
+	return setup_port(priv, port, bar, offset, board->reg_shift);
+}
+
+/*
+ * Some Titan cards are also a little weird
+ */
+static int
+titan_400l_800l_setup(struct serial_private *priv,
+		      const struct pciserial_board *board,
+		      struct uart_port *port, int idx)
+{
+	unsigned int bar, offset = board->first_offset;
+
+	switch (idx) {
+	case 0:
+		bar = 1;
+		break;
+	case 1:
+		bar = 2;
+		break;
+	default:
+		bar = 4;
+		offset = (idx - 2) * board->uart_offset;
+	}
+
+	return setup_port(priv, port, bar, offset, board->reg_shift);
+}
+
+static int pci_xircom_init(struct pci_dev *dev)
+{
+	msleep(100);
+	return 0;
+}
+
+static int pci_ni8420_init(struct pci_dev *dev)
+{
+	void __iomem *p;
+	unsigned long base, len;
+	unsigned int bar = 0;
+
+	if ((pci_resource_flags(dev, bar) & IORESOURCE_MEM) == 0) {
+		moan_device("no memory in bar", dev);
+		return 0;
+	}
+
+	base = pci_resource_start(dev, bar);
+	len =  pci_resource_len(dev, bar);
+	p = ioremap_nocache(base, len);
+	if (p == NULL)
+		return -ENOMEM;
+
+	/* Enable CPU Interrupt */
+	writel(readl(p + NI8420_INT_ENABLE_REG) | NI8420_INT_ENABLE_BIT,
+	       p + NI8420_INT_ENABLE_REG);
+
+	iounmap(p);
+	return 0;
+}
+
+#define MITE_IOWBSR1_WSIZE	0xa
+#define MITE_IOWBSR1_WIN_OFFSET	0x800
+#define MITE_IOWBSR1_WENAB	(1 << 7)
+#define MITE_LCIMR1_IO_IE_0	(1 << 24)
+#define MITE_LCIMR2_SET_CPU_IE	(1 << 31)
+#define MITE_IOWCR1_RAMSEL_MASK	0xfffffffe
+
+static int pci_ni8430_init(struct pci_dev *dev)
+{
+	void __iomem *p;
+	unsigned long base, len;
+	u32 device_window;
+	unsigned int bar = 0;
+
+	if ((pci_resource_flags(dev, bar) & IORESOURCE_MEM) == 0) {
+		moan_device("no memory in bar", dev);
+		return 0;
+	}
+
+	base = pci_resource_start(dev, bar);
+	len =  pci_resource_len(dev, bar);
+	p = ioremap_nocache(base, len);
+	if (p == NULL)
+		return -ENOMEM;
+
+	/* Set device window address and size in BAR0 */
+	device_window = ((base + MITE_IOWBSR1_WIN_OFFSET) & 0xffffff00)
+	                | MITE_IOWBSR1_WENAB | MITE_IOWBSR1_WSIZE;
+	writel(device_window, p + MITE_IOWBSR1);
+
+	/* Set window access to go to RAMSEL IO address space */
+	writel((readl(p + MITE_IOWCR1) & MITE_IOWCR1_RAMSEL_MASK),
+	       p + MITE_IOWCR1);
+
+	/* Enable IO Bus Interrupt 0 */
+	writel(MITE_LCIMR1_IO_IE_0, p + MITE_LCIMR1);
+
+	/* Enable CPU Interrupt */
+	writel(MITE_LCIMR2_SET_CPU_IE, p + MITE_LCIMR2);
+
+	iounmap(p);
+	return 0;
+}
+
+/* UART Port Control Register */
+#define NI8430_PORTCON	0x0f
+#define NI8430_PORTCON_TXVR_ENABLE	(1 << 3)
+
+static int
+pci_ni8430_setup(struct serial_private *priv,
+		 const struct pciserial_board *board,
+		 struct uart_port *port, int idx)
+{
+	void __iomem *p;
+	unsigned long base, len;
+	unsigned int bar, offset = board->first_offset;
+
+	if (idx >= board->num_ports)
+		return 1;
+
+	bar = FL_GET_BASE(board->flags);
+	offset += idx * board->uart_offset;
+
+	base = pci_resource_start(priv->dev, bar);
+	len =  pci_resource_len(priv->dev, bar);
+	p = ioremap_nocache(base, len);
+
+	/* enable the transceiver */
+	writeb(readb(p + offset + NI8430_PORTCON) | NI8430_PORTCON_TXVR_ENABLE,
+	       p + offset + NI8430_PORTCON);
+
+	iounmap(p);
+
+	return setup_port(priv, port, bar, offset, board->reg_shift);
+}
+
+static int pci_netmos_9900_setup(struct serial_private *priv,
+				const struct pciserial_board *board,
+				struct uart_port *port, int idx)
+{
+	unsigned int bar;
+
+	if ((priv->dev->subsystem_device & 0xff00) == 0x3000) {
+		/* netmos apparently orders BARs by datasheet layout, so serial
+		 * ports get BARs 0 and 3 (or 1 and 4 for memmapped)
+		 */
+		bar = 3 * idx;
+
+		return setup_port(priv, port, bar, 0, board->reg_shift);
+	} else {
+		return pci_default_setup(priv, board, port, idx);
+	}
+}
+
+/* the 99xx series comes with a range of device IDs and a variety
+ * of capabilities:
+ *
+ * 9900 has varying capabilities and can cascade to sub-controllers
+ *   (cascading should be purely internal)
+ * 9904 is hardwired with 4 serial ports
+ * 9912 and 9922 are hardwired with 2 serial ports
+ */
+static int pci_netmos_9900_numports(struct pci_dev *dev)
+{
+	unsigned int c = dev->class;
+	unsigned int pi;
+	unsigned short sub_serports;
+
+	pi = (c & 0xff);
+
+	if (pi == 2) {
+		return 1;
+	} else if ((pi == 0) &&
+			   (dev->device == PCI_DEVICE_ID_NETMOS_9900)) {
+		/* two possibilities: 0x30ps encodes number of parallel and
+		 * serial ports, or 0x1000 indicates *something*. This is not
+		 * immediately obvious, since the 2s1p+4s configuration seems
+		 * to offer all functionality on functions 0..2, while still
+		 * advertising the same function 3 as the 4s+2s1p config.
+		 */
+		sub_serports = dev->subsystem_device & 0xf;
+		if (sub_serports > 0) {
+			return sub_serports;
+		} else {
+			printk(KERN_NOTICE "NetMos/Mostech serial driver ignoring port on ambiguous config.\n");
+			return 0;
+		}
+	}
+
+	moan_device("unknown NetMos/Mostech program interface", dev);
+	return 0;
+}
+
+static int pci_netmos_init(struct pci_dev *dev)
+{
+	/* subdevice 0x00PS means <P> parallel, <S> serial */
+	unsigned int num_serial = dev->subsystem_device & 0xf;
+
+	if ((dev->device == PCI_DEVICE_ID_NETMOS_9901) ||
+		(dev->device == PCI_DEVICE_ID_NETMOS_9865))
+		return 0;
+
+	if (dev->subsystem_vendor == PCI_VENDOR_ID_IBM &&
+			dev->subsystem_device == 0x0299)
+		return 0;
+
+	switch (dev->device) { /* FALLTHROUGH on all */
+		case PCI_DEVICE_ID_NETMOS_9904:
+		case PCI_DEVICE_ID_NETMOS_9912:
+		case PCI_DEVICE_ID_NETMOS_9922:
+		case PCI_DEVICE_ID_NETMOS_9900:
+			num_serial = pci_netmos_9900_numports(dev);
+			break;
+
+		default:
+			if (num_serial == 0 ) {
+				moan_device("unknown NetMos/Mostech device", dev);
+			}
+	}
+
+	if (num_serial == 0)
+		return -ENODEV;
+
+	return num_serial;
+}
+
+/*
+ * These chips are available with optionally one parallel port and up to
+ * two serial ports. Unfortunately they all have the same product id.
+ *
+ * Basic configuration is done over a region of 32 I/O ports. The base
+ * ioport is called INTA or INTC, depending on docs/other drivers.
+ *
+ * The region of the 32 I/O ports is configured in POSIO0R...
+ */
+
+/* registers */
+#define ITE_887x_MISCR		0x9c
+#define ITE_887x_INTCBAR	0x78
+#define ITE_887x_UARTBAR	0x7c
+#define ITE_887x_PS0BAR		0x10
+#define ITE_887x_POSIO0		0x60
+
+/* I/O space size */
+#define ITE_887x_IOSIZE		32
+/* I/O space size (bits 26-24; 8 bytes = 011b) */
+#define ITE_887x_POSIO_IOSIZE_8		(3 << 24)
+/* I/O space size (bits 26-24; 32 bytes = 101b) */
+#define ITE_887x_POSIO_IOSIZE_32	(5 << 24)
+/* Decoding speed (1 = slow, 2 = medium, 3 = fast) */
+#define ITE_887x_POSIO_SPEED		(3 << 29)
+/* enable IO_Space bit */
+#define ITE_887x_POSIO_ENABLE		(1 << 31)
+
+static int pci_ite887x_init(struct pci_dev *dev)
+{
+	/* inta_addr are the configuration addresses of the ITE */
+	static const short inta_addr[] = { 0x2a0, 0x2c0, 0x220, 0x240, 0x1e0,
+							0x200, 0x280, 0 };
+	int ret, i, type;
+	struct resource *iobase = NULL;
+	u32 miscr, uartbar, ioport;
+
+	/* search for the base-ioport */
+	i = 0;
+	while (inta_addr[i] && iobase == NULL) {
+		iobase = request_region(inta_addr[i], ITE_887x_IOSIZE,
+								"ite887x");
+		if (iobase != NULL) {
+			/* write POSIO0R - speed | size | ioport */
+			pci_write_config_dword(dev, ITE_887x_POSIO0,
+				ITE_887x_POSIO_ENABLE | ITE_887x_POSIO_SPEED |
+				ITE_887x_POSIO_IOSIZE_32 | inta_addr[i]);
+			/* write INTCBAR - ioport */
+			pci_write_config_dword(dev, ITE_887x_INTCBAR,
+								inta_addr[i]);
+			ret = inb(inta_addr[i]);
+			if (ret != 0xff) {
+				/* ioport connected */
+				break;
+			}
+			release_region(iobase->start, ITE_887x_IOSIZE);
+			iobase = NULL;
+		}
+		i++;
+	}
+
+	if (!inta_addr[i]) {
+		printk(KERN_ERR "ite887x: could not find iobase\n");
+		return -ENODEV;
+	}
+
+	/* start of undocumented type checking (see parport_pc.c) */
+	type = inb(iobase->start + 0x18) & 0x0f;
+
+	switch (type) {
+	case 0x2:	/* ITE8871 (1P) */
+	case 0xa:	/* ITE8875 (1P) */
+		ret = 0;
+		break;
+	case 0xe:	/* ITE8872 (2S1P) */
+		ret = 2;
+		break;
+	case 0x6:	/* ITE8873 (1S) */
+		ret = 1;
+		break;
+	case 0x8:	/* ITE8874 (2S) */
+		ret = 2;
+		break;
+	default:
+		moan_device("Unknown ITE887x", dev);
+		ret = -ENODEV;
+	}
+
+	/* configure all serial ports */
+	for (i = 0; i < ret; i++) {
+		/* read the I/O port from the device */
+		pci_read_config_dword(dev, ITE_887x_PS0BAR + (0x4 * (i + 1)),
+								&ioport);
+		ioport &= 0x0000FF00;	/* the actual base address */
+		pci_write_config_dword(dev, ITE_887x_POSIO0 + (0x4 * (i + 1)),
+			ITE_887x_POSIO_ENABLE | ITE_887x_POSIO_SPEED |
+			ITE_887x_POSIO_IOSIZE_8 | ioport);
+
+		/* write the ioport to the UARTBAR */
+		pci_read_config_dword(dev, ITE_887x_UARTBAR, &uartbar);
+		uartbar &= ~(0xffff << (16 * i));	/* clear half the reg */
+		uartbar |= (ioport << (16 * i));	/* set the ioport */
+		pci_write_config_dword(dev, ITE_887x_UARTBAR, uartbar);
+
+		/* get current config */
+		pci_read_config_dword(dev, ITE_887x_MISCR, &miscr);
+		/* disable interrupts (UARTx_Routing[3:0]) */
+		miscr &= ~(0xf << (12 - 4 * i));
+		/* activate the UART (UARTx_En) */
+		miscr |= 1 << (23 - i);
+		/* write new config with activated UART */
+		pci_write_config_dword(dev, ITE_887x_MISCR, miscr);
+	}
+
+	if (ret <= 0) {
+		/* the device has no UARTs if we get here */
+		release_region(iobase->start, ITE_887x_IOSIZE);
+	}
+
+	return ret;
+}
+
+static void __devexit pci_ite887x_exit(struct pci_dev *dev)
+{
+	u32 ioport;
+	/* the ioport is bit 0-15 in POSIO0R */
+	pci_read_config_dword(dev, ITE_887x_POSIO0, &ioport);
+	ioport &= 0xffff;
+	release_region(ioport, ITE_887x_IOSIZE);
+}
+
+/*
+ * Oxford Semiconductor Inc.
+ * Check that device is part of the Tornado range of devices, then determine
+ * the number of ports available on the device.
+ */
+static int pci_oxsemi_tornado_init(struct pci_dev *dev)
+{
+	u8 __iomem *p;
+	unsigned long deviceID;
+	unsigned int  number_uarts = 0;
+
+	/* OxSemi Tornado devices are all 0xCxxx */
+	if (dev->vendor == PCI_VENDOR_ID_OXSEMI &&
+	    (dev->device & 0xF000) != 0xC000)
+		return 0;
+
+	p = pci_iomap(dev, 0, 5);
+	if (p == NULL)
+		return -ENOMEM;
+
+	deviceID = ioread32(p);
+	/* Tornado device */
+	if (deviceID == 0x07000200) {
+		number_uarts = ioread8(p + 4);
+		printk(KERN_DEBUG
+			"%d ports detected on Oxford PCI Express device\n",
+								number_uarts);
+	}
+	pci_iounmap(dev, p);
+	return number_uarts;
+}
+
+static int
+pci_default_setup(struct serial_private *priv,
+		  const struct pciserial_board *board,
+		  struct uart_port *port, int idx)
+{
+	unsigned int bar, offset = board->first_offset, maxnr;
+
+	bar = FL_GET_BASE(board->flags);
+	if (board->flags & FL_BASE_BARS)
+		bar += idx;
+	else
+		offset += idx * board->uart_offset;
+
+	maxnr = (pci_resource_len(priv->dev, bar) - board->first_offset) >>
+		(board->reg_shift + 3);
+
+	if (board->flags & FL_REGION_SZ_CAP && idx >= maxnr)
+		return 1;
+
+	return setup_port(priv, port, bar, offset, board->reg_shift);
+}
+
+static int
+ce4100_serial_setup(struct serial_private *priv,
+		  const struct pciserial_board *board,
+		  struct uart_port *port, int idx)
+{
+	int ret;
+
+	ret = setup_port(priv, port, 0, 0, board->reg_shift);
+	port->iotype = UPIO_MEM32;
+	port->type = PORT_XSCALE;
+	port->flags = (port->flags | UPF_FIXED_PORT | UPF_FIXED_TYPE);
+	port->regshift = 2;
+
+	return ret;
+}
+
+static int
+pci_omegapci_setup(struct serial_private *priv,
+		      const struct pciserial_board *board,
+		      struct uart_port *port, int idx)
+{
+	return setup_port(priv, port, 2, idx * 8, 0);
+}
+
+static int
+pci_brcm_trumanage_setup(struct serial_private *priv,
+			 const struct pciserial_board *board,
+			 struct uart_port *port, int idx)
+{
+	int ret = pci_default_setup(priv, board, port, idx);
+
+	port->type = PORT_BRCM_TRUMANAGE;
+	port->flags = (port->flags | UPF_FIXED_PORT | UPF_FIXED_TYPE);
+	return ret;
+}
+
+static int skip_tx_en_setup(struct serial_private *priv,
+			const struct pciserial_board *board,
+			struct uart_port *port, int idx)
+{
+	port->flags |= UPF_NO_TXEN_TEST;
+	printk(KERN_DEBUG "serial8250: skipping TxEn test for device "
+			  "[%04x:%04x] subsystem [%04x:%04x]\n",
+			  priv->dev->vendor,
+			  priv->dev->device,
+			  priv->dev->subsystem_vendor,
+			  priv->dev->subsystem_device);
+
+	return pci_default_setup(priv, board, port, idx);
+}
+
+static int kt_serial_setup(struct serial_private *priv,
+			   const struct pciserial_board *board,
+			   struct uart_port *port, int idx)
+{
+	port->flags |= UPF_BUG_THRE;
+	return skip_tx_en_setup(priv, board, port, idx);
+}
+
+static int pci_eg20t_init(struct pci_dev *dev)
+{
+#if defined(CONFIG_SERIAL_PCH_UART) || defined(CONFIG_SERIAL_PCH_UART_MODULE)
+	return -ENODEV;
+#else
+	return 0;
+#endif
+}
+
+static int
+pci_xr17c154_setup(struct serial_private *priv,
+		  const struct pciserial_board *board,
+		  struct uart_port *port, int idx)
+{
+	port->flags |= UPF_EXAR_EFR;
+	return pci_default_setup(priv, board, port, idx);
+}
+
+#define PCI_VENDOR_ID_SBSMODULARIO	0x124B
+#define PCI_SUBVENDOR_ID_SBSMODULARIO	0x124B
+#define PCI_DEVICE_ID_OCTPRO		0x0001
+#define PCI_SUBDEVICE_ID_OCTPRO232	0x0108
+#define PCI_SUBDEVICE_ID_OCTPRO422	0x0208
+#define PCI_SUBDEVICE_ID_POCTAL232	0x0308
+#define PCI_SUBDEVICE_ID_POCTAL422	0x0408
+#define PCI_SUBDEVICE_ID_SIIG_DUAL_00	0x2500
+#define PCI_SUBDEVICE_ID_SIIG_DUAL_30	0x2530
+#define PCI_VENDOR_ID_ADVANTECH		0x13fe
+#define PCI_DEVICE_ID_INTEL_CE4100_UART 0x2e66
+#define PCI_DEVICE_ID_ADVANTECH_PCI3620	0x3620
+#define PCI_DEVICE_ID_TITAN_200I	0x8028
+#define PCI_DEVICE_ID_TITAN_400I	0x8048
+#define PCI_DEVICE_ID_TITAN_800I	0x8088
+#define PCI_DEVICE_ID_TITAN_800EH	0xA007
+#define PCI_DEVICE_ID_TITAN_800EHB	0xA008
+#define PCI_DEVICE_ID_TITAN_400EH	0xA009
+#define PCI_DEVICE_ID_TITAN_100E	0xA010
+#define PCI_DEVICE_ID_TITAN_200E	0xA012
+#define PCI_DEVICE_ID_TITAN_400E	0xA013
+#define PCI_DEVICE_ID_TITAN_800E	0xA014
+#define PCI_DEVICE_ID_TITAN_200EI	0xA016
+#define PCI_DEVICE_ID_TITAN_200EISI	0xA017
+#define PCI_DEVICE_ID_TITAN_200V3	0xA306
+#define PCI_DEVICE_ID_TITAN_400V3	0xA310
+#define PCI_DEVICE_ID_TITAN_410V3	0xA312
+#define PCI_DEVICE_ID_TITAN_800V3	0xA314
+#define PCI_DEVICE_ID_TITAN_800V3B	0xA315
+#define PCI_DEVICE_ID_OXSEMI_16PCI958	0x9538
+#define PCIE_DEVICE_ID_NEO_2_OX_IBM	0x00F6
+#define PCI_DEVICE_ID_PLX_CRONYX_OMEGA	0xc001
+#define PCI_DEVICE_ID_INTEL_PATSBURG_KT 0x1d3d
+#define PCI_DEVICE_ID_BROADCOM_TRUMANAGE 0x160a
+#define PCI_DEVICE_ID_INTEL_QRK_UART	0x0936
+
+/* Unknown vendors/cards - this should not be in linux/pci_ids.h */
+#define PCI_SUBDEVICE_ID_UNKNOWN_0x1584	0x1584
+#define PCI_SUBDEVICE_ID_UNKNOWN_0x1588	0x1588
+
+/*
+ * Master list of serial port init/setup/exit quirks.
+ * This does not describe the general nature of the port.
+ * (ie, baud base, number and location of ports, etc)
+ *
+ * This list is ordered alphabetically by vendor then device.
+ * Specific entries must come before more generic entries.
+ */
+static struct pci_serial_quirk pci_serial_quirks[] __refdata = {
+	/*
+	* ADDI-DATA GmbH communication cards <info@addi-data.com>
+	*/
+	{
+		.vendor         = PCI_VENDOR_ID_ADDIDATA_OLD,
+		.device         = PCI_DEVICE_ID_ADDIDATA_APCI7800,
+		.subvendor      = PCI_ANY_ID,
+		.subdevice      = PCI_ANY_ID,
+		.setup          = addidata_apci7800_setup,
+	},
+	/*
+	 * AFAVLAB cards - these may be called via parport_serial
+	 *  It is not clear whether this applies to all products.
+	 */
+	{
+		.vendor		= PCI_VENDOR_ID_AFAVLAB,
+		.device		= PCI_ANY_ID,
+		.subvendor	= PCI_ANY_ID,
+		.subdevice	= PCI_ANY_ID,
+		.setup		= afavlab_setup,
+	},
+	/*
+	 * HP Diva
+	 */
+	{
+		.vendor		= PCI_VENDOR_ID_HP,
+		.device		= PCI_DEVICE_ID_HP_DIVA,
+		.subvendor	= PCI_ANY_ID,
+		.subdevice	= PCI_ANY_ID,
+		.init		= pci_hp_diva_init,
+		.setup		= pci_hp_diva_setup,
+	},
+	/*
+	 * Intel
+	 */
+	{
+		.vendor		= PCI_VENDOR_ID_INTEL,
+		.device		= PCI_DEVICE_ID_INTEL_80960_RP,
+		.subvendor	= 0xe4bf,
+		.subdevice	= PCI_ANY_ID,
+		.init		= pci_inteli960ni_init,
+		.setup		= pci_default_setup,
+	},
+	{
+		.vendor		= PCI_VENDOR_ID_INTEL,
+		.device		= PCI_DEVICE_ID_INTEL_8257X_SOL,
+		.subvendor	= PCI_ANY_ID,
+		.subdevice	= PCI_ANY_ID,
+		.setup		= skip_tx_en_setup,
+	},
+	{
+		.vendor		= PCI_VENDOR_ID_INTEL,
+		.device		= PCI_DEVICE_ID_INTEL_82573L_SOL,
+		.subvendor	= PCI_ANY_ID,
+		.subdevice	= PCI_ANY_ID,
+		.setup		= skip_tx_en_setup,
+	},
+	{
+		.vendor		= PCI_VENDOR_ID_INTEL,
+		.device		= PCI_DEVICE_ID_INTEL_82573E_SOL,
+		.subvendor	= PCI_ANY_ID,
+		.subdevice	= PCI_ANY_ID,
+		.setup		= skip_tx_en_setup,
+	},
+	{
+		.vendor		= PCI_VENDOR_ID_INTEL,
+		.device		= PCI_DEVICE_ID_INTEL_CE4100_UART,
+		.subvendor	= PCI_ANY_ID,
+		.subdevice	= PCI_ANY_ID,
+		.setup		= ce4100_serial_setup,
+	},
+	{
+		.vendor		= PCI_VENDOR_ID_INTEL,
+		.device		= PCI_DEVICE_ID_INTEL_PATSBURG_KT,
+		.subvendor	= PCI_ANY_ID,
+		.subdevice	= PCI_ANY_ID,
+		.setup		= kt_serial_setup,
+	},
+	/*
+	 * ITE
+	 */
+	{
+		.vendor		= PCI_VENDOR_ID_ITE,
+		.device		= PCI_DEVICE_ID_ITE_8872,
+		.subvendor	= PCI_ANY_ID,
+		.subdevice	= PCI_ANY_ID,
+		.init		= pci_ite887x_init,
+		.setup		= pci_default_setup,
+		.exit		= __devexit_p(pci_ite887x_exit),
+	},
+	/*
+	 * National Instruments
+	 */
+	{
+		.vendor		= PCI_VENDOR_ID_NI,
+		.device		= PCI_DEVICE_ID_NI_PCI23216,
+		.subvendor	= PCI_ANY_ID,
+		.subdevice	= PCI_ANY_ID,
+		.init		= pci_ni8420_init,
+		.setup		= pci_default_setup,
+		.exit		= __devexit_p(pci_ni8420_exit),
+	},
+	{
+		.vendor		= PCI_VENDOR_ID_NI,
+		.device		= PCI_DEVICE_ID_NI_PCI2328,
+		.subvendor	= PCI_ANY_ID,
+		.subdevice	= PCI_ANY_ID,
+		.init		= pci_ni8420_init,
+		.setup		= pci_default_setup,
+		.exit		= __devexit_p(pci_ni8420_exit),
+	},
+	{
+		.vendor		= PCI_VENDOR_ID_NI,
+		.device		= PCI_DEVICE_ID_NI_PCI2324,
+		.subvendor	= PCI_ANY_ID,
+		.subdevice	= PCI_ANY_ID,
+		.init		= pci_ni8420_init,
+		.setup		= pci_default_setup,
+		.exit		= __devexit_p(pci_ni8420_exit),
+	},
+	{
+		.vendor		= PCI_VENDOR_ID_NI,
+		.device		= PCI_DEVICE_ID_NI_PCI2322,
+		.subvendor	= PCI_ANY_ID,
+		.subdevice	= PCI_ANY_ID,
+		.init		= pci_ni8420_init,
+		.setup		= pci_default_setup,
+		.exit		= __devexit_p(pci_ni8420_exit),
+	},
+	{
+		.vendor		= PCI_VENDOR_ID_NI,
+		.device		= PCI_DEVICE_ID_NI_PCI2324I,
+		.subvendor	= PCI_ANY_ID,
+		.subdevice	= PCI_ANY_ID,
+		.init		= pci_ni8420_init,
+		.setup		= pci_default_setup,
+		.exit		= __devexit_p(pci_ni8420_exit),
+	},
+	{
+		.vendor		= PCI_VENDOR_ID_NI,
+		.device		= PCI_DEVICE_ID_NI_PCI2322I,
+		.subvendor	= PCI_ANY_ID,
+		.subdevice	= PCI_ANY_ID,
+		.init		= pci_ni8420_init,
+		.setup		= pci_default_setup,
+		.exit		= __devexit_p(pci_ni8420_exit),
+	},
+	{
+		.vendor		= PCI_VENDOR_ID_NI,
+		.device		= PCI_DEVICE_ID_NI_PXI8420_23216,
+		.subvendor	= PCI_ANY_ID,
+		.subdevice	= PCI_ANY_ID,
+		.init		= pci_ni8420_init,
+		.setup		= pci_default_setup,
+		.exit		= __devexit_p(pci_ni8420_exit),
+	},
+	{
+		.vendor		= PCI_VENDOR_ID_NI,
+		.device		= PCI_DEVICE_ID_NI_PXI8420_2328,
+		.subvendor	= PCI_ANY_ID,
+		.subdevice	= PCI_ANY_ID,
+		.init		= pci_ni8420_init,
+		.setup		= pci_default_setup,
+		.exit		= __devexit_p(pci_ni8420_exit),
+	},
+	{
+		.vendor		= PCI_VENDOR_ID_NI,
+		.device		= PCI_DEVICE_ID_NI_PXI8420_2324,
+		.subvendor	= PCI_ANY_ID,
+		.subdevice	= PCI_ANY_ID,
+		.init		= pci_ni8420_init,
+		.setup		= pci_default_setup,
+		.exit		= __devexit_p(pci_ni8420_exit),
+	},
+	{
+		.vendor		= PCI_VENDOR_ID_NI,
+		.device		= PCI_DEVICE_ID_NI_PXI8420_2322,
+		.subvendor	= PCI_ANY_ID,
+		.subdevice	= PCI_ANY_ID,
+		.init		= pci_ni8420_init,
+		.setup		= pci_default_setup,
+		.exit		= __devexit_p(pci_ni8420_exit),
+	},
+	{
+		.vendor		= PCI_VENDOR_ID_NI,
+		.device		= PCI_DEVICE_ID_NI_PXI8422_2324,
+		.subvendor	= PCI_ANY_ID,
+		.subdevice	= PCI_ANY_ID,
+		.init		= pci_ni8420_init,
+		.setup		= pci_default_setup,
+		.exit		= __devexit_p(pci_ni8420_exit),
+	},
+	{
+		.vendor		= PCI_VENDOR_ID_NI,
+		.device		= PCI_DEVICE_ID_NI_PXI8422_2322,
+		.subvendor	= PCI_ANY_ID,
+		.subdevice	= PCI_ANY_ID,
+		.init		= pci_ni8420_init,
+		.setup		= pci_default_setup,
+		.exit		= __devexit_p(pci_ni8420_exit),
+	},
+	{
+		.vendor		= PCI_VENDOR_ID_NI,
+		.device		= PCI_ANY_ID,
+		.subvendor	= PCI_ANY_ID,
+		.subdevice	= PCI_ANY_ID,
+		.init		= pci_ni8430_init,
+		.setup		= pci_ni8430_setup,
+		.exit		= __devexit_p(pci_ni8430_exit),
+	},
+	/*
+	 * Panacom
+	 */
+	{
+		.vendor		= PCI_VENDOR_ID_PANACOM,
+		.device		= PCI_DEVICE_ID_PANACOM_QUADMODEM,
+		.subvendor	= PCI_ANY_ID,
+		.subdevice	= PCI_ANY_ID,
+		.init		= pci_plx9050_init,
+		.setup		= pci_default_setup,
+		.exit		= __devexit_p(pci_plx9050_exit),
+	},
+	{
+		.vendor		= PCI_VENDOR_ID_PANACOM,
+		.device		= PCI_DEVICE_ID_PANACOM_DUALMODEM,
+		.subvendor	= PCI_ANY_ID,
+		.subdevice	= PCI_ANY_ID,
+		.init		= pci_plx9050_init,
+		.setup		= pci_default_setup,
+		.exit		= __devexit_p(pci_plx9050_exit),
+	},
+	/*
+	 * PLX
+	 */
+	{
+		.vendor		= PCI_VENDOR_ID_PLX,
+		.device		= PCI_DEVICE_ID_PLX_9030,
+		.subvendor	= PCI_SUBVENDOR_ID_PERLE,
+		.subdevice	= PCI_ANY_ID,
+		.setup		= pci_default_setup,
+	},
+	{
+		.vendor		= PCI_VENDOR_ID_PLX,
+		.device		= PCI_DEVICE_ID_PLX_9050,
+		.subvendor	= PCI_SUBVENDOR_ID_EXSYS,
+		.subdevice	= PCI_SUBDEVICE_ID_EXSYS_4055,
+		.init		= pci_plx9050_init,
+		.setup		= pci_default_setup,
+		.exit		= __devexit_p(pci_plx9050_exit),
+	},
+	{
+		.vendor		= PCI_VENDOR_ID_PLX,
+		.device		= PCI_DEVICE_ID_PLX_9050,
+		.subvendor	= PCI_SUBVENDOR_ID_KEYSPAN,
+		.subdevice	= PCI_SUBDEVICE_ID_KEYSPAN_SX2,
+		.init		= pci_plx9050_init,
+		.setup		= pci_default_setup,
+		.exit		= __devexit_p(pci_plx9050_exit),
+	},
+	{
+		.vendor		= PCI_VENDOR_ID_PLX,
+		.device		= PCI_DEVICE_ID_PLX_ROMULUS,
+		.subvendor	= PCI_VENDOR_ID_PLX,
+		.subdevice	= PCI_DEVICE_ID_PLX_ROMULUS,
+		.init		= pci_plx9050_init,
+		.setup		= pci_default_setup,
+		.exit		= __devexit_p(pci_plx9050_exit),
+	},
+	/*
+	 * SBS Technologies, Inc., PMC-OCTALPRO 232
+	 */
+	{
+		.vendor		= PCI_VENDOR_ID_SBSMODULARIO,
+		.device		= PCI_DEVICE_ID_OCTPRO,
+		.subvendor	= PCI_SUBVENDOR_ID_SBSMODULARIO,
+		.subdevice	= PCI_SUBDEVICE_ID_OCTPRO232,
+		.init		= sbs_init,
+		.setup		= sbs_setup,
+		.exit		= __devexit_p(sbs_exit),
+	},
+	/*
+	 * SBS Technologies, Inc., PMC-OCTALPRO 422
+	 */
+	{
+		.vendor		= PCI_VENDOR_ID_SBSMODULARIO,
+		.device		= PCI_DEVICE_ID_OCTPRO,
+		.subvendor	= PCI_SUBVENDOR_ID_SBSMODULARIO,
+		.subdevice	= PCI_SUBDEVICE_ID_OCTPRO422,
+		.init		= sbs_init,
+		.setup		= sbs_setup,
+		.exit		= __devexit_p(sbs_exit),
+	},
+	/*
+	 * SBS Technologies, Inc., P-Octal 232
+	 */
+	{
+		.vendor		= PCI_VENDOR_ID_SBSMODULARIO,
+		.device		= PCI_DEVICE_ID_OCTPRO,
+		.subvendor	= PCI_SUBVENDOR_ID_SBSMODULARIO,
+		.subdevice	= PCI_SUBDEVICE_ID_POCTAL232,
+		.init		= sbs_init,
+		.setup		= sbs_setup,
+		.exit		= __devexit_p(sbs_exit),
+	},
+	/*
+	 * SBS Technologies, Inc., P-Octal 422
+	 */
+	{
+		.vendor		= PCI_VENDOR_ID_SBSMODULARIO,
+		.device		= PCI_DEVICE_ID_OCTPRO,
+		.subvendor	= PCI_SUBVENDOR_ID_SBSMODULARIO,
+		.subdevice	= PCI_SUBDEVICE_ID_POCTAL422,
+		.init		= sbs_init,
+		.setup		= sbs_setup,
+		.exit		= __devexit_p(sbs_exit),
+	},
+	/*
+	 * SIIG cards - these may be called via parport_serial
+	 */
+	{
+		.vendor		= PCI_VENDOR_ID_SIIG,
+		.device		= PCI_ANY_ID,
+		.subvendor	= PCI_ANY_ID,
+		.subdevice	= PCI_ANY_ID,
+		.init		= pci_siig_init,
+		.setup		= pci_siig_setup,
+	},
+	/*
+	 * Titan cards
+	 */
+	{
+		.vendor		= PCI_VENDOR_ID_TITAN,
+		.device		= PCI_DEVICE_ID_TITAN_400L,
+		.subvendor	= PCI_ANY_ID,
+		.subdevice	= PCI_ANY_ID,
+		.setup		= titan_400l_800l_setup,
+	},
+	{
+		.vendor		= PCI_VENDOR_ID_TITAN,
+		.device		= PCI_DEVICE_ID_TITAN_800L,
+		.subvendor	= PCI_ANY_ID,
+		.subdevice	= PCI_ANY_ID,
+		.setup		= titan_400l_800l_setup,
+	},
+	/*
+	 * Timedia cards
+	 */
+	{
+		.vendor		= PCI_VENDOR_ID_TIMEDIA,
+		.device		= PCI_DEVICE_ID_TIMEDIA_1889,
+		.subvendor	= PCI_VENDOR_ID_TIMEDIA,
+		.subdevice	= PCI_ANY_ID,
+		.probe		= pci_timedia_probe,
+		.init		= pci_timedia_init,
+		.setup		= pci_timedia_setup,
+	},
+	{
+		.vendor		= PCI_VENDOR_ID_TIMEDIA,
+		.device		= PCI_ANY_ID,
+		.subvendor	= PCI_ANY_ID,
+		.subdevice	= PCI_ANY_ID,
+		.setup		= pci_timedia_setup,
+	},
+	/*
+	 * Exar cards
+	 */
+	{
+		.vendor = PCI_VENDOR_ID_EXAR,
+		.device = PCI_DEVICE_ID_EXAR_XR17C152,
+		.subvendor	= PCI_ANY_ID,
+		.subdevice	= PCI_ANY_ID,
+		.setup		= pci_xr17c154_setup,
+	},
+	{
+		.vendor = PCI_VENDOR_ID_EXAR,
+		.device = PCI_DEVICE_ID_EXAR_XR17C154,
+		.subvendor	= PCI_ANY_ID,
+		.subdevice	= PCI_ANY_ID,
+		.setup		= pci_xr17c154_setup,
+	},
+	{
+		.vendor = PCI_VENDOR_ID_EXAR,
+		.device = PCI_DEVICE_ID_EXAR_XR17C158,
+		.subvendor	= PCI_ANY_ID,
+		.subdevice	= PCI_ANY_ID,
+		.setup		= pci_xr17c154_setup,
+	},
+	/*
+	 * Xircom cards
+	 */
+	{
+		.vendor		= PCI_VENDOR_ID_XIRCOM,
+		.device		= PCI_DEVICE_ID_XIRCOM_X3201_MDM,
+		.subvendor	= PCI_ANY_ID,
+		.subdevice	= PCI_ANY_ID,
+		.init		= pci_xircom_init,
+		.setup		= pci_default_setup,
+	},
+	/*
+	 * Netmos cards - these may be called via parport_serial
+	 */
+	{
+		.vendor		= PCI_VENDOR_ID_NETMOS,
+		.device		= PCI_ANY_ID,
+		.subvendor	= PCI_ANY_ID,
+		.subdevice	= PCI_ANY_ID,
+		.init		= pci_netmos_init,
+		.setup		= pci_netmos_9900_setup,
+	},
+	/*
+	 * For Oxford Semiconductor Tornado based devices
+	 */
+	{
+		.vendor		= PCI_VENDOR_ID_OXSEMI,
+		.device		= PCI_ANY_ID,
+		.subvendor	= PCI_ANY_ID,
+		.subdevice	= PCI_ANY_ID,
+		.init		= pci_oxsemi_tornado_init,
+		.setup		= pci_default_setup,
+	},
+	{
+		.vendor		= PCI_VENDOR_ID_MAINPINE,
+		.device		= PCI_ANY_ID,
+		.subvendor	= PCI_ANY_ID,
+		.subdevice	= PCI_ANY_ID,
+		.init		= pci_oxsemi_tornado_init,
+		.setup		= pci_default_setup,
+	},
+	{
+		.vendor		= PCI_VENDOR_ID_DIGI,
+		.device		= PCIE_DEVICE_ID_NEO_2_OX_IBM,
+		.subvendor		= PCI_SUBVENDOR_ID_IBM,
+		.subdevice		= PCI_ANY_ID,
+		.init			= pci_oxsemi_tornado_init,
+		.setup		= pci_default_setup,
+	},
+	{
+		.vendor         = PCI_VENDOR_ID_INTEL,
+		.device         = 0x8811,
+		.subvendor	= PCI_ANY_ID,
+		.subdevice	= PCI_ANY_ID,
+		.init		= pci_eg20t_init,
+		.setup		= pci_default_setup,
+	},
+	{
+		.vendor         = PCI_VENDOR_ID_INTEL,
+		.device         = 0x8812,
+		.subvendor	= PCI_ANY_ID,
+		.subdevice	= PCI_ANY_ID,
+		.init		= pci_eg20t_init,
+		.setup		= pci_default_setup,
+	},
+	{
+		.vendor         = PCI_VENDOR_ID_INTEL,
+		.device         = 0x8813,
+		.subvendor	= PCI_ANY_ID,
+		.subdevice	= PCI_ANY_ID,
+		.init		= pci_eg20t_init,
+		.setup		= pci_default_setup,
+	},
+	{
+		.vendor         = PCI_VENDOR_ID_INTEL,
+		.device         = 0x8814,
+		.subvendor	= PCI_ANY_ID,
+		.subdevice	= PCI_ANY_ID,
+		.init		= pci_eg20t_init,
+		.setup		= pci_default_setup,
+	},
+	{
+		.vendor         = 0x10DB,
+		.device         = 0x8027,
+		.subvendor	= PCI_ANY_ID,
+		.subdevice	= PCI_ANY_ID,
+		.init		= pci_eg20t_init,
+		.setup		= pci_default_setup,
+	},
+	{
+		.vendor         = 0x10DB,
+		.device         = 0x8028,
+		.subvendor	= PCI_ANY_ID,
+		.subdevice	= PCI_ANY_ID,
+		.init		= pci_eg20t_init,
+		.setup		= pci_default_setup,
+	},
+	{
+		.vendor         = 0x10DB,
+		.device         = 0x8029,
+		.subvendor	= PCI_ANY_ID,
+		.subdevice	= PCI_ANY_ID,
+		.init		= pci_eg20t_init,
+		.setup		= pci_default_setup,
+	},
+	{
+		.vendor         = 0x10DB,
+		.device         = 0x800C,
+		.subvendor	= PCI_ANY_ID,
+		.subdevice	= PCI_ANY_ID,
+		.init		= pci_eg20t_init,
+		.setup		= pci_default_setup,
+	},
+	{
+		.vendor         = 0x10DB,
+		.device         = 0x800D,
+		.subvendor	= PCI_ANY_ID,
+		.subdevice	= PCI_ANY_ID,
+		.init		= pci_eg20t_init,
+		.setup		= pci_default_setup,
+	},
+	{
+		.vendor		= PCI_VENDOR_ID_INTEL,
+		.device		= PCI_DEVICE_ID_INTEL_QRK_UART,
+		.subvendor	= PCI_ANY_ID,
+		.subdevice	= PCI_ANY_ID,
+		.setup		= pci_default_setup,
+	},
+	/*
+	 * Cronyx Omega PCI (PLX-chip based)
+	 */
+	{
+		.vendor		= PCI_VENDOR_ID_PLX,
+		.device		= PCI_DEVICE_ID_PLX_CRONYX_OMEGA,
+		.subvendor	= PCI_ANY_ID,
+		.subdevice	= PCI_ANY_ID,
+		.setup		= pci_omegapci_setup,
+	 },
+	/*
+	 * Broadcom TruManage (NetXtreme)
+	 */
+	{
+		.vendor		= PCI_VENDOR_ID_BROADCOM,
+		.device		= PCI_DEVICE_ID_BROADCOM_TRUMANAGE,
+		.subvendor	= PCI_ANY_ID,
+		.subdevice	= PCI_ANY_ID,
+		.setup		= pci_brcm_trumanage_setup,
+	},
+
+	/*
+	 * Default "match everything" terminator entry
+	 */
+	{
+		.vendor		= PCI_ANY_ID,
+		.device		= PCI_ANY_ID,
+		.subvendor	= PCI_ANY_ID,
+		.subdevice	= PCI_ANY_ID,
+		.setup		= pci_default_setup,
+	}
+};
+
+static inline int quirk_id_matches(u32 quirk_id, u32 dev_id)
+{
+	return quirk_id == PCI_ANY_ID || quirk_id == dev_id;
+}
+
+static struct pci_serial_quirk *find_quirk(struct pci_dev *dev)
+{
+	struct pci_serial_quirk *quirk;
+
+	for (quirk = pci_serial_quirks; ; quirk++)
+		if (quirk_id_matches(quirk->vendor, dev->vendor) &&
+		    quirk_id_matches(quirk->device, dev->device) &&
+		    quirk_id_matches(quirk->subvendor, dev->subsystem_vendor) &&
+		    quirk_id_matches(quirk->subdevice, dev->subsystem_device))
+			break;
+	return quirk;
+}
+
+static inline int get_pci_irq(struct pci_dev *dev,
+				const struct pciserial_board *board)
+{
+	if (board->flags & FL_NOIRQ)
+		return 0;
+	else
+		return dev->irq;
+}
+
+/*
+ * This is the configuration table for all of the PCI serial boards
+ * which we support.  It is directly indexed by the pci_board_num_t enum
+ * value, which is encoded in the pci_device_id PCI probe table's
+ * driver_data member.
+ *
+ * The makeup of these names are:
+ *  pbn_bn{_bt}_n_baud{_offsetinhex}
+ *
+ *  bn		= PCI BAR number
+ *  bt		= Index using PCI BARs
+ *  n		= number of serial ports
+ *  baud	= baud rate
+ *  offsetinhex	= offset for each sequential port (in hex)
+ *
+ * This table is sorted by (in order): bn, bt, baud, offsetindex, n.
+ *
+ * Please note: in theory if n = 1, _bt infix should make no difference.
+ * ie, pbn_b0_1_115200 is the same as pbn_b0_bt_1_115200
+ */
+enum pci_board_num_t {
+	pbn_default = 0,
+
+	pbn_b0_1_115200,
+	pbn_b0_2_115200,
+	pbn_b0_4_115200,
+	pbn_b0_5_115200,
+	pbn_b0_8_115200,
+
+	pbn_b0_1_921600,
+	pbn_b0_2_921600,
+	pbn_b0_4_921600,
+
+	pbn_b0_2_1130000,
+
+	pbn_b0_4_1152000,
+
+	pbn_b0_2_1843200,
+	pbn_b0_4_1843200,
+
+	pbn_b0_2_1843200_200,
+	pbn_b0_4_1843200_200,
+	pbn_b0_8_1843200_200,
+
+	pbn_b0_1_4000000,
+
+	pbn_b0_bt_1_115200,
+	pbn_b0_bt_2_115200,
+	pbn_b0_bt_4_115200,
+	pbn_b0_bt_8_115200,
+
+	pbn_b0_bt_1_460800,
+	pbn_b0_bt_2_460800,
+	pbn_b0_bt_4_460800,
+
+	pbn_b0_bt_1_921600,
+	pbn_b0_bt_2_921600,
+	pbn_b0_bt_4_921600,
+	pbn_b0_bt_8_921600,
+
+	pbn_b1_1_115200,
+	pbn_b1_2_115200,
+	pbn_b1_4_115200,
+	pbn_b1_8_115200,
+	pbn_b1_16_115200,
+
+	pbn_b1_1_921600,
+	pbn_b1_2_921600,
+	pbn_b1_4_921600,
+	pbn_b1_8_921600,
+
+	pbn_b1_2_1250000,
+
+	pbn_b1_bt_1_115200,
+	pbn_b1_bt_2_115200,
+	pbn_b1_bt_4_115200,
+
+	pbn_b1_bt_2_921600,
+
+	pbn_b1_1_1382400,
+	pbn_b1_2_1382400,
+	pbn_b1_4_1382400,
+	pbn_b1_8_1382400,
+
+	pbn_b2_1_115200,
+	pbn_b2_2_115200,
+	pbn_b2_4_115200,
+	pbn_b2_8_115200,
+
+	pbn_b2_1_460800,
+	pbn_b2_4_460800,
+	pbn_b2_8_460800,
+	pbn_b2_16_460800,
+
+	pbn_b2_1_921600,
+	pbn_b2_4_921600,
+	pbn_b2_8_921600,
+
+	pbn_b2_8_1152000,
+
+	pbn_b2_bt_1_115200,
+	pbn_b2_bt_2_115200,
+	pbn_b2_bt_4_115200,
+
+	pbn_b2_bt_2_921600,
+	pbn_b2_bt_4_921600,
+
+	pbn_b3_2_115200,
+	pbn_b3_4_115200,
+	pbn_b3_8_115200,
+
+	pbn_b4_bt_2_921600,
+	pbn_b4_bt_4_921600,
+	pbn_b4_bt_8_921600,
+
+	/*
+	 * Board-specific versions.
+	 */
+	pbn_panacom,
+	pbn_panacom2,
+	pbn_panacom4,
+	pbn_exsys_4055,
+	pbn_plx_romulus,
+	pbn_oxsemi,
+	pbn_oxsemi_1_4000000,
+	pbn_oxsemi_2_4000000,
+	pbn_oxsemi_4_4000000,
+	pbn_oxsemi_8_4000000,
+	pbn_intel_i960,
+	pbn_sgi_ioc3,
+	pbn_computone_4,
+	pbn_computone_6,
+	pbn_computone_8,
+	pbn_sbsxrsio,
+	pbn_exar_XR17C152,
+	pbn_exar_XR17C154,
+	pbn_exar_XR17C158,
+	pbn_exar_ibm_saturn,
+	pbn_pasemi_1682M,
+	pbn_ni8430_2,
+	pbn_ni8430_4,
+	pbn_ni8430_8,
+	pbn_ni8430_16,
+	pbn_ADDIDATA_PCIe_1_3906250,
+	pbn_ADDIDATA_PCIe_2_3906250,
+	pbn_ADDIDATA_PCIe_4_3906250,
+	pbn_ADDIDATA_PCIe_8_3906250,
+	pbn_ce4100_1_115200,
+	pbn_qrk,
+	pbn_omegapci,
+	pbn_NETMOS9900_2s_115200,
+	pbn_brcm_trumanage,
+};
+
+/*
+ * uart_offset - the space between channels
+ * reg_shift   - describes how the UART registers are mapped
+ *               to PCI memory by the card.
+ * For example IER register on SBS, Inc. PMC-OctPro is located at
+ * offset 0x10 from the UART base, while UART_IER is defined as 1
+ * in include/linux/serial_reg.h,
+ * see first lines of serial_in() and serial_out() in 8250.c
+*/
+
+static struct pciserial_board pci_boards[] __devinitdata = {
+	[pbn_default] = {
+		.flags		= FL_BASE0,
+		.num_ports	= 1,
+		.base_baud	= 115200,
+		.uart_offset	= 8,
+	},
+	[pbn_b0_1_115200] = {
+		.flags		= FL_BASE0,
+		.num_ports	= 1,
+		.base_baud	= 115200,
+		.uart_offset	= 8,
+	},
+	[pbn_b0_2_115200] = {
+		.flags		= FL_BASE0,
+		.num_ports	= 2,
+		.base_baud	= 115200,
+		.uart_offset	= 8,
+	},
+	[pbn_b0_4_115200] = {
+		.flags		= FL_BASE0,
+		.num_ports	= 4,
+		.base_baud	= 115200,
+		.uart_offset	= 8,
+	},
+	[pbn_b0_5_115200] = {
+		.flags		= FL_BASE0,
+		.num_ports	= 5,
+		.base_baud	= 115200,
+		.uart_offset	= 8,
+	},
+	[pbn_b0_8_115200] = {
+		.flags		= FL_BASE0,
+		.num_ports	= 8,
+		.base_baud	= 115200,
+		.uart_offset	= 8,
+	},
+	[pbn_b0_1_921600] = {
+		.flags		= FL_BASE0,
+		.num_ports	= 1,
+		.base_baud	= 921600,
+		.uart_offset	= 8,
+	},
+	[pbn_b0_2_921600] = {
+		.flags		= FL_BASE0,
+		.num_ports	= 2,
+		.base_baud	= 921600,
+		.uart_offset	= 8,
+	},
+	[pbn_b0_4_921600] = {
+		.flags		= FL_BASE0,
+		.num_ports	= 4,
+		.base_baud	= 921600,
+		.uart_offset	= 8,
+	},
+
+	[pbn_b0_2_1130000] = {
+		.flags          = FL_BASE0,
+		.num_ports      = 2,
+		.base_baud      = 1130000,
+		.uart_offset    = 8,
+	},
+
+	[pbn_b0_4_1152000] = {
+		.flags		= FL_BASE0,
+		.num_ports	= 4,
+		.base_baud	= 1152000,
+		.uart_offset	= 8,
+	},
+
+	[pbn_b0_2_1843200] = {
+		.flags		= FL_BASE0,
+		.num_ports	= 2,
+		.base_baud	= 1843200,
+		.uart_offset	= 8,
+	},
+	[pbn_b0_4_1843200] = {
+		.flags		= FL_BASE0,
+		.num_ports	= 4,
+		.base_baud	= 1843200,
+		.uart_offset	= 8,
+	},
+
+	[pbn_b0_2_1843200_200] = {
+		.flags		= FL_BASE0,
+		.num_ports	= 2,
+		.base_baud	= 1843200,
+		.uart_offset	= 0x200,
+	},
+	[pbn_b0_4_1843200_200] = {
+		.flags		= FL_BASE0,
+		.num_ports	= 4,
+		.base_baud	= 1843200,
+		.uart_offset	= 0x200,
+	},
+	[pbn_b0_8_1843200_200] = {
+		.flags		= FL_BASE0,
+		.num_ports	= 8,
+		.base_baud	= 1843200,
+		.uart_offset	= 0x200,
+	},
+	[pbn_b0_1_4000000] = {
+		.flags		= FL_BASE0,
+		.num_ports	= 1,
+		.base_baud	= 4000000,
+		.uart_offset	= 8,
+	},
+
+	[pbn_b0_bt_1_115200] = {
+		.flags		= FL_BASE0|FL_BASE_BARS,
+		.num_ports	= 1,
+		.base_baud	= 115200,
+		.uart_offset	= 8,
+	},
+	[pbn_b0_bt_2_115200] = {
+		.flags		= FL_BASE0|FL_BASE_BARS,
+		.num_ports	= 2,
+		.base_baud	= 115200,
+		.uart_offset	= 8,
+	},
+	[pbn_b0_bt_4_115200] = {
+		.flags		= FL_BASE0|FL_BASE_BARS,
+		.num_ports	= 4,
+		.base_baud	= 115200,
+		.uart_offset	= 8,
+	},
+	[pbn_b0_bt_8_115200] = {
+		.flags		= FL_BASE0|FL_BASE_BARS,
+		.num_ports	= 8,
+		.base_baud	= 115200,
+		.uart_offset	= 8,
+	},
+
+	[pbn_b0_bt_1_460800] = {
+		.flags		= FL_BASE0|FL_BASE_BARS,
+		.num_ports	= 1,
+		.base_baud	= 460800,
+		.uart_offset	= 8,
+	},
+	[pbn_b0_bt_2_460800] = {
+		.flags		= FL_BASE0|FL_BASE_BARS,
+		.num_ports	= 2,
+		.base_baud	= 460800,
+		.uart_offset	= 8,
+	},
+	[pbn_b0_bt_4_460800] = {
+		.flags		= FL_BASE0|FL_BASE_BARS,
+		.num_ports	= 4,
+		.base_baud	= 460800,
+		.uart_offset	= 8,
+	},
+
+	[pbn_b0_bt_1_921600] = {
+		.flags		= FL_BASE0|FL_BASE_BARS,
+		.num_ports	= 1,
+		.base_baud	= 921600,
+		.uart_offset	= 8,
+	},
+	[pbn_b0_bt_2_921600] = {
+		.flags		= FL_BASE0|FL_BASE_BARS,
+		.num_ports	= 2,
+		.base_baud	= 921600,
+		.uart_offset	= 8,
+	},
+	[pbn_b0_bt_4_921600] = {
+		.flags		= FL_BASE0|FL_BASE_BARS,
+		.num_ports	= 4,
+		.base_baud	= 921600,
+		.uart_offset	= 8,
+	},
+	[pbn_b0_bt_8_921600] = {
+		.flags		= FL_BASE0|FL_BASE_BARS,
+		.num_ports	= 8,
+		.base_baud	= 921600,
+		.uart_offset	= 8,
+	},
+
+	[pbn_b1_1_115200] = {
+		.flags		= FL_BASE1,
+		.num_ports	= 1,
+		.base_baud	= 115200,
+		.uart_offset	= 8,
+	},
+	[pbn_b1_2_115200] = {
+		.flags		= FL_BASE1,
+		.num_ports	= 2,
+		.base_baud	= 115200,
+		.uart_offset	= 8,
+	},
+	[pbn_b1_4_115200] = {
+		.flags		= FL_BASE1,
+		.num_ports	= 4,
+		.base_baud	= 115200,
+		.uart_offset	= 8,
+	},
+	[pbn_b1_8_115200] = {
+		.flags		= FL_BASE1,
+		.num_ports	= 8,
+		.base_baud	= 115200,
+		.uart_offset	= 8,
+	},
+	[pbn_b1_16_115200] = {
+		.flags		= FL_BASE1,
+		.num_ports	= 16,
+		.base_baud	= 115200,
+		.uart_offset	= 8,
+	},
+
+	[pbn_b1_1_921600] = {
+		.flags		= FL_BASE1,
+		.num_ports	= 1,
+		.base_baud	= 921600,
+		.uart_offset	= 8,
+	},
+	[pbn_b1_2_921600] = {
+		.flags		= FL_BASE1,
+		.num_ports	= 2,
+		.base_baud	= 921600,
+		.uart_offset	= 8,
+	},
+	[pbn_b1_4_921600] = {
+		.flags		= FL_BASE1,
+		.num_ports	= 4,
+		.base_baud	= 921600,
+		.uart_offset	= 8,
+	},
+	[pbn_b1_8_921600] = {
+		.flags		= FL_BASE1,
+		.num_ports	= 8,
+		.base_baud	= 921600,
+		.uart_offset	= 8,
+	},
+	[pbn_b1_2_1250000] = {
+		.flags		= FL_BASE1,
+		.num_ports	= 2,
+		.base_baud	= 1250000,
+		.uart_offset	= 8,
+	},
+
+	[pbn_b1_bt_1_115200] = {
+		.flags		= FL_BASE1|FL_BASE_BARS,
+		.num_ports	= 1,
+		.base_baud	= 115200,
+		.uart_offset	= 8,
+	},
+	[pbn_b1_bt_2_115200] = {
+		.flags		= FL_BASE1|FL_BASE_BARS,
+		.num_ports	= 2,
+		.base_baud	= 115200,
+		.uart_offset	= 8,
+	},
+	[pbn_b1_bt_4_115200] = {
+		.flags		= FL_BASE1|FL_BASE_BARS,
+		.num_ports	= 4,
+		.base_baud	= 115200,
+		.uart_offset	= 8,
+	},
+
+	[pbn_b1_bt_2_921600] = {
+		.flags		= FL_BASE1|FL_BASE_BARS,
+		.num_ports	= 2,
+		.base_baud	= 921600,
+		.uart_offset	= 8,
+	},
+
+	[pbn_b1_1_1382400] = {
+		.flags		= FL_BASE1,
+		.num_ports	= 1,
+		.base_baud	= 1382400,
+		.uart_offset	= 8,
+	},
+	[pbn_b1_2_1382400] = {
+		.flags		= FL_BASE1,
+		.num_ports	= 2,
+		.base_baud	= 1382400,
+		.uart_offset	= 8,
+	},
+	[pbn_b1_4_1382400] = {
+		.flags		= FL_BASE1,
+		.num_ports	= 4,
+		.base_baud	= 1382400,
+		.uart_offset	= 8,
+	},
+	[pbn_b1_8_1382400] = {
+		.flags		= FL_BASE1,
+		.num_ports	= 8,
+		.base_baud	= 1382400,
+		.uart_offset	= 8,
+	},
+
+	[pbn_b2_1_115200] = {
+		.flags		= FL_BASE2,
+		.num_ports	= 1,
+		.base_baud	= 115200,
+		.uart_offset	= 8,
+	},
+	[pbn_b2_2_115200] = {
+		.flags		= FL_BASE2,
+		.num_ports	= 2,
+		.base_baud	= 115200,
+		.uart_offset	= 8,
+	},
+	[pbn_b2_4_115200] = {
+		.flags          = FL_BASE2,
+		.num_ports      = 4,
+		.base_baud      = 115200,
+		.uart_offset    = 8,
+	},
+	[pbn_b2_8_115200] = {
+		.flags		= FL_BASE2,
+		.num_ports	= 8,
+		.base_baud	= 115200,
+		.uart_offset	= 8,
+	},
+
+	[pbn_b2_1_460800] = {
+		.flags		= FL_BASE2,
+		.num_ports	= 1,
+		.base_baud	= 460800,
+		.uart_offset	= 8,
+	},
+	[pbn_b2_4_460800] = {
+		.flags		= FL_BASE2,
+		.num_ports	= 4,
+		.base_baud	= 460800,
+		.uart_offset	= 8,
+	},
+	[pbn_b2_8_460800] = {
+		.flags		= FL_BASE2,
+		.num_ports	= 8,
+		.base_baud	= 460800,
+		.uart_offset	= 8,
+	},
+	[pbn_b2_16_460800] = {
+		.flags		= FL_BASE2,
+		.num_ports	= 16,
+		.base_baud	= 460800,
+		.uart_offset	= 8,
+	 },
+
+	[pbn_b2_1_921600] = {
+		.flags		= FL_BASE2,
+		.num_ports	= 1,
+		.base_baud	= 921600,
+		.uart_offset	= 8,
+	},
+	[pbn_b2_4_921600] = {
+		.flags		= FL_BASE2,
+		.num_ports	= 4,
+		.base_baud	= 921600,
+		.uart_offset	= 8,
+	},
+	[pbn_b2_8_921600] = {
+		.flags		= FL_BASE2,
+		.num_ports	= 8,
+		.base_baud	= 921600,
+		.uart_offset	= 8,
+	},
+
+	[pbn_b2_8_1152000] = {
+		.flags		= FL_BASE2,
+		.num_ports	= 8,
+		.base_baud	= 1152000,
+		.uart_offset	= 8,
+	},
+
+	[pbn_b2_bt_1_115200] = {
+		.flags		= FL_BASE2|FL_BASE_BARS,
+		.num_ports	= 1,
+		.base_baud	= 115200,
+		.uart_offset	= 8,
+	},
+	[pbn_b2_bt_2_115200] = {
+		.flags		= FL_BASE2|FL_BASE_BARS,
+		.num_ports	= 2,
+		.base_baud	= 115200,
+		.uart_offset	= 8,
+	},
+	[pbn_b2_bt_4_115200] = {
+		.flags		= FL_BASE2|FL_BASE_BARS,
+		.num_ports	= 4,
+		.base_baud	= 115200,
+		.uart_offset	= 8,
+	},
+
+	[pbn_b2_bt_2_921600] = {
+		.flags		= FL_BASE2|FL_BASE_BARS,
+		.num_ports	= 2,
+		.base_baud	= 921600,
+		.uart_offset	= 8,
+	},
+	[pbn_b2_bt_4_921600] = {
+		.flags		= FL_BASE2|FL_BASE_BARS,
+		.num_ports	= 4,
+		.base_baud	= 921600,
+		.uart_offset	= 8,
+	},
+
+	[pbn_b3_2_115200] = {
+		.flags		= FL_BASE3,
+		.num_ports	= 2,
+		.base_baud	= 115200,
+		.uart_offset	= 8,
+	},
+	[pbn_b3_4_115200] = {
+		.flags		= FL_BASE3,
+		.num_ports	= 4,
+		.base_baud	= 115200,
+		.uart_offset	= 8,
+	},
+	[pbn_b3_8_115200] = {
+		.flags		= FL_BASE3,
+		.num_ports	= 8,
+		.base_baud	= 115200,
+		.uart_offset	= 8,
+	},
+
+	[pbn_b4_bt_2_921600] = {
+		.flags		= FL_BASE4,
+		.num_ports	= 2,
+		.base_baud	= 921600,
+		.uart_offset	= 8,
+	},
+	[pbn_b4_bt_4_921600] = {
+		.flags		= FL_BASE4,
+		.num_ports	= 4,
+		.base_baud	= 921600,
+		.uart_offset	= 8,
+	},
+	[pbn_b4_bt_8_921600] = {
+		.flags		= FL_BASE4,
+		.num_ports	= 8,
+		.base_baud	= 921600,
+		.uart_offset	= 8,
+	},
+
+	/*
+	 * Entries following this are board-specific.
+	 */
+
+	/*
+	 * Panacom - IOMEM
+	 */
+	[pbn_panacom] = {
+		.flags		= FL_BASE2,
+		.num_ports	= 2,
+		.base_baud	= 921600,
+		.uart_offset	= 0x400,
+		.reg_shift	= 7,
+	},
+	[pbn_panacom2] = {
+		.flags		= FL_BASE2|FL_BASE_BARS,
+		.num_ports	= 2,
+		.base_baud	= 921600,
+		.uart_offset	= 0x400,
+		.reg_shift	= 7,
+	},
+	[pbn_panacom4] = {
+		.flags		= FL_BASE2|FL_BASE_BARS,
+		.num_ports	= 4,
+		.base_baud	= 921600,
+		.uart_offset	= 0x400,
+		.reg_shift	= 7,
+	},
+
+	[pbn_exsys_4055] = {
+		.flags		= FL_BASE2,
+		.num_ports	= 4,
+		.base_baud	= 115200,
+		.uart_offset	= 8,
+	},
+
+	/* I think this entry is broken - the first_offset looks wrong --rmk */
+	[pbn_plx_romulus] = {
+		.flags		= FL_BASE2,
+		.num_ports	= 4,
+		.base_baud	= 921600,
+		.uart_offset	= 8 << 2,
+		.reg_shift	= 2,
+		.first_offset	= 0x03,
+	},
+
+	/*
+	 * This board uses the size of PCI Base region 0 to
+	 * signal now many ports are available
+	 */
+	[pbn_oxsemi] = {
+		.flags		= FL_BASE0|FL_REGION_SZ_CAP,
+		.num_ports	= 32,
+		.base_baud	= 115200,
+		.uart_offset	= 8,
+	},
+	[pbn_oxsemi_1_4000000] = {
+		.flags		= FL_BASE0,
+		.num_ports	= 1,
+		.base_baud	= 4000000,
+		.uart_offset	= 0x200,
+		.first_offset	= 0x1000,
+	},
+	[pbn_oxsemi_2_4000000] = {
+		.flags		= FL_BASE0,
+		.num_ports	= 2,
+		.base_baud	= 4000000,
+		.uart_offset	= 0x200,
+		.first_offset	= 0x1000,
+	},
+	[pbn_oxsemi_4_4000000] = {
+		.flags		= FL_BASE0,
+		.num_ports	= 4,
+		.base_baud	= 4000000,
+		.uart_offset	= 0x200,
+		.first_offset	= 0x1000,
+	},
+	[pbn_oxsemi_8_4000000] = {
+		.flags		= FL_BASE0,
+		.num_ports	= 8,
+		.base_baud	= 4000000,
+		.uart_offset	= 0x200,
+		.first_offset	= 0x1000,
+	},
+
+
+	/*
+	 * EKF addition for i960 Boards form EKF with serial port.
+	 * Max 256 ports.
+	 */
+	[pbn_intel_i960] = {
+		.flags		= FL_BASE0,
+		.num_ports	= 32,
+		.base_baud	= 921600,
+		.uart_offset	= 8 << 2,
+		.reg_shift	= 2,
+		.first_offset	= 0x10000,
+	},
+	[pbn_sgi_ioc3] = {
+		.flags		= FL_BASE0|FL_NOIRQ,
+		.num_ports	= 1,
+		.base_baud	= 458333,
+		.uart_offset	= 8,
+		.reg_shift	= 0,
+		.first_offset	= 0x20178,
+	},
+
+	/*
+	 * Computone - uses IOMEM.
+	 */
+	[pbn_computone_4] = {
+		.flags		= FL_BASE0,
+		.num_ports	= 4,
+		.base_baud	= 921600,
+		.uart_offset	= 0x40,
+		.reg_shift	= 2,
+		.first_offset	= 0x200,
+	},
+	[pbn_computone_6] = {
+		.flags		= FL_BASE0,
+		.num_ports	= 6,
+		.base_baud	= 921600,
+		.uart_offset	= 0x40,
+		.reg_shift	= 2,
+		.first_offset	= 0x200,
+	},
+	[pbn_computone_8] = {
+		.flags		= FL_BASE0,
+		.num_ports	= 8,
+		.base_baud	= 921600,
+		.uart_offset	= 0x40,
+		.reg_shift	= 2,
+		.first_offset	= 0x200,
+	},
+	[pbn_sbsxrsio] = {
+		.flags		= FL_BASE0,
+		.num_ports	= 8,
+		.base_baud	= 460800,
+		.uart_offset	= 256,
+		.reg_shift	= 4,
+	},
+	/*
+	 * Exar Corp. XR17C15[248] Dual/Quad/Octal UART
+	 *  Only basic 16550A support.
+	 *  XR17C15[24] are not tested, but they should work.
+	 */
+	[pbn_exar_XR17C152] = {
+		.flags		= FL_BASE0,
+		.num_ports	= 2,
+		.base_baud	= 921600,
+		.uart_offset	= 0x200,
+	},
+	[pbn_exar_XR17C154] = {
+		.flags		= FL_BASE0,
+		.num_ports	= 4,
+		.base_baud	= 921600,
+		.uart_offset	= 0x200,
+	},
+	[pbn_exar_XR17C158] = {
+		.flags		= FL_BASE0,
+		.num_ports	= 8,
+		.base_baud	= 921600,
+		.uart_offset	= 0x200,
+	},
+	[pbn_exar_ibm_saturn] = {
+		.flags		= FL_BASE0,
+		.num_ports	= 1,
+		.base_baud	= 921600,
+		.uart_offset	= 0x200,
+	},
+
+	/*
+	 * PA Semi PWRficient PA6T-1682M on-chip UART
+	 */
+	[pbn_pasemi_1682M] = {
+		.flags		= FL_BASE0,
+		.num_ports	= 1,
+		.base_baud	= 8333333,
+	},
+	/*
+	 * National Instruments 843x
+	 */
+	[pbn_ni8430_16] = {
+		.flags		= FL_BASE0,
+		.num_ports	= 16,
+		.base_baud	= 3686400,
+		.uart_offset	= 0x10,
+		.first_offset	= 0x800,
+	},
+	[pbn_ni8430_8] = {
+		.flags		= FL_BASE0,
+		.num_ports	= 8,
+		.base_baud	= 3686400,
+		.uart_offset	= 0x10,
+		.first_offset	= 0x800,
+	},
+	[pbn_ni8430_4] = {
+		.flags		= FL_BASE0,
+		.num_ports	= 4,
+		.base_baud	= 3686400,
+		.uart_offset	= 0x10,
+		.first_offset	= 0x800,
+	},
+	[pbn_ni8430_2] = {
+		.flags		= FL_BASE0,
+		.num_ports	= 2,
+		.base_baud	= 3686400,
+		.uart_offset	= 0x10,
+		.first_offset	= 0x800,
+	},
+	/*
+	 * ADDI-DATA GmbH PCI-Express communication cards <info@addi-data.com>
+	 */
+	[pbn_ADDIDATA_PCIe_1_3906250] = {
+		.flags		= FL_BASE0,
+		.num_ports	= 1,
+		.base_baud	= 3906250,
+		.uart_offset	= 0x200,
+		.first_offset	= 0x1000,
+	},
+	[pbn_ADDIDATA_PCIe_2_3906250] = {
+		.flags		= FL_BASE0,
+		.num_ports	= 2,
+		.base_baud	= 3906250,
+		.uart_offset	= 0x200,
+		.first_offset	= 0x1000,
+	},
+	[pbn_ADDIDATA_PCIe_4_3906250] = {
+		.flags		= FL_BASE0,
+		.num_ports	= 4,
+		.base_baud	= 3906250,
+		.uart_offset	= 0x200,
+		.first_offset	= 0x1000,
+	},
+	[pbn_ADDIDATA_PCIe_8_3906250] = {
+		.flags		= FL_BASE0,
+		.num_ports	= 8,
+		.base_baud	= 3906250,
+		.uart_offset	= 0x200,
+		.first_offset	= 0x1000,
+	},
+	[pbn_ce4100_1_115200] = {
+		.flags		= FL_BASE0,
+		.num_ports	= 1,
+		.base_baud	= 921600,
+		.reg_shift      = 2,
+	},
+	[pbn_qrk] = {
+		.flags		= FL_BASE0,
+		.num_ports	= 1,
+		.base_baud	= 2764800,
+		.reg_shift	= 2,
+	},
+	[pbn_omegapci] = {
+		.flags		= FL_BASE0,
+		.num_ports	= 8,
+		.base_baud	= 115200,
+		.uart_offset	= 0x200,
+	},
+	[pbn_NETMOS9900_2s_115200] = {
+		.flags		= FL_BASE0,
+		.num_ports	= 2,
+		.base_baud	= 115200,
+	},
+	[pbn_brcm_trumanage] = {
+		.flags		= FL_BASE0,
+		.num_ports	= 1,
+		.reg_shift	= 2,
+		.base_baud	= 115200,
+	},
+};
+
+static const struct pci_device_id softmodem_blacklist[] = {
+	{ PCI_VDEVICE(AL, 0x5457), }, /* ALi Corporation M5457 AC'97 Modem */
+	{ PCI_VDEVICE(MOTOROLA, 0x3052), }, /* Motorola Si3052-based modem */
+	{ PCI_DEVICE(0x1543, 0x3052), }, /* Si3052-based modem, default IDs */
+};
+
+/*
+ * Given a complete unknown PCI device, try to use some heuristics to
+ * guess what the configuration might be, based on the pitiful PCI
+ * serial specs.  Returns 0 on success, 1 on failure.
+ */
+static int __devinit
+serial_pci_guess_board(struct pci_dev *dev, struct pciserial_board *board)
+{
+	const struct pci_device_id *blacklist;
+	int num_iomem, num_port, first_port = -1, i;
+
+	/*
+	 * If it is not a communications device or the programming
+	 * interface is greater than 6, give up.
+	 *
+	 * (Should we try to make guesses for multiport serial devices
+	 * later?)
+	 */
+	if ((((dev->class >> 8) != PCI_CLASS_COMMUNICATION_SERIAL) &&
+	     ((dev->class >> 8) != PCI_CLASS_COMMUNICATION_MODEM)) ||
+	    (dev->class & 0xff) > 6)
+		return -ENODEV;
+
+	/*
+	 * Do not access blacklisted devices that are known not to
+	 * feature serial ports.
+	 */
+	for (blacklist = softmodem_blacklist;
+	     blacklist < softmodem_blacklist + ARRAY_SIZE(softmodem_blacklist);
+	     blacklist++) {
+		if (dev->vendor == blacklist->vendor &&
+		    dev->device == blacklist->device)
+			return -ENODEV;
+	}
+
+	num_iomem = num_port = 0;
+	for (i = 0; i < PCI_NUM_BAR_RESOURCES; i++) {
+		if (pci_resource_flags(dev, i) & IORESOURCE_IO) {
+			num_port++;
+			if (first_port == -1)
+				first_port = i;
+		}
+		if (pci_resource_flags(dev, i) & IORESOURCE_MEM)
+			num_iomem++;
+	}
+
+	/*
+	 * If there is 1 or 0 iomem regions, and exactly one port,
+	 * use it.  We guess the number of ports based on the IO
+	 * region size.
+	 */
+	if (num_iomem <= 1 && num_port == 1) {
+		board->flags = first_port;
+		board->num_ports = pci_resource_len(dev, first_port) / 8;
+		return 0;
+	}
+
+	/*
+	 * Now guess if we've got a board which indexes by BARs.
+	 * Each IO BAR should be 8 bytes, and they should follow
+	 * consecutively.
+	 */
+	first_port = -1;
+	num_port = 0;
+	for (i = 0; i < PCI_NUM_BAR_RESOURCES; i++) {
+		if (pci_resource_flags(dev, i) & IORESOURCE_IO &&
+		    pci_resource_len(dev, i) == 8 &&
+		    (first_port == -1 || (first_port + num_port) == i)) {
+			num_port++;
+			if (first_port == -1)
+				first_port = i;
+		}
+	}
+
+	if (num_port > 1) {
+		board->flags = first_port | FL_BASE_BARS;
+		board->num_ports = num_port;
+		return 0;
+	}
+
+	return -ENODEV;
+}
+
+static inline int
+serial_pci_matches(const struct pciserial_board *board,
+		   const struct pciserial_board *guessed)
+{
+	return
+	    board->num_ports == guessed->num_ports &&
+	    board->base_baud == guessed->base_baud &&
+	    board->uart_offset == guessed->uart_offset &&
+	    board->reg_shift == guessed->reg_shift &&
+	    board->first_offset == guessed->first_offset;
+}
+
+struct serial_private *
+pciserial_init_ports(struct pci_dev *dev, const struct pciserial_board *board)
+{
+	struct uart_port serial_port;
+	struct serial_private *priv;
+	struct pci_serial_quirk *quirk;
+	int rc, nr_ports, i;
+
+	nr_ports = board->num_ports;
+
+	/*
+	 * Find an init and setup quirks.
+	 */
+	quirk = find_quirk(dev);
+
+	/*
+	 * Run the new-style initialization function.
+	 * The initialization function returns:
+	 *  <0  - error
+	 *   0  - use board->num_ports
+	 *  >0  - number of ports
+	 */
+	if (quirk->init) {
+		rc = quirk->init(dev);
+		if (rc < 0) {
+			priv = ERR_PTR(rc);
+			goto err_out;
+		}
+		if (rc)
+			nr_ports = rc;
+	}
+
+	priv = kzalloc(sizeof(struct serial_private) +
+		       sizeof(unsigned int) * nr_ports,
+		       GFP_KERNEL);
+	if (!priv) {
+		priv = ERR_PTR(-ENOMEM);
+		goto err_deinit;
+	}
+
+	priv->dev = dev;
+	priv->quirk = quirk;
+
+	memset(&serial_port, 0, sizeof(struct uart_port));
+	serial_port.flags = UPF_SKIP_TEST | UPF_BOOT_AUTOCONF | UPF_SHARE_IRQ;
+	serial_port.uartclk = board->base_baud * 16;
+	serial_port.irq = get_pci_irq(dev, board);
+	serial_port.dev = &dev->dev;
+
+	for (i = 0; i < nr_ports; i++) {
+		if (quirk->setup(priv, board, &serial_port, i))
+			break;
+
+#ifdef SERIAL_DEBUG_PCI
+		printk(KERN_DEBUG "Setup PCI port: port %lx, irq %d, type %d\n",
+		       serial_port.iobase, serial_port.irq, serial_port.iotype);
+#endif
+
+		priv->line[i] = serial8250_register_port(&serial_port);
+		if (priv->line[i] < 0) {
+			printk(KERN_WARNING "Couldn't register serial port %s: %d\n", pci_name(dev), priv->line[i]);
+			break;
+		}
+	}
+	priv->nr = i;
+	return priv;
+
+err_deinit:
+	if (quirk->exit)
+		quirk->exit(dev);
+err_out:
+	return priv;
+}
+EXPORT_SYMBOL_GPL(pciserial_init_ports);
+
+void pciserial_remove_ports(struct serial_private *priv)
+{
+	struct pci_serial_quirk *quirk;
+	int i;
+
+	for (i = 0; i < priv->nr; i++)
+		serial8250_unregister_port(priv->line[i]);
+
+	for (i = 0; i < PCI_NUM_BAR_RESOURCES; i++) {
+		if (priv->remapped_bar[i])
+			iounmap(priv->remapped_bar[i]);
+		priv->remapped_bar[i] = NULL;
+	}
+
+	/*
+	 * Find the exit quirks.
+	 */
+	quirk = find_quirk(priv->dev);
+	if (quirk->exit)
+		quirk->exit(priv->dev);
+
+	kfree(priv);
+}
+EXPORT_SYMBOL_GPL(pciserial_remove_ports);
+
+void pciserial_suspend_ports(struct serial_private *priv)
+{
+	int i;
+
+	for (i = 0; i < priv->nr; i++)
+		if (priv->line[i] >= 0)
+			serial8250_suspend_port(priv->line[i]);
+}
+EXPORT_SYMBOL_GPL(pciserial_suspend_ports);
+
+void pciserial_resume_ports(struct serial_private *priv)
+{
+	int i;
+
+	/*
+	 * Ensure that the board is correctly configured.
+	 */
+	if (priv->quirk->init)
+		priv->quirk->init(priv->dev);
+
+	for (i = 0; i < priv->nr; i++)
+		if (priv->line[i] >= 0)
+			serial8250_resume_port(priv->line[i]);
+}
+EXPORT_SYMBOL_GPL(pciserial_resume_ports);
+
+/*
+ * Probe one serial board.  Unfortunately, there is no rhyme nor reason
+ * to the arrangement of serial ports on a PCI card.
+ */
+static int __devinit
+pciserial_init_one(struct pci_dev *dev, const struct pci_device_id *ent)
+{
+	struct pci_serial_quirk *quirk;
+	struct serial_private *priv;
+	const struct pciserial_board *board;
+	struct pciserial_board tmp;
+	int rc;
+
+	quirk = find_quirk(dev);
+	if (quirk->probe) {
+		rc = quirk->probe(dev);
+		if (rc)
+			return rc;
+	}
+
+	if (ent->driver_data >= ARRAY_SIZE(pci_boards)) {
+		printk(KERN_ERR "pci_init_one: invalid driver_data: %ld\n",
+			ent->driver_data);
+		return -EINVAL;
+	}
+
+	board = &pci_boards[ent->driver_data];
+
+	rc = pci_enable_device(dev);
+	pci_save_state(dev);
+	if (rc)
+		return rc;
+
+	if (ent->driver_data == pbn_default) {
+		/*
+		 * Use a copy of the pci_board entry for this;
+		 * avoid changing entries in the table.
+		 */
+		memcpy(&tmp, board, sizeof(struct pciserial_board));
+		board = &tmp;
+
+		/*
+		 * We matched one of our class entries.  Try to
+		 * determine the parameters of this board.
+		 */
+		rc = serial_pci_guess_board(dev, &tmp);
+		if (rc)
+			goto disable;
+	} else {
+		/*
+		 * We matched an explicit entry.  If we are able to
+		 * detect this boards settings with our heuristic,
+		 * then we no longer need this entry.
+		 */
+		memcpy(&tmp, &pci_boards[pbn_default],
+		       sizeof(struct pciserial_board));
+		rc = serial_pci_guess_board(dev, &tmp);
+		if (rc == 0 && serial_pci_matches(board, &tmp))
+			moan_device("Redundant entry in serial pci_table.",
+				    dev);
+	}
+
+	priv = pciserial_init_ports(dev, board);
+	if (!IS_ERR(priv)) {
+		pci_set_drvdata(dev, priv);
+		return 0;
+	}
+
+	rc = PTR_ERR(priv);
+
+ disable:
+	pci_disable_device(dev);
+	return rc;
+}
+
+static void __devexit pciserial_remove_one(struct pci_dev *dev)
+{
+	struct serial_private *priv = pci_get_drvdata(dev);
+
+	pci_set_drvdata(dev, NULL);
+
+	pciserial_remove_ports(priv);
+
+	pci_disable_device(dev);
+}
+
+#ifdef CONFIG_PM
+static int pciserial_suspend_one(struct pci_dev *dev, pm_message_t state)
+{
+	struct serial_private *priv = pci_get_drvdata(dev);
+
+	if (priv)
+		pciserial_suspend_ports(priv);
+
+	pci_save_state(dev);
+	pci_set_power_state(dev, pci_choose_state(dev, state));
+	return 0;
+}
+
+static int pciserial_resume_one(struct pci_dev *dev)
+{
+	int err;
+	struct serial_private *priv = pci_get_drvdata(dev);
+
+	pci_set_power_state(dev, PCI_D0);
+	pci_restore_state(dev);
+
+	if (priv) {
+		/*
+		 * The device may have been disabled.  Re-enable it.
+		 */
+		err = pci_enable_device(dev);
+		/* FIXME: We cannot simply error out here */
+		if (err)
+			printk(KERN_ERR "pciserial: Unable to re-enable ports, trying to continue.\n");
+		pciserial_resume_ports(priv);
+	}
+	return 0;
+}
+#endif
+
+static struct pci_device_id serial_pci_tbl[] = {
+	/* Advantech use PCI_DEVICE_ID_ADVANTECH_PCI3620 (0x3620) as 'PCI_SUBVENDOR_ID' */
+	{	PCI_VENDOR_ID_ADVANTECH, PCI_DEVICE_ID_ADVANTECH_PCI3620,
+		PCI_DEVICE_ID_ADVANTECH_PCI3620, 0x0001, 0, 0,
+		pbn_b2_8_921600 },
+	{	PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V960,
+		PCI_SUBVENDOR_ID_CONNECT_TECH,
+		PCI_SUBDEVICE_ID_CONNECT_TECH_BH8_232, 0, 0,
+		pbn_b1_8_1382400 },
+	{	PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V960,
+		PCI_SUBVENDOR_ID_CONNECT_TECH,
+		PCI_SUBDEVICE_ID_CONNECT_TECH_BH4_232, 0, 0,
+		pbn_b1_4_1382400 },
+	{	PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V960,
+		PCI_SUBVENDOR_ID_CONNECT_TECH,
+		PCI_SUBDEVICE_ID_CONNECT_TECH_BH2_232, 0, 0,
+		pbn_b1_2_1382400 },
+	{	PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V351,
+		PCI_SUBVENDOR_ID_CONNECT_TECH,
+		PCI_SUBDEVICE_ID_CONNECT_TECH_BH8_232, 0, 0,
+		pbn_b1_8_1382400 },
+	{	PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V351,
+		PCI_SUBVENDOR_ID_CONNECT_TECH,
+		PCI_SUBDEVICE_ID_CONNECT_TECH_BH4_232, 0, 0,
+		pbn_b1_4_1382400 },
+	{	PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V351,
+		PCI_SUBVENDOR_ID_CONNECT_TECH,
+		PCI_SUBDEVICE_ID_CONNECT_TECH_BH2_232, 0, 0,
+		pbn_b1_2_1382400 },
+	{	PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V351,
+		PCI_SUBVENDOR_ID_CONNECT_TECH,
+		PCI_SUBDEVICE_ID_CONNECT_TECH_BH8_485, 0, 0,
+		pbn_b1_8_921600 },
+	{	PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V351,
+		PCI_SUBVENDOR_ID_CONNECT_TECH,
+		PCI_SUBDEVICE_ID_CONNECT_TECH_BH8_485_4_4, 0, 0,
+		pbn_b1_8_921600 },
+	{	PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V351,
+		PCI_SUBVENDOR_ID_CONNECT_TECH,
+		PCI_SUBDEVICE_ID_CONNECT_TECH_BH4_485, 0, 0,
+		pbn_b1_4_921600 },
+	{	PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V351,
+		PCI_SUBVENDOR_ID_CONNECT_TECH,
+		PCI_SUBDEVICE_ID_CONNECT_TECH_BH4_485_2_2, 0, 0,
+		pbn_b1_4_921600 },
+	{	PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V351,
+		PCI_SUBVENDOR_ID_CONNECT_TECH,
+		PCI_SUBDEVICE_ID_CONNECT_TECH_BH2_485, 0, 0,
+		pbn_b1_2_921600 },
+	{	PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V351,
+		PCI_SUBVENDOR_ID_CONNECT_TECH,
+		PCI_SUBDEVICE_ID_CONNECT_TECH_BH8_485_2_6, 0, 0,
+		pbn_b1_8_921600 },
+	{	PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V351,
+		PCI_SUBVENDOR_ID_CONNECT_TECH,
+		PCI_SUBDEVICE_ID_CONNECT_TECH_BH081101V1, 0, 0,
+		pbn_b1_8_921600 },
+	{	PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V351,
+		PCI_SUBVENDOR_ID_CONNECT_TECH,
+		PCI_SUBDEVICE_ID_CONNECT_TECH_BH041101V1, 0, 0,
+		pbn_b1_4_921600 },
+	{	PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V351,
+		PCI_SUBVENDOR_ID_CONNECT_TECH,
+		PCI_SUBDEVICE_ID_CONNECT_TECH_BH2_20MHZ, 0, 0,
+		pbn_b1_2_1250000 },
+	{	PCI_VENDOR_ID_OXSEMI, PCI_DEVICE_ID_OXSEMI_16PCI954,
+		PCI_SUBVENDOR_ID_CONNECT_TECH,
+		PCI_SUBDEVICE_ID_CONNECT_TECH_TITAN_2, 0, 0,
+		pbn_b0_2_1843200 },
+	{	PCI_VENDOR_ID_OXSEMI, PCI_DEVICE_ID_OXSEMI_16PCI954,
+		PCI_SUBVENDOR_ID_CONNECT_TECH,
+		PCI_SUBDEVICE_ID_CONNECT_TECH_TITAN_4, 0, 0,
+		pbn_b0_4_1843200 },
+	{	PCI_VENDOR_ID_OXSEMI, PCI_DEVICE_ID_OXSEMI_16PCI954,
+		PCI_VENDOR_ID_AFAVLAB,
+		PCI_SUBDEVICE_ID_AFAVLAB_P061, 0, 0,
+		pbn_b0_4_1152000 },
+	{	PCI_VENDOR_ID_EXAR, PCI_DEVICE_ID_EXAR_XR17C152,
+		PCI_SUBVENDOR_ID_CONNECT_TECH,
+		PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_2_232, 0, 0,
+		pbn_b0_2_1843200_200 },
+	{	PCI_VENDOR_ID_EXAR, PCI_DEVICE_ID_EXAR_XR17C154,
+		PCI_SUBVENDOR_ID_CONNECT_TECH,
+		PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_4_232, 0, 0,
+		pbn_b0_4_1843200_200 },
+	{	PCI_VENDOR_ID_EXAR, PCI_DEVICE_ID_EXAR_XR17C158,
+		PCI_SUBVENDOR_ID_CONNECT_TECH,
+		PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_8_232, 0, 0,
+		pbn_b0_8_1843200_200 },
+	{	PCI_VENDOR_ID_EXAR, PCI_DEVICE_ID_EXAR_XR17C152,
+		PCI_SUBVENDOR_ID_CONNECT_TECH,
+		PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_1_1, 0, 0,
+		pbn_b0_2_1843200_200 },
+	{	PCI_VENDOR_ID_EXAR, PCI_DEVICE_ID_EXAR_XR17C154,
+		PCI_SUBVENDOR_ID_CONNECT_TECH,
+		PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_2_2, 0, 0,
+		pbn_b0_4_1843200_200 },
+	{	PCI_VENDOR_ID_EXAR, PCI_DEVICE_ID_EXAR_XR17C158,
+		PCI_SUBVENDOR_ID_CONNECT_TECH,
+		PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_4_4, 0, 0,
+		pbn_b0_8_1843200_200 },
+	{	PCI_VENDOR_ID_EXAR, PCI_DEVICE_ID_EXAR_XR17C152,
+		PCI_SUBVENDOR_ID_CONNECT_TECH,
+		PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_2, 0, 0,
+		pbn_b0_2_1843200_200 },
+	{	PCI_VENDOR_ID_EXAR, PCI_DEVICE_ID_EXAR_XR17C154,
+		PCI_SUBVENDOR_ID_CONNECT_TECH,
+		PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_4, 0, 0,
+		pbn_b0_4_1843200_200 },
+	{	PCI_VENDOR_ID_EXAR, PCI_DEVICE_ID_EXAR_XR17C158,
+		PCI_SUBVENDOR_ID_CONNECT_TECH,
+		PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_8, 0, 0,
+		pbn_b0_8_1843200_200 },
+	{	PCI_VENDOR_ID_EXAR, PCI_DEVICE_ID_EXAR_XR17C152,
+		PCI_SUBVENDOR_ID_CONNECT_TECH,
+		PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_2_485, 0, 0,
+		pbn_b0_2_1843200_200 },
+	{	PCI_VENDOR_ID_EXAR, PCI_DEVICE_ID_EXAR_XR17C154,
+		PCI_SUBVENDOR_ID_CONNECT_TECH,
+		PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_4_485, 0, 0,
+		pbn_b0_4_1843200_200 },
+	{	PCI_VENDOR_ID_EXAR, PCI_DEVICE_ID_EXAR_XR17C158,
+		PCI_SUBVENDOR_ID_CONNECT_TECH,
+		PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_8_485, 0, 0,
+		pbn_b0_8_1843200_200 },
+	{	PCI_VENDOR_ID_EXAR, PCI_DEVICE_ID_EXAR_XR17C152,
+		PCI_VENDOR_ID_IBM, PCI_SUBDEVICE_ID_IBM_SATURN_SERIAL_ONE_PORT,
+		0, 0, pbn_exar_ibm_saturn },
+
+	{	PCI_VENDOR_ID_SEALEVEL, PCI_DEVICE_ID_SEALEVEL_U530,
+		PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+		pbn_b2_bt_1_115200 },
+	{	PCI_VENDOR_ID_SEALEVEL, PCI_DEVICE_ID_SEALEVEL_UCOMM2,
+		PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+		pbn_b2_bt_2_115200 },
+	{	PCI_VENDOR_ID_SEALEVEL, PCI_DEVICE_ID_SEALEVEL_UCOMM422,
+		PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+		pbn_b2_bt_4_115200 },
+	{	PCI_VENDOR_ID_SEALEVEL, PCI_DEVICE_ID_SEALEVEL_UCOMM232,
+		PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+		pbn_b2_bt_2_115200 },
+	{	PCI_VENDOR_ID_SEALEVEL, PCI_DEVICE_ID_SEALEVEL_COMM4,
+		PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+		pbn_b2_bt_4_115200 },
+	{	PCI_VENDOR_ID_SEALEVEL, PCI_DEVICE_ID_SEALEVEL_COMM8,
+		PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+		pbn_b2_8_115200 },
+	{	PCI_VENDOR_ID_SEALEVEL, PCI_DEVICE_ID_SEALEVEL_7803,
+		PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+		pbn_b2_8_460800 },
+	{	PCI_VENDOR_ID_SEALEVEL, PCI_DEVICE_ID_SEALEVEL_UCOMM8,
+		PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+		pbn_b2_8_115200 },
+
+	{	PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_GTEK_SERIAL2,
+		PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+		pbn_b2_bt_2_115200 },
+	{	PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_SPCOM200,
+		PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+		pbn_b2_bt_2_921600 },
+	/*
+	 * VScom SPCOM800, from sl@s.pl
+	 */
+	{	PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_SPCOM800,
+		PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+		pbn_b2_8_921600 },
+	{	PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_1077,
+		PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+		pbn_b2_4_921600 },
+	/* Unknown card - subdevice 0x1584 */
+	{	PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9050,
+		PCI_VENDOR_ID_PLX,
+		PCI_SUBDEVICE_ID_UNKNOWN_0x1584, 0, 0,
+		pbn_b2_4_115200 },
+	/* Unknown card - subdevice 0x1588 */
+	{	PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9050,
+		PCI_VENDOR_ID_PLX,
+		PCI_SUBDEVICE_ID_UNKNOWN_0x1588, 0, 0,
+		pbn_b2_8_115200 },
+	{	PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9050,
+		PCI_SUBVENDOR_ID_KEYSPAN,
+		PCI_SUBDEVICE_ID_KEYSPAN_SX2, 0, 0,
+		pbn_panacom },
+	{	PCI_VENDOR_ID_PANACOM, PCI_DEVICE_ID_PANACOM_QUADMODEM,
+		PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+		pbn_panacom4 },
+	{	PCI_VENDOR_ID_PANACOM, PCI_DEVICE_ID_PANACOM_DUALMODEM,
+		PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+		pbn_panacom2 },
+	{	PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9030,
+		PCI_VENDOR_ID_ESDGMBH,
+		PCI_DEVICE_ID_ESDGMBH_CPCIASIO4, 0, 0,
+		pbn_b2_4_115200 },
+	{	PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9050,
+		PCI_SUBVENDOR_ID_CHASE_PCIFAST,
+		PCI_SUBDEVICE_ID_CHASE_PCIFAST4, 0, 0,
+		pbn_b2_4_460800 },
+	{	PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9050,
+		PCI_SUBVENDOR_ID_CHASE_PCIFAST,
+		PCI_SUBDEVICE_ID_CHASE_PCIFAST8, 0, 0,
+		pbn_b2_8_460800 },
+	{	PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9050,
+		PCI_SUBVENDOR_ID_CHASE_PCIFAST,
+		PCI_SUBDEVICE_ID_CHASE_PCIFAST16, 0, 0,
+		pbn_b2_16_460800 },
+	{	PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9050,
+		PCI_SUBVENDOR_ID_CHASE_PCIFAST,
+		PCI_SUBDEVICE_ID_CHASE_PCIFAST16FMC, 0, 0,
+		pbn_b2_16_460800 },
+	{	PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9050,
+		PCI_SUBVENDOR_ID_CHASE_PCIRAS,
+		PCI_SUBDEVICE_ID_CHASE_PCIRAS4, 0, 0,
+		pbn_b2_4_460800 },
+	{	PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9050,
+		PCI_SUBVENDOR_ID_CHASE_PCIRAS,
+		PCI_SUBDEVICE_ID_CHASE_PCIRAS8, 0, 0,
+		pbn_b2_8_460800 },
+	{	PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9050,
+		PCI_SUBVENDOR_ID_EXSYS,
+		PCI_SUBDEVICE_ID_EXSYS_4055, 0, 0,
+		pbn_exsys_4055 },
+	/*
+	 * Megawolf Romulus PCI Serial Card, from Mike Hudson
+	 * (Exoray@isys.ca)
+	 */
+	{	PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_ROMULUS,
+		0x10b5, 0x106a, 0, 0,
+		pbn_plx_romulus },
+	{	PCI_VENDOR_ID_QUATECH, PCI_DEVICE_ID_QUATECH_QSC100,
+		PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+		pbn_b1_4_115200 },
+	{	PCI_VENDOR_ID_QUATECH, PCI_DEVICE_ID_QUATECH_DSC100,
+		PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+		pbn_b1_2_115200 },
+	{	PCI_VENDOR_ID_QUATECH, PCI_DEVICE_ID_QUATECH_ESC100D,
+		PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+		pbn_b1_8_115200 },
+	{	PCI_VENDOR_ID_QUATECH, PCI_DEVICE_ID_QUATECH_ESC100M,
+		PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+		pbn_b1_8_115200 },
+	{	PCI_VENDOR_ID_SPECIALIX, PCI_DEVICE_ID_OXSEMI_16PCI954,
+		PCI_VENDOR_ID_SPECIALIX, PCI_SUBDEVICE_ID_SPECIALIX_SPEED4,
+		0, 0,
+		pbn_b0_4_921600 },
+	{	PCI_VENDOR_ID_OXSEMI, PCI_DEVICE_ID_OXSEMI_16PCI954,
+		PCI_SUBVENDOR_ID_SIIG, PCI_SUBDEVICE_ID_SIIG_QUARTET_SERIAL,
+		0, 0,
+		pbn_b0_4_1152000 },
+	{	PCI_VENDOR_ID_OXSEMI, 0x9505,
+		PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+		pbn_b0_bt_2_921600 },
+
+		/*
+		 * The below card is a little controversial since it is the
+		 * subject of a PCI vendor/device ID clash.  (See
+		 * www.ussg.iu.edu/hypermail/linux/kernel/0303.1/0516.html).
+		 * For now just used the hex ID 0x950a.
+		 */
+	{	PCI_VENDOR_ID_OXSEMI, 0x950a,
+		PCI_SUBVENDOR_ID_SIIG, PCI_SUBDEVICE_ID_SIIG_DUAL_00,
+		0, 0, pbn_b0_2_115200 },
+	{	PCI_VENDOR_ID_OXSEMI, 0x950a,
+		PCI_SUBVENDOR_ID_SIIG, PCI_SUBDEVICE_ID_SIIG_DUAL_30,
+		0, 0, pbn_b0_2_115200 },
+	{	PCI_VENDOR_ID_OXSEMI, 0x950a,
+		PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+		pbn_b0_2_1130000 },
+	{	PCI_VENDOR_ID_OXSEMI, PCI_DEVICE_ID_OXSEMI_C950,
+		PCI_VENDOR_ID_OXSEMI, PCI_SUBDEVICE_ID_OXSEMI_C950, 0, 0,
+		pbn_b0_1_921600 },
+	{	PCI_VENDOR_ID_OXSEMI, PCI_DEVICE_ID_OXSEMI_16PCI954,
+		PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+		pbn_b0_4_115200 },
+	{	PCI_VENDOR_ID_OXSEMI, PCI_DEVICE_ID_OXSEMI_16PCI952,
+		PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+		pbn_b0_bt_2_921600 },
+	{	PCI_VENDOR_ID_OXSEMI, PCI_DEVICE_ID_OXSEMI_16PCI958,
+		PCI_ANY_ID , PCI_ANY_ID, 0, 0,
+		pbn_b2_8_1152000 },
+
+	/*
+	 * Oxford Semiconductor Inc. Tornado PCI express device range.
+	 */
+	{	PCI_VENDOR_ID_OXSEMI, 0xc101,    /* OXPCIe952 1 Legacy UART */
+		PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+		pbn_b0_1_4000000 },
+	{	PCI_VENDOR_ID_OXSEMI, 0xc105,    /* OXPCIe952 1 Legacy UART */
+		PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+		pbn_b0_1_4000000 },
+	{	PCI_VENDOR_ID_OXSEMI, 0xc11b,    /* OXPCIe952 1 Native UART */
+		PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+		pbn_oxsemi_1_4000000 },
+	{	PCI_VENDOR_ID_OXSEMI, 0xc11f,    /* OXPCIe952 1 Native UART */
+		PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+		pbn_oxsemi_1_4000000 },
+	{	PCI_VENDOR_ID_OXSEMI, 0xc120,    /* OXPCIe952 1 Legacy UART */
+		PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+		pbn_b0_1_4000000 },
+	{	PCI_VENDOR_ID_OXSEMI, 0xc124,    /* OXPCIe952 1 Legacy UART */
+		PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+		pbn_b0_1_4000000 },
+	{	PCI_VENDOR_ID_OXSEMI, 0xc138,    /* OXPCIe952 1 Native UART */
+		PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+		pbn_oxsemi_1_4000000 },
+	{	PCI_VENDOR_ID_OXSEMI, 0xc13d,    /* OXPCIe952 1 Native UART */
+		PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+		pbn_oxsemi_1_4000000 },
+	{	PCI_VENDOR_ID_OXSEMI, 0xc140,    /* OXPCIe952 1 Legacy UART */
+		PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+		pbn_b0_1_4000000 },
+	{	PCI_VENDOR_ID_OXSEMI, 0xc141,    /* OXPCIe952 1 Legacy UART */
+		PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+		pbn_b0_1_4000000 },
+	{	PCI_VENDOR_ID_OXSEMI, 0xc144,    /* OXPCIe952 1 Legacy UART */
+		PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+		pbn_b0_1_4000000 },
+	{	PCI_VENDOR_ID_OXSEMI, 0xc145,    /* OXPCIe952 1 Legacy UART */
+		PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+		pbn_b0_1_4000000 },
+	{	PCI_VENDOR_ID_OXSEMI, 0xc158,    /* OXPCIe952 2 Native UART */
+		PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+		pbn_oxsemi_2_4000000 },
+	{	PCI_VENDOR_ID_OXSEMI, 0xc15d,    /* OXPCIe952 2 Native UART */
+		PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+		pbn_oxsemi_2_4000000 },
+	{	PCI_VENDOR_ID_OXSEMI, 0xc208,    /* OXPCIe954 4 Native UART */
+		PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+		pbn_oxsemi_4_4000000 },
+	{	PCI_VENDOR_ID_OXSEMI, 0xc20d,    /* OXPCIe954 4 Native UART */
+		PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+		pbn_oxsemi_4_4000000 },
+	{	PCI_VENDOR_ID_OXSEMI, 0xc308,    /* OXPCIe958 8 Native UART */
+		PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+		pbn_oxsemi_8_4000000 },
+	{	PCI_VENDOR_ID_OXSEMI, 0xc30d,    /* OXPCIe958 8 Native UART */
+		PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+		pbn_oxsemi_8_4000000 },
+	{	PCI_VENDOR_ID_OXSEMI, 0xc40b,    /* OXPCIe200 1 Native UART */
+		PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+		pbn_oxsemi_1_4000000 },
+	{	PCI_VENDOR_ID_OXSEMI, 0xc40f,    /* OXPCIe200 1 Native UART */
+		PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+		pbn_oxsemi_1_4000000 },
+	{	PCI_VENDOR_ID_OXSEMI, 0xc41b,    /* OXPCIe200 1 Native UART */
+		PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+		pbn_oxsemi_1_4000000 },
+	{	PCI_VENDOR_ID_OXSEMI, 0xc41f,    /* OXPCIe200 1 Native UART */
+		PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+		pbn_oxsemi_1_4000000 },
+	{	PCI_VENDOR_ID_OXSEMI, 0xc42b,    /* OXPCIe200 1 Native UART */
+		PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+		pbn_oxsemi_1_4000000 },
+	{	PCI_VENDOR_ID_OXSEMI, 0xc42f,    /* OXPCIe200 1 Native UART */
+		PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+		pbn_oxsemi_1_4000000 },
+	{	PCI_VENDOR_ID_OXSEMI, 0xc43b,    /* OXPCIe200 1 Native UART */
+		PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+		pbn_oxsemi_1_4000000 },
+	{	PCI_VENDOR_ID_OXSEMI, 0xc43f,    /* OXPCIe200 1 Native UART */
+		PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+		pbn_oxsemi_1_4000000 },
+	{	PCI_VENDOR_ID_OXSEMI, 0xc44b,    /* OXPCIe200 1 Native UART */
+		PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+		pbn_oxsemi_1_4000000 },
+	{	PCI_VENDOR_ID_OXSEMI, 0xc44f,    /* OXPCIe200 1 Native UART */
+		PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+		pbn_oxsemi_1_4000000 },
+	{	PCI_VENDOR_ID_OXSEMI, 0xc45b,    /* OXPCIe200 1 Native UART */
+		PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+		pbn_oxsemi_1_4000000 },
+	{	PCI_VENDOR_ID_OXSEMI, 0xc45f,    /* OXPCIe200 1 Native UART */
+		PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+		pbn_oxsemi_1_4000000 },
+	{	PCI_VENDOR_ID_OXSEMI, 0xc46b,    /* OXPCIe200 1 Native UART */
+		PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+		pbn_oxsemi_1_4000000 },
+	{	PCI_VENDOR_ID_OXSEMI, 0xc46f,    /* OXPCIe200 1 Native UART */
+		PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+		pbn_oxsemi_1_4000000 },
+	{	PCI_VENDOR_ID_OXSEMI, 0xc47b,    /* OXPCIe200 1 Native UART */
+		PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+		pbn_oxsemi_1_4000000 },
+	{	PCI_VENDOR_ID_OXSEMI, 0xc47f,    /* OXPCIe200 1 Native UART */
+		PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+		pbn_oxsemi_1_4000000 },
+	{	PCI_VENDOR_ID_OXSEMI, 0xc48b,    /* OXPCIe200 1 Native UART */
+		PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+		pbn_oxsemi_1_4000000 },
+	{	PCI_VENDOR_ID_OXSEMI, 0xc48f,    /* OXPCIe200 1 Native UART */
+		PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+		pbn_oxsemi_1_4000000 },
+	{	PCI_VENDOR_ID_OXSEMI, 0xc49b,    /* OXPCIe200 1 Native UART */
+		PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+		pbn_oxsemi_1_4000000 },
+	{	PCI_VENDOR_ID_OXSEMI, 0xc49f,    /* OXPCIe200 1 Native UART */
+		PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+		pbn_oxsemi_1_4000000 },
+	{	PCI_VENDOR_ID_OXSEMI, 0xc4ab,    /* OXPCIe200 1 Native UART */
+		PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+		pbn_oxsemi_1_4000000 },
+	{	PCI_VENDOR_ID_OXSEMI, 0xc4af,    /* OXPCIe200 1 Native UART */
+		PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+		pbn_oxsemi_1_4000000 },
+	{	PCI_VENDOR_ID_OXSEMI, 0xc4bb,    /* OXPCIe200 1 Native UART */
+		PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+		pbn_oxsemi_1_4000000 },
+	{	PCI_VENDOR_ID_OXSEMI, 0xc4bf,    /* OXPCIe200 1 Native UART */
+		PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+		pbn_oxsemi_1_4000000 },
+	{	PCI_VENDOR_ID_OXSEMI, 0xc4cb,    /* OXPCIe200 1 Native UART */
+		PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+		pbn_oxsemi_1_4000000 },
+	{	PCI_VENDOR_ID_OXSEMI, 0xc4cf,    /* OXPCIe200 1 Native UART */
+		PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+		pbn_oxsemi_1_4000000 },
+	/*
+	 * Mainpine Inc. IQ Express "Rev3" utilizing OxSemi Tornado
+	 */
+	{	PCI_VENDOR_ID_MAINPINE, 0x4000,	/* IQ Express 1 Port V.34 Super-G3 Fax */
+		PCI_VENDOR_ID_MAINPINE, 0x4001, 0, 0,
+		pbn_oxsemi_1_4000000 },
+	{	PCI_VENDOR_ID_MAINPINE, 0x4000,	/* IQ Express 2 Port V.34 Super-G3 Fax */
+		PCI_VENDOR_ID_MAINPINE, 0x4002, 0, 0,
+		pbn_oxsemi_2_4000000 },
+	{	PCI_VENDOR_ID_MAINPINE, 0x4000,	/* IQ Express 4 Port V.34 Super-G3 Fax */
+		PCI_VENDOR_ID_MAINPINE, 0x4004, 0, 0,
+		pbn_oxsemi_4_4000000 },
+	{	PCI_VENDOR_ID_MAINPINE, 0x4000,	/* IQ Express 8 Port V.34 Super-G3 Fax */
+		PCI_VENDOR_ID_MAINPINE, 0x4008, 0, 0,
+		pbn_oxsemi_8_4000000 },
+
+	/*
+	 * Digi/IBM PCIe 2-port Async EIA-232 Adapter utilizing OxSemi Tornado
+	 */
+	{	PCI_VENDOR_ID_DIGI, PCIE_DEVICE_ID_NEO_2_OX_IBM,
+		PCI_SUBVENDOR_ID_IBM, PCI_ANY_ID, 0, 0,
+		pbn_oxsemi_2_4000000 },
+
+	/*
+	 * SBS Technologies, Inc. P-Octal and PMC-OCTPRO cards,
+	 * from skokodyn@yahoo.com
+	 */
+	{	PCI_VENDOR_ID_SBSMODULARIO, PCI_DEVICE_ID_OCTPRO,
+		PCI_SUBVENDOR_ID_SBSMODULARIO, PCI_SUBDEVICE_ID_OCTPRO232, 0, 0,
+		pbn_sbsxrsio },
+	{	PCI_VENDOR_ID_SBSMODULARIO, PCI_DEVICE_ID_OCTPRO,
+		PCI_SUBVENDOR_ID_SBSMODULARIO, PCI_SUBDEVICE_ID_OCTPRO422, 0, 0,
+		pbn_sbsxrsio },
+	{	PCI_VENDOR_ID_SBSMODULARIO, PCI_DEVICE_ID_OCTPRO,
+		PCI_SUBVENDOR_ID_SBSMODULARIO, PCI_SUBDEVICE_ID_POCTAL232, 0, 0,
+		pbn_sbsxrsio },
+	{	PCI_VENDOR_ID_SBSMODULARIO, PCI_DEVICE_ID_OCTPRO,
+		PCI_SUBVENDOR_ID_SBSMODULARIO, PCI_SUBDEVICE_ID_POCTAL422, 0, 0,
+		pbn_sbsxrsio },
+
+	/*
+	 * Digitan DS560-558, from jimd@esoft.com
+	 */
+	{	PCI_VENDOR_ID_ATT, PCI_DEVICE_ID_ATT_VENUS_MODEM,
+		PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+		pbn_b1_1_115200 },
+
+	/*
+	 * Titan Electronic cards
+	 *  The 400L and 800L have a custom setup quirk.
+	 */
+	{	PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_100,
+		PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+		pbn_b0_1_921600 },
+	{	PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_200,
+		PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+		pbn_b0_2_921600 },
+	{	PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_400,
+		PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+		pbn_b0_4_921600 },
+	{	PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_800B,
+		PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+		pbn_b0_4_921600 },
+	{	PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_100L,
+		PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+		pbn_b1_1_921600 },
+	{	PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_200L,
+		PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+		pbn_b1_bt_2_921600 },
+	{	PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_400L,
+		PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+		pbn_b0_bt_4_921600 },
+	{	PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_800L,
+		PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+		pbn_b0_bt_8_921600 },
+	{	PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_200I,
+		PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+		pbn_b4_bt_2_921600 },
+	{	PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_400I,
+		PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+		pbn_b4_bt_4_921600 },
+	{	PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_800I,
+		PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+		pbn_b4_bt_8_921600 },
+	{	PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_400EH,
+		PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+		pbn_b0_4_921600 },
+	{	PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_800EH,
+		PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+		pbn_b0_4_921600 },
+	{	PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_800EHB,
+		PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+		pbn_b0_4_921600 },
+	{	PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_100E,
+		PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+		pbn_oxsemi_1_4000000 },
+	{	PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_200E,
+		PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+		pbn_oxsemi_2_4000000 },
+	{	PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_400E,
+		PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+		pbn_oxsemi_4_4000000 },
+	{	PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_800E,
+		PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+		pbn_oxsemi_8_4000000 },
+	{	PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_200EI,
+		PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+		pbn_oxsemi_2_4000000 },
+	{	PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_200EISI,
+		PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+		pbn_oxsemi_2_4000000 },
+	{	PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_200V3,
+		PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+		pbn_b0_bt_2_921600 },
+	{	PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_400V3,
+		PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+		pbn_b0_4_921600 },
+	{	PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_410V3,
+		PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+		pbn_b0_4_921600 },
+	{	PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_800V3,
+		PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+		pbn_b0_4_921600 },
+	{	PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_800V3B,
+		PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+		pbn_b0_4_921600 },
+
+	{	PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S_10x_550,
+		PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+		pbn_b2_1_460800 },
+	{	PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S_10x_650,
+		PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+		pbn_b2_1_460800 },
+	{	PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S_10x_850,
+		PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+		pbn_b2_1_460800 },
+	{	PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S_10x_550,
+		PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+		pbn_b2_bt_2_921600 },
+	{	PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S_10x_650,
+		PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+		pbn_b2_bt_2_921600 },
+	{	PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S_10x_850,
+		PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+		pbn_b2_bt_2_921600 },
+	{	PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_4S_10x_550,
+		PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+		pbn_b2_bt_4_921600 },
+	{	PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_4S_10x_650,
+		PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+		pbn_b2_bt_4_921600 },
+	{	PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_4S_10x_850,
+		PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+		pbn_b2_bt_4_921600 },
+	{	PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S_20x_550,
+		PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+		pbn_b0_1_921600 },
+	{	PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S_20x_650,
+		PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+		pbn_b0_1_921600 },
+	{	PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S_20x_850,
+		PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+		pbn_b0_1_921600 },
+	{	PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S_20x_550,
+		PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+		pbn_b0_bt_2_921600 },
+	{	PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S_20x_650,
+		PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+		pbn_b0_bt_2_921600 },
+	{	PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S_20x_850,
+		PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+		pbn_b0_bt_2_921600 },
+	{	PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_4S_20x_550,
+		PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+		pbn_b0_bt_4_921600 },
+	{	PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_4S_20x_650,
+		PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+		pbn_b0_bt_4_921600 },
+	{	PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_4S_20x_850,
+		PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+		pbn_b0_bt_4_921600 },
+	{	PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_8S_20x_550,
+		PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+		pbn_b0_bt_8_921600 },
+	{	PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_8S_20x_650,
+		PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+		pbn_b0_bt_8_921600 },
+	{	PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_8S_20x_850,
+		PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+		pbn_b0_bt_8_921600 },
+
+	/*
+	 * Computone devices submitted by Doug McNash dmcnash@computone.com
+	 */
+	{	PCI_VENDOR_ID_COMPUTONE, PCI_DEVICE_ID_COMPUTONE_PG,
+		PCI_SUBVENDOR_ID_COMPUTONE, PCI_SUBDEVICE_ID_COMPUTONE_PG4,
+		0, 0, pbn_computone_4 },
+	{	PCI_VENDOR_ID_COMPUTONE, PCI_DEVICE_ID_COMPUTONE_PG,
+		PCI_SUBVENDOR_ID_COMPUTONE, PCI_SUBDEVICE_ID_COMPUTONE_PG8,
+		0, 0, pbn_computone_8 },
+	{	PCI_VENDOR_ID_COMPUTONE, PCI_DEVICE_ID_COMPUTONE_PG,
+		PCI_SUBVENDOR_ID_COMPUTONE, PCI_SUBDEVICE_ID_COMPUTONE_PG6,
+		0, 0, pbn_computone_6 },
+
+	{	PCI_VENDOR_ID_OXSEMI, PCI_DEVICE_ID_OXSEMI_16PCI95N,
+		PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+		pbn_oxsemi },
+	{	PCI_VENDOR_ID_TIMEDIA, PCI_DEVICE_ID_TIMEDIA_1889,
+		PCI_VENDOR_ID_TIMEDIA, PCI_ANY_ID, 0, 0,
+		pbn_b0_bt_1_921600 },
+
+	/*
+	 * AFAVLAB serial card, from Harald Welte <laforge@gnumonks.org>
+	 */
+	{	PCI_VENDOR_ID_AFAVLAB, PCI_DEVICE_ID_AFAVLAB_P028,
+		PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+		pbn_b0_bt_8_115200 },
+	{	PCI_VENDOR_ID_AFAVLAB, PCI_DEVICE_ID_AFAVLAB_P030,
+		PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+		pbn_b0_bt_8_115200 },
+
+	{	PCI_VENDOR_ID_LAVA, PCI_DEVICE_ID_LAVA_DSERIAL,
+		PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+		pbn_b0_bt_2_115200 },
+	{	PCI_VENDOR_ID_LAVA, PCI_DEVICE_ID_LAVA_QUATRO_A,
+		PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+		pbn_b0_bt_2_115200 },
+	{	PCI_VENDOR_ID_LAVA, PCI_DEVICE_ID_LAVA_QUATRO_B,
+		PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+		pbn_b0_bt_2_115200 },
+	{	PCI_VENDOR_ID_LAVA, PCI_DEVICE_ID_LAVA_QUATTRO_A,
+		PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+		pbn_b0_bt_2_115200 },
+	{	PCI_VENDOR_ID_LAVA, PCI_DEVICE_ID_LAVA_QUATTRO_B,
+		PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+		pbn_b0_bt_2_115200 },
+	{	PCI_VENDOR_ID_LAVA, PCI_DEVICE_ID_LAVA_OCTO_A,
+		PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+		pbn_b0_bt_4_460800 },
+	{	PCI_VENDOR_ID_LAVA, PCI_DEVICE_ID_LAVA_OCTO_B,
+		PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+		pbn_b0_bt_4_460800 },
+	{	PCI_VENDOR_ID_LAVA, PCI_DEVICE_ID_LAVA_PORT_PLUS,
+		PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+		pbn_b0_bt_2_460800 },
+	{	PCI_VENDOR_ID_LAVA, PCI_DEVICE_ID_LAVA_QUAD_A,
+		PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+		pbn_b0_bt_2_460800 },
+	{	PCI_VENDOR_ID_LAVA, PCI_DEVICE_ID_LAVA_QUAD_B,
+		PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+		pbn_b0_bt_2_460800 },
+	{	PCI_VENDOR_ID_LAVA, PCI_DEVICE_ID_LAVA_SSERIAL,
+		PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+		pbn_b0_bt_1_115200 },
+	{	PCI_VENDOR_ID_LAVA, PCI_DEVICE_ID_LAVA_PORT_650,
+		PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+		pbn_b0_bt_1_460800 },
+
+	/*
+	 * Korenix Jetcard F0/F1 cards (JC1204, JC1208, JC1404, JC1408).
+	 * Cards are identified by their subsystem vendor IDs, which
+	 * (in hex) match the model number.
+	 *
+	 * Note that JC140x are RS422/485 cards which require ox950
+	 * ACR = 0x10, and as such are not currently fully supported.
+	 */
+	{	PCI_VENDOR_ID_KORENIX, PCI_DEVICE_ID_KORENIX_JETCARDF0,
+		0x1204, 0x0004, 0, 0,
+		pbn_b0_4_921600 },
+	{	PCI_VENDOR_ID_KORENIX, PCI_DEVICE_ID_KORENIX_JETCARDF0,
+		0x1208, 0x0004, 0, 0,
+		pbn_b0_4_921600 },
+/*	{	PCI_VENDOR_ID_KORENIX, PCI_DEVICE_ID_KORENIX_JETCARDF0,
+		0x1402, 0x0002, 0, 0,
+		pbn_b0_2_921600 }, */
+/*	{	PCI_VENDOR_ID_KORENIX, PCI_DEVICE_ID_KORENIX_JETCARDF0,
+		0x1404, 0x0004, 0, 0,
+		pbn_b0_4_921600 }, */
+	{	PCI_VENDOR_ID_KORENIX, PCI_DEVICE_ID_KORENIX_JETCARDF1,
+		0x1208, 0x0004, 0, 0,
+		pbn_b0_4_921600 },
+
+	{	PCI_VENDOR_ID_KORENIX, PCI_DEVICE_ID_KORENIX_JETCARDF2,
+		0x1204, 0x0004, 0, 0,
+		pbn_b0_4_921600 },
+	{	PCI_VENDOR_ID_KORENIX, PCI_DEVICE_ID_KORENIX_JETCARDF2,
+		0x1208, 0x0004, 0, 0,
+		pbn_b0_4_921600 },
+	{	PCI_VENDOR_ID_KORENIX, PCI_DEVICE_ID_KORENIX_JETCARDF3,
+		0x1208, 0x0004, 0, 0,
+		pbn_b0_4_921600 },
+	/*
+	 * Dell Remote Access Card 4 - Tim_T_Murphy@Dell.com
+	 */
+	{	PCI_VENDOR_ID_DELL, PCI_DEVICE_ID_DELL_RAC4,
+		PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+		pbn_b1_1_1382400 },
+
+	/*
+	 * Dell Remote Access Card III - Tim_T_Murphy@Dell.com
+	 */
+	{	PCI_VENDOR_ID_DELL, PCI_DEVICE_ID_DELL_RACIII,
+		PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+		pbn_b1_1_1382400 },
+
+	/*
+	 * RAStel 2 port modem, gerg@moreton.com.au
+	 */
+	{	PCI_VENDOR_ID_MORETON, PCI_DEVICE_ID_RASTEL_2PORT,
+		PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+		pbn_b2_bt_2_115200 },
+
+	/*
+	 * EKF addition for i960 Boards form EKF with serial port
+	 */
+	{	PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_80960_RP,
+		0xE4BF, PCI_ANY_ID, 0, 0,
+		pbn_intel_i960 },
+
+	/*
+	 * Xircom Cardbus/Ethernet combos
+	 */
+	{	PCI_VENDOR_ID_XIRCOM, PCI_DEVICE_ID_XIRCOM_X3201_MDM,
+		PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+		pbn_b0_1_115200 },
+	/*
+	 * Xircom RBM56G cardbus modem - Dirk Arnold (temp entry)
+	 */
+	{	PCI_VENDOR_ID_XIRCOM, PCI_DEVICE_ID_XIRCOM_RBM56G,
+		PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+		pbn_b0_1_115200 },
+
+	/*
+	 * Untested PCI modems, sent in from various folks...
+	 */
+
+	/*
+	 * Elsa Model 56K PCI Modem, from Andreas Rath <arh@01019freenet.de>
+	 */
+	{	PCI_VENDOR_ID_ROCKWELL, 0x1004,
+		0x1048, 0x1500, 0, 0,
+		pbn_b1_1_115200 },
+
+	{	PCI_VENDOR_ID_SGI, PCI_DEVICE_ID_SGI_IOC3,
+		0xFF00, 0, 0, 0,
+		pbn_sgi_ioc3 },
+
+	/*
+	 * HP Diva card
+	 */
+	{	PCI_VENDOR_ID_HP, PCI_DEVICE_ID_HP_DIVA,
+		PCI_VENDOR_ID_HP, PCI_DEVICE_ID_HP_DIVA_RMP3, 0, 0,
+		pbn_b1_1_115200 },
+	{	PCI_VENDOR_ID_HP, PCI_DEVICE_ID_HP_DIVA,
+		PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+		pbn_b0_5_115200 },
+	{	PCI_VENDOR_ID_HP, PCI_DEVICE_ID_HP_DIVA_AUX,
+		PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+		pbn_b2_1_115200 },
+
+	{	PCI_VENDOR_ID_DCI, PCI_DEVICE_ID_DCI_PCCOM2,
+		PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+		pbn_b3_2_115200 },
+	{	PCI_VENDOR_ID_DCI, PCI_DEVICE_ID_DCI_PCCOM4,
+		PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+		pbn_b3_4_115200 },
+	{	PCI_VENDOR_ID_DCI, PCI_DEVICE_ID_DCI_PCCOM8,
+		PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+		pbn_b3_8_115200 },
+
+	/*
+	 * Exar Corp. XR17C15[248] Dual/Quad/Octal UART
+	 */
+	{	PCI_VENDOR_ID_EXAR, PCI_DEVICE_ID_EXAR_XR17C152,
+		PCI_ANY_ID, PCI_ANY_ID,
+		0,
+		0, pbn_exar_XR17C152 },
+	{	PCI_VENDOR_ID_EXAR, PCI_DEVICE_ID_EXAR_XR17C154,
+		PCI_ANY_ID, PCI_ANY_ID,
+		0,
+		0, pbn_exar_XR17C154 },
+	{	PCI_VENDOR_ID_EXAR, PCI_DEVICE_ID_EXAR_XR17C158,
+		PCI_ANY_ID, PCI_ANY_ID,
+		0,
+		0, pbn_exar_XR17C158 },
+
+	/*
+	 * Topic TP560 Data/Fax/Voice 56k modem (reported by Evan Clarke)
+	 */
+	{	PCI_VENDOR_ID_TOPIC, PCI_DEVICE_ID_TOPIC_TP560,
+		PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+		pbn_b0_1_115200 },
+	/*
+	 * ITE
+	 */
+	{	PCI_VENDOR_ID_ITE, PCI_DEVICE_ID_ITE_8872,
+		PCI_ANY_ID, PCI_ANY_ID,
+		0, 0,
+		pbn_b1_bt_1_115200 },
+
+	/*
+	 * IntaShield IS-200
+	 */
+	{	PCI_VENDOR_ID_INTASHIELD, PCI_DEVICE_ID_INTASHIELD_IS200,
+		PCI_ANY_ID, PCI_ANY_ID, 0, 0,	/* 135a.0811 */
+		pbn_b2_2_115200 },
+	/*
+	 * IntaShield IS-400
+	 */
+	{	PCI_VENDOR_ID_INTASHIELD, PCI_DEVICE_ID_INTASHIELD_IS400,
+		PCI_ANY_ID, PCI_ANY_ID, 0, 0,    /* 135a.0dc0 */
+		pbn_b2_4_115200 },
+	/*
+	 * Perle PCI-RAS cards
+	 */
+	{       PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9030,
+		PCI_SUBVENDOR_ID_PERLE, PCI_SUBDEVICE_ID_PCI_RAS4,
+		0, 0, pbn_b2_4_921600 },
+	{       PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9030,
+		PCI_SUBVENDOR_ID_PERLE, PCI_SUBDEVICE_ID_PCI_RAS8,
+		0, 0, pbn_b2_8_921600 },
+
+	/*
+	 * Mainpine series cards: Fairly standard layout but fools
+	 * parts of the autodetect in some cases and uses otherwise
+	 * unmatched communications subclasses in the PCI Express case
+	 */
+
+	{	/* RockForceDUO */
+		PCI_VENDOR_ID_MAINPINE, PCI_DEVICE_ID_MAINPINE_PBRIDGE,
+		PCI_VENDOR_ID_MAINPINE, 0x0200,
+		0, 0, pbn_b0_2_115200 },
+	{	/* RockForceQUATRO */
+		PCI_VENDOR_ID_MAINPINE, PCI_DEVICE_ID_MAINPINE_PBRIDGE,
+		PCI_VENDOR_ID_MAINPINE, 0x0300,
+		0, 0, pbn_b0_4_115200 },
+	{	/* RockForceDUO+ */
+		PCI_VENDOR_ID_MAINPINE, PCI_DEVICE_ID_MAINPINE_PBRIDGE,
+		PCI_VENDOR_ID_MAINPINE, 0x0400,
+		0, 0, pbn_b0_2_115200 },
+	{	/* RockForceQUATRO+ */
+		PCI_VENDOR_ID_MAINPINE, PCI_DEVICE_ID_MAINPINE_PBRIDGE,
+		PCI_VENDOR_ID_MAINPINE, 0x0500,
+		0, 0, pbn_b0_4_115200 },
+	{	/* RockForce+ */
+		PCI_VENDOR_ID_MAINPINE, PCI_DEVICE_ID_MAINPINE_PBRIDGE,
+		PCI_VENDOR_ID_MAINPINE, 0x0600,
+		0, 0, pbn_b0_2_115200 },
+	{	/* RockForce+ */
+		PCI_VENDOR_ID_MAINPINE, PCI_DEVICE_ID_MAINPINE_PBRIDGE,
+		PCI_VENDOR_ID_MAINPINE, 0x0700,
+		0, 0, pbn_b0_4_115200 },
+	{	/* RockForceOCTO+ */
+		PCI_VENDOR_ID_MAINPINE, PCI_DEVICE_ID_MAINPINE_PBRIDGE,
+		PCI_VENDOR_ID_MAINPINE, 0x0800,
+		0, 0, pbn_b0_8_115200 },
+	{	/* RockForceDUO+ */
+		PCI_VENDOR_ID_MAINPINE, PCI_DEVICE_ID_MAINPINE_PBRIDGE,
+		PCI_VENDOR_ID_MAINPINE, 0x0C00,
+		0, 0, pbn_b0_2_115200 },
+	{	/* RockForceQUARTRO+ */
+		PCI_VENDOR_ID_MAINPINE, PCI_DEVICE_ID_MAINPINE_PBRIDGE,
+		PCI_VENDOR_ID_MAINPINE, 0x0D00,
+		0, 0, pbn_b0_4_115200 },
+	{	/* RockForceOCTO+ */
+		PCI_VENDOR_ID_MAINPINE, PCI_DEVICE_ID_MAINPINE_PBRIDGE,
+		PCI_VENDOR_ID_MAINPINE, 0x1D00,
+		0, 0, pbn_b0_8_115200 },
+	{	/* RockForceD1 */
+		PCI_VENDOR_ID_MAINPINE, PCI_DEVICE_ID_MAINPINE_PBRIDGE,
+		PCI_VENDOR_ID_MAINPINE, 0x2000,
+		0, 0, pbn_b0_1_115200 },
+	{	/* RockForceF1 */
+		PCI_VENDOR_ID_MAINPINE, PCI_DEVICE_ID_MAINPINE_PBRIDGE,
+		PCI_VENDOR_ID_MAINPINE, 0x2100,
+		0, 0, pbn_b0_1_115200 },
+	{	/* RockForceD2 */
+		PCI_VENDOR_ID_MAINPINE, PCI_DEVICE_ID_MAINPINE_PBRIDGE,
+		PCI_VENDOR_ID_MAINPINE, 0x2200,
+		0, 0, pbn_b0_2_115200 },
+	{	/* RockForceF2 */
+		PCI_VENDOR_ID_MAINPINE, PCI_DEVICE_ID_MAINPINE_PBRIDGE,
+		PCI_VENDOR_ID_MAINPINE, 0x2300,
+		0, 0, pbn_b0_2_115200 },
+	{	/* RockForceD4 */
+		PCI_VENDOR_ID_MAINPINE, PCI_DEVICE_ID_MAINPINE_PBRIDGE,
+		PCI_VENDOR_ID_MAINPINE, 0x2400,
+		0, 0, pbn_b0_4_115200 },
+	{	/* RockForceF4 */
+		PCI_VENDOR_ID_MAINPINE, PCI_DEVICE_ID_MAINPINE_PBRIDGE,
+		PCI_VENDOR_ID_MAINPINE, 0x2500,
+		0, 0, pbn_b0_4_115200 },
+	{	/* RockForceD8 */
+		PCI_VENDOR_ID_MAINPINE, PCI_DEVICE_ID_MAINPINE_PBRIDGE,
+		PCI_VENDOR_ID_MAINPINE, 0x2600,
+		0, 0, pbn_b0_8_115200 },
+	{	/* RockForceF8 */
+		PCI_VENDOR_ID_MAINPINE, PCI_DEVICE_ID_MAINPINE_PBRIDGE,
+		PCI_VENDOR_ID_MAINPINE, 0x2700,
+		0, 0, pbn_b0_8_115200 },
+	{	/* IQ Express D1 */
+		PCI_VENDOR_ID_MAINPINE, PCI_DEVICE_ID_MAINPINE_PBRIDGE,
+		PCI_VENDOR_ID_MAINPINE, 0x3000,
+		0, 0, pbn_b0_1_115200 },
+	{	/* IQ Express F1 */
+		PCI_VENDOR_ID_MAINPINE, PCI_DEVICE_ID_MAINPINE_PBRIDGE,
+		PCI_VENDOR_ID_MAINPINE, 0x3100,
+		0, 0, pbn_b0_1_115200 },
+	{	/* IQ Express D2 */
+		PCI_VENDOR_ID_MAINPINE, PCI_DEVICE_ID_MAINPINE_PBRIDGE,
+		PCI_VENDOR_ID_MAINPINE, 0x3200,
+		0, 0, pbn_b0_2_115200 },
+	{	/* IQ Express F2 */
+		PCI_VENDOR_ID_MAINPINE, PCI_DEVICE_ID_MAINPINE_PBRIDGE,
+		PCI_VENDOR_ID_MAINPINE, 0x3300,
+		0, 0, pbn_b0_2_115200 },
+	{	/* IQ Express D4 */
+		PCI_VENDOR_ID_MAINPINE, PCI_DEVICE_ID_MAINPINE_PBRIDGE,
+		PCI_VENDOR_ID_MAINPINE, 0x3400,
+		0, 0, pbn_b0_4_115200 },
+	{	/* IQ Express F4 */
+		PCI_VENDOR_ID_MAINPINE, PCI_DEVICE_ID_MAINPINE_PBRIDGE,
+		PCI_VENDOR_ID_MAINPINE, 0x3500,
+		0, 0, pbn_b0_4_115200 },
+	{	/* IQ Express D8 */
+		PCI_VENDOR_ID_MAINPINE, PCI_DEVICE_ID_MAINPINE_PBRIDGE,
+		PCI_VENDOR_ID_MAINPINE, 0x3C00,
+		0, 0, pbn_b0_8_115200 },
+	{	/* IQ Express F8 */
+		PCI_VENDOR_ID_MAINPINE, PCI_DEVICE_ID_MAINPINE_PBRIDGE,
+		PCI_VENDOR_ID_MAINPINE, 0x3D00,
+		0, 0, pbn_b0_8_115200 },
+
+
+	/*
+	 * PA Semi PA6T-1682M on-chip UART
+	 */
+	{	PCI_VENDOR_ID_PASEMI, 0xa004,
+		PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+		pbn_pasemi_1682M },
+
+	/*
+	 * National Instruments
+	 */
+	{	PCI_VENDOR_ID_NI, PCI_DEVICE_ID_NI_PCI23216,
+		PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+		pbn_b1_16_115200 },
+	{	PCI_VENDOR_ID_NI, PCI_DEVICE_ID_NI_PCI2328,
+		PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+		pbn_b1_8_115200 },
+	{	PCI_VENDOR_ID_NI, PCI_DEVICE_ID_NI_PCI2324,
+		PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+		pbn_b1_bt_4_115200 },
+	{	PCI_VENDOR_ID_NI, PCI_DEVICE_ID_NI_PCI2322,
+		PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+		pbn_b1_bt_2_115200 },
+	{	PCI_VENDOR_ID_NI, PCI_DEVICE_ID_NI_PCI2324I,
+		PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+		pbn_b1_bt_4_115200 },
+	{	PCI_VENDOR_ID_NI, PCI_DEVICE_ID_NI_PCI2322I,
+		PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+		pbn_b1_bt_2_115200 },
+	{	PCI_VENDOR_ID_NI, PCI_DEVICE_ID_NI_PXI8420_23216,
+		PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+		pbn_b1_16_115200 },
+	{	PCI_VENDOR_ID_NI, PCI_DEVICE_ID_NI_PXI8420_2328,
+		PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+		pbn_b1_8_115200 },
+	{	PCI_VENDOR_ID_NI, PCI_DEVICE_ID_NI_PXI8420_2324,
+		PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+		pbn_b1_bt_4_115200 },
+	{	PCI_VENDOR_ID_NI, PCI_DEVICE_ID_NI_PXI8420_2322,
+		PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+		pbn_b1_bt_2_115200 },
+	{	PCI_VENDOR_ID_NI, PCI_DEVICE_ID_NI_PXI8422_2324,
+		PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+		pbn_b1_bt_4_115200 },
+	{	PCI_VENDOR_ID_NI, PCI_DEVICE_ID_NI_PXI8422_2322,
+		PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+		pbn_b1_bt_2_115200 },
+	{	PCI_VENDOR_ID_NI, PCI_DEVICE_ID_NI_PXI8430_2322,
+		PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+		pbn_ni8430_2 },
+	{	PCI_VENDOR_ID_NI, PCI_DEVICE_ID_NI_PCI8430_2322,
+		PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+		pbn_ni8430_2 },
+	{	PCI_VENDOR_ID_NI, PCI_DEVICE_ID_NI_PXI8430_2324,
+		PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+		pbn_ni8430_4 },
+	{	PCI_VENDOR_ID_NI, PCI_DEVICE_ID_NI_PCI8430_2324,
+		PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+		pbn_ni8430_4 },
+	{	PCI_VENDOR_ID_NI, PCI_DEVICE_ID_NI_PXI8430_2328,
+		PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+		pbn_ni8430_8 },
+	{	PCI_VENDOR_ID_NI, PCI_DEVICE_ID_NI_PCI8430_2328,
+		PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+		pbn_ni8430_8 },
+	{	PCI_VENDOR_ID_NI, PCI_DEVICE_ID_NI_PXI8430_23216,
+		PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+		pbn_ni8430_16 },
+	{	PCI_VENDOR_ID_NI, PCI_DEVICE_ID_NI_PCI8430_23216,
+		PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+		pbn_ni8430_16 },
+	{	PCI_VENDOR_ID_NI, PCI_DEVICE_ID_NI_PXI8432_2322,
+		PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+		pbn_ni8430_2 },
+	{	PCI_VENDOR_ID_NI, PCI_DEVICE_ID_NI_PCI8432_2322,
+		PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+		pbn_ni8430_2 },
+	{	PCI_VENDOR_ID_NI, PCI_DEVICE_ID_NI_PXI8432_2324,
+		PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+		pbn_ni8430_4 },
+	{	PCI_VENDOR_ID_NI, PCI_DEVICE_ID_NI_PCI8432_2324,
+		PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+		pbn_ni8430_4 },
+
+	/*
+	* ADDI-DATA GmbH communication cards <info@addi-data.com>
+	*/
+	{	PCI_VENDOR_ID_ADDIDATA,
+		PCI_DEVICE_ID_ADDIDATA_APCI7500,
+		PCI_ANY_ID,
+		PCI_ANY_ID,
+		0,
+		0,
+		pbn_b0_4_115200 },
+
+	{	PCI_VENDOR_ID_ADDIDATA,
+		PCI_DEVICE_ID_ADDIDATA_APCI7420,
+		PCI_ANY_ID,
+		PCI_ANY_ID,
+		0,
+		0,
+		pbn_b0_2_115200 },
+
+	{	PCI_VENDOR_ID_ADDIDATA,
+		PCI_DEVICE_ID_ADDIDATA_APCI7300,
+		PCI_ANY_ID,
+		PCI_ANY_ID,
+		0,
+		0,
+		pbn_b0_1_115200 },
+
+	{	PCI_VENDOR_ID_ADDIDATA_OLD,
+		PCI_DEVICE_ID_ADDIDATA_APCI7800,
+		PCI_ANY_ID,
+		PCI_ANY_ID,
+		0,
+		0,
+		pbn_b1_8_115200 },
+
+	{	PCI_VENDOR_ID_ADDIDATA,
+		PCI_DEVICE_ID_ADDIDATA_APCI7500_2,
+		PCI_ANY_ID,
+		PCI_ANY_ID,
+		0,
+		0,
+		pbn_b0_4_115200 },
+
+	{	PCI_VENDOR_ID_ADDIDATA,
+		PCI_DEVICE_ID_ADDIDATA_APCI7420_2,
+		PCI_ANY_ID,
+		PCI_ANY_ID,
+		0,
+		0,
+		pbn_b0_2_115200 },
+
+	{	PCI_VENDOR_ID_ADDIDATA,
+		PCI_DEVICE_ID_ADDIDATA_APCI7300_2,
+		PCI_ANY_ID,
+		PCI_ANY_ID,
+		0,
+		0,
+		pbn_b0_1_115200 },
+
+	{	PCI_VENDOR_ID_ADDIDATA,
+		PCI_DEVICE_ID_ADDIDATA_APCI7500_3,
+		PCI_ANY_ID,
+		PCI_ANY_ID,
+		0,
+		0,
+		pbn_b0_4_115200 },
+
+	{	PCI_VENDOR_ID_ADDIDATA,
+		PCI_DEVICE_ID_ADDIDATA_APCI7420_3,
+		PCI_ANY_ID,
+		PCI_ANY_ID,
+		0,
+		0,
+		pbn_b0_2_115200 },
+
+	{	PCI_VENDOR_ID_ADDIDATA,
+		PCI_DEVICE_ID_ADDIDATA_APCI7300_3,
+		PCI_ANY_ID,
+		PCI_ANY_ID,
+		0,
+		0,
+		pbn_b0_1_115200 },
+
+	{	PCI_VENDOR_ID_ADDIDATA,
+		PCI_DEVICE_ID_ADDIDATA_APCI7800_3,
+		PCI_ANY_ID,
+		PCI_ANY_ID,
+		0,
+		0,
+		pbn_b0_8_115200 },
+
+	{	PCI_VENDOR_ID_ADDIDATA,
+		PCI_DEVICE_ID_ADDIDATA_APCIe7500,
+		PCI_ANY_ID,
+		PCI_ANY_ID,
+		0,
+		0,
+		pbn_ADDIDATA_PCIe_4_3906250 },
+
+	{	PCI_VENDOR_ID_ADDIDATA,
+		PCI_DEVICE_ID_ADDIDATA_APCIe7420,
+		PCI_ANY_ID,
+		PCI_ANY_ID,
+		0,
+		0,
+		pbn_ADDIDATA_PCIe_2_3906250 },
+
+	{	PCI_VENDOR_ID_ADDIDATA,
+		PCI_DEVICE_ID_ADDIDATA_APCIe7300,
+		PCI_ANY_ID,
+		PCI_ANY_ID,
+		0,
+		0,
+		pbn_ADDIDATA_PCIe_1_3906250 },
+
+	{	PCI_VENDOR_ID_ADDIDATA,
+		PCI_DEVICE_ID_ADDIDATA_APCIe7800,
+		PCI_ANY_ID,
+		PCI_ANY_ID,
+		0,
+		0,
+		pbn_ADDIDATA_PCIe_8_3906250 },
+
+	{	PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9835,
+		PCI_VENDOR_ID_IBM, 0x0299,
+		0, 0, pbn_b0_bt_2_115200 },
+
+	{	PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9901,
+		0xA000, 0x1000,
+		0, 0, pbn_b0_1_115200 },
+
+	/* the 9901 is a rebranded 9912 */
+	{	PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9912,
+		0xA000, 0x1000,
+		0, 0, pbn_b0_1_115200 },
+
+	{	PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9922,
+		0xA000, 0x1000,
+		0, 0, pbn_b0_1_115200 },
+
+	{	PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9904,
+		0xA000, 0x1000,
+		0, 0, pbn_b0_1_115200 },
+
+	{	PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9900,
+		0xA000, 0x1000,
+		0, 0, pbn_b0_1_115200 },
+
+	{	PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9900,
+		0xA000, 0x3002,
+		0, 0, pbn_NETMOS9900_2s_115200 },
+
+	/*
+	 * Best Connectivity and Rosewill PCI Multi I/O cards
+	 */
+
+	{	PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9865,
+		0xA000, 0x1000,
+		0, 0, pbn_b0_1_115200 },
+
+	{	PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9865,
+		0xA000, 0x3002,
+		0, 0, pbn_b0_bt_2_115200 },
+
+	{	PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9865,
+		0xA000, 0x3004,
+		0, 0, pbn_b0_bt_4_115200 },
+	/* Intel CE4100 */
+	{	PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_CE4100_UART,
+		PCI_ANY_ID,  PCI_ANY_ID, 0, 0,
+		pbn_ce4100_1_115200 },
+
+	/*
+	 * Intel Quark x1000
+	 */
+	{	PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_QRK_UART,
+		PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+		pbn_qrk },
+	/*
+	 * Cronyx Omega PCI
+	 */
+	{	PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_CRONYX_OMEGA,
+		PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+		pbn_omegapci },
+
+	/*
+	 * Broadcom TruManage
+	 */
+	{	PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_BROADCOM_TRUMANAGE,
+		PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+		pbn_brcm_trumanage },
+
+	/*
+	 * These entries match devices with class COMMUNICATION_SERIAL,
+	 * COMMUNICATION_MODEM or COMMUNICATION_MULTISERIAL
+	 */
+	{	PCI_ANY_ID, PCI_ANY_ID,
+		PCI_ANY_ID, PCI_ANY_ID,
+		PCI_CLASS_COMMUNICATION_SERIAL << 8,
+		0xffff00, pbn_default },
+	{	PCI_ANY_ID, PCI_ANY_ID,
+		PCI_ANY_ID, PCI_ANY_ID,
+		PCI_CLASS_COMMUNICATION_MODEM << 8,
+		0xffff00, pbn_default },
+	{	PCI_ANY_ID, PCI_ANY_ID,
+		PCI_ANY_ID, PCI_ANY_ID,
+		PCI_CLASS_COMMUNICATION_MULTISERIAL << 8,
+		0xffff00, pbn_default },
+	{ 0, }
+};
+
+static pci_ers_result_t serial8250_io_error_detected(struct pci_dev *dev,
+						pci_channel_state_t state)
+{
+	struct serial_private *priv = pci_get_drvdata(dev);
+
+	if (state == pci_channel_io_perm_failure)
+		return PCI_ERS_RESULT_DISCONNECT;
+
+	if (priv)
+		pciserial_suspend_ports(priv);
+
+	pci_disable_device(dev);
+
+	return PCI_ERS_RESULT_NEED_RESET;
+}
+
+static pci_ers_result_t serial8250_io_slot_reset(struct pci_dev *dev)
+{
+	int rc;
+
+	rc = pci_enable_device(dev);
+
+	if (rc)
+		return PCI_ERS_RESULT_DISCONNECT;
+
+	pci_restore_state(dev);
+	pci_save_state(dev);
+
+	return PCI_ERS_RESULT_RECOVERED;
+}
+
+static void serial8250_io_resume(struct pci_dev *dev)
+{
+	struct serial_private *priv = pci_get_drvdata(dev);
+
+	if (priv)
+		pciserial_resume_ports(priv);
+}
+
+static struct pci_error_handlers serial8250_err_handler = {
+	.error_detected = serial8250_io_error_detected,
+	.slot_reset = serial8250_io_slot_reset,
+	.resume = serial8250_io_resume,
+};
+
+static struct pci_driver serial_pci_driver = {
+	.name		= "serial",
+	.probe		= pciserial_init_one,
+	.remove		= __devexit_p(pciserial_remove_one),
+#ifdef CONFIG_PM
+	.suspend	= pciserial_suspend_one,
+	.resume		= pciserial_resume_one,
+#endif
+	.id_table	= serial_pci_tbl,
+	.err_handler	= &serial8250_err_handler,
+};
+
+static int __init serial8250_pci_init(void)
+{
+	return pci_register_driver(&serial_pci_driver);
+}
+
+static void __exit serial8250_pci_exit(void)
+{
+	pci_unregister_driver(&serial_pci_driver);
+}
+
+module_init(serial8250_pci_init);
+module_exit(serial8250_pci_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Generic 8250/16x50 PCI serial probe module");
+MODULE_DEVICE_TABLE(pci, serial_pci_tbl);
diff --git a/ap/os/linux/linux-3.4.x/drivers/tty/serial/8250/8250_pnp.c b/ap/os/linux/linux-3.4.x/drivers/tty/serial/8250/8250_pnp.c
new file mode 100644
index 0000000..a2f2365
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/tty/serial/8250/8250_pnp.c
@@ -0,0 +1,524 @@
+/*
+ *  Probe module for 8250/16550-type ISAPNP serial ports.
+ *
+ *  Based on drivers/char/serial.c, by Linus Torvalds, Theodore Ts'o.
+ *
+ *  Copyright (C) 2001 Russell King, All Rights Reserved.
+ *
+ *  Ported to the Linux PnP Layer - (C) Adam Belay.
+ *
+ * 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.
+ */
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/pci.h>
+#include <linux/pnp.h>
+#include <linux/string.h>
+#include <linux/kernel.h>
+#include <linux/serial_core.h>
+#include <linux/bitops.h>
+
+#include <asm/byteorder.h>
+
+#include "8250.h"
+
+#define UNKNOWN_DEV 0x3000
+
+
+static const struct pnp_device_id pnp_dev_table[] = {
+	/* Archtek America Corp. */
+	/* Archtek SmartLink Modem 3334BT Plug & Play */
+	{	"AAC000F",		0	},
+	/* Anchor Datacomm BV */
+	/* SXPro 144 External Data Fax Modem Plug & Play */
+	{	"ADC0001",		0	},
+	/* SXPro 288 External Data Fax Modem Plug & Play */
+	{	"ADC0002",		0	},
+	/* PROLiNK 1456VH ISA PnP K56flex Fax Modem */
+	{	"AEI0250",		0	},
+	/* Actiontec ISA PNP 56K X2 Fax Modem */
+	{	"AEI1240",		0	},
+	/* Rockwell 56K ACF II Fax+Data+Voice Modem */
+	{	"AKY1021",		0 /*SPCI_FL_NO_SHIRQ*/	},
+	/* AZT3005 PnP SOUND DEVICE */
+	{	"AZT4001",		0	},
+	/* Best Data Products Inc. Smart One 336F PnP Modem */
+	{	"BDP3336",		0	},
+	/*  Boca Research */
+	/* Boca Complete Ofc Communicator 14.4 Data-FAX */
+	{	"BRI0A49",		0	},
+	/* Boca Research 33,600 ACF Modem */
+	{	"BRI1400",		0	},
+	/* Boca 33.6 Kbps Internal FD34FSVD */
+	{	"BRI3400",		0	},
+	/* Boca 33.6 Kbps Internal FD34FSVD */
+	{	"BRI0A49",		0	},
+	/* Best Data Products Inc. Smart One 336F PnP Modem */
+	{	"BDP3336",		0	},
+	/* Computer Peripherals Inc */
+	/* EuroViVa CommCenter-33.6 SP PnP */
+	{	"CPI4050",		0	},
+	/* Creative Labs */
+	/* Creative Labs Phone Blaster 28.8 DSVD PnP Voice */
+	{	"CTL3001",		0	},
+	/* Creative Labs Modem Blaster 28.8 DSVD PnP Voice */
+	{	"CTL3011",		0	},
+	/* Davicom ISA 33.6K Modem */
+	{	"DAV0336",		0	},
+	/* Creative */
+	/* Creative Modem Blaster Flash56 DI5601-1 */
+	{	"DMB1032",		0	},
+	/* Creative Modem Blaster V.90 DI5660 */
+	{	"DMB2001",		0	},
+	/* E-Tech */
+	/* E-Tech CyberBULLET PC56RVP */
+	{	"ETT0002",		0	},
+	/* FUJITSU */
+	/* Fujitsu 33600 PnP-I2 R Plug & Play */
+	{	"FUJ0202",		0	},
+	/* Fujitsu FMV-FX431 Plug & Play */
+	{	"FUJ0205",		0	},
+	/* Fujitsu 33600 PnP-I4 R Plug & Play */
+	{	"FUJ0206",		0	},
+	/* Fujitsu Fax Voice 33600 PNP-I5 R Plug & Play */
+	{	"FUJ0209",		0	},
+	/* Archtek America Corp. */
+	/* Archtek SmartLink Modem 3334BT Plug & Play */
+	{	"GVC000F",		0	},
+	/* Archtek SmartLink Modem 3334BRV 33.6K Data Fax Voice */
+	{	"GVC0303",		0	},
+	/* Hayes */
+	/* Hayes Optima 288 V.34-V.FC + FAX + Voice Plug & Play */
+	{	"HAY0001",		0	},
+	/* Hayes Optima 336 V.34 + FAX + Voice PnP */
+	{	"HAY000C",		0	},
+	/* Hayes Optima 336B V.34 + FAX + Voice PnP */
+	{	"HAY000D",		0	},
+	/* Hayes Accura 56K Ext Fax Modem PnP */
+	{	"HAY5670",		0	},
+	/* Hayes Accura 56K Ext Fax Modem PnP */
+	{	"HAY5674",		0	},
+	/* Hayes Accura 56K Fax Modem PnP */
+	{	"HAY5675",		0	},
+	/* Hayes 288, V.34 + FAX */
+	{	"HAYF000",		0	},
+	/* Hayes Optima 288 V.34 + FAX + Voice, Plug & Play */
+	{	"HAYF001",		0	},
+	/* IBM */
+	/* IBM Thinkpad 701 Internal Modem Voice */
+	{	"IBM0033",		0	},
+	/* Intermec */
+	/* Intermec CV60 touchscreen port */
+	{	"PNP4972",		0	},
+	/* Intertex */
+	/* Intertex 28k8 33k6 Voice EXT PnP */
+	{	"IXDC801",		0	},
+	/* Intertex 33k6 56k Voice EXT PnP */
+	{	"IXDC901",		0	},
+	/* Intertex 28k8 33k6 Voice SP EXT PnP */
+	{	"IXDD801",		0	},
+	/* Intertex 33k6 56k Voice SP EXT PnP */
+	{	"IXDD901",		0	},
+	/* Intertex 28k8 33k6 Voice SP INT PnP */
+	{	"IXDF401",		0	},
+	/* Intertex 28k8 33k6 Voice SP EXT PnP */
+	{	"IXDF801",		0	},
+	/* Intertex 33k6 56k Voice SP EXT PnP */
+	{	"IXDF901",		0	},
+	/* Kortex International */
+	/* KORTEX 28800 Externe PnP */
+	{	"KOR4522",		0	},
+	/* KXPro 33.6 Vocal ASVD PnP */
+	{	"KORF661",		0	},
+	/* Lasat */
+	/* LASAT Internet 33600 PnP */
+	{	"LAS4040",		0	},
+	/* Lasat Safire 560 PnP */
+	{	"LAS4540",		0	},
+	/* Lasat Safire 336  PnP */
+	{	"LAS5440",		0	},
+	/* Microcom, Inc. */
+	/* Microcom TravelPorte FAST V.34 Plug & Play */
+	{	"MNP0281",		0	},
+	/* Microcom DeskPorte V.34 FAST or FAST+ Plug & Play */
+	{	"MNP0336",		0	},
+	/* Microcom DeskPorte FAST EP 28.8 Plug & Play */
+	{	"MNP0339",		0	},
+	/* Microcom DeskPorte 28.8P Plug & Play */
+	{	"MNP0342",		0	},
+	/* Microcom DeskPorte FAST ES 28.8 Plug & Play */
+	{	"MNP0500",		0	},
+	/* Microcom DeskPorte FAST ES 28.8 Plug & Play */
+	{	"MNP0501",		0	},
+	/* Microcom DeskPorte 28.8S Internal Plug & Play */
+	{	"MNP0502",		0	},
+	/* Motorola */
+	/* Motorola BitSURFR Plug & Play */
+	{	"MOT1105",		0	},
+	/* Motorola TA210 Plug & Play */
+	{	"MOT1111",		0	},
+	/* Motorola HMTA 200 (ISDN) Plug & Play */
+	{	"MOT1114",		0	},
+	/* Motorola BitSURFR Plug & Play */
+	{	"MOT1115",		0	},
+	/* Motorola Lifestyle 28.8 Internal */
+	{	"MOT1190",		0	},
+	/* Motorola V.3400 Plug & Play */
+	{	"MOT1501",		0	},
+	/* Motorola Lifestyle 28.8 V.34 Plug & Play */
+	{	"MOT1502",		0	},
+	/* Motorola Power 28.8 V.34 Plug & Play */
+	{	"MOT1505",		0	},
+	/* Motorola ModemSURFR External 28.8 Plug & Play */
+	{	"MOT1509",		0	},
+	/* Motorola Premier 33.6 Desktop Plug & Play */
+	{	"MOT150A",		0	},
+	/* Motorola VoiceSURFR 56K External PnP */
+	{	"MOT150F",		0	},
+	/* Motorola ModemSURFR 56K External PnP */
+	{	"MOT1510",		0	},
+	/* Motorola ModemSURFR 56K Internal PnP */
+	{	"MOT1550",		0	},
+	/* Motorola ModemSURFR Internal 28.8 Plug & Play */
+	{	"MOT1560",		0	},
+	/* Motorola Premier 33.6 Internal Plug & Play */
+	{	"MOT1580",		0	},
+	/* Motorola OnlineSURFR 28.8 Internal Plug & Play */
+	{	"MOT15B0",		0	},
+	/* Motorola VoiceSURFR 56K Internal PnP */
+	{	"MOT15F0",		0	},
+	/* Com 1 */
+	/*  Deskline K56 Phone System PnP */
+	{	"MVX00A1",		0	},
+	/* PC Rider K56 Phone System PnP */
+	{	"MVX00F2",		0	},
+	/* NEC 98NOTE SPEAKER PHONE FAX MODEM(33600bps) */
+	{	"nEC8241",		0	},
+	/* Pace 56 Voice Internal Plug & Play Modem */
+	{	"PMC2430",		0	},
+	/* Generic */
+	/* Generic standard PC COM port	 */
+	{	"PNP0500",		0	},
+	/* Generic 16550A-compatible COM port */
+	{	"PNP0501",		0	},
+	/* Compaq 14400 Modem */
+	{	"PNPC000",		0	},
+	/* Compaq 2400/9600 Modem */
+	{	"PNPC001",		0	},
+	/* Dial-Up Networking Serial Cable between 2 PCs */
+	{	"PNPC031",		0	},
+	/* Dial-Up Networking Parallel Cable between 2 PCs */
+	{	"PNPC032",		0	},
+	/* Standard 9600 bps Modem */
+	{	"PNPC100",		0	},
+	/* Standard 14400 bps Modem */
+	{	"PNPC101",		0	},
+	/*  Standard 28800 bps Modem*/
+	{	"PNPC102",		0	},
+	/*  Standard Modem*/
+	{	"PNPC103",		0	},
+	/*  Standard 9600 bps Modem*/
+	{	"PNPC104",		0	},
+	/*  Standard 14400 bps Modem*/
+	{	"PNPC105",		0	},
+	/*  Standard 28800 bps Modem*/
+	{	"PNPC106",		0	},
+	/*  Standard Modem */
+	{	"PNPC107",		0	},
+	/* Standard 9600 bps Modem */
+	{	"PNPC108",		0	},
+	/* Standard 14400 bps Modem */
+	{	"PNPC109",		0	},
+	/* Standard 28800 bps Modem */
+	{	"PNPC10A",		0	},
+	/* Standard Modem */
+	{	"PNPC10B",		0	},
+	/* Standard 9600 bps Modem */
+	{	"PNPC10C",		0	},
+	/* Standard 14400 bps Modem */
+	{	"PNPC10D",		0	},
+	/* Standard 28800 bps Modem */
+	{	"PNPC10E",		0	},
+	/* Standard Modem */
+	{	"PNPC10F",		0	},
+	/* Standard PCMCIA Card Modem */
+	{	"PNP2000",		0	},
+	/* Rockwell */
+	/* Modular Technology */
+	/* Rockwell 33.6 DPF Internal PnP */
+	/* Modular Technology 33.6 Internal PnP */
+	{	"ROK0030",		0	},
+	/* Kortex International */
+	/* KORTEX 14400 Externe PnP */
+	{	"ROK0100",		0	},
+	/* Rockwell 28.8 */
+	{	"ROK4120",		0	},
+	/* Viking Components, Inc */
+	/* Viking 28.8 INTERNAL Fax+Data+Voice PnP */
+	{	"ROK4920",		0	},
+	/* Rockwell */
+	/* British Telecom */
+	/* Modular Technology */
+	/* Rockwell 33.6 DPF External PnP */
+	/* BT Prologue 33.6 External PnP */
+	/* Modular Technology 33.6 External PnP */
+	{	"RSS00A0",		0	},
+	/* Viking 56K FAX INT */
+	{	"RSS0262",		0	},
+	/* K56 par,VV,Voice,Speakphone,AudioSpan,PnP */
+	{       "RSS0250",              0       },
+	/* SupraExpress 28.8 Data/Fax PnP modem */
+	{	"SUP1310",		0	},
+	/* SupraExpress 336i PnP Voice Modem */
+	{	"SUP1381",		0	},
+	/* SupraExpress 33.6 Data/Fax PnP modem */
+	{	"SUP1421",		0	},
+	/* SupraExpress 33.6 Data/Fax PnP modem */
+	{	"SUP1590",		0	},
+	/* SupraExpress 336i Sp ASVD */
+	{	"SUP1620",		0	},
+	/* SupraExpress 33.6 Data/Fax PnP modem */
+	{	"SUP1760",		0	},
+	/* SupraExpress 56i Sp Intl */
+	{	"SUP2171",		0	},
+	/* Phoebe Micro */
+	/* Phoebe Micro 33.6 Data Fax 1433VQH Plug & Play */
+	{	"TEX0011",		0	},
+	/* Archtek America Corp. */
+	/* Archtek SmartLink Modem 3334BT Plug & Play */
+	{	"UAC000F",		0	},
+	/* 3Com Corp. */
+	/* Gateway Telepath IIvi 33.6 */
+	{	"USR0000",		0	},
+	/* U.S. Robotics Sporster 33.6K Fax INT PnP */
+	{	"USR0002",		0	},
+	/*  Sportster Vi 14.4 PnP FAX Voicemail */
+	{	"USR0004",		0	},
+	/* U.S. Robotics 33.6K Voice INT PnP */
+	{	"USR0006",		0	},
+	/* U.S. Robotics 33.6K Voice EXT PnP */
+	{	"USR0007",		0	},
+	/* U.S. Robotics Courier V.Everything INT PnP */
+	{	"USR0009",		0	},
+	/* U.S. Robotics 33.6K Voice INT PnP */
+	{	"USR2002",		0	},
+	/* U.S. Robotics 56K Voice INT PnP */
+	{	"USR2070",		0	},
+	/* U.S. Robotics 56K Voice EXT PnP */
+	{	"USR2080",		0	},
+	/* U.S. Robotics 56K FAX INT */
+	{	"USR3031",		0	},
+	/* U.S. Robotics 56K FAX INT */
+	{	"USR3050",		0	},
+	/* U.S. Robotics 56K Voice INT PnP */
+	{	"USR3070",		0	},
+	/* U.S. Robotics 56K Voice EXT PnP */
+	{	"USR3080",		0	},
+	/* U.S. Robotics 56K Voice INT PnP */
+	{	"USR3090",		0	},
+	/* U.S. Robotics 56K Message  */
+	{	"USR9100",		0	},
+	/* U.S. Robotics 56K FAX EXT PnP*/
+	{	"USR9160",		0	},
+	/* U.S. Robotics 56K FAX INT PnP*/
+	{	"USR9170",		0	},
+	/* U.S. Robotics 56K Voice EXT PnP*/
+	{	"USR9180",		0	},
+	/* U.S. Robotics 56K Voice INT PnP*/
+	{	"USR9190",		0	},
+	/* Wacom tablets */
+	{	"WACFXXX",		0	},
+	/* Compaq touchscreen */
+	{       "FPI2002",              0 },
+	/* Fujitsu Stylistic touchscreens */
+	{       "FUJ02B2",              0 },
+	{       "FUJ02B3",              0 },
+	/* Fujitsu Stylistic LT touchscreens */
+	{       "FUJ02B4",              0 },
+	/* Passive Fujitsu Stylistic touchscreens */
+	{       "FUJ02B6",              0 },
+	{       "FUJ02B7",              0 },
+	{       "FUJ02B8",              0 },
+	{       "FUJ02B9",              0 },
+	{       "FUJ02BC",              0 },
+	/* Fujitsu Wacom Tablet PC device */
+	{	"FUJ02E5",		0	},
+	/* Fujitsu P-series tablet PC device */
+	{	"FUJ02E6",		0	},
+	/* Fujitsu Wacom 2FGT Tablet PC device */
+	{	"FUJ02E7",		0	},
+	/* Fujitsu Wacom 1FGT Tablet PC device */
+	{	"FUJ02E9",		0	},
+	/*
+	 * LG C1 EXPRESS DUAL (C1-PB11A3) touch screen (actually a FUJ02E6 in
+	 * disguise)
+	 */
+	{	"LTS0001",		0       },
+	/* Rockwell's (PORALiNK) 33600 INT PNP */
+	{	"WCI0003",		0	},
+	/* Unknown PnP modems */
+	{	"PNPCXXX",		UNKNOWN_DEV	},
+	/* More unknown PnP modems */
+	{	"PNPDXXX",		UNKNOWN_DEV	},
+	{	"",			0	}
+};
+
+MODULE_DEVICE_TABLE(pnp, pnp_dev_table);
+
+static char *modem_names[] __devinitdata = {
+	"MODEM", "Modem", "modem", "FAX", "Fax", "fax",
+	"56K", "56k", "K56", "33.6", "28.8", "14.4",
+	"33,600", "28,800", "14,400", "33.600", "28.800", "14.400",
+	"33600", "28800", "14400", "V.90", "V.34", "V.32", NULL
+};
+
+static int __devinit check_name(char *name)
+{
+	char **tmp;
+
+	for (tmp = modem_names; *tmp; tmp++)
+		if (strstr(name, *tmp))
+			return 1;
+
+	return 0;
+}
+
+static int __devinit check_resources(struct pnp_dev *dev)
+{
+	resource_size_t base[] = {0x2f8, 0x3f8, 0x2e8, 0x3e8};
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(base); i++) {
+		if (pnp_possible_config(dev, IORESOURCE_IO, base[i], 8))
+			return 1;
+	}
+
+	return 0;
+}
+
+/*
+ * Given a complete unknown PnP device, try to use some heuristics to
+ * detect modems. Currently use such heuristic set:
+ *     - dev->name or dev->bus->name must contain "modem" substring;
+ *     - device must have only one IO region (8 byte long) with base address
+ *       0x2e8, 0x3e8, 0x2f8 or 0x3f8.
+ *
+ * Such detection looks very ugly, but can detect at least some of numerous
+ * PnP modems, alternatively we must hardcode all modems in pnp_devices[]
+ * table.
+ */
+static int __devinit serial_pnp_guess_board(struct pnp_dev *dev, int *flags)
+{
+	if (!(check_name(pnp_dev_name(dev)) ||
+		(dev->card && check_name(dev->card->name))))
+			return -ENODEV;
+
+	if (check_resources(dev))
+		return 0;
+
+	return -ENODEV;
+}
+
+static int __devinit
+serial_pnp_probe(struct pnp_dev *dev, const struct pnp_device_id *dev_id)
+{
+	struct uart_port port;
+	int ret, line, flags = dev_id->driver_data;
+
+	if (flags & UNKNOWN_DEV) {
+		ret = serial_pnp_guess_board(dev, &flags);
+		if (ret < 0)
+			return ret;
+	}
+
+	memset(&port, 0, sizeof(struct uart_port));
+	if (pnp_irq_valid(dev, 0))
+		port.irq = pnp_irq(dev, 0);
+	if (pnp_port_valid(dev, 0)) {
+		port.iobase = pnp_port_start(dev, 0);
+		port.iotype = UPIO_PORT;
+	} else if (pnp_mem_valid(dev, 0)) {
+		port.mapbase = pnp_mem_start(dev, 0);
+		port.iotype = UPIO_MEM;
+		port.flags = UPF_IOREMAP;
+	} else
+		return -ENODEV;
+
+#ifdef SERIAL_DEBUG_PNP
+	printk(KERN_DEBUG
+		"Setup PNP port: port %x, mem 0x%lx, irq %d, type %d\n",
+		       port.iobase, port.mapbase, port.irq, port.iotype);
+#endif
+
+	port.flags |= UPF_SKIP_TEST | UPF_BOOT_AUTOCONF;
+	if (pnp_irq_flags(dev, 0) & IORESOURCE_IRQ_SHAREABLE)
+		port.flags |= UPF_SHARE_IRQ;
+	port.uartclk = 1843200;
+	port.dev = &dev->dev;
+
+	line = serial8250_register_port(&port);
+	if (line < 0)
+		return -ENODEV;
+
+	pnp_set_drvdata(dev, (void *)((long)line + 1));
+	return 0;
+}
+
+static void __devexit serial_pnp_remove(struct pnp_dev *dev)
+{
+	long line = (long)pnp_get_drvdata(dev);
+	if (line)
+		serial8250_unregister_port(line - 1);
+}
+
+#ifdef CONFIG_PM
+static int serial_pnp_suspend(struct pnp_dev *dev, pm_message_t state)
+{
+	long line = (long)pnp_get_drvdata(dev);
+
+	if (!line)
+		return -ENODEV;
+	serial8250_suspend_port(line - 1);
+	return 0;
+}
+
+static int serial_pnp_resume(struct pnp_dev *dev)
+{
+	long line = (long)pnp_get_drvdata(dev);
+
+	if (!line)
+		return -ENODEV;
+	serial8250_resume_port(line - 1);
+	return 0;
+}
+#else
+#define serial_pnp_suspend NULL
+#define serial_pnp_resume NULL
+#endif /* CONFIG_PM */
+
+static struct pnp_driver serial_pnp_driver = {
+	.name		= "serial",
+	.probe		= serial_pnp_probe,
+	.remove		= __devexit_p(serial_pnp_remove),
+	.suspend	= serial_pnp_suspend,
+	.resume		= serial_pnp_resume,
+	.id_table	= pnp_dev_table,
+};
+
+static int __init serial8250_pnp_init(void)
+{
+	return pnp_register_driver(&serial_pnp_driver);
+}
+
+static void __exit serial8250_pnp_exit(void)
+{
+	pnp_unregister_driver(&serial_pnp_driver);
+}
+
+module_init(serial8250_pnp_init);
+module_exit(serial8250_pnp_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Generic 8250/16x50 PnP serial driver");
diff --git a/ap/os/linux/linux-3.4.x/drivers/tty/serial/8250/Kconfig b/ap/os/linux/linux-3.4.x/drivers/tty/serial/8250/Kconfig
new file mode 100644
index 0000000..591f801
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/tty/serial/8250/Kconfig
@@ -0,0 +1,280 @@
+#
+# The 8250/16550 serial drivers.  You shouldn't be in this list unless
+# you somehow have an implicit or explicit dependency on SERIAL_8250.
+#
+
+config SERIAL_8250
+	tristate "8250/16550 and compatible serial support"
+	select SERIAL_CORE
+	---help---
+	  This selects whether you want to include the driver for the standard
+	  serial ports.  The standard answer is Y.  People who might say N
+	  here are those that are setting up dedicated Ethernet WWW/FTP
+	  servers, or users that have one of the various bus mice instead of a
+	  serial mouse and don't intend to use their machine's standard serial
+	  port for anything.  (Note that the Cyclades and Stallion multi
+	  serial port drivers do not need this driver built in for them to
+	  work.)
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called 8250.
+	  [WARNING: Do not compile this driver as a module if you are using
+	  non-standard serial ports, since the configuration information will
+	  be lost when the driver is unloaded.  This limitation may be lifted
+	  in the future.]
+
+	  BTW1: If you have a mouseman serial mouse which is not recognized by
+	  the X window system, try running gpm first.
+
+	  BTW2: If you intend to use a software modem (also called Winmodem)
+	  under Linux, forget it.  These modems are crippled and require
+	  proprietary drivers which are only available under Windows.
+
+	  Most people will say Y or M here, so that they can use serial mice,
+	  modems and similar devices connecting to the standard serial ports.
+
+config SERIAL_8250_CONSOLE
+	bool "Console on 8250/16550 and compatible serial port"
+	depends on SERIAL_8250=y
+	select SERIAL_CORE_CONSOLE
+	---help---
+	  If you say Y here, it will be possible to use a serial port as the
+	  system console (the system console is the device which receives all
+	  kernel messages and warnings and which allows logins in single user
+	  mode). This could be useful if some terminal or printer is connected
+	  to that serial port.
+
+	  Even if you say Y here, the currently visible virtual console
+	  (/dev/tty0) will still be used as the system console by default, but
+	  you can alter that using a kernel command line option such as
+	  "console=ttyS1". (Try "man bootparam" or see the documentation of
+	  your boot loader (grub or lilo or loadlin) about how to pass options
+	  to the kernel at boot time.)
+
+	  If you don't have a VGA card installed and you say Y here, the
+	  kernel will automatically use the first serial line, /dev/ttyS0, as
+	  system console.
+
+	  You can set that using a kernel command line option such as
+	  "console=uart8250,io,0x3f8,9600n8"
+	  "console=uart8250,mmio,0xff5e0000,115200n8".
+	  and it will switch to normal serial console when the corresponding
+	  port is ready.
+	  "earlycon=uart8250,io,0x3f8,9600n8"
+	  "earlycon=uart8250,mmio,0xff5e0000,115200n8".
+	  it will not only setup early console.
+
+	  If unsure, say N.
+
+config FIX_EARLYCON_MEM
+	bool
+	depends on X86
+	default y
+
+config SERIAL_8250_GSC
+	tristate
+	depends on SERIAL_8250 && GSC
+	default SERIAL_8250
+
+config SERIAL_8250_PCI
+	tristate "8250/16550 PCI device support" if EXPERT
+	depends on SERIAL_8250 && PCI
+	default SERIAL_8250
+	help
+	  This builds standard PCI serial support. You may be able to
+	  disable this feature if you only need legacy serial support.
+	  Saves about 9K.
+
+config SERIAL_8250_PNP
+	tristate "8250/16550 PNP device support" if EXPERT
+	depends on SERIAL_8250 && PNP
+	default SERIAL_8250
+	help
+	  This builds standard PNP serial support. You may be able to
+	  disable this feature if you only need legacy serial support.
+
+config SERIAL_8250_HP300
+	tristate
+	depends on SERIAL_8250 && HP300
+	default SERIAL_8250
+
+config SERIAL_8250_CS
+	tristate "8250/16550 PCMCIA device support"
+	depends on PCMCIA && SERIAL_8250
+	---help---
+	  Say Y here to enable support for 16-bit PCMCIA serial devices,
+	  including serial port cards, modems, and the modem functions of
+	  multi-function Ethernet/modem cards. (PCMCIA- or PC-cards are
+	  credit-card size devices often used with laptops.)
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called serial_cs.
+
+	  If unsure, say N.
+
+config SERIAL_8250_NR_UARTS
+	int "Maximum number of 8250/16550 serial ports"
+	depends on SERIAL_8250
+	default "4"
+	help
+	  Set this to the number of serial ports you want the driver
+	  to support.  This includes any ports discovered via ACPI or
+	  PCI enumeration and any ports that may be added at run-time
+	  via hot-plug, or any ISA multi-port serial cards.
+
+config SERIAL_8250_RUNTIME_UARTS
+	int "Number of 8250/16550 serial ports to register at runtime"
+	depends on SERIAL_8250
+	range 0 SERIAL_8250_NR_UARTS
+	default "4"
+	help
+	  Set this to the maximum number of serial ports you want
+	  the kernel to register at boot time.  This can be overridden
+	  with the module parameter "nr_uarts", or boot-time parameter
+	  8250.nr_uarts
+
+config SERIAL_8250_EXTENDED
+	bool "Extended 8250/16550 serial driver options"
+	depends on SERIAL_8250
+	help
+	  If you wish to use any non-standard features of the standard "dumb"
+	  driver, say Y here. This includes HUB6 support, shared serial
+	  interrupts, special multiport support, support for more than the
+	  four COM 1/2/3/4 boards, etc.
+
+	  Note that the answer to this question won't directly affect the
+	  kernel: saying N will just cause the configurator to skip all
+	  the questions about serial driver options. If unsure, say N.
+
+config SERIAL_8250_MANY_PORTS
+	bool "Support more than 4 legacy serial ports"
+	depends on SERIAL_8250_EXTENDED && !IA64
+	help
+	  Say Y here if you have dumb serial boards other than the four
+	  standard COM 1/2/3/4 ports. This may happen if you have an AST
+	  FourPort, Accent Async, Boca (read the Boca mini-HOWTO, available
+	  from <http://www.tldp.org/docs.html#howto>), or other custom
+	  serial port hardware which acts similar to standard serial port
+	  hardware. If you only use the standard COM 1/2/3/4 ports, you can
+	  say N here to save some memory. You can also say Y if you have an
+	  "intelligent" multiport card such as Cyclades, Digiboards, etc.
+
+#
+# Multi-port serial cards
+#
+
+config SERIAL_8250_FOURPORT
+	tristate "Support Fourport cards"
+	depends on SERIAL_8250 != n && ISA && SERIAL_8250_MANY_PORTS
+	help
+	  Say Y here if you have an AST FourPort serial board.
+
+	  To compile this driver as a module, choose M here: the module
+	  will be called 8250_fourport.
+
+config SERIAL_8250_ACCENT
+	tristate "Support Accent cards"
+	depends on SERIAL_8250 != n && ISA && SERIAL_8250_MANY_PORTS
+	help
+	  Say Y here if you have an Accent Async serial board.
+
+	  To compile this driver as a module, choose M here: the module
+	  will be called 8250_accent.
+
+config SERIAL_8250_BOCA
+	tristate "Support Boca cards"
+	depends on SERIAL_8250 != n && ISA && SERIAL_8250_MANY_PORTS
+	help
+	  Say Y here if you have a Boca serial board.  Please read the Boca
+	  mini-HOWTO, available from <http://www.tldp.org/docs.html#howto>
+
+	  To compile this driver as a module, choose M here: the module
+	  will be called 8250_boca.
+
+config SERIAL_8250_EXAR_ST16C554
+	tristate "Support Exar ST16C554/554D Quad UART"
+	depends on SERIAL_8250 != n && ISA && SERIAL_8250_MANY_PORTS
+	help
+	  The Uplogix Envoy TU301 uses this Exar Quad UART.  If you are
+	  tinkering with your Envoy TU301, or have a machine with this UART,
+	  say Y here.
+
+	  To compile this driver as a module, choose M here: the module
+	  will be called 8250_exar_st16c554.
+
+config SERIAL_8250_HUB6
+	tristate "Support Hub6 cards"
+	depends on SERIAL_8250 != n && ISA && SERIAL_8250_MANY_PORTS
+	help
+	  Say Y here if you have a HUB6 serial board.
+
+	  To compile this driver as a module, choose M here: the module
+	  will be called 8250_hub6.
+
+#
+# Misc. options/drivers.
+#
+
+config SERIAL_8250_SHARE_IRQ
+	bool "Support for sharing serial interrupts"
+	depends on SERIAL_8250_EXTENDED
+	help
+	  Some serial boards have hardware support which allows multiple dumb
+	  serial ports on the same board to share a single IRQ. To enable
+	  support for this in the serial driver, say Y here.
+
+config SERIAL_8250_DETECT_IRQ
+	bool "Autodetect IRQ on standard ports (unsafe)"
+	depends on SERIAL_8250_EXTENDED
+	help
+	  Say Y here if you want the kernel to try to guess which IRQ
+	  to use for your serial port.
+
+	  This is considered unsafe; it is far better to configure the IRQ in
+	  a boot script using the setserial command.
+
+	  If unsure, say N.
+
+config SERIAL_8250_RSA
+	bool "Support RSA serial ports"
+	depends on SERIAL_8250_EXTENDED
+	help
+	  ::: To be written :::
+
+config SERIAL_8250_MCA
+	tristate "Support 8250-type ports on MCA buses"
+	depends on SERIAL_8250 != n && MCA
+	help
+	  Say Y here if you have a MCA serial ports.
+
+	  To compile this driver as a module, choose M here: the module
+	  will be called 8250_mca.
+
+config SERIAL_8250_ACORN
+	tristate "Acorn expansion card serial port support"
+	depends on ARCH_ACORN && SERIAL_8250
+	help
+	  If you have an Atomwide Serial card or Serial Port card for an Acorn
+	  system, say Y to this option.  The driver can handle 1, 2, or 3 port
+	  cards.  If unsure, say N.
+
+config SERIAL_8250_RM9K
+	bool "Support for MIPS RM9xxx integrated serial port"
+	depends on SERIAL_8250 != n && SERIAL_RM9000
+	select SERIAL_8250_SHARE_IRQ
+	help
+	  Selecting this option will add support for the integrated serial
+	  port hardware found on MIPS RM9122 and similar processors.
+	  If unsure, say N.
+
+config SERIAL_8250_FSL
+	bool
+	depends on SERIAL_8250_CONSOLE && PPC_UDBG_16550
+	default PPC
+
+config SERIAL_8250_DW
+	tristate "Support for Synopsys DesignWare 8250 quirks"
+	depends on SERIAL_8250 && OF
+	help
+	  Selecting this option will enable handling of the extra features
+	  present in the Synopsys DesignWare APB UART.
diff --git a/ap/os/linux/linux-3.4.x/drivers/tty/serial/8250/Makefile b/ap/os/linux/linux-3.4.x/drivers/tty/serial/8250/Makefile
new file mode 100644
index 0000000..867bba7
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/tty/serial/8250/Makefile
@@ -0,0 +1,20 @@
+#
+# Makefile for the 8250 serial device drivers.
+#
+
+obj-$(CONFIG_SERIAL_8250)		+= 8250.o
+obj-$(CONFIG_SERIAL_8250_PNP)		+= 8250_pnp.o
+obj-$(CONFIG_SERIAL_8250_GSC)		+= 8250_gsc.o
+obj-$(CONFIG_SERIAL_8250_PCI)		+= 8250_pci.o
+obj-$(CONFIG_SERIAL_8250_HP300)		+= 8250_hp300.o
+obj-$(CONFIG_SERIAL_8250_CS)		+= serial_cs.o
+obj-$(CONFIG_SERIAL_8250_ACORN)		+= 8250_acorn.o
+obj-$(CONFIG_SERIAL_8250_CONSOLE)	+= 8250_early.o
+obj-$(CONFIG_SERIAL_8250_FOURPORT)	+= 8250_fourport.o
+obj-$(CONFIG_SERIAL_8250_ACCENT)	+= 8250_accent.o
+obj-$(CONFIG_SERIAL_8250_BOCA)		+= 8250_boca.o
+obj-$(CONFIG_SERIAL_8250_EXAR_ST16C554)	+= 8250_exar_st16c554.o
+obj-$(CONFIG_SERIAL_8250_HUB6)		+= 8250_hub6.o
+obj-$(CONFIG_SERIAL_8250_MCA)		+= 8250_mca.o
+obj-$(CONFIG_SERIAL_8250_FSL)		+= 8250_fsl.o
+obj-$(CONFIG_SERIAL_8250_DW)		+= 8250_dw.o
diff --git a/ap/os/linux/linux-3.4.x/drivers/tty/serial/8250/serial_cs.c b/ap/os/linux/linux-3.4.x/drivers/tty/serial/8250/serial_cs.c
new file mode 100644
index 0000000..29b695d
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/tty/serial/8250/serial_cs.c
@@ -0,0 +1,869 @@
+/*======================================================================
+
+    A driver for PCMCIA serial devices
+
+    serial_cs.c 1.134 2002/05/04 05:48:53
+
+    The contents of this file are subject to the Mozilla Public
+    License Version 1.1 (the "License"); you may not use this file
+    except in compliance with the License. You may obtain a copy of
+    the License at http://www.mozilla.org/MPL/
+
+    Software distributed under the License is distributed on an "AS
+    IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+    implied. See the License for the specific language governing
+    rights and limitations under the License.
+
+    The initial developer of the original code is David A. Hinds
+    <dahinds@users.sourceforge.net>.  Portions created by David A. Hinds
+    are Copyright (C) 1999 David A. Hinds.  All Rights Reserved.
+
+    Alternatively, the contents of this file may be used under the
+    terms of the GNU General Public License version 2 (the "GPL"), in which
+    case the provisions of the GPL are applicable instead of the
+    above.  If you wish to allow the use of your version of this file
+    only under the terms of the GPL and not to allow others to use
+    your version of this file under the MPL, indicate your decision
+    by deleting the provisions above and replace them with the notice
+    and other provisions required by the GPL.  If you do not delete
+    the provisions above, a recipient may use your version of this
+    file under either the MPL or the GPL.
+    
+======================================================================*/
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/ptrace.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/timer.h>
+#include <linux/serial_core.h>
+#include <linux/delay.h>
+#include <linux/major.h>
+#include <asm/io.h>
+
+#include <pcmcia/cistpl.h>
+#include <pcmcia/ciscode.h>
+#include <pcmcia/ds.h>
+#include <pcmcia/cisreg.h>
+
+#include "8250.h"
+
+
+/*====================================================================*/
+
+/* Parameters that can be set with 'insmod' */
+
+/* Enable the speaker? */
+static int do_sound = 1;
+/* Skip strict UART tests? */
+static int buggy_uart;
+
+module_param(do_sound, int, 0444);
+module_param(buggy_uart, int, 0444);
+
+/*====================================================================*/
+
+/* Table of multi-port card ID's */
+
+struct serial_quirk {
+	unsigned int manfid;
+	unsigned int prodid;
+	int multi;		/* 1 = multifunction, > 1 = # ports */
+	void (*config)(struct pcmcia_device *);
+	void (*setup)(struct pcmcia_device *, struct uart_port *);
+	void (*wakeup)(struct pcmcia_device *);
+	int (*post)(struct pcmcia_device *);
+};
+
+struct serial_info {
+	struct pcmcia_device	*p_dev;
+	int			ndev;
+	int			multi;
+	int			slave;
+	int			manfid;
+	int			prodid;
+	int			c950ctrl;
+	int			line[4];
+	const struct serial_quirk *quirk;
+};
+
+struct serial_cfg_mem {
+	tuple_t tuple;
+	cisparse_t parse;
+	u_char buf[256];
+};
+
+/*
+ * vers_1 5.0, "Brain Boxes", "2-Port RS232 card", "r6"
+ * manfid 0x0160, 0x0104
+ * This card appears to have a 14.7456MHz clock.
+ */
+/* Generic Modem: MD55x (GPRS/EDGE) have
+ * Elan VPU16551 UART with 14.7456MHz oscillator
+ * manfid 0x015D, 0x4C45
+ */
+static void quirk_setup_brainboxes_0104(struct pcmcia_device *link, struct uart_port *port)
+{
+	port->uartclk = 14745600;
+}
+
+static int quirk_post_ibm(struct pcmcia_device *link)
+{
+	u8 val;
+	int ret;
+
+	ret = pcmcia_read_config_byte(link, 0x800, &val);
+	if (ret)
+		goto failed;
+
+	ret = pcmcia_write_config_byte(link, 0x800, val | 1);
+	if (ret)
+		goto failed;
+	return 0;
+
+ failed:
+	return -ENODEV;
+}
+
+/*
+ * Nokia cards are not really multiport cards.  Shouldn't this
+ * be handled by setting the quirk entry .multi = 0 | 1 ?
+ */
+static void quirk_config_nokia(struct pcmcia_device *link)
+{
+	struct serial_info *info = link->priv;
+
+	if (info->multi > 1)
+		info->multi = 1;
+}
+
+static void quirk_wakeup_oxsemi(struct pcmcia_device *link)
+{
+	struct serial_info *info = link->priv;
+
+	if (info->c950ctrl)
+		outb(12, info->c950ctrl + 1);
+}
+
+/* request_region? oxsemi branch does no request_region too... */
+/*
+ * This sequence is needed to properly initialize MC45 attached to OXCF950.
+ * I tried decreasing these msleep()s, but it worked properly (survived
+ * 1000 stop/start operations) with these timeouts (or bigger).
+ */
+static void quirk_wakeup_possio_gcc(struct pcmcia_device *link)
+{
+	struct serial_info *info = link->priv;
+	unsigned int ctrl = info->c950ctrl;
+
+	outb(0xA, ctrl + 1);
+	msleep(100);
+	outb(0xE, ctrl + 1);
+	msleep(300);
+	outb(0xC, ctrl + 1);
+	msleep(100);
+	outb(0xE, ctrl + 1);
+	msleep(200);
+	outb(0xF, ctrl + 1);
+	msleep(100);
+	outb(0xE, ctrl + 1);
+	msleep(100);
+	outb(0xC, ctrl + 1);
+}
+
+/*
+ * Socket Dual IO: this enables irq's for second port
+ */
+static void quirk_config_socket(struct pcmcia_device *link)
+{
+	struct serial_info *info = link->priv;
+
+	if (info->multi)
+		link->config_flags |= CONF_ENABLE_ESR;
+}
+
+static const struct serial_quirk quirks[] = {
+	{
+		.manfid	= 0x0160,
+		.prodid	= 0x0104,
+		.multi	= -1,
+		.setup	= quirk_setup_brainboxes_0104,
+	}, {
+		.manfid	= 0x015D,
+		.prodid	= 0x4C45,
+		.multi	= -1,
+		.setup	= quirk_setup_brainboxes_0104,
+	}, {
+		.manfid	= MANFID_IBM,
+		.prodid	= ~0,
+		.multi	= -1,
+		.post	= quirk_post_ibm,
+	}, {
+		.manfid	= MANFID_INTEL,
+		.prodid	= PRODID_INTEL_DUAL_RS232,
+		.multi	= 2,
+	}, {
+		.manfid	= MANFID_NATINST,
+		.prodid	= PRODID_NATINST_QUAD_RS232,
+		.multi	= 4,
+	}, {
+		.manfid	= MANFID_NOKIA,
+		.prodid	= ~0,
+		.multi	= -1,
+		.config	= quirk_config_nokia,
+	}, {
+		.manfid	= MANFID_OMEGA,
+		.prodid	= PRODID_OMEGA_QSP_100,
+		.multi	= 4,
+	}, {
+		.manfid	= MANFID_OXSEMI,
+		.prodid	= ~0,
+		.multi	= -1,
+		.wakeup	= quirk_wakeup_oxsemi,
+	}, {
+		.manfid	= MANFID_POSSIO,
+		.prodid	= PRODID_POSSIO_GCC,
+		.multi	= -1,
+		.wakeup	= quirk_wakeup_possio_gcc,
+	}, {
+		.manfid	= MANFID_QUATECH,
+		.prodid	= PRODID_QUATECH_DUAL_RS232,
+		.multi	= 2,
+	}, {
+		.manfid	= MANFID_QUATECH,
+		.prodid	= PRODID_QUATECH_DUAL_RS232_D1,
+		.multi	= 2,
+	}, {
+		.manfid	= MANFID_QUATECH,
+		.prodid	= PRODID_QUATECH_DUAL_RS232_G,
+		.multi	= 2,
+	}, {
+		.manfid	= MANFID_QUATECH,
+		.prodid	= PRODID_QUATECH_QUAD_RS232,
+		.multi	= 4,
+	}, {
+		.manfid	= MANFID_SOCKET,
+		.prodid	= PRODID_SOCKET_DUAL_RS232,
+		.multi	= 2,
+		.config	= quirk_config_socket,
+	}, {
+		.manfid	= MANFID_SOCKET,
+		.prodid	= ~0,
+		.multi	= -1,
+		.config	= quirk_config_socket,
+	}
+};
+
+
+static int serial_config(struct pcmcia_device * link);
+
+
+static void serial_remove(struct pcmcia_device *link)
+{
+	struct serial_info *info = link->priv;
+	int i;
+
+	dev_dbg(&link->dev, "serial_release\n");
+
+	/*
+	 * Recheck to see if the device is still configured.
+	 */
+	for (i = 0; i < info->ndev; i++)
+		serial8250_unregister_port(info->line[i]);
+
+	if (!info->slave)
+		pcmcia_disable_device(link);
+}
+
+static int serial_suspend(struct pcmcia_device *link)
+{
+	struct serial_info *info = link->priv;
+	int i;
+
+	for (i = 0; i < info->ndev; i++)
+		serial8250_suspend_port(info->line[i]);
+
+	return 0;
+}
+
+static int serial_resume(struct pcmcia_device *link)
+{
+	struct serial_info *info = link->priv;
+	int i;
+
+	for (i = 0; i < info->ndev; i++)
+		serial8250_resume_port(info->line[i]);
+
+	if (info->quirk && info->quirk->wakeup)
+		info->quirk->wakeup(link);
+
+	return 0;
+}
+
+static int serial_probe(struct pcmcia_device *link)
+{
+	struct serial_info *info;
+
+	dev_dbg(&link->dev, "serial_attach()\n");
+
+	/* Create new serial device */
+	info = kzalloc(sizeof (*info), GFP_KERNEL);
+	if (!info)
+		return -ENOMEM;
+	info->p_dev = link;
+	link->priv = info;
+
+	link->config_flags |= CONF_ENABLE_IRQ | CONF_AUTO_SET_IO;
+	if (do_sound)
+		link->config_flags |= CONF_ENABLE_SPKR;
+
+	return serial_config(link);
+}
+
+static void serial_detach(struct pcmcia_device *link)
+{
+	struct serial_info *info = link->priv;
+
+	dev_dbg(&link->dev, "serial_detach\n");
+
+	/*
+	 * Ensure that the ports have been released.
+	 */
+	serial_remove(link);
+
+	/* free bits */
+	kfree(info);
+}
+
+/*====================================================================*/
+
+static int setup_serial(struct pcmcia_device *handle, struct serial_info * info,
+			unsigned int iobase, int irq)
+{
+	struct uart_port port;
+	int line;
+
+	memset(&port, 0, sizeof (struct uart_port));
+	port.iobase = iobase;
+	port.irq = irq;
+	port.flags = UPF_BOOT_AUTOCONF | UPF_SKIP_TEST | UPF_SHARE_IRQ;
+	port.uartclk = 1843200;
+	port.dev = &handle->dev;
+	if (buggy_uart)
+		port.flags |= UPF_BUGGY_UART;
+
+	if (info->quirk && info->quirk->setup)
+		info->quirk->setup(handle, &port);
+
+	line = serial8250_register_port(&port);
+	if (line < 0) {
+		printk(KERN_NOTICE "serial_cs: serial8250_register_port() at "
+		       "0x%04lx, irq %d failed\n", (u_long)iobase, irq);
+		return -EINVAL;
+	}
+
+	info->line[info->ndev] = line;
+	info->ndev++;
+
+	return 0;
+}
+
+/*====================================================================*/
+
+static int pfc_config(struct pcmcia_device *p_dev)
+{
+	unsigned int port = 0;
+	struct serial_info *info = p_dev->priv;
+
+	if ((p_dev->resource[1]->end != 0) &&
+		(resource_size(p_dev->resource[1]) == 8)) {
+		port = p_dev->resource[1]->start;
+		info->slave = 1;
+	} else if ((info->manfid == MANFID_OSITECH) &&
+		(resource_size(p_dev->resource[0]) == 0x40)) {
+		port = p_dev->resource[0]->start + 0x28;
+		info->slave = 1;
+	}
+	if (info->slave)
+		return setup_serial(p_dev, info, port, p_dev->irq);
+
+	dev_warn(&p_dev->dev, "no usable port range found, giving up\n");
+	return -ENODEV;
+}
+
+static int simple_config_check(struct pcmcia_device *p_dev, void *priv_data)
+{
+	static const int size_table[2] = { 8, 16 };
+	int *try = priv_data;
+
+	if (p_dev->resource[0]->start == 0)
+		return -ENODEV;
+
+	if ((*try & 0x1) == 0)
+		p_dev->io_lines = 16;
+
+	if (p_dev->resource[0]->end != size_table[(*try >> 1)])
+		return -ENODEV;
+
+	p_dev->resource[0]->end = 8;
+	p_dev->resource[0]->flags &= ~IO_DATA_PATH_WIDTH;
+	p_dev->resource[0]->flags |= IO_DATA_PATH_WIDTH_8;
+
+	return pcmcia_request_io(p_dev);
+}
+
+static int simple_config_check_notpicky(struct pcmcia_device *p_dev,
+					void *priv_data)
+{
+	static const unsigned int base[5] = { 0x3f8, 0x2f8, 0x3e8, 0x2e8, 0x0 };
+	int j;
+
+	if (p_dev->io_lines > 3)
+		return -ENODEV;
+
+	p_dev->resource[0]->flags &= ~IO_DATA_PATH_WIDTH;
+	p_dev->resource[0]->flags |= IO_DATA_PATH_WIDTH_8;
+	p_dev->resource[0]->end = 8;
+
+	for (j = 0; j < 5; j++) {
+		p_dev->resource[0]->start = base[j];
+		p_dev->io_lines = base[j] ? 16 : 3;
+		if (!pcmcia_request_io(p_dev))
+			return 0;
+	}
+	return -ENODEV;
+}
+
+static int simple_config(struct pcmcia_device *link)
+{
+	struct serial_info *info = link->priv;
+	int i = -ENODEV, try;
+
+	/* First pass: look for a config entry that looks normal.
+	 * Two tries: without IO aliases, then with aliases */
+	link->config_flags |= CONF_AUTO_SET_VPP;
+	for (try = 0; try < 4; try++)
+		if (!pcmcia_loop_config(link, simple_config_check, &try))
+			goto found_port;
+
+	/* Second pass: try to find an entry that isn't picky about
+	   its base address, then try to grab any standard serial port
+	   address, and finally try to get any free port. */
+	if (!pcmcia_loop_config(link, simple_config_check_notpicky, NULL))
+		goto found_port;
+
+	dev_warn(&link->dev, "no usable port range found, giving up\n");
+	return -1;
+
+found_port:
+	if (info->multi && (info->manfid == MANFID_3COM))
+		link->config_index &= ~(0x08);
+
+	/*
+	 * Apply any configuration quirks.
+	 */
+	if (info->quirk && info->quirk->config)
+		info->quirk->config(link);
+
+	i = pcmcia_enable_device(link);
+	if (i != 0)
+		return -1;
+	return setup_serial(link, info, link->resource[0]->start, link->irq);
+}
+
+static int multi_config_check(struct pcmcia_device *p_dev, void *priv_data)
+{
+	int *multi = priv_data;
+
+	if (p_dev->resource[1]->end)
+		return -EINVAL;
+
+	/* The quad port cards have bad CIS's, so just look for a
+	   window larger than 8 ports and assume it will be right */
+	if (p_dev->resource[0]->end <= 8)
+		return -EINVAL;
+
+	p_dev->resource[0]->flags &= ~IO_DATA_PATH_WIDTH;
+	p_dev->resource[0]->flags |= IO_DATA_PATH_WIDTH_8;
+	p_dev->resource[0]->end = *multi * 8;
+
+	if (pcmcia_request_io(p_dev))
+		return -ENODEV;
+	return 0;
+}
+
+static int multi_config_check_notpicky(struct pcmcia_device *p_dev,
+				       void *priv_data)
+{
+	int *base2 = priv_data;
+
+	if (!p_dev->resource[0]->end || !p_dev->resource[1]->end ||
+		p_dev->resource[0]->start + 8 != p_dev->resource[1]->start)
+		return -ENODEV;
+
+	p_dev->resource[0]->end = p_dev->resource[1]->end = 8;
+	p_dev->resource[0]->flags &= ~IO_DATA_PATH_WIDTH;
+	p_dev->resource[0]->flags |= IO_DATA_PATH_WIDTH_8;
+
+	if (pcmcia_request_io(p_dev))
+		return -ENODEV;
+
+	*base2 = p_dev->resource[0]->start + 8;
+	return 0;
+}
+
+static int multi_config(struct pcmcia_device *link)
+{
+	struct serial_info *info = link->priv;
+	int i, base2 = 0;
+
+	/* First, look for a generic full-sized window */
+	if (!pcmcia_loop_config(link, multi_config_check, &info->multi))
+		base2 = link->resource[0]->start + 8;
+	else {
+		/* If that didn't work, look for two windows */
+		info->multi = 2;
+		if (pcmcia_loop_config(link, multi_config_check_notpicky,
+				       &base2)) {
+			dev_warn(&link->dev, "no usable port range "
+			       "found, giving up\n");
+			return -ENODEV;
+		}
+	}
+
+	if (!link->irq)
+		dev_warn(&link->dev, "no usable IRQ found, continuing...\n");
+
+	/*
+	 * Apply any configuration quirks.
+	 */
+	if (info->quirk && info->quirk->config)
+		info->quirk->config(link);
+
+	i = pcmcia_enable_device(link);
+	if (i != 0)
+		return -ENODEV;
+
+	/* The Oxford Semiconductor OXCF950 cards are in fact single-port:
+	 * 8 registers are for the UART, the others are extra registers.
+	 * Siemen's MC45 PCMCIA (Possio's GCC) is OXCF950 based too.
+	 */
+	if (info->manfid == MANFID_OXSEMI || (info->manfid == MANFID_POSSIO &&
+				info->prodid == PRODID_POSSIO_GCC)) {
+		int err;
+
+		if (link->config_index == 1 ||
+		    link->config_index == 3) {
+			err = setup_serial(link, info, base2,
+					link->irq);
+			base2 = link->resource[0]->start;
+		} else {
+			err = setup_serial(link, info, link->resource[0]->start,
+					link->irq);
+		}
+		info->c950ctrl = base2;
+
+		/*
+		 * FIXME: We really should wake up the port prior to
+		 * handing it over to the serial layer.
+		 */
+		if (info->quirk && info->quirk->wakeup)
+			info->quirk->wakeup(link);
+
+		return 0;
+	}
+
+	setup_serial(link, info, link->resource[0]->start, link->irq);
+	for (i = 0; i < info->multi - 1; i++)
+		setup_serial(link, info, base2 + (8 * i),
+				link->irq);
+	return 0;
+}
+
+static int serial_check_for_multi(struct pcmcia_device *p_dev,  void *priv_data)
+{
+	struct serial_info *info = p_dev->priv;
+
+	if (!p_dev->resource[0]->end)
+		return -EINVAL;
+
+	if ((!p_dev->resource[1]->end) && (p_dev->resource[0]->end % 8 == 0))
+		info->multi = p_dev->resource[0]->end >> 3;
+
+	if ((p_dev->resource[1]->end) && (p_dev->resource[0]->end == 8)
+		&& (p_dev->resource[1]->end == 8))
+		info->multi = 2;
+
+	return 0; /* break */
+}
+
+
+static int serial_config(struct pcmcia_device * link)
+{
+	struct serial_info *info = link->priv;
+	int i;
+
+	dev_dbg(&link->dev, "serial_config\n");
+
+	/* Is this a compliant multifunction card? */
+	info->multi = (link->socket->functions > 1);
+
+	/* Is this a multiport card? */
+	info->manfid = link->manf_id;
+	info->prodid = link->card_id;
+
+	for (i = 0; i < ARRAY_SIZE(quirks); i++)
+		if ((quirks[i].manfid == ~0 ||
+		     quirks[i].manfid == info->manfid) &&
+		    (quirks[i].prodid == ~0 ||
+		     quirks[i].prodid == info->prodid)) {
+			info->quirk = &quirks[i];
+			break;
+		}
+
+	/* Another check for dual-serial cards: look for either serial or
+	   multifunction cards that ask for appropriate IO port ranges */
+	if ((info->multi == 0) &&
+	    (link->has_func_id) &&
+	    (link->socket->pcmcia_pfc == 0) &&
+	    ((link->func_id == CISTPL_FUNCID_MULTI) ||
+	     (link->func_id == CISTPL_FUNCID_SERIAL)))
+		pcmcia_loop_config(link, serial_check_for_multi, info);
+
+	/*
+	 * Apply any multi-port quirk.
+	 */
+	if (info->quirk && info->quirk->multi != -1)
+		info->multi = info->quirk->multi;
+
+	dev_info(&link->dev,
+		"trying to set up [0x%04x:0x%04x] (pfc: %d, multi: %d, quirk: %p)\n",
+		link->manf_id, link->card_id,
+		link->socket->pcmcia_pfc, info->multi, info->quirk);
+	if (link->socket->pcmcia_pfc)
+		i = pfc_config(link);
+	else if (info->multi > 1)
+		i = multi_config(link);
+	else
+		i = simple_config(link);
+
+	if (i || info->ndev == 0)
+		goto failed;
+
+	/*
+	 * Apply any post-init quirk.  FIXME: This should really happen
+	 * before we register the port, since it might already be in use.
+	 */
+	if (info->quirk && info->quirk->post)
+		if (info->quirk->post(link))
+			goto failed;
+
+	return 0;
+
+failed:
+	dev_warn(&link->dev, "failed to initialize\n");
+	serial_remove(link);
+	return -ENODEV;
+}
+
+static const struct pcmcia_device_id serial_ids[] = {
+	PCMCIA_PFC_DEVICE_MANF_CARD(1, 0x0057, 0x0021),
+	PCMCIA_PFC_DEVICE_MANF_CARD(1, 0x0089, 0x110a),
+	PCMCIA_PFC_DEVICE_MANF_CARD(1, 0x0104, 0x000a),
+	PCMCIA_PFC_DEVICE_MANF_CARD(1, 0x0105, 0x0d0a),
+	PCMCIA_PFC_DEVICE_MANF_CARD(1, 0x0105, 0x0e0a),
+	PCMCIA_PFC_DEVICE_MANF_CARD(1, 0x0105, 0xea15),
+	PCMCIA_PFC_DEVICE_MANF_CARD(1, 0x0109, 0x0501),
+	PCMCIA_PFC_DEVICE_MANF_CARD(1, 0x0138, 0x110a),
+	PCMCIA_PFC_DEVICE_MANF_CARD(1, 0x0140, 0x000a),
+	PCMCIA_PFC_DEVICE_MANF_CARD(1, 0x0143, 0x3341),
+	PCMCIA_PFC_DEVICE_MANF_CARD(1, 0x0143, 0xc0ab),
+	PCMCIA_PFC_DEVICE_MANF_CARD(1, 0x016c, 0x0081),
+	PCMCIA_PFC_DEVICE_MANF_CARD(1, 0x021b, 0x0101),
+	PCMCIA_PFC_DEVICE_MANF_CARD(1, 0x08a1, 0xc0ab),
+	PCMCIA_PFC_DEVICE_PROD_ID123(1, "MEGAHERTZ", "CC/XJEM3288", "DATA/FAX/CELL ETHERNET MODEM", 0xf510db04, 0x04cd2988, 0x46a52d63),
+	PCMCIA_PFC_DEVICE_PROD_ID123(1, "MEGAHERTZ", "CC/XJEM3336", "DATA/FAX/CELL ETHERNET MODEM", 0xf510db04, 0x0143b773, 0x46a52d63),
+	PCMCIA_PFC_DEVICE_PROD_ID123(1, "MEGAHERTZ", "EM1144T", "PCMCIA MODEM", 0xf510db04, 0x856d66c8, 0xbd6c43ef),
+	PCMCIA_PFC_DEVICE_PROD_ID123(1, "MEGAHERTZ", "XJEM1144/CCEM1144", "PCMCIA MODEM", 0xf510db04, 0x52d21e1e, 0xbd6c43ef),
+	PCMCIA_PFC_DEVICE_PROD_ID13(1, "Xircom", "CEM28", 0x2e3ee845, 0x0ea978ea),
+	PCMCIA_PFC_DEVICE_PROD_ID13(1, "Xircom", "CEM33", 0x2e3ee845, 0x80609023),
+	PCMCIA_PFC_DEVICE_PROD_ID13(1, "Xircom", "CEM56", 0x2e3ee845, 0xa650c32a),
+	PCMCIA_PFC_DEVICE_PROD_ID13(1, "Xircom", "REM10", 0x2e3ee845, 0x76df1d29),
+	PCMCIA_PFC_DEVICE_PROD_ID13(1, "Xircom", "XEM5600", 0x2e3ee845, 0xf1403719),
+	PCMCIA_PFC_DEVICE_PROD_ID12(1, "AnyCom", "Fast Ethernet + 56K COMBO", 0x578ba6e7, 0xb0ac62c4),
+	PCMCIA_PFC_DEVICE_PROD_ID12(1, "ATKK", "LM33-PCM-T", 0xba9eb7e2, 0x077c174e),
+	PCMCIA_PFC_DEVICE_PROD_ID12(1, "D-Link", "DME336T", 0x1a424a1c, 0xb23897ff),
+	PCMCIA_PFC_DEVICE_PROD_ID12(1, "Gateway 2000", "XJEM3336", 0xdd9989be, 0x662c394c),
+	PCMCIA_PFC_DEVICE_PROD_ID12(1, "Grey Cell", "GCS3000", 0x2a151fac, 0x48b932ae),
+	PCMCIA_PFC_DEVICE_PROD_ID12(1, "Linksys", "EtherFast 10&100 + 56K PC Card (PCMLM56)", 0x0733cc81, 0xb3765033),
+	PCMCIA_PFC_DEVICE_PROD_ID12(1, "LINKSYS", "PCMLM336", 0xf7cb0b07, 0x7a821b58),
+	PCMCIA_PFC_DEVICE_PROD_ID12(1, "MEGAHERTZ", "XJEM1144/CCEM1144", 0xf510db04, 0x52d21e1e),
+	PCMCIA_PFC_DEVICE_PROD_ID12(1, "MICRO RESEARCH", "COMBO-L/M-336", 0xb2ced065, 0x3ced0555),
+	PCMCIA_PFC_DEVICE_PROD_ID12(1, "NEC", "PK-UG-J001" ,0x18df0ba0 ,0x831b1064),
+	PCMCIA_PFC_DEVICE_PROD_ID12(1, "Ositech", "Trumpcard:Jack of Diamonds Modem+Ethernet", 0xc2f80cd, 0x656947b9),
+	PCMCIA_PFC_DEVICE_PROD_ID12(1, "Ositech", "Trumpcard:Jack of Hearts Modem+Ethernet", 0xc2f80cd, 0xdc9ba5ed),
+	PCMCIA_PFC_DEVICE_PROD_ID12(1, "PCMCIAs", "ComboCard", 0xdcfe12d3, 0xcd8906cc),
+	PCMCIA_PFC_DEVICE_PROD_ID12(1, "PCMCIAs", "LanModem", 0xdcfe12d3, 0xc67c648f),
+	PCMCIA_PFC_DEVICE_PROD_ID12(1, "TDK", "GlobalNetworker 3410/3412", 0x1eae9475, 0xd9a93bed),
+	PCMCIA_PFC_DEVICE_PROD_ID12(1, "Xircom", "CreditCard Ethernet+Modem II", 0x2e3ee845, 0xeca401bf),
+	PCMCIA_PFC_DEVICE_MANF_CARD(1, 0x0032, 0x0e01),
+	PCMCIA_PFC_DEVICE_MANF_CARD(1, 0x0032, 0x0a05),
+	PCMCIA_PFC_DEVICE_MANF_CARD(1, 0x0032, 0x0b05),
+	PCMCIA_PFC_DEVICE_MANF_CARD(1, 0x0032, 0x1101),
+	PCMCIA_MFC_DEVICE_MANF_CARD(0, 0x0104, 0x0070),
+	PCMCIA_MFC_DEVICE_MANF_CARD(1, 0x0101, 0x0562),
+	PCMCIA_MFC_DEVICE_MANF_CARD(1, 0x0104, 0x0070),
+	PCMCIA_MFC_DEVICE_MANF_CARD(1, 0x016c, 0x0020),
+	PCMCIA_MFC_DEVICE_PROD_ID123(1, "APEX DATA", "MULTICARD", "ETHERNET-MODEM", 0x11c2da09, 0x7289dc5d, 0xaad95e1f),
+	PCMCIA_MFC_DEVICE_PROD_ID12(1, "IBM", "Home and Away 28.8 PC Card       ", 0xb569a6e5, 0x5bd4ff2c),
+	PCMCIA_MFC_DEVICE_PROD_ID12(1, "IBM", "Home and Away Credit Card Adapter", 0xb569a6e5, 0x4bdf15c3),
+	PCMCIA_MFC_DEVICE_PROD_ID12(1, "IBM", "w95 Home and Away Credit Card ", 0xb569a6e5, 0xae911c15),
+	PCMCIA_MFC_DEVICE_PROD_ID1(1, "Motorola MARQUIS", 0xf03e4e77),
+	PCMCIA_MFC_DEVICE_PROD_ID2(1, "FAX/Modem/Ethernet Combo Card ", 0x1ed59302),
+	PCMCIA_DEVICE_MANF_CARD(0x0089, 0x0301),
+	PCMCIA_DEVICE_MANF_CARD(0x00a4, 0x0276),
+	PCMCIA_DEVICE_MANF_CARD(0x0101, 0x0039),
+	PCMCIA_DEVICE_MANF_CARD(0x0104, 0x0006),
+	PCMCIA_DEVICE_MANF_CARD(0x0105, 0x0101), /* TDK DF2814 */
+	PCMCIA_DEVICE_MANF_CARD(0x0105, 0x100a), /* Xircom CM-56G */
+	PCMCIA_DEVICE_MANF_CARD(0x0105, 0x3e0a), /* TDK DF5660 */
+	PCMCIA_DEVICE_MANF_CARD(0x0105, 0x410a),
+	PCMCIA_DEVICE_MANF_CARD(0x0107, 0x0002), /* USRobotics 14,400 */
+	PCMCIA_DEVICE_MANF_CARD(0x010b, 0x0d50),
+	PCMCIA_DEVICE_MANF_CARD(0x010b, 0x0d51),
+	PCMCIA_DEVICE_MANF_CARD(0x010b, 0x0d52),
+	PCMCIA_DEVICE_MANF_CARD(0x010b, 0x0d53),
+	PCMCIA_DEVICE_MANF_CARD(0x010b, 0xd180),
+	PCMCIA_DEVICE_MANF_CARD(0x0115, 0x3330), /* USRobotics/SUN 14,400 */
+	PCMCIA_DEVICE_MANF_CARD(0x0124, 0x0100), /* Nokia DTP-2 ver II */
+	PCMCIA_DEVICE_MANF_CARD(0x0134, 0x5600), /* LASAT COMMUNICATIONS A/S */
+	PCMCIA_DEVICE_MANF_CARD(0x0137, 0x000e),
+	PCMCIA_DEVICE_MANF_CARD(0x0137, 0x001b),
+	PCMCIA_DEVICE_MANF_CARD(0x0137, 0x0025),
+	PCMCIA_DEVICE_MANF_CARD(0x0137, 0x0045),
+	PCMCIA_DEVICE_MANF_CARD(0x0137, 0x0052),
+	PCMCIA_DEVICE_MANF_CARD(0x016c, 0x0006), /* Psion 56K+Fax */
+	PCMCIA_DEVICE_MANF_CARD(0x0200, 0x0001), /* MultiMobile */
+	PCMCIA_DEVICE_PROD_ID134("ADV", "TECH", "COMpad-32/85", 0x67459937, 0x916d02ba, 0x8fbe92ae),
+	PCMCIA_DEVICE_PROD_ID124("GATEWAY2000", "CC3144", "PCMCIA MODEM", 0x506bccae, 0xcb3685f1, 0xbd6c43ef),
+	PCMCIA_DEVICE_PROD_ID14("MEGAHERTZ", "PCMCIA MODEM", 0xf510db04, 0xbd6c43ef),
+	PCMCIA_DEVICE_PROD_ID124("TOSHIBA", "T144PF", "PCMCIA MODEM", 0xb4585a1a, 0x7271409c, 0xbd6c43ef),
+	PCMCIA_DEVICE_PROD_ID123("FUJITSU", "FC14F ", "MBH10213", 0x6ee5a3d8, 0x30ead12b, 0xb00f05a0),
+	PCMCIA_DEVICE_PROD_ID123("Novatel Wireless", "Merlin UMTS Modem", "U630", 0x32607776, 0xd9e73b13, 0xe87332e),
+	PCMCIA_DEVICE_PROD_ID13("MEGAHERTZ", "V.34 PCMCIA MODEM", 0xf510db04, 0xbb2cce4a),
+	PCMCIA_DEVICE_PROD_ID12("Brain Boxes", "Bluetooth PC Card", 0xee138382, 0xd4ce9b02),
+	PCMCIA_DEVICE_PROD_ID12("CIRRUS LOGIC", "FAX MODEM", 0xe625f451, 0xcecd6dfa),
+	PCMCIA_DEVICE_PROD_ID12("COMPAQ", "PCMCIA 28800 FAX/DATA MODEM", 0xa3a3062c, 0x8cbd7c76),
+	PCMCIA_DEVICE_PROD_ID12("COMPAQ", "PCMCIA 33600 FAX/DATA MODEM", 0xa3a3062c, 0x5a00ce95),
+	PCMCIA_DEVICE_PROD_ID12("Computerboards, Inc.", "PCM-COM422", 0xd0b78f51, 0x7e2d49ed),
+	PCMCIA_DEVICE_PROD_ID12("Dr. Neuhaus", "FURY CARD 14K4", 0x76942813, 0x8b96ce65),
+	PCMCIA_DEVICE_PROD_ID12("IBM", "ISDN/56K/GSM", 0xb569a6e5, 0xfee5297b),
+	PCMCIA_DEVICE_PROD_ID12("Intelligent", "ANGIA FAX/MODEM", 0xb496e65e, 0xf31602a6),
+	PCMCIA_DEVICE_PROD_ID12("Intel", "MODEM 2400+", 0x816cc815, 0x412729fb),
+	PCMCIA_DEVICE_PROD_ID12("Intertex", "IX34-PCMCIA", 0xf8a097e3, 0x97880447),
+	PCMCIA_DEVICE_PROD_ID12("IOTech Inc ", "PCMCIA Dual RS-232 Serial Port Card", 0x3bd2d898, 0x92abc92f),
+	PCMCIA_DEVICE_PROD_ID12("MACRONIX", "FAX/MODEM", 0x668388b3, 0x3f9bdf2f),
+	PCMCIA_DEVICE_PROD_ID12("Multi-Tech", "MT1432LT", 0x5f73be51, 0x0b3e2383),
+	PCMCIA_DEVICE_PROD_ID12("Multi-Tech", "MT2834LT", 0x5f73be51, 0x4cd7c09e),
+	PCMCIA_DEVICE_PROD_ID12("OEM      ", "C288MX     ", 0xb572d360, 0xd2385b7a),
+	PCMCIA_DEVICE_PROD_ID12("Option International", "V34bis GSM/PSTN Data/Fax Modem", 0x9d7cd6f5, 0x5cb8bf41),
+	PCMCIA_DEVICE_PROD_ID12("PCMCIA   ", "C336MX     ", 0x99bcafe9, 0xaa25bcab),
+	PCMCIA_DEVICE_PROD_ID12("Quatech Inc", "PCMCIA Dual RS-232 Serial Port Card", 0xc4420b35, 0x92abc92f),
+	PCMCIA_DEVICE_PROD_ID12("Quatech Inc", "Dual RS-232 Serial Port PC Card", 0xc4420b35, 0x031a380d),
+	PCMCIA_DEVICE_PROD_ID12("Telia", "SurfinBird 560P/A+", 0xe2cdd5e, 0xc9314b38),
+	PCMCIA_DEVICE_PROD_ID1("Smart Serial Port", 0x2d8ce292),
+	PCMCIA_PFC_DEVICE_CIS_PROD_ID12(1, "PCMCIA", "EN2218-LAN/MODEM", 0x281f1c5d, 0x570f348e, "cis/PCMLM28.cis"),
+	PCMCIA_PFC_DEVICE_CIS_PROD_ID12(1, "PCMCIA", "UE2218-LAN/MODEM", 0x281f1c5d, 0x6fdcacee, "cis/PCMLM28.cis"),
+	PCMCIA_PFC_DEVICE_CIS_PROD_ID12(1, "Psion Dacom", "Gold Card V34 Ethernet", 0xf5f025c2, 0x338e8155, "cis/PCMLM28.cis"),
+	PCMCIA_PFC_DEVICE_CIS_PROD_ID12(1, "Psion Dacom", "Gold Card V34 Ethernet GSM", 0xf5f025c2, 0x4ae85d35, "cis/PCMLM28.cis"),
+	PCMCIA_PFC_DEVICE_CIS_PROD_ID12(1, "LINKSYS", "PCMLM28", 0xf7cb0b07, 0x66881874, "cis/PCMLM28.cis"),
+	PCMCIA_PFC_DEVICE_CIS_PROD_ID12(1, "TOSHIBA", "Modem/LAN Card", 0xb4585a1a, 0x53f922f8, "cis/PCMLM28.cis"),
+	PCMCIA_MFC_DEVICE_CIS_PROD_ID12(1, "DAYNA COMMUNICATIONS", "LAN AND MODEM MULTIFUNCTION", 0x8fdf8f89, 0xdd5ed9e8, "cis/DP83903.cis"),
+	PCMCIA_MFC_DEVICE_CIS_PROD_ID4(1, "NSC MF LAN/Modem", 0x58fc6056, "cis/DP83903.cis"),
+	PCMCIA_MFC_DEVICE_CIS_MANF_CARD(1, 0x0101, 0x0556, "cis/3CCFEM556.cis"),
+	PCMCIA_MFC_DEVICE_CIS_MANF_CARD(1, 0x0175, 0x0000, "cis/DP83903.cis"),
+	PCMCIA_MFC_DEVICE_CIS_MANF_CARD(1, 0x0101, 0x0035, "cis/3CXEM556.cis"),
+	PCMCIA_MFC_DEVICE_CIS_MANF_CARD(1, 0x0101, 0x003d, "cis/3CXEM556.cis"),
+	PCMCIA_DEVICE_CIS_PROD_ID12("Sierra Wireless", "AC850", 0xd85f6206, 0x42a2c018, "cis/SW_8xx_SER.cis"), /* Sierra Wireless AC850 3G Network Adapter R1 */
+	PCMCIA_DEVICE_CIS_PROD_ID12("Sierra Wireless", "AC860", 0xd85f6206, 0x698f93db, "cis/SW_8xx_SER.cis"), /* Sierra Wireless AC860 3G Network Adapter R1 */
+	PCMCIA_DEVICE_CIS_PROD_ID12("Sierra Wireless", "AC710/AC750", 0xd85f6206, 0x761b11e0, "cis/SW_7xx_SER.cis"),  /* Sierra Wireless AC710/AC750 GPRS Network Adapter R1 */
+	PCMCIA_DEVICE_CIS_MANF_CARD(0x0192, 0xa555, "cis/SW_555_SER.cis"),  /* Sierra Aircard 555 CDMA 1xrtt Modem -- pre update */
+	PCMCIA_DEVICE_CIS_MANF_CARD(0x013f, 0xa555, "cis/SW_555_SER.cis"),  /* Sierra Aircard 555 CDMA 1xrtt Modem -- post update */
+	PCMCIA_DEVICE_CIS_PROD_ID12("MultiTech", "PCMCIA 56K DataFax", 0x842047ee, 0xc2efcf03, "cis/MT5634ZLX.cis"),
+	PCMCIA_DEVICE_CIS_PROD_ID12("ADVANTECH", "COMpad-32/85B-2", 0x96913a85, 0x27ab5437, "cis/COMpad2.cis"),
+	PCMCIA_DEVICE_CIS_PROD_ID12("ADVANTECH", "COMpad-32/85B-4", 0x96913a85, 0xcec8f102, "cis/COMpad4.cis"),
+	PCMCIA_DEVICE_CIS_PROD_ID123("ADVANTECH", "COMpad-32/85", "1.0", 0x96913a85, 0x8fbe92ae, 0x0877b627, "cis/COMpad2.cis"),
+	PCMCIA_DEVICE_CIS_PROD_ID2("RS-COM 2P", 0xad20b156, "cis/RS-COM-2P.cis"),
+	PCMCIA_DEVICE_CIS_MANF_CARD(0x0013, 0x0000, "cis/GLOBETROTTER.cis"),
+	PCMCIA_DEVICE_PROD_ID12("ELAN DIGITAL SYSTEMS LTD, c1997.","SERIAL CARD: SL100  1.00.",0x19ca78af,0xf964f42b),
+	PCMCIA_DEVICE_PROD_ID12("ELAN DIGITAL SYSTEMS LTD, c1997.","SERIAL CARD: SL100",0x19ca78af,0x71d98e83),
+	PCMCIA_DEVICE_PROD_ID12("ELAN DIGITAL SYSTEMS LTD, c1997.","SERIAL CARD: SL232  1.00.",0x19ca78af,0x69fb7490),
+	PCMCIA_DEVICE_PROD_ID12("ELAN DIGITAL SYSTEMS LTD, c1997.","SERIAL CARD: SL232",0x19ca78af,0xb6bc0235),
+	PCMCIA_DEVICE_PROD_ID12("ELAN DIGITAL SYSTEMS LTD, c2000.","SERIAL CARD: CF232",0x63f2e0bd,0xb9e175d3),
+	PCMCIA_DEVICE_PROD_ID12("ELAN DIGITAL SYSTEMS LTD, c2000.","SERIAL CARD: CF232-5",0x63f2e0bd,0xfce33442),
+	PCMCIA_DEVICE_PROD_ID12("Elan","Serial Port: CF232",0x3beb8cf2,0x171e7190),
+	PCMCIA_DEVICE_PROD_ID12("Elan","Serial Port: CF232-5",0x3beb8cf2,0x20da4262),
+	PCMCIA_DEVICE_PROD_ID12("Elan","Serial Port: CF428",0x3beb8cf2,0xea5dd57d),
+	PCMCIA_DEVICE_PROD_ID12("Elan","Serial Port: CF500",0x3beb8cf2,0xd77255fa),
+	PCMCIA_DEVICE_PROD_ID12("Elan","Serial Port: IC232",0x3beb8cf2,0x6a709903),
+	PCMCIA_DEVICE_PROD_ID12("Elan","Serial Port: SL232",0x3beb8cf2,0x18430676),
+	PCMCIA_DEVICE_PROD_ID12("Elan","Serial Port: XL232",0x3beb8cf2,0x6f933767),
+	PCMCIA_MFC_DEVICE_PROD_ID12(0,"Elan","Serial Port: CF332",0x3beb8cf2,0x16dc1ba7),
+	PCMCIA_MFC_DEVICE_PROD_ID12(0,"Elan","Serial Port: SL332",0x3beb8cf2,0x19816c41),
+	PCMCIA_MFC_DEVICE_PROD_ID12(0,"Elan","Serial Port: SL385",0x3beb8cf2,0x64112029),
+	PCMCIA_MFC_DEVICE_PROD_ID12(0,"Elan","Serial Port: SL432",0x3beb8cf2,0x1cce7ac4),
+	PCMCIA_MFC_DEVICE_PROD_ID12(0,"Elan","Serial+Parallel Port: SP230",0x3beb8cf2,0xdb9e58bc),
+	PCMCIA_MFC_DEVICE_PROD_ID12(1,"Elan","Serial Port: CF332",0x3beb8cf2,0x16dc1ba7),
+	PCMCIA_MFC_DEVICE_PROD_ID12(1,"Elan","Serial Port: SL332",0x3beb8cf2,0x19816c41),
+	PCMCIA_MFC_DEVICE_PROD_ID12(1,"Elan","Serial Port: SL385",0x3beb8cf2,0x64112029),
+	PCMCIA_MFC_DEVICE_PROD_ID12(1,"Elan","Serial Port: SL432",0x3beb8cf2,0x1cce7ac4),
+	PCMCIA_MFC_DEVICE_PROD_ID12(2,"Elan","Serial Port: SL432",0x3beb8cf2,0x1cce7ac4),
+	PCMCIA_MFC_DEVICE_PROD_ID12(3,"Elan","Serial Port: SL432",0x3beb8cf2,0x1cce7ac4),
+	PCMCIA_DEVICE_MANF_CARD(0x0279, 0x950b),
+	/* too generic */
+	/* PCMCIA_MFC_DEVICE_MANF_CARD(0, 0x0160, 0x0002), */
+	/* PCMCIA_MFC_DEVICE_MANF_CARD(1, 0x0160, 0x0002), */
+	PCMCIA_DEVICE_FUNC_ID(2),
+	PCMCIA_DEVICE_NULL,
+};
+MODULE_DEVICE_TABLE(pcmcia, serial_ids);
+
+MODULE_FIRMWARE("cis/PCMLM28.cis");
+MODULE_FIRMWARE("cis/DP83903.cis");
+MODULE_FIRMWARE("cis/3CCFEM556.cis");
+MODULE_FIRMWARE("cis/3CXEM556.cis");
+MODULE_FIRMWARE("cis/SW_8xx_SER.cis");
+MODULE_FIRMWARE("cis/SW_7xx_SER.cis");
+MODULE_FIRMWARE("cis/SW_555_SER.cis");
+MODULE_FIRMWARE("cis/MT5634ZLX.cis");
+MODULE_FIRMWARE("cis/COMpad2.cis");
+MODULE_FIRMWARE("cis/COMpad4.cis");
+MODULE_FIRMWARE("cis/RS-COM-2P.cis");
+
+static struct pcmcia_driver serial_cs_driver = {
+	.owner		= THIS_MODULE,
+	.name		= "serial_cs",
+	.probe		= serial_probe,
+	.remove		= serial_detach,
+	.id_table	= serial_ids,
+	.suspend	= serial_suspend,
+	.resume		= serial_resume,
+};
+
+static int __init init_serial_cs(void)
+{
+	return pcmcia_register_driver(&serial_cs_driver);
+}
+
+static void __exit exit_serial_cs(void)
+{
+	pcmcia_unregister_driver(&serial_cs_driver);
+}
+
+module_init(init_serial_cs);
+module_exit(exit_serial_cs);
+
+MODULE_LICENSE("GPL");
diff --git a/ap/os/linux/linux-3.4.x/drivers/tty/serial/Kconfig b/ap/os/linux/linux-3.4.x/drivers/tty/serial/Kconfig
new file mode 100644
index 0000000..6e031eb
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/tty/serial/Kconfig
@@ -0,0 +1,1420 @@
+#
+# Serial device configuration
+#
+
+menu "Serial drivers"
+	depends on HAS_IOMEM
+
+source "drivers/tty/serial/8250/Kconfig"
+
+comment "Non-8250 serial port support"
+
+config SERIAL_AMBA_PL010
+	tristate "ARM AMBA PL010 serial port support"
+	depends on ARM_AMBA && (BROKEN || !ARCH_VERSATILE)
+	select SERIAL_CORE
+	help
+	  This selects the ARM(R) AMBA(R) PrimeCell PL010 UART.  If you have
+	  an Integrator/AP or Integrator/PP2 platform, or if you have a
+	  Cirrus Logic EP93xx CPU, say Y or M here.
+
+	  If unsure, say N.
+
+config SERIAL_AMBA_PL010_CONSOLE
+	bool "Support for console on AMBA serial port"
+	depends on SERIAL_AMBA_PL010=y
+	select SERIAL_CORE_CONSOLE
+	---help---
+	  Say Y here if you wish to use an AMBA PrimeCell UART as the system
+	  console (the system console is the device which receives all kernel
+	  messages and warnings and which allows logins in single user mode).
+
+	  Even if you say Y here, the currently visible framebuffer console
+	  (/dev/tty0) will still be used as the system console by default, but
+	  you can alter that using a kernel command line option such as
+	  "console=ttyAM0". (Try "man bootparam" or see the documentation of
+	  your boot loader (lilo or loadlin) about how to pass options to the
+	  kernel at boot time.)
+
+config SERIAL_AMBA_PL011
+	tristate "ARM AMBA PL011 serial port support"
+	depends on ARM_AMBA
+	select SERIAL_CORE
+	help
+	  This selects the ARM(R) AMBA(R) PrimeCell PL011 UART.  If you have
+	  an Integrator/PP2, Integrator/CP or Versatile platform, say Y or M
+	  here.
+
+	  If unsure, say N.
+
+config SERIAL_AMBA_PL011_CONSOLE
+	bool "Support for console on AMBA serial port"
+	depends on SERIAL_AMBA_PL011=y
+	select SERIAL_CORE_CONSOLE
+	---help---
+	  Say Y here if you wish to use an AMBA PrimeCell UART as the system
+	  console (the system console is the device which receives all kernel
+	  messages and warnings and which allows logins in single user mode).
+
+	  Even if you say Y here, the currently visible framebuffer console
+	  (/dev/tty0) will still be used as the system console by default, but
+	  you can alter that using a kernel command line option such as
+	  "console=ttyAMA0". (Try "man bootparam" or see the documentation of
+	  your boot loader (lilo or loadlin) about how to pass options to the
+	  kernel at boot time.)
+
+config SERIAL_SB1250_DUART
+	tristate "BCM1xxx on-chip DUART serial support"
+	depends on SIBYTE_SB1xxx_SOC=y
+	select SERIAL_CORE
+	default y
+	---help---
+	  Support for the asynchronous serial interface (DUART) included in
+	  the BCM1250 and derived System-On-a-Chip (SOC) devices.  Note that
+	  the letter D in DUART stands for "dual", which is how the device
+	  is implemented.  Depending on the SOC configuration there may be
+	  one or more DUARTs available of which all are handled.
+
+	  If unsure, say Y.  To compile this driver as a module, choose M here:
+	  the module will be called sb1250-duart.
+
+config SERIAL_SB1250_DUART_CONSOLE
+	bool "Support for console on a BCM1xxx DUART serial port"
+	depends on SERIAL_SB1250_DUART=y
+	select SERIAL_CORE_CONSOLE
+	default y
+	---help---
+	  If you say Y here, it will be possible to use a serial port as the
+	  system console (the system console is the device which receives all
+	  kernel messages and warnings and which allows logins in single user
+	  mode).
+
+	  If unsure, say Y.
+
+config SERIAL_ATMEL
+	bool "AT91 / AT32 on-chip serial port support"
+	depends on (ARM && ARCH_AT91) || AVR32
+	select SERIAL_CORE
+	help
+	  This enables the driver for the on-chip UARTs of the Atmel
+	  AT91 and AT32 processors.
+
+config SERIAL_ATMEL_CONSOLE
+	bool "Support for console on AT91 / AT32 serial port"
+	depends on SERIAL_ATMEL=y
+	select SERIAL_CORE_CONSOLE
+	help
+	  Say Y here if you wish to use an on-chip UART on a Atmel
+	  AT91 or AT32 processor as the system console (the system
+	  console is the device which receives all kernel messages and
+	  warnings and which allows logins in single user mode).
+
+config SERIAL_ATMEL_PDC
+	bool "Support DMA transfers on AT91 / AT32 serial port"
+	depends on SERIAL_ATMEL
+	default y
+	help
+	  Say Y here if you wish to use the PDC to do DMA transfers to
+	  and from the Atmel AT91 / AT32 serial port. In order to
+	  actually use DMA transfers, make sure that the use_dma_tx
+	  and use_dma_rx members in the atmel_uart_data struct is set
+	  appropriately for each port.
+
+	  Note that break and error handling currently doesn't work
+	  properly when DMA is enabled. Make sure that ports where
+	  this matters don't use DMA.
+
+config SERIAL_ATMEL_TTYAT
+	bool "Install as device ttyATn instead of ttySn"
+	depends on SERIAL_ATMEL=y
+	help
+	  Say Y here if you wish to have the internal AT91 / AT32 UARTs
+	  appear as /dev/ttyATn (major 204, minor starting at 154)
+	  instead of the normal /dev/ttySn (major 4, minor starting at
+	  64). This is necessary if you also want other UARTs, such as
+	  external 8250/16C550 compatible UARTs.
+	  The ttySn nodes are legally reserved for the 8250 serial driver
+	  but are often misused by other serial drivers.
+
+	  To use this, you should create suitable ttyATn device nodes in
+	  /dev/, and pass "console=ttyATn" to the kernel.
+
+	  Say Y if you have an external 8250/16C550 UART.  If unsure, say N.
+
+config SERIAL_KS8695
+	bool "Micrel KS8695 (Centaur) serial port support"
+	depends on ARCH_KS8695
+	select SERIAL_CORE
+	help
+	  This selects the Micrel Centaur KS8695 UART.  Say Y here.
+
+config SERIAL_KS8695_CONSOLE
+	bool "Support for console on KS8695 (Centaur) serial port"
+	depends on SERIAL_KS8695=y
+	select SERIAL_CORE_CONSOLE
+	help
+	  Say Y here if you wish to use a KS8695 (Centaur) UART as the
+	  system console (the system console is the device which
+	  receives all kernel messages and warnings and which allows
+	  logins in single user mode).
+
+config SERIAL_CLPS711X
+	tristate "CLPS711X serial port support"
+	depends on ARM && ARCH_CLPS711X
+	select SERIAL_CORE
+	help
+	  ::: To be written :::
+
+config SERIAL_CLPS711X_CONSOLE
+	bool "Support for console on CLPS711X serial port"
+	depends on SERIAL_CLPS711X=y
+	select SERIAL_CORE_CONSOLE
+	help
+	  Even if you say Y here, the currently visible virtual console
+	  (/dev/tty0) will still be used as the system console by default, but
+	  you can alter that using a kernel command line option such as
+	  "console=ttyCL1". (Try "man bootparam" or see the documentation of
+	  your boot loader (lilo or loadlin) about how to pass options to the
+	  kernel at boot time.)
+
+config SERIAL_SAMSUNG
+	tristate "Samsung SoC serial support"
+	depends on ARM && PLAT_SAMSUNG
+	select SERIAL_CORE
+	help
+	  Support for the on-chip UARTs on the Samsung S3C24XX series CPUs,
+	  providing /dev/ttySAC0, 1 and 2 (note, some machines may not
+	  provide all of these ports, depending on how the serial port
+	  pins are configured.
+
+config SERIAL_SAMSUNG_UARTS_4
+	bool
+	depends on ARM && PLAT_SAMSUNG
+	default y if !(CPU_S3C2410 || SERIAL_S3C2412 || CPU_S3C2440 || CPU_S3C2442)
+	help
+	  Internal node for the common case of 4 Samsung compatible UARTs
+
+config SERIAL_SAMSUNG_UARTS
+	int
+	depends on ARM && PLAT_SAMSUNG
+	default 6 if CPU_S5P6450
+	default 4 if SERIAL_SAMSUNG_UARTS_4 || CPU_S3C2416
+	default 3
+	help
+	  Select the number of available UART ports for the Samsung S3C
+	  serial driver
+	
+config SERIAL_SAMSUNG_DEBUG
+	bool "Samsung SoC serial debug"
+	depends on SERIAL_SAMSUNG && DEBUG_LL
+	help
+	  Add support for debugging the serial driver. Since this is
+	  generally being used as a console, we use our own output
+	  routines that go via the low-level debug printascii()
+	  function.
+
+config SERIAL_SAMSUNG_CONSOLE
+	bool "Support for console on Samsung SoC serial port"
+	depends on SERIAL_SAMSUNG=y
+	select SERIAL_CORE_CONSOLE
+	help
+	  Allow selection of the S3C24XX on-board serial ports for use as
+	  an virtual console.
+
+	  Even if you say Y here, the currently visible virtual console
+	  (/dev/tty0) will still be used as the system console by default, but
+	  you can alter that using a kernel command line option such as
+	  "console=ttySACx". (Try "man bootparam" or see the documentation of
+	  your boot loader about how to pass options to the kernel at
+	  boot time.)
+
+config SERIAL_SIRFSOC
+        tristate "SiRF SoC Platform Serial port support"
+        depends on ARM && ARCH_PRIMA2
+        select SERIAL_CORE
+        help
+          Support for the on-chip UART on the CSR SiRFprimaII series,
+          providing /dev/ttySiRF0, 1 and 2 (note, some machines may not
+          provide all of these ports, depending on how the serial port
+          pins are configured).
+
+config SERIAL_SIRFSOC_CONSOLE
+        bool "Support for console on SiRF SoC serial port"
+        depends on SERIAL_SIRFSOC=y
+        select SERIAL_CORE_CONSOLE
+        help
+          Even if you say Y here, the currently visible virtual console
+          (/dev/tty0) will still be used as the system console by default, but
+          you can alter that using a kernel command line option such as
+          "console=ttySiRFx". (Try "man bootparam" or see the documentation of
+          your boot loader about how to pass options to the kernel at
+          boot time.)
+
+config SERIAL_MAX3100
+	tristate "MAX3100 support"
+	depends on SPI
+	select SERIAL_CORE
+	help
+	  MAX3100 chip support
+
+config SERIAL_MAX3107
+	tristate "MAX3107 support"
+	depends on SPI
+	select SERIAL_CORE
+	help
+	  MAX3107 chip support
+
+config SERIAL_DZ
+	bool "DECstation DZ serial driver"
+	depends on MACH_DECSTATION && 32BIT
+	select SERIAL_CORE
+	default y
+	---help---
+	  DZ11-family serial controllers for DECstations and VAXstations,
+	  including the DC7085, M7814, and M7819.
+
+config SERIAL_DZ_CONSOLE
+	bool "Support console on DECstation DZ serial driver"
+	depends on SERIAL_DZ=y
+	select SERIAL_CORE_CONSOLE
+	default y
+	---help---
+	  If you say Y here, it will be possible to use a serial port as the
+	  system console (the system console is the device which receives all
+	  kernel messages and warnings and which allows logins in single user
+	  mode).
+
+	  Note that the firmware uses ttyS3 as the serial console on
+	  DECstations that use this driver.
+
+	  If unsure, say Y.
+
+config SERIAL_ZS
+	tristate "DECstation Z85C30 serial support"
+	depends on MACH_DECSTATION
+	select SERIAL_CORE
+	default y
+	---help---
+	  Support for the Zilog 85C350 serial communications controller used
+	  for serial ports in newer DECstation systems.  These include the
+	  DECsystem 5900 and all models of the DECstation and DECsystem 5000
+	  systems except from model 200.
+
+	  If unsure, say Y.  To compile this driver as a module, choose M here:
+	  the module will be called zs.
+
+config SERIAL_ZS_CONSOLE
+	bool "Support for console on a DECstation Z85C30 serial port"
+	depends on SERIAL_ZS=y
+	select SERIAL_CORE_CONSOLE
+	default y
+	---help---
+	  If you say Y here, it will be possible to use a serial port as the
+	  system console (the system console is the device which receives all
+	  kernel messages and warnings and which allows logins in single user
+	  mode).
+
+	  Note that the firmware uses ttyS1 as the serial console on the
+	  Maxine and ttyS3 on the others using this driver.
+
+	  If unsure, say Y.
+
+config SERIAL_21285
+	tristate "DC21285 serial port support"
+	depends on ARM && FOOTBRIDGE
+	select SERIAL_CORE
+	help
+	  If you have a machine based on a 21285 (Footbridge) StrongARM(R)/
+	  PCI bridge you can enable its onboard serial port by enabling this
+	  option.
+
+config SERIAL_21285_CONSOLE
+	bool "Console on DC21285 serial port"
+	depends on SERIAL_21285=y
+	select SERIAL_CORE_CONSOLE
+	help
+	  If you have enabled the serial port on the 21285 footbridge you can
+	  make it the console by answering Y to this option.
+
+	  Even if you say Y here, the currently visible virtual console
+	  (/dev/tty0) will still be used as the system console by default, but
+	  you can alter that using a kernel command line option such as
+	  "console=ttyFB". (Try "man bootparam" or see the documentation of
+	  your boot loader (lilo or loadlin) about how to pass options to the
+	  kernel at boot time.)
+
+config SERIAL_MPSC
+	bool "Marvell MPSC serial port support"
+	depends on PPC32 && MV64X60
+	select SERIAL_CORE
+	help
+	  Say Y here if you want to use the Marvell MPSC serial controller.
+
+config SERIAL_MPSC_CONSOLE
+	bool "Support for console on Marvell MPSC serial port"
+	depends on SERIAL_MPSC
+	select SERIAL_CORE_CONSOLE
+	help
+	  Say Y here if you want to support a serial console on a Marvell MPSC.
+
+config SERIAL_PXA
+	bool "PXA serial port support"
+	depends on ARCH_PXA || ARCH_MMP
+	select SERIAL_CORE
+	help
+	  If you have a machine based on an Intel XScale PXA2xx CPU you
+	  can enable its onboard serial ports by enabling this option.
+
+config SERIAL_PXA_CONSOLE
+	bool "Console on PXA serial port"
+	depends on SERIAL_PXA
+	select SERIAL_CORE_CONSOLE
+	help
+	  If you have enabled the serial port on the Intel XScale PXA
+	  CPU you can make it the console by answering Y to this option.
+
+	  Even if you say Y here, the currently visible virtual console
+	  (/dev/tty0) will still be used as the system console by default, but
+	  you can alter that using a kernel command line option such as
+	  "console=ttySA0". (Try "man bootparam" or see the documentation of
+	  your boot loader (lilo or loadlin) about how to pass options to the
+	  kernel at boot time.)
+
+config SERIAL_SA1100
+	bool "SA1100 serial port support"
+	depends on ARM && ARCH_SA1100
+	select SERIAL_CORE
+	help
+	  If you have a machine based on a SA1100/SA1110 StrongARM(R) CPU you
+	  can enable its onboard serial port by enabling this option.
+	  Please read <file:Documentation/arm/SA1100/serial_UART> for further
+	  info.
+
+config SERIAL_SA1100_CONSOLE
+	bool "Console on SA1100 serial port"
+	depends on SERIAL_SA1100
+	select SERIAL_CORE_CONSOLE
+	help
+	  If you have enabled the serial port on the SA1100/SA1110 StrongARM
+	  CPU you can make it the console by answering Y to this option.
+
+	  Even if you say Y here, the currently visible virtual console
+	  (/dev/tty0) will still be used as the system console by default, but
+	  you can alter that using a kernel command line option such as
+	  "console=ttySA0". (Try "man bootparam" or see the documentation of
+	  your boot loader (lilo or loadlin) about how to pass options to the
+	  kernel at boot time.)
+
+config SERIAL_MRST_MAX3110
+	tristate "SPI UART driver for Max3110"
+	depends on SPI_DW_PCI
+	select SERIAL_CORE
+	select SERIAL_CORE_CONSOLE
+	help
+	  This is the UART protocol driver for the MAX3110 device on
+	  the Intel Moorestown platform. On other systems use the max3100
+	  driver.
+
+config SERIAL_MFD_HSU
+	tristate "Medfield High Speed UART support"
+	depends on PCI
+	select SERIAL_CORE
+
+config SERIAL_MFD_HSU_CONSOLE
+	boolean "Medfile HSU serial console support"
+	depends on SERIAL_MFD_HSU=y
+	select SERIAL_CORE_CONSOLE
+
+config SERIAL_BFIN
+	tristate "Blackfin serial port support"
+	depends on BLACKFIN
+	select SERIAL_CORE
+	select SERIAL_BFIN_UART0 if (BF531 || BF532 || BF533 || BF561)
+	help
+	  Add support for the built-in UARTs on the Blackfin.
+
+	  To compile this driver as a module, choose M here: the
+	  module is named bfin_uart.ko.
+
+config SERIAL_BFIN_CONSOLE
+	bool "Console on Blackfin serial port"
+	depends on SERIAL_BFIN=y
+	select SERIAL_CORE_CONSOLE
+
+choice
+	prompt "UART Mode"
+	depends on SERIAL_BFIN
+	default SERIAL_BFIN_DMA
+	help
+	  This driver supports the built-in serial ports of the Blackfin family
+	  of CPUs
+
+config SERIAL_BFIN_DMA
+	bool "DMA mode"
+	depends on !DMA_UNCACHED_NONE && KGDB_SERIAL_CONSOLE=n
+	help
+	  This driver works under DMA mode. If this option is selected, the
+	  blackfin simple dma driver is also enabled.
+
+config SERIAL_BFIN_PIO
+	bool "PIO mode"
+	help
+	  This driver works under PIO mode.
+
+endchoice
+
+config SERIAL_BFIN_UART0
+	bool "Enable UART0"
+	depends on SERIAL_BFIN
+	help
+	  Enable UART0
+
+config BFIN_UART0_CTSRTS
+	bool "Enable UART0 hardware flow control"
+	depends on SERIAL_BFIN_UART0
+	help
+	  Enable hardware flow control in the driver.
+
+config SERIAL_BFIN_UART1
+	bool "Enable UART1"
+	depends on SERIAL_BFIN && (!BF531 && !BF532 && !BF533 && !BF561)
+	help
+	  Enable UART1
+
+config BFIN_UART1_CTSRTS
+	bool "Enable UART1 hardware flow control"
+	depends on SERIAL_BFIN_UART1
+	help
+	  Enable hardware flow control in the driver.
+
+config SERIAL_BFIN_UART2
+	bool "Enable UART2"
+	depends on SERIAL_BFIN && (BF54x || BF538 || BF539)
+	help
+	  Enable UART2
+
+config BFIN_UART2_CTSRTS
+	bool "Enable UART2 hardware flow control"
+	depends on SERIAL_BFIN_UART2
+	help
+	  Enable hardware flow control in the driver.
+
+config SERIAL_BFIN_UART3
+	bool "Enable UART3"
+	depends on SERIAL_BFIN && (BF54x)
+	help
+	  Enable UART3
+
+config BFIN_UART3_CTSRTS
+	bool "Enable UART3 hardware flow control"
+	depends on SERIAL_BFIN_UART3
+	help
+	  Enable hardware flow control in the driver.
+
+config SERIAL_IMX
+	bool "IMX serial port support"
+	depends on ARCH_MXC
+	select SERIAL_CORE
+	select RATIONAL
+	help
+	  If you have a machine based on a Motorola IMX CPU you
+	  can enable its onboard serial port by enabling this option.
+
+config SERIAL_IMX_CONSOLE
+	bool "Console on IMX serial port"
+	depends on SERIAL_IMX
+	select SERIAL_CORE_CONSOLE
+	help
+	  If you have enabled the serial port on the Motorola IMX
+	  CPU you can make it the console by answering Y to this option.
+
+	  Even if you say Y here, the currently visible virtual console
+	  (/dev/tty0) will still be used as the system console by default, but
+	  you can alter that using a kernel command line option such as
+	  "console=ttySA0". (Try "man bootparam" or see the documentation of
+	  your boot loader (lilo or loadlin) about how to pass options to the
+	  kernel at boot time.)
+
+config SERIAL_UARTLITE
+	tristate "Xilinx uartlite serial port support"
+	depends on PPC32 || MICROBLAZE || MFD_TIMBERDALE
+	select SERIAL_CORE
+	help
+	  Say Y here if you want to use the Xilinx uartlite serial controller.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called uartlite.
+
+config SERIAL_UARTLITE_CONSOLE
+	bool "Support for console on Xilinx uartlite serial port"
+	depends on SERIAL_UARTLITE=y
+	select SERIAL_CORE_CONSOLE
+	help
+	  Say Y here if you wish to use a Xilinx uartlite as the system
+	  console (the system console is the device which receives all kernel
+	  messages and warnings and which allows logins in single user mode).
+
+config SERIAL_SUNCORE
+	bool
+	depends on SPARC
+	select SERIAL_CORE
+	select SERIAL_CORE_CONSOLE
+	default y
+
+config SERIAL_SUNZILOG
+	tristate "Sun Zilog8530 serial support"
+	depends on SPARC
+	help
+	  This driver supports the Zilog8530 serial ports found on many Sparc
+	  systems.  Say Y or M if you want to be able to these serial ports.
+
+config SERIAL_SUNZILOG_CONSOLE
+	bool "Console on Sun Zilog8530 serial port"
+	depends on SERIAL_SUNZILOG=y
+	help
+	  If you would like to be able to use the Zilog8530 serial port
+	  on your Sparc system as the console, you can do so by answering
+	  Y to this option.
+
+config SERIAL_SUNSU
+	tristate "Sun SU serial support"
+	depends on SPARC && PCI
+	help
+	  This driver supports the 8250 serial ports that run the keyboard and
+	  mouse on (PCI) UltraSPARC systems.  Say Y or M if you want to be able
+	  to these serial ports.
+
+config SERIAL_SUNSU_CONSOLE
+	bool "Console on Sun SU serial port"
+	depends on SERIAL_SUNSU=y
+	help
+	  If you would like to be able to use the SU serial port
+	  on your Sparc system as the console, you can do so by answering
+	  Y to this option.
+
+config SERIAL_MUX
+	tristate "Serial MUX support"
+	depends on GSC
+	select SERIAL_CORE
+	default y
+	---help---
+	  Saying Y here will enable the hardware MUX serial driver for
+	  the Nova, K class systems and D class with a 'remote control card'.
+	  The hardware MUX is not 8250/16550 compatible therefore the
+	  /dev/ttyB0 device is shared between the Serial MUX and the PDC
+	  software console. The following steps need to be completed to use
+	  the Serial MUX:
+
+	    1. create the device entry (mknod /dev/ttyB0 c 11 0)
+	    2. Edit the /etc/inittab to start a getty listening on /dev/ttyB0
+	    3. Add device ttyB0 to /etc/securetty (if you want to log on as
+		 root on this console.)
+	    4. Change the kernel command console parameter to: console=ttyB0
+
+config SERIAL_MUX_CONSOLE
+	bool "Support for console on serial MUX"
+	depends on SERIAL_MUX=y
+	select SERIAL_CORE_CONSOLE
+	default y
+
+config PDC_CONSOLE
+	bool "PDC software console support"
+	depends on PARISC && !SERIAL_MUX && VT
+	default n
+	help
+	  Saying Y here will enable the software based PDC console to be 
+	  used as the system console.  This is useful for machines in 
+	  which the hardware based console has not been written yet.  The
+	  following steps must be competed to use the PDC console:
+
+	    1. create the device entry (mknod /dev/ttyB0 c 11 0)
+	    2. Edit the /etc/inittab to start a getty listening on /dev/ttyB0
+	    3. Add device ttyB0 to /etc/securetty (if you want to log on as
+		 root on this console.)
+	    4. Change the kernel command console parameter to: console=ttyB0
+
+config SERIAL_SUNSAB
+	tristate "Sun Siemens SAB82532 serial support"
+	depends on SPARC && PCI
+	help
+	  This driver supports the Siemens SAB82532 DUSCC serial ports on newer
+	  (PCI) UltraSPARC systems.  Say Y or M if you want to be able to these
+	  serial ports.
+
+config SERIAL_SUNSAB_CONSOLE
+	bool "Console on Sun Siemens SAB82532 serial port"
+	depends on SERIAL_SUNSAB=y
+	help
+	  If you would like to be able to use the SAB82532 serial port
+	  on your Sparc system as the console, you can do so by answering
+	  Y to this option.
+
+config SERIAL_SUNHV
+	bool "Sun4v Hypervisor Console support"
+	depends on SPARC64
+	help
+	  This driver supports the console device found on SUN4V Sparc
+	  systems.  Say Y if you want to be able to use this device.
+
+config SERIAL_IP22_ZILOG
+	tristate "SGI Zilog8530 serial support"
+	depends on SGI_HAS_ZILOG
+	select SERIAL_CORE
+	help
+	  This driver supports the Zilog8530 serial ports found on SGI
+	  systems.  Say Y or M if you want to be able to these serial ports.
+
+config SERIAL_IP22_ZILOG_CONSOLE
+	bool "Console on SGI Zilog8530 serial port"
+	depends on SERIAL_IP22_ZILOG=y
+	select SERIAL_CORE_CONSOLE
+
+config SERIAL_SH_SCI
+	tristate "SuperH SCI(F) serial port support"
+	depends on HAVE_CLK && (SUPERH || ARCH_SHMOBILE)
+	select SERIAL_CORE
+
+config SERIAL_SH_SCI_NR_UARTS
+	int "Maximum number of SCI(F) serial ports"
+	depends on SERIAL_SH_SCI
+	default "2"
+
+config SERIAL_SH_SCI_CONSOLE
+	bool "Support for console on SuperH SCI(F)"
+	depends on SERIAL_SH_SCI=y
+	select SERIAL_CORE_CONSOLE
+
+config SERIAL_SH_SCI_DMA
+	bool "DMA support"
+	depends on SERIAL_SH_SCI && SH_DMAE && EXPERIMENTAL
+
+config SERIAL_PNX8XXX
+	bool "Enable PNX8XXX SoCs' UART Support"
+	depends on MIPS && (SOC_PNX8550 || SOC_PNX833X)
+	select SERIAL_CORE
+	help
+	  If you have a MIPS-based Philips SoC such as PNX8550 or PNX8330
+	  and you want to use serial ports, say Y.  Otherwise, say N.
+
+config SERIAL_PNX8XXX_CONSOLE
+	bool "Enable PNX8XX0 serial console"
+	depends on SERIAL_PNX8XXX
+	select SERIAL_CORE_CONSOLE
+	help
+	  If you have a MIPS-based Philips SoC such as PNX8550 or PNX8330
+	  and you want to use serial console, say Y. Otherwise, say N.
+
+config SERIAL_CORE
+	tristate
+
+config SERIAL_CORE_CONSOLE
+	bool
+
+config CONSOLE_POLL
+	bool
+
+config SERIAL_68328
+	bool "68328 serial support"
+	depends on M68328 || M68EZ328 || M68VZ328
+	help
+	  This driver supports the built-in serial port of the Motorola 68328
+	  (standard, EZ and VZ varieties).
+
+config SERIAL_68328_RTS_CTS
+	bool "Support RTS/CTS on 68328 serial port"
+	depends on SERIAL_68328
+
+config SERIAL_MCF
+	bool "Coldfire serial support"
+	depends on COLDFIRE
+	select SERIAL_CORE
+	help
+	  This serial driver supports the Freescale Coldfire serial ports.
+
+config SERIAL_MCF_BAUDRATE
+	int "Default baudrate for Coldfire serial ports"
+	depends on SERIAL_MCF
+	default 19200
+	help
+	  This setting lets you define what the default baudrate is for the
+	  ColdFire serial ports. The usual default varies from board to board,
+	  and this setting is a way of catering for that.
+
+config SERIAL_MCF_CONSOLE
+	bool "Coldfire serial console support"
+	depends on SERIAL_MCF
+	select SERIAL_CORE_CONSOLE
+	help
+	  Enable a ColdFire internal serial port to be the system console.
+
+config SERIAL_PMACZILOG
+	tristate "Mac or PowerMac z85c30 ESCC support"
+	depends on (M68K && MAC) || (PPC_OF && PPC_PMAC)
+	select SERIAL_CORE
+	help
+	  This driver supports the Zilog z85C30 serial ports found on
+	  (Power)Mac machines.
+	  Say Y or M if you want to be able to these serial ports.
+
+config SERIAL_PMACZILOG_TTYS
+	bool "Use ttySn device nodes for Zilog z85c30"
+	depends on SERIAL_PMACZILOG
+	help
+	  The pmac_zilog driver for the z85C30 chip on many powermacs
+	  historically used the device numbers for /dev/ttySn.  The
+	  8250 serial port driver also uses these numbers, which means
+	  the two drivers being unable to coexist; you could not use
+	  both z85C30 and 8250 type ports at the same time.
+
+	  If this option is not selected, the pmac_zilog driver will
+	  use the device numbers allocated for /dev/ttyPZn.  This allows
+	  the pmac_zilog and 8250 drivers to co-exist, but may cause
+	  existing userspace setups to break.  Programs that need to
+	  access the built-in serial ports on powermacs will need to
+	  be reconfigured to use /dev/ttyPZn instead of /dev/ttySn.
+
+	  If you enable this option, any z85c30 ports in the system will
+	  be registered as ttyS0 onwards as in the past, and you will be
+	  unable to use the 8250 module for PCMCIA or other 16C550-style
+	  UARTs.
+
+	  Say N unless you need the z85c30 ports on your (Power)Mac
+	  to appear as /dev/ttySn.
+
+config SERIAL_PMACZILOG_CONSOLE
+	bool "Console on Mac or PowerMac z85c30 serial port"
+	depends on SERIAL_PMACZILOG=y
+	select SERIAL_CORE_CONSOLE
+	help
+	  If you would like to be able to use the z85c30 serial port
+	  on your (Power)Mac as the console, you can do so by answering
+	  Y to this option.
+
+config SERIAL_CPM
+	tristate "CPM SCC/SMC serial port support"
+	depends on CPM2 || 8xx
+	select SERIAL_CORE
+	help
+	  This driver supports the SCC and SMC serial ports on Motorola 
+	  embedded PowerPC that contain a CPM1 (8xx) or CPM2 (8xxx)
+
+config SERIAL_CPM_CONSOLE
+	bool "Support for console on CPM SCC/SMC serial port"
+	depends on SERIAL_CPM=y
+	select SERIAL_CORE_CONSOLE
+	help
+	  Say Y here if you wish to use a SCC or SMC CPM UART as the system
+	  console (the system console is the device which receives all kernel
+	  messages and warnings and which allows logins in single user mode).
+
+	  Even if you say Y here, the currently visible framebuffer console
+	  (/dev/tty0) will still be used as the system console by default, but
+	  you can alter that using a kernel command line option such as
+	  "console=ttyCPM0". (Try "man bootparam" or see the documentation of
+	  your boot loader (lilo or loadlin) about how to pass options to the
+	  kernel at boot time.)
+
+config SERIAL_SGI_L1_CONSOLE
+	bool "SGI Altix L1 serial console support"
+	depends on IA64_GENERIC || IA64_SGI_SN2
+	select SERIAL_CORE
+	select SERIAL_CORE_CONSOLE
+	help
+		If you have an SGI Altix and you would like to use the system
+		controller serial port as your console (you want this!),
+		say Y.  Otherwise, say N.
+
+config SERIAL_MPC52xx
+	tristate "Freescale MPC52xx/MPC512x family PSC serial support"
+	depends on PPC_MPC52xx || PPC_MPC512x
+	select SERIAL_CORE
+	help
+	  This driver supports MPC52xx and MPC512x PSC serial ports. If you would
+	  like to use them, you must answer Y or M to this option. Note that
+	  for use as console, it must be included in kernel and not as a
+	  module.
+
+config SERIAL_MPC52xx_CONSOLE
+	bool "Console on a Freescale MPC52xx/MPC512x family PSC serial port"
+	depends on SERIAL_MPC52xx=y
+	select SERIAL_CORE_CONSOLE
+	help
+	  Select this options if you'd like to use one of the PSC serial port
+	  of the Freescale MPC52xx family as a console.
+
+config SERIAL_MPC52xx_CONSOLE_BAUD
+	int "Freescale MPC52xx/MPC512x family PSC serial port baud"
+	depends on SERIAL_MPC52xx_CONSOLE=y
+	default "9600"
+	help
+	  Select the MPC52xx console baud rate.
+	  This value is only used if the bootloader doesn't pass in the
+	  console baudrate
+
+config SERIAL_ICOM
+	tristate "IBM Multiport Serial Adapter"
+	depends on PCI && PPC_PSERIES
+	select SERIAL_CORE
+	select FW_LOADER
+	help
+	  This driver is for a family of multiport serial adapters
+	  including 2 port RVX, 2 port internal modem, 4 port internal
+	  modem and a split 1 port RVX and 1 port internal modem.
+
+	  This driver can also be built as a module.  If so, the module
+	  will be called icom.
+
+config SERIAL_M32R_SIO
+	bool "M32R SIO I/F"
+	depends on M32R
+	default y
+	select SERIAL_CORE
+	help
+	  Say Y here if you want to use the M32R serial controller.
+
+config SERIAL_M32R_SIO_CONSOLE
+	bool "use SIO console"
+	depends on SERIAL_M32R_SIO=y
+	select SERIAL_CORE_CONSOLE
+	help
+	  Say Y here if you want to support a serial console.
+
+	  If you use an M3T-M32700UT or an OPSPUT platform,
+	  please say also y for SERIAL_M32R_PLDSIO.
+
+config SERIAL_M32R_PLDSIO
+	bool "M32R SIO I/F on a PLD"
+	depends on SERIAL_M32R_SIO=y && (PLAT_OPSPUT || PLAT_USRV || PLAT_M32700UT)
+	default n
+	help
+	  Say Y here if you want to use the M32R serial controller
+	  on a PLD (Programmable Logic Device).
+
+	  If you use an M3T-M32700UT or an OPSPUT platform,
+	  please say Y.
+
+config SERIAL_TXX9
+	bool "TMPTX39XX/49XX SIO support"
+	depends on HAS_TXX9_SERIAL
+	select SERIAL_CORE
+	default y
+
+config HAS_TXX9_SERIAL
+	bool
+
+config SERIAL_TXX9_NR_UARTS
+	int "Maximum number of TMPTX39XX/49XX SIO ports"
+	depends on SERIAL_TXX9
+	default "6"
+
+config SERIAL_TXX9_CONSOLE
+	bool "TMPTX39XX/49XX SIO Console support"
+	depends on SERIAL_TXX9=y
+	select SERIAL_CORE_CONSOLE
+
+config SERIAL_TXX9_STDSERIAL
+	bool "TX39XX/49XX SIO act as standard serial"
+	depends on !SERIAL_8250 && SERIAL_TXX9
+
+config SERIAL_VR41XX
+	tristate "NEC VR4100 series Serial Interface Unit support"
+	depends on CPU_VR41XX
+	select SERIAL_CORE
+	help
+	  If you have a NEC VR4100 series processor and you want to use
+	  Serial Interface Unit(SIU) or Debug Serial Interface Unit(DSIU)
+	  (not include VR4111/VR4121 DSIU), say Y.  Otherwise, say N.
+
+config SERIAL_VR41XX_CONSOLE
+	bool "Enable NEC VR4100 series Serial Interface Unit console"
+	depends on SERIAL_VR41XX=y
+	select SERIAL_CORE_CONSOLE
+	help
+	  If you have a NEC VR4100 series processor and you want to use
+	  a console on a serial port, say Y.  Otherwise, say N.
+
+config SERIAL_JSM
+	tristate "Digi International NEO PCI Support"
+	depends on PCI
+	select SERIAL_CORE
+	help
+	  This is a driver for Digi International's Neo series
+	  of cards which provide multiple serial ports. You would need
+	  something like this to connect more than two modems to your Linux
+	  box, for instance in order to become a dial-in server. This driver
+	  supports PCI boards only.
+
+	  If you have a card like this, say Y here, otherwise say N.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called jsm.
+
+config SERIAL_SGI_IOC4
+	tristate "SGI IOC4 controller serial support"
+	depends on (IA64_GENERIC || IA64_SGI_SN2) && SGI_IOC4
+	select SERIAL_CORE
+	help
+		If you have an SGI Altix with an IOC4 based Base IO card
+		and wish to use the serial ports on this card, say Y.
+		Otherwise, say N.
+
+config SERIAL_SGI_IOC3
+	tristate "SGI Altix IOC3 serial support"
+	depends on (IA64_GENERIC || IA64_SGI_SN2) && SGI_IOC3
+	select SERIAL_CORE
+	help
+	  If you have an SGI Altix with an IOC3 serial card,
+	  say Y or M.  Otherwise, say N.
+
+config SERIAL_MSM
+	bool "MSM on-chip serial port support"
+	depends on ARM && ARCH_MSM
+	select SERIAL_CORE
+
+config SERIAL_MSM_CONSOLE
+	bool "MSM serial console support"
+	depends on SERIAL_MSM=y
+	select SERIAL_CORE_CONSOLE
+
+config SERIAL_MSM_HS
+	tristate "MSM UART High Speed: Serial Driver"
+	depends on ARCH_MSM
+	select SERIAL_CORE
+	help
+	  If you have a machine based on MSM family of SoCs, you
+	  can enable its onboard high speed serial port by enabling
+	  this option.
+
+	  Choose M here to compile it as a module. The module will be
+	  called msm_serial_hs.
+
+config SERIAL_VT8500
+	bool "VIA VT8500 on-chip serial port support"
+	depends on ARM && ARCH_VT8500
+	select SERIAL_CORE
+
+config SERIAL_VT8500_CONSOLE
+	bool "VIA VT8500 serial console support"
+	depends on SERIAL_VT8500=y
+	select SERIAL_CORE_CONSOLE
+
+config SERIAL_NETX
+	tristate "NetX serial port support"
+	depends on ARM && ARCH_NETX
+	select SERIAL_CORE
+	help
+	  If you have a machine based on a Hilscher NetX SoC you
+	  can enable its onboard serial port by enabling this option.
+
+          To compile this driver as a module, choose M here: the
+          module will be called netx-serial.
+
+config SERIAL_NETX_CONSOLE
+	bool "Console on NetX serial port"
+	depends on SERIAL_NETX=y
+	select SERIAL_CORE_CONSOLE
+	help
+	  If you have enabled the serial port on the Hilscher NetX SoC
+	  you can make it the console by answering Y to this option.
+
+config SERIAL_OF_PLATFORM
+	tristate "Serial port on Open Firmware platform bus"
+	depends on OF
+	depends on SERIAL_8250 || SERIAL_OF_PLATFORM_NWPSERIAL
+	help
+	  If you have a PowerPC based system that has serial ports
+	  on a platform specific bus, you should enable this option.
+	  Currently, only 8250 compatible ports are supported, but
+	  others can easily be added.
+
+config SERIAL_OMAP
+	tristate "OMAP serial port support"
+	depends on ARCH_OMAP2PLUS
+	select SERIAL_CORE
+	help
+	  If you have a machine based on an Texas Instruments OMAP CPU you
+	  can enable its onboard serial ports by enabling this option.
+
+	  By enabling this option you take advantage of dma feature available
+	  with the omap-serial driver. DMA support can be enabled from platform
+	  data.
+
+config SERIAL_OMAP_CONSOLE
+	bool "Console on OMAP serial port"
+	depends on SERIAL_OMAP=y
+	select SERIAL_CORE_CONSOLE
+	help
+	  Select this option if you would like to use omap serial port as
+	  console.
+
+	  Even if you say Y here, the currently visible virtual console
+	  (/dev/tty0) will still be used as the system console by default, but
+	  you can alter that using a kernel command line option such as
+	  "console=ttyOx". (Try "man bootparam" or see the documentation of
+	  your boot loader about how to pass options to the kernel at
+	  boot time.)
+
+config SERIAL_OF_PLATFORM_NWPSERIAL
+	tristate "NWP serial port driver"
+	depends on PPC_OF && PPC_DCR
+	select SERIAL_OF_PLATFORM
+	select SERIAL_CORE_CONSOLE
+	select SERIAL_CORE
+	help
+	  This driver supports the cell network processor nwp serial
+	  device.
+
+config SERIAL_OF_PLATFORM_NWPSERIAL_CONSOLE
+	bool "Console on NWP serial port"
+	depends on SERIAL_OF_PLATFORM_NWPSERIAL=y
+	select SERIAL_CORE_CONSOLE
+	help
+	  Support for Console on the NWP serial ports.
+
+config SERIAL_LANTIQ
+	bool "Lantiq serial driver"
+	depends on LANTIQ
+	select SERIAL_CORE
+	select SERIAL_CORE_CONSOLE
+	help
+	  Support for console and UART on Lantiq SoCs.
+
+config SERIAL_QE
+	tristate "Freescale QUICC Engine serial port support"
+	depends on QUICC_ENGINE
+	select SERIAL_CORE
+	select FW_LOADER
+	default n
+	help
+	  This driver supports the QE serial ports on Freescale embedded
+	  PowerPC that contain a QUICC Engine.
+
+config SERIAL_SC26XX
+	tristate "SC2681/SC2692 serial port support"
+	depends on SNI_RM
+	select SERIAL_CORE
+	help
+	  This is a driver for the onboard serial ports of
+	  older RM400 machines.
+
+config SERIAL_SC26XX_CONSOLE
+	bool "Console on SC2681/SC2692 serial port"
+	depends on SERIAL_SC26XX=y
+	select SERIAL_CORE_CONSOLE
+	help
+	  Support for Console on SC2681/SC2692 serial ports.
+
+config SERIAL_BFIN_SPORT
+	tristate "Blackfin SPORT emulate UART"
+	depends on BLACKFIN
+	select SERIAL_CORE
+	help
+	  Enable SPORT emulate UART on Blackfin series.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called bfin_sport_uart.
+
+config SERIAL_BFIN_SPORT_CONSOLE
+	bool "Console on Blackfin sport emulated uart"
+	depends on SERIAL_BFIN_SPORT=y
+	select SERIAL_CORE_CONSOLE
+
+config SERIAL_BFIN_SPORT0_UART
+	bool "Enable UART over SPORT0"
+	depends on SERIAL_BFIN_SPORT && !(BF542 || BF544)
+	help
+	  Enable UART over SPORT0
+
+config SERIAL_BFIN_SPORT0_UART_CTSRTS
+	bool "Enable UART over SPORT0 hardware flow control"
+	depends on SERIAL_BFIN_SPORT0_UART
+	help
+	  Enable hardware flow control in the driver.
+
+config SERIAL_BFIN_SPORT1_UART
+	bool "Enable UART over SPORT1"
+	depends on SERIAL_BFIN_SPORT
+	help
+	  Enable UART over SPORT1
+
+config SERIAL_BFIN_SPORT1_UART_CTSRTS
+	bool "Enable UART over SPORT1 hardware flow control"
+	depends on SERIAL_BFIN_SPORT1_UART
+	help
+	  Enable hardware flow control in the driver.
+
+config SERIAL_BFIN_SPORT2_UART
+	bool "Enable UART over SPORT2"
+	depends on SERIAL_BFIN_SPORT && (BF54x || BF538 || BF539)
+	help
+	  Enable UART over SPORT2
+
+config SERIAL_BFIN_SPORT2_UART_CTSRTS
+	bool "Enable UART over SPORT2 hardware flow control"
+	depends on SERIAL_BFIN_SPORT2_UART
+	help
+	  Enable hardware flow control in the driver.
+
+config SERIAL_BFIN_SPORT3_UART
+	bool "Enable UART over SPORT3"
+	depends on SERIAL_BFIN_SPORT && (BF54x || BF538 || BF539)
+	help
+	  Enable UART over SPORT3
+
+config SERIAL_BFIN_SPORT3_UART_CTSRTS
+	bool "Enable UART over SPORT3 hardware flow control"
+	depends on SERIAL_BFIN_SPORT3_UART
+	help
+	  Enable hardware flow control in the driver.
+
+config SERIAL_TIMBERDALE
+	tristate "Support for timberdale UART"
+	select SERIAL_CORE
+	---help---
+	Add support for UART controller on timberdale.
+
+config SERIAL_BCM63XX
+	tristate "bcm63xx serial port support"
+	select SERIAL_CORE
+	depends on BCM63XX
+	help
+	  If you have a bcm63xx CPU, you can enable its onboard
+	  serial port by enabling this options.
+
+          To compile this driver as a module, choose M here: the
+          module will be called bcm963xx_uart.
+
+config SERIAL_BCM63XX_CONSOLE
+	bool "Console on bcm63xx serial port"
+	depends on SERIAL_BCM63XX=y
+	select SERIAL_CORE_CONSOLE
+	help
+	  If you have enabled the serial port on the bcm63xx CPU
+	  you can make it the console by answering Y to this option.
+
+config SERIAL_GRLIB_GAISLER_APBUART
+	tristate "GRLIB APBUART serial support"
+	depends on OF && SPARC
+	select SERIAL_CORE
+	---help---
+	Add support for the GRLIB APBUART serial port.
+
+config SERIAL_GRLIB_GAISLER_APBUART_CONSOLE
+	bool "Console on GRLIB APBUART serial port"
+	depends on SERIAL_GRLIB_GAISLER_APBUART=y
+	select SERIAL_CORE_CONSOLE
+	help
+	Support for running a console on the GRLIB APBUART
+
+config SERIAL_ALTERA_JTAGUART
+	tristate "Altera JTAG UART support"
+	select SERIAL_CORE
+	help
+	  This driver supports the Altera JTAG UART port.
+
+config SERIAL_ALTERA_JTAGUART_CONSOLE
+	bool "Altera JTAG UART console support"
+	depends on SERIAL_ALTERA_JTAGUART=y
+	select SERIAL_CORE_CONSOLE
+	help
+	  Enable a Altera JTAG UART port to be the system console.
+
+config SERIAL_ALTERA_JTAGUART_CONSOLE_BYPASS
+	bool "Bypass output when no connection"
+	depends on SERIAL_ALTERA_JTAGUART_CONSOLE
+	select SERIAL_CORE_CONSOLE
+	help
+	  Bypass console output and keep going even if there is no
+	  JTAG terminal connection with the host.
+
+config SERIAL_ALTERA_UART
+	tristate "Altera UART support"
+	select SERIAL_CORE
+	help
+	  This driver supports the Altera softcore UART port.
+
+config SERIAL_ALTERA_UART_MAXPORTS
+	int "Maximum number of Altera UART ports"
+	depends on SERIAL_ALTERA_UART
+	default 4
+	help
+	  This setting lets you define the maximum number of the Altera
+	  UART ports. The usual default varies from board to board, and
+	  this setting is a way of catering for that.
+
+config SERIAL_ALTERA_UART_BAUDRATE
+	int "Default baudrate for Altera UART ports"
+	depends on SERIAL_ALTERA_UART
+	default 115200
+	help
+	  This setting lets you define what the default baudrate is for the
+	  Altera UART ports. The usual default varies from board to board,
+	  and this setting is a way of catering for that.
+
+config SERIAL_ALTERA_UART_CONSOLE
+	bool "Altera UART console support"
+	depends on SERIAL_ALTERA_UART=y
+	select SERIAL_CORE_CONSOLE
+	help
+	  Enable a Altera UART port to be the system console.
+
+config SERIAL_IFX6X60
+        tristate "SPI protocol driver for Infineon 6x60 modem (EXPERIMENTAL)"
+	depends on GPIOLIB && SPI && EXPERIMENTAL
+	help
+	  Support for the IFX6x60 modem devices on Intel MID platforms.
+
+config SERIAL_PCH_UART
+	tristate "Intel EG20T PCH/LAPIS Semicon IOH(ML7213/ML7223/ML7831) UART"
+	depends on PCI
+	select SERIAL_CORE
+	help
+	  This driver is for PCH(Platform controller Hub) UART of Intel EG20T
+	  which is an IOH(Input/Output Hub) for x86 embedded processor.
+	  Enabling PCH_DMA, this PCH UART works as DMA mode.
+
+	  This driver also can be used for LAPIS Semiconductor IOH(Input/
+	  Output Hub), ML7213, ML7223 and ML7831.
+	  ML7213 IOH is for IVI(In-Vehicle Infotainment) use, ML7223 IOH is
+	  for MP(Media Phone) use and ML7831 IOH is for general purpose use.
+	  ML7213/ML7223/ML7831 is companion chip for Intel Atom E6xx series.
+	  ML7213/ML7223/ML7831 is completely compatible for Intel EG20T PCH.
+
+config SERIAL_PCH_UART_CONSOLE
+	bool "Support for console on Intel EG20T PCH UART/OKI SEMICONDUCTOR ML7213 IOH"
+	depends on SERIAL_PCH_UART=y
+	select SERIAL_CORE_CONSOLE
+	help
+	  Say Y here if you wish to use the PCH UART as the system console
+	  (the system  console is the device which receives all kernel messages and
+	  warnings and which allows logins in single user mode).
+
+config SERIAL_MSM_SMD
+	bool "Enable tty device interface for some SMD ports"
+	default n
+	depends on MSM_SMD
+	help
+	  Enables userspace clients to read and write to some streaming SMD
+	  ports via tty device interface for MSM chipset.
+
+config SERIAL_MXS_AUART
+	depends on ARCH_MXS
+	tristate "MXS AUART support"
+	select SERIAL_CORE
+	help
+	  This driver supports the MXS Application UART (AUART) port.
+
+config SERIAL_MXS_AUART_CONSOLE
+	bool "MXS AUART console support"
+	depends on SERIAL_MXS_AUART=y
+	select SERIAL_CORE_CONSOLE
+	help
+	  Enable a MXS AUART port to be the system console.
+
+config SERIAL_XILINX_PS_UART
+	tristate "Xilinx PS UART support"
+	select SERIAL_CORE
+	help
+	  This driver supports the Xilinx PS UART port.
+
+config SERIAL_XILINX_PS_UART_CONSOLE
+	bool "Xilinx PS UART console support"
+	depends on SERIAL_XILINX_PS_UART=y
+	select SERIAL_CORE_CONSOLE
+	help
+	  Enable a Xilinx PS UART port to be the system console.
+
+config SERIAL_AR933X
+	bool "AR933X serial port support"
+	depends on SOC_AR933X
+	select SERIAL_CORE
+	help
+	  If you have an Atheros AR933X SOC based board and want to use the
+	  built-in UART of the SoC, say Y to this option.
+
+config SERIAL_AR933X_CONSOLE
+	bool "Console on AR933X serial port"
+	depends on SERIAL_AR933X=y
+	select SERIAL_CORE_CONSOLE
+	help
+	  Enable a built-in UART port of the AR933X to be the system console.
+
+config SERIAL_AR933X_NR_UARTS
+	int "Maximum number of AR933X serial ports"
+	depends on SERIAL_AR933X
+	default "2"
+	help
+	  Set this to the number of serial ports you want the driver
+	  to support.
+
+config SERIAL_EFM32_UART
+	tristate "EFM32 UART/USART port."
+	depends on ARCH_EFM32
+	select SERIAL_CORE
+	help
+	  This driver support the USART and UART ports on
+	  Energy Micro's efm32 SoCs.
+
+config SERIAL_EFM32_UART_CONSOLE
+	bool "EFM32 UART/USART console support"
+	depends on SERIAL_EFM32_UART=y
+	select SERIAL_CORE_CONSOLE
+
+config SERIAL_ZX297502_UART
+	tristate "zx297502 UART support"
+	select SERIAL_CORE
+	help
+	  This driver supports the zx297502 UART port.
+	  
+config SERIAL_ZX297502_UART_CONSOLE
+	bool "Support for console on zx297502 serial port"
+	depends on SERIAL_ZX297502_UART=y
+	select SERIAL_CORE_CONSOLE
+	---help---
+	  Say Y here if you wish to use an zx297502 UART as the system
+	  console (the system console is the device which receives all kernel
+	  messages and warnings and which allows logins in single user mode).
+
+config SERIAL_ZX297510_UART
+	tristate "zx297510 UART support"
+	select SERIAL_CORE
+	help
+	  This driver supports the zx297510 UART port.
+	  
+config SERIAL_ZX297510_UART_CONSOLE
+	bool "Support for console on zx297510 serial port"
+	depends on SERIAL_ZX297510_UART=y
+	select SERIAL_CORE_CONSOLE
+	---help---
+	  Say Y here if you wish to use an zx297510 UART as the system
+	  console (the system console is the device which receives all kernel
+	  messages and warnings and which allows logins in single user mode).
+
+
+config SERIAL_ZX29_UART
+	tristate "zx29 UART support"
+	select SERIAL_CORE
+	default y
+	help
+	  This driver supports the zx297510 UART port.
+	  
+config SERIAL_ZX29_UART_CONSOLE
+	bool "Support for console on zx29 serial port"
+	depends on SERIAL_ZX29_UART=y
+	select SERIAL_CORE_CONSOLE
+	default y
+	---help---
+	  Say Y here if you wish to use an zx29 UART as the system
+	  console (the system console is the device which receives all kernel
+	  messages and warnings and which allows logins in single user mode).
+	  
+config UART_CONSOLE_ID
+	int  "zx29 serial port id to be used console "
+	depends on SERIAL_ZX29_UART_CONSOLE=y
+	range 0 2 
+    default 1 
+	---help---
+	  there are 3 uart ports ,if you wish to use an zx29 UART as the system console, 
+	  you should select one,range is 0-2, default value is 1.  
+
+endmenu
diff --git a/ap/os/linux/linux-3.4.x/drivers/tty/serial/Makefile b/ap/os/linux/linux-3.4.x/drivers/tty/serial/Makefile
new file mode 100644
index 0000000..648b395
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/tty/serial/Makefile
@@ -0,0 +1,85 @@
+#
+# Makefile for the kernel serial device drivers.
+#
+
+obj-$(CONFIG_SERIAL_CORE) += serial_core.o
+obj-$(CONFIG_SERIAL_21285) += 21285.o
+
+# These Sparc drivers have to appear before others such as 8250
+# which share ttySx minor node space.  Otherwise console device
+# names change and other unplesantries.
+obj-$(CONFIG_SERIAL_SUNCORE) += suncore.o
+obj-$(CONFIG_SERIAL_SUNHV) += sunhv.o
+obj-$(CONFIG_SERIAL_SUNZILOG) += sunzilog.o
+obj-$(CONFIG_SERIAL_SUNSU) += sunsu.o
+obj-$(CONFIG_SERIAL_SUNSAB) += sunsab.o
+
+# Now bring in any enabled 8250/16450/16550 type drivers.
+obj-$(CONFIG_SERIAL_8250) += 8250/
+
+obj-$(CONFIG_SERIAL_AMBA_PL010) += amba-pl010.o
+obj-$(CONFIG_SERIAL_AMBA_PL011) += amba-pl011.o
+obj-$(CONFIG_SERIAL_CLPS711X) += clps711x.o
+obj-$(CONFIG_SERIAL_PXA) += pxa.o
+obj-$(CONFIG_SERIAL_PNX8XXX) += pnx8xxx_uart.o
+obj-$(CONFIG_SERIAL_SA1100) += sa1100.o
+obj-$(CONFIG_SERIAL_BCM63XX) += bcm63xx_uart.o
+obj-$(CONFIG_SERIAL_BFIN) += bfin_uart.o
+obj-$(CONFIG_SERIAL_BFIN_SPORT) += bfin_sport_uart.o
+obj-$(CONFIG_SERIAL_SAMSUNG) += samsung.o
+obj-$(CONFIG_SERIAL_MAX3100) += max3100.o
+obj-$(CONFIG_SERIAL_MAX3107) += max3107.o
+obj-$(CONFIG_SERIAL_IP22_ZILOG) += ip22zilog.o
+obj-$(CONFIG_SERIAL_MUX) += mux.o
+obj-$(CONFIG_SERIAL_68328) += 68328serial.o
+obj-$(CONFIG_SERIAL_MCF) += mcf.o
+obj-$(CONFIG_SERIAL_PMACZILOG) += pmac_zilog.o
+obj-$(CONFIG_SERIAL_DZ) += dz.o
+obj-$(CONFIG_SERIAL_ZS) += zs.o
+obj-$(CONFIG_SERIAL_SH_SCI) += sh-sci.o
+obj-$(CONFIG_SERIAL_SGI_L1_CONSOLE) += sn_console.o
+obj-$(CONFIG_SERIAL_CPM) += cpm_uart/
+obj-$(CONFIG_SERIAL_IMX) += imx.o
+obj-$(CONFIG_SERIAL_MPC52xx) += mpc52xx_uart.o
+obj-$(CONFIG_SERIAL_ICOM) += icom.o
+obj-$(CONFIG_SERIAL_M32R_SIO) += m32r_sio.o
+obj-$(CONFIG_SERIAL_MPSC) += mpsc.o
+obj-$(CONFIG_SERIAL_SB1250_DUART) += sb1250-duart.o
+obj-$(CONFIG_ETRAX_SERIAL) += crisv10.o
+obj-$(CONFIG_SERIAL_SC26XX) += sc26xx.o
+obj-$(CONFIG_SERIAL_JSM) += jsm/
+obj-$(CONFIG_SERIAL_TXX9) += serial_txx9.o
+obj-$(CONFIG_SERIAL_VR41XX) += vr41xx_siu.o
+obj-$(CONFIG_SERIAL_SGI_IOC4) += ioc4_serial.o
+obj-$(CONFIG_SERIAL_SGI_IOC3) += ioc3_serial.o
+obj-$(CONFIG_SERIAL_ATMEL) += atmel_serial.o
+obj-$(CONFIG_SERIAL_UARTLITE) += uartlite.o
+obj-$(CONFIG_SERIAL_MSM) += msm_serial.o
+obj-$(CONFIG_SERIAL_MSM_HS) += msm_serial_hs.o
+obj-$(CONFIG_SERIAL_NETX) += netx-serial.o
+obj-$(CONFIG_SERIAL_OF_PLATFORM) += of_serial.o
+obj-$(CONFIG_SERIAL_OF_PLATFORM_NWPSERIAL) += nwpserial.o
+obj-$(CONFIG_SERIAL_KS8695) += serial_ks8695.o
+obj-$(CONFIG_SERIAL_OMAP) += omap-serial.o
+obj-$(CONFIG_SERIAL_ALTERA_UART) += altera_uart.o
+obj-$(CONFIG_KGDB_SERIAL_CONSOLE) += kgdboc.o
+obj-$(CONFIG_SERIAL_QE) += ucc_uart.o
+obj-$(CONFIG_SERIAL_TIMBERDALE)	+= timbuart.o
+obj-$(CONFIG_SERIAL_GRLIB_GAISLER_APBUART) += apbuart.o
+obj-$(CONFIG_SERIAL_ALTERA_JTAGUART) += altera_jtaguart.o
+obj-$(CONFIG_SERIAL_VT8500) += vt8500_serial.o
+obj-$(CONFIG_SERIAL_MRST_MAX3110)	+= mrst_max3110.o
+obj-$(CONFIG_SERIAL_MFD_HSU)	+= mfd.o
+obj-$(CONFIG_SERIAL_IFX6X60)  	+= ifx6x60.o
+obj-$(CONFIG_SERIAL_PCH_UART)	+= pch_uart.o
+obj-$(CONFIG_SERIAL_MSM_SMD)	+= msm_smd_tty.o
+obj-$(CONFIG_SERIAL_MXS_AUART) += mxs-auart.o
+obj-$(CONFIG_SERIAL_LANTIQ)	+= lantiq.o
+obj-$(CONFIG_SERIAL_XILINX_PS_UART) += xilinx_uartps.o
+obj-$(CONFIG_SERIAL_SIRFSOC) += sirfsoc_uart.o
+obj-$(CONFIG_SERIAL_AR933X)   += ar933x_uart.o
+obj-$(CONFIG_SERIAL_EFM32_UART) += efm32-uart.o
+obj-$(CONFIG_SERIAL_ZX297502_UART) += zx297502_uart.o
+obj-$(CONFIG_SERIAL_ZX297510_UART) += zx297510_uart.o
+obj-$(CONFIG_SERIAL_ZX29_UART) 	   += zx29_uart.o
+
diff --git a/ap/os/linux/linux-3.4.x/drivers/tty/serial/altera_jtaguart.c b/ap/os/linux/linux-3.4.x/drivers/tty/serial/altera_jtaguart.c
new file mode 100644
index 0000000..530181e
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/tty/serial/altera_jtaguart.c
@@ -0,0 +1,515 @@
+/*
+ * altera_jtaguart.c -- Altera JTAG UART driver
+ *
+ * Based on mcf.c -- Freescale ColdFire UART driver
+ *
+ * (C) Copyright 2003-2007, Greg Ungerer <gerg@snapgear.com>
+ * (C) Copyright 2008, Thomas Chou <thomas@wytron.com.tw>
+ * (C) Copyright 2010, Tobias Klauser <tklauser@distanz.ch>
+ *
+ * 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.
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/console.h>
+#include <linux/of.h>
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
+#include <linux/serial.h>
+#include <linux/serial_core.h>
+#include <linux/platform_device.h>
+#include <linux/io.h>
+#include <linux/altera_jtaguart.h>
+
+#define DRV_NAME "altera_jtaguart"
+
+/*
+ * Altera JTAG UART register definitions according to the Altera JTAG UART
+ * datasheet: http://www.altera.com/literature/hb/nios2/n2cpu_nii51009.pdf
+ */
+
+#define ALTERA_JTAGUART_SIZE			8
+
+#define ALTERA_JTAGUART_DATA_REG		0
+
+#define ALTERA_JTAGUART_DATA_DATA_MSK		0x000000FF
+#define ALTERA_JTAGUART_DATA_RVALID_MSK		0x00008000
+#define ALTERA_JTAGUART_DATA_RAVAIL_MSK		0xFFFF0000
+#define ALTERA_JTAGUART_DATA_RAVAIL_OFF		16
+
+#define ALTERA_JTAGUART_CONTROL_REG		4
+
+#define ALTERA_JTAGUART_CONTROL_RE_MSK		0x00000001
+#define ALTERA_JTAGUART_CONTROL_WE_MSK		0x00000002
+#define ALTERA_JTAGUART_CONTROL_RI_MSK		0x00000100
+#define ALTERA_JTAGUART_CONTROL_RI_OFF		8
+#define ALTERA_JTAGUART_CONTROL_WI_MSK		0x00000200
+#define ALTERA_JTAGUART_CONTROL_AC_MSK		0x00000400
+#define ALTERA_JTAGUART_CONTROL_WSPACE_MSK	0xFFFF0000
+#define ALTERA_JTAGUART_CONTROL_WSPACE_OFF	16
+
+/*
+ * Local per-uart structure.
+ */
+struct altera_jtaguart {
+	struct uart_port port;
+	unsigned int sigs;	/* Local copy of line sigs */
+	unsigned long imr;	/* Local IMR mirror */
+};
+
+static unsigned int altera_jtaguart_tx_empty(struct uart_port *port)
+{
+	return (readl(port->membase + ALTERA_JTAGUART_CONTROL_REG) &
+		ALTERA_JTAGUART_CONTROL_WSPACE_MSK) ? TIOCSER_TEMT : 0;
+}
+
+static unsigned int altera_jtaguart_get_mctrl(struct uart_port *port)
+{
+	return TIOCM_CAR | TIOCM_DSR | TIOCM_CTS;
+}
+
+static void altera_jtaguart_set_mctrl(struct uart_port *port, unsigned int sigs)
+{
+}
+
+static void altera_jtaguart_start_tx(struct uart_port *port)
+{
+	struct altera_jtaguart *pp =
+	    container_of(port, struct altera_jtaguart, port);
+
+	pp->imr |= ALTERA_JTAGUART_CONTROL_WE_MSK;
+	writel(pp->imr, port->membase + ALTERA_JTAGUART_CONTROL_REG);
+}
+
+static void altera_jtaguart_stop_tx(struct uart_port *port)
+{
+	struct altera_jtaguart *pp =
+	    container_of(port, struct altera_jtaguart, port);
+
+	pp->imr &= ~ALTERA_JTAGUART_CONTROL_WE_MSK;
+	writel(pp->imr, port->membase + ALTERA_JTAGUART_CONTROL_REG);
+}
+
+static void altera_jtaguart_stop_rx(struct uart_port *port)
+{
+	struct altera_jtaguart *pp =
+	    container_of(port, struct altera_jtaguart, port);
+
+	pp->imr &= ~ALTERA_JTAGUART_CONTROL_RE_MSK;
+	writel(pp->imr, port->membase + ALTERA_JTAGUART_CONTROL_REG);
+}
+
+static void altera_jtaguart_break_ctl(struct uart_port *port, int break_state)
+{
+}
+
+static void altera_jtaguart_enable_ms(struct uart_port *port)
+{
+}
+
+static void altera_jtaguart_set_termios(struct uart_port *port,
+					struct ktermios *termios,
+					struct ktermios *old)
+{
+	/* Just copy the old termios settings back */
+	if (old)
+		tty_termios_copy_hw(termios, old);
+}
+
+static void altera_jtaguart_rx_chars(struct altera_jtaguart *pp)
+{
+	struct uart_port *port = &pp->port;
+	unsigned char ch, flag;
+	unsigned long status;
+
+	while ((status = readl(port->membase + ALTERA_JTAGUART_DATA_REG)) &
+	       ALTERA_JTAGUART_DATA_RVALID_MSK) {
+		ch = status & ALTERA_JTAGUART_DATA_DATA_MSK;
+		flag = TTY_NORMAL;
+		port->icount.rx++;
+
+		if (uart_handle_sysrq_char(port, ch))
+			continue;
+		uart_insert_char(port, 0, 0, ch, flag);
+	}
+
+	tty_flip_buffer_push(port->state->port.tty);
+}
+
+static void altera_jtaguart_tx_chars(struct altera_jtaguart *pp)
+{
+	struct uart_port *port = &pp->port;
+	struct circ_buf *xmit = &port->state->xmit;
+	unsigned int pending, count;
+
+	if (port->x_char) {
+		/* Send special char - probably flow control */
+		writel(port->x_char, port->membase + ALTERA_JTAGUART_DATA_REG);
+		port->x_char = 0;
+		port->icount.tx++;
+		return;
+	}
+
+	pending = uart_circ_chars_pending(xmit);
+	if (pending > 0) {
+		count = (readl(port->membase + ALTERA_JTAGUART_CONTROL_REG) &
+				ALTERA_JTAGUART_CONTROL_WSPACE_MSK) >>
+			ALTERA_JTAGUART_CONTROL_WSPACE_OFF;
+		if (count > pending)
+			count = pending;
+		if (count > 0) {
+			pending -= count;
+			while (count--) {
+				writel(xmit->buf[xmit->tail],
+				       port->membase + ALTERA_JTAGUART_DATA_REG);
+				xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
+				port->icount.tx++;
+			}
+			if (pending < WAKEUP_CHARS)
+				uart_write_wakeup(port);
+		}
+	}
+
+	if (pending == 0) {
+		pp->imr &= ~ALTERA_JTAGUART_CONTROL_WE_MSK;
+		writel(pp->imr, port->membase + ALTERA_JTAGUART_CONTROL_REG);
+	}
+}
+
+static irqreturn_t altera_jtaguart_interrupt(int irq, void *data)
+{
+	struct uart_port *port = data;
+	struct altera_jtaguart *pp =
+	    container_of(port, struct altera_jtaguart, port);
+	unsigned int isr;
+
+	isr = (readl(port->membase + ALTERA_JTAGUART_CONTROL_REG) >>
+	       ALTERA_JTAGUART_CONTROL_RI_OFF) & pp->imr;
+
+	spin_lock(&port->lock);
+
+	if (isr & ALTERA_JTAGUART_CONTROL_RE_MSK)
+		altera_jtaguart_rx_chars(pp);
+	if (isr & ALTERA_JTAGUART_CONTROL_WE_MSK)
+		altera_jtaguart_tx_chars(pp);
+
+	spin_unlock(&port->lock);
+
+	return IRQ_RETVAL(isr);
+}
+
+static void altera_jtaguart_config_port(struct uart_port *port, int flags)
+{
+	port->type = PORT_ALTERA_JTAGUART;
+
+	/* Clear mask, so no surprise interrupts. */
+	writel(0, port->membase + ALTERA_JTAGUART_CONTROL_REG);
+}
+
+static int altera_jtaguart_startup(struct uart_port *port)
+{
+	struct altera_jtaguart *pp =
+	    container_of(port, struct altera_jtaguart, port);
+	unsigned long flags;
+	int ret;
+
+	ret = request_irq(port->irq, altera_jtaguart_interrupt, 0,
+			DRV_NAME, port);
+	if (ret) {
+		pr_err(DRV_NAME ": unable to attach Altera JTAG UART %d "
+		       "interrupt vector=%d\n", port->line, port->irq);
+		return ret;
+	}
+
+	spin_lock_irqsave(&port->lock, flags);
+
+	/* Enable RX interrupts now */
+	pp->imr = ALTERA_JTAGUART_CONTROL_RE_MSK;
+	writel(pp->imr, port->membase + ALTERA_JTAGUART_CONTROL_REG);
+
+	spin_unlock_irqrestore(&port->lock, flags);
+
+	return 0;
+}
+
+static void altera_jtaguart_shutdown(struct uart_port *port)
+{
+	struct altera_jtaguart *pp =
+	    container_of(port, struct altera_jtaguart, port);
+	unsigned long flags;
+
+	spin_lock_irqsave(&port->lock, flags);
+
+	/* Disable all interrupts now */
+	pp->imr = 0;
+	writel(pp->imr, port->membase + ALTERA_JTAGUART_CONTROL_REG);
+
+	spin_unlock_irqrestore(&port->lock, flags);
+
+	free_irq(port->irq, port);
+}
+
+static const char *altera_jtaguart_type(struct uart_port *port)
+{
+	return (port->type == PORT_ALTERA_JTAGUART) ? "Altera JTAG UART" : NULL;
+}
+
+static int altera_jtaguart_request_port(struct uart_port *port)
+{
+	/* UARTs always present */
+	return 0;
+}
+
+static void altera_jtaguart_release_port(struct uart_port *port)
+{
+	/* Nothing to release... */
+}
+
+static int altera_jtaguart_verify_port(struct uart_port *port,
+				       struct serial_struct *ser)
+{
+	if (ser->type != PORT_UNKNOWN && ser->type != PORT_ALTERA_JTAGUART)
+		return -EINVAL;
+	return 0;
+}
+
+/*
+ *	Define the basic serial functions we support.
+ */
+static struct uart_ops altera_jtaguart_ops = {
+	.tx_empty	= altera_jtaguart_tx_empty,
+	.get_mctrl	= altera_jtaguart_get_mctrl,
+	.set_mctrl	= altera_jtaguart_set_mctrl,
+	.start_tx	= altera_jtaguart_start_tx,
+	.stop_tx	= altera_jtaguart_stop_tx,
+	.stop_rx	= altera_jtaguart_stop_rx,
+	.enable_ms	= altera_jtaguart_enable_ms,
+	.break_ctl	= altera_jtaguart_break_ctl,
+	.startup	= altera_jtaguart_startup,
+	.shutdown	= altera_jtaguart_shutdown,
+	.set_termios	= altera_jtaguart_set_termios,
+	.type		= altera_jtaguart_type,
+	.request_port	= altera_jtaguart_request_port,
+	.release_port	= altera_jtaguart_release_port,
+	.config_port	= altera_jtaguart_config_port,
+	.verify_port	= altera_jtaguart_verify_port,
+};
+
+#define ALTERA_JTAGUART_MAXPORTS 1
+static struct altera_jtaguart altera_jtaguart_ports[ALTERA_JTAGUART_MAXPORTS];
+
+#if defined(CONFIG_SERIAL_ALTERA_JTAGUART_CONSOLE)
+
+#if defined(CONFIG_SERIAL_ALTERA_JTAGUART_CONSOLE_BYPASS)
+static void altera_jtaguart_console_putc(struct console *co, const char c)
+{
+	struct uart_port *port = &(altera_jtaguart_ports + co->index)->port;
+	unsigned long status;
+	unsigned long flags;
+
+	spin_lock_irqsave(&port->lock, flags);
+	while (((status = readl(port->membase + ALTERA_JTAGUART_CONTROL_REG)) &
+		ALTERA_JTAGUART_CONTROL_WSPACE_MSK) == 0) {
+		if ((status & ALTERA_JTAGUART_CONTROL_AC_MSK) == 0) {
+			spin_unlock_irqrestore(&port->lock, flags);
+			return;	/* no connection activity */
+		}
+		spin_unlock_irqrestore(&port->lock, flags);
+		cpu_relax();
+		spin_lock_irqsave(&port->lock, flags);
+	}
+	writel(c, port->membase + ALTERA_JTAGUART_DATA_REG);
+	spin_unlock_irqrestore(&port->lock, flags);
+}
+#else
+static void altera_jtaguart_console_putc(struct console *co, const char c)
+{
+	struct uart_port *port = &(altera_jtaguart_ports + co->index)->port;
+	unsigned long flags;
+
+	spin_lock_irqsave(&port->lock, flags);
+	while ((readl(port->membase + ALTERA_JTAGUART_CONTROL_REG) &
+		ALTERA_JTAGUART_CONTROL_WSPACE_MSK) == 0) {
+		spin_unlock_irqrestore(&port->lock, flags);
+		cpu_relax();
+		spin_lock_irqsave(&port->lock, flags);
+	}
+	writel(c, port->membase + ALTERA_JTAGUART_DATA_REG);
+	spin_unlock_irqrestore(&port->lock, flags);
+}
+#endif
+
+static void altera_jtaguart_console_write(struct console *co, const char *s,
+					  unsigned int count)
+{
+	for (; count; count--, s++) {
+		altera_jtaguart_console_putc(co, *s);
+		if (*s == '\n')
+			altera_jtaguart_console_putc(co, '\r');
+	}
+}
+
+static int __init altera_jtaguart_console_setup(struct console *co,
+						char *options)
+{
+	struct uart_port *port;
+
+	if (co->index < 0 || co->index >= ALTERA_JTAGUART_MAXPORTS)
+		return -EINVAL;
+	port = &altera_jtaguart_ports[co->index].port;
+	if (port->membase == NULL)
+		return -ENODEV;
+	return 0;
+}
+
+static struct uart_driver altera_jtaguart_driver;
+
+static struct console altera_jtaguart_console = {
+	.name	= "ttyJ",
+	.write	= altera_jtaguart_console_write,
+	.device	= uart_console_device,
+	.setup	= altera_jtaguart_console_setup,
+	.flags	= CON_PRINTBUFFER,
+	.index	= -1,
+	.data	= &altera_jtaguart_driver,
+};
+
+static int __init altera_jtaguart_console_init(void)
+{
+	register_console(&altera_jtaguart_console);
+	return 0;
+}
+
+console_initcall(altera_jtaguart_console_init);
+
+#define	ALTERA_JTAGUART_CONSOLE	(&altera_jtaguart_console)
+
+#else
+
+#define	ALTERA_JTAGUART_CONSOLE	NULL
+
+#endif /* CONFIG_ALTERA_JTAGUART_CONSOLE */
+
+static struct uart_driver altera_jtaguart_driver = {
+	.owner		= THIS_MODULE,
+	.driver_name	= "altera_jtaguart",
+	.dev_name	= "ttyJ",
+	.major		= ALTERA_JTAGUART_MAJOR,
+	.minor		= ALTERA_JTAGUART_MINOR,
+	.nr		= ALTERA_JTAGUART_MAXPORTS,
+	.cons		= ALTERA_JTAGUART_CONSOLE,
+};
+
+static int __devinit altera_jtaguart_probe(struct platform_device *pdev)
+{
+	struct altera_jtaguart_platform_uart *platp = pdev->dev.platform_data;
+	struct uart_port *port;
+	struct resource *res_irq, *res_mem;
+	int i = pdev->id;
+
+	/* -1 emphasizes that the platform must have one port, no .N suffix */
+	if (i == -1)
+		i = 0;
+
+	if (i >= ALTERA_JTAGUART_MAXPORTS)
+		return -EINVAL;
+
+	port = &altera_jtaguart_ports[i].port;
+
+	res_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (res_mem)
+		port->mapbase = res_mem->start;
+	else if (platp)
+		port->mapbase = platp->mapbase;
+	else
+		return -ENODEV;
+
+	res_irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+	if (res_irq)
+		port->irq = res_irq->start;
+	else if (platp)
+		port->irq = platp->irq;
+	else
+		return -ENODEV;
+
+	port->membase = ioremap(port->mapbase, ALTERA_JTAGUART_SIZE);
+	if (!port->membase)
+		return -ENOMEM;
+
+	port->line = i;
+	port->type = PORT_ALTERA_JTAGUART;
+	port->iotype = SERIAL_IO_MEM;
+	port->ops = &altera_jtaguart_ops;
+	port->flags = UPF_BOOT_AUTOCONF;
+
+	uart_add_one_port(&altera_jtaguart_driver, port);
+
+	return 0;
+}
+
+static int __devexit altera_jtaguart_remove(struct platform_device *pdev)
+{
+	struct uart_port *port;
+	int i = pdev->id;
+
+	if (i == -1)
+		i = 0;
+
+	port = &altera_jtaguart_ports[i].port;
+	uart_remove_one_port(&altera_jtaguart_driver, port);
+
+	return 0;
+}
+
+#ifdef CONFIG_OF
+static struct of_device_id altera_jtaguart_match[] = {
+	{ .compatible = "ALTR,juart-1.0", },
+	{},
+};
+MODULE_DEVICE_TABLE(of, altera_jtaguart_match);
+#endif /* CONFIG_OF */
+
+static struct platform_driver altera_jtaguart_platform_driver = {
+	.probe	= altera_jtaguart_probe,
+	.remove	= __devexit_p(altera_jtaguart_remove),
+	.driver	= {
+		.name		= DRV_NAME,
+		.owner		= THIS_MODULE,
+		.of_match_table	= of_match_ptr(altera_jtaguart_match),
+	},
+};
+
+static int __init altera_jtaguart_init(void)
+{
+	int rc;
+
+	rc = uart_register_driver(&altera_jtaguart_driver);
+	if (rc)
+		return rc;
+	rc = platform_driver_register(&altera_jtaguart_platform_driver);
+	if (rc) {
+		uart_unregister_driver(&altera_jtaguart_driver);
+		return rc;
+	}
+	return 0;
+}
+
+static void __exit altera_jtaguart_exit(void)
+{
+	platform_driver_unregister(&altera_jtaguart_platform_driver);
+	uart_unregister_driver(&altera_jtaguart_driver);
+}
+
+module_init(altera_jtaguart_init);
+module_exit(altera_jtaguart_exit);
+
+MODULE_DESCRIPTION("Altera JTAG UART driver");
+MODULE_AUTHOR("Thomas Chou <thomas@wytron.com.tw>");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:" DRV_NAME);
diff --git a/ap/os/linux/linux-3.4.x/drivers/tty/serial/altera_uart.c b/ap/os/linux/linux-3.4.x/drivers/tty/serial/altera_uart.c
new file mode 100644
index 0000000..1f03309
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/tty/serial/altera_uart.c
@@ -0,0 +1,660 @@
+/*
+ * altera_uart.c -- Altera UART driver
+ *
+ * Based on mcf.c -- Freescale ColdFire UART driver
+ *
+ * (C) Copyright 2003-2007, Greg Ungerer <gerg@snapgear.com>
+ * (C) Copyright 2008, Thomas Chou <thomas@wytron.com.tw>
+ * (C) Copyright 2010, Tobias Klauser <tklauser@distanz.ch>
+ *
+ * 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.
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/timer.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/console.h>
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
+#include <linux/serial.h>
+#include <linux/serial_core.h>
+#include <linux/platform_device.h>
+#include <linux/of.h>
+#include <linux/io.h>
+#include <linux/altera_uart.h>
+
+#define DRV_NAME "altera_uart"
+#define SERIAL_ALTERA_MAJOR 204
+#define SERIAL_ALTERA_MINOR 213
+
+/*
+ * Altera UART register definitions according to the Nios UART datasheet:
+ * http://www.altera.com/literature/ds/ds_nios_uart.pdf
+ */
+
+#define ALTERA_UART_SIZE		32
+
+#define ALTERA_UART_RXDATA_REG		0
+#define ALTERA_UART_TXDATA_REG		4
+#define ALTERA_UART_STATUS_REG		8
+#define ALTERA_UART_CONTROL_REG		12
+#define ALTERA_UART_DIVISOR_REG		16
+#define ALTERA_UART_EOP_REG		20
+
+#define ALTERA_UART_STATUS_PE_MSK	0x0001	/* parity error */
+#define ALTERA_UART_STATUS_FE_MSK	0x0002	/* framing error */
+#define ALTERA_UART_STATUS_BRK_MSK	0x0004	/* break */
+#define ALTERA_UART_STATUS_ROE_MSK	0x0008	/* RX overrun error */
+#define ALTERA_UART_STATUS_TOE_MSK	0x0010	/* TX overrun error */
+#define ALTERA_UART_STATUS_TMT_MSK	0x0020	/* TX shift register state */
+#define ALTERA_UART_STATUS_TRDY_MSK	0x0040	/* TX ready */
+#define ALTERA_UART_STATUS_RRDY_MSK	0x0080	/* RX ready */
+#define ALTERA_UART_STATUS_E_MSK	0x0100	/* exception condition */
+#define ALTERA_UART_STATUS_DCTS_MSK	0x0400	/* CTS logic-level change */
+#define ALTERA_UART_STATUS_CTS_MSK	0x0800	/* CTS logic state */
+#define ALTERA_UART_STATUS_EOP_MSK	0x1000	/* EOP written/read */
+
+						/* Enable interrupt on... */
+#define ALTERA_UART_CONTROL_PE_MSK	0x0001	/* ...parity error */
+#define ALTERA_UART_CONTROL_FE_MSK	0x0002	/* ...framing error */
+#define ALTERA_UART_CONTROL_BRK_MSK	0x0004	/* ...break */
+#define ALTERA_UART_CONTROL_ROE_MSK	0x0008	/* ...RX overrun */
+#define ALTERA_UART_CONTROL_TOE_MSK	0x0010	/* ...TX overrun */
+#define ALTERA_UART_CONTROL_TMT_MSK	0x0020	/* ...TX shift register empty */
+#define ALTERA_UART_CONTROL_TRDY_MSK	0x0040	/* ...TX ready */
+#define ALTERA_UART_CONTROL_RRDY_MSK	0x0080	/* ...RX ready */
+#define ALTERA_UART_CONTROL_E_MSK	0x0100	/* ...exception*/
+
+#define ALTERA_UART_CONTROL_TRBK_MSK	0x0200	/* TX break */
+#define ALTERA_UART_CONTROL_DCTS_MSK	0x0400	/* Interrupt on CTS change */
+#define ALTERA_UART_CONTROL_RTS_MSK	0x0800	/* RTS signal */
+#define ALTERA_UART_CONTROL_EOP_MSK	0x1000	/* Interrupt on EOP */
+
+/*
+ * Local per-uart structure.
+ */
+struct altera_uart {
+	struct uart_port port;
+	struct timer_list tmr;
+	unsigned int sigs;	/* Local copy of line sigs */
+	unsigned short imr;	/* Local IMR mirror */
+};
+
+static u32 altera_uart_readl(struct uart_port *port, int reg)
+{
+	return readl(port->membase + (reg << port->regshift));
+}
+
+static void altera_uart_writel(struct uart_port *port, u32 dat, int reg)
+{
+	writel(dat, port->membase + (reg << port->regshift));
+}
+
+static unsigned int altera_uart_tx_empty(struct uart_port *port)
+{
+	return (altera_uart_readl(port, ALTERA_UART_STATUS_REG) &
+		ALTERA_UART_STATUS_TMT_MSK) ? TIOCSER_TEMT : 0;
+}
+
+static unsigned int altera_uart_get_mctrl(struct uart_port *port)
+{
+	struct altera_uart *pp = container_of(port, struct altera_uart, port);
+	unsigned int sigs;
+
+	sigs = (altera_uart_readl(port, ALTERA_UART_STATUS_REG) &
+	     ALTERA_UART_STATUS_CTS_MSK) ? TIOCM_CTS : 0;
+	sigs |= (pp->sigs & TIOCM_RTS);
+
+	return sigs;
+}
+
+static void altera_uart_set_mctrl(struct uart_port *port, unsigned int sigs)
+{
+	struct altera_uart *pp = container_of(port, struct altera_uart, port);
+
+	pp->sigs = sigs;
+	if (sigs & TIOCM_RTS)
+		pp->imr |= ALTERA_UART_CONTROL_RTS_MSK;
+	else
+		pp->imr &= ~ALTERA_UART_CONTROL_RTS_MSK;
+	altera_uart_writel(port, pp->imr, ALTERA_UART_CONTROL_REG);
+}
+
+static void altera_uart_start_tx(struct uart_port *port)
+{
+	struct altera_uart *pp = container_of(port, struct altera_uart, port);
+
+	pp->imr |= ALTERA_UART_CONTROL_TRDY_MSK;
+	altera_uart_writel(port, pp->imr, ALTERA_UART_CONTROL_REG);
+}
+
+static void altera_uart_stop_tx(struct uart_port *port)
+{
+	struct altera_uart *pp = container_of(port, struct altera_uart, port);
+
+	pp->imr &= ~ALTERA_UART_CONTROL_TRDY_MSK;
+	altera_uart_writel(port, pp->imr, ALTERA_UART_CONTROL_REG);
+}
+
+static void altera_uart_stop_rx(struct uart_port *port)
+{
+	struct altera_uart *pp = container_of(port, struct altera_uart, port);
+
+	pp->imr &= ~ALTERA_UART_CONTROL_RRDY_MSK;
+	altera_uart_writel(port, pp->imr, ALTERA_UART_CONTROL_REG);
+}
+
+static void altera_uart_break_ctl(struct uart_port *port, int break_state)
+{
+	struct altera_uart *pp = container_of(port, struct altera_uart, port);
+	unsigned long flags;
+
+	spin_lock_irqsave(&port->lock, flags);
+	if (break_state == -1)
+		pp->imr |= ALTERA_UART_CONTROL_TRBK_MSK;
+	else
+		pp->imr &= ~ALTERA_UART_CONTROL_TRBK_MSK;
+	altera_uart_writel(port, pp->imr, ALTERA_UART_CONTROL_REG);
+	spin_unlock_irqrestore(&port->lock, flags);
+}
+
+static void altera_uart_enable_ms(struct uart_port *port)
+{
+}
+
+static void altera_uart_set_termios(struct uart_port *port,
+				    struct ktermios *termios,
+				    struct ktermios *old)
+{
+	unsigned long flags;
+	unsigned int baud, baudclk;
+
+	baud = uart_get_baud_rate(port, termios, old, 0, 4000000);
+	baudclk = port->uartclk / baud;
+
+	if (old)
+		tty_termios_copy_hw(termios, old);
+	tty_termios_encode_baud_rate(termios, baud, baud);
+
+	spin_lock_irqsave(&port->lock, flags);
+	uart_update_timeout(port, termios->c_cflag, baud);
+	altera_uart_writel(port, baudclk, ALTERA_UART_DIVISOR_REG);
+	spin_unlock_irqrestore(&port->lock, flags);
+}
+
+static void altera_uart_rx_chars(struct altera_uart *pp)
+{
+	struct uart_port *port = &pp->port;
+	unsigned char ch, flag;
+	unsigned short status;
+
+	while ((status = altera_uart_readl(port, ALTERA_UART_STATUS_REG)) &
+	       ALTERA_UART_STATUS_RRDY_MSK) {
+		ch = altera_uart_readl(port, ALTERA_UART_RXDATA_REG);
+		flag = TTY_NORMAL;
+		port->icount.rx++;
+
+		if (status & ALTERA_UART_STATUS_E_MSK) {
+			altera_uart_writel(port, status,
+					   ALTERA_UART_STATUS_REG);
+
+			if (status & ALTERA_UART_STATUS_BRK_MSK) {
+				port->icount.brk++;
+				if (uart_handle_break(port))
+					continue;
+			} else if (status & ALTERA_UART_STATUS_PE_MSK) {
+				port->icount.parity++;
+			} else if (status & ALTERA_UART_STATUS_ROE_MSK) {
+				port->icount.overrun++;
+			} else if (status & ALTERA_UART_STATUS_FE_MSK) {
+				port->icount.frame++;
+			}
+
+			status &= port->read_status_mask;
+
+			if (status & ALTERA_UART_STATUS_BRK_MSK)
+				flag = TTY_BREAK;
+			else if (status & ALTERA_UART_STATUS_PE_MSK)
+				flag = TTY_PARITY;
+			else if (status & ALTERA_UART_STATUS_FE_MSK)
+				flag = TTY_FRAME;
+		}
+
+		if (uart_handle_sysrq_char(port, ch))
+			continue;
+		uart_insert_char(port, status, ALTERA_UART_STATUS_ROE_MSK, ch,
+				 flag);
+	}
+
+	tty_flip_buffer_push(port->state->port.tty);
+}
+
+static void altera_uart_tx_chars(struct altera_uart *pp)
+{
+	struct uart_port *port = &pp->port;
+	struct circ_buf *xmit = &port->state->xmit;
+
+	if (port->x_char) {
+		/* Send special char - probably flow control */
+		altera_uart_writel(port, port->x_char, ALTERA_UART_TXDATA_REG);
+		port->x_char = 0;
+		port->icount.tx++;
+		return;
+	}
+
+	while (altera_uart_readl(port, ALTERA_UART_STATUS_REG) &
+	       ALTERA_UART_STATUS_TRDY_MSK) {
+		if (xmit->head == xmit->tail)
+			break;
+		altera_uart_writel(port, xmit->buf[xmit->tail],
+		       ALTERA_UART_TXDATA_REG);
+		xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
+		port->icount.tx++;
+	}
+
+	if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
+		uart_write_wakeup(port);
+
+	if (xmit->head == xmit->tail) {
+		pp->imr &= ~ALTERA_UART_CONTROL_TRDY_MSK;
+		altera_uart_writel(port, pp->imr, ALTERA_UART_CONTROL_REG);
+	}
+}
+
+static irqreturn_t altera_uart_interrupt(int irq, void *data)
+{
+	struct uart_port *port = data;
+	struct altera_uart *pp = container_of(port, struct altera_uart, port);
+	unsigned int isr;
+
+	isr = altera_uart_readl(port, ALTERA_UART_STATUS_REG) & pp->imr;
+
+	spin_lock(&port->lock);
+	if (isr & ALTERA_UART_STATUS_RRDY_MSK)
+		altera_uart_rx_chars(pp);
+	if (isr & ALTERA_UART_STATUS_TRDY_MSK)
+		altera_uart_tx_chars(pp);
+	spin_unlock(&port->lock);
+
+	return IRQ_RETVAL(isr);
+}
+
+static void altera_uart_timer(unsigned long data)
+{
+	struct uart_port *port = (void *)data;
+	struct altera_uart *pp = container_of(port, struct altera_uart, port);
+
+	altera_uart_interrupt(0, port);
+	mod_timer(&pp->tmr, jiffies + uart_poll_timeout(port));
+}
+
+static void altera_uart_config_port(struct uart_port *port, int flags)
+{
+	port->type = PORT_ALTERA_UART;
+
+	/* Clear mask, so no surprise interrupts. */
+	altera_uart_writel(port, 0, ALTERA_UART_CONTROL_REG);
+	/* Clear status register */
+	altera_uart_writel(port, 0, ALTERA_UART_STATUS_REG);
+}
+
+static int altera_uart_startup(struct uart_port *port)
+{
+	struct altera_uart *pp = container_of(port, struct altera_uart, port);
+	unsigned long flags;
+	int ret;
+
+	if (!port->irq) {
+		setup_timer(&pp->tmr, altera_uart_timer, (unsigned long)port);
+		mod_timer(&pp->tmr, jiffies + uart_poll_timeout(port));
+		return 0;
+	}
+
+	ret = request_irq(port->irq, altera_uart_interrupt, 0,
+			DRV_NAME, port);
+	if (ret) {
+		pr_err(DRV_NAME ": unable to attach Altera UART %d "
+		       "interrupt vector=%d\n", port->line, port->irq);
+		return ret;
+	}
+
+	spin_lock_irqsave(&port->lock, flags);
+
+	/* Enable RX interrupts now */
+	pp->imr = ALTERA_UART_CONTROL_RRDY_MSK;
+	writel(pp->imr, port->membase + ALTERA_UART_CONTROL_REG);
+
+	spin_unlock_irqrestore(&port->lock, flags);
+
+	return 0;
+}
+
+static void altera_uart_shutdown(struct uart_port *port)
+{
+	struct altera_uart *pp = container_of(port, struct altera_uart, port);
+	unsigned long flags;
+
+	spin_lock_irqsave(&port->lock, flags);
+
+	/* Disable all interrupts now */
+	pp->imr = 0;
+	writel(pp->imr, port->membase + ALTERA_UART_CONTROL_REG);
+
+	spin_unlock_irqrestore(&port->lock, flags);
+
+	if (port->irq)
+		free_irq(port->irq, port);
+	else
+		del_timer_sync(&pp->tmr);
+}
+
+static const char *altera_uart_type(struct uart_port *port)
+{
+	return (port->type == PORT_ALTERA_UART) ? "Altera UART" : NULL;
+}
+
+static int altera_uart_request_port(struct uart_port *port)
+{
+	/* UARTs always present */
+	return 0;
+}
+
+static void altera_uart_release_port(struct uart_port *port)
+{
+	/* Nothing to release... */
+}
+
+static int altera_uart_verify_port(struct uart_port *port,
+				   struct serial_struct *ser)
+{
+	if ((ser->type != PORT_UNKNOWN) && (ser->type != PORT_ALTERA_UART))
+		return -EINVAL;
+	return 0;
+}
+
+#ifdef CONFIG_CONSOLE_POLL
+static int altera_uart_poll_get_char(struct uart_port *port)
+{
+	while (!(altera_uart_readl(port, ALTERA_UART_STATUS_REG) &
+		 ALTERA_UART_STATUS_RRDY_MSK))
+		cpu_relax();
+
+	return altera_uart_readl(port, ALTERA_UART_RXDATA_REG);
+}
+
+static void altera_uart_poll_put_char(struct uart_port *port, unsigned char c)
+{
+	while (!(altera_uart_readl(port, ALTERA_UART_STATUS_REG) &
+		 ALTERA_UART_STATUS_TRDY_MSK))
+		cpu_relax();
+
+	altera_uart_writel(port, c, ALTERA_UART_TXDATA_REG);
+}
+#endif
+
+/*
+ *	Define the basic serial functions we support.
+ */
+static struct uart_ops altera_uart_ops = {
+	.tx_empty	= altera_uart_tx_empty,
+	.get_mctrl	= altera_uart_get_mctrl,
+	.set_mctrl	= altera_uart_set_mctrl,
+	.start_tx	= altera_uart_start_tx,
+	.stop_tx	= altera_uart_stop_tx,
+	.stop_rx	= altera_uart_stop_rx,
+	.enable_ms	= altera_uart_enable_ms,
+	.break_ctl	= altera_uart_break_ctl,
+	.startup	= altera_uart_startup,
+	.shutdown	= altera_uart_shutdown,
+	.set_termios	= altera_uart_set_termios,
+	.type		= altera_uart_type,
+	.request_port	= altera_uart_request_port,
+	.release_port	= altera_uart_release_port,
+	.config_port	= altera_uart_config_port,
+	.verify_port	= altera_uart_verify_port,
+#ifdef CONFIG_CONSOLE_POLL
+	.poll_get_char	= altera_uart_poll_get_char,
+	.poll_put_char	= altera_uart_poll_put_char,
+#endif
+};
+
+static struct altera_uart altera_uart_ports[CONFIG_SERIAL_ALTERA_UART_MAXPORTS];
+
+#if defined(CONFIG_SERIAL_ALTERA_UART_CONSOLE)
+
+static void altera_uart_console_putc(struct uart_port *port, const char c)
+{
+	while (!(altera_uart_readl(port, ALTERA_UART_STATUS_REG) &
+		 ALTERA_UART_STATUS_TRDY_MSK))
+		cpu_relax();
+
+	writel(c, port->membase + ALTERA_UART_TXDATA_REG);
+}
+
+static void altera_uart_console_write(struct console *co, const char *s,
+				      unsigned int count)
+{
+	struct uart_port *port = &(altera_uart_ports + co->index)->port;
+
+	for (; count; count--, s++) {
+		altera_uart_console_putc(port, *s);
+		if (*s == '\n')
+			altera_uart_console_putc(port, '\r');
+	}
+}
+
+static int __init altera_uart_console_setup(struct console *co, char *options)
+{
+	struct uart_port *port;
+	int baud = CONFIG_SERIAL_ALTERA_UART_BAUDRATE;
+	int bits = 8;
+	int parity = 'n';
+	int flow = 'n';
+
+	if (co->index < 0 || co->index >= CONFIG_SERIAL_ALTERA_UART_MAXPORTS)
+		return -EINVAL;
+	port = &altera_uart_ports[co->index].port;
+	if (!port->membase)
+		return -ENODEV;
+
+	if (options)
+		uart_parse_options(options, &baud, &parity, &bits, &flow);
+
+	return uart_set_options(port, co, baud, parity, bits, flow);
+}
+
+static struct uart_driver altera_uart_driver;
+
+static struct console altera_uart_console = {
+	.name	= "ttyAL",
+	.write	= altera_uart_console_write,
+	.device	= uart_console_device,
+	.setup	= altera_uart_console_setup,
+	.flags	= CON_PRINTBUFFER,
+	.index	= -1,
+	.data	= &altera_uart_driver,
+};
+
+static int __init altera_uart_console_init(void)
+{
+	register_console(&altera_uart_console);
+	return 0;
+}
+
+console_initcall(altera_uart_console_init);
+
+#define	ALTERA_UART_CONSOLE	(&altera_uart_console)
+
+#else
+
+#define	ALTERA_UART_CONSOLE	NULL
+
+#endif /* CONFIG_ALTERA_UART_CONSOLE */
+
+/*
+ *	Define the altera_uart UART driver structure.
+ */
+static struct uart_driver altera_uart_driver = {
+	.owner		= THIS_MODULE,
+	.driver_name	= DRV_NAME,
+	.dev_name	= "ttyAL",
+	.major		= SERIAL_ALTERA_MAJOR,
+	.minor		= SERIAL_ALTERA_MINOR,
+	.nr		= CONFIG_SERIAL_ALTERA_UART_MAXPORTS,
+	.cons		= ALTERA_UART_CONSOLE,
+};
+
+#ifdef CONFIG_OF
+static int altera_uart_get_of_uartclk(struct platform_device *pdev,
+				      struct uart_port *port)
+{
+	int len;
+	const __be32 *clk;
+
+	clk = of_get_property(pdev->dev.of_node, "clock-frequency", &len);
+	if (!clk || len < sizeof(__be32))
+		return -ENODEV;
+
+	port->uartclk = be32_to_cpup(clk);
+
+	return 0;
+}
+#else
+static int altera_uart_get_of_uartclk(struct platform_device *pdev,
+				      struct uart_port *port)
+{
+	return -ENODEV;
+}
+#endif /* CONFIG_OF */
+
+static int __devinit altera_uart_probe(struct platform_device *pdev)
+{
+	struct altera_uart_platform_uart *platp = pdev->dev.platform_data;
+	struct uart_port *port;
+	struct resource *res_mem;
+	struct resource *res_irq;
+	int i = pdev->id;
+	int ret;
+
+	/* if id is -1 scan for a free id and use that one */
+	if (i == -1) {
+		for (i = 0; i < CONFIG_SERIAL_ALTERA_UART_MAXPORTS; i++)
+			if (altera_uart_ports[i].port.mapbase == 0)
+				break;
+	}
+
+	if (i < 0 || i >= CONFIG_SERIAL_ALTERA_UART_MAXPORTS)
+		return -EINVAL;
+
+	port = &altera_uart_ports[i].port;
+
+	res_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (res_mem)
+		port->mapbase = res_mem->start;
+	else if (platp)
+		port->mapbase = platp->mapbase;
+	else
+		return -EINVAL;
+
+	res_irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+	if (res_irq)
+		port->irq = res_irq->start;
+	else if (platp)
+		port->irq = platp->irq;
+
+	/* Check platform data first so we can override device node data */
+	if (platp)
+		port->uartclk = platp->uartclk;
+	else {
+		ret = altera_uart_get_of_uartclk(pdev, port);
+		if (ret)
+			return ret;
+	}
+
+	port->membase = ioremap(port->mapbase, ALTERA_UART_SIZE);
+	if (!port->membase)
+		return -ENOMEM;
+
+	if (platp)
+		port->regshift = platp->bus_shift;
+	else
+		port->regshift = 0;
+
+	port->line = i;
+	port->type = PORT_ALTERA_UART;
+	port->iotype = SERIAL_IO_MEM;
+	port->ops = &altera_uart_ops;
+	port->flags = UPF_BOOT_AUTOCONF;
+
+	dev_set_drvdata(&pdev->dev, port);
+
+	uart_add_one_port(&altera_uart_driver, port);
+
+	return 0;
+}
+
+static int __devexit altera_uart_remove(struct platform_device *pdev)
+{
+	struct uart_port *port = dev_get_drvdata(&pdev->dev);
+
+	if (port) {
+		uart_remove_one_port(&altera_uart_driver, port);
+		dev_set_drvdata(&pdev->dev, NULL);
+		port->mapbase = 0;
+	}
+
+	return 0;
+}
+
+#ifdef CONFIG_OF
+static struct of_device_id altera_uart_match[] = {
+	{ .compatible = "ALTR,uart-1.0", },
+	{},
+};
+MODULE_DEVICE_TABLE(of, altera_uart_match);
+#endif /* CONFIG_OF */
+
+static struct platform_driver altera_uart_platform_driver = {
+	.probe	= altera_uart_probe,
+	.remove	= __devexit_p(altera_uart_remove),
+	.driver	= {
+		.name		= DRV_NAME,
+		.owner		= THIS_MODULE,
+		.of_match_table	= of_match_ptr(altera_uart_match),
+	},
+};
+
+static int __init altera_uart_init(void)
+{
+	int rc;
+
+	rc = uart_register_driver(&altera_uart_driver);
+	if (rc)
+		return rc;
+	rc = platform_driver_register(&altera_uart_platform_driver);
+	if (rc) {
+		uart_unregister_driver(&altera_uart_driver);
+		return rc;
+	}
+	return 0;
+}
+
+static void __exit altera_uart_exit(void)
+{
+	platform_driver_unregister(&altera_uart_platform_driver);
+	uart_unregister_driver(&altera_uart_driver);
+}
+
+module_init(altera_uart_init);
+module_exit(altera_uart_exit);
+
+MODULE_DESCRIPTION("Altera UART driver");
+MODULE_AUTHOR("Thomas Chou <thomas@wytron.com.tw>");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:" DRV_NAME);
+MODULE_ALIAS_CHARDEV_MAJOR(SERIAL_ALTERA_MAJOR);
diff --git a/ap/os/linux/linux-3.4.x/drivers/tty/serial/amba-pl010.c b/ap/os/linux/linux-3.4.x/drivers/tty/serial/amba-pl010.c
new file mode 100644
index 0000000..0d91a54
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/tty/serial/amba-pl010.c
@@ -0,0 +1,837 @@
+/*
+ *  Driver for AMBA serial ports
+ *
+ *  Based on drivers/char/serial.c, by Linus Torvalds, Theodore Ts'o.
+ *
+ *  Copyright 1999 ARM Limited
+ *  Copyright (C) 2000 Deep Blue Solutions Ltd.
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ * This is a generic driver for ARM AMBA-type serial ports.  They
+ * have a lot of 16550-like features, but are not register compatible.
+ * Note that although they do have CTS, DCD and DSR inputs, they do
+ * not have an RI input, nor do they have DTR or RTS outputs.  If
+ * required, these have to be supplied via some other means (eg, GPIO)
+ * and hooked into this driver.
+ */
+
+#if defined(CONFIG_SERIAL_AMBA_PL010_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ)
+#define SUPPORT_SYSRQ
+#endif
+
+#include <linux/module.h>
+#include <linux/ioport.h>
+#include <linux/init.h>
+#include <linux/console.h>
+#include <linux/sysrq.h>
+#include <linux/device.h>
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
+#include <linux/serial_core.h>
+#include <linux/serial.h>
+#include <linux/amba/bus.h>
+#include <linux/amba/serial.h>
+#include <linux/clk.h>
+#include <linux/slab.h>
+
+#include <asm/io.h>
+
+#define UART_NR		8
+
+#define SERIAL_AMBA_MAJOR	204
+#define SERIAL_AMBA_MINOR	16
+#define SERIAL_AMBA_NR		UART_NR
+
+#define AMBA_ISR_PASS_LIMIT	256
+
+#define UART_RX_DATA(s)		(((s) & UART01x_FR_RXFE) == 0)
+#define UART_TX_READY(s)	(((s) & UART01x_FR_TXFF) == 0)
+
+#define UART_DUMMY_RSR_RX	256
+#define UART_PORT_SIZE		64
+
+/*
+ * We wrap our port structure around the generic uart_port.
+ */
+struct uart_amba_port {
+	struct uart_port	port;
+	struct clk		*clk;
+	struct amba_device	*dev;
+	struct amba_pl010_data	*data;
+	unsigned int		old_status;
+};
+
+static void pl010_stop_tx(struct uart_port *port)
+{
+	struct uart_amba_port *uap = (struct uart_amba_port *)port;
+	unsigned int cr;
+
+	cr = readb(uap->port.membase + UART010_CR);
+	cr &= ~UART010_CR_TIE;
+	writel(cr, uap->port.membase + UART010_CR);
+}
+
+static void pl010_start_tx(struct uart_port *port)
+{
+	struct uart_amba_port *uap = (struct uart_amba_port *)port;
+	unsigned int cr;
+
+	cr = readb(uap->port.membase + UART010_CR);
+	cr |= UART010_CR_TIE;
+	writel(cr, uap->port.membase + UART010_CR);
+}
+
+static void pl010_stop_rx(struct uart_port *port)
+{
+	struct uart_amba_port *uap = (struct uart_amba_port *)port;
+	unsigned int cr;
+
+	cr = readb(uap->port.membase + UART010_CR);
+	cr &= ~(UART010_CR_RIE | UART010_CR_RTIE);
+	writel(cr, uap->port.membase + UART010_CR);
+}
+
+static void pl010_enable_ms(struct uart_port *port)
+{
+	struct uart_amba_port *uap = (struct uart_amba_port *)port;
+	unsigned int cr;
+
+	cr = readb(uap->port.membase + UART010_CR);
+	cr |= UART010_CR_MSIE;
+	writel(cr, uap->port.membase + UART010_CR);
+}
+
+static void pl010_rx_chars(struct uart_amba_port *uap)
+{
+	struct tty_struct *tty = uap->port.state->port.tty;
+	unsigned int status, ch, flag, rsr, max_count = 256;
+
+	status = readb(uap->port.membase + UART01x_FR);
+	while (UART_RX_DATA(status) && max_count--) {
+		ch = readb(uap->port.membase + UART01x_DR);
+		flag = TTY_NORMAL;
+
+		uap->port.icount.rx++;
+
+		/*
+		 * Note that the error handling code is
+		 * out of the main execution path
+		 */
+		rsr = readb(uap->port.membase + UART01x_RSR) | UART_DUMMY_RSR_RX;
+		if (unlikely(rsr & UART01x_RSR_ANY)) {
+			writel(0, uap->port.membase + UART01x_ECR);
+
+			if (rsr & UART01x_RSR_BE) {
+				rsr &= ~(UART01x_RSR_FE | UART01x_RSR_PE);
+				uap->port.icount.brk++;
+				if (uart_handle_break(&uap->port))
+					goto ignore_char;
+			} else if (rsr & UART01x_RSR_PE)
+				uap->port.icount.parity++;
+			else if (rsr & UART01x_RSR_FE)
+				uap->port.icount.frame++;
+			if (rsr & UART01x_RSR_OE)
+				uap->port.icount.overrun++;
+
+			rsr &= uap->port.read_status_mask;
+
+			if (rsr & UART01x_RSR_BE)
+				flag = TTY_BREAK;
+			else if (rsr & UART01x_RSR_PE)
+				flag = TTY_PARITY;
+			else if (rsr & UART01x_RSR_FE)
+				flag = TTY_FRAME;
+		}
+
+		if (uart_handle_sysrq_char(&uap->port, ch))
+			goto ignore_char;
+
+		uart_insert_char(&uap->port, rsr, UART01x_RSR_OE, ch, flag);
+
+	ignore_char:
+		status = readb(uap->port.membase + UART01x_FR);
+	}
+	spin_unlock(&uap->port.lock);
+	tty_flip_buffer_push(tty);
+	spin_lock(&uap->port.lock);
+}
+
+static void pl010_tx_chars(struct uart_amba_port *uap)
+{
+	struct circ_buf *xmit = &uap->port.state->xmit;
+	int count;
+
+	if (uap->port.x_char) {
+		writel(uap->port.x_char, uap->port.membase + UART01x_DR);
+		uap->port.icount.tx++;
+		uap->port.x_char = 0;
+		return;
+	}
+	if (uart_circ_empty(xmit) || uart_tx_stopped(&uap->port)) {
+		pl010_stop_tx(&uap->port);
+		return;
+	}
+
+	count = uap->port.fifosize >> 1;
+	do {
+		writel(xmit->buf[xmit->tail], uap->port.membase + UART01x_DR);
+		xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
+		uap->port.icount.tx++;
+		if (uart_circ_empty(xmit))
+			break;
+	} while (--count > 0);
+
+	if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
+		uart_write_wakeup(&uap->port);
+
+	if (uart_circ_empty(xmit))
+		pl010_stop_tx(&uap->port);
+}
+
+static void pl010_modem_status(struct uart_amba_port *uap)
+{
+	unsigned int status, delta;
+
+	writel(0, uap->port.membase + UART010_ICR);
+
+	status = readb(uap->port.membase + UART01x_FR) & UART01x_FR_MODEM_ANY;
+
+	delta = status ^ uap->old_status;
+	uap->old_status = status;
+
+	if (!delta)
+		return;
+
+	if (delta & UART01x_FR_DCD)
+		uart_handle_dcd_change(&uap->port, status & UART01x_FR_DCD);
+
+	if (delta & UART01x_FR_DSR)
+		uap->port.icount.dsr++;
+
+	if (delta & UART01x_FR_CTS)
+		uart_handle_cts_change(&uap->port, status & UART01x_FR_CTS);
+
+	wake_up_interruptible(&uap->port.state->port.delta_msr_wait);
+}
+
+static irqreturn_t pl010_int(int irq, void *dev_id)
+{
+	struct uart_amba_port *uap = dev_id;
+	unsigned int status, pass_counter = AMBA_ISR_PASS_LIMIT;
+	int handled = 0;
+
+	spin_lock(&uap->port.lock);
+
+	status = readb(uap->port.membase + UART010_IIR);
+	if (status) {
+		do {
+			if (status & (UART010_IIR_RTIS | UART010_IIR_RIS))
+				pl010_rx_chars(uap);
+			if (status & UART010_IIR_MIS)
+				pl010_modem_status(uap);
+			if (status & UART010_IIR_TIS)
+				pl010_tx_chars(uap);
+
+			if (pass_counter-- == 0)
+				break;
+
+			status = readb(uap->port.membase + UART010_IIR);
+		} while (status & (UART010_IIR_RTIS | UART010_IIR_RIS |
+				   UART010_IIR_TIS));
+		handled = 1;
+	}
+
+	spin_unlock(&uap->port.lock);
+
+	return IRQ_RETVAL(handled);
+}
+
+static unsigned int pl010_tx_empty(struct uart_port *port)
+{
+	struct uart_amba_port *uap = (struct uart_amba_port *)port;
+	unsigned int status = readb(uap->port.membase + UART01x_FR);
+	return status & UART01x_FR_BUSY ? 0 : TIOCSER_TEMT;
+}
+
+static unsigned int pl010_get_mctrl(struct uart_port *port)
+{
+	struct uart_amba_port *uap = (struct uart_amba_port *)port;
+	unsigned int result = 0;
+	unsigned int status;
+
+	status = readb(uap->port.membase + UART01x_FR);
+	if (status & UART01x_FR_DCD)
+		result |= TIOCM_CAR;
+	if (status & UART01x_FR_DSR)
+		result |= TIOCM_DSR;
+	if (status & UART01x_FR_CTS)
+		result |= TIOCM_CTS;
+
+	return result;
+}
+
+static void pl010_set_mctrl(struct uart_port *port, unsigned int mctrl)
+{
+	struct uart_amba_port *uap = (struct uart_amba_port *)port;
+
+	if (uap->data)
+		uap->data->set_mctrl(uap->dev, uap->port.membase, mctrl);
+}
+
+static void pl010_break_ctl(struct uart_port *port, int break_state)
+{
+	struct uart_amba_port *uap = (struct uart_amba_port *)port;
+	unsigned long flags;
+	unsigned int lcr_h;
+
+	spin_lock_irqsave(&uap->port.lock, flags);
+	lcr_h = readb(uap->port.membase + UART010_LCRH);
+	if (break_state == -1)
+		lcr_h |= UART01x_LCRH_BRK;
+	else
+		lcr_h &= ~UART01x_LCRH_BRK;
+	writel(lcr_h, uap->port.membase + UART010_LCRH);
+	spin_unlock_irqrestore(&uap->port.lock, flags);
+}
+
+static int pl010_startup(struct uart_port *port)
+{
+	struct uart_amba_port *uap = (struct uart_amba_port *)port;
+	int retval;
+
+	retval = clk_prepare(uap->clk);
+	if (retval)
+		goto out;
+
+	/*
+	 * Try to enable the clock producer.
+	 */
+	retval = clk_enable(uap->clk);
+	if (retval)
+		goto clk_unprep;
+
+	uap->port.uartclk = clk_get_rate(uap->clk);
+
+	/*
+	 * Allocate the IRQ
+	 */
+	retval = request_irq(uap->port.irq, pl010_int, 0, "uart-pl010", uap);
+	if (retval)
+		goto clk_dis;
+
+	/*
+	 * initialise the old status of the modem signals
+	 */
+	uap->old_status = readb(uap->port.membase + UART01x_FR) & UART01x_FR_MODEM_ANY;
+
+	/*
+	 * Finally, enable interrupts
+	 */
+	writel(UART01x_CR_UARTEN | UART010_CR_RIE | UART010_CR_RTIE,
+	       uap->port.membase + UART010_CR);
+
+	return 0;
+
+ clk_dis:
+	clk_disable(uap->clk);
+ clk_unprep:
+	clk_unprepare(uap->clk);
+ out:
+	return retval;
+}
+
+static void pl010_shutdown(struct uart_port *port)
+{
+	struct uart_amba_port *uap = (struct uart_amba_port *)port;
+
+	/*
+	 * Free the interrupt
+	 */
+	free_irq(uap->port.irq, uap);
+
+	/*
+	 * disable all interrupts, disable the port
+	 */
+	writel(0, uap->port.membase + UART010_CR);
+
+	/* disable break condition and fifos */
+	writel(readb(uap->port.membase + UART010_LCRH) &
+		~(UART01x_LCRH_BRK | UART01x_LCRH_FEN),
+	       uap->port.membase + UART010_LCRH);
+
+	/*
+	 * Shut down the clock producer
+	 */
+	clk_disable(uap->clk);
+	clk_unprepare(uap->clk);
+}
+
+static void
+pl010_set_termios(struct uart_port *port, struct ktermios *termios,
+		     struct ktermios *old)
+{
+	struct uart_amba_port *uap = (struct uart_amba_port *)port;
+	unsigned int lcr_h, old_cr;
+	unsigned long flags;
+	unsigned int baud, quot;
+
+	/*
+	 * Ask the core to calculate the divisor for us.
+	 */
+	baud = uart_get_baud_rate(port, termios, old, 0, uap->port.uartclk/16); 
+	quot = uart_get_divisor(port, baud);
+
+	switch (termios->c_cflag & CSIZE) {
+	case CS5:
+		lcr_h = UART01x_LCRH_WLEN_5;
+		break;
+	case CS6:
+		lcr_h = UART01x_LCRH_WLEN_6;
+		break;
+	case CS7:
+		lcr_h = UART01x_LCRH_WLEN_7;
+		break;
+	default: // CS8
+		lcr_h = UART01x_LCRH_WLEN_8;
+		break;
+	}
+	if (termios->c_cflag & CSTOPB)
+		lcr_h |= UART01x_LCRH_STP2;
+	if (termios->c_cflag & PARENB) {
+		lcr_h |= UART01x_LCRH_PEN;
+		if (!(termios->c_cflag & PARODD))
+			lcr_h |= UART01x_LCRH_EPS;
+	}
+	if (uap->port.fifosize > 1)
+		lcr_h |= UART01x_LCRH_FEN;
+
+	spin_lock_irqsave(&uap->port.lock, flags);
+
+	/*
+	 * Update the per-port timeout.
+	 */
+	uart_update_timeout(port, termios->c_cflag, baud);
+
+	uap->port.read_status_mask = UART01x_RSR_OE;
+	if (termios->c_iflag & INPCK)
+		uap->port.read_status_mask |= UART01x_RSR_FE | UART01x_RSR_PE;
+	if (termios->c_iflag & (BRKINT | PARMRK))
+		uap->port.read_status_mask |= UART01x_RSR_BE;
+
+	/*
+	 * Characters to ignore
+	 */
+	uap->port.ignore_status_mask = 0;
+	if (termios->c_iflag & IGNPAR)
+		uap->port.ignore_status_mask |= UART01x_RSR_FE | UART01x_RSR_PE;
+	if (termios->c_iflag & IGNBRK) {
+		uap->port.ignore_status_mask |= UART01x_RSR_BE;
+		/*
+		 * If we're ignoring parity and break indicators,
+		 * ignore overruns too (for real raw support).
+		 */
+		if (termios->c_iflag & IGNPAR)
+			uap->port.ignore_status_mask |= UART01x_RSR_OE;
+	}
+
+	/*
+	 * Ignore all characters if CREAD is not set.
+	 */
+	if ((termios->c_cflag & CREAD) == 0)
+		uap->port.ignore_status_mask |= UART_DUMMY_RSR_RX;
+
+	/* first, disable everything */
+	old_cr = readb(uap->port.membase + UART010_CR) & ~UART010_CR_MSIE;
+
+	if (UART_ENABLE_MS(port, termios->c_cflag))
+		old_cr |= UART010_CR_MSIE;
+
+	writel(0, uap->port.membase + UART010_CR);
+
+	/* Set baud rate */
+	quot -= 1;
+	writel((quot & 0xf00) >> 8, uap->port.membase + UART010_LCRM);
+	writel(quot & 0xff, uap->port.membase + UART010_LCRL);
+
+	/*
+	 * ----------v----------v----------v----------v-----
+	 * NOTE: MUST BE WRITTEN AFTER UARTLCR_M & UARTLCR_L
+	 * ----------^----------^----------^----------^-----
+	 */
+	writel(lcr_h, uap->port.membase + UART010_LCRH);
+	writel(old_cr, uap->port.membase + UART010_CR);
+
+	spin_unlock_irqrestore(&uap->port.lock, flags);
+}
+
+static void pl010_set_ldisc(struct uart_port *port, int new)
+{
+	if (new == N_PPS) {
+		port->flags |= UPF_HARDPPS_CD;
+		pl010_enable_ms(port);
+	} else
+		port->flags &= ~UPF_HARDPPS_CD;
+}
+
+static const char *pl010_type(struct uart_port *port)
+{
+	return port->type == PORT_AMBA ? "AMBA" : NULL;
+}
+
+/*
+ * Release the memory region(s) being used by 'port'
+ */
+static void pl010_release_port(struct uart_port *port)
+{
+	release_mem_region(port->mapbase, UART_PORT_SIZE);
+}
+
+/*
+ * Request the memory region(s) being used by 'port'
+ */
+static int pl010_request_port(struct uart_port *port)
+{
+	return request_mem_region(port->mapbase, UART_PORT_SIZE, "uart-pl010")
+			!= NULL ? 0 : -EBUSY;
+}
+
+/*
+ * Configure/autoconfigure the port.
+ */
+static void pl010_config_port(struct uart_port *port, int flags)
+{
+	if (flags & UART_CONFIG_TYPE) {
+		port->type = PORT_AMBA;
+		pl010_request_port(port);
+	}
+}
+
+/*
+ * verify the new serial_struct (for TIOCSSERIAL).
+ */
+static int pl010_verify_port(struct uart_port *port, struct serial_struct *ser)
+{
+	int ret = 0;
+	if (ser->type != PORT_UNKNOWN && ser->type != PORT_AMBA)
+		ret = -EINVAL;
+	if (ser->irq < 0 || ser->irq >= nr_irqs)
+		ret = -EINVAL;
+	if (ser->baud_base < 9600)
+		ret = -EINVAL;
+	return ret;
+}
+
+static struct uart_ops amba_pl010_pops = {
+	.tx_empty	= pl010_tx_empty,
+	.set_mctrl	= pl010_set_mctrl,
+	.get_mctrl	= pl010_get_mctrl,
+	.stop_tx	= pl010_stop_tx,
+	.start_tx	= pl010_start_tx,
+	.stop_rx	= pl010_stop_rx,
+	.enable_ms	= pl010_enable_ms,
+	.break_ctl	= pl010_break_ctl,
+	.startup	= pl010_startup,
+	.shutdown	= pl010_shutdown,
+	.set_termios	= pl010_set_termios,
+	.set_ldisc	= pl010_set_ldisc,
+	.type		= pl010_type,
+	.release_port	= pl010_release_port,
+	.request_port	= pl010_request_port,
+	.config_port	= pl010_config_port,
+	.verify_port	= pl010_verify_port,
+};
+
+static struct uart_amba_port *amba_ports[UART_NR];
+
+#ifdef CONFIG_SERIAL_AMBA_PL010_CONSOLE
+
+static void pl010_console_putchar(struct uart_port *port, int ch)
+{
+	struct uart_amba_port *uap = (struct uart_amba_port *)port;
+	unsigned int status;
+
+	do {
+		status = readb(uap->port.membase + UART01x_FR);
+		barrier();
+	} while (!UART_TX_READY(status));
+	writel(ch, uap->port.membase + UART01x_DR);
+}
+
+static void
+pl010_console_write(struct console *co, const char *s, unsigned int count)
+{
+	struct uart_amba_port *uap = amba_ports[co->index];
+	unsigned int status, old_cr;
+
+	clk_enable(uap->clk);
+
+	/*
+	 *	First save the CR then disable the interrupts
+	 */
+	old_cr = readb(uap->port.membase + UART010_CR);
+	writel(UART01x_CR_UARTEN, uap->port.membase + UART010_CR);
+
+	uart_console_write(&uap->port, s, count, pl010_console_putchar);
+
+	/*
+	 *	Finally, wait for transmitter to become empty
+	 *	and restore the TCR
+	 */
+	do {
+		status = readb(uap->port.membase + UART01x_FR);
+		barrier();
+	} while (status & UART01x_FR_BUSY);
+	writel(old_cr, uap->port.membase + UART010_CR);
+
+	clk_disable(uap->clk);
+}
+
+static void __init
+pl010_console_get_options(struct uart_amba_port *uap, int *baud,
+			     int *parity, int *bits)
+{
+	if (readb(uap->port.membase + UART010_CR) & UART01x_CR_UARTEN) {
+		unsigned int lcr_h, quot;
+		lcr_h = readb(uap->port.membase + UART010_LCRH);
+
+		*parity = 'n';
+		if (lcr_h & UART01x_LCRH_PEN) {
+			if (lcr_h & UART01x_LCRH_EPS)
+				*parity = 'e';
+			else
+				*parity = 'o';
+		}
+
+		if ((lcr_h & 0x60) == UART01x_LCRH_WLEN_7)
+			*bits = 7;
+		else
+			*bits = 8;
+
+		quot = readb(uap->port.membase + UART010_LCRL) |
+		       readb(uap->port.membase + UART010_LCRM) << 8;
+		*baud = uap->port.uartclk / (16 * (quot + 1));
+	}
+}
+
+static int __init pl010_console_setup(struct console *co, char *options)
+{
+	struct uart_amba_port *uap;
+	int baud = 38400;
+	int bits = 8;
+	int parity = 'n';
+	int flow = 'n';
+	int ret;
+
+	/*
+	 * 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 >= UART_NR)
+		co->index = 0;
+	uap = amba_ports[co->index];
+	if (!uap)
+		return -ENODEV;
+
+	ret = clk_prepare(uap->clk);
+	if (ret)
+		return ret;
+
+	uap->port.uartclk = clk_get_rate(uap->clk);
+
+	if (options)
+		uart_parse_options(options, &baud, &parity, &bits, &flow);
+	else
+		pl010_console_get_options(uap, &baud, &parity, &bits);
+
+	return uart_set_options(&uap->port, co, baud, parity, bits, flow);
+}
+
+static struct uart_driver amba_reg;
+static struct console amba_console = {
+	.name		= "ttyAM",
+	.write		= pl010_console_write,
+	.device		= uart_console_device,
+	.setup		= pl010_console_setup,
+	.flags		= CON_PRINTBUFFER,
+	.index		= -1,
+	.data		= &amba_reg,
+};
+
+#define AMBA_CONSOLE	&amba_console
+#else
+#define AMBA_CONSOLE	NULL
+#endif
+
+static struct uart_driver amba_reg = {
+	.owner			= THIS_MODULE,
+	.driver_name		= "ttyAM",
+	.dev_name		= "ttyAM",
+	.major			= SERIAL_AMBA_MAJOR,
+	.minor			= SERIAL_AMBA_MINOR,
+	.nr			= UART_NR,
+	.cons			= AMBA_CONSOLE,
+};
+
+static int pl010_probe(struct amba_device *dev, const struct amba_id *id)
+{
+	struct uart_amba_port *uap;
+	void __iomem *base;
+	int i, ret;
+
+	for (i = 0; i < ARRAY_SIZE(amba_ports); i++)
+		if (amba_ports[i] == NULL)
+			break;
+
+	if (i == ARRAY_SIZE(amba_ports)) {
+		ret = -EBUSY;
+		goto out;
+	}
+
+	uap = kzalloc(sizeof(struct uart_amba_port), GFP_KERNEL);
+	if (!uap) {
+		ret = -ENOMEM;
+		goto out;
+	}
+
+	base = ioremap(dev->res.start, resource_size(&dev->res));
+	if (!base) {
+		ret = -ENOMEM;
+		goto free;
+	}
+
+	uap->clk = clk_get(&dev->dev, NULL);
+	if (IS_ERR(uap->clk)) {
+		ret = PTR_ERR(uap->clk);
+		goto unmap;
+	}
+
+	uap->port.dev = &dev->dev;
+	uap->port.mapbase = dev->res.start;
+	uap->port.membase = base;
+	uap->port.iotype = UPIO_MEM;
+	uap->port.irq = dev->irq[0];
+	uap->port.fifosize = 16;
+	uap->port.ops = &amba_pl010_pops;
+	uap->port.flags = UPF_BOOT_AUTOCONF;
+	uap->port.line = i;
+	uap->dev = dev;
+	uap->data = dev->dev.platform_data;
+
+	amba_ports[i] = uap;
+
+	amba_set_drvdata(dev, uap);
+	ret = uart_add_one_port(&amba_reg, &uap->port);
+	if (ret) {
+		amba_set_drvdata(dev, NULL);
+		amba_ports[i] = NULL;
+		clk_put(uap->clk);
+ unmap:
+		iounmap(base);
+ free:
+		kfree(uap);
+	}
+ out:
+	return ret;
+}
+
+static int pl010_remove(struct amba_device *dev)
+{
+	struct uart_amba_port *uap = amba_get_drvdata(dev);
+	int i;
+
+	amba_set_drvdata(dev, NULL);
+
+	uart_remove_one_port(&amba_reg, &uap->port);
+
+	for (i = 0; i < ARRAY_SIZE(amba_ports); i++)
+		if (amba_ports[i] == uap)
+			amba_ports[i] = NULL;
+
+	iounmap(uap->port.membase);
+	clk_put(uap->clk);
+	kfree(uap);
+	return 0;
+}
+
+static int pl010_suspend(struct amba_device *dev, pm_message_t state)
+{
+	struct uart_amba_port *uap = amba_get_drvdata(dev);
+
+	if (uap)
+		uart_suspend_port(&amba_reg, &uap->port);
+
+	return 0;
+}
+
+static int pl010_resume(struct amba_device *dev)
+{
+	struct uart_amba_port *uap = amba_get_drvdata(dev);
+
+	if (uap)
+		uart_resume_port(&amba_reg, &uap->port);
+
+	return 0;
+}
+
+static struct amba_id pl010_ids[] = {
+	{
+		.id	= 0x00041010,
+		.mask	= 0x000fffff,
+	},
+	{ 0, 0 },
+};
+
+MODULE_DEVICE_TABLE(amba, pl010_ids);
+
+static struct amba_driver pl010_driver = {
+	.drv = {
+		.name	= "uart-pl010",
+	},
+	.id_table	= pl010_ids,
+	.probe		= pl010_probe,
+	.remove		= pl010_remove,
+	.suspend	= pl010_suspend,
+	.resume		= pl010_resume,
+};
+
+static int __init pl010_init(void)
+{
+	int ret;
+
+	printk(KERN_INFO "Serial: AMBA driver\n");
+
+	ret = uart_register_driver(&amba_reg);
+	if (ret == 0) {
+		ret = amba_driver_register(&pl010_driver);
+		if (ret)
+			uart_unregister_driver(&amba_reg);
+	}
+	return ret;
+}
+
+static void __exit pl010_exit(void)
+{
+	amba_driver_unregister(&pl010_driver);
+	uart_unregister_driver(&amba_reg);
+}
+
+module_init(pl010_init);
+module_exit(pl010_exit);
+
+MODULE_AUTHOR("ARM Ltd/Deep Blue Solutions Ltd");
+MODULE_DESCRIPTION("ARM AMBA serial port driver");
+MODULE_LICENSE("GPL");
diff --git a/ap/os/linux/linux-3.4.x/drivers/tty/serial/amba-pl011.c b/ap/os/linux/linux-3.4.x/drivers/tty/serial/amba-pl011.c
new file mode 100644
index 0000000..7b29c92
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/tty/serial/amba-pl011.c
@@ -0,0 +1,2108 @@
+/*
+ *  Driver for AMBA serial ports
+ *
+ *  Based on drivers/char/serial.c, by Linus Torvalds, Theodore Ts'o.
+ *
+ *  Copyright 1999 ARM Limited
+ *  Copyright (C) 2000 Deep Blue Solutions Ltd.
+ *  Copyright (C) 2010 ST-Ericsson SA
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ * This is a generic driver for ARM AMBA-type serial ports.  They
+ * have a lot of 16550-like features, but are not register compatible.
+ * Note that although they do have CTS, DCD and DSR inputs, they do
+ * not have an RI input, nor do they have DTR or RTS outputs.  If
+ * required, these have to be supplied via some other means (eg, GPIO)
+ * and hooked into this driver.
+ */
+
+#if defined(CONFIG_SERIAL_AMBA_PL011_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ)
+#define SUPPORT_SYSRQ
+#endif
+
+#include <linux/module.h>
+#include <linux/ioport.h>
+#include <linux/init.h>
+#include <linux/console.h>
+#include <linux/sysrq.h>
+#include <linux/device.h>
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
+#include <linux/serial_core.h>
+#include <linux/serial.h>
+#include <linux/amba/bus.h>
+#include <linux/amba/serial.h>
+#include <linux/clk.h>
+#include <linux/slab.h>
+#include <linux/dmaengine.h>
+#include <linux/dma-mapping.h>
+#include <linux/scatterlist.h>
+#include <linux/delay.h>
+#include <linux/types.h>
+
+#include <asm/io.h>
+#include <asm/sizes.h>
+
+#define UART_NR			14
+
+#define SERIAL_AMBA_MAJOR	204
+#define SERIAL_AMBA_MINOR	64
+#define SERIAL_AMBA_NR		UART_NR
+
+#define AMBA_ISR_PASS_LIMIT	256
+
+#define UART_DR_ERROR		(UART011_DR_OE|UART011_DR_BE|UART011_DR_PE|UART011_DR_FE)
+#define UART_DUMMY_DR_RX	(1 << 16)
+
+
+#define UART_WA_SAVE_NR 14
+
+static void pl011_lockup_wa(unsigned long data);
+static const u32 uart_wa_reg[UART_WA_SAVE_NR] = {
+	ST_UART011_DMAWM,
+	ST_UART011_TIMEOUT,
+	ST_UART011_LCRH_RX,
+	UART011_IBRD,
+	UART011_FBRD,
+	ST_UART011_LCRH_TX,
+	UART011_IFLS,
+	ST_UART011_XFCR,
+	ST_UART011_XON1,
+	ST_UART011_XON2,
+	ST_UART011_XOFF1,
+	ST_UART011_XOFF2,
+	UART011_CR,
+	UART011_IMSC
+};
+
+static u32 uart_wa_regdata[UART_WA_SAVE_NR];
+static DECLARE_TASKLET(pl011_lockup_tlet, pl011_lockup_wa, 0);
+
+/* There is by now at least one vendor with differing details, so handle it */
+struct vendor_data {
+	unsigned int		ifls;
+	unsigned int		fifosize;
+	unsigned int		lcrh_tx;
+	unsigned int		lcrh_rx;
+	bool			oversampling;
+	bool			interrupt_may_hang;   /* vendor-specific */
+	bool			dma_threshold;
+};
+
+static struct vendor_data vendor_arm = {
+	.ifls			= UART011_IFLS_RX4_8|UART011_IFLS_TX4_8,
+	.fifosize		= 16,
+	.lcrh_tx		= UART011_LCRH,
+	.lcrh_rx		= UART011_LCRH,
+	.oversampling		= false,
+	.dma_threshold		= false,
+};
+
+static struct vendor_data vendor_st = {
+	.ifls			= UART011_IFLS_RX_HALF|UART011_IFLS_TX_HALF,
+	.fifosize		= 64,
+	.lcrh_tx		= ST_UART011_LCRH_TX,
+	.lcrh_rx		= ST_UART011_LCRH_RX,
+	.oversampling		= true,
+	.interrupt_may_hang	= true,
+	.dma_threshold		= true,
+};
+
+static struct uart_amba_port *amba_ports[UART_NR];
+
+/* Deals with DMA transactions */
+
+struct pl011_sgbuf {
+	struct scatterlist sg;
+	char *buf;
+};
+
+struct pl011_dmarx_data {
+	struct dma_chan		*chan;
+	struct completion	complete;
+	bool			use_buf_b;
+	struct pl011_sgbuf	sgbuf_a;
+	struct pl011_sgbuf	sgbuf_b;
+	dma_cookie_t		cookie;
+	bool			running;
+};
+
+struct pl011_dmatx_data {
+	struct dma_chan		*chan;
+	struct scatterlist	sg;
+	char			*buf;
+	bool			queued;
+};
+
+/*
+ * We wrap our port structure around the generic uart_port.
+ */
+struct uart_amba_port {
+	struct uart_port	port;
+	struct clk		*clk;
+	const struct vendor_data *vendor;
+	unsigned int		dmacr;		/* dma control reg */
+	unsigned int		im;		/* interrupt mask */
+	unsigned int		old_status;
+	unsigned int		fifosize;	/* vendor-specific */
+	unsigned int		lcrh_tx;	/* vendor-specific */
+	unsigned int		lcrh_rx;	/* vendor-specific */
+	unsigned int		old_cr;		/* state during shutdown */
+	bool			autorts;
+	char			type[12];
+	bool			interrupt_may_hang; /* vendor-specific */
+#ifdef CONFIG_DMA_ENGINE
+	/* DMA stuff */
+	bool			using_tx_dma;
+	bool			using_rx_dma;
+	struct pl011_dmarx_data dmarx;
+	struct pl011_dmatx_data	dmatx;
+#endif
+};
+
+/*
+ * Reads up to 256 characters from the FIFO or until it's empty and
+ * inserts them into the TTY layer. Returns the number of characters
+ * read from the FIFO.
+ */
+static int pl011_fifo_to_tty(struct uart_amba_port *uap)
+{
+	u16 status, ch;
+	unsigned int flag, max_count = 256;
+	int fifotaken = 0;
+
+	while (max_count--) {
+		status = readw(uap->port.membase + UART01x_FR);
+		if (status & UART01x_FR_RXFE)
+			break;
+
+		/* Take chars from the FIFO and update status */
+		ch = readw(uap->port.membase + UART01x_DR) |
+			UART_DUMMY_DR_RX;
+		flag = TTY_NORMAL;
+		uap->port.icount.rx++;
+		fifotaken++;
+
+		if (unlikely(ch & UART_DR_ERROR)) {
+			if (ch & UART011_DR_BE) {
+				ch &= ~(UART011_DR_FE | UART011_DR_PE);
+				uap->port.icount.brk++;
+				if (uart_handle_break(&uap->port))
+					continue;
+			} else if (ch & UART011_DR_PE)
+				uap->port.icount.parity++;
+			else if (ch & UART011_DR_FE)
+				uap->port.icount.frame++;
+			if (ch & UART011_DR_OE)
+				uap->port.icount.overrun++;
+
+			ch &= uap->port.read_status_mask;
+
+			if (ch & UART011_DR_BE)
+				flag = TTY_BREAK;
+			else if (ch & UART011_DR_PE)
+				flag = TTY_PARITY;
+			else if (ch & UART011_DR_FE)
+				flag = TTY_FRAME;
+		}
+
+		if (uart_handle_sysrq_char(&uap->port, ch & 255))
+			continue;
+
+		uart_insert_char(&uap->port, ch, UART011_DR_OE, ch, flag);
+	}
+
+	return fifotaken;
+}
+
+
+/*
+ * All the DMA operation mode stuff goes inside this ifdef.
+ * This assumes that you have a generic DMA device interface,
+ * no custom DMA interfaces are supported.
+ */
+#ifdef CONFIG_DMA_ENGINE
+
+#define PL011_DMA_BUFFER_SIZE PAGE_SIZE
+
+static int pl011_sgbuf_init(struct dma_chan *chan, struct pl011_sgbuf *sg,
+	enum dma_data_direction dir)
+{
+	sg->buf = kmalloc(PL011_DMA_BUFFER_SIZE, GFP_KERNEL);
+	if (!sg->buf)
+		return -ENOMEM;
+
+	sg_init_one(&sg->sg, sg->buf, PL011_DMA_BUFFER_SIZE);
+
+	if (dma_map_sg(chan->device->dev, &sg->sg, 1, dir) != 1) {
+		kfree(sg->buf);
+		return -EINVAL;
+	}
+	return 0;
+}
+
+static void pl011_sgbuf_free(struct dma_chan *chan, struct pl011_sgbuf *sg,
+	enum dma_data_direction dir)
+{
+	if (sg->buf) {
+		dma_unmap_sg(chan->device->dev, &sg->sg, 1, dir);
+		kfree(sg->buf);
+	}
+}
+
+static void pl011_dma_probe_initcall(struct uart_amba_port *uap)
+{
+	/* DMA is the sole user of the platform data right now */
+	struct amba_pl011_data *plat = uap->port.dev->platform_data;
+	struct dma_slave_config tx_conf = {
+		.dst_addr = uap->port.mapbase + UART01x_DR,
+		.dst_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE,
+		.direction = DMA_MEM_TO_DEV,
+		.dst_maxburst = uap->fifosize >> 1,
+		.device_fc = false,
+	};
+	struct dma_chan *chan;
+	dma_cap_mask_t mask;
+
+	/* We need platform data */
+	if (!plat || !plat->dma_filter) {
+		dev_info(uap->port.dev, "no DMA platform data\n");
+		return;
+	}
+
+	/* Try to acquire a generic DMA engine slave TX channel */
+	dma_cap_zero(mask);
+	dma_cap_set(DMA_SLAVE, mask);
+
+	chan = dma_request_channel(mask, plat->dma_filter, plat->dma_tx_param);
+	if (!chan) {
+		dev_err(uap->port.dev, "no TX DMA channel!\n");
+		return;
+	}
+
+	dmaengine_slave_config(chan, &tx_conf);
+	uap->dmatx.chan = chan;
+
+	dev_info(uap->port.dev, "DMA channel TX %s\n",
+		 dma_chan_name(uap->dmatx.chan));
+
+	/* Optionally make use of an RX channel as well */
+	if (plat->dma_rx_param) {
+		struct dma_slave_config rx_conf = {
+			.src_addr = uap->port.mapbase + UART01x_DR,
+			.src_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE,
+			.direction = DMA_DEV_TO_MEM,
+			.src_maxburst = uap->fifosize >> 1,
+			.device_fc = false,
+		};
+
+		chan = dma_request_channel(mask, plat->dma_filter, plat->dma_rx_param);
+		if (!chan) {
+			dev_err(uap->port.dev, "no RX DMA channel!\n");
+			return;
+		}
+
+		dmaengine_slave_config(chan, &rx_conf);
+		uap->dmarx.chan = chan;
+
+		dev_info(uap->port.dev, "DMA channel RX %s\n",
+			 dma_chan_name(uap->dmarx.chan));
+	}
+}
+
+#ifndef MODULE
+/*
+ * Stack up the UARTs and let the above initcall be done at device
+ * initcall time, because the serial driver is called as an arch
+ * initcall, and at this time the DMA subsystem is not yet registered.
+ * At this point the driver will switch over to using DMA where desired.
+ */
+struct dma_uap {
+	struct list_head node;
+	struct uart_amba_port *uap;
+};
+
+static LIST_HEAD(pl011_dma_uarts);
+
+static int __init pl011_dma_initcall(void)
+{
+	struct list_head *node, *tmp;
+
+	list_for_each_safe(node, tmp, &pl011_dma_uarts) {
+		struct dma_uap *dmau = list_entry(node, struct dma_uap, node);
+		pl011_dma_probe_initcall(dmau->uap);
+		list_del(node);
+		kfree(dmau);
+	}
+	return 0;
+}
+
+device_initcall(pl011_dma_initcall);
+
+static void pl011_dma_probe(struct uart_amba_port *uap)
+{
+	struct dma_uap *dmau = kzalloc(sizeof(struct dma_uap), GFP_KERNEL);
+	if (dmau) {
+		dmau->uap = uap;
+		list_add_tail(&dmau->node, &pl011_dma_uarts);
+	}
+}
+#else
+static void pl011_dma_probe(struct uart_amba_port *uap)
+{
+	pl011_dma_probe_initcall(uap);
+}
+#endif
+
+static void pl011_dma_remove(struct uart_amba_port *uap)
+{
+	/* TODO: remove the initcall if it has not yet executed */
+	if (uap->dmatx.chan)
+		dma_release_channel(uap->dmatx.chan);
+	if (uap->dmarx.chan)
+		dma_release_channel(uap->dmarx.chan);
+}
+
+/* Forward declare this for the refill routine */
+static int pl011_dma_tx_refill(struct uart_amba_port *uap);
+
+/*
+ * The current DMA TX buffer has been sent.
+ * Try to queue up another DMA buffer.
+ */
+static void pl011_dma_tx_callback(void *data)
+{
+	struct uart_amba_port *uap = data;
+	struct pl011_dmatx_data *dmatx = &uap->dmatx;
+	unsigned long flags;
+	u16 dmacr;
+
+	spin_lock_irqsave(&uap->port.lock, flags);
+	if (uap->dmatx.queued)
+		dma_unmap_sg(dmatx->chan->device->dev, &dmatx->sg, 1,
+			     DMA_TO_DEVICE);
+
+	dmacr = uap->dmacr;
+	uap->dmacr = dmacr & ~UART011_TXDMAE;
+	writew(uap->dmacr, uap->port.membase + UART011_DMACR);
+
+	/*
+	 * If TX DMA was disabled, it means that we've stopped the DMA for
+	 * some reason (eg, XOFF received, or we want to send an X-char.)
+	 *
+	 * Note: we need to be careful here of a potential race between DMA
+	 * and the rest of the driver - if the driver disables TX DMA while
+	 * a TX buffer completing, we must update the tx queued status to
+	 * get further refills (hence we check dmacr).
+	 */
+	if (!(dmacr & UART011_TXDMAE) || uart_tx_stopped(&uap->port) ||
+	    uart_circ_empty(&uap->port.state->xmit)) {
+		uap->dmatx.queued = false;
+		spin_unlock_irqrestore(&uap->port.lock, flags);
+		return;
+	}
+
+	if (pl011_dma_tx_refill(uap) <= 0) {
+		/*
+		 * We didn't queue a DMA buffer for some reason, but we
+		 * have data pending to be sent.  Re-enable the TX IRQ.
+		 */
+		uap->im |= UART011_TXIM;
+		writew(uap->im, uap->port.membase + UART011_IMSC);
+	}
+	spin_unlock_irqrestore(&uap->port.lock, flags);
+}
+
+/*
+ * Try to refill the TX DMA buffer.
+ * Locking: called with port lock held and IRQs disabled.
+ * Returns:
+ *   1 if we queued up a TX DMA buffer.
+ *   0 if we didn't want to handle this by DMA
+ *  <0 on error
+ */
+static int pl011_dma_tx_refill(struct uart_amba_port *uap)
+{
+	struct pl011_dmatx_data *dmatx = &uap->dmatx;
+	struct dma_chan *chan = dmatx->chan;
+	struct dma_device *dma_dev = chan->device;
+	struct dma_async_tx_descriptor *desc;
+	struct circ_buf *xmit = &uap->port.state->xmit;
+	unsigned int count;
+
+	/*
+	 * Try to avoid the overhead involved in using DMA if the
+	 * transaction fits in the first half of the FIFO, by using
+	 * the standard interrupt handling.  This ensures that we
+	 * issue a uart_write_wakeup() at the appropriate time.
+	 */
+	count = uart_circ_chars_pending(xmit);
+	if (count < (uap->fifosize >> 1)) {
+		uap->dmatx.queued = false;
+		return 0;
+	}
+
+	/*
+	 * Bodge: don't send the last character by DMA, as this
+	 * will prevent XON from notifying us to restart DMA.
+	 */
+	count -= 1;
+
+	/* Else proceed to copy the TX chars to the DMA buffer and fire DMA */
+	if (count > PL011_DMA_BUFFER_SIZE)
+		count = PL011_DMA_BUFFER_SIZE;
+
+	if (xmit->tail < xmit->head)
+		memcpy(&dmatx->buf[0], &xmit->buf[xmit->tail], count);
+	else {
+		size_t first = UART_XMIT_SIZE - xmit->tail;
+		size_t second = xmit->head;
+
+		memcpy(&dmatx->buf[0], &xmit->buf[xmit->tail], first);
+		if (second)
+			memcpy(&dmatx->buf[first], &xmit->buf[0], second);
+	}
+
+	dmatx->sg.length = count;
+
+	if (dma_map_sg(dma_dev->dev, &dmatx->sg, 1, DMA_TO_DEVICE) != 1) {
+		uap->dmatx.queued = false;
+		dev_dbg(uap->port.dev, "unable to map TX DMA\n");
+		return -EBUSY;
+	}
+
+	desc = dmaengine_prep_slave_sg(chan, &dmatx->sg, 1, DMA_MEM_TO_DEV,
+					     DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
+	if (!desc) {
+		dma_unmap_sg(dma_dev->dev, &dmatx->sg, 1, DMA_TO_DEVICE);
+		uap->dmatx.queued = false;
+		/*
+		 * If DMA cannot be used right now, we complete this
+		 * transaction via IRQ and let the TTY layer retry.
+		 */
+		dev_dbg(uap->port.dev, "TX DMA busy\n");
+		return -EBUSY;
+	}
+
+	/* Some data to go along to the callback */
+	desc->callback = pl011_dma_tx_callback;
+	desc->callback_param = uap;
+
+	/* All errors should happen at prepare time */
+	dmaengine_submit(desc);
+
+	/* Fire the DMA transaction */
+	dma_dev->device_issue_pending(chan);
+
+	uap->dmacr |= UART011_TXDMAE;
+	writew(uap->dmacr, uap->port.membase + UART011_DMACR);
+	uap->dmatx.queued = true;
+
+	/*
+	 * Now we know that DMA will fire, so advance the ring buffer
+	 * with the stuff we just dispatched.
+	 */
+	xmit->tail = (xmit->tail + count) & (UART_XMIT_SIZE - 1);
+	uap->port.icount.tx += count;
+
+	if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
+		uart_write_wakeup(&uap->port);
+
+	return 1;
+}
+
+/*
+ * We received a transmit interrupt without a pending X-char but with
+ * pending characters.
+ * Locking: called with port lock held and IRQs disabled.
+ * Returns:
+ *   false if we want to use PIO to transmit
+ *   true if we queued a DMA buffer
+ */
+static bool pl011_dma_tx_irq(struct uart_amba_port *uap)
+{
+	if (!uap->using_tx_dma)
+		return false;
+
+	/*
+	 * If we already have a TX buffer queued, but received a
+	 * TX interrupt, it will be because we've just sent an X-char.
+	 * Ensure the TX DMA is enabled and the TX IRQ is disabled.
+	 */
+	if (uap->dmatx.queued) {
+		uap->dmacr |= UART011_TXDMAE;
+		writew(uap->dmacr, uap->port.membase + UART011_DMACR);
+		uap->im &= ~UART011_TXIM;
+		writew(uap->im, uap->port.membase + UART011_IMSC);
+		return true;
+	}
+
+	/*
+	 * We don't have a TX buffer queued, so try to queue one.
+	 * If we successfully queued a buffer, mask the TX IRQ.
+	 */
+	if (pl011_dma_tx_refill(uap) > 0) {
+		uap->im &= ~UART011_TXIM;
+		writew(uap->im, uap->port.membase + UART011_IMSC);
+		return true;
+	}
+	return false;
+}
+
+/*
+ * Stop the DMA transmit (eg, due to received XOFF).
+ * Locking: called with port lock held and IRQs disabled.
+ */
+static inline void pl011_dma_tx_stop(struct uart_amba_port *uap)
+{
+	if (uap->dmatx.queued) {
+		uap->dmacr &= ~UART011_TXDMAE;
+		writew(uap->dmacr, uap->port.membase + UART011_DMACR);
+	}
+}
+
+/*
+ * Try to start a DMA transmit, or in the case of an XON/OFF
+ * character queued for send, try to get that character out ASAP.
+ * Locking: called with port lock held and IRQs disabled.
+ * Returns:
+ *   false if we want the TX IRQ to be enabled
+ *   true if we have a buffer queued
+ */
+static inline bool pl011_dma_tx_start(struct uart_amba_port *uap)
+{
+	u16 dmacr;
+
+	if (!uap->using_tx_dma)
+		return false;
+
+	if (!uap->port.x_char) {
+		/* no X-char, try to push chars out in DMA mode */
+		bool ret = true;
+
+		if (!uap->dmatx.queued) {
+			if (pl011_dma_tx_refill(uap) > 0) {
+				uap->im &= ~UART011_TXIM;
+				ret = true;
+			} else {
+				uap->im |= UART011_TXIM;
+				ret = false;
+			}
+			writew(uap->im, uap->port.membase + UART011_IMSC);
+		} else if (!(uap->dmacr & UART011_TXDMAE)) {
+			uap->dmacr |= UART011_TXDMAE;
+			writew(uap->dmacr,
+				       uap->port.membase + UART011_DMACR);
+		}
+		return ret;
+	}
+
+	/*
+	 * We have an X-char to send.  Disable DMA to prevent it loading
+	 * the TX fifo, and then see if we can stuff it into the FIFO.
+	 */
+	dmacr = uap->dmacr;
+	uap->dmacr &= ~UART011_TXDMAE;
+	writew(uap->dmacr, uap->port.membase + UART011_DMACR);
+
+	if (readw(uap->port.membase + UART01x_FR) & UART01x_FR_TXFF) {
+		/*
+		 * No space in the FIFO, so enable the transmit interrupt
+		 * so we know when there is space.  Note that once we've
+		 * loaded the character, we should just re-enable DMA.
+		 */
+		return false;
+	}
+
+	writew(uap->port.x_char, uap->port.membase + UART01x_DR);
+	uap->port.icount.tx++;
+	uap->port.x_char = 0;
+
+	/* Success - restore the DMA state */
+	uap->dmacr = dmacr;
+	writew(dmacr, uap->port.membase + UART011_DMACR);
+
+	return true;
+}
+
+/*
+ * Flush the transmit buffer.
+ * Locking: called with port lock held and IRQs disabled.
+ */
+static void pl011_dma_flush_buffer(struct uart_port *port)
+{
+	struct uart_amba_port *uap = (struct uart_amba_port *)port;
+
+	if (!uap->using_tx_dma)
+		return;
+
+	/* Avoid deadlock with the DMA engine callback */
+	spin_unlock(&uap->port.lock);
+	dmaengine_terminate_all(uap->dmatx.chan);
+	spin_lock(&uap->port.lock);
+	if (uap->dmatx.queued) {
+		dma_unmap_sg(uap->dmatx.chan->device->dev, &uap->dmatx.sg, 1,
+			     DMA_TO_DEVICE);
+		uap->dmatx.queued = false;
+		uap->dmacr &= ~UART011_TXDMAE;
+		writew(uap->dmacr, uap->port.membase + UART011_DMACR);
+	}
+}
+
+static void pl011_dma_rx_callback(void *data);
+
+static int pl011_dma_rx_trigger_dma(struct uart_amba_port *uap)
+{
+	struct dma_chan *rxchan = uap->dmarx.chan;
+	struct pl011_dmarx_data *dmarx = &uap->dmarx;
+	struct dma_async_tx_descriptor *desc;
+	struct pl011_sgbuf *sgbuf;
+
+	if (!rxchan)
+		return -EIO;
+
+	/* Start the RX DMA job */
+	sgbuf = uap->dmarx.use_buf_b ?
+		&uap->dmarx.sgbuf_b : &uap->dmarx.sgbuf_a;
+	desc = dmaengine_prep_slave_sg(rxchan, &sgbuf->sg, 1,
+					DMA_DEV_TO_MEM,
+					DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
+	/*
+	 * If the DMA engine is busy and cannot prepare a
+	 * channel, no big deal, the driver will fall back
+	 * to interrupt mode as a result of this error code.
+	 */
+	if (!desc) {
+		uap->dmarx.running = false;
+		dmaengine_terminate_all(rxchan);
+		return -EBUSY;
+	}
+
+	/* Some data to go along to the callback */
+	desc->callback = pl011_dma_rx_callback;
+	desc->callback_param = uap;
+	dmarx->cookie = dmaengine_submit(desc);
+	dma_async_issue_pending(rxchan);
+
+	uap->dmacr |= UART011_RXDMAE;
+	writew(uap->dmacr, uap->port.membase + UART011_DMACR);
+	uap->dmarx.running = true;
+
+	uap->im &= ~UART011_RXIM;
+	writew(uap->im, uap->port.membase + UART011_IMSC);
+
+	return 0;
+}
+
+/*
+ * This is called when either the DMA job is complete, or
+ * the FIFO timeout interrupt occurred. This must be called
+ * with the port spinlock uap->port.lock held.
+ */
+static void pl011_dma_rx_chars(struct uart_amba_port *uap,
+			       u32 pending, bool use_buf_b,
+			       bool readfifo)
+{
+	struct tty_struct *tty = uap->port.state->port.tty;
+	struct pl011_sgbuf *sgbuf = use_buf_b ?
+		&uap->dmarx.sgbuf_b : &uap->dmarx.sgbuf_a;
+	struct device *dev = uap->dmarx.chan->device->dev;
+	int dma_count = 0;
+	u32 fifotaken = 0; /* only used for vdbg() */
+
+	/* Pick everything from the DMA first */
+	if (pending) {
+		/* Sync in buffer */
+		dma_sync_sg_for_cpu(dev, &sgbuf->sg, 1, DMA_FROM_DEVICE);
+
+		/*
+		 * First take all chars in the DMA pipe, then look in the FIFO.
+		 * Note that tty_insert_flip_buf() tries to take as many chars
+		 * as it can.
+		 */
+		dma_count = tty_insert_flip_string(uap->port.state->port.tty,
+						   sgbuf->buf, pending);
+
+		/* Return buffer to device */
+		dma_sync_sg_for_device(dev, &sgbuf->sg, 1, DMA_FROM_DEVICE);
+
+		uap->port.icount.rx += dma_count;
+		if (dma_count < pending)
+			dev_warn(uap->port.dev,
+				 "couldn't insert all characters (TTY is full?)\n");
+	}
+
+	/*
+	 * Only continue with trying to read the FIFO if all DMA chars have
+	 * been taken first.
+	 */
+	if (dma_count == pending && readfifo) {
+		/* Clear any error flags */
+		writew(UART011_OEIS | UART011_BEIS | UART011_PEIS | UART011_FEIS,
+		       uap->port.membase + UART011_ICR);
+
+		/*
+		 * If we read all the DMA'd characters, and we had an
+		 * incomplete buffer, that could be due to an rx error, or
+		 * maybe we just timed out. Read any pending chars and check
+		 * the error status.
+		 *
+		 * Error conditions will only occur in the FIFO, these will
+		 * trigger an immediate interrupt and stop the DMA job, so we
+		 * will always find the error in the FIFO, never in the DMA
+		 * buffer.
+		 */
+		fifotaken = pl011_fifo_to_tty(uap);
+	}
+
+	spin_unlock(&uap->port.lock);
+	dev_vdbg(uap->port.dev,
+		 "Took %d chars from DMA buffer and %d chars from the FIFO\n",
+		 dma_count, fifotaken);
+	tty_flip_buffer_push(tty);
+	spin_lock(&uap->port.lock);
+}
+
+static void pl011_dma_rx_irq(struct uart_amba_port *uap)
+{
+	struct pl011_dmarx_data *dmarx = &uap->dmarx;
+	struct dma_chan *rxchan = dmarx->chan;
+	struct pl011_sgbuf *sgbuf = dmarx->use_buf_b ?
+		&dmarx->sgbuf_b : &dmarx->sgbuf_a;
+	size_t pending;
+	struct dma_tx_state state;
+	enum dma_status dmastat;
+
+	/*
+	 * Pause the transfer so we can trust the current counter,
+	 * do this before we pause the PL011 block, else we may
+	 * overflow the FIFO.
+	 */
+	if (dmaengine_pause(rxchan))
+		dev_err(uap->port.dev, "unable to pause DMA transfer\n");
+	dmastat = rxchan->device->device_tx_status(rxchan,
+						   dmarx->cookie, &state);
+	if (dmastat != DMA_PAUSED)
+		dev_err(uap->port.dev, "unable to pause DMA transfer\n");
+
+	/* Disable RX DMA - incoming data will wait in the FIFO */
+	uap->dmacr &= ~UART011_RXDMAE;
+	writew(uap->dmacr, uap->port.membase + UART011_DMACR);
+	uap->dmarx.running = false;
+
+	pending = sgbuf->sg.length - state.residue;
+	BUG_ON(pending > PL011_DMA_BUFFER_SIZE);
+	/* Then we terminate the transfer - we now know our residue */
+	dmaengine_terminate_all(rxchan);
+
+	/*
+	 * This will take the chars we have so far and insert
+	 * into the framework.
+	 */
+	pl011_dma_rx_chars(uap, pending, dmarx->use_buf_b, true);
+
+	/* Switch buffer & re-trigger DMA job */
+	dmarx->use_buf_b = !dmarx->use_buf_b;
+	if (pl011_dma_rx_trigger_dma(uap)) {
+		dev_dbg(uap->port.dev, "could not retrigger RX DMA job "
+			"fall back to interrupt mode\n");
+		uap->im |= UART011_RXIM;
+		writew(uap->im, uap->port.membase + UART011_IMSC);
+	}
+}
+
+static void pl011_dma_rx_callback(void *data)
+{
+	struct uart_amba_port *uap = data;
+	struct pl011_dmarx_data *dmarx = &uap->dmarx;
+	struct dma_chan *rxchan = dmarx->chan;
+	bool lastbuf = dmarx->use_buf_b;
+	struct pl011_sgbuf *sgbuf = dmarx->use_buf_b ?
+		&dmarx->sgbuf_b : &dmarx->sgbuf_a;
+	size_t pending;
+	struct dma_tx_state state;
+	int ret;
+
+	/*
+	 * This completion interrupt occurs typically when the
+	 * RX buffer is totally stuffed but no timeout has yet
+	 * occurred. When that happens, we just want the RX
+	 * routine to flush out the secondary DMA buffer while
+	 * we immediately trigger the next DMA job.
+	 */
+	spin_lock_irq(&uap->port.lock);
+	/*
+	 * Rx data can be taken by the UART interrupts during
+	 * the DMA irq handler. So we check the residue here.
+	 */
+	rxchan->device->device_tx_status(rxchan, dmarx->cookie, &state);
+	pending = sgbuf->sg.length - state.residue;
+	BUG_ON(pending > PL011_DMA_BUFFER_SIZE);
+	/* Then we terminate the transfer - we now know our residue */
+	dmaengine_terminate_all(rxchan);
+
+	uap->dmarx.running = false;
+	dmarx->use_buf_b = !lastbuf;
+	ret = pl011_dma_rx_trigger_dma(uap);
+
+	pl011_dma_rx_chars(uap, pending, lastbuf, false);
+	spin_unlock_irq(&uap->port.lock);
+	/*
+	 * Do this check after we picked the DMA chars so we don't
+	 * get some IRQ immediately from RX.
+	 */
+	if (ret) {
+		dev_dbg(uap->port.dev, "could not retrigger RX DMA job "
+			"fall back to interrupt mode\n");
+		uap->im |= UART011_RXIM;
+		writew(uap->im, uap->port.membase + UART011_IMSC);
+	}
+}
+
+/*
+ * Stop accepting received characters, when we're shutting down or
+ * suspending this port.
+ * Locking: called with port lock held and IRQs disabled.
+ */
+static inline void pl011_dma_rx_stop(struct uart_amba_port *uap)
+{
+	/* FIXME.  Just disable the DMA enable */
+	uap->dmacr &= ~UART011_RXDMAE;
+	writew(uap->dmacr, uap->port.membase + UART011_DMACR);
+}
+
+static void pl011_dma_startup(struct uart_amba_port *uap)
+{
+	int ret;
+
+	if (!uap->dmatx.chan)
+		return;
+
+	uap->dmatx.buf = kmalloc(PL011_DMA_BUFFER_SIZE, GFP_KERNEL);
+	if (!uap->dmatx.buf) {
+		dev_err(uap->port.dev, "no memory for DMA TX buffer\n");
+		uap->port.fifosize = uap->fifosize;
+		return;
+	}
+
+	sg_init_one(&uap->dmatx.sg, uap->dmatx.buf, PL011_DMA_BUFFER_SIZE);
+
+	/* The DMA buffer is now the FIFO the TTY subsystem can use */
+	uap->port.fifosize = PL011_DMA_BUFFER_SIZE;
+	uap->using_tx_dma = true;
+
+	if (!uap->dmarx.chan)
+		goto skip_rx;
+
+	/* Allocate and map DMA RX buffers */
+	ret = pl011_sgbuf_init(uap->dmarx.chan, &uap->dmarx.sgbuf_a,
+			       DMA_FROM_DEVICE);
+	if (ret) {
+		dev_err(uap->port.dev, "failed to init DMA %s: %d\n",
+			"RX buffer A", ret);
+		goto skip_rx;
+	}
+
+	ret = pl011_sgbuf_init(uap->dmarx.chan, &uap->dmarx.sgbuf_b,
+			       DMA_FROM_DEVICE);
+	if (ret) {
+		dev_err(uap->port.dev, "failed to init DMA %s: %d\n",
+			"RX buffer B", ret);
+		pl011_sgbuf_free(uap->dmarx.chan, &uap->dmarx.sgbuf_a,
+				 DMA_FROM_DEVICE);
+		goto skip_rx;
+	}
+
+	uap->using_rx_dma = true;
+
+skip_rx:
+	/* Turn on DMA error (RX/TX will be enabled on demand) */
+	uap->dmacr |= UART011_DMAONERR;
+	writew(uap->dmacr, uap->port.membase + UART011_DMACR);
+
+	/*
+	 * ST Micro variants has some specific dma burst threshold
+	 * compensation. Set this to 16 bytes, so burst will only
+	 * be issued above/below 16 bytes.
+	 */
+	if (uap->vendor->dma_threshold)
+		writew(ST_UART011_DMAWM_RX_16 | ST_UART011_DMAWM_TX_16,
+			       uap->port.membase + ST_UART011_DMAWM);
+
+	if (uap->using_rx_dma) {
+		if (pl011_dma_rx_trigger_dma(uap))
+			dev_dbg(uap->port.dev, "could not trigger initial "
+				"RX DMA job, fall back to interrupt mode\n");
+	}
+}
+
+static void pl011_dma_shutdown(struct uart_amba_port *uap)
+{
+	if (!(uap->using_tx_dma || uap->using_rx_dma))
+		return;
+
+	/* Disable RX and TX DMA */
+	while (readw(uap->port.membase + UART01x_FR) & UART01x_FR_BUSY)
+		barrier();
+
+	spin_lock_irq(&uap->port.lock);
+	uap->dmacr &= ~(UART011_DMAONERR | UART011_RXDMAE | UART011_TXDMAE);
+	writew(uap->dmacr, uap->port.membase + UART011_DMACR);
+	spin_unlock_irq(&uap->port.lock);
+
+	if (uap->using_tx_dma) {
+		/* In theory, this should already be done by pl011_dma_flush_buffer */
+		dmaengine_terminate_all(uap->dmatx.chan);
+		if (uap->dmatx.queued) {
+			dma_unmap_sg(uap->dmatx.chan->device->dev, &uap->dmatx.sg, 1,
+				     DMA_TO_DEVICE);
+			uap->dmatx.queued = false;
+		}
+
+		kfree(uap->dmatx.buf);
+		uap->using_tx_dma = false;
+	}
+
+	if (uap->using_rx_dma) {
+		dmaengine_terminate_all(uap->dmarx.chan);
+		/* Clean up the RX DMA */
+		pl011_sgbuf_free(uap->dmarx.chan, &uap->dmarx.sgbuf_a, DMA_FROM_DEVICE);
+		pl011_sgbuf_free(uap->dmarx.chan, &uap->dmarx.sgbuf_b, DMA_FROM_DEVICE);
+		uap->using_rx_dma = false;
+	}
+}
+
+static inline bool pl011_dma_rx_available(struct uart_amba_port *uap)
+{
+	return uap->using_rx_dma;
+}
+
+static inline bool pl011_dma_rx_running(struct uart_amba_port *uap)
+{
+	return uap->using_rx_dma && uap->dmarx.running;
+}
+
+
+#else
+/* Blank functions if the DMA engine is not available */
+static inline void pl011_dma_probe(struct uart_amba_port *uap)
+{
+}
+
+static inline void pl011_dma_remove(struct uart_amba_port *uap)
+{
+}
+
+static inline void pl011_dma_startup(struct uart_amba_port *uap)
+{
+}
+
+static inline void pl011_dma_shutdown(struct uart_amba_port *uap)
+{
+}
+
+static inline bool pl011_dma_tx_irq(struct uart_amba_port *uap)
+{
+	return false;
+}
+
+static inline void pl011_dma_tx_stop(struct uart_amba_port *uap)
+{
+}
+
+static inline bool pl011_dma_tx_start(struct uart_amba_port *uap)
+{
+	return false;
+}
+
+static inline void pl011_dma_rx_irq(struct uart_amba_port *uap)
+{
+}
+
+static inline void pl011_dma_rx_stop(struct uart_amba_port *uap)
+{
+}
+
+static inline int pl011_dma_rx_trigger_dma(struct uart_amba_port *uap)
+{
+	return -EIO;
+}
+
+static inline bool pl011_dma_rx_available(struct uart_amba_port *uap)
+{
+	return false;
+}
+
+static inline bool pl011_dma_rx_running(struct uart_amba_port *uap)
+{
+	return false;
+}
+
+#define pl011_dma_flush_buffer	NULL
+#endif
+
+
+/*
+ * pl011_lockup_wa
+ * This workaround aims to break the deadlock situation
+ * when after long transfer over uart in hardware flow
+ * control, uart interrupt registers cannot be cleared.
+ * Hence uart transfer gets blocked.
+ *
+ * It is seen that during such deadlock condition ICR
+ * don't get cleared even on multiple write. This leads
+ * pass_counter to decrease and finally reach zero. This
+ * can be taken as trigger point to run this UART_BT_WA.
+ *
+ */
+static void pl011_lockup_wa(unsigned long data)
+{
+	struct uart_amba_port *uap = amba_ports[0];
+	void __iomem *base = uap->port.membase;
+	struct circ_buf *xmit = &uap->port.state->xmit;
+	struct tty_struct *tty = uap->port.state->port.tty;
+	int buf_empty_retries = 200;
+	int loop;
+
+	/* Stop HCI layer from submitting data for tx */
+	tty->hw_stopped = 1;
+	while (!uart_circ_empty(xmit)) {
+		if (buf_empty_retries-- == 0)
+			break;
+		udelay(100);
+	}
+
+	/* Backup registers */
+	for (loop = 0; loop < UART_WA_SAVE_NR; loop++)
+		uart_wa_regdata[loop] = readl(base + uart_wa_reg[loop]);
+
+	/* Disable UART so that FIFO data is flushed out */
+	writew(0x00, uap->port.membase + UART011_CR);
+
+	/* Soft reset UART module */
+	if (uap->port.dev->platform_data) {
+		struct amba_pl011_data *plat;
+
+		plat = uap->port.dev->platform_data;
+		if (plat->reset)
+			plat->reset();
+	}
+
+	/* Restore registers */
+	for (loop = 0; loop < UART_WA_SAVE_NR; loop++)
+		writew(uart_wa_regdata[loop] ,
+				uap->port.membase + uart_wa_reg[loop]);
+
+	/* Initialise the old status of the modem signals */
+	uap->old_status = readw(uap->port.membase + UART01x_FR) &
+		UART01x_FR_MODEM_ANY;
+
+	if (readl(base + UART011_MIS) & 0x2)
+		printk(KERN_EMERG "UART_BT_WA: ***FAILED***\n");
+
+	/* Start Tx/Rx */
+	tty->hw_stopped = 0;
+}
+
+static void pl011_stop_tx(struct uart_port *port)
+{
+	struct uart_amba_port *uap = (struct uart_amba_port *)port;
+
+	uap->im &= ~UART011_TXIM;
+	writew(uap->im, uap->port.membase + UART011_IMSC);
+	pl011_dma_tx_stop(uap);
+}
+
+static void pl011_start_tx(struct uart_port *port)
+{
+	struct uart_amba_port *uap = (struct uart_amba_port *)port;
+
+	if (!pl011_dma_tx_start(uap)) {
+		uap->im |= UART011_TXIM;
+		writew(uap->im, uap->port.membase + UART011_IMSC);
+	}
+}
+
+static void pl011_stop_rx(struct uart_port *port)
+{
+	struct uart_amba_port *uap = (struct uart_amba_port *)port;
+
+	uap->im &= ~(UART011_RXIM|UART011_RTIM|UART011_FEIM|
+		     UART011_PEIM|UART011_BEIM|UART011_OEIM);
+	writew(uap->im, uap->port.membase + UART011_IMSC);
+
+	pl011_dma_rx_stop(uap);
+}
+
+static void pl011_enable_ms(struct uart_port *port)
+{
+	struct uart_amba_port *uap = (struct uart_amba_port *)port;
+
+	uap->im |= UART011_RIMIM|UART011_CTSMIM|UART011_DCDMIM|UART011_DSRMIM;
+	writew(uap->im, uap->port.membase + UART011_IMSC);
+}
+
+static void pl011_rx_chars(struct uart_amba_port *uap)
+{
+	struct tty_struct *tty = uap->port.state->port.tty;
+
+	pl011_fifo_to_tty(uap);
+
+	spin_unlock(&uap->port.lock);
+	tty_flip_buffer_push(tty);
+	/*
+	 * If we were temporarily out of DMA mode for a while,
+	 * attempt to switch back to DMA mode again.
+	 */
+	if (pl011_dma_rx_available(uap)) {
+		if (pl011_dma_rx_trigger_dma(uap)) {
+			dev_dbg(uap->port.dev, "could not trigger RX DMA job "
+				"fall back to interrupt mode again\n");
+			uap->im |= UART011_RXIM;
+		} else
+			uap->im &= ~UART011_RXIM;
+		writew(uap->im, uap->port.membase + UART011_IMSC);
+	}
+	spin_lock(&uap->port.lock);
+}
+
+static void pl011_tx_chars(struct uart_amba_port *uap)
+{
+	struct circ_buf *xmit = &uap->port.state->xmit;
+	int count;
+
+	if (uap->port.x_char) {
+		writew(uap->port.x_char, uap->port.membase + UART01x_DR);
+		uap->port.icount.tx++;
+		uap->port.x_char = 0;
+		return;
+	}
+	if (uart_circ_empty(xmit) || uart_tx_stopped(&uap->port)) {
+		pl011_stop_tx(&uap->port);
+		return;
+	}
+
+	/* If we are using DMA mode, try to send some characters. */
+	if (pl011_dma_tx_irq(uap))
+		return;
+
+	count = uap->fifosize >> 1;
+	do {
+		writew(xmit->buf[xmit->tail], uap->port.membase + UART01x_DR);
+		xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
+		uap->port.icount.tx++;
+		if (uart_circ_empty(xmit))
+			break;
+	} while (--count > 0);
+
+	if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
+		uart_write_wakeup(&uap->port);
+
+	if (uart_circ_empty(xmit))
+		pl011_stop_tx(&uap->port);
+}
+
+static void pl011_modem_status(struct uart_amba_port *uap)
+{
+	unsigned int status, delta;
+
+	status = readw(uap->port.membase + UART01x_FR) & UART01x_FR_MODEM_ANY;
+
+	delta = status ^ uap->old_status;
+	uap->old_status = status;
+
+	if (!delta)
+		return;
+
+	if (delta & UART01x_FR_DCD)
+		uart_handle_dcd_change(&uap->port, status & UART01x_FR_DCD);
+
+	if (delta & UART01x_FR_DSR)
+		uap->port.icount.dsr++;
+
+	if (delta & UART01x_FR_CTS)
+		uart_handle_cts_change(&uap->port, status & UART01x_FR_CTS);
+
+	wake_up_interruptible(&uap->port.state->port.delta_msr_wait);
+}
+
+static irqreturn_t pl011_int(int irq, void *dev_id)
+{
+	struct uart_amba_port *uap = dev_id;
+	unsigned long flags;
+	unsigned int status, pass_counter = AMBA_ISR_PASS_LIMIT;
+	int handled = 0;
+
+	spin_lock_irqsave(&uap->port.lock, flags);
+
+	status = readw(uap->port.membase + UART011_MIS);
+	if (status) {
+		do {
+			writew(status & ~(UART011_TXIS|UART011_RTIS|
+					  UART011_RXIS),
+			       uap->port.membase + UART011_ICR);
+
+			if (status & (UART011_RTIS|UART011_RXIS)) {
+				if (pl011_dma_rx_running(uap))
+					pl011_dma_rx_irq(uap);
+				else
+					pl011_rx_chars(uap);
+			}
+			if (status & (UART011_DSRMIS|UART011_DCDMIS|
+				      UART011_CTSMIS|UART011_RIMIS))
+				pl011_modem_status(uap);
+			if (status & UART011_TXIS)
+				pl011_tx_chars(uap);
+
+			if (pass_counter-- == 0) {
+				if (uap->interrupt_may_hang)
+					tasklet_schedule(&pl011_lockup_tlet);
+				break;
+			}
+
+			status = readw(uap->port.membase + UART011_MIS);
+		} while (status != 0);
+		handled = 1;
+	}
+
+	spin_unlock_irqrestore(&uap->port.lock, flags);
+
+	return IRQ_RETVAL(handled);
+}
+
+static unsigned int pl01x_tx_empty(struct uart_port *port)
+{
+	struct uart_amba_port *uap = (struct uart_amba_port *)port;
+	unsigned int status = readw(uap->port.membase + UART01x_FR);
+	return status & (UART01x_FR_BUSY|UART01x_FR_TXFF) ? 0 : TIOCSER_TEMT;
+}
+
+static unsigned int pl01x_get_mctrl(struct uart_port *port)
+{
+	struct uart_amba_port *uap = (struct uart_amba_port *)port;
+	unsigned int result = 0;
+	unsigned int status = readw(uap->port.membase + UART01x_FR);
+
+#define TIOCMBIT(uartbit, tiocmbit)	\
+	if (status & uartbit)		\
+		result |= tiocmbit
+
+	TIOCMBIT(UART01x_FR_DCD, TIOCM_CAR);
+	TIOCMBIT(UART01x_FR_DSR, TIOCM_DSR);
+	TIOCMBIT(UART01x_FR_CTS, TIOCM_CTS);
+	TIOCMBIT(UART011_FR_RI, TIOCM_RNG);
+#undef TIOCMBIT
+	return result;
+}
+
+static void pl011_set_mctrl(struct uart_port *port, unsigned int mctrl)
+{
+	struct uart_amba_port *uap = (struct uart_amba_port *)port;
+	unsigned int cr;
+
+	cr = readw(uap->port.membase + UART011_CR);
+
+#define	TIOCMBIT(tiocmbit, uartbit)		\
+	if (mctrl & tiocmbit)		\
+		cr |= uartbit;		\
+	else				\
+		cr &= ~uartbit
+
+	TIOCMBIT(TIOCM_RTS, UART011_CR_RTS);
+	TIOCMBIT(TIOCM_DTR, UART011_CR_DTR);
+	TIOCMBIT(TIOCM_OUT1, UART011_CR_OUT1);
+	TIOCMBIT(TIOCM_OUT2, UART011_CR_OUT2);
+	TIOCMBIT(TIOCM_LOOP, UART011_CR_LBE);
+
+	if (uap->autorts) {
+		/* We need to disable auto-RTS if we want to turn RTS off */
+		TIOCMBIT(TIOCM_RTS, UART011_CR_RTSEN);
+	}
+#undef TIOCMBIT
+
+	writew(cr, uap->port.membase + UART011_CR);
+}
+
+static void pl011_break_ctl(struct uart_port *port, int break_state)
+{
+	struct uart_amba_port *uap = (struct uart_amba_port *)port;
+	unsigned long flags;
+	unsigned int lcr_h;
+
+	spin_lock_irqsave(&uap->port.lock, flags);
+	lcr_h = readw(uap->port.membase + uap->lcrh_tx);
+	if (break_state == -1)
+		lcr_h |= UART01x_LCRH_BRK;
+	else
+		lcr_h &= ~UART01x_LCRH_BRK;
+	writew(lcr_h, uap->port.membase + uap->lcrh_tx);
+	spin_unlock_irqrestore(&uap->port.lock, flags);
+}
+
+#ifdef CONFIG_CONSOLE_POLL
+static int pl010_get_poll_char(struct uart_port *port)
+{
+	struct uart_amba_port *uap = (struct uart_amba_port *)port;
+	unsigned int status;
+
+	status = readw(uap->port.membase + UART01x_FR);
+	if (status & UART01x_FR_RXFE)
+		return NO_POLL_CHAR;
+
+	return readw(uap->port.membase + UART01x_DR);
+}
+
+static void pl010_put_poll_char(struct uart_port *port,
+			 unsigned char ch)
+{
+	struct uart_amba_port *uap = (struct uart_amba_port *)port;
+
+	while (readw(uap->port.membase + UART01x_FR) & UART01x_FR_TXFF)
+		barrier();
+
+	writew(ch, uap->port.membase + UART01x_DR);
+}
+
+#endif /* CONFIG_CONSOLE_POLL */
+
+static int pl011_startup(struct uart_port *port)
+{
+	struct uart_amba_port *uap = (struct uart_amba_port *)port;
+	unsigned int cr;
+	int retval;
+
+	retval = clk_prepare(uap->clk);
+	if (retval)
+		goto out;
+
+	/*
+	 * Try to enable the clock producer.
+	 */
+	retval = clk_enable(uap->clk);
+	if (retval)
+		goto clk_unprep;
+
+	uap->port.uartclk = clk_get_rate(uap->clk);
+
+	/* Clear pending error and receive interrupts */
+	writew(UART011_OEIS | UART011_BEIS | UART011_PEIS | UART011_FEIS |
+	       UART011_RTIS | UART011_RXIS, uap->port.membase + UART011_ICR);
+
+	/*
+	 * Allocate the IRQ
+	 */
+	retval = request_irq(uap->port.irq, pl011_int, 0, "uart-pl011", uap);
+	if (retval)
+		goto clk_dis;
+
+	writew(uap->vendor->ifls, uap->port.membase + UART011_IFLS);
+
+	/*
+	 * Provoke TX FIFO interrupt into asserting.
+	 */
+	cr = UART01x_CR_UARTEN | UART011_CR_TXE | UART011_CR_LBE;
+	writew(cr, uap->port.membase + UART011_CR);
+	writew(0, uap->port.membase + UART011_FBRD);
+	writew(1, uap->port.membase + UART011_IBRD);
+	writew(0, uap->port.membase + uap->lcrh_rx);
+	if (uap->lcrh_tx != uap->lcrh_rx) {
+		int i;
+		/*
+		 * Wait 10 PCLKs before writing LCRH_TX register,
+		 * to get this delay write read only register 10 times
+		 */
+		for (i = 0; i < 10; ++i)
+			writew(0xff, uap->port.membase + UART011_MIS);
+		writew(0, uap->port.membase + uap->lcrh_tx);
+	}
+	writew(0, uap->port.membase + UART01x_DR);
+	while (readw(uap->port.membase + UART01x_FR) & UART01x_FR_BUSY)
+		barrier();
+
+	/* restore RTS and DTR */
+	cr = uap->old_cr & (UART011_CR_RTS | UART011_CR_DTR);
+	cr |= UART01x_CR_UARTEN | UART011_CR_RXE | UART011_CR_TXE;
+	writew(cr, uap->port.membase + UART011_CR);
+
+	/*
+	 * initialise the old status of the modem signals
+	 */
+	uap->old_status = readw(uap->port.membase + UART01x_FR) & UART01x_FR_MODEM_ANY;
+
+	/* Startup DMA */
+	pl011_dma_startup(uap);
+
+	/*
+	 * Finally, enable interrupts, only timeouts when using DMA
+	 * if initial RX DMA job failed, start in interrupt mode
+	 * as well.
+	 */
+	spin_lock_irq(&uap->port.lock);
+	/* Clear out any spuriously appearing RX interrupts */
+	 writew(UART011_RTIS | UART011_RXIS,
+		uap->port.membase + UART011_ICR);
+	uap->im = UART011_RTIM;
+	if (!pl011_dma_rx_running(uap))
+		uap->im |= UART011_RXIM;
+	writew(uap->im, uap->port.membase + UART011_IMSC);
+	spin_unlock_irq(&uap->port.lock);
+
+	if (uap->port.dev->platform_data) {
+		struct amba_pl011_data *plat;
+
+		plat = uap->port.dev->platform_data;
+		if (plat->init)
+			plat->init();
+	}
+
+	return 0;
+
+ clk_dis:
+	clk_disable(uap->clk);
+ clk_unprep:
+	clk_unprepare(uap->clk);
+ out:
+	return retval;
+}
+
+static void pl011_shutdown_channel(struct uart_amba_port *uap,
+					unsigned int lcrh)
+{
+      unsigned long val;
+
+      val = readw(uap->port.membase + lcrh);
+      val &= ~(UART01x_LCRH_BRK | UART01x_LCRH_FEN);
+      writew(val, uap->port.membase + lcrh);
+}
+
+static void pl011_shutdown(struct uart_port *port)
+{
+	struct uart_amba_port *uap = (struct uart_amba_port *)port;
+	unsigned int cr;
+
+	/*
+	 * disable all interrupts
+	 */
+	spin_lock_irq(&uap->port.lock);
+	uap->im = 0;
+	writew(uap->im, uap->port.membase + UART011_IMSC);
+	writew(0xffff, uap->port.membase + UART011_ICR);
+	spin_unlock_irq(&uap->port.lock);
+
+	pl011_dma_shutdown(uap);
+
+	/*
+	 * Free the interrupt
+	 */
+	free_irq(uap->port.irq, uap);
+
+	/*
+	 * disable the port
+	 * disable the port. It should not disable RTS and DTR.
+	 * Also RTS and DTR state should be preserved to restore
+	 * it during startup().
+	 */
+	uap->autorts = false;
+	cr = readw(uap->port.membase + UART011_CR);
+	uap->old_cr = cr;
+	cr &= UART011_CR_RTS | UART011_CR_DTR;
+	cr |= UART01x_CR_UARTEN | UART011_CR_TXE;
+	writew(cr, uap->port.membase + UART011_CR);
+
+	/*
+	 * disable break condition and fifos
+	 */
+	pl011_shutdown_channel(uap, uap->lcrh_rx);
+	if (uap->lcrh_rx != uap->lcrh_tx)
+		pl011_shutdown_channel(uap, uap->lcrh_tx);
+
+	/*
+	 * Shut down the clock producer
+	 */
+	clk_disable(uap->clk);
+	clk_unprepare(uap->clk);
+
+	if (uap->port.dev->platform_data) {
+		struct amba_pl011_data *plat;
+
+		plat = uap->port.dev->platform_data;
+		if (plat->exit)
+			plat->exit();
+	}
+
+}
+
+static void
+pl011_set_termios(struct uart_port *port, struct ktermios *termios,
+		     struct ktermios *old)
+{
+	struct uart_amba_port *uap = (struct uart_amba_port *)port;
+	unsigned int lcr_h, old_cr;
+	unsigned long flags;
+	unsigned int baud, quot, clkdiv;
+
+	if (uap->vendor->oversampling)
+		clkdiv = 8;
+	else
+		clkdiv = 16;
+
+	/*
+	 * Ask the core to calculate the divisor for us.
+	 */
+	baud = uart_get_baud_rate(port, termios, old, 0,
+				  port->uartclk / clkdiv);
+
+	if (baud > port->uartclk/16)
+		quot = DIV_ROUND_CLOSEST(port->uartclk * 8, baud);
+	else
+		quot = DIV_ROUND_CLOSEST(port->uartclk * 4, baud);
+
+	switch (termios->c_cflag & CSIZE) {
+	case CS5:
+		lcr_h = UART01x_LCRH_WLEN_5;
+		break;
+	case CS6:
+		lcr_h = UART01x_LCRH_WLEN_6;
+		break;
+	case CS7:
+		lcr_h = UART01x_LCRH_WLEN_7;
+		break;
+	default: // CS8
+		lcr_h = UART01x_LCRH_WLEN_8;
+		break;
+	}
+	if (termios->c_cflag & CSTOPB)
+		lcr_h |= UART01x_LCRH_STP2;
+	if (termios->c_cflag & PARENB) {
+		lcr_h |= UART01x_LCRH_PEN;
+		if (!(termios->c_cflag & PARODD))
+			lcr_h |= UART01x_LCRH_EPS;
+	}
+	if (uap->fifosize > 1)
+		lcr_h |= UART01x_LCRH_FEN;
+
+	spin_lock_irqsave(&port->lock, flags);
+
+	/*
+	 * Update the per-port timeout.
+	 */
+	uart_update_timeout(port, termios->c_cflag, baud);
+
+	port->read_status_mask = UART011_DR_OE | 255;
+	if (termios->c_iflag & INPCK)
+		port->read_status_mask |= UART011_DR_FE | UART011_DR_PE;
+	if (termios->c_iflag & (BRKINT | PARMRK))
+		port->read_status_mask |= UART011_DR_BE;
+
+	/*
+	 * Characters to ignore
+	 */
+	port->ignore_status_mask = 0;
+	if (termios->c_iflag & IGNPAR)
+		port->ignore_status_mask |= UART011_DR_FE | UART011_DR_PE;
+	if (termios->c_iflag & IGNBRK) {
+		port->ignore_status_mask |= UART011_DR_BE;
+		/*
+		 * If we're ignoring parity and break indicators,
+		 * ignore overruns too (for real raw support).
+		 */
+		if (termios->c_iflag & IGNPAR)
+			port->ignore_status_mask |= UART011_DR_OE;
+	}
+
+	/*
+	 * Ignore all characters if CREAD is not set.
+	 */
+	if ((termios->c_cflag & CREAD) == 0)
+		port->ignore_status_mask |= UART_DUMMY_DR_RX;
+
+	if (UART_ENABLE_MS(port, termios->c_cflag))
+		pl011_enable_ms(port);
+
+	/* first, disable everything */
+	old_cr = readw(port->membase + UART011_CR);
+	writew(0, port->membase + UART011_CR);
+
+	if (termios->c_cflag & CRTSCTS) {
+		if (old_cr & UART011_CR_RTS)
+			old_cr |= UART011_CR_RTSEN;
+
+		old_cr |= UART011_CR_CTSEN;
+		uap->autorts = true;
+	} else {
+		old_cr &= ~(UART011_CR_CTSEN | UART011_CR_RTSEN);
+		uap->autorts = false;
+	}
+
+	if (uap->vendor->oversampling) {
+		if (baud > port->uartclk / 16)
+			old_cr |= ST_UART011_CR_OVSFACT;
+		else
+			old_cr &= ~ST_UART011_CR_OVSFACT;
+	}
+
+	/*
+	 * Workaround for the ST Micro oversampling variants to
+	 * increase the bitrate slightly, by lowering the divisor,
+	 * to avoid delayed sampling of start bit at high speeds,
+	 * else we see data corruption.
+	 */
+	if (uap->vendor->oversampling) {
+		if ((baud >= 3000000) && (baud < 3250000) && (quot > 1))
+			quot -= 1;
+		else if ((baud > 3250000) && (quot > 2))
+			quot -= 2;
+	}
+	/* Set baud rate */
+	writew(quot & 0x3f, port->membase + UART011_FBRD);
+	writew(quot >> 6, port->membase + UART011_IBRD);
+
+	/*
+	 * ----------v----------v----------v----------v-----
+	 * NOTE: lcrh_tx and lcrh_rx MUST BE WRITTEN AFTER
+	 * UART011_FBRD & UART011_IBRD.
+	 * ----------^----------^----------^----------^-----
+	 */
+	writew(lcr_h, port->membase + uap->lcrh_rx);
+	if (uap->lcrh_rx != uap->lcrh_tx) {
+		int i;
+		/*
+		 * Wait 10 PCLKs before writing LCRH_TX register,
+		 * to get this delay write read only register 10 times
+		 */
+		for (i = 0; i < 10; ++i)
+			writew(0xff, uap->port.membase + UART011_MIS);
+		writew(lcr_h, port->membase + uap->lcrh_tx);
+	}
+	writew(old_cr, port->membase + UART011_CR);
+
+	spin_unlock_irqrestore(&port->lock, flags);
+}
+
+static const char *pl011_type(struct uart_port *port)
+{
+	struct uart_amba_port *uap = (struct uart_amba_port *)port;
+	return uap->port.type == PORT_AMBA ? uap->type : NULL;
+}
+
+/*
+ * Release the memory region(s) being used by 'port'
+ */
+static void pl010_release_port(struct uart_port *port)
+{
+	release_mem_region(port->mapbase, SZ_4K);
+}
+
+/*
+ * Request the memory region(s) being used by 'port'
+ */
+static int pl010_request_port(struct uart_port *port)
+{
+	return request_mem_region(port->mapbase, SZ_4K, "uart-pl011")
+			!= NULL ? 0 : -EBUSY;
+}
+
+/*
+ * Configure/autoconfigure the port.
+ */
+static void pl010_config_port(struct uart_port *port, int flags)
+{
+	if (flags & UART_CONFIG_TYPE) {
+		port->type = PORT_AMBA;
+		pl010_request_port(port);
+	}
+}
+
+/*
+ * verify the new serial_struct (for TIOCSSERIAL).
+ */
+static int pl010_verify_port(struct uart_port *port, struct serial_struct *ser)
+{
+	int ret = 0;
+	if (ser->type != PORT_UNKNOWN && ser->type != PORT_AMBA)
+		ret = -EINVAL;
+	if (ser->irq < 0 || ser->irq >= nr_irqs)
+		ret = -EINVAL;
+	if (ser->baud_base < 9600)
+		ret = -EINVAL;
+	return ret;
+}
+
+static struct uart_ops amba_pl011_pops = {
+	.tx_empty	= pl01x_tx_empty,
+	.set_mctrl	= pl011_set_mctrl,
+	.get_mctrl	= pl01x_get_mctrl,
+	.stop_tx	= pl011_stop_tx,
+	.start_tx	= pl011_start_tx,
+	.stop_rx	= pl011_stop_rx,
+	.enable_ms	= pl011_enable_ms,
+	.break_ctl	= pl011_break_ctl,
+	.startup	= pl011_startup,
+	.shutdown	= pl011_shutdown,
+	.flush_buffer	= pl011_dma_flush_buffer,
+	.set_termios	= pl011_set_termios,
+	.type		= pl011_type,
+	.release_port	= pl010_release_port,
+	.request_port	= pl010_request_port,
+	.config_port	= pl010_config_port,
+	.verify_port	= pl010_verify_port,
+#ifdef CONFIG_CONSOLE_POLL
+	.poll_get_char = pl010_get_poll_char,
+	.poll_put_char = pl010_put_poll_char,
+#endif
+};
+
+static struct uart_amba_port *amba_ports[UART_NR];
+
+#ifdef CONFIG_SERIAL_AMBA_PL011_CONSOLE
+
+static void pl011_console_putchar(struct uart_port *port, int ch)
+{
+	struct uart_amba_port *uap = (struct uart_amba_port *)port;
+
+	while (readw(uap->port.membase + UART01x_FR) & UART01x_FR_TXFF)
+		barrier();
+	writew(ch, uap->port.membase + UART01x_DR);
+}
+
+static void
+pl011_console_write(struct console *co, const char *s, unsigned int count)
+{
+	struct uart_amba_port *uap = amba_ports[co->index];
+	unsigned int status, old_cr, new_cr;
+	unsigned long flags;
+	int locked = 1;
+
+	clk_enable(uap->clk);
+
+	/*
+	 * local_irq_save(flags);
+	 *
+	 * This local_irq_save() is nonsense. If we come in via sysrq
+	 * handling then interrupts are already disabled. Aside of
+	 * that the port.sysrq check is racy on SMP regardless.
+	*/
+	if (uap->port.sysrq)
+		locked = 0;
+	else if (oops_in_progress)
+		locked = spin_trylock_irqsave(&uap->port.lock, flags);
+	else
+		spin_lock_irqsave(&uap->port.lock, flags);
+
+	/*
+	 *	First save the CR then disable the interrupts
+	 */
+	old_cr = readw(uap->port.membase + UART011_CR);
+	new_cr = old_cr & ~UART011_CR_CTSEN;
+	new_cr |= UART01x_CR_UARTEN | UART011_CR_TXE;
+	writew(new_cr, uap->port.membase + UART011_CR);
+
+	uart_console_write(&uap->port, s, count, pl011_console_putchar);
+
+	/*
+	 *	Finally, wait for transmitter to become empty
+	 *	and restore the TCR
+	 */
+	do {
+		status = readw(uap->port.membase + UART01x_FR);
+	} while (status & UART01x_FR_BUSY);
+	writew(old_cr, uap->port.membase + UART011_CR);
+
+	if (locked)
+		spin_unlock_irqrestore(&uap->port.lock, flags);
+
+	clk_disable(uap->clk);
+}
+
+static void __init
+pl011_console_get_options(struct uart_amba_port *uap, int *baud,
+			     int *parity, int *bits)
+{
+	if (readw(uap->port.membase + UART011_CR) & UART01x_CR_UARTEN) {
+		unsigned int lcr_h, ibrd, fbrd;
+
+		lcr_h = readw(uap->port.membase + uap->lcrh_tx);
+
+		*parity = 'n';
+		if (lcr_h & UART01x_LCRH_PEN) {
+			if (lcr_h & UART01x_LCRH_EPS)
+				*parity = 'e';
+			else
+				*parity = 'o';
+		}
+
+		if ((lcr_h & 0x60) == UART01x_LCRH_WLEN_7)
+			*bits = 7;
+		else
+			*bits = 8;
+
+		ibrd = readw(uap->port.membase + UART011_IBRD);
+		fbrd = readw(uap->port.membase + UART011_FBRD);
+
+		*baud = uap->port.uartclk * 4 / (64 * ibrd + fbrd);
+
+		if (uap->vendor->oversampling) {
+			if (readw(uap->port.membase + UART011_CR)
+				  & ST_UART011_CR_OVSFACT)
+				*baud *= 2;
+		}
+	}
+}
+
+static int __init pl011_console_setup(struct console *co, char *options)
+{
+	struct uart_amba_port *uap;
+	int baud = 38400;
+	int bits = 8;
+	int parity = 'n';
+	int flow = 'n';
+	int ret;
+
+	/*
+	 * 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 >= UART_NR)
+		co->index = 0;
+	uap = amba_ports[co->index];
+	if (!uap)
+		return -ENODEV;
+
+	ret = clk_prepare(uap->clk);
+	if (ret)
+		return ret;
+
+	if (uap->port.dev->platform_data) {
+		struct amba_pl011_data *plat;
+
+		plat = uap->port.dev->platform_data;
+		if (plat->init)
+			plat->init();
+	}
+
+	uap->port.uartclk = clk_get_rate(uap->clk);
+
+	if (options)
+		uart_parse_options(options, &baud, &parity, &bits, &flow);
+	else
+		pl011_console_get_options(uap, &baud, &parity, &bits);
+
+	return uart_set_options(&uap->port, co, baud, parity, bits, flow);
+}
+
+static struct uart_driver amba_reg;
+static struct console amba_console = {
+	.name		= "ttyAMA",
+	.write		= pl011_console_write,
+	.device		= uart_console_device,
+	.setup		= pl011_console_setup,
+	.flags		= CON_PRINTBUFFER,
+	.index		= -1,
+	.data		= &amba_reg,
+};
+
+#define AMBA_CONSOLE	(&amba_console)
+#else
+#define AMBA_CONSOLE	NULL
+#endif
+
+static struct uart_driver amba_reg = {
+	.owner			= THIS_MODULE,
+	.driver_name		= "ttyAMA",
+	.dev_name		= "ttyAMA",
+	.major			= SERIAL_AMBA_MAJOR,
+	.minor			= SERIAL_AMBA_MINOR,
+	.nr			= UART_NR,
+	.cons			= AMBA_CONSOLE,
+};
+
+static int pl011_probe(struct amba_device *dev, const struct amba_id *id)
+{
+	struct uart_amba_port *uap;
+	struct vendor_data *vendor = id->data;
+	void __iomem *base;
+	int i, ret;
+
+	for (i = 0; i < ARRAY_SIZE(amba_ports); i++)
+		if (amba_ports[i] == NULL)
+			break;
+
+	if (i == ARRAY_SIZE(amba_ports)) {
+		ret = -EBUSY;
+		goto out;
+	}
+
+	uap = kzalloc(sizeof(struct uart_amba_port), GFP_KERNEL);
+	if (uap == NULL) {
+		ret = -ENOMEM;
+		goto out;
+	}
+
+	base = ioremap(dev->res.start, resource_size(&dev->res));
+	if (!base) {
+		ret = -ENOMEM;
+		goto free;
+	}
+
+	uap->clk = clk_get(&dev->dev, NULL);
+	if (IS_ERR(uap->clk)) {
+		ret = PTR_ERR(uap->clk);
+		goto unmap;
+	}
+
+	uap->vendor = vendor;
+	uap->lcrh_rx = vendor->lcrh_rx;
+	uap->lcrh_tx = vendor->lcrh_tx;
+	uap->old_cr = 0;
+	uap->fifosize = vendor->fifosize;
+	uap->interrupt_may_hang = vendor->interrupt_may_hang;
+	uap->port.dev = &dev->dev;
+	uap->port.mapbase = dev->res.start;
+	uap->port.membase = base;
+	uap->port.iotype = UPIO_MEM;
+	uap->port.irq = dev->irq[0];
+	uap->port.fifosize = uap->fifosize;
+	uap->port.ops = &amba_pl011_pops;
+	uap->port.flags = UPF_BOOT_AUTOCONF;
+	uap->port.line = i;
+	pl011_dma_probe(uap);
+
+	/* Ensure interrupts from this UART are masked and cleared */
+	writew(0, uap->port.membase + UART011_IMSC);
+	writew(0xffff, uap->port.membase + UART011_ICR);
+
+	snprintf(uap->type, sizeof(uap->type), "PL011 rev%u", amba_rev(dev));
+
+	amba_ports[i] = uap;
+
+	amba_set_drvdata(dev, uap);
+	ret = uart_add_one_port(&amba_reg, &uap->port);
+	if (ret) {
+		amba_set_drvdata(dev, NULL);
+		amba_ports[i] = NULL;
+		pl011_dma_remove(uap);
+		clk_put(uap->clk);
+ unmap:
+		iounmap(base);
+ free:
+		kfree(uap);
+	}
+ out:
+	return ret;
+}
+
+static int pl011_remove(struct amba_device *dev)
+{
+	struct uart_amba_port *uap = amba_get_drvdata(dev);
+	int i;
+
+	amba_set_drvdata(dev, NULL);
+
+	uart_remove_one_port(&amba_reg, &uap->port);
+
+	for (i = 0; i < ARRAY_SIZE(amba_ports); i++)
+		if (amba_ports[i] == uap)
+			amba_ports[i] = NULL;
+
+	pl011_dma_remove(uap);
+	iounmap(uap->port.membase);
+	clk_put(uap->clk);
+	kfree(uap);
+	return 0;
+}
+
+#ifdef CONFIG_PM
+static int pl011_suspend(struct amba_device *dev, pm_message_t state)
+{
+	struct uart_amba_port *uap = amba_get_drvdata(dev);
+
+	if (!uap)
+		return -EINVAL;
+
+	return uart_suspend_port(&amba_reg, &uap->port);
+}
+
+static int pl011_resume(struct amba_device *dev)
+{
+	struct uart_amba_port *uap = amba_get_drvdata(dev);
+
+	if (!uap)
+		return -EINVAL;
+
+	return uart_resume_port(&amba_reg, &uap->port);
+}
+#endif
+
+static struct amba_id pl011_ids[] = {
+	{
+		.id	= 0x00041011,
+		.mask	= 0x000fffff,
+		.data	= &vendor_arm,
+	},
+	{
+		.id	= 0x00380802,
+		.mask	= 0x00ffffff,
+		.data	= &vendor_st,
+	},
+	{ 0, 0 },
+};
+
+MODULE_DEVICE_TABLE(amba, pl011_ids);
+
+static struct amba_driver pl011_driver = {
+	.drv = {
+		.name	= "uart-pl011",
+	},
+	.id_table	= pl011_ids,
+	.probe		= pl011_probe,
+	.remove		= pl011_remove,
+#ifdef CONFIG_PM
+	.suspend	= pl011_suspend,
+	.resume		= pl011_resume,
+#endif
+};
+
+static int __init pl011_init(void)
+{
+	int ret;
+	printk(KERN_INFO "Serial: AMBA PL011 UART driver\n");
+
+	ret = uart_register_driver(&amba_reg);
+	if (ret == 0) {
+		ret = amba_driver_register(&pl011_driver);
+		if (ret)
+			uart_unregister_driver(&amba_reg);
+	}
+	return ret;
+}
+
+static void __exit pl011_exit(void)
+{
+	amba_driver_unregister(&pl011_driver);
+	uart_unregister_driver(&amba_reg);
+}
+
+/*
+ * While this can be a module, if builtin it's most likely the console
+ * So let's leave module_exit but move module_init to an earlier place
+ */
+arch_initcall(pl011_init);
+module_exit(pl011_exit);
+
+MODULE_AUTHOR("ARM Ltd/Deep Blue Solutions Ltd");
+MODULE_DESCRIPTION("ARM AMBA serial port driver");
+MODULE_LICENSE("GPL");
diff --git a/ap/os/linux/linux-3.4.x/drivers/tty/serial/apbuart.c b/ap/os/linux/linux-3.4.x/drivers/tty/serial/apbuart.c
new file mode 100644
index 0000000..7162f70
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/tty/serial/apbuart.c
@@ -0,0 +1,697 @@
+/*
+ *  Driver for GRLIB serial ports (APBUART)
+ *
+ *  Based on linux/drivers/serial/amba.c
+ *
+ *  Copyright (C) 2000 Deep Blue Solutions Ltd.
+ *  Copyright (C) 2003 Konrad Eisele <eiselekd@web.de>
+ *  Copyright (C) 2006 Daniel Hellstrom <daniel@gaisler.com>, Aeroflex Gaisler AB
+ *  Copyright (C) 2008 Gilead Kutnick <kutnickg@zin-tech.com>
+ *  Copyright (C) 2009 Kristoffer Glembo <kristoffer@gaisler.com>, Aeroflex Gaisler AB
+ */
+
+#if defined(CONFIG_SERIAL_GRLIB_GAISLER_APBUART_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ)
+#define SUPPORT_SYSRQ
+#endif
+
+#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/kthread.h>
+#include <linux/device.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/of_platform.h>
+#include <linux/of_irq.h>
+#include <linux/platform_device.h>
+#include <linux/io.h>
+#include <linux/serial_core.h>
+#include <asm/irq.h>
+
+#include "apbuart.h"
+
+#define SERIAL_APBUART_MAJOR	TTY_MAJOR
+#define SERIAL_APBUART_MINOR	64
+#define UART_DUMMY_RSR_RX	0x8000	/* for ignore all read */
+
+static void apbuart_tx_chars(struct uart_port *port);
+
+static void apbuart_stop_tx(struct uart_port *port)
+{
+	unsigned int cr;
+
+	cr = UART_GET_CTRL(port);
+	cr &= ~UART_CTRL_TI;
+	UART_PUT_CTRL(port, cr);
+}
+
+static void apbuart_start_tx(struct uart_port *port)
+{
+	unsigned int cr;
+
+	cr = UART_GET_CTRL(port);
+	cr |= UART_CTRL_TI;
+	UART_PUT_CTRL(port, cr);
+
+	if (UART_GET_STATUS(port) & UART_STATUS_THE)
+		apbuart_tx_chars(port);
+}
+
+static void apbuart_stop_rx(struct uart_port *port)
+{
+	unsigned int cr;
+
+	cr = UART_GET_CTRL(port);
+	cr &= ~(UART_CTRL_RI);
+	UART_PUT_CTRL(port, cr);
+}
+
+static void apbuart_enable_ms(struct uart_port *port)
+{
+	/* No modem status change interrupts for APBUART */
+}
+
+static void apbuart_rx_chars(struct uart_port *port)
+{
+	struct tty_struct *tty = port->state->port.tty;
+	unsigned int status, ch, rsr, flag;
+	unsigned int max_chars = port->fifosize;
+
+	status = UART_GET_STATUS(port);
+
+	while (UART_RX_DATA(status) && (max_chars--)) {
+
+		ch = UART_GET_CHAR(port);
+		flag = TTY_NORMAL;
+
+		port->icount.rx++;
+
+		rsr = UART_GET_STATUS(port) | UART_DUMMY_RSR_RX;
+		UART_PUT_STATUS(port, 0);
+		if (rsr & UART_STATUS_ERR) {
+
+			if (rsr & UART_STATUS_BR) {
+				rsr &= ~(UART_STATUS_FE | UART_STATUS_PE);
+				port->icount.brk++;
+				if (uart_handle_break(port))
+					goto ignore_char;
+			} else if (rsr & UART_STATUS_PE) {
+				port->icount.parity++;
+			} else if (rsr & UART_STATUS_FE) {
+				port->icount.frame++;
+			}
+			if (rsr & UART_STATUS_OE)
+				port->icount.overrun++;
+
+			rsr &= port->read_status_mask;
+
+			if (rsr & UART_STATUS_PE)
+				flag = TTY_PARITY;
+			else if (rsr & UART_STATUS_FE)
+				flag = TTY_FRAME;
+		}
+
+		if (uart_handle_sysrq_char(port, ch))
+			goto ignore_char;
+
+		uart_insert_char(port, rsr, UART_STATUS_OE, ch, flag);
+
+
+	      ignore_char:
+		status = UART_GET_STATUS(port);
+	}
+
+	tty_flip_buffer_push(tty);
+}
+
+static void apbuart_tx_chars(struct uart_port *port)
+{
+	struct circ_buf *xmit = &port->state->xmit;
+	int count;
+
+	if (port->x_char) {
+		UART_PUT_CHAR(port, port->x_char);
+		port->icount.tx++;
+		port->x_char = 0;
+		return;
+	}
+
+	if (uart_circ_empty(xmit) || uart_tx_stopped(port)) {
+		apbuart_stop_tx(port);
+		return;
+	}
+
+	/* amba: fill FIFO */
+	count = port->fifosize >> 1;
+	do {
+		UART_PUT_CHAR(port, xmit->buf[xmit->tail]);
+		xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
+		port->icount.tx++;
+		if (uart_circ_empty(xmit))
+			break;
+	} while (--count > 0);
+
+	if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
+		uart_write_wakeup(port);
+
+	if (uart_circ_empty(xmit))
+		apbuart_stop_tx(port);
+}
+
+static irqreturn_t apbuart_int(int irq, void *dev_id)
+{
+	struct uart_port *port = dev_id;
+	unsigned int status;
+
+	spin_lock(&port->lock);
+
+	status = UART_GET_STATUS(port);
+	if (status & UART_STATUS_DR)
+		apbuart_rx_chars(port);
+	if (status & UART_STATUS_THE)
+		apbuart_tx_chars(port);
+
+	spin_unlock(&port->lock);
+
+	return IRQ_HANDLED;
+}
+
+static unsigned int apbuart_tx_empty(struct uart_port *port)
+{
+	unsigned int status = UART_GET_STATUS(port);
+	return status & UART_STATUS_THE ? TIOCSER_TEMT : 0;
+}
+
+static unsigned int apbuart_get_mctrl(struct uart_port *port)
+{
+	/* The GRLIB APBUART handles flow control in hardware */
+	return TIOCM_CAR | TIOCM_DSR | TIOCM_CTS;
+}
+
+static void apbuart_set_mctrl(struct uart_port *port, unsigned int mctrl)
+{
+	/* The GRLIB APBUART handles flow control in hardware */
+}
+
+static void apbuart_break_ctl(struct uart_port *port, int break_state)
+{
+	/* We don't support sending break */
+}
+
+static int apbuart_startup(struct uart_port *port)
+{
+	int retval;
+	unsigned int cr;
+
+	/* Allocate the IRQ */
+	retval = request_irq(port->irq, apbuart_int, 0, "apbuart", port);
+	if (retval)
+		return retval;
+
+	/* Finally, enable interrupts */
+	cr = UART_GET_CTRL(port);
+	UART_PUT_CTRL(port,
+		      cr | UART_CTRL_RE | UART_CTRL_TE |
+		      UART_CTRL_RI | UART_CTRL_TI);
+
+	return 0;
+}
+
+static void apbuart_shutdown(struct uart_port *port)
+{
+	unsigned int cr;
+
+	/* disable all interrupts, disable the port */
+	cr = UART_GET_CTRL(port);
+	UART_PUT_CTRL(port,
+		      cr & ~(UART_CTRL_RE | UART_CTRL_TE |
+			     UART_CTRL_RI | UART_CTRL_TI));
+
+	/* Free the interrupt */
+	free_irq(port->irq, port);
+}
+
+static void apbuart_set_termios(struct uart_port *port,
+				struct ktermios *termios, struct ktermios *old)
+{
+	unsigned int cr;
+	unsigned long flags;
+	unsigned int baud, quot;
+
+	/* Ask the core to calculate the divisor for us. */
+	baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk / 16);
+	if (baud == 0)
+		panic("invalid baudrate %i\n", port->uartclk / 16);
+
+	/* uart_get_divisor calc a *16 uart freq, apbuart is *8 */
+	quot = (uart_get_divisor(port, baud)) * 2;
+	cr = UART_GET_CTRL(port);
+	cr &= ~(UART_CTRL_PE | UART_CTRL_PS);
+
+	if (termios->c_cflag & PARENB) {
+		cr |= UART_CTRL_PE;
+		if ((termios->c_cflag & PARODD))
+			cr |= UART_CTRL_PS;
+	}
+
+	/* Enable flow control. */
+	if (termios->c_cflag & CRTSCTS)
+		cr |= UART_CTRL_FL;
+
+	spin_lock_irqsave(&port->lock, flags);
+
+	/* Update the per-port timeout. */
+	uart_update_timeout(port, termios->c_cflag, baud);
+
+	port->read_status_mask = UART_STATUS_OE;
+	if (termios->c_iflag & INPCK)
+		port->read_status_mask |= UART_STATUS_FE | UART_STATUS_PE;
+
+	/* Characters to ignore */
+	port->ignore_status_mask = 0;
+	if (termios->c_iflag & IGNPAR)
+		port->ignore_status_mask |= UART_STATUS_FE | UART_STATUS_PE;
+
+	/* Ignore all characters if CREAD is not set. */
+	if ((termios->c_cflag & CREAD) == 0)
+		port->ignore_status_mask |= UART_DUMMY_RSR_RX;
+
+	/* Set baud rate */
+	quot -= 1;
+	UART_PUT_SCAL(port, quot);
+	UART_PUT_CTRL(port, cr);
+
+	spin_unlock_irqrestore(&port->lock, flags);
+}
+
+static const char *apbuart_type(struct uart_port *port)
+{
+	return port->type == PORT_APBUART ? "GRLIB/APBUART" : NULL;
+}
+
+static void apbuart_release_port(struct uart_port *port)
+{
+	release_mem_region(port->mapbase, 0x100);
+}
+
+static int apbuart_request_port(struct uart_port *port)
+{
+	return request_mem_region(port->mapbase, 0x100, "grlib-apbuart")
+	    != NULL ? 0 : -EBUSY;
+	return 0;
+}
+
+/* Configure/autoconfigure the port */
+static void apbuart_config_port(struct uart_port *port, int flags)
+{
+	if (flags & UART_CONFIG_TYPE) {
+		port->type = PORT_APBUART;
+		apbuart_request_port(port);
+	}
+}
+
+/* Verify the new serial_struct (for TIOCSSERIAL) */
+static int apbuart_verify_port(struct uart_port *port,
+			       struct serial_struct *ser)
+{
+	int ret = 0;
+	if (ser->type != PORT_UNKNOWN && ser->type != PORT_APBUART)
+		ret = -EINVAL;
+	if (ser->irq < 0 || ser->irq >= NR_IRQS)
+		ret = -EINVAL;
+	if (ser->baud_base < 9600)
+		ret = -EINVAL;
+	return ret;
+}
+
+static struct uart_ops grlib_apbuart_ops = {
+	.tx_empty = apbuart_tx_empty,
+	.set_mctrl = apbuart_set_mctrl,
+	.get_mctrl = apbuart_get_mctrl,
+	.stop_tx = apbuart_stop_tx,
+	.start_tx = apbuart_start_tx,
+	.stop_rx = apbuart_stop_rx,
+	.enable_ms = apbuart_enable_ms,
+	.break_ctl = apbuart_break_ctl,
+	.startup = apbuart_startup,
+	.shutdown = apbuart_shutdown,
+	.set_termios = apbuart_set_termios,
+	.type = apbuart_type,
+	.release_port = apbuart_release_port,
+	.request_port = apbuart_request_port,
+	.config_port = apbuart_config_port,
+	.verify_port = apbuart_verify_port,
+};
+
+static struct uart_port grlib_apbuart_ports[UART_NR];
+static struct device_node *grlib_apbuart_nodes[UART_NR];
+
+static int apbuart_scan_fifo_size(struct uart_port *port, int portnumber)
+{
+	int ctrl, loop = 0;
+	int status;
+	int fifosize;
+	unsigned long flags;
+
+	ctrl = UART_GET_CTRL(port);
+
+	/*
+	 * Enable the transceiver and wait for it to be ready to send data.
+	 * Clear interrupts so that this process will not be externally
+	 * interrupted in the middle (which can cause the transceiver to
+	 * drain prematurely).
+	 */
+
+	local_irq_save(flags);
+
+	UART_PUT_CTRL(port, ctrl | UART_CTRL_TE);
+
+	while (!UART_TX_READY(UART_GET_STATUS(port)))
+		loop++;
+
+	/*
+	 * Disable the transceiver so data isn't actually sent during the
+	 * actual test.
+	 */
+
+	UART_PUT_CTRL(port, ctrl & ~(UART_CTRL_TE));
+
+	fifosize = 1;
+	UART_PUT_CHAR(port, 0);
+
+	/*
+	 * So long as transmitting a character increments the tranceivier FIFO
+	 * length the FIFO must be at least that big. These bytes will
+	 * automatically drain off of the FIFO.
+	 */
+
+	status = UART_GET_STATUS(port);
+	while (((status >> 20) & 0x3F) == fifosize) {
+		fifosize++;
+		UART_PUT_CHAR(port, 0);
+		status = UART_GET_STATUS(port);
+	}
+
+	fifosize--;
+
+	UART_PUT_CTRL(port, ctrl);
+	local_irq_restore(flags);
+
+	if (fifosize == 0)
+		fifosize = 1;
+
+	return fifosize;
+}
+
+static void apbuart_flush_fifo(struct uart_port *port)
+{
+	int i;
+
+	for (i = 0; i < port->fifosize; i++)
+		UART_GET_CHAR(port);
+}
+
+
+/* ======================================================================== */
+/* Console driver, if enabled                                               */
+/* ======================================================================== */
+
+#ifdef CONFIG_SERIAL_GRLIB_GAISLER_APBUART_CONSOLE
+
+static void apbuart_console_putchar(struct uart_port *port, int ch)
+{
+	unsigned int status;
+	do {
+		status = UART_GET_STATUS(port);
+	} while (!UART_TX_READY(status));
+	UART_PUT_CHAR(port, ch);
+}
+
+static void
+apbuart_console_write(struct console *co, const char *s, unsigned int count)
+{
+	struct uart_port *port = &grlib_apbuart_ports[co->index];
+	unsigned int status, old_cr, new_cr;
+
+	/* First save the CR then disable the interrupts */
+	old_cr = UART_GET_CTRL(port);
+	new_cr = old_cr & ~(UART_CTRL_RI | UART_CTRL_TI);
+	UART_PUT_CTRL(port, new_cr);
+
+	uart_console_write(port, s, count, apbuart_console_putchar);
+
+	/*
+	 *      Finally, wait for transmitter to become empty
+	 *      and restore the TCR
+	 */
+	do {
+		status = UART_GET_STATUS(port);
+	} while (!UART_TX_READY(status));
+	UART_PUT_CTRL(port, old_cr);
+}
+
+static void __init
+apbuart_console_get_options(struct uart_port *port, int *baud,
+			    int *parity, int *bits)
+{
+	if (UART_GET_CTRL(port) & (UART_CTRL_RE | UART_CTRL_TE)) {
+
+		unsigned int quot, status;
+		status = UART_GET_STATUS(port);
+
+		*parity = 'n';
+		if (status & UART_CTRL_PE) {
+			if ((status & UART_CTRL_PS) == 0)
+				*parity = 'e';
+			else
+				*parity = 'o';
+		}
+
+		*bits = 8;
+		quot = UART_GET_SCAL(port) / 8;
+		*baud = port->uartclk / (16 * (quot + 1));
+	}
+}
+
+static int __init apbuart_console_setup(struct console *co, char *options)
+{
+	struct uart_port *port;
+	int baud = 38400;
+	int bits = 8;
+	int parity = 'n';
+	int flow = 'n';
+
+	pr_debug("apbuart_console_setup co=%p, co->index=%i, options=%s\n",
+		 co, co->index, options);
+
+	/*
+	 * 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 >= grlib_apbuart_port_nr)
+		co->index = 0;
+
+	port = &grlib_apbuart_ports[co->index];
+
+	spin_lock_init(&port->lock);
+
+	if (options)
+		uart_parse_options(options, &baud, &parity, &bits, &flow);
+	else
+		apbuart_console_get_options(port, &baud, &parity, &bits);
+
+	return uart_set_options(port, co, baud, parity, bits, flow);
+}
+
+static struct uart_driver grlib_apbuart_driver;
+
+static struct console grlib_apbuart_console = {
+	.name = "ttyS",
+	.write = apbuart_console_write,
+	.device = uart_console_device,
+	.setup = apbuart_console_setup,
+	.flags = CON_PRINTBUFFER,
+	.index = -1,
+	.data = &grlib_apbuart_driver,
+};
+
+
+static int grlib_apbuart_configure(void);
+
+static int __init apbuart_console_init(void)
+{
+	if (grlib_apbuart_configure())
+		return -ENODEV;
+	register_console(&grlib_apbuart_console);
+	return 0;
+}
+
+console_initcall(apbuart_console_init);
+
+#define APBUART_CONSOLE	(&grlib_apbuart_console)
+#else
+#define APBUART_CONSOLE	NULL
+#endif
+
+static struct uart_driver grlib_apbuart_driver = {
+	.owner = THIS_MODULE,
+	.driver_name = "serial",
+	.dev_name = "ttyS",
+	.major = SERIAL_APBUART_MAJOR,
+	.minor = SERIAL_APBUART_MINOR,
+	.nr = UART_NR,
+	.cons = APBUART_CONSOLE,
+};
+
+
+/* ======================================================================== */
+/* OF Platform Driver                                                       */
+/* ======================================================================== */
+
+static int __devinit apbuart_probe(struct platform_device *op)
+{
+	int i;
+	struct uart_port *port = NULL;
+
+	for (i = 0; i < grlib_apbuart_port_nr; i++) {
+		if (op->dev.of_node == grlib_apbuart_nodes[i])
+			break;
+	}
+
+	port = &grlib_apbuart_ports[i];
+	port->dev = &op->dev;
+	port->irq = op->archdata.irqs[0];
+
+	uart_add_one_port(&grlib_apbuart_driver, (struct uart_port *) port);
+
+	apbuart_flush_fifo((struct uart_port *) port);
+
+	printk(KERN_INFO "grlib-apbuart at 0x%llx, irq %d\n",
+	       (unsigned long long) port->mapbase, port->irq);
+	return 0;
+}
+
+static struct of_device_id apbuart_match[] = {
+	{
+	 .name = "GAISLER_APBUART",
+	 },
+	{
+	 .name = "01_00c",
+	 },
+	{},
+};
+
+static struct platform_driver grlib_apbuart_of_driver = {
+	.probe = apbuart_probe,
+	.driver = {
+		.owner = THIS_MODULE,
+		.name = "grlib-apbuart",
+		.of_match_table = apbuart_match,
+	},
+};
+
+
+static int __init grlib_apbuart_configure(void)
+{
+	struct device_node *np;
+	int line = 0;
+
+	for_each_matching_node(np, apbuart_match) {
+		const int *ampopts;
+		const u32 *freq_hz;
+		const struct amba_prom_registers *regs;
+		struct uart_port *port;
+		unsigned long addr;
+
+		ampopts = of_get_property(np, "ampopts", NULL);
+		if (ampopts && (*ampopts == 0))
+			continue; /* Ignore if used by another OS instance */
+		regs = of_get_property(np, "reg", NULL);
+		/* Frequency of APB Bus is frequency of UART */
+		freq_hz = of_get_property(np, "freq", NULL);
+
+		if (!regs || !freq_hz || (*freq_hz == 0))
+			continue;
+
+		grlib_apbuart_nodes[line] = np;
+
+		addr = regs->phys_addr;
+
+		port = &grlib_apbuart_ports[line];
+
+		port->mapbase = addr;
+		port->membase = ioremap(addr, sizeof(struct grlib_apbuart_regs_map));
+		port->irq = 0;
+		port->iotype = UPIO_MEM;
+		port->ops = &grlib_apbuart_ops;
+		port->flags = UPF_BOOT_AUTOCONF;
+		port->line = line;
+		port->uartclk = *freq_hz;
+		port->fifosize = apbuart_scan_fifo_size((struct uart_port *) port, line);
+		line++;
+
+		/* We support maximum UART_NR uarts ... */
+		if (line == UART_NR)
+			break;
+	}
+
+	grlib_apbuart_driver.nr = grlib_apbuart_port_nr = line;
+	return line ? 0 : -ENODEV;
+}
+
+static int __init grlib_apbuart_init(void)
+{
+	int ret;
+
+	/* Find all APBUARTS in device the tree and initialize their ports */
+	ret = grlib_apbuart_configure();
+	if (ret)
+		return ret;
+
+	printk(KERN_INFO "Serial: GRLIB APBUART driver\n");
+
+	ret = uart_register_driver(&grlib_apbuart_driver);
+
+	if (ret) {
+		printk(KERN_ERR "%s: uart_register_driver failed (%i)\n",
+		       __FILE__, ret);
+		return ret;
+	}
+
+	ret = platform_driver_register(&grlib_apbuart_of_driver);
+	if (ret) {
+		printk(KERN_ERR
+		       "%s: platform_driver_register failed (%i)\n",
+		       __FILE__, ret);
+		uart_unregister_driver(&grlib_apbuart_driver);
+		return ret;
+	}
+
+	return ret;
+}
+
+static void __exit grlib_apbuart_exit(void)
+{
+	int i;
+
+	for (i = 0; i < grlib_apbuart_port_nr; i++)
+		uart_remove_one_port(&grlib_apbuart_driver,
+				     &grlib_apbuart_ports[i]);
+
+	uart_unregister_driver(&grlib_apbuart_driver);
+	platform_driver_unregister(&grlib_apbuart_of_driver);
+}
+
+module_init(grlib_apbuart_init);
+module_exit(grlib_apbuart_exit);
+
+MODULE_AUTHOR("Aeroflex Gaisler AB");
+MODULE_DESCRIPTION("GRLIB APBUART serial driver");
+MODULE_VERSION("2.1");
+MODULE_LICENSE("GPL");
diff --git a/ap/os/linux/linux-3.4.x/drivers/tty/serial/apbuart.h b/ap/os/linux/linux-3.4.x/drivers/tty/serial/apbuart.h
new file mode 100644
index 0000000..5faf87c
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/tty/serial/apbuart.h
@@ -0,0 +1,64 @@
+#ifndef __GRLIB_APBUART_H__
+#define __GRLIB_APBUART_H__
+
+#include <asm/io.h>
+
+#define UART_NR		8
+static int grlib_apbuart_port_nr;
+
+struct grlib_apbuart_regs_map {
+	u32 data;
+	u32 status;
+	u32 ctrl;
+	u32 scaler;
+};
+
+struct amba_prom_registers {
+	unsigned int phys_addr;
+	unsigned int reg_size;
+};
+
+/*
+ *  The following defines the bits in the APBUART Status Registers.
+ */
+#define UART_STATUS_DR   0x00000001	/* Data Ready */
+#define UART_STATUS_TSE  0x00000002	/* TX Send Register Empty */
+#define UART_STATUS_THE  0x00000004	/* TX Hold Register Empty */
+#define UART_STATUS_BR   0x00000008	/* Break Error */
+#define UART_STATUS_OE   0x00000010	/* RX Overrun Error */
+#define UART_STATUS_PE   0x00000020	/* RX Parity Error */
+#define UART_STATUS_FE   0x00000040	/* RX Framing Error */
+#define UART_STATUS_ERR  0x00000078	/* Error Mask */
+
+/*
+ *  The following defines the bits in the APBUART Ctrl Registers.
+ */
+#define UART_CTRL_RE     0x00000001	/* Receiver enable */
+#define UART_CTRL_TE     0x00000002	/* Transmitter enable */
+#define UART_CTRL_RI     0x00000004	/* Receiver interrupt enable */
+#define UART_CTRL_TI     0x00000008	/* Transmitter irq */
+#define UART_CTRL_PS     0x00000010	/* Parity select */
+#define UART_CTRL_PE     0x00000020	/* Parity enable */
+#define UART_CTRL_FL     0x00000040	/* Flow control enable */
+#define UART_CTRL_LB     0x00000080	/* Loopback enable */
+
+#define APBBASE(port) ((struct grlib_apbuart_regs_map *)((port)->membase))
+
+#define APBBASE_DATA_P(port)	(&(APBBASE(port)->data))
+#define APBBASE_STATUS_P(port)	(&(APBBASE(port)->status))
+#define APBBASE_CTRL_P(port)	(&(APBBASE(port)->ctrl))
+#define APBBASE_SCALAR_P(port)	(&(APBBASE(port)->scaler))
+
+#define UART_GET_CHAR(port)	(__raw_readl(APBBASE_DATA_P(port)))
+#define UART_PUT_CHAR(port, v)	(__raw_writel(v, APBBASE_DATA_P(port)))
+#define UART_GET_STATUS(port)	(__raw_readl(APBBASE_STATUS_P(port)))
+#define UART_PUT_STATUS(port, v)(__raw_writel(v, APBBASE_STATUS_P(port)))
+#define UART_GET_CTRL(port)	(__raw_readl(APBBASE_CTRL_P(port)))
+#define UART_PUT_CTRL(port, v)	(__raw_writel(v, APBBASE_CTRL_P(port)))
+#define UART_GET_SCAL(port)	(__raw_readl(APBBASE_SCALAR_P(port)))
+#define UART_PUT_SCAL(port, v)	(__raw_writel(v, APBBASE_SCALAR_P(port)))
+
+#define UART_RX_DATA(s)		(((s) & UART_STATUS_DR) != 0)
+#define UART_TX_READY(s)	(((s) & UART_STATUS_THE) != 0)
+
+#endif /* __GRLIB_APBUART_H__ */
diff --git a/ap/os/linux/linux-3.4.x/drivers/tty/serial/ar933x_uart.c b/ap/os/linux/linux-3.4.x/drivers/tty/serial/ar933x_uart.c
new file mode 100644
index 0000000..e4f60e2
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/tty/serial/ar933x_uart.c
@@ -0,0 +1,688 @@
+/*
+ *  Atheros AR933X SoC built-in UART driver
+ *
+ *  Copyright (C) 2011 Gabor Juhos <juhosg@openwrt.org>
+ *
+ *  Based on drivers/char/serial.c, by Linus Torvalds, Theodore Ts'o.
+ *
+ *  This program is free software; you can redistribute it and/or modify it
+ *  under the terms of the GNU General Public License version 2 as published
+ *  by the Free Software Foundation.
+ */
+
+#include <linux/module.h>
+#include <linux/ioport.h>
+#include <linux/init.h>
+#include <linux/console.h>
+#include <linux/sysrq.h>
+#include <linux/delay.h>
+#include <linux/platform_device.h>
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
+#include <linux/serial_core.h>
+#include <linux/serial.h>
+#include <linux/slab.h>
+#include <linux/io.h>
+#include <linux/irq.h>
+
+#include <asm/mach-ath79/ar933x_uart.h>
+#include <asm/mach-ath79/ar933x_uart_platform.h>
+
+#define DRIVER_NAME "ar933x-uart"
+
+#define AR933X_DUMMY_STATUS_RD	0x01
+
+static struct uart_driver ar933x_uart_driver;
+
+struct ar933x_uart_port {
+	struct uart_port	port;
+	unsigned int		ier;	/* shadow Interrupt Enable Register */
+};
+
+static inline unsigned int ar933x_uart_read(struct ar933x_uart_port *up,
+					    int offset)
+{
+	return readl(up->port.membase + offset);
+}
+
+static inline void ar933x_uart_write(struct ar933x_uart_port *up,
+				     int offset, unsigned int value)
+{
+	writel(value, up->port.membase + offset);
+}
+
+static inline void ar933x_uart_rmw(struct ar933x_uart_port *up,
+				  unsigned int offset,
+				  unsigned int mask,
+				  unsigned int val)
+{
+	unsigned int t;
+
+	t = ar933x_uart_read(up, offset);
+	t &= ~mask;
+	t |= val;
+	ar933x_uart_write(up, offset, t);
+}
+
+static inline void ar933x_uart_rmw_set(struct ar933x_uart_port *up,
+				       unsigned int offset,
+				       unsigned int val)
+{
+	ar933x_uart_rmw(up, offset, 0, val);
+}
+
+static inline void ar933x_uart_rmw_clear(struct ar933x_uart_port *up,
+					 unsigned int offset,
+					 unsigned int val)
+{
+	ar933x_uart_rmw(up, offset, val, 0);
+}
+
+static inline void ar933x_uart_start_tx_interrupt(struct ar933x_uart_port *up)
+{
+	up->ier |= AR933X_UART_INT_TX_EMPTY;
+	ar933x_uart_write(up, AR933X_UART_INT_EN_REG, up->ier);
+}
+
+static inline void ar933x_uart_stop_tx_interrupt(struct ar933x_uart_port *up)
+{
+	up->ier &= ~AR933X_UART_INT_TX_EMPTY;
+	ar933x_uart_write(up, AR933X_UART_INT_EN_REG, up->ier);
+}
+
+static inline void ar933x_uart_putc(struct ar933x_uart_port *up, int ch)
+{
+	unsigned int rdata;
+
+	rdata = ch & AR933X_UART_DATA_TX_RX_MASK;
+	rdata |= AR933X_UART_DATA_TX_CSR;
+	ar933x_uart_write(up, AR933X_UART_DATA_REG, rdata);
+}
+
+static unsigned int ar933x_uart_tx_empty(struct uart_port *port)
+{
+	struct ar933x_uart_port *up = (struct ar933x_uart_port *) port;
+	unsigned long flags;
+	unsigned int rdata;
+
+	spin_lock_irqsave(&up->port.lock, flags);
+	rdata = ar933x_uart_read(up, AR933X_UART_DATA_REG);
+	spin_unlock_irqrestore(&up->port.lock, flags);
+
+	return (rdata & AR933X_UART_DATA_TX_CSR) ? 0 : TIOCSER_TEMT;
+}
+
+static unsigned int ar933x_uart_get_mctrl(struct uart_port *port)
+{
+	return TIOCM_CAR;
+}
+
+static void ar933x_uart_set_mctrl(struct uart_port *port, unsigned int mctrl)
+{
+}
+
+static void ar933x_uart_start_tx(struct uart_port *port)
+{
+	struct ar933x_uart_port *up = (struct ar933x_uart_port *) port;
+
+	ar933x_uart_start_tx_interrupt(up);
+}
+
+static void ar933x_uart_stop_tx(struct uart_port *port)
+{
+	struct ar933x_uart_port *up = (struct ar933x_uart_port *) port;
+
+	ar933x_uart_stop_tx_interrupt(up);
+}
+
+static void ar933x_uart_stop_rx(struct uart_port *port)
+{
+	struct ar933x_uart_port *up = (struct ar933x_uart_port *) port;
+
+	up->ier &= ~AR933X_UART_INT_RX_VALID;
+	ar933x_uart_write(up, AR933X_UART_INT_EN_REG, up->ier);
+}
+
+static void ar933x_uart_break_ctl(struct uart_port *port, int break_state)
+{
+	struct ar933x_uart_port *up = (struct ar933x_uart_port *) port;
+	unsigned long flags;
+
+	spin_lock_irqsave(&up->port.lock, flags);
+	if (break_state == -1)
+		ar933x_uart_rmw_set(up, AR933X_UART_CS_REG,
+				    AR933X_UART_CS_TX_BREAK);
+	else
+		ar933x_uart_rmw_clear(up, AR933X_UART_CS_REG,
+				      AR933X_UART_CS_TX_BREAK);
+	spin_unlock_irqrestore(&up->port.lock, flags);
+}
+
+static void ar933x_uart_enable_ms(struct uart_port *port)
+{
+}
+
+static void ar933x_uart_set_termios(struct uart_port *port,
+				    struct ktermios *new,
+				    struct ktermios *old)
+{
+	struct ar933x_uart_port *up = (struct ar933x_uart_port *) port;
+	unsigned int cs;
+	unsigned long flags;
+	unsigned int baud, scale;
+
+	/* Only CS8 is supported */
+	new->c_cflag &= ~CSIZE;
+	new->c_cflag |= CS8;
+
+	/* Only one stop bit is supported */
+	new->c_cflag &= ~CSTOPB;
+
+	cs = 0;
+	if (new->c_cflag & PARENB) {
+		if (!(new->c_cflag & PARODD))
+			cs |= AR933X_UART_CS_PARITY_EVEN;
+		else
+			cs |= AR933X_UART_CS_PARITY_ODD;
+	} else {
+		cs |= AR933X_UART_CS_PARITY_NONE;
+	}
+
+	/* Mark/space parity is not supported */
+	new->c_cflag &= ~CMSPAR;
+
+	baud = uart_get_baud_rate(port, new, old, 0, port->uartclk / 16);
+	scale = (port->uartclk / (16 * baud)) - 1;
+
+	/*
+	 * 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, new->c_cflag, baud);
+
+	up->port.ignore_status_mask = 0;
+
+	/* ignore all characters if CREAD is not set */
+	if ((new->c_cflag & CREAD) == 0)
+		up->port.ignore_status_mask |= AR933X_DUMMY_STATUS_RD;
+
+	ar933x_uart_write(up, AR933X_UART_CLOCK_REG,
+			  scale << AR933X_UART_CLOCK_SCALE_S | 8192);
+
+	/* setup configuration register */
+	ar933x_uart_rmw(up, AR933X_UART_CS_REG, AR933X_UART_CS_PARITY_M, cs);
+
+	/* enable host interrupt */
+	ar933x_uart_rmw_set(up, AR933X_UART_CS_REG,
+			    AR933X_UART_CS_HOST_INT_EN);
+
+	spin_unlock_irqrestore(&up->port.lock, flags);
+
+	if (tty_termios_baud_rate(new))
+		tty_termios_encode_baud_rate(new, baud, baud);
+}
+
+static void ar933x_uart_rx_chars(struct ar933x_uart_port *up)
+{
+	struct tty_struct *tty;
+	int max_count = 256;
+
+	tty = tty_port_tty_get(&up->port.state->port);
+	do {
+		unsigned int rdata;
+		unsigned char ch;
+
+		rdata = ar933x_uart_read(up, AR933X_UART_DATA_REG);
+		if ((rdata & AR933X_UART_DATA_RX_CSR) == 0)
+			break;
+
+		/* remove the character from the FIFO */
+		ar933x_uart_write(up, AR933X_UART_DATA_REG,
+				  AR933X_UART_DATA_RX_CSR);
+
+		if (!tty) {
+			/* discard the data if no tty available */
+			continue;
+		}
+
+		up->port.icount.rx++;
+		ch = rdata & AR933X_UART_DATA_TX_RX_MASK;
+
+		if (uart_handle_sysrq_char(&up->port, ch))
+			continue;
+
+		if ((up->port.ignore_status_mask & AR933X_DUMMY_STATUS_RD) == 0)
+			tty_insert_flip_char(tty, ch, TTY_NORMAL);
+	} while (max_count-- > 0);
+
+	if (tty) {
+		tty_flip_buffer_push(tty);
+		tty_kref_put(tty);
+	}
+}
+
+static void ar933x_uart_tx_chars(struct ar933x_uart_port *up)
+{
+	struct circ_buf *xmit = &up->port.state->xmit;
+	int count;
+
+	if (uart_tx_stopped(&up->port))
+		return;
+
+	count = up->port.fifosize;
+	do {
+		unsigned int rdata;
+
+		rdata = ar933x_uart_read(up, AR933X_UART_DATA_REG);
+		if ((rdata & AR933X_UART_DATA_TX_CSR) == 0)
+			break;
+
+		if (up->port.x_char) {
+			ar933x_uart_putc(up, up->port.x_char);
+			up->port.icount.tx++;
+			up->port.x_char = 0;
+			continue;
+		}
+
+		if (uart_circ_empty(xmit))
+			break;
+
+		ar933x_uart_putc(up, xmit->buf[xmit->tail]);
+
+		xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
+		up->port.icount.tx++;
+	} while (--count > 0);
+
+	if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
+		uart_write_wakeup(&up->port);
+
+	if (!uart_circ_empty(xmit))
+		ar933x_uart_start_tx_interrupt(up);
+}
+
+static irqreturn_t ar933x_uart_interrupt(int irq, void *dev_id)
+{
+	struct ar933x_uart_port *up = dev_id;
+	unsigned int status;
+
+	status = ar933x_uart_read(up, AR933X_UART_CS_REG);
+	if ((status & AR933X_UART_CS_HOST_INT) == 0)
+		return IRQ_NONE;
+
+	spin_lock(&up->port.lock);
+
+	status = ar933x_uart_read(up, AR933X_UART_INT_REG);
+	status &= ar933x_uart_read(up, AR933X_UART_INT_EN_REG);
+
+	if (status & AR933X_UART_INT_RX_VALID) {
+		ar933x_uart_write(up, AR933X_UART_INT_REG,
+				  AR933X_UART_INT_RX_VALID);
+		ar933x_uart_rx_chars(up);
+	}
+
+	if (status & AR933X_UART_INT_TX_EMPTY) {
+		ar933x_uart_write(up, AR933X_UART_INT_REG,
+				  AR933X_UART_INT_TX_EMPTY);
+		ar933x_uart_stop_tx_interrupt(up);
+		ar933x_uart_tx_chars(up);
+	}
+
+	spin_unlock(&up->port.lock);
+
+	return IRQ_HANDLED;
+}
+
+static int ar933x_uart_startup(struct uart_port *port)
+{
+	struct ar933x_uart_port *up = (struct ar933x_uart_port *) port;
+	unsigned long flags;
+	int ret;
+
+	ret = request_irq(up->port.irq, ar933x_uart_interrupt,
+			  up->port.irqflags, dev_name(up->port.dev), up);
+	if (ret)
+		return ret;
+
+	spin_lock_irqsave(&up->port.lock, flags);
+
+	/* Enable HOST interrupts */
+	ar933x_uart_rmw_set(up, AR933X_UART_CS_REG,
+			    AR933X_UART_CS_HOST_INT_EN);
+
+	/* Enable RX interrupts */
+	up->ier = AR933X_UART_INT_RX_VALID;
+	ar933x_uart_write(up, AR933X_UART_INT_EN_REG, up->ier);
+
+	spin_unlock_irqrestore(&up->port.lock, flags);
+
+	return 0;
+}
+
+static void ar933x_uart_shutdown(struct uart_port *port)
+{
+	struct ar933x_uart_port *up = (struct ar933x_uart_port *) port;
+
+	/* Disable all interrupts */
+	up->ier = 0;
+	ar933x_uart_write(up, AR933X_UART_INT_EN_REG, up->ier);
+
+	/* Disable break condition */
+	ar933x_uart_rmw_clear(up, AR933X_UART_CS_REG,
+			      AR933X_UART_CS_TX_BREAK);
+
+	free_irq(up->port.irq, up);
+}
+
+static const char *ar933x_uart_type(struct uart_port *port)
+{
+	return (port->type == PORT_AR933X) ? "AR933X UART" : NULL;
+}
+
+static void ar933x_uart_release_port(struct uart_port *port)
+{
+	/* Nothing to release ... */
+}
+
+static int ar933x_uart_request_port(struct uart_port *port)
+{
+	/* UARTs always present */
+	return 0;
+}
+
+static void ar933x_uart_config_port(struct uart_port *port, int flags)
+{
+	if (flags & UART_CONFIG_TYPE)
+		port->type = PORT_AR933X;
+}
+
+static int ar933x_uart_verify_port(struct uart_port *port,
+				   struct serial_struct *ser)
+{
+	if (ser->type != PORT_UNKNOWN &&
+	    ser->type != PORT_AR933X)
+		return -EINVAL;
+
+	if (ser->irq < 0 || ser->irq >= NR_IRQS)
+		return -EINVAL;
+
+	if (ser->baud_base < 28800)
+		return -EINVAL;
+
+	return 0;
+}
+
+static struct uart_ops ar933x_uart_ops = {
+	.tx_empty	= ar933x_uart_tx_empty,
+	.set_mctrl	= ar933x_uart_set_mctrl,
+	.get_mctrl	= ar933x_uart_get_mctrl,
+	.stop_tx	= ar933x_uart_stop_tx,
+	.start_tx	= ar933x_uart_start_tx,
+	.stop_rx	= ar933x_uart_stop_rx,
+	.enable_ms	= ar933x_uart_enable_ms,
+	.break_ctl	= ar933x_uart_break_ctl,
+	.startup	= ar933x_uart_startup,
+	.shutdown	= ar933x_uart_shutdown,
+	.set_termios	= ar933x_uart_set_termios,
+	.type		= ar933x_uart_type,
+	.release_port	= ar933x_uart_release_port,
+	.request_port	= ar933x_uart_request_port,
+	.config_port	= ar933x_uart_config_port,
+	.verify_port	= ar933x_uart_verify_port,
+};
+
+#ifdef CONFIG_SERIAL_AR933X_CONSOLE
+
+static struct ar933x_uart_port *
+ar933x_console_ports[CONFIG_SERIAL_AR933X_NR_UARTS];
+
+static void ar933x_uart_wait_xmitr(struct ar933x_uart_port *up)
+{
+	unsigned int status;
+	unsigned int timeout = 60000;
+
+	/* Wait up to 60ms for the character(s) to be sent. */
+	do {
+		status = ar933x_uart_read(up, AR933X_UART_DATA_REG);
+		if (--timeout == 0)
+			break;
+		udelay(1);
+	} while ((status & AR933X_UART_DATA_TX_CSR) == 0);
+}
+
+static void ar933x_uart_console_putchar(struct uart_port *port, int ch)
+{
+	struct ar933x_uart_port *up = (struct ar933x_uart_port *) port;
+
+	ar933x_uart_wait_xmitr(up);
+	ar933x_uart_putc(up, ch);
+}
+
+static void ar933x_uart_console_write(struct console *co, const char *s,
+				      unsigned int count)
+{
+	struct ar933x_uart_port *up = ar933x_console_ports[co->index];
+	unsigned long flags;
+	unsigned int int_en;
+	int locked = 1;
+
+	local_irq_save(flags);
+
+	if (up->port.sysrq)
+		locked = 0;
+	else if (oops_in_progress)
+		locked = spin_trylock(&up->port.lock);
+	else
+		spin_lock(&up->port.lock);
+
+	/*
+	 * First save the IER then disable the interrupts
+	 */
+	int_en = ar933x_uart_read(up, AR933X_UART_INT_EN_REG);
+	ar933x_uart_write(up, AR933X_UART_INT_EN_REG, 0);
+
+	uart_console_write(&up->port, s, count, ar933x_uart_console_putchar);
+
+	/*
+	 * Finally, wait for transmitter to become empty
+	 * and restore the IER
+	 */
+	ar933x_uart_wait_xmitr(up);
+	ar933x_uart_write(up, AR933X_UART_INT_EN_REG, int_en);
+
+	ar933x_uart_write(up, AR933X_UART_INT_REG, AR933X_UART_INT_ALLINTS);
+
+	if (locked)
+		spin_unlock(&up->port.lock);
+
+	local_irq_restore(flags);
+}
+
+static int ar933x_uart_console_setup(struct console *co, char *options)
+{
+	struct ar933x_uart_port *up;
+	int baud = 115200;
+	int bits = 8;
+	int parity = 'n';
+	int flow = 'n';
+
+	if (co->index < 0 || co->index >= CONFIG_SERIAL_AR933X_NR_UARTS)
+		return -EINVAL;
+
+	up = ar933x_console_ports[co->index];
+	if (!up)
+		return -ENODEV;
+
+	if (options)
+		uart_parse_options(options, &baud, &parity, &bits, &flow);
+
+	return uart_set_options(&up->port, co, baud, parity, bits, flow);
+}
+
+static struct console ar933x_uart_console = {
+	.name		= "ttyATH",
+	.write		= ar933x_uart_console_write,
+	.device		= uart_console_device,
+	.setup		= ar933x_uart_console_setup,
+	.flags		= CON_PRINTBUFFER,
+	.index		= -1,
+	.data		= &ar933x_uart_driver,
+};
+
+static void ar933x_uart_add_console_port(struct ar933x_uart_port *up)
+{
+	ar933x_console_ports[up->port.line] = up;
+}
+
+#define AR933X_SERIAL_CONSOLE	(&ar933x_uart_console)
+
+#else
+
+static inline void ar933x_uart_add_console_port(struct ar933x_uart_port *up) {}
+
+#define AR933X_SERIAL_CONSOLE	NULL
+
+#endif /* CONFIG_SERIAL_AR933X_CONSOLE */
+
+static struct uart_driver ar933x_uart_driver = {
+	.owner		= THIS_MODULE,
+	.driver_name	= DRIVER_NAME,
+	.dev_name	= "ttyATH",
+	.nr		= CONFIG_SERIAL_AR933X_NR_UARTS,
+	.cons		= AR933X_SERIAL_CONSOLE,
+};
+
+static int __devinit ar933x_uart_probe(struct platform_device *pdev)
+{
+	struct ar933x_uart_platform_data *pdata;
+	struct ar933x_uart_port *up;
+	struct uart_port *port;
+	struct resource *mem_res;
+	struct resource *irq_res;
+	int id;
+	int ret;
+
+	pdata = pdev->dev.platform_data;
+	if (!pdata)
+		return -EINVAL;
+
+	id = pdev->id;
+	if (id == -1)
+		id = 0;
+
+	if (id > CONFIG_SERIAL_AR933X_NR_UARTS)
+		return -EINVAL;
+
+	mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!mem_res) {
+		dev_err(&pdev->dev, "no MEM resource\n");
+		return -EINVAL;
+	}
+
+	irq_res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+	if (!irq_res) {
+		dev_err(&pdev->dev, "no IRQ resource\n");
+		return -EINVAL;
+	}
+
+	up = kzalloc(sizeof(struct ar933x_uart_port), GFP_KERNEL);
+	if (!up)
+		return -ENOMEM;
+
+	port = &up->port;
+	port->mapbase = mem_res->start;
+
+	port->membase = ioremap(mem_res->start, AR933X_UART_REGS_SIZE);
+	if (!port->membase) {
+		ret = -ENOMEM;
+		goto err_free_up;
+	}
+
+	port->line = id;
+	port->irq = irq_res->start;
+	port->dev = &pdev->dev;
+	port->type = PORT_AR933X;
+	port->iotype = UPIO_MEM32;
+	port->uartclk = pdata->uartclk;
+
+	port->regshift = 2;
+	port->fifosize = AR933X_UART_FIFO_SIZE;
+	port->ops = &ar933x_uart_ops;
+
+	ar933x_uart_add_console_port(up);
+
+	ret = uart_add_one_port(&ar933x_uart_driver, &up->port);
+	if (ret)
+		goto err_unmap;
+
+	platform_set_drvdata(pdev, up);
+	return 0;
+
+err_unmap:
+	iounmap(up->port.membase);
+err_free_up:
+	kfree(up);
+	return ret;
+}
+
+static int __devexit ar933x_uart_remove(struct platform_device *pdev)
+{
+	struct ar933x_uart_port *up;
+
+	up = platform_get_drvdata(pdev);
+	platform_set_drvdata(pdev, NULL);
+
+	if (up) {
+		uart_remove_one_port(&ar933x_uart_driver, &up->port);
+		iounmap(up->port.membase);
+		kfree(up);
+	}
+
+	return 0;
+}
+
+static struct platform_driver ar933x_uart_platform_driver = {
+	.probe		= ar933x_uart_probe,
+	.remove		= __devexit_p(ar933x_uart_remove),
+	.driver		= {
+		.name		= DRIVER_NAME,
+		.owner		= THIS_MODULE,
+	},
+};
+
+static int __init ar933x_uart_init(void)
+{
+	int ret;
+
+	ar933x_uart_driver.nr = CONFIG_SERIAL_AR933X_NR_UARTS;
+	ret = uart_register_driver(&ar933x_uart_driver);
+	if (ret)
+		goto err_out;
+
+	ret = platform_driver_register(&ar933x_uart_platform_driver);
+	if (ret)
+		goto err_unregister_uart_driver;
+
+	return 0;
+
+err_unregister_uart_driver:
+	uart_unregister_driver(&ar933x_uart_driver);
+err_out:
+	return ret;
+}
+
+static void __exit ar933x_uart_exit(void)
+{
+	platform_driver_unregister(&ar933x_uart_platform_driver);
+	uart_unregister_driver(&ar933x_uart_driver);
+}
+
+module_init(ar933x_uart_init);
+module_exit(ar933x_uart_exit);
+
+MODULE_DESCRIPTION("Atheros AR933X UART driver");
+MODULE_AUTHOR("Gabor Juhos <juhosg@openwrt.org>");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:" DRIVER_NAME);
diff --git a/ap/os/linux/linux-3.4.x/drivers/tty/serial/atmel_serial.c b/ap/os/linux/linux-3.4.x/drivers/tty/serial/atmel_serial.c
new file mode 100644
index 0000000..85c28e3
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/tty/serial/atmel_serial.c
@@ -0,0 +1,1916 @@
+/*
+ *  Driver for Atmel AT91 / AT32 Serial ports
+ *  Copyright (C) 2003 Rick Bronson
+ *
+ *  Based on drivers/char/serial_sa1100.c, by Deep Blue Solutions Ltd.
+ *  Based on drivers/char/serial.c, by Linus Torvalds, Theodore Ts'o.
+ *
+ *  DMA support added by Chip Coldwell.
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+#include <linux/module.h>
+#include <linux/tty.h>
+#include <linux/ioport.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <linux/serial.h>
+#include <linux/clk.h>
+#include <linux/console.h>
+#include <linux/sysrq.h>
+#include <linux/tty_flip.h>
+#include <linux/platform_device.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/dma-mapping.h>
+#include <linux/atmel_pdc.h>
+#include <linux/atmel_serial.h>
+#include <linux/uaccess.h>
+
+#include <asm/io.h>
+#include <asm/ioctls.h>
+
+#include <asm/mach/serial_at91.h>
+#include <mach/board.h>
+
+#ifdef CONFIG_ARM
+#include <mach/cpu.h>
+#include <asm/gpio.h>
+#endif
+
+#define PDC_BUFFER_SIZE		512
+/* Revisit: We should calculate this based on the actual port settings */
+#define PDC_RX_TIMEOUT		(3 * 10)		/* 3 bytes */
+
+#if defined(CONFIG_SERIAL_ATMEL_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ)
+#define SUPPORT_SYSRQ
+#endif
+
+#include <linux/serial_core.h>
+
+static void atmel_start_rx(struct uart_port *port);
+static void atmel_stop_rx(struct uart_port *port);
+
+#ifdef CONFIG_SERIAL_ATMEL_TTYAT
+
+/* Use device name ttyAT, major 204 and minor 154-169.  This is necessary if we
+ * should coexist with the 8250 driver, such as if we have an external 16C550
+ * UART. */
+#define SERIAL_ATMEL_MAJOR	204
+#define MINOR_START		154
+#define ATMEL_DEVICENAME	"ttyAT"
+
+#else
+
+/* Use device name ttyS, major 4, minor 64-68.  This is the usual serial port
+ * name, but it is legally reserved for the 8250 driver. */
+#define SERIAL_ATMEL_MAJOR	TTY_MAJOR
+#define MINOR_START		64
+#define ATMEL_DEVICENAME	"ttyS"
+
+#endif
+
+#define ATMEL_ISR_PASS_LIMIT	256
+
+/* UART registers. CR is write-only, hence no GET macro */
+#define UART_PUT_CR(port,v)	__raw_writel(v, (port)->membase + ATMEL_US_CR)
+#define UART_GET_MR(port)	__raw_readl((port)->membase + ATMEL_US_MR)
+#define UART_PUT_MR(port,v)	__raw_writel(v, (port)->membase + ATMEL_US_MR)
+#define UART_PUT_IER(port,v)	__raw_writel(v, (port)->membase + ATMEL_US_IER)
+#define UART_PUT_IDR(port,v)	__raw_writel(v, (port)->membase + ATMEL_US_IDR)
+#define UART_GET_IMR(port)	__raw_readl((port)->membase + ATMEL_US_IMR)
+#define UART_GET_CSR(port)	__raw_readl((port)->membase + ATMEL_US_CSR)
+#define UART_GET_CHAR(port)	__raw_readl((port)->membase + ATMEL_US_RHR)
+#define UART_PUT_CHAR(port,v)	__raw_writel(v, (port)->membase + ATMEL_US_THR)
+#define UART_GET_BRGR(port)	__raw_readl((port)->membase + ATMEL_US_BRGR)
+#define UART_PUT_BRGR(port,v)	__raw_writel(v, (port)->membase + ATMEL_US_BRGR)
+#define UART_PUT_RTOR(port,v)	__raw_writel(v, (port)->membase + ATMEL_US_RTOR)
+#define UART_PUT_TTGR(port, v)	__raw_writel(v, (port)->membase + ATMEL_US_TTGR)
+
+ /* PDC registers */
+#define UART_PUT_PTCR(port,v)	__raw_writel(v, (port)->membase + ATMEL_PDC_PTCR)
+#define UART_GET_PTSR(port)	__raw_readl((port)->membase + ATMEL_PDC_PTSR)
+
+#define UART_PUT_RPR(port,v)	__raw_writel(v, (port)->membase + ATMEL_PDC_RPR)
+#define UART_GET_RPR(port)	__raw_readl((port)->membase + ATMEL_PDC_RPR)
+#define UART_PUT_RCR(port,v)	__raw_writel(v, (port)->membase + ATMEL_PDC_RCR)
+#define UART_PUT_RNPR(port,v)	__raw_writel(v, (port)->membase + ATMEL_PDC_RNPR)
+#define UART_PUT_RNCR(port,v)	__raw_writel(v, (port)->membase + ATMEL_PDC_RNCR)
+
+#define UART_PUT_TPR(port,v)	__raw_writel(v, (port)->membase + ATMEL_PDC_TPR)
+#define UART_PUT_TCR(port,v)	__raw_writel(v, (port)->membase + ATMEL_PDC_TCR)
+#define UART_GET_TCR(port)	__raw_readl((port)->membase + ATMEL_PDC_TCR)
+
+static int (*atmel_open_hook)(struct uart_port *);
+static void (*atmel_close_hook)(struct uart_port *);
+
+struct atmel_dma_buffer {
+	unsigned char	*buf;
+	dma_addr_t	dma_addr;
+	unsigned int	dma_size;
+	unsigned int	ofs;
+};
+
+struct atmel_uart_char {
+	u16		status;
+	u16		ch;
+};
+
+#define ATMEL_SERIAL_RINGSIZE 1024
+
+/*
+ * We wrap our port structure around the generic uart_port.
+ */
+struct atmel_uart_port {
+	struct uart_port	uart;		/* uart */
+	struct clk		*clk;		/* uart clock */
+	int			may_wakeup;	/* cached value of device_may_wakeup for times we need to disable it */
+	u32			backup_imr;	/* IMR saved during suspend */
+	int			break_active;	/* break being received */
+
+	short			use_dma_rx;	/* enable PDC receiver */
+	short			pdc_rx_idx;	/* current PDC RX buffer */
+	struct atmel_dma_buffer	pdc_rx[2];	/* PDC receier */
+
+	short			use_dma_tx;	/* enable PDC transmitter */
+	struct atmel_dma_buffer	pdc_tx;		/* PDC transmitter */
+
+	struct tasklet_struct	tasklet;
+	unsigned int		irq_status;
+	unsigned int		irq_status_prev;
+
+	struct circ_buf		rx_ring;
+
+	struct serial_rs485	rs485;		/* rs485 settings */
+	unsigned int		tx_done_mask;
+};
+
+static struct atmel_uart_port atmel_ports[ATMEL_MAX_UART];
+static DECLARE_BITMAP(atmel_ports_in_use, ATMEL_MAX_UART);
+
+#ifdef SUPPORT_SYSRQ
+static struct console atmel_console;
+#endif
+
+#if defined(CONFIG_OF)
+static const struct of_device_id atmel_serial_dt_ids[] = {
+	{ .compatible = "atmel,at91rm9200-usart" },
+	{ .compatible = "atmel,at91sam9260-usart" },
+	{ /* sentinel */ }
+};
+
+MODULE_DEVICE_TABLE(of, atmel_serial_dt_ids);
+#endif
+
+static inline struct atmel_uart_port *
+to_atmel_uart_port(struct uart_port *uart)
+{
+	return container_of(uart, struct atmel_uart_port, uart);
+}
+
+#ifdef CONFIG_SERIAL_ATMEL_PDC
+static bool atmel_use_dma_rx(struct uart_port *port)
+{
+	struct atmel_uart_port *atmel_port = to_atmel_uart_port(port);
+
+	return atmel_port->use_dma_rx;
+}
+
+static bool atmel_use_dma_tx(struct uart_port *port)
+{
+	struct atmel_uart_port *atmel_port = to_atmel_uart_port(port);
+
+	return atmel_port->use_dma_tx;
+}
+#else
+static bool atmel_use_dma_rx(struct uart_port *port)
+{
+	return false;
+}
+
+static bool atmel_use_dma_tx(struct uart_port *port)
+{
+	return false;
+}
+#endif
+
+/* Enable or disable the rs485 support */
+void atmel_config_rs485(struct uart_port *port, struct serial_rs485 *rs485conf)
+{
+	struct atmel_uart_port *atmel_port = to_atmel_uart_port(port);
+	unsigned int mode;
+	unsigned long flags;
+
+	spin_lock_irqsave(&port->lock, flags);
+
+	/* Disable interrupts */
+	UART_PUT_IDR(port, atmel_port->tx_done_mask);
+
+	mode = UART_GET_MR(port);
+
+	/* Resetting serial mode to RS232 (0x0) */
+	mode &= ~ATMEL_US_USMODE;
+
+	atmel_port->rs485 = *rs485conf;
+
+	if (rs485conf->flags & SER_RS485_ENABLED) {
+		dev_dbg(port->dev, "Setting UART to RS485\n");
+		atmel_port->tx_done_mask = ATMEL_US_TXEMPTY;
+		UART_PUT_TTGR(port, rs485conf->delay_rts_after_send);
+		mode |= ATMEL_US_USMODE_RS485;
+	} else {
+		dev_dbg(port->dev, "Setting UART to RS232\n");
+		if (atmel_use_dma_tx(port))
+			atmel_port->tx_done_mask = ATMEL_US_ENDTX |
+				ATMEL_US_TXBUFE;
+		else
+			atmel_port->tx_done_mask = ATMEL_US_TXRDY;
+	}
+	UART_PUT_MR(port, mode);
+
+	/* Enable interrupts */
+	UART_PUT_IER(port, atmel_port->tx_done_mask);
+
+	spin_unlock_irqrestore(&port->lock, flags);
+
+}
+
+/*
+ * Return TIOCSER_TEMT when transmitter FIFO and Shift register is empty.
+ */
+static u_int atmel_tx_empty(struct uart_port *port)
+{
+	return (UART_GET_CSR(port) & ATMEL_US_TXEMPTY) ? TIOCSER_TEMT : 0;
+}
+
+/*
+ * Set state of the modem control output lines
+ */
+static void atmel_set_mctrl(struct uart_port *port, u_int mctrl)
+{
+	unsigned int control = 0;
+	unsigned int mode;
+	struct atmel_uart_port *atmel_port = to_atmel_uart_port(port);
+
+#ifdef CONFIG_ARCH_AT91RM9200
+	if (cpu_is_at91rm9200()) {
+		/*
+		 * AT91RM9200 Errata #39: RTS0 is not internally connected
+		 * to PA21. We need to drive the pin manually.
+		 */
+		if (port->mapbase == AT91RM9200_BASE_US0) {
+			if (mctrl & TIOCM_RTS)
+				at91_set_gpio_value(AT91_PIN_PA21, 0);
+			else
+				at91_set_gpio_value(AT91_PIN_PA21, 1);
+		}
+	}
+#endif
+
+	if (mctrl & TIOCM_RTS)
+		control |= ATMEL_US_RTSEN;
+	else
+		control |= ATMEL_US_RTSDIS;
+
+	if (mctrl & TIOCM_DTR)
+		control |= ATMEL_US_DTREN;
+	else
+		control |= ATMEL_US_DTRDIS;
+
+	UART_PUT_CR(port, control);
+
+	/* Local loopback mode? */
+	mode = UART_GET_MR(port) & ~ATMEL_US_CHMODE;
+	if (mctrl & TIOCM_LOOP)
+		mode |= ATMEL_US_CHMODE_LOC_LOOP;
+	else
+		mode |= ATMEL_US_CHMODE_NORMAL;
+
+	/* Resetting serial mode to RS232 (0x0) */
+	mode &= ~ATMEL_US_USMODE;
+
+	if (atmel_port->rs485.flags & SER_RS485_ENABLED) {
+		dev_dbg(port->dev, "Setting UART to RS485\n");
+		UART_PUT_TTGR(port, atmel_port->rs485.delay_rts_after_send);
+		mode |= ATMEL_US_USMODE_RS485;
+	} else {
+		dev_dbg(port->dev, "Setting UART to RS232\n");
+	}
+	UART_PUT_MR(port, mode);
+}
+
+/*
+ * Get state of the modem control input lines
+ */
+static u_int atmel_get_mctrl(struct uart_port *port)
+{
+	unsigned int status, ret = 0;
+
+	status = UART_GET_CSR(port);
+
+	/*
+	 * The control signals are active low.
+	 */
+	if (!(status & ATMEL_US_DCD))
+		ret |= TIOCM_CD;
+	if (!(status & ATMEL_US_CTS))
+		ret |= TIOCM_CTS;
+	if (!(status & ATMEL_US_DSR))
+		ret |= TIOCM_DSR;
+	if (!(status & ATMEL_US_RI))
+		ret |= TIOCM_RI;
+
+	return ret;
+}
+
+/*
+ * Stop transmitting.
+ */
+static void atmel_stop_tx(struct uart_port *port)
+{
+	struct atmel_uart_port *atmel_port = to_atmel_uart_port(port);
+
+	if (atmel_use_dma_tx(port)) {
+		/* disable PDC transmit */
+		UART_PUT_PTCR(port, ATMEL_PDC_TXTDIS);
+	}
+	/* Disable interrupts */
+	UART_PUT_IDR(port, atmel_port->tx_done_mask);
+
+	if ((atmel_port->rs485.flags & SER_RS485_ENABLED) &&
+	    !(atmel_port->rs485.flags & SER_RS485_RX_DURING_TX))
+		atmel_start_rx(port);
+}
+
+/*
+ * Start transmitting.
+ */
+static void atmel_start_tx(struct uart_port *port)
+{
+	struct atmel_uart_port *atmel_port = to_atmel_uart_port(port);
+
+	if (atmel_use_dma_tx(port)) {
+		if (UART_GET_PTSR(port) & ATMEL_PDC_TXTEN)
+			/* The transmitter is already running.  Yes, we
+			   really need this.*/
+			return;
+
+		if ((atmel_port->rs485.flags & SER_RS485_ENABLED) &&
+		    !(atmel_port->rs485.flags & SER_RS485_RX_DURING_TX))
+			atmel_stop_rx(port);
+
+		/* re-enable PDC transmit */
+		UART_PUT_PTCR(port, ATMEL_PDC_TXTEN);
+	}
+	/* Enable interrupts */
+	UART_PUT_IER(port, atmel_port->tx_done_mask);
+}
+
+/*
+ * start receiving - port is in process of being opened.
+ */
+static void atmel_start_rx(struct uart_port *port)
+{
+	UART_PUT_CR(port, ATMEL_US_RSTSTA);  /* reset status and receiver */
+
+	UART_PUT_CR(port, ATMEL_US_RXEN);
+
+	if (atmel_use_dma_rx(port)) {
+		/* enable PDC controller */
+		UART_PUT_IER(port, ATMEL_US_ENDRX | ATMEL_US_TIMEOUT |
+			port->read_status_mask);
+		UART_PUT_PTCR(port, ATMEL_PDC_RXTEN);
+	} else {
+		UART_PUT_IER(port, ATMEL_US_RXRDY);
+	}
+}
+
+/*
+ * Stop receiving - port is in process of being closed.
+ */
+static void atmel_stop_rx(struct uart_port *port)
+{
+	UART_PUT_CR(port, ATMEL_US_RXDIS);
+
+	if (atmel_use_dma_rx(port)) {
+		/* disable PDC receive */
+		UART_PUT_PTCR(port, ATMEL_PDC_RXTDIS);
+		UART_PUT_IDR(port, ATMEL_US_ENDRX | ATMEL_US_TIMEOUT |
+			port->read_status_mask);
+	} else {
+		UART_PUT_IDR(port, ATMEL_US_RXRDY);
+	}
+}
+
+/*
+ * Enable modem status interrupts
+ */
+static void atmel_enable_ms(struct uart_port *port)
+{
+	UART_PUT_IER(port, ATMEL_US_RIIC | ATMEL_US_DSRIC
+			| ATMEL_US_DCDIC | ATMEL_US_CTSIC);
+}
+
+/*
+ * Control the transmission of a break signal
+ */
+static void atmel_break_ctl(struct uart_port *port, int break_state)
+{
+	if (break_state != 0)
+		UART_PUT_CR(port, ATMEL_US_STTBRK);	/* start break */
+	else
+		UART_PUT_CR(port, ATMEL_US_STPBRK);	/* stop break */
+}
+
+/*
+ * Stores the incoming character in the ring buffer
+ */
+static void
+atmel_buffer_rx_char(struct uart_port *port, unsigned int status,
+		     unsigned int ch)
+{
+	struct atmel_uart_port *atmel_port = to_atmel_uart_port(port);
+	struct circ_buf *ring = &atmel_port->rx_ring;
+	struct atmel_uart_char *c;
+
+	if (!CIRC_SPACE(ring->head, ring->tail, ATMEL_SERIAL_RINGSIZE))
+		/* Buffer overflow, ignore char */
+		return;
+
+	c = &((struct atmel_uart_char *)ring->buf)[ring->head];
+	c->status	= status;
+	c->ch		= ch;
+
+	/* Make sure the character is stored before we update head. */
+	smp_wmb();
+
+	ring->head = (ring->head + 1) & (ATMEL_SERIAL_RINGSIZE - 1);
+}
+
+/*
+ * Deal with parity, framing and overrun errors.
+ */
+static void atmel_pdc_rxerr(struct uart_port *port, unsigned int status)
+{
+	/* clear error */
+	UART_PUT_CR(port, ATMEL_US_RSTSTA);
+
+	if (status & ATMEL_US_RXBRK) {
+		/* ignore side-effect */
+		status &= ~(ATMEL_US_PARE | ATMEL_US_FRAME);
+		port->icount.brk++;
+	}
+	if (status & ATMEL_US_PARE)
+		port->icount.parity++;
+	if (status & ATMEL_US_FRAME)
+		port->icount.frame++;
+	if (status & ATMEL_US_OVRE)
+		port->icount.overrun++;
+}
+
+/*
+ * Characters received (called from interrupt handler)
+ */
+static void atmel_rx_chars(struct uart_port *port)
+{
+	struct atmel_uart_port *atmel_port = to_atmel_uart_port(port);
+	unsigned int status, ch;
+
+	status = UART_GET_CSR(port);
+	while (status & ATMEL_US_RXRDY) {
+		ch = UART_GET_CHAR(port);
+
+		/*
+		 * note that the error handling code is
+		 * out of the main execution path
+		 */
+		if (unlikely(status & (ATMEL_US_PARE | ATMEL_US_FRAME
+				       | ATMEL_US_OVRE | ATMEL_US_RXBRK)
+			     || atmel_port->break_active)) {
+
+			/* clear error */
+			UART_PUT_CR(port, ATMEL_US_RSTSTA);
+
+			if (status & ATMEL_US_RXBRK
+			    && !atmel_port->break_active) {
+				atmel_port->break_active = 1;
+				UART_PUT_IER(port, ATMEL_US_RXBRK);
+			} else {
+				/*
+				 * This is either the end-of-break
+				 * condition or we've received at
+				 * least one character without RXBRK
+				 * being set. In both cases, the next
+				 * RXBRK will indicate start-of-break.
+				 */
+				UART_PUT_IDR(port, ATMEL_US_RXBRK);
+				status &= ~ATMEL_US_RXBRK;
+				atmel_port->break_active = 0;
+			}
+		}
+
+		atmel_buffer_rx_char(port, status, ch);
+		status = UART_GET_CSR(port);
+	}
+
+	tasklet_schedule(&atmel_port->tasklet);
+}
+
+/*
+ * Transmit characters (called from tasklet with TXRDY interrupt
+ * disabled)
+ */
+static void atmel_tx_chars(struct uart_port *port)
+{
+	struct circ_buf *xmit = &port->state->xmit;
+	struct atmel_uart_port *atmel_port = to_atmel_uart_port(port);
+
+	if (port->x_char && UART_GET_CSR(port) & atmel_port->tx_done_mask) {
+		UART_PUT_CHAR(port, port->x_char);
+		port->icount.tx++;
+		port->x_char = 0;
+	}
+	if (uart_circ_empty(xmit) || uart_tx_stopped(port))
+		return;
+
+	while (UART_GET_CSR(port) & atmel_port->tx_done_mask) {
+		UART_PUT_CHAR(port, xmit->buf[xmit->tail]);
+		xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
+		port->icount.tx++;
+		if (uart_circ_empty(xmit))
+			break;
+	}
+
+	if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
+		uart_write_wakeup(port);
+
+	if (!uart_circ_empty(xmit))
+		/* Enable interrupts */
+		UART_PUT_IER(port, atmel_port->tx_done_mask);
+}
+
+/*
+ * receive interrupt handler.
+ */
+static void
+atmel_handle_receive(struct uart_port *port, unsigned int pending)
+{
+	struct atmel_uart_port *atmel_port = to_atmel_uart_port(port);
+
+	if (atmel_use_dma_rx(port)) {
+		/*
+		 * PDC receive. Just schedule the tasklet and let it
+		 * figure out the details.
+		 *
+		 * TODO: We're not handling error flags correctly at
+		 * the moment.
+		 */
+		if (pending & (ATMEL_US_ENDRX | ATMEL_US_TIMEOUT)) {
+			UART_PUT_IDR(port, (ATMEL_US_ENDRX
+						| ATMEL_US_TIMEOUT));
+			tasklet_schedule(&atmel_port->tasklet);
+		}
+
+		if (pending & (ATMEL_US_RXBRK | ATMEL_US_OVRE |
+				ATMEL_US_FRAME | ATMEL_US_PARE))
+			atmel_pdc_rxerr(port, pending);
+	}
+
+	/* Interrupt receive */
+	if (pending & ATMEL_US_RXRDY)
+		atmel_rx_chars(port);
+	else if (pending & ATMEL_US_RXBRK) {
+		/*
+		 * End of break detected. If it came along with a
+		 * character, atmel_rx_chars will handle it.
+		 */
+		UART_PUT_CR(port, ATMEL_US_RSTSTA);
+		UART_PUT_IDR(port, ATMEL_US_RXBRK);
+		atmel_port->break_active = 0;
+	}
+}
+
+/*
+ * transmit interrupt handler. (Transmit is IRQF_NODELAY safe)
+ */
+static void
+atmel_handle_transmit(struct uart_port *port, unsigned int pending)
+{
+	struct atmel_uart_port *atmel_port = to_atmel_uart_port(port);
+
+	if (pending & atmel_port->tx_done_mask) {
+		/* Either PDC or interrupt transmission */
+		UART_PUT_IDR(port, atmel_port->tx_done_mask);
+		tasklet_schedule(&atmel_port->tasklet);
+	}
+}
+
+/*
+ * status flags interrupt handler.
+ */
+static void
+atmel_handle_status(struct uart_port *port, unsigned int pending,
+		    unsigned int status)
+{
+	struct atmel_uart_port *atmel_port = to_atmel_uart_port(port);
+
+	if (pending & (ATMEL_US_RIIC | ATMEL_US_DSRIC | ATMEL_US_DCDIC
+				| ATMEL_US_CTSIC)) {
+		atmel_port->irq_status = status;
+		tasklet_schedule(&atmel_port->tasklet);
+	}
+}
+
+/*
+ * Interrupt handler
+ */
+static irqreturn_t atmel_interrupt(int irq, void *dev_id)
+{
+	struct uart_port *port = dev_id;
+	unsigned int status, pending, pass_counter = 0;
+
+	do {
+		status = UART_GET_CSR(port);
+		pending = status & UART_GET_IMR(port);
+		if (!pending)
+			break;
+
+		atmel_handle_receive(port, pending);
+		atmel_handle_status(port, pending, status);
+		atmel_handle_transmit(port, pending);
+	} while (pass_counter++ < ATMEL_ISR_PASS_LIMIT);
+
+	return pass_counter ? IRQ_HANDLED : IRQ_NONE;
+}
+
+/*
+ * Called from tasklet with ENDTX and TXBUFE interrupts disabled.
+ */
+static void atmel_tx_dma(struct uart_port *port)
+{
+	struct atmel_uart_port *atmel_port = to_atmel_uart_port(port);
+	struct circ_buf *xmit = &port->state->xmit;
+	struct atmel_dma_buffer *pdc = &atmel_port->pdc_tx;
+	int count;
+
+	/* nothing left to transmit? */
+	if (UART_GET_TCR(port))
+		return;
+
+	xmit->tail += pdc->ofs;
+	xmit->tail &= UART_XMIT_SIZE - 1;
+
+	port->icount.tx += pdc->ofs;
+	pdc->ofs = 0;
+
+	/* more to transmit - setup next transfer */
+
+	/* disable PDC transmit */
+	UART_PUT_PTCR(port, ATMEL_PDC_TXTDIS);
+
+	if (!uart_circ_empty(xmit) && !uart_tx_stopped(port)) {
+		dma_sync_single_for_device(port->dev,
+					   pdc->dma_addr,
+					   pdc->dma_size,
+					   DMA_TO_DEVICE);
+
+		count = CIRC_CNT_TO_END(xmit->head, xmit->tail, UART_XMIT_SIZE);
+		pdc->ofs = count;
+
+		UART_PUT_TPR(port, pdc->dma_addr + xmit->tail);
+		UART_PUT_TCR(port, count);
+		/* re-enable PDC transmit */
+		UART_PUT_PTCR(port, ATMEL_PDC_TXTEN);
+		/* Enable interrupts */
+		UART_PUT_IER(port, atmel_port->tx_done_mask);
+	} else {
+		if ((atmel_port->rs485.flags & SER_RS485_ENABLED) &&
+		    !(atmel_port->rs485.flags & SER_RS485_RX_DURING_TX)) {
+			/* DMA done, stop TX, start RX for RS485 */
+			atmel_start_rx(port);
+		}
+	}
+
+	if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
+		uart_write_wakeup(port);
+}
+
+static void atmel_rx_from_ring(struct uart_port *port)
+{
+	struct atmel_uart_port *atmel_port = to_atmel_uart_port(port);
+	struct circ_buf *ring = &atmel_port->rx_ring;
+	unsigned int flg;
+	unsigned int status;
+
+	while (ring->head != ring->tail) {
+		struct atmel_uart_char c;
+
+		/* Make sure c is loaded after head. */
+		smp_rmb();
+
+		c = ((struct atmel_uart_char *)ring->buf)[ring->tail];
+
+		ring->tail = (ring->tail + 1) & (ATMEL_SERIAL_RINGSIZE - 1);
+
+		port->icount.rx++;
+		status = c.status;
+		flg = TTY_NORMAL;
+
+		/*
+		 * note that the error handling code is
+		 * out of the main execution path
+		 */
+		if (unlikely(status & (ATMEL_US_PARE | ATMEL_US_FRAME
+				       | ATMEL_US_OVRE | ATMEL_US_RXBRK))) {
+			if (status & ATMEL_US_RXBRK) {
+				/* ignore side-effect */
+				status &= ~(ATMEL_US_PARE | ATMEL_US_FRAME);
+
+				port->icount.brk++;
+				if (uart_handle_break(port))
+					continue;
+			}
+			if (status & ATMEL_US_PARE)
+				port->icount.parity++;
+			if (status & ATMEL_US_FRAME)
+				port->icount.frame++;
+			if (status & ATMEL_US_OVRE)
+				port->icount.overrun++;
+
+			status &= port->read_status_mask;
+
+			if (status & ATMEL_US_RXBRK)
+				flg = TTY_BREAK;
+			else if (status & ATMEL_US_PARE)
+				flg = TTY_PARITY;
+			else if (status & ATMEL_US_FRAME)
+				flg = TTY_FRAME;
+		}
+
+
+		if (uart_handle_sysrq_char(port, c.ch))
+			continue;
+
+		uart_insert_char(port, status, ATMEL_US_OVRE, c.ch, flg);
+	}
+
+	/*
+	 * Drop the lock here since it might end up calling
+	 * uart_start(), which takes the lock.
+	 */
+	spin_unlock(&port->lock);
+	tty_flip_buffer_push(port->state->port.tty);
+	spin_lock(&port->lock);
+}
+
+static void atmel_rx_from_dma(struct uart_port *port)
+{
+	struct atmel_uart_port *atmel_port = to_atmel_uart_port(port);
+	struct tty_struct *tty = port->state->port.tty;
+	struct atmel_dma_buffer *pdc;
+	int rx_idx = atmel_port->pdc_rx_idx;
+	unsigned int head;
+	unsigned int tail;
+	unsigned int count;
+
+	do {
+		/* Reset the UART timeout early so that we don't miss one */
+		UART_PUT_CR(port, ATMEL_US_STTTO);
+
+		pdc = &atmel_port->pdc_rx[rx_idx];
+		head = UART_GET_RPR(port) - pdc->dma_addr;
+		tail = pdc->ofs;
+
+		/* If the PDC has switched buffers, RPR won't contain
+		 * any address within the current buffer. Since head
+		 * is unsigned, we just need a one-way comparison to
+		 * find out.
+		 *
+		 * In this case, we just need to consume the entire
+		 * buffer and resubmit it for DMA. This will clear the
+		 * ENDRX bit as well, so that we can safely re-enable
+		 * all interrupts below.
+		 */
+		head = min(head, pdc->dma_size);
+
+		if (likely(head != tail)) {
+			dma_sync_single_for_cpu(port->dev, pdc->dma_addr,
+					pdc->dma_size, DMA_FROM_DEVICE);
+
+			/*
+			 * head will only wrap around when we recycle
+			 * the DMA buffer, and when that happens, we
+			 * explicitly set tail to 0. So head will
+			 * always be greater than tail.
+			 */
+			count = head - tail;
+
+			tty_insert_flip_string(tty, pdc->buf + pdc->ofs, count);
+
+			dma_sync_single_for_device(port->dev, pdc->dma_addr,
+					pdc->dma_size, DMA_FROM_DEVICE);
+
+			port->icount.rx += count;
+			pdc->ofs = head;
+		}
+
+		/*
+		 * If the current buffer is full, we need to check if
+		 * the next one contains any additional data.
+		 */
+		if (head >= pdc->dma_size) {
+			pdc->ofs = 0;
+			UART_PUT_RNPR(port, pdc->dma_addr);
+			UART_PUT_RNCR(port, pdc->dma_size);
+
+			rx_idx = !rx_idx;
+			atmel_port->pdc_rx_idx = rx_idx;
+		}
+	} while (head >= pdc->dma_size);
+
+	/*
+	 * Drop the lock here since it might end up calling
+	 * uart_start(), which takes the lock.
+	 */
+	spin_unlock(&port->lock);
+	tty_flip_buffer_push(tty);
+	spin_lock(&port->lock);
+
+	UART_PUT_IER(port, ATMEL_US_ENDRX | ATMEL_US_TIMEOUT);
+}
+
+/*
+ * tasklet handling tty stuff outside the interrupt handler.
+ */
+static void atmel_tasklet_func(unsigned long data)
+{
+	struct uart_port *port = (struct uart_port *)data;
+	struct atmel_uart_port *atmel_port = to_atmel_uart_port(port);
+	unsigned int status;
+	unsigned int status_change;
+
+	/* The interrupt handler does not take the lock */
+	spin_lock(&port->lock);
+
+	if (atmel_use_dma_tx(port))
+		atmel_tx_dma(port);
+	else
+		atmel_tx_chars(port);
+
+	status = atmel_port->irq_status;
+	status_change = status ^ atmel_port->irq_status_prev;
+
+	if (status_change & (ATMEL_US_RI | ATMEL_US_DSR
+				| ATMEL_US_DCD | ATMEL_US_CTS)) {
+		/* TODO: All reads to CSR will clear these interrupts! */
+		if (status_change & ATMEL_US_RI)
+			port->icount.rng++;
+		if (status_change & ATMEL_US_DSR)
+			port->icount.dsr++;
+		if (status_change & ATMEL_US_DCD)
+			uart_handle_dcd_change(port, !(status & ATMEL_US_DCD));
+		if (status_change & ATMEL_US_CTS)
+			uart_handle_cts_change(port, !(status & ATMEL_US_CTS));
+
+		wake_up_interruptible(&port->state->port.delta_msr_wait);
+
+		atmel_port->irq_status_prev = status;
+	}
+
+	if (atmel_use_dma_rx(port))
+		atmel_rx_from_dma(port);
+	else
+		atmel_rx_from_ring(port);
+
+	spin_unlock(&port->lock);
+}
+
+/*
+ * Perform initialization and enable port for reception
+ */
+static int atmel_startup(struct uart_port *port)
+{
+	struct atmel_uart_port *atmel_port = to_atmel_uart_port(port);
+	struct tty_struct *tty = port->state->port.tty;
+	int retval;
+
+	/*
+	 * Ensure that no interrupts are enabled otherwise when
+	 * request_irq() is called we could get stuck trying to
+	 * handle an unexpected interrupt
+	 */
+	UART_PUT_IDR(port, -1);
+
+	/*
+	 * Allocate the IRQ
+	 */
+	retval = request_irq(port->irq, atmel_interrupt, IRQF_SHARED,
+			tty ? tty->name : "atmel_serial", port);
+	if (retval) {
+		printk("atmel_serial: atmel_startup - Can't get irq\n");
+		return retval;
+	}
+
+	/*
+	 * Initialize DMA (if necessary)
+	 */
+	if (atmel_use_dma_rx(port)) {
+		int i;
+
+		for (i = 0; i < 2; i++) {
+			struct atmel_dma_buffer *pdc = &atmel_port->pdc_rx[i];
+
+			pdc->buf = kmalloc(PDC_BUFFER_SIZE, GFP_KERNEL);
+			if (pdc->buf == NULL) {
+				if (i != 0) {
+					dma_unmap_single(port->dev,
+						atmel_port->pdc_rx[0].dma_addr,
+						PDC_BUFFER_SIZE,
+						DMA_FROM_DEVICE);
+					kfree(atmel_port->pdc_rx[0].buf);
+				}
+				free_irq(port->irq, port);
+				return -ENOMEM;
+			}
+			pdc->dma_addr = dma_map_single(port->dev,
+						       pdc->buf,
+						       PDC_BUFFER_SIZE,
+						       DMA_FROM_DEVICE);
+			pdc->dma_size = PDC_BUFFER_SIZE;
+			pdc->ofs = 0;
+		}
+
+		atmel_port->pdc_rx_idx = 0;
+
+		UART_PUT_RPR(port, atmel_port->pdc_rx[0].dma_addr);
+		UART_PUT_RCR(port, PDC_BUFFER_SIZE);
+
+		UART_PUT_RNPR(port, atmel_port->pdc_rx[1].dma_addr);
+		UART_PUT_RNCR(port, PDC_BUFFER_SIZE);
+	}
+	if (atmel_use_dma_tx(port)) {
+		struct atmel_dma_buffer *pdc = &atmel_port->pdc_tx;
+		struct circ_buf *xmit = &port->state->xmit;
+
+		pdc->buf = xmit->buf;
+		pdc->dma_addr = dma_map_single(port->dev,
+					       pdc->buf,
+					       UART_XMIT_SIZE,
+					       DMA_TO_DEVICE);
+		pdc->dma_size = UART_XMIT_SIZE;
+		pdc->ofs = 0;
+	}
+
+	/*
+	 * If there is a specific "open" function (to register
+	 * control line interrupts)
+	 */
+	if (atmel_open_hook) {
+		retval = atmel_open_hook(port);
+		if (retval) {
+			free_irq(port->irq, port);
+			return retval;
+		}
+	}
+
+	/* Save current CSR for comparison in atmel_tasklet_func() */
+	atmel_port->irq_status_prev = UART_GET_CSR(port);
+	atmel_port->irq_status = atmel_port->irq_status_prev;
+
+	/*
+	 * Finally, enable the serial port
+	 */
+	UART_PUT_CR(port, ATMEL_US_RSTSTA | ATMEL_US_RSTRX);
+	/* enable xmit & rcvr */
+	UART_PUT_CR(port, ATMEL_US_TXEN | ATMEL_US_RXEN);
+
+	if (atmel_use_dma_rx(port)) {
+		/* set UART timeout */
+		UART_PUT_RTOR(port, PDC_RX_TIMEOUT);
+		UART_PUT_CR(port, ATMEL_US_STTTO);
+
+		UART_PUT_IER(port, ATMEL_US_ENDRX | ATMEL_US_TIMEOUT);
+		/* enable PDC controller */
+		UART_PUT_PTCR(port, ATMEL_PDC_RXTEN);
+	} else {
+		/* enable receive only */
+		UART_PUT_IER(port, ATMEL_US_RXRDY);
+	}
+
+	return 0;
+}
+
+/*
+ * Disable the port
+ */
+static void atmel_shutdown(struct uart_port *port)
+{
+	struct atmel_uart_port *atmel_port = to_atmel_uart_port(port);
+
+	/*
+	 * Clear out any scheduled tasklets before
+	 * we destroy the buffers
+	 */
+	tasklet_kill(&atmel_port->tasklet);
+
+	/*
+	 * Ensure everything is stopped and
+	 * disable all interrupts, port and break condition.
+	 */
+	atmel_stop_rx(port);
+	atmel_stop_tx(port);
+
+	UART_PUT_CR(port, ATMEL_US_RSTSTA);
+	UART_PUT_IDR(port, -1);
+
+
+	/*
+	 * Shut-down the DMA.
+	 */
+	if (atmel_use_dma_rx(port)) {
+		int i;
+
+		for (i = 0; i < 2; i++) {
+			struct atmel_dma_buffer *pdc = &atmel_port->pdc_rx[i];
+
+			dma_unmap_single(port->dev,
+					 pdc->dma_addr,
+					 pdc->dma_size,
+					 DMA_FROM_DEVICE);
+			kfree(pdc->buf);
+		}
+	}
+	if (atmel_use_dma_tx(port)) {
+		struct atmel_dma_buffer *pdc = &atmel_port->pdc_tx;
+
+		dma_unmap_single(port->dev,
+				 pdc->dma_addr,
+				 pdc->dma_size,
+				 DMA_TO_DEVICE);
+	}
+
+	/*
+	 * Free the interrupt
+	 */
+	free_irq(port->irq, port);
+
+	/*
+	 * If there is a specific "close" function (to unregister
+	 * control line interrupts)
+	 */
+	if (atmel_close_hook)
+		atmel_close_hook(port);
+}
+
+/*
+ * Flush any TX data submitted for DMA. Called when the TX circular
+ * buffer is reset.
+ */
+static void atmel_flush_buffer(struct uart_port *port)
+{
+	struct atmel_uart_port *atmel_port = to_atmel_uart_port(port);
+
+	if (atmel_use_dma_tx(port)) {
+		UART_PUT_TCR(port, 0);
+		atmel_port->pdc_tx.ofs = 0;
+	}
+}
+
+/*
+ * Power / Clock management.
+ */
+static void atmel_serial_pm(struct uart_port *port, unsigned int state,
+			    unsigned int oldstate)
+{
+	struct atmel_uart_port *atmel_port = to_atmel_uart_port(port);
+
+	switch (state) {
+	case 0:
+		/*
+		 * Enable the peripheral clock for this serial port.
+		 * This is called on uart_open() or a resume event.
+		 */
+		clk_enable(atmel_port->clk);
+
+		/* re-enable interrupts if we disabled some on suspend */
+		UART_PUT_IER(port, atmel_port->backup_imr);
+		break;
+	case 3:
+		/* Back up the interrupt mask and disable all interrupts */
+		atmel_port->backup_imr = UART_GET_IMR(port);
+		UART_PUT_IDR(port, -1);
+
+		/*
+		 * Disable the peripheral clock for this serial port.
+		 * This is called on uart_close() or a suspend event.
+		 */
+		clk_disable(atmel_port->clk);
+		break;
+	default:
+		printk(KERN_ERR "atmel_serial: unknown pm %d\n", state);
+	}
+}
+
+/*
+ * Change the port parameters
+ */
+static void atmel_set_termios(struct uart_port *port, struct ktermios *termios,
+			      struct ktermios *old)
+{
+	unsigned long flags;
+	unsigned int mode, imr, quot, baud;
+	struct atmel_uart_port *atmel_port = to_atmel_uart_port(port);
+
+	/* Get current mode register */
+	mode = UART_GET_MR(port) & ~(ATMEL_US_USCLKS | ATMEL_US_CHRL
+					| ATMEL_US_NBSTOP | ATMEL_US_PAR
+					| ATMEL_US_USMODE);
+
+	baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk / 16);
+	quot = uart_get_divisor(port, baud);
+
+	if (quot > 65535) {	/* BRGR is 16-bit, so switch to slower clock */
+		quot /= 8;
+		mode |= ATMEL_US_USCLKS_MCK_DIV8;
+	}
+
+	/* byte size */
+	switch (termios->c_cflag & CSIZE) {
+	case CS5:
+		mode |= ATMEL_US_CHRL_5;
+		break;
+	case CS6:
+		mode |= ATMEL_US_CHRL_6;
+		break;
+	case CS7:
+		mode |= ATMEL_US_CHRL_7;
+		break;
+	default:
+		mode |= ATMEL_US_CHRL_8;
+		break;
+	}
+
+	/* stop bits */
+	if (termios->c_cflag & CSTOPB)
+		mode |= ATMEL_US_NBSTOP_2;
+
+	/* parity */
+	if (termios->c_cflag & PARENB) {
+		/* Mark or Space parity */
+		if (termios->c_cflag & CMSPAR) {
+			if (termios->c_cflag & PARODD)
+				mode |= ATMEL_US_PAR_MARK;
+			else
+				mode |= ATMEL_US_PAR_SPACE;
+		} else if (termios->c_cflag & PARODD)
+			mode |= ATMEL_US_PAR_ODD;
+		else
+			mode |= ATMEL_US_PAR_EVEN;
+	} else
+		mode |= ATMEL_US_PAR_NONE;
+
+	/* hardware handshake (RTS/CTS) */
+	if (termios->c_cflag & CRTSCTS)
+		mode |= ATMEL_US_USMODE_HWHS;
+	else
+		mode |= ATMEL_US_USMODE_NORMAL;
+
+	spin_lock_irqsave(&port->lock, flags);
+
+	port->read_status_mask = ATMEL_US_OVRE;
+	if (termios->c_iflag & INPCK)
+		port->read_status_mask |= (ATMEL_US_FRAME | ATMEL_US_PARE);
+	if (termios->c_iflag & (BRKINT | PARMRK))
+		port->read_status_mask |= ATMEL_US_RXBRK;
+
+	if (atmel_use_dma_rx(port))
+		/* need to enable error interrupts */
+		UART_PUT_IER(port, port->read_status_mask);
+
+	/*
+	 * Characters to ignore
+	 */
+	port->ignore_status_mask = 0;
+	if (termios->c_iflag & IGNPAR)
+		port->ignore_status_mask |= (ATMEL_US_FRAME | ATMEL_US_PARE);
+	if (termios->c_iflag & IGNBRK) {
+		port->ignore_status_mask |= ATMEL_US_RXBRK;
+		/*
+		 * If we're ignoring parity and break indicators,
+		 * ignore overruns too (for real raw support).
+		 */
+		if (termios->c_iflag & IGNPAR)
+			port->ignore_status_mask |= ATMEL_US_OVRE;
+	}
+	/* TODO: Ignore all characters if CREAD is set.*/
+
+	/* update the per-port timeout */
+	uart_update_timeout(port, termios->c_cflag, baud);
+
+	/*
+	 * save/disable interrupts. The tty layer will ensure that the
+	 * transmitter is empty if requested by the caller, so there's
+	 * no need to wait for it here.
+	 */
+	imr = UART_GET_IMR(port);
+	UART_PUT_IDR(port, -1);
+
+	/* disable receiver and transmitter */
+	UART_PUT_CR(port, ATMEL_US_TXDIS | ATMEL_US_RXDIS);
+
+	/* Resetting serial mode to RS232 (0x0) */
+	mode &= ~ATMEL_US_USMODE;
+
+	if (atmel_port->rs485.flags & SER_RS485_ENABLED) {
+		dev_dbg(port->dev, "Setting UART to RS485\n");
+		UART_PUT_TTGR(port, atmel_port->rs485.delay_rts_after_send);
+		mode |= ATMEL_US_USMODE_RS485;
+	} else {
+		dev_dbg(port->dev, "Setting UART to RS232\n");
+	}
+
+	/* set the parity, stop bits and data size */
+	UART_PUT_MR(port, mode);
+
+	/* set the baud rate */
+	UART_PUT_BRGR(port, quot);
+	UART_PUT_CR(port, ATMEL_US_RSTSTA | ATMEL_US_RSTRX);
+	UART_PUT_CR(port, ATMEL_US_TXEN | ATMEL_US_RXEN);
+
+	/* restore interrupts */
+	UART_PUT_IER(port, imr);
+
+	/* CTS flow-control and modem-status interrupts */
+	if (UART_ENABLE_MS(port, termios->c_cflag))
+		port->ops->enable_ms(port);
+
+	spin_unlock_irqrestore(&port->lock, flags);
+}
+
+static void atmel_set_ldisc(struct uart_port *port, int new)
+{
+	if (new == N_PPS) {
+		port->flags |= UPF_HARDPPS_CD;
+		atmel_enable_ms(port);
+	} else {
+		port->flags &= ~UPF_HARDPPS_CD;
+	}
+}
+
+/*
+ * Return string describing the specified port
+ */
+static const char *atmel_type(struct uart_port *port)
+{
+	return (port->type == PORT_ATMEL) ? "ATMEL_SERIAL" : NULL;
+}
+
+/*
+ * Release the memory region(s) being used by 'port'.
+ */
+static void atmel_release_port(struct uart_port *port)
+{
+	struct platform_device *pdev = to_platform_device(port->dev);
+	int size = pdev->resource[0].end - pdev->resource[0].start + 1;
+
+	release_mem_region(port->mapbase, size);
+
+	if (port->flags & UPF_IOREMAP) {
+		iounmap(port->membase);
+		port->membase = NULL;
+	}
+}
+
+/*
+ * Request the memory region(s) being used by 'port'.
+ */
+static int atmel_request_port(struct uart_port *port)
+{
+	struct platform_device *pdev = to_platform_device(port->dev);
+	int size = pdev->resource[0].end - pdev->resource[0].start + 1;
+
+	if (!request_mem_region(port->mapbase, size, "atmel_serial"))
+		return -EBUSY;
+
+	if (port->flags & UPF_IOREMAP) {
+		port->membase = ioremap(port->mapbase, size);
+		if (port->membase == NULL) {
+			release_mem_region(port->mapbase, size);
+			return -ENOMEM;
+		}
+	}
+
+	return 0;
+}
+
+/*
+ * Configure/autoconfigure the port.
+ */
+static void atmel_config_port(struct uart_port *port, int flags)
+{
+	if (flags & UART_CONFIG_TYPE) {
+		port->type = PORT_ATMEL;
+		atmel_request_port(port);
+	}
+}
+
+/*
+ * Verify the new serial_struct (for TIOCSSERIAL).
+ */
+static int atmel_verify_port(struct uart_port *port, struct serial_struct *ser)
+{
+	int ret = 0;
+	if (ser->type != PORT_UNKNOWN && ser->type != PORT_ATMEL)
+		ret = -EINVAL;
+	if (port->irq != ser->irq)
+		ret = -EINVAL;
+	if (ser->io_type != SERIAL_IO_MEM)
+		ret = -EINVAL;
+	if (port->uartclk / 16 != ser->baud_base)
+		ret = -EINVAL;
+	if ((void *)port->mapbase != ser->iomem_base)
+		ret = -EINVAL;
+	if (port->iobase != ser->port)
+		ret = -EINVAL;
+	if (ser->hub6 != 0)
+		ret = -EINVAL;
+	return ret;
+}
+
+#ifdef CONFIG_CONSOLE_POLL
+static int atmel_poll_get_char(struct uart_port *port)
+{
+	while (!(UART_GET_CSR(port) & ATMEL_US_RXRDY))
+		cpu_relax();
+
+	return UART_GET_CHAR(port);
+}
+
+static void atmel_poll_put_char(struct uart_port *port, unsigned char ch)
+{
+	while (!(UART_GET_CSR(port) & ATMEL_US_TXRDY))
+		cpu_relax();
+
+	UART_PUT_CHAR(port, ch);
+}
+#endif
+
+static int
+atmel_ioctl(struct uart_port *port, unsigned int cmd, unsigned long arg)
+{
+	struct serial_rs485 rs485conf;
+
+	switch (cmd) {
+	case TIOCSRS485:
+		if (copy_from_user(&rs485conf, (struct serial_rs485 *) arg,
+					sizeof(rs485conf)))
+			return -EFAULT;
+
+		atmel_config_rs485(port, &rs485conf);
+		break;
+
+	case TIOCGRS485:
+		if (copy_to_user((struct serial_rs485 *) arg,
+					&(to_atmel_uart_port(port)->rs485),
+					sizeof(rs485conf)))
+			return -EFAULT;
+		break;
+
+	default:
+		return -ENOIOCTLCMD;
+	}
+	return 0;
+}
+
+
+
+static struct uart_ops atmel_pops = {
+	.tx_empty	= atmel_tx_empty,
+	.set_mctrl	= atmel_set_mctrl,
+	.get_mctrl	= atmel_get_mctrl,
+	.stop_tx	= atmel_stop_tx,
+	.start_tx	= atmel_start_tx,
+	.stop_rx	= atmel_stop_rx,
+	.enable_ms	= atmel_enable_ms,
+	.break_ctl	= atmel_break_ctl,
+	.startup	= atmel_startup,
+	.shutdown	= atmel_shutdown,
+	.flush_buffer	= atmel_flush_buffer,
+	.set_termios	= atmel_set_termios,
+	.set_ldisc	= atmel_set_ldisc,
+	.type		= atmel_type,
+	.release_port	= atmel_release_port,
+	.request_port	= atmel_request_port,
+	.config_port	= atmel_config_port,
+	.verify_port	= atmel_verify_port,
+	.pm		= atmel_serial_pm,
+	.ioctl		= atmel_ioctl,
+#ifdef CONFIG_CONSOLE_POLL
+	.poll_get_char	= atmel_poll_get_char,
+	.poll_put_char	= atmel_poll_put_char,
+#endif
+};
+
+static void __devinit atmel_of_init_port(struct atmel_uart_port *atmel_port,
+					 struct device_node *np)
+{
+	u32 rs485_delay[2];
+
+	/* DMA/PDC usage specification */
+	if (of_get_property(np, "atmel,use-dma-rx", NULL))
+		atmel_port->use_dma_rx	= 1;
+	else
+		atmel_port->use_dma_rx	= 0;
+	if (of_get_property(np, "atmel,use-dma-tx", NULL))
+		atmel_port->use_dma_tx	= 1;
+	else
+		atmel_port->use_dma_tx	= 0;
+
+	/* rs485 properties */
+	if (of_property_read_u32_array(np, "rs485-rts-delay",
+					    rs485_delay, 2) == 0) {
+		struct serial_rs485 *rs485conf = &atmel_port->rs485;
+
+		rs485conf->delay_rts_before_send = rs485_delay[0];
+		rs485conf->delay_rts_after_send = rs485_delay[1];
+		rs485conf->flags = 0;
+
+		if (of_get_property(np, "rs485-rx-during-tx", NULL))
+			rs485conf->flags |= SER_RS485_RX_DURING_TX;
+
+		if (of_get_property(np, "linux,rs485-enabled-at-boot-time", NULL))
+			rs485conf->flags |= SER_RS485_ENABLED;
+	}
+}
+
+/*
+ * Configure the port from the platform device resource info.
+ */
+static void __devinit atmel_init_port(struct atmel_uart_port *atmel_port,
+				      struct platform_device *pdev)
+{
+	struct uart_port *port = &atmel_port->uart;
+	struct atmel_uart_data *pdata = pdev->dev.platform_data;
+
+	if (pdev->dev.of_node) {
+		atmel_of_init_port(atmel_port, pdev->dev.of_node);
+	} else {
+		atmel_port->use_dma_rx	= pdata->use_dma_rx;
+		atmel_port->use_dma_tx	= pdata->use_dma_tx;
+		atmel_port->rs485	= pdata->rs485;
+	}
+
+	port->iotype		= UPIO_MEM;
+	port->flags		= UPF_BOOT_AUTOCONF;
+	port->ops		= &atmel_pops;
+	port->fifosize		= 1;
+	port->dev		= &pdev->dev;
+	port->mapbase	= pdev->resource[0].start;
+	port->irq	= pdev->resource[1].start;
+
+	tasklet_init(&atmel_port->tasklet, atmel_tasklet_func,
+			(unsigned long)port);
+
+	memset(&atmel_port->rx_ring, 0, sizeof(atmel_port->rx_ring));
+
+	if (pdata && pdata->regs) {
+		/* Already mapped by setup code */
+		port->membase = pdata->regs;
+	} else {
+		port->flags	|= UPF_IOREMAP;
+		port->membase	= NULL;
+	}
+
+	/* for console, the clock could already be configured */
+	if (!atmel_port->clk) {
+		atmel_port->clk = clk_get(&pdev->dev, "usart");
+		clk_enable(atmel_port->clk);
+		port->uartclk = clk_get_rate(atmel_port->clk);
+		clk_disable(atmel_port->clk);
+		/* only enable clock when USART is in use */
+	}
+
+	/* Use TXEMPTY for interrupt when rs485 else TXRDY or ENDTX|TXBUFE */
+	if (atmel_port->rs485.flags & SER_RS485_ENABLED)
+		atmel_port->tx_done_mask = ATMEL_US_TXEMPTY;
+	else if (atmel_use_dma_tx(port)) {
+		port->fifosize = PDC_BUFFER_SIZE;
+		atmel_port->tx_done_mask = ATMEL_US_ENDTX | ATMEL_US_TXBUFE;
+	} else {
+		atmel_port->tx_done_mask = ATMEL_US_TXRDY;
+	}
+}
+
+/*
+ * Register board-specific modem-control line handlers.
+ */
+void __init atmel_register_uart_fns(struct atmel_port_fns *fns)
+{
+	if (fns->enable_ms)
+		atmel_pops.enable_ms = fns->enable_ms;
+	if (fns->get_mctrl)
+		atmel_pops.get_mctrl = fns->get_mctrl;
+	if (fns->set_mctrl)
+		atmel_pops.set_mctrl = fns->set_mctrl;
+	atmel_open_hook		= fns->open;
+	atmel_close_hook	= fns->close;
+	atmel_pops.pm		= fns->pm;
+	atmel_pops.set_wake	= fns->set_wake;
+}
+
+struct platform_device *atmel_default_console_device;	/* the serial console device */
+
+#ifdef CONFIG_SERIAL_ATMEL_CONSOLE
+static void atmel_console_putchar(struct uart_port *port, int ch)
+{
+	while (!(UART_GET_CSR(port) & ATMEL_US_TXRDY))
+		cpu_relax();
+	UART_PUT_CHAR(port, ch);
+}
+
+/*
+ * Interrupts are disabled on entering
+ */
+static void atmel_console_write(struct console *co, const char *s, u_int count)
+{
+	struct uart_port *port = &atmel_ports[co->index].uart;
+	struct atmel_uart_port *atmel_port = to_atmel_uart_port(port);
+	unsigned int status, imr;
+	unsigned int pdc_tx;
+
+	/*
+	 * First, save IMR and then disable interrupts
+	 */
+	imr = UART_GET_IMR(port);
+	UART_PUT_IDR(port, ATMEL_US_RXRDY | atmel_port->tx_done_mask);
+
+	/* Store PDC transmit status and disable it */
+	pdc_tx = UART_GET_PTSR(port) & ATMEL_PDC_TXTEN;
+	UART_PUT_PTCR(port, ATMEL_PDC_TXTDIS);
+
+	uart_console_write(port, s, count, atmel_console_putchar);
+
+	/*
+	 * Finally, wait for transmitter to become empty
+	 * and restore IMR
+	 */
+	do {
+		status = UART_GET_CSR(port);
+	} while (!(status & ATMEL_US_TXRDY));
+
+	/* Restore PDC transmit status */
+	if (pdc_tx)
+		UART_PUT_PTCR(port, ATMEL_PDC_TXTEN);
+
+	/* set interrupts back the way they were */
+	UART_PUT_IER(port, imr);
+}
+
+/*
+ * If the port was already initialised (eg, by a boot loader),
+ * try to determine the current setup.
+ */
+static void __init atmel_console_get_options(struct uart_port *port, int *baud,
+					     int *parity, int *bits)
+{
+	unsigned int mr, quot;
+
+	/*
+	 * If the baud rate generator isn't running, the port wasn't
+	 * initialized by the boot loader.
+	 */
+	quot = UART_GET_BRGR(port) & ATMEL_US_CD;
+	if (!quot)
+		return;
+
+	mr = UART_GET_MR(port) & ATMEL_US_CHRL;
+	if (mr == ATMEL_US_CHRL_8)
+		*bits = 8;
+	else
+		*bits = 7;
+
+	mr = UART_GET_MR(port) & ATMEL_US_PAR;
+	if (mr == ATMEL_US_PAR_EVEN)
+		*parity = 'e';
+	else if (mr == ATMEL_US_PAR_ODD)
+		*parity = 'o';
+
+	/*
+	 * The serial core only rounds down when matching this to a
+	 * supported baud rate. Make sure we don't end up slightly
+	 * lower than one of those, as it would make us fall through
+	 * to a much lower baud rate than we really want.
+	 */
+	*baud = port->uartclk / (16 * (quot - 1));
+}
+
+static int __init atmel_console_setup(struct console *co, char *options)
+{
+	struct uart_port *port = &atmel_ports[co->index].uart;
+	int baud = 115200;
+	int bits = 8;
+	int parity = 'n';
+	int flow = 'n';
+
+	if (port->membase == NULL) {
+		/* Port not initialized yet - delay setup */
+		return -ENODEV;
+	}
+
+	clk_enable(atmel_ports[co->index].clk);
+
+	UART_PUT_IDR(port, -1);
+	UART_PUT_CR(port, ATMEL_US_RSTSTA | ATMEL_US_RSTRX);
+	UART_PUT_CR(port, ATMEL_US_TXEN | ATMEL_US_RXEN);
+
+	if (options)
+		uart_parse_options(options, &baud, &parity, &bits, &flow);
+	else
+		atmel_console_get_options(port, &baud, &parity, &bits);
+
+	return uart_set_options(port, co, baud, parity, bits, flow);
+}
+
+static struct uart_driver atmel_uart;
+
+static struct console atmel_console = {
+	.name		= ATMEL_DEVICENAME,
+	.write		= atmel_console_write,
+	.device		= uart_console_device,
+	.setup		= atmel_console_setup,
+	.flags		= CON_PRINTBUFFER,
+	.index		= -1,
+	.data		= &atmel_uart,
+};
+
+#define ATMEL_CONSOLE_DEVICE	(&atmel_console)
+
+/*
+ * Early console initialization (before VM subsystem initialized).
+ */
+static int __init atmel_console_init(void)
+{
+	if (atmel_default_console_device) {
+		struct atmel_uart_data *pdata =
+			atmel_default_console_device->dev.platform_data;
+		int id = pdata->num;
+		struct atmel_uart_port *port = &atmel_ports[id];
+
+		port->backup_imr = 0;
+		port->uart.line = id;
+
+		add_preferred_console(ATMEL_DEVICENAME, id, NULL);
+		atmel_init_port(port, atmel_default_console_device);
+		register_console(&atmel_console);
+	}
+
+	return 0;
+}
+
+console_initcall(atmel_console_init);
+
+/*
+ * Late console initialization.
+ */
+static int __init atmel_late_console_init(void)
+{
+	if (atmel_default_console_device
+	    && !(atmel_console.flags & CON_ENABLED))
+		register_console(&atmel_console);
+
+	return 0;
+}
+
+core_initcall(atmel_late_console_init);
+
+static inline bool atmel_is_console_port(struct uart_port *port)
+{
+	return port->cons && port->cons->index == port->line;
+}
+
+#else
+#define ATMEL_CONSOLE_DEVICE	NULL
+
+static inline bool atmel_is_console_port(struct uart_port *port)
+{
+	return false;
+}
+#endif
+
+static struct uart_driver atmel_uart = {
+	.owner		= THIS_MODULE,
+	.driver_name	= "atmel_serial",
+	.dev_name	= ATMEL_DEVICENAME,
+	.major		= SERIAL_ATMEL_MAJOR,
+	.minor		= MINOR_START,
+	.nr		= ATMEL_MAX_UART,
+	.cons		= ATMEL_CONSOLE_DEVICE,
+};
+
+#ifdef CONFIG_PM
+static bool atmel_serial_clk_will_stop(void)
+{
+#ifdef CONFIG_ARCH_AT91
+	return at91_suspend_entering_slow_clock();
+#else
+	return false;
+#endif
+}
+
+static int atmel_serial_suspend(struct platform_device *pdev,
+				pm_message_t state)
+{
+	struct uart_port *port = platform_get_drvdata(pdev);
+	struct atmel_uart_port *atmel_port = to_atmel_uart_port(port);
+
+	if (atmel_is_console_port(port) && console_suspend_enabled) {
+		/* Drain the TX shifter */
+		while (!(UART_GET_CSR(port) & ATMEL_US_TXEMPTY))
+			cpu_relax();
+	}
+
+	/* we can not wake up if we're running on slow clock */
+	atmel_port->may_wakeup = device_may_wakeup(&pdev->dev);
+	if (atmel_serial_clk_will_stop())
+		device_set_wakeup_enable(&pdev->dev, 0);
+
+	uart_suspend_port(&atmel_uart, port);
+
+	return 0;
+}
+
+static int atmel_serial_resume(struct platform_device *pdev)
+{
+	struct uart_port *port = platform_get_drvdata(pdev);
+	struct atmel_uart_port *atmel_port = to_atmel_uart_port(port);
+
+	uart_resume_port(&atmel_uart, port);
+	device_set_wakeup_enable(&pdev->dev, atmel_port->may_wakeup);
+
+	return 0;
+}
+#else
+#define atmel_serial_suspend NULL
+#define atmel_serial_resume NULL
+#endif
+
+static int __devinit atmel_serial_probe(struct platform_device *pdev)
+{
+	struct atmel_uart_port *port;
+	struct device_node *np = pdev->dev.of_node;
+	struct atmel_uart_data *pdata = pdev->dev.platform_data;
+	void *data;
+	int ret = -ENODEV;
+
+	BUILD_BUG_ON(ATMEL_SERIAL_RINGSIZE & (ATMEL_SERIAL_RINGSIZE - 1));
+
+	if (np)
+		ret = of_alias_get_id(np, "serial");
+	else
+		if (pdata)
+			ret = pdata->num;
+
+	if (ret < 0)
+		/* port id not found in platform data nor device-tree aliases:
+		 * auto-enumerate it */
+		ret = find_first_zero_bit(atmel_ports_in_use, ATMEL_MAX_UART);
+
+	if (ret >= ATMEL_MAX_UART) {
+		ret = -ENODEV;
+		goto err;
+	}
+
+	if (test_and_set_bit(ret, atmel_ports_in_use)) {
+		/* port already in use */
+		ret = -EBUSY;
+		goto err;
+	}
+
+	port = &atmel_ports[ret];
+	port->backup_imr = 0;
+	port->uart.line = ret;
+
+	atmel_init_port(port, pdev);
+
+	if (!atmel_use_dma_rx(&port->uart)) {
+		ret = -ENOMEM;
+		data = kmalloc(sizeof(struct atmel_uart_char)
+				* ATMEL_SERIAL_RINGSIZE, GFP_KERNEL);
+		if (!data)
+			goto err_alloc_ring;
+		port->rx_ring.buf = data;
+	}
+
+	ret = uart_add_one_port(&atmel_uart, &port->uart);
+	if (ret)
+		goto err_add_port;
+
+#ifdef CONFIG_SERIAL_ATMEL_CONSOLE
+	if (atmel_is_console_port(&port->uart)
+			&& ATMEL_CONSOLE_DEVICE->flags & CON_ENABLED) {
+		/*
+		 * The serial core enabled the clock for us, so undo
+		 * the clk_enable() in atmel_console_setup()
+		 */
+		clk_disable(port->clk);
+	}
+#endif
+
+	device_init_wakeup(&pdev->dev, 1);
+	platform_set_drvdata(pdev, port);
+
+	if (port->rs485.flags & SER_RS485_ENABLED) {
+		UART_PUT_MR(&port->uart, ATMEL_US_USMODE_NORMAL);
+		UART_PUT_CR(&port->uart, ATMEL_US_RTSEN);
+	}
+
+	return 0;
+
+err_add_port:
+	kfree(port->rx_ring.buf);
+	port->rx_ring.buf = NULL;
+err_alloc_ring:
+	if (!atmel_is_console_port(&port->uart)) {
+		clk_put(port->clk);
+		port->clk = NULL;
+	}
+err:
+	return ret;
+}
+
+static int __devexit atmel_serial_remove(struct platform_device *pdev)
+{
+	struct uart_port *port = platform_get_drvdata(pdev);
+	struct atmel_uart_port *atmel_port = to_atmel_uart_port(port);
+	int ret = 0;
+
+	device_init_wakeup(&pdev->dev, 0);
+	platform_set_drvdata(pdev, NULL);
+
+	ret = uart_remove_one_port(&atmel_uart, port);
+
+	tasklet_kill(&atmel_port->tasklet);
+	kfree(atmel_port->rx_ring.buf);
+
+	/* "port" is allocated statically, so we shouldn't free it */
+
+	clear_bit(port->line, atmel_ports_in_use);
+
+	clk_put(atmel_port->clk);
+
+	return ret;
+}
+
+static struct platform_driver atmel_serial_driver = {
+	.probe		= atmel_serial_probe,
+	.remove		= __devexit_p(atmel_serial_remove),
+	.suspend	= atmel_serial_suspend,
+	.resume		= atmel_serial_resume,
+	.driver		= {
+		.name	= "atmel_usart",
+		.owner	= THIS_MODULE,
+		.of_match_table	= of_match_ptr(atmel_serial_dt_ids),
+	},
+};
+
+static int __init atmel_serial_init(void)
+{
+	int ret;
+
+	ret = uart_register_driver(&atmel_uart);
+	if (ret)
+		return ret;
+
+	ret = platform_driver_register(&atmel_serial_driver);
+	if (ret)
+		uart_unregister_driver(&atmel_uart);
+
+	return ret;
+}
+
+static void __exit atmel_serial_exit(void)
+{
+	platform_driver_unregister(&atmel_serial_driver);
+	uart_unregister_driver(&atmel_uart);
+}
+
+module_init(atmel_serial_init);
+module_exit(atmel_serial_exit);
+
+MODULE_AUTHOR("Rick Bronson");
+MODULE_DESCRIPTION("Atmel AT91 / AT32 serial port driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:atmel_usart");
diff --git a/ap/os/linux/linux-3.4.x/drivers/tty/serial/bcm63xx_uart.c b/ap/os/linux/linux-3.4.x/drivers/tty/serial/bcm63xx_uart.c
new file mode 100644
index 0000000..c0b68b9
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/tty/serial/bcm63xx_uart.c
@@ -0,0 +1,901 @@
+/*
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License.  See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * Derived from many drivers using generic_serial interface.
+ *
+ * Copyright (C) 2008 Maxime Bizon <mbizon@freebox.fr>
+ *
+ *  Serial driver for BCM63xx integrated UART.
+ *
+ * Hardware flow control was _not_ tested since I only have RX/TX on
+ * my board.
+ */
+
+#if defined(CONFIG_SERIAL_BCM63XX_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ)
+#define SUPPORT_SYSRQ
+#endif
+
+#include <linux/kernel.h>
+#include <linux/platform_device.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/module.h>
+#include <linux/console.h>
+#include <linux/clk.h>
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
+#include <linux/sysrq.h>
+#include <linux/serial.h>
+#include <linux/serial_core.h>
+
+#include <bcm63xx_clk.h>
+#include <bcm63xx_irq.h>
+#include <bcm63xx_regs.h>
+#include <bcm63xx_io.h>
+
+#define BCM63XX_NR_UARTS	2
+
+static struct uart_port ports[BCM63XX_NR_UARTS];
+
+/*
+ * rx interrupt mask / stat
+ *
+ * mask:
+ *  - rx fifo full
+ *  - rx fifo above threshold
+ *  - rx fifo not empty for too long
+ */
+#define UART_RX_INT_MASK	(UART_IR_MASK(UART_IR_RXOVER) |		\
+				UART_IR_MASK(UART_IR_RXTHRESH) |	\
+				UART_IR_MASK(UART_IR_RXTIMEOUT))
+
+#define UART_RX_INT_STAT	(UART_IR_STAT(UART_IR_RXOVER) |		\
+				UART_IR_STAT(UART_IR_RXTHRESH) |	\
+				UART_IR_STAT(UART_IR_RXTIMEOUT))
+
+/*
+ * tx interrupt mask / stat
+ *
+ * mask:
+ * - tx fifo empty
+ * - tx fifo below threshold
+ */
+#define UART_TX_INT_MASK	(UART_IR_MASK(UART_IR_TXEMPTY) |	\
+				UART_IR_MASK(UART_IR_TXTRESH))
+
+#define UART_TX_INT_STAT	(UART_IR_STAT(UART_IR_TXEMPTY) |	\
+				UART_IR_STAT(UART_IR_TXTRESH))
+
+/*
+ * external input interrupt
+ *
+ * mask: any edge on CTS, DCD
+ */
+#define UART_EXTINP_INT_MASK	(UART_EXTINP_IRMASK(UART_EXTINP_IR_CTS) | \
+				 UART_EXTINP_IRMASK(UART_EXTINP_IR_DCD))
+
+/*
+ * handy uart register accessor
+ */
+static inline unsigned int bcm_uart_readl(struct uart_port *port,
+					 unsigned int offset)
+{
+	return bcm_readl(port->membase + offset);
+}
+
+static inline void bcm_uart_writel(struct uart_port *port,
+				  unsigned int value, unsigned int offset)
+{
+	bcm_writel(value, port->membase + offset);
+}
+
+/*
+ * serial core request to check if uart tx fifo is empty
+ */
+static unsigned int bcm_uart_tx_empty(struct uart_port *port)
+{
+	unsigned int val;
+
+	val = bcm_uart_readl(port, UART_IR_REG);
+	return (val & UART_IR_STAT(UART_IR_TXEMPTY)) ? 1 : 0;
+}
+
+/*
+ * serial core request to set RTS and DTR pin state and loopback mode
+ */
+static void bcm_uart_set_mctrl(struct uart_port *port, unsigned int mctrl)
+{
+	unsigned int val;
+
+	val = bcm_uart_readl(port, UART_MCTL_REG);
+	val &= ~(UART_MCTL_DTR_MASK | UART_MCTL_RTS_MASK);
+	/* invert of written value is reflected on the pin */
+	if (!(mctrl & TIOCM_DTR))
+		val |= UART_MCTL_DTR_MASK;
+	if (!(mctrl & TIOCM_RTS))
+		val |= UART_MCTL_RTS_MASK;
+	bcm_uart_writel(port, val, UART_MCTL_REG);
+
+	val = bcm_uart_readl(port, UART_CTL_REG);
+	if (mctrl & TIOCM_LOOP)
+		val |= UART_CTL_LOOPBACK_MASK;
+	else
+		val &= ~UART_CTL_LOOPBACK_MASK;
+	bcm_uart_writel(port, val, UART_CTL_REG);
+}
+
+/*
+ * serial core request to return RI, CTS, DCD and DSR pin state
+ */
+static unsigned int bcm_uart_get_mctrl(struct uart_port *port)
+{
+	unsigned int val, mctrl;
+
+	mctrl = 0;
+	val = bcm_uart_readl(port, UART_EXTINP_REG);
+	if (val & UART_EXTINP_RI_MASK)
+		mctrl |= TIOCM_RI;
+	if (val & UART_EXTINP_CTS_MASK)
+		mctrl |= TIOCM_CTS;
+	if (val & UART_EXTINP_DCD_MASK)
+		mctrl |= TIOCM_CD;
+	if (val & UART_EXTINP_DSR_MASK)
+		mctrl |= TIOCM_DSR;
+	return mctrl;
+}
+
+/*
+ * serial core request to disable tx ASAP (used for flow control)
+ */
+static void bcm_uart_stop_tx(struct uart_port *port)
+{
+	unsigned int val;
+
+	val = bcm_uart_readl(port, UART_CTL_REG);
+	val &= ~(UART_CTL_TXEN_MASK);
+	bcm_uart_writel(port, val, UART_CTL_REG);
+
+	val = bcm_uart_readl(port, UART_IR_REG);
+	val &= ~UART_TX_INT_MASK;
+	bcm_uart_writel(port, val, UART_IR_REG);
+}
+
+/*
+ * serial core request to (re)enable tx
+ */
+static void bcm_uart_start_tx(struct uart_port *port)
+{
+	unsigned int val;
+
+	val = bcm_uart_readl(port, UART_IR_REG);
+	val |= UART_TX_INT_MASK;
+	bcm_uart_writel(port, val, UART_IR_REG);
+
+	val = bcm_uart_readl(port, UART_CTL_REG);
+	val |= UART_CTL_TXEN_MASK;
+	bcm_uart_writel(port, val, UART_CTL_REG);
+}
+
+/*
+ * serial core request to stop rx, called before port shutdown
+ */
+static void bcm_uart_stop_rx(struct uart_port *port)
+{
+	unsigned int val;
+
+	val = bcm_uart_readl(port, UART_IR_REG);
+	val &= ~UART_RX_INT_MASK;
+	bcm_uart_writel(port, val, UART_IR_REG);
+}
+
+/*
+ * serial core request to enable modem status interrupt reporting
+ */
+static void bcm_uart_enable_ms(struct uart_port *port)
+{
+	unsigned int val;
+
+	val = bcm_uart_readl(port, UART_IR_REG);
+	val |= UART_IR_MASK(UART_IR_EXTIP);
+	bcm_uart_writel(port, val, UART_IR_REG);
+}
+
+/*
+ * serial core request to start/stop emitting break char
+ */
+static void bcm_uart_break_ctl(struct uart_port *port, int ctl)
+{
+	unsigned long flags;
+	unsigned int val;
+
+	spin_lock_irqsave(&port->lock, flags);
+
+	val = bcm_uart_readl(port, UART_CTL_REG);
+	if (ctl)
+		val |= UART_CTL_XMITBRK_MASK;
+	else
+		val &= ~UART_CTL_XMITBRK_MASK;
+	bcm_uart_writel(port, val, UART_CTL_REG);
+
+	spin_unlock_irqrestore(&port->lock, flags);
+}
+
+/*
+ * return port type in string format
+ */
+static const char *bcm_uart_type(struct uart_port *port)
+{
+	return (port->type == PORT_BCM63XX) ? "bcm63xx_uart" : NULL;
+}
+
+/*
+ * read all chars in rx fifo and send them to core
+ */
+static void bcm_uart_do_rx(struct uart_port *port)
+{
+	struct tty_struct *tty;
+	unsigned int max_count;
+
+	/* limit number of char read in interrupt, should not be
+	 * higher than fifo size anyway since we're much faster than
+	 * serial port */
+	max_count = 32;
+	tty = port->state->port.tty;
+	do {
+		unsigned int iestat, c, cstat;
+		char flag;
+
+		/* get overrun/fifo empty information from ier
+		 * register */
+		iestat = bcm_uart_readl(port, UART_IR_REG);
+
+		if (unlikely(iestat & UART_IR_STAT(UART_IR_RXOVER))) {
+			unsigned int val;
+
+			/* fifo reset is required to clear
+			 * interrupt */
+			val = bcm_uart_readl(port, UART_CTL_REG);
+			val |= UART_CTL_RSTRXFIFO_MASK;
+			bcm_uart_writel(port, val, UART_CTL_REG);
+
+			port->icount.overrun++;
+			tty_insert_flip_char(tty, 0, TTY_OVERRUN);
+		}
+
+		if (!(iestat & UART_IR_STAT(UART_IR_RXNOTEMPTY)))
+			break;
+
+		cstat = c = bcm_uart_readl(port, UART_FIFO_REG);
+		port->icount.rx++;
+		flag = TTY_NORMAL;
+		c &= 0xff;
+
+		if (unlikely((cstat & UART_FIFO_ANYERR_MASK))) {
+			/* do stats first */
+			if (cstat & UART_FIFO_BRKDET_MASK) {
+				port->icount.brk++;
+				if (uart_handle_break(port))
+					continue;
+			}
+
+			if (cstat & UART_FIFO_PARERR_MASK)
+				port->icount.parity++;
+			if (cstat & UART_FIFO_FRAMEERR_MASK)
+				port->icount.frame++;
+
+			/* update flag wrt read_status_mask */
+			cstat &= port->read_status_mask;
+			if (cstat & UART_FIFO_BRKDET_MASK)
+				flag = TTY_BREAK;
+			if (cstat & UART_FIFO_FRAMEERR_MASK)
+				flag = TTY_FRAME;
+			if (cstat & UART_FIFO_PARERR_MASK)
+				flag = TTY_PARITY;
+		}
+
+		if (uart_handle_sysrq_char(port, c))
+			continue;
+
+
+		if ((cstat & port->ignore_status_mask) == 0)
+			tty_insert_flip_char(tty, c, flag);
+
+	} while (--max_count);
+
+	tty_flip_buffer_push(tty);
+}
+
+/*
+ * fill tx fifo with chars to send, stop when fifo is about to be full
+ * or when all chars have been sent.
+ */
+static void bcm_uart_do_tx(struct uart_port *port)
+{
+	struct circ_buf *xmit;
+	unsigned int val, max_count;
+
+	if (port->x_char) {
+		bcm_uart_writel(port, port->x_char, UART_FIFO_REG);
+		port->icount.tx++;
+		port->x_char = 0;
+		return;
+	}
+
+	if (uart_tx_stopped(port)) {
+		bcm_uart_stop_tx(port);
+		return;
+	}
+
+	xmit = &port->state->xmit;
+	if (uart_circ_empty(xmit))
+		goto txq_empty;
+
+	val = bcm_uart_readl(port, UART_MCTL_REG);
+	val = (val & UART_MCTL_TXFIFOFILL_MASK) >> UART_MCTL_TXFIFOFILL_SHIFT;
+	max_count = port->fifosize - val;
+
+	while (max_count--) {
+		unsigned int c;
+
+		c = xmit->buf[xmit->tail];
+		bcm_uart_writel(port, c, UART_FIFO_REG);
+		xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
+		port->icount.tx++;
+		if (uart_circ_empty(xmit))
+			break;
+	}
+
+	if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
+		uart_write_wakeup(port);
+
+	if (uart_circ_empty(xmit))
+		goto txq_empty;
+	return;
+
+txq_empty:
+	/* nothing to send, disable transmit interrupt */
+	val = bcm_uart_readl(port, UART_IR_REG);
+	val &= ~UART_TX_INT_MASK;
+	bcm_uart_writel(port, val, UART_IR_REG);
+	return;
+}
+
+/*
+ * process uart interrupt
+ */
+static irqreturn_t bcm_uart_interrupt(int irq, void *dev_id)
+{
+	struct uart_port *port;
+	unsigned int irqstat;
+
+	port = dev_id;
+	spin_lock(&port->lock);
+
+	irqstat = bcm_uart_readl(port, UART_IR_REG);
+	if (irqstat & UART_RX_INT_STAT)
+		bcm_uart_do_rx(port);
+
+	if (irqstat & UART_TX_INT_STAT)
+		bcm_uart_do_tx(port);
+
+	if (irqstat & UART_IR_MASK(UART_IR_EXTIP)) {
+		unsigned int estat;
+
+		estat = bcm_uart_readl(port, UART_EXTINP_REG);
+		if (estat & UART_EXTINP_IRSTAT(UART_EXTINP_IR_CTS))
+			uart_handle_cts_change(port,
+					       estat & UART_EXTINP_CTS_MASK);
+		if (estat & UART_EXTINP_IRSTAT(UART_EXTINP_IR_DCD))
+			uart_handle_dcd_change(port,
+					       estat & UART_EXTINP_DCD_MASK);
+	}
+
+	spin_unlock(&port->lock);
+	return IRQ_HANDLED;
+}
+
+/*
+ * enable rx & tx operation on uart
+ */
+static void bcm_uart_enable(struct uart_port *port)
+{
+	unsigned int val;
+
+	val = bcm_uart_readl(port, UART_CTL_REG);
+	val |= (UART_CTL_BRGEN_MASK | UART_CTL_TXEN_MASK | UART_CTL_RXEN_MASK);
+	bcm_uart_writel(port, val, UART_CTL_REG);
+}
+
+/*
+ * disable rx & tx operation on uart
+ */
+static void bcm_uart_disable(struct uart_port *port)
+{
+	unsigned int val;
+
+	val = bcm_uart_readl(port, UART_CTL_REG);
+	val &= ~(UART_CTL_BRGEN_MASK | UART_CTL_TXEN_MASK |
+		 UART_CTL_RXEN_MASK);
+	bcm_uart_writel(port, val, UART_CTL_REG);
+}
+
+/*
+ * clear all unread data in rx fifo and unsent data in tx fifo
+ */
+static void bcm_uart_flush(struct uart_port *port)
+{
+	unsigned int val;
+
+	/* empty rx and tx fifo */
+	val = bcm_uart_readl(port, UART_CTL_REG);
+	val |= UART_CTL_RSTRXFIFO_MASK | UART_CTL_RSTTXFIFO_MASK;
+	bcm_uart_writel(port, val, UART_CTL_REG);
+
+	/* read any pending char to make sure all irq status are
+	 * cleared */
+	(void)bcm_uart_readl(port, UART_FIFO_REG);
+}
+
+/*
+ * serial core request to initialize uart and start rx operation
+ */
+static int bcm_uart_startup(struct uart_port *port)
+{
+	unsigned int val;
+	int ret;
+
+	/* mask all irq and flush port */
+	bcm_uart_disable(port);
+	bcm_uart_writel(port, 0, UART_IR_REG);
+	bcm_uart_flush(port);
+
+	/* clear any pending external input interrupt */
+	(void)bcm_uart_readl(port, UART_EXTINP_REG);
+
+	/* set rx/tx fifo thresh to fifo half size */
+	val = bcm_uart_readl(port, UART_MCTL_REG);
+	val &= ~(UART_MCTL_RXFIFOTHRESH_MASK | UART_MCTL_TXFIFOTHRESH_MASK);
+	val |= (port->fifosize / 2) << UART_MCTL_RXFIFOTHRESH_SHIFT;
+	val |= (port->fifosize / 2) << UART_MCTL_TXFIFOTHRESH_SHIFT;
+	bcm_uart_writel(port, val, UART_MCTL_REG);
+
+	/* set rx fifo timeout to 1 char time */
+	val = bcm_uart_readl(port, UART_CTL_REG);
+	val &= ~UART_CTL_RXTMOUTCNT_MASK;
+	val |= 1 << UART_CTL_RXTMOUTCNT_SHIFT;
+	bcm_uart_writel(port, val, UART_CTL_REG);
+
+	/* report any edge on dcd and cts */
+	val = UART_EXTINP_INT_MASK;
+	val |= UART_EXTINP_DCD_NOSENSE_MASK;
+	val |= UART_EXTINP_CTS_NOSENSE_MASK;
+	bcm_uart_writel(port, val, UART_EXTINP_REG);
+
+	/* register irq and enable rx interrupts */
+	ret = request_irq(port->irq, bcm_uart_interrupt, 0,
+			  bcm_uart_type(port), port);
+	if (ret)
+		return ret;
+	bcm_uart_writel(port, UART_RX_INT_MASK, UART_IR_REG);
+	bcm_uart_enable(port);
+	return 0;
+}
+
+/*
+ * serial core request to flush & disable uart
+ */
+static void bcm_uart_shutdown(struct uart_port *port)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&port->lock, flags);
+	bcm_uart_writel(port, 0, UART_IR_REG);
+	spin_unlock_irqrestore(&port->lock, flags);
+
+	bcm_uart_disable(port);
+	bcm_uart_flush(port);
+	free_irq(port->irq, port);
+}
+
+/*
+ * serial core request to change current uart setting
+ */
+static void bcm_uart_set_termios(struct uart_port *port,
+				 struct ktermios *new,
+				 struct ktermios *old)
+{
+	unsigned int ctl, baud, quot, ier;
+	unsigned long flags;
+
+	spin_lock_irqsave(&port->lock, flags);
+
+	/* disable uart while changing speed */
+	bcm_uart_disable(port);
+	bcm_uart_flush(port);
+
+	/* update Control register */
+	ctl = bcm_uart_readl(port, UART_CTL_REG);
+	ctl &= ~UART_CTL_BITSPERSYM_MASK;
+
+	switch (new->c_cflag & CSIZE) {
+	case CS5:
+		ctl |= (0 << UART_CTL_BITSPERSYM_SHIFT);
+		break;
+	case CS6:
+		ctl |= (1 << UART_CTL_BITSPERSYM_SHIFT);
+		break;
+	case CS7:
+		ctl |= (2 << UART_CTL_BITSPERSYM_SHIFT);
+		break;
+	default:
+		ctl |= (3 << UART_CTL_BITSPERSYM_SHIFT);
+		break;
+	}
+
+	ctl &= ~UART_CTL_STOPBITS_MASK;
+	if (new->c_cflag & CSTOPB)
+		ctl |= UART_CTL_STOPBITS_2;
+	else
+		ctl |= UART_CTL_STOPBITS_1;
+
+	ctl &= ~(UART_CTL_RXPAREN_MASK | UART_CTL_TXPAREN_MASK);
+	if (new->c_cflag & PARENB)
+		ctl |= (UART_CTL_RXPAREN_MASK | UART_CTL_TXPAREN_MASK);
+	ctl &= ~(UART_CTL_RXPAREVEN_MASK | UART_CTL_TXPAREVEN_MASK);
+	if (new->c_cflag & PARODD)
+		ctl |= (UART_CTL_RXPAREVEN_MASK | UART_CTL_TXPAREVEN_MASK);
+	bcm_uart_writel(port, ctl, UART_CTL_REG);
+
+	/* update Baudword register */
+	baud = uart_get_baud_rate(port, new, old, 0, port->uartclk / 16);
+	quot = uart_get_divisor(port, baud) - 1;
+	bcm_uart_writel(port, quot, UART_BAUD_REG);
+
+	/* update Interrupt register */
+	ier = bcm_uart_readl(port, UART_IR_REG);
+
+	ier &= ~UART_IR_MASK(UART_IR_EXTIP);
+	if (UART_ENABLE_MS(port, new->c_cflag))
+		ier |= UART_IR_MASK(UART_IR_EXTIP);
+
+	bcm_uart_writel(port, ier, UART_IR_REG);
+
+	/* update read/ignore mask */
+	port->read_status_mask = UART_FIFO_VALID_MASK;
+	if (new->c_iflag & INPCK) {
+		port->read_status_mask |= UART_FIFO_FRAMEERR_MASK;
+		port->read_status_mask |= UART_FIFO_PARERR_MASK;
+	}
+	if (new->c_iflag & (BRKINT))
+		port->read_status_mask |= UART_FIFO_BRKDET_MASK;
+
+	port->ignore_status_mask = 0;
+	if (new->c_iflag & IGNPAR)
+		port->ignore_status_mask |= UART_FIFO_PARERR_MASK;
+	if (new->c_iflag & IGNBRK)
+		port->ignore_status_mask |= UART_FIFO_BRKDET_MASK;
+	if (!(new->c_cflag & CREAD))
+		port->ignore_status_mask |= UART_FIFO_VALID_MASK;
+
+	uart_update_timeout(port, new->c_cflag, baud);
+	bcm_uart_enable(port);
+	spin_unlock_irqrestore(&port->lock, flags);
+}
+
+/*
+ * serial core request to claim uart iomem
+ */
+static int bcm_uart_request_port(struct uart_port *port)
+{
+	unsigned int size;
+
+	size = RSET_UART_SIZE;
+	if (!request_mem_region(port->mapbase, size, "bcm63xx")) {
+		dev_err(port->dev, "Memory region busy\n");
+		return -EBUSY;
+	}
+
+	port->membase = ioremap(port->mapbase, size);
+	if (!port->membase) {
+		dev_err(port->dev, "Unable to map registers\n");
+		release_mem_region(port->mapbase, size);
+		return -EBUSY;
+	}
+	return 0;
+}
+
+/*
+ * serial core request to release uart iomem
+ */
+static void bcm_uart_release_port(struct uart_port *port)
+{
+	release_mem_region(port->mapbase, RSET_UART_SIZE);
+	iounmap(port->membase);
+}
+
+/*
+ * serial core request to do any port required autoconfiguration
+ */
+static void bcm_uart_config_port(struct uart_port *port, int flags)
+{
+	if (flags & UART_CONFIG_TYPE) {
+		if (bcm_uart_request_port(port))
+			return;
+		port->type = PORT_BCM63XX;
+	}
+}
+
+/*
+ * serial core request to check that port information in serinfo are
+ * suitable
+ */
+static int bcm_uart_verify_port(struct uart_port *port,
+				struct serial_struct *serinfo)
+{
+	if (port->type != PORT_BCM63XX)
+		return -EINVAL;
+	if (port->irq != serinfo->irq)
+		return -EINVAL;
+	if (port->iotype != serinfo->io_type)
+		return -EINVAL;
+	if (port->mapbase != (unsigned long)serinfo->iomem_base)
+		return -EINVAL;
+	return 0;
+}
+
+/* serial core callbacks */
+static struct uart_ops bcm_uart_ops = {
+	.tx_empty	= bcm_uart_tx_empty,
+	.get_mctrl	= bcm_uart_get_mctrl,
+	.set_mctrl	= bcm_uart_set_mctrl,
+	.start_tx	= bcm_uart_start_tx,
+	.stop_tx	= bcm_uart_stop_tx,
+	.stop_rx	= bcm_uart_stop_rx,
+	.enable_ms	= bcm_uart_enable_ms,
+	.break_ctl	= bcm_uart_break_ctl,
+	.startup	= bcm_uart_startup,
+	.shutdown	= bcm_uart_shutdown,
+	.set_termios	= bcm_uart_set_termios,
+	.type		= bcm_uart_type,
+	.release_port	= bcm_uart_release_port,
+	.request_port	= bcm_uart_request_port,
+	.config_port	= bcm_uart_config_port,
+	.verify_port	= bcm_uart_verify_port,
+};
+
+
+
+#ifdef CONFIG_SERIAL_BCM63XX_CONSOLE
+static inline void wait_for_xmitr(struct uart_port *port)
+{
+	unsigned int tmout;
+
+	/* Wait up to 10ms for the character(s) to be sent. */
+	tmout = 10000;
+	while (--tmout) {
+		unsigned int val;
+
+		val = bcm_uart_readl(port, UART_IR_REG);
+		if (val & UART_IR_STAT(UART_IR_TXEMPTY))
+			break;
+		udelay(1);
+	}
+
+	/* Wait up to 1s for flow control if necessary */
+	if (port->flags & UPF_CONS_FLOW) {
+		tmout = 1000000;
+		while (--tmout) {
+			unsigned int val;
+
+			val = bcm_uart_readl(port, UART_EXTINP_REG);
+			if (val & UART_EXTINP_CTS_MASK)
+				break;
+			udelay(1);
+		}
+	}
+}
+
+/*
+ * output given char
+ */
+static void bcm_console_putchar(struct uart_port *port, int ch)
+{
+	wait_for_xmitr(port);
+	bcm_uart_writel(port, ch, UART_FIFO_REG);
+}
+
+/*
+ * console core request to output given string
+ */
+static void bcm_console_write(struct console *co, const char *s,
+			      unsigned int count)
+{
+	struct uart_port *port;
+	unsigned long flags;
+	int locked;
+
+	port = &ports[co->index];
+
+	local_irq_save(flags);
+	if (port->sysrq) {
+		/* bcm_uart_interrupt() already took the lock */
+		locked = 0;
+	} else if (oops_in_progress) {
+		locked = spin_trylock(&port->lock);
+	} else {
+		spin_lock(&port->lock);
+		locked = 1;
+	}
+
+	/* call helper to deal with \r\n */
+	uart_console_write(port, s, count, bcm_console_putchar);
+
+	/* and wait for char to be transmitted */
+	wait_for_xmitr(port);
+
+	if (locked)
+		spin_unlock(&port->lock);
+	local_irq_restore(flags);
+}
+
+/*
+ * console core request to setup given console, find matching uart
+ * port and setup it.
+ */
+static int bcm_console_setup(struct console *co, char *options)
+{
+	struct uart_port *port;
+	int baud = 9600;
+	int bits = 8;
+	int parity = 'n';
+	int flow = 'n';
+
+	if (co->index < 0 || co->index >= BCM63XX_NR_UARTS)
+		return -EINVAL;
+	port = &ports[co->index];
+	if (!port->membase)
+		return -ENODEV;
+	if (options)
+		uart_parse_options(options, &baud, &parity, &bits, &flow);
+
+	return uart_set_options(port, co, baud, parity, bits, flow);
+}
+
+static struct uart_driver bcm_uart_driver;
+
+static struct console bcm63xx_console = {
+	.name		= "ttyS",
+	.write		= bcm_console_write,
+	.device		= uart_console_device,
+	.setup		= bcm_console_setup,
+	.flags		= CON_PRINTBUFFER,
+	.index		= -1,
+	.data		= &bcm_uart_driver,
+};
+
+static int __init bcm63xx_console_init(void)
+{
+	register_console(&bcm63xx_console);
+	return 0;
+}
+
+console_initcall(bcm63xx_console_init);
+
+#define BCM63XX_CONSOLE	(&bcm63xx_console)
+#else
+#define BCM63XX_CONSOLE	NULL
+#endif /* CONFIG_SERIAL_BCM63XX_CONSOLE */
+
+static struct uart_driver bcm_uart_driver = {
+	.owner		= THIS_MODULE,
+	.driver_name	= "bcm63xx_uart",
+	.dev_name	= "ttyS",
+	.major		= TTY_MAJOR,
+	.minor		= 64,
+	.nr		= BCM63XX_NR_UARTS,
+	.cons		= BCM63XX_CONSOLE,
+};
+
+/*
+ * platform driver probe/remove callback
+ */
+static int __devinit bcm_uart_probe(struct platform_device *pdev)
+{
+	struct resource *res_mem, *res_irq;
+	struct uart_port *port;
+	struct clk *clk;
+	int ret;
+
+	if (pdev->id < 0 || pdev->id >= BCM63XX_NR_UARTS)
+		return -EINVAL;
+
+	if (ports[pdev->id].membase)
+		return -EBUSY;
+
+	res_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!res_mem)
+		return -ENODEV;
+
+	res_irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+	if (!res_irq)
+		return -ENODEV;
+
+	clk = clk_get(&pdev->dev, "periph");
+	if (IS_ERR(clk))
+		return -ENODEV;
+
+	port = &ports[pdev->id];
+	memset(port, 0, sizeof(*port));
+	port->iotype = UPIO_MEM;
+	port->mapbase = res_mem->start;
+	port->irq = res_irq->start;
+	port->ops = &bcm_uart_ops;
+	port->flags = UPF_BOOT_AUTOCONF;
+	port->dev = &pdev->dev;
+	port->fifosize = 16;
+	port->uartclk = clk_get_rate(clk) / 2;
+	port->line = pdev->id;
+	clk_put(clk);
+
+	ret = uart_add_one_port(&bcm_uart_driver, port);
+	if (ret) {
+		ports[pdev->id].membase = 0;
+		return ret;
+	}
+	platform_set_drvdata(pdev, port);
+	return 0;
+}
+
+static int __devexit bcm_uart_remove(struct platform_device *pdev)
+{
+	struct uart_port *port;
+
+	port = platform_get_drvdata(pdev);
+	uart_remove_one_port(&bcm_uart_driver, port);
+	platform_set_drvdata(pdev, NULL);
+	/* mark port as free */
+	ports[pdev->id].membase = 0;
+	return 0;
+}
+
+/*
+ * platform driver stuff
+ */
+static struct platform_driver bcm_uart_platform_driver = {
+	.probe	= bcm_uart_probe,
+	.remove	= __devexit_p(bcm_uart_remove),
+	.driver	= {
+		.owner = THIS_MODULE,
+		.name  = "bcm63xx_uart",
+	},
+};
+
+static int __init bcm_uart_init(void)
+{
+	int ret;
+
+	ret = uart_register_driver(&bcm_uart_driver);
+	if (ret)
+		return ret;
+
+	ret = platform_driver_register(&bcm_uart_platform_driver);
+	if (ret)
+		uart_unregister_driver(&bcm_uart_driver);
+
+	return ret;
+}
+
+static void __exit bcm_uart_exit(void)
+{
+	platform_driver_unregister(&bcm_uart_platform_driver);
+	uart_unregister_driver(&bcm_uart_driver);
+}
+
+module_init(bcm_uart_init);
+module_exit(bcm_uart_exit);
+
+MODULE_AUTHOR("Maxime Bizon <mbizon@freebox.fr>");
+MODULE_DESCRIPTION("Broadcom 63<xx integrated uart driver");
+MODULE_LICENSE("GPL");
diff --git a/ap/os/linux/linux-3.4.x/drivers/tty/serial/bfin_sport_uart.c b/ap/os/linux/linux-3.4.x/drivers/tty/serial/bfin_sport_uart.c
new file mode 100644
index 0000000..7fbc3a0
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/tty/serial/bfin_sport_uart.c
@@ -0,0 +1,936 @@
+/*
+ * Blackfin On-Chip Sport Emulated UART Driver
+ *
+ * Copyright 2006-2009 Analog Devices Inc.
+ *
+ * Enter bugs at http://blackfin.uclinux.org/
+ *
+ * Licensed under the GPL-2 or later.
+ */
+
+/*
+ * This driver and the hardware supported are in term of EE-191 of ADI.
+ * http://www.analog.com/static/imported-files/application_notes/EE191.pdf 
+ * This application note describe how to implement a UART on a Sharc DSP,
+ * but this driver is implemented on Blackfin Processor.
+ * Transmit Frame Sync is not used by this driver to transfer data out.
+ */
+
+/* #define DEBUG */
+
+#define DRV_NAME "bfin-sport-uart"
+#define DEVICE_NAME	"ttySS"
+#define pr_fmt(fmt) DRV_NAME ": " fmt
+
+#include <linux/module.h>
+#include <linux/ioport.h>
+#include <linux/io.h>
+#include <linux/init.h>
+#include <linux/console.h>
+#include <linux/sysrq.h>
+#include <linux/slab.h>
+#include <linux/platform_device.h>
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
+#include <linux/serial_core.h>
+
+#include <asm/bfin_sport.h>
+#include <asm/delay.h>
+#include <asm/portmux.h>
+
+#include "bfin_sport_uart.h"
+
+struct sport_uart_port {
+	struct uart_port	port;
+	int			err_irq;
+	unsigned short		csize;
+	unsigned short		rxmask;
+	unsigned short		txmask1;
+	unsigned short		txmask2;
+	unsigned char		stopb;
+/*	unsigned char		parib; */
+#ifdef CONFIG_SERIAL_BFIN_SPORT_CTSRTS
+	int cts_pin;
+	int rts_pin;
+#endif
+};
+
+static int sport_uart_tx_chars(struct sport_uart_port *up);
+static void sport_stop_tx(struct uart_port *port);
+
+static inline void tx_one_byte(struct sport_uart_port *up, unsigned int value)
+{
+	pr_debug("%s value:%x, mask1=0x%x, mask2=0x%x\n", __func__, value,
+		up->txmask1, up->txmask2);
+
+	/* Place Start and Stop bits */
+	__asm__ __volatile__ (
+		"%[val] <<= 1;"
+		"%[val] = %[val] & %[mask1];"
+		"%[val] = %[val] | %[mask2];"
+		: [val]"+d"(value)
+		: [mask1]"d"(up->txmask1), [mask2]"d"(up->txmask2)
+		: "ASTAT"
+	);
+	pr_debug("%s value:%x\n", __func__, value);
+
+	SPORT_PUT_TX(up, value);
+}
+
+static inline unsigned char rx_one_byte(struct sport_uart_port *up)
+{
+	unsigned int value;
+	unsigned char extract;
+	u32 tmp_mask1, tmp_mask2, tmp_shift, tmp;
+
+	if ((up->csize + up->stopb) > 7)
+		value = SPORT_GET_RX32(up);
+	else
+		value = SPORT_GET_RX(up);
+
+	pr_debug("%s value:%x, cs=%d, mask=0x%x\n", __func__, value,
+		up->csize, up->rxmask);
+
+	/* Extract data */
+	__asm__ __volatile__ (
+		"%[extr] = 0;"
+		"%[mask1] = %[rxmask];"
+		"%[mask2] = 0x0200(Z);"
+		"%[shift] = 0;"
+		"LSETUP(.Lloop_s, .Lloop_e) LC0 = %[lc];"
+		".Lloop_s:"
+		"%[tmp] = extract(%[val], %[mask1].L)(Z);"
+		"%[tmp] <<= %[shift];"
+		"%[extr] = %[extr] | %[tmp];"
+		"%[mask1] = %[mask1] - %[mask2];"
+		".Lloop_e:"
+		"%[shift] += 1;"
+		: [extr]"=&d"(extract), [shift]"=&d"(tmp_shift), [tmp]"=&d"(tmp),
+		  [mask1]"=&d"(tmp_mask1), [mask2]"=&d"(tmp_mask2)
+		: [val]"d"(value), [rxmask]"d"(up->rxmask), [lc]"a"(up->csize)
+		: "ASTAT", "LB0", "LC0", "LT0"
+	);
+
+	pr_debug("	extract:%x\n", extract);
+	return extract;
+}
+
+static int sport_uart_setup(struct sport_uart_port *up, int size, int baud_rate)
+{
+	int tclkdiv, rclkdiv;
+	unsigned int sclk = get_sclk();
+
+	/* Set TCR1 and TCR2, TFSR is not enabled for uart */
+	SPORT_PUT_TCR1(up, (LATFS | ITFS | TFSR | TLSBIT | ITCLK));
+	SPORT_PUT_TCR2(up, size + 1);
+	pr_debug("%s TCR1:%x, TCR2:%x\n", __func__, SPORT_GET_TCR1(up), SPORT_GET_TCR2(up));
+
+	/* Set RCR1 and RCR2 */
+	SPORT_PUT_RCR1(up, (RCKFE | LARFS | LRFS | RFSR | IRCLK));
+	SPORT_PUT_RCR2(up, (size + 1) * 2 - 1);
+	pr_debug("%s RCR1:%x, RCR2:%x\n", __func__, SPORT_GET_RCR1(up), SPORT_GET_RCR2(up));
+
+	tclkdiv = sclk / (2 * baud_rate) - 1;
+	/* The actual uart baud rate of devices vary between +/-2%. The sport
+	 * RX sample rate should be faster than the double of the worst case,
+	 * otherwise, wrong data are received. So, set sport RX clock to be
+	 * 3% faster.
+	 */
+	rclkdiv = sclk / (2 * baud_rate * 2 * 97 / 100) - 1;
+	SPORT_PUT_TCLKDIV(up, tclkdiv);
+	SPORT_PUT_RCLKDIV(up, rclkdiv);
+	SSYNC();
+	pr_debug("%s sclk:%d, baud_rate:%d, tclkdiv:%d, rclkdiv:%d\n",
+			__func__, sclk, baud_rate, tclkdiv, rclkdiv);
+
+	return 0;
+}
+
+static irqreturn_t sport_uart_rx_irq(int irq, void *dev_id)
+{
+	struct sport_uart_port *up = dev_id;
+	struct tty_struct *tty = up->port.state->port.tty;
+	unsigned int ch;
+
+	spin_lock(&up->port.lock);
+
+	while (SPORT_GET_STAT(up) & RXNE) {
+		ch = rx_one_byte(up);
+		up->port.icount.rx++;
+
+		if (!uart_handle_sysrq_char(&up->port, ch))
+			tty_insert_flip_char(tty, ch, TTY_NORMAL);
+	}
+	tty_flip_buffer_push(tty);
+
+	spin_unlock(&up->port.lock);
+
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t sport_uart_tx_irq(int irq, void *dev_id)
+{
+	struct sport_uart_port *up = dev_id;
+
+	spin_lock(&up->port.lock);
+	sport_uart_tx_chars(up);
+	spin_unlock(&up->port.lock);
+
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t sport_uart_err_irq(int irq, void *dev_id)
+{
+	struct sport_uart_port *up = dev_id;
+	struct tty_struct *tty = up->port.state->port.tty;
+	unsigned int stat = SPORT_GET_STAT(up);
+
+	spin_lock(&up->port.lock);
+
+	/* Overflow in RX FIFO */
+	if (stat & ROVF) {
+		up->port.icount.overrun++;
+		tty_insert_flip_char(tty, 0, TTY_OVERRUN);
+		SPORT_PUT_STAT(up, ROVF); /* Clear ROVF bit */
+	}
+	/* These should not happen */
+	if (stat & (TOVF | TUVF | RUVF)) {
+		pr_err("SPORT Error:%s %s %s\n",
+		       (stat & TOVF) ? "TX overflow" : "",
+		       (stat & TUVF) ? "TX underflow" : "",
+		       (stat & RUVF) ? "RX underflow" : "");
+		SPORT_PUT_TCR1(up, SPORT_GET_TCR1(up) & ~TSPEN);
+		SPORT_PUT_RCR1(up, SPORT_GET_RCR1(up) & ~RSPEN);
+	}
+	SSYNC();
+
+	spin_unlock(&up->port.lock);
+	return IRQ_HANDLED;
+}
+
+#ifdef CONFIG_SERIAL_BFIN_SPORT_CTSRTS
+static unsigned int sport_get_mctrl(struct uart_port *port)
+{
+	struct sport_uart_port *up = (struct sport_uart_port *)port;
+	if (up->cts_pin < 0)
+		return TIOCM_CTS | TIOCM_DSR | TIOCM_CAR;
+
+	/* CTS PIN is negative assertive. */
+	if (SPORT_UART_GET_CTS(up))
+		return TIOCM_CTS | TIOCM_DSR | TIOCM_CAR;
+	else
+		return TIOCM_DSR | TIOCM_CAR;
+}
+
+static void sport_set_mctrl(struct uart_port *port, unsigned int mctrl)
+{
+	struct sport_uart_port *up = (struct sport_uart_port *)port;
+	if (up->rts_pin < 0)
+		return;
+
+	/* RTS PIN is negative assertive. */
+	if (mctrl & TIOCM_RTS)
+		SPORT_UART_ENABLE_RTS(up);
+	else
+		SPORT_UART_DISABLE_RTS(up);
+}
+
+/*
+ * Handle any change of modem status signal.
+ */
+static irqreturn_t sport_mctrl_cts_int(int irq, void *dev_id)
+{
+	struct sport_uart_port *up = (struct sport_uart_port *)dev_id;
+	unsigned int status;
+
+	status = sport_get_mctrl(&up->port);
+	uart_handle_cts_change(&up->port, status & TIOCM_CTS);
+
+	return IRQ_HANDLED;
+}
+#else
+static unsigned int sport_get_mctrl(struct uart_port *port)
+{
+	pr_debug("%s enter\n", __func__);
+	return TIOCM_CTS | TIOCM_CD | TIOCM_DSR;
+}
+
+static void sport_set_mctrl(struct uart_port *port, unsigned int mctrl)
+{
+	pr_debug("%s enter\n", __func__);
+}
+#endif
+
+/* Reqeust IRQ, Setup clock */
+static int sport_startup(struct uart_port *port)
+{
+	struct sport_uart_port *up = (struct sport_uart_port *)port;
+	int ret;
+
+	pr_debug("%s enter\n", __func__);
+	ret = request_irq(up->port.irq, sport_uart_rx_irq, 0,
+		"SPORT_UART_RX", up);
+	if (ret) {
+		dev_err(port->dev, "unable to request SPORT RX interrupt\n");
+		return ret;
+	}
+
+	ret = request_irq(up->port.irq+1, sport_uart_tx_irq, 0,
+		"SPORT_UART_TX", up);
+	if (ret) {
+		dev_err(port->dev, "unable to request SPORT TX interrupt\n");
+		goto fail1;
+	}
+
+	ret = request_irq(up->err_irq, sport_uart_err_irq, 0,
+		"SPORT_UART_STATUS", up);
+	if (ret) {
+		dev_err(port->dev, "unable to request SPORT status interrupt\n");
+		goto fail2;
+	}
+
+#ifdef CONFIG_SERIAL_BFIN_SPORT_CTSRTS
+	if (up->cts_pin >= 0) {
+		if (request_irq(gpio_to_irq(up->cts_pin),
+			sport_mctrl_cts_int,
+			IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING |
+			0, "BFIN_SPORT_UART_CTS", up)) {
+			up->cts_pin = -1;
+			dev_info(port->dev, "Unable to attach BlackFin UART over SPORT CTS interrupt. So, disable it.\n");
+		}
+	}
+	if (up->rts_pin >= 0) {
+		if (gpio_request(up->rts_pin, DRV_NAME)) {
+			dev_info(port->dev, "fail to request RTS PIN at GPIO_%d\n", up->rts_pin);
+			up->rts_pin = -1;
+		} else
+			gpio_direction_output(up->rts_pin, 0);
+	}
+#endif
+
+	return 0;
+ fail2:
+	free_irq(up->port.irq+1, up);
+ fail1:
+	free_irq(up->port.irq, up);
+
+	return ret;
+}
+
+/*
+ * sport_uart_tx_chars
+ *
+ * ret 1 means need to enable sport.
+ * ret 0 means do nothing.
+ */
+static int sport_uart_tx_chars(struct sport_uart_port *up)
+{
+	struct circ_buf *xmit = &up->port.state->xmit;
+
+	if (SPORT_GET_STAT(up) & TXF)
+		return 0;
+
+	if (up->port.x_char) {
+		tx_one_byte(up, up->port.x_char);
+		up->port.icount.tx++;
+		up->port.x_char = 0;
+		return 1;
+	}
+
+	if (uart_circ_empty(xmit) || uart_tx_stopped(&up->port)) {
+		/* The waiting loop to stop SPORT TX from TX interrupt is
+		 * too long. This may block SPORT RX interrupts and cause
+		 * RX FIFO overflow. So, do stop sport TX only after the last
+		 * char in TX FIFO is moved into the shift register.
+		 */
+		if (SPORT_GET_STAT(up) & TXHRE)
+			sport_stop_tx(&up->port);
+		return 0;
+	}
+
+	while(!(SPORT_GET_STAT(up) & TXF) && !uart_circ_empty(xmit)) {
+		tx_one_byte(up, xmit->buf[xmit->tail]);
+		xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE -1);
+		up->port.icount.tx++;
+	}
+
+	if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
+		uart_write_wakeup(&up->port);
+
+	return 1;
+}
+
+static unsigned int sport_tx_empty(struct uart_port *port)
+{
+	struct sport_uart_port *up = (struct sport_uart_port *)port;
+	unsigned int stat;
+
+	stat = SPORT_GET_STAT(up);
+	pr_debug("%s stat:%04x\n", __func__, stat);
+	if (stat & TXHRE) {
+		return TIOCSER_TEMT;
+	} else
+		return 0;
+}
+
+static void sport_stop_tx(struct uart_port *port)
+{
+	struct sport_uart_port *up = (struct sport_uart_port *)port;
+
+	pr_debug("%s enter\n", __func__);
+
+	if (!(SPORT_GET_TCR1(up) & TSPEN))
+		return;
+
+	/* Although the hold register is empty, last byte is still in shift
+	 * register and not sent out yet. So, put a dummy data into TX FIFO.
+	 * Then, sport tx stops when last byte is shift out and the dummy
+	 * data is moved into the shift register.
+	 */
+	SPORT_PUT_TX(up, 0xffff);
+	while (!(SPORT_GET_STAT(up) & TXHRE))
+		cpu_relax();
+
+	SPORT_PUT_TCR1(up, (SPORT_GET_TCR1(up) & ~TSPEN));
+	SSYNC();
+
+	return;
+}
+
+static void sport_start_tx(struct uart_port *port)
+{
+	struct sport_uart_port *up = (struct sport_uart_port *)port;
+
+	pr_debug("%s enter\n", __func__);
+
+	/* Write data into SPORT FIFO before enable SPROT to transmit */
+	if (sport_uart_tx_chars(up)) {
+		/* Enable transmit, then an interrupt will generated */
+		SPORT_PUT_TCR1(up, (SPORT_GET_TCR1(up) | TSPEN));
+		SSYNC();
+	}
+
+	pr_debug("%s exit\n", __func__);
+}
+
+static void sport_stop_rx(struct uart_port *port)
+{
+	struct sport_uart_port *up = (struct sport_uart_port *)port;
+
+	pr_debug("%s enter\n", __func__);
+	/* Disable sport to stop rx */
+	SPORT_PUT_RCR1(up, (SPORT_GET_RCR1(up) & ~RSPEN));
+	SSYNC();
+}
+
+static void sport_enable_ms(struct uart_port *port)
+{
+	pr_debug("%s enter\n", __func__);
+}
+
+static void sport_break_ctl(struct uart_port *port, int break_state)
+{
+	pr_debug("%s enter\n", __func__);
+}
+
+static void sport_shutdown(struct uart_port *port)
+{
+	struct sport_uart_port *up = (struct sport_uart_port *)port;
+
+	dev_dbg(port->dev, "%s enter\n", __func__);
+
+	/* Disable sport */
+	SPORT_PUT_TCR1(up, (SPORT_GET_TCR1(up) & ~TSPEN));
+	SPORT_PUT_RCR1(up, (SPORT_GET_RCR1(up) & ~RSPEN));
+	SSYNC();
+
+	free_irq(up->port.irq, up);
+	free_irq(up->port.irq+1, up);
+	free_irq(up->err_irq, up);
+#ifdef CONFIG_SERIAL_BFIN_SPORT_CTSRTS
+	if (up->cts_pin >= 0)
+		free_irq(gpio_to_irq(up->cts_pin), up);
+	if (up->rts_pin >= 0)
+		gpio_free(up->rts_pin);
+#endif
+}
+
+static const char *sport_type(struct uart_port *port)
+{
+	struct sport_uart_port *up = (struct sport_uart_port *)port;
+
+	pr_debug("%s enter\n", __func__);
+	return up->port.type == PORT_BFIN_SPORT ? "BFIN-SPORT-UART" : NULL;
+}
+
+static void sport_release_port(struct uart_port *port)
+{
+	pr_debug("%s enter\n", __func__);
+}
+
+static int sport_request_port(struct uart_port *port)
+{
+	pr_debug("%s enter\n", __func__);
+	return 0;
+}
+
+static void sport_config_port(struct uart_port *port, int flags)
+{
+	struct sport_uart_port *up = (struct sport_uart_port *)port;
+
+	pr_debug("%s enter\n", __func__);
+	up->port.type = PORT_BFIN_SPORT;
+}
+
+static int sport_verify_port(struct uart_port *port, struct serial_struct *ser)
+{
+	pr_debug("%s enter\n", __func__);
+	return 0;
+}
+
+static void sport_set_termios(struct uart_port *port,
+		struct ktermios *termios, struct ktermios *old)
+{
+	struct sport_uart_port *up = (struct sport_uart_port *)port;
+	unsigned long flags;
+	int i;
+
+	pr_debug("%s enter, c_cflag:%08x\n", __func__, termios->c_cflag);
+
+	switch (termios->c_cflag & CSIZE) {
+	case CS8:
+		up->csize = 8;
+		break;
+	case CS7:
+		up->csize = 7;
+		break;
+	case CS6:
+		up->csize = 6;
+		break;
+	case CS5:
+		up->csize = 5;
+		break;
+	default:
+		pr_warning("requested word length not supported\n");
+	}
+
+	if (termios->c_cflag & CSTOPB) {
+		up->stopb = 1;
+	}
+	if (termios->c_cflag & PARENB) {
+		pr_warning("PAREN bits is not supported yet\n");
+		/* up->parib = 1; */
+	}
+
+	spin_lock_irqsave(&up->port.lock, flags);
+
+	port->read_status_mask = 0;
+
+	/*
+	 * Characters to ignore
+	 */
+	port->ignore_status_mask = 0;
+
+	/* RX extract mask */
+	up->rxmask = 0x01 | (((up->csize + up->stopb) * 2 - 1) << 0x8);
+	/* TX masks, 8 bit data and 1 bit stop for example:
+	 * mask1 = b#0111111110
+	 * mask2 = b#1000000000
+	 */
+	for (i = 0, up->txmask1 = 0; i < up->csize; i++)
+		up->txmask1 |= (1<<i);
+	up->txmask2 = (1<<i);
+	if (up->stopb) {
+		++i;
+		up->txmask2 |= (1<<i);
+	}
+	up->txmask1 <<= 1;
+	up->txmask2 <<= 1;
+	/* uart baud rate */
+	port->uartclk = uart_get_baud_rate(port, termios, old, 0, get_sclk()/16);
+
+	/* Disable UART */
+	SPORT_PUT_TCR1(up, SPORT_GET_TCR1(up) & ~TSPEN);
+	SPORT_PUT_RCR1(up, SPORT_GET_RCR1(up) & ~RSPEN);
+
+	sport_uart_setup(up, up->csize + up->stopb, port->uartclk);
+
+	/* driver TX line high after config, one dummy data is
+	 * necessary to stop sport after shift one byte
+	 */
+	SPORT_PUT_TX(up, 0xffff);
+	SPORT_PUT_TX(up, 0xffff);
+	SPORT_PUT_TCR1(up, (SPORT_GET_TCR1(up) | TSPEN));
+	SSYNC();
+	while (!(SPORT_GET_STAT(up) & TXHRE))
+		cpu_relax();
+	SPORT_PUT_TCR1(up, SPORT_GET_TCR1(up) & ~TSPEN);
+	SSYNC();
+
+	/* Port speed changed, update the per-port timeout. */
+	uart_update_timeout(port, termios->c_cflag, port->uartclk);
+
+	/* Enable sport rx */
+	SPORT_PUT_RCR1(up, SPORT_GET_RCR1(up) | RSPEN);
+	SSYNC();
+
+	spin_unlock_irqrestore(&up->port.lock, flags);
+}
+
+struct uart_ops sport_uart_ops = {
+	.tx_empty	= sport_tx_empty,
+	.set_mctrl	= sport_set_mctrl,
+	.get_mctrl	= sport_get_mctrl,
+	.stop_tx	= sport_stop_tx,
+	.start_tx	= sport_start_tx,
+	.stop_rx	= sport_stop_rx,
+	.enable_ms	= sport_enable_ms,
+	.break_ctl	= sport_break_ctl,
+	.startup	= sport_startup,
+	.shutdown	= sport_shutdown,
+	.set_termios	= sport_set_termios,
+	.type		= sport_type,
+	.release_port	= sport_release_port,
+	.request_port	= sport_request_port,
+	.config_port	= sport_config_port,
+	.verify_port	= sport_verify_port,
+};
+
+#define BFIN_SPORT_UART_MAX_PORTS 4
+
+static struct sport_uart_port *bfin_sport_uart_ports[BFIN_SPORT_UART_MAX_PORTS];
+
+#ifdef CONFIG_SERIAL_BFIN_SPORT_CONSOLE
+#define CLASS_BFIN_SPORT_CONSOLE	"bfin-sport-console"
+
+static int __init
+sport_uart_console_setup(struct console *co, char *options)
+{
+	struct sport_uart_port *up;
+	int baud = 57600;
+	int bits = 8;
+	int parity = 'n';
+# ifdef CONFIG_SERIAL_BFIN_SPORT_CTSRTS
+	int flow = 'r';
+# else
+	int flow = 'n';
+# endif
+
+	/* Check whether an invalid uart number has been specified */
+	if (co->index < 0 || co->index >= BFIN_SPORT_UART_MAX_PORTS)
+		return -ENODEV;
+
+	up = bfin_sport_uart_ports[co->index];
+	if (!up)
+		return -ENODEV;
+
+	if (options)
+		uart_parse_options(options, &baud, &parity, &bits, &flow);
+
+	return uart_set_options(&up->port, co, baud, parity, bits, flow);
+}
+
+static void sport_uart_console_putchar(struct uart_port *port, int ch)
+{
+	struct sport_uart_port *up = (struct sport_uart_port *)port;
+
+	while (SPORT_GET_STAT(up) & TXF)
+		barrier();
+
+	tx_one_byte(up, ch);
+}
+
+/*
+ * Interrupts are disabled on entering
+ */
+static void
+sport_uart_console_write(struct console *co, const char *s, unsigned int count)
+{
+	struct sport_uart_port *up = bfin_sport_uart_ports[co->index];
+	unsigned long flags;
+
+	spin_lock_irqsave(&up->port.lock, flags);
+
+	if (SPORT_GET_TCR1(up) & TSPEN)
+		uart_console_write(&up->port, s, count, sport_uart_console_putchar);
+	else {
+		/* dummy data to start sport */
+		while (SPORT_GET_STAT(up) & TXF)
+			barrier();
+		SPORT_PUT_TX(up, 0xffff);
+		/* Enable transmit, then an interrupt will generated */
+		SPORT_PUT_TCR1(up, (SPORT_GET_TCR1(up) | TSPEN));
+		SSYNC();
+
+		uart_console_write(&up->port, s, count, sport_uart_console_putchar);
+
+		/* Although the hold register is empty, last byte is still in shift
+		 * register and not sent out yet. So, put a dummy data into TX FIFO.
+		 * Then, sport tx stops when last byte is shift out and the dummy
+		 * data is moved into the shift register.
+		 */
+		while (SPORT_GET_STAT(up) & TXF)
+			barrier();
+		SPORT_PUT_TX(up, 0xffff);
+		while (!(SPORT_GET_STAT(up) & TXHRE))
+			barrier();
+
+		/* Stop sport tx transfer */
+		SPORT_PUT_TCR1(up, (SPORT_GET_TCR1(up) & ~TSPEN));
+		SSYNC();
+	}
+
+	spin_unlock_irqrestore(&up->port.lock, flags);
+}
+
+static struct uart_driver sport_uart_reg;
+
+static struct console sport_uart_console = {
+	.name		= DEVICE_NAME,
+	.write		= sport_uart_console_write,
+	.device		= uart_console_device,
+	.setup		= sport_uart_console_setup,
+	.flags		= CON_PRINTBUFFER,
+	.index		= -1,
+	.data		= &sport_uart_reg,
+};
+
+#define SPORT_UART_CONSOLE	(&sport_uart_console)
+#else
+#define SPORT_UART_CONSOLE	NULL
+#endif /* CONFIG_SERIAL_BFIN_SPORT_CONSOLE */
+
+
+static struct uart_driver sport_uart_reg = {
+	.owner		= THIS_MODULE,
+	.driver_name	= DRV_NAME,
+	.dev_name	= DEVICE_NAME,
+	.major		= 204,
+	.minor		= 84,
+	.nr		= BFIN_SPORT_UART_MAX_PORTS,
+	.cons		= SPORT_UART_CONSOLE,
+};
+
+#ifdef CONFIG_PM
+static int sport_uart_suspend(struct device *dev)
+{
+	struct sport_uart_port *sport = dev_get_drvdata(dev);
+
+	dev_dbg(dev, "%s enter\n", __func__);
+	if (sport)
+		uart_suspend_port(&sport_uart_reg, &sport->port);
+
+	return 0;
+}
+
+static int sport_uart_resume(struct device *dev)
+{
+	struct sport_uart_port *sport = dev_get_drvdata(dev);
+
+	dev_dbg(dev, "%s enter\n", __func__);
+	if (sport)
+		uart_resume_port(&sport_uart_reg, &sport->port);
+
+	return 0;
+}
+
+static struct dev_pm_ops bfin_sport_uart_dev_pm_ops = {
+	.suspend	= sport_uart_suspend,
+	.resume		= sport_uart_resume,
+};
+#endif
+
+static int __devinit sport_uart_probe(struct platform_device *pdev)
+{
+	struct resource *res;
+	struct sport_uart_port *sport;
+	int ret = 0;
+
+	dev_dbg(&pdev->dev, "%s enter\n", __func__);
+
+	if (pdev->id < 0 || pdev->id >= BFIN_SPORT_UART_MAX_PORTS) {
+		dev_err(&pdev->dev, "Wrong sport uart platform device id.\n");
+		return -ENOENT;
+	}
+
+	if (bfin_sport_uart_ports[pdev->id] == NULL) {
+		bfin_sport_uart_ports[pdev->id] =
+			kzalloc(sizeof(struct sport_uart_port), GFP_KERNEL);
+		sport = bfin_sport_uart_ports[pdev->id];
+		if (!sport) {
+			dev_err(&pdev->dev,
+				"Fail to malloc sport_uart_port\n");
+			return -ENOMEM;
+		}
+
+		ret = peripheral_request_list(
+			(unsigned short *)pdev->dev.platform_data, DRV_NAME);
+		if (ret) {
+			dev_err(&pdev->dev,
+				"Fail to request SPORT peripherals\n");
+			goto out_error_free_mem;
+		}
+
+		spin_lock_init(&sport->port.lock);
+		sport->port.fifosize  = SPORT_TX_FIFO_SIZE,
+		sport->port.ops       = &sport_uart_ops;
+		sport->port.line      = pdev->id;
+		sport->port.iotype    = UPIO_MEM;
+		sport->port.flags     = UPF_BOOT_AUTOCONF;
+
+		res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+		if (res == NULL) {
+			dev_err(&pdev->dev, "Cannot get IORESOURCE_MEM\n");
+			ret = -ENOENT;
+			goto out_error_free_peripherals;
+		}
+
+		sport->port.membase = ioremap(res->start, resource_size(res));
+		if (!sport->port.membase) {
+			dev_err(&pdev->dev, "Cannot map sport IO\n");
+			ret = -ENXIO;
+			goto out_error_free_peripherals;
+		}
+		sport->port.mapbase = res->start;
+
+		sport->port.irq = platform_get_irq(pdev, 0);
+		if ((int)sport->port.irq < 0) {
+			dev_err(&pdev->dev, "No sport RX/TX IRQ specified\n");
+			ret = -ENOENT;
+			goto out_error_unmap;
+		}
+
+		sport->err_irq = platform_get_irq(pdev, 1);
+		if (sport->err_irq < 0) {
+			dev_err(&pdev->dev, "No sport status IRQ specified\n");
+			ret = -ENOENT;
+			goto out_error_unmap;
+		}
+#ifdef CONFIG_SERIAL_BFIN_SPORT_CTSRTS
+		res = platform_get_resource(pdev, IORESOURCE_IO, 0);
+		if (res == NULL)
+			sport->cts_pin = -1;
+		else {
+			sport->cts_pin = res->start;
+			sport->port.flags |= ASYNC_CTS_FLOW;
+		}
+
+		res = platform_get_resource(pdev, IORESOURCE_IO, 1);
+		if (res == NULL)
+			sport->rts_pin = -1;
+		else
+			sport->rts_pin = res->start;
+#endif
+	}
+
+#ifdef CONFIG_SERIAL_BFIN_SPORT_CONSOLE
+	if (!is_early_platform_device(pdev)) {
+#endif
+		sport = bfin_sport_uart_ports[pdev->id];
+		sport->port.dev = &pdev->dev;
+		dev_set_drvdata(&pdev->dev, sport);
+		ret = uart_add_one_port(&sport_uart_reg, &sport->port);
+#ifdef CONFIG_SERIAL_BFIN_SPORT_CONSOLE
+	}
+#endif
+	if (!ret)
+		return 0;
+
+	if (sport) {
+out_error_unmap:
+		iounmap(sport->port.membase);
+out_error_free_peripherals:
+		peripheral_free_list(
+			(unsigned short *)pdev->dev.platform_data);
+out_error_free_mem:
+		kfree(sport);
+		bfin_sport_uart_ports[pdev->id] = NULL;
+	}
+
+	return ret;
+}
+
+static int __devexit sport_uart_remove(struct platform_device *pdev)
+{
+	struct sport_uart_port *sport = platform_get_drvdata(pdev);
+
+	dev_dbg(&pdev->dev, "%s enter\n", __func__);
+	dev_set_drvdata(&pdev->dev, NULL);
+
+	if (sport) {
+		uart_remove_one_port(&sport_uart_reg, &sport->port);
+		iounmap(sport->port.membase);
+		peripheral_free_list(
+			(unsigned short *)pdev->dev.platform_data);
+		kfree(sport);
+		bfin_sport_uart_ports[pdev->id] = NULL;
+	}
+
+	return 0;
+}
+
+static struct platform_driver sport_uart_driver = {
+	.probe		= sport_uart_probe,
+	.remove		= __devexit_p(sport_uart_remove),
+	.driver		= {
+		.name	= DRV_NAME,
+#ifdef CONFIG_PM
+		.pm	= &bfin_sport_uart_dev_pm_ops,
+#endif
+	},
+};
+
+#ifdef CONFIG_SERIAL_BFIN_SPORT_CONSOLE
+static __initdata struct early_platform_driver early_sport_uart_driver = {
+	.class_str = CLASS_BFIN_SPORT_CONSOLE,
+	.pdrv = &sport_uart_driver,
+	.requested_id = EARLY_PLATFORM_ID_UNSET,
+};
+
+static int __init sport_uart_rs_console_init(void)
+{
+	early_platform_driver_register(&early_sport_uart_driver, DRV_NAME);
+
+	early_platform_driver_probe(CLASS_BFIN_SPORT_CONSOLE,
+		BFIN_SPORT_UART_MAX_PORTS, 0);
+
+	register_console(&sport_uart_console);
+
+	return 0;
+}
+console_initcall(sport_uart_rs_console_init);
+#endif
+
+static int __init sport_uart_init(void)
+{
+	int ret;
+
+	pr_info("Blackfin uart over sport driver\n");
+
+	ret = uart_register_driver(&sport_uart_reg);
+	if (ret) {
+		pr_err("failed to register %s:%d\n",
+				sport_uart_reg.driver_name, ret);
+		return ret;
+	}
+
+	ret = platform_driver_register(&sport_uart_driver);
+	if (ret) {
+		pr_err("failed to register sport uart driver:%d\n", ret);
+		uart_unregister_driver(&sport_uart_reg);
+	}
+
+	return ret;
+}
+module_init(sport_uart_init);
+
+static void __exit sport_uart_exit(void)
+{
+	platform_driver_unregister(&sport_uart_driver);
+	uart_unregister_driver(&sport_uart_reg);
+}
+module_exit(sport_uart_exit);
+
+MODULE_AUTHOR("Sonic Zhang, Roy Huang");
+MODULE_DESCRIPTION("Blackfin serial over SPORT driver");
+MODULE_LICENSE("GPL");
diff --git a/ap/os/linux/linux-3.4.x/drivers/tty/serial/bfin_sport_uart.h b/ap/os/linux/linux-3.4.x/drivers/tty/serial/bfin_sport_uart.h
new file mode 100644
index 0000000..e4510ea
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/tty/serial/bfin_sport_uart.h
@@ -0,0 +1,87 @@
+/*
+ * Blackfin On-Chip Sport Emulated UART Driver
+ *
+ * Copyright 2006-2008 Analog Devices Inc.
+ *
+ * Enter bugs at http://blackfin.uclinux.org/
+ *
+ * Licensed under the GPL-2 or later.
+ */
+
+/*
+ * This driver and the hardware supported are in term of EE-191 of ADI.
+ * http://www.analog.com/static/imported-files/application_notes/EE191.pdf 
+ * This application note describe how to implement a UART on a Sharc DSP,
+ * but this driver is implemented on Blackfin Processor.
+ * Transmit Frame Sync is not used by this driver to transfer data out.
+ */
+
+#ifndef _BFIN_SPORT_UART_H
+#define _BFIN_SPORT_UART_H
+
+#define OFFSET_TCR1		0x00	/* Transmit Configuration 1 Register */
+#define OFFSET_TCR2		0x04	/* Transmit Configuration 2 Register */
+#define OFFSET_TCLKDIV		0x08	/* Transmit Serial Clock Divider Register */
+#define OFFSET_TFSDIV		0x0C	/* Transmit Frame Sync Divider Register */
+#define OFFSET_TX		0x10	/* Transmit Data Register		*/
+#define OFFSET_RX		0x18	/* Receive Data Register		*/
+#define OFFSET_RCR1		0x20	/* Receive Configuration 1 Register	*/
+#define OFFSET_RCR2		0x24	/* Receive Configuration 2 Register	*/
+#define OFFSET_RCLKDIV		0x28	/* Receive Serial Clock Divider Register */
+#define OFFSET_RFSDIV		0x2c	/* Receive Frame Sync Divider Register */
+#define OFFSET_STAT		0x30	/* Status Register			*/
+
+#define SPORT_GET_TCR1(sport)		bfin_read16(((sport)->port.membase + OFFSET_TCR1))
+#define SPORT_GET_TCR2(sport)		bfin_read16(((sport)->port.membase + OFFSET_TCR2))
+#define SPORT_GET_TCLKDIV(sport)	bfin_read16(((sport)->port.membase + OFFSET_TCLKDIV))
+#define SPORT_GET_TFSDIV(sport)		bfin_read16(((sport)->port.membase + OFFSET_TFSDIV))
+#define SPORT_GET_TX(sport)		bfin_read16(((sport)->port.membase + OFFSET_TX))
+#define SPORT_GET_RX(sport)		bfin_read16(((sport)->port.membase + OFFSET_RX))
+/*
+ * If another interrupt fires while doing a 32-bit read from RX FIFO,
+ * a fake RX underflow error will be generated.  So disable interrupts
+ * to prevent interruption while reading the FIFO.
+ */
+#define SPORT_GET_RX32(sport) \
+({ \
+	unsigned int __ret; \
+	unsigned long flags; \
+	if (ANOMALY_05000473) \
+		local_irq_save(flags); \
+	__ret = bfin_read32((sport)->port.membase + OFFSET_RX); \
+	if (ANOMALY_05000473) \
+		local_irq_restore(flags); \
+	__ret; \
+})
+#define SPORT_GET_RCR1(sport)		bfin_read16(((sport)->port.membase + OFFSET_RCR1))
+#define SPORT_GET_RCR2(sport)		bfin_read16(((sport)->port.membase + OFFSET_RCR2))
+#define SPORT_GET_RCLKDIV(sport)	bfin_read16(((sport)->port.membase + OFFSET_RCLKDIV))
+#define SPORT_GET_RFSDIV(sport)		bfin_read16(((sport)->port.membase + OFFSET_RFSDIV))
+#define SPORT_GET_STAT(sport)		bfin_read16(((sport)->port.membase + OFFSET_STAT))
+
+#define SPORT_PUT_TCR1(sport, v)	bfin_write16(((sport)->port.membase + OFFSET_TCR1), v)
+#define SPORT_PUT_TCR2(sport, v)	bfin_write16(((sport)->port.membase + OFFSET_TCR2), v)
+#define SPORT_PUT_TCLKDIV(sport, v)	bfin_write16(((sport)->port.membase + OFFSET_TCLKDIV), v)
+#define SPORT_PUT_TFSDIV(sport, v)	bfin_write16(((sport)->port.membase + OFFSET_TFSDIV), v)
+#define SPORT_PUT_TX(sport, v)		bfin_write16(((sport)->port.membase + OFFSET_TX), v)
+#define SPORT_PUT_RX(sport, v)		bfin_write16(((sport)->port.membase + OFFSET_RX), v)
+#define SPORT_PUT_RCR1(sport, v)	bfin_write16(((sport)->port.membase + OFFSET_RCR1), v)
+#define SPORT_PUT_RCR2(sport, v)	bfin_write16(((sport)->port.membase + OFFSET_RCR2), v)
+#define SPORT_PUT_RCLKDIV(sport, v)	bfin_write16(((sport)->port.membase + OFFSET_RCLKDIV), v)
+#define SPORT_PUT_RFSDIV(sport, v)	bfin_write16(((sport)->port.membase + OFFSET_RFSDIV), v)
+#define SPORT_PUT_STAT(sport, v)	bfin_write16(((sport)->port.membase + OFFSET_STAT), v)
+
+#define SPORT_TX_FIFO_SIZE	8
+
+#define SPORT_UART_GET_CTS(x)		gpio_get_value(x->cts_pin)
+#define SPORT_UART_DISABLE_RTS(x)	gpio_set_value(x->rts_pin, 1)
+#define SPORT_UART_ENABLE_RTS(x)	gpio_set_value(x->rts_pin, 0)
+
+#if defined(CONFIG_SERIAL_BFIN_SPORT0_UART_CTSRTS) \
+	|| defined(CONFIG_SERIAL_BFIN_SPORT1_UART_CTSRTS) \
+	|| defined(CONFIG_SERIAL_BFIN_SPORT2_UART_CTSRTS) \
+	|| defined(CONFIG_SERIAL_BFIN_SPORT3_UART_CTSRTS)
+# define CONFIG_SERIAL_BFIN_SPORT_CTSRTS
+#endif
+
+#endif /* _BFIN_SPORT_UART_H */
diff --git a/ap/os/linux/linux-3.4.x/drivers/tty/serial/bfin_uart.c b/ap/os/linux/linux-3.4.x/drivers/tty/serial/bfin_uart.c
new file mode 100644
index 0000000..5832fde
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/tty/serial/bfin_uart.c
@@ -0,0 +1,1604 @@
+/*
+ * Blackfin On-Chip Serial Driver
+ *
+ * Copyright 2006-2010 Analog Devices Inc.
+ *
+ * Enter bugs at http://blackfin.uclinux.org/
+ *
+ * Licensed under the GPL-2 or later.
+ */
+
+#if defined(CONFIG_SERIAL_BFIN_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ)
+#define SUPPORT_SYSRQ
+#endif
+
+#define DRIVER_NAME "bfin-uart"
+#define pr_fmt(fmt) DRIVER_NAME ": " fmt
+
+#include <linux/module.h>
+#include <linux/ioport.h>
+#include <linux/gfp.h>
+#include <linux/io.h>
+#include <linux/init.h>
+#include <linux/console.h>
+#include <linux/sysrq.h>
+#include <linux/platform_device.h>
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
+#include <linux/serial_core.h>
+#include <linux/gpio.h>
+#include <linux/irq.h>
+#include <linux/kgdb.h>
+#include <linux/slab.h>
+#include <linux/dma-mapping.h>
+
+#include <asm/portmux.h>
+#include <asm/cacheflush.h>
+#include <asm/dma.h>
+
+#define port_membase(uart)     (((struct bfin_serial_port *)(uart))->port.membase)
+#define get_lsr_cache(uart)    (((struct bfin_serial_port *)(uart))->lsr)
+#define put_lsr_cache(uart, v) (((struct bfin_serial_port *)(uart))->lsr = (v))
+#include <asm/bfin_serial.h>
+
+#ifdef CONFIG_SERIAL_BFIN_MODULE
+# undef CONFIG_EARLY_PRINTK
+#endif
+
+#ifdef CONFIG_SERIAL_BFIN_MODULE
+# undef CONFIG_EARLY_PRINTK
+#endif
+
+/* UART name and device definitions */
+#define BFIN_SERIAL_DEV_NAME	"ttyBF"
+#define BFIN_SERIAL_MAJOR	204
+#define BFIN_SERIAL_MINOR	64
+
+static struct bfin_serial_port *bfin_serial_ports[BFIN_UART_NR_PORTS];
+
+#if defined(CONFIG_KGDB_SERIAL_CONSOLE) || \
+	defined(CONFIG_KGDB_SERIAL_CONSOLE_MODULE)
+
+# ifndef CONFIG_SERIAL_BFIN_PIO
+#  error KGDB only support UART in PIO mode.
+# endif
+
+static int kgdboc_port_line;
+static int kgdboc_break_enabled;
+#endif
+/*
+ * Setup for console. Argument comes from the menuconfig
+ */
+#define DMA_RX_XCOUNT		512
+#define DMA_RX_YCOUNT		(PAGE_SIZE / DMA_RX_XCOUNT)
+
+#define DMA_RX_FLUSH_JIFFIES	(HZ / 50)
+
+#ifdef CONFIG_SERIAL_BFIN_DMA
+static void bfin_serial_dma_tx_chars(struct bfin_serial_port *uart);
+#else
+static void bfin_serial_tx_chars(struct bfin_serial_port *uart);
+#endif
+
+static void bfin_serial_reset_irda(struct uart_port *port);
+
+#if defined(CONFIG_SERIAL_BFIN_CTSRTS) || \
+	defined(CONFIG_SERIAL_BFIN_HARD_CTSRTS)
+static unsigned int bfin_serial_get_mctrl(struct uart_port *port)
+{
+	struct bfin_serial_port *uart = (struct bfin_serial_port *)port;
+	if (uart->cts_pin < 0)
+		return TIOCM_CTS | TIOCM_DSR | TIOCM_CAR;
+
+	/* CTS PIN is negative assertive. */
+	if (UART_GET_CTS(uart))
+		return TIOCM_CTS | TIOCM_DSR | TIOCM_CAR;
+	else
+		return TIOCM_DSR | TIOCM_CAR;
+}
+
+static void bfin_serial_set_mctrl(struct uart_port *port, unsigned int mctrl)
+{
+	struct bfin_serial_port *uart = (struct bfin_serial_port *)port;
+	if (uart->rts_pin < 0)
+		return;
+
+	/* RTS PIN is negative assertive. */
+	if (mctrl & TIOCM_RTS)
+		UART_ENABLE_RTS(uart);
+	else
+		UART_DISABLE_RTS(uart);
+}
+
+/*
+ * Handle any change of modem status signal.
+ */
+static irqreturn_t bfin_serial_mctrl_cts_int(int irq, void *dev_id)
+{
+	struct bfin_serial_port *uart = dev_id;
+	unsigned int status = bfin_serial_get_mctrl(&uart->port);
+#ifdef CONFIG_SERIAL_BFIN_HARD_CTSRTS
+	struct tty_struct *tty = uart->port.state->port.tty;
+
+	UART_CLEAR_SCTS(uart);
+	if (tty->hw_stopped) {
+		if (status) {
+			tty->hw_stopped = 0;
+			uart_write_wakeup(&uart->port);
+		}
+	} else {
+		if (!status)
+			tty->hw_stopped = 1;
+	}
+#endif
+	uart_handle_cts_change(&uart->port, status & TIOCM_CTS);
+
+	return IRQ_HANDLED;
+}
+#else
+static unsigned int bfin_serial_get_mctrl(struct uart_port *port)
+{
+	return TIOCM_CTS | TIOCM_DSR | TIOCM_CAR;
+}
+
+static void bfin_serial_set_mctrl(struct uart_port *port, unsigned int mctrl)
+{
+}
+#endif
+
+/*
+ * interrupts are disabled on entry
+ */
+static void bfin_serial_stop_tx(struct uart_port *port)
+{
+	struct bfin_serial_port *uart = (struct bfin_serial_port *)port;
+#ifdef CONFIG_SERIAL_BFIN_DMA
+	struct circ_buf *xmit = &uart->port.state->xmit;
+#endif
+
+	while (!(UART_GET_LSR(uart) & TEMT))
+		cpu_relax();
+
+#ifdef CONFIG_SERIAL_BFIN_DMA
+	disable_dma(uart->tx_dma_channel);
+	xmit->tail = (xmit->tail + uart->tx_count) & (UART_XMIT_SIZE - 1);
+	uart->port.icount.tx += uart->tx_count;
+	uart->tx_count = 0;
+	uart->tx_done = 1;
+#else
+#ifdef CONFIG_BF54x
+	/* Clear TFI bit */
+	UART_PUT_LSR(uart, TFI);
+#endif
+	UART_CLEAR_IER(uart, ETBEI);
+#endif
+}
+
+/*
+ * port is locked and interrupts are disabled
+ */
+static void bfin_serial_start_tx(struct uart_port *port)
+{
+	struct bfin_serial_port *uart = (struct bfin_serial_port *)port;
+	struct tty_struct *tty = uart->port.state->port.tty;
+
+	/*
+	 * To avoid losting RX interrupt, we reset IR function
+	 * before sending data.
+	 */
+	if (tty->termios->c_line == N_IRDA)
+		bfin_serial_reset_irda(port);
+
+#ifdef CONFIG_SERIAL_BFIN_DMA
+	if (uart->tx_done)
+		bfin_serial_dma_tx_chars(uart);
+#else
+	UART_SET_IER(uart, ETBEI);
+	bfin_serial_tx_chars(uart);
+#endif
+}
+
+/*
+ * Interrupts are enabled
+ */
+static void bfin_serial_stop_rx(struct uart_port *port)
+{
+	struct bfin_serial_port *uart = (struct bfin_serial_port *)port;
+
+	UART_CLEAR_IER(uart, ERBFI);
+}
+
+/*
+ * Set the modem control timer to fire immediately.
+ */
+static void bfin_serial_enable_ms(struct uart_port *port)
+{
+}
+
+
+#if ANOMALY_05000363 && defined(CONFIG_SERIAL_BFIN_PIO)
+# define UART_GET_ANOMALY_THRESHOLD(uart)    ((uart)->anomaly_threshold)
+# define UART_SET_ANOMALY_THRESHOLD(uart, v) ((uart)->anomaly_threshold = (v))
+#else
+# define UART_GET_ANOMALY_THRESHOLD(uart)    0
+# define UART_SET_ANOMALY_THRESHOLD(uart, v)
+#endif
+
+#ifdef CONFIG_SERIAL_BFIN_PIO
+static void bfin_serial_rx_chars(struct bfin_serial_port *uart)
+{
+	struct tty_struct *tty = NULL;
+	unsigned int status, ch, flg;
+	static struct timeval anomaly_start = { .tv_sec = 0 };
+
+	status = UART_GET_LSR(uart);
+	UART_CLEAR_LSR(uart);
+
+	ch = UART_GET_CHAR(uart);
+	uart->port.icount.rx++;
+
+#if defined(CONFIG_KGDB_SERIAL_CONSOLE) || \
+	defined(CONFIG_KGDB_SERIAL_CONSOLE_MODULE)
+	if (kgdb_connected && kgdboc_port_line == uart->port.line
+		&& kgdboc_break_enabled)
+		if (ch == 0x3) {/* Ctrl + C */
+			kgdb_breakpoint();
+			return;
+		}
+
+	if (!uart->port.state || !uart->port.state->port.tty)
+		return;
+#endif
+	tty = uart->port.state->port.tty;
+
+	if (ANOMALY_05000363) {
+		/* The BF533 (and BF561) family of processors have a nice anomaly
+		 * where they continuously generate characters for a "single" break.
+		 * We have to basically ignore this flood until the "next" valid
+		 * character comes across.  Due to the nature of the flood, it is
+		 * not possible to reliably catch bytes that are sent too quickly
+		 * after this break.  So application code talking to the Blackfin
+		 * which sends a break signal must allow at least 1.5 character
+		 * times after the end of the break for things to stabilize.  This
+		 * timeout was picked as it must absolutely be larger than 1
+		 * character time +/- some percent.  So 1.5 sounds good.  All other
+		 * Blackfin families operate properly.  Woo.
+		 */
+		if (anomaly_start.tv_sec) {
+			struct timeval curr;
+			suseconds_t usecs;
+
+			if ((~ch & (~ch + 1)) & 0xff)
+				goto known_good_char;
+
+			do_gettimeofday(&curr);
+			if (curr.tv_sec - anomaly_start.tv_sec > 1)
+				goto known_good_char;
+
+			usecs = 0;
+			if (curr.tv_sec != anomaly_start.tv_sec)
+				usecs += USEC_PER_SEC;
+			usecs += curr.tv_usec - anomaly_start.tv_usec;
+
+			if (usecs > UART_GET_ANOMALY_THRESHOLD(uart))
+				goto known_good_char;
+
+			if (ch)
+				anomaly_start.tv_sec = 0;
+			else
+				anomaly_start = curr;
+
+			return;
+
+ known_good_char:
+			status &= ~BI;
+			anomaly_start.tv_sec = 0;
+		}
+	}
+
+	if (status & BI) {
+		if (ANOMALY_05000363)
+			if (bfin_revid() < 5)
+				do_gettimeofday(&anomaly_start);
+		uart->port.icount.brk++;
+		if (uart_handle_break(&uart->port))
+			goto ignore_char;
+		status &= ~(PE | FE);
+	}
+	if (status & PE)
+		uart->port.icount.parity++;
+	if (status & OE)
+		uart->port.icount.overrun++;
+	if (status & FE)
+		uart->port.icount.frame++;
+
+	status &= uart->port.read_status_mask;
+
+	if (status & BI)
+		flg = TTY_BREAK;
+	else if (status & PE)
+		flg = TTY_PARITY;
+	else if (status & FE)
+		flg = TTY_FRAME;
+	else
+		flg = TTY_NORMAL;
+
+	if (uart_handle_sysrq_char(&uart->port, ch))
+		goto ignore_char;
+
+	uart_insert_char(&uart->port, status, OE, ch, flg);
+
+ ignore_char:
+	tty_flip_buffer_push(tty);
+}
+
+static void bfin_serial_tx_chars(struct bfin_serial_port *uart)
+{
+	struct circ_buf *xmit = &uart->port.state->xmit;
+
+	if (uart_circ_empty(xmit) || uart_tx_stopped(&uart->port)) {
+#ifdef CONFIG_BF54x
+		/* Clear TFI bit */
+		UART_PUT_LSR(uart, TFI);
+#endif
+		/* Anomaly notes:
+		 *  05000215 -	we always clear ETBEI within last UART TX
+		 *		interrupt to end a string. It is always set
+		 *		when start a new tx.
+		 */
+		UART_CLEAR_IER(uart, ETBEI);
+		return;
+	}
+
+	if (uart->port.x_char) {
+		UART_PUT_CHAR(uart, uart->port.x_char);
+		uart->port.icount.tx++;
+		uart->port.x_char = 0;
+	}
+
+	while ((UART_GET_LSR(uart) & THRE) && xmit->tail != xmit->head) {
+		UART_PUT_CHAR(uart, xmit->buf[xmit->tail]);
+		xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
+		uart->port.icount.tx++;
+	}
+
+	if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
+		uart_write_wakeup(&uart->port);
+}
+
+static irqreturn_t bfin_serial_rx_int(int irq, void *dev_id)
+{
+	struct bfin_serial_port *uart = dev_id;
+
+	while (UART_GET_LSR(uart) & DR)
+		bfin_serial_rx_chars(uart);
+
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t bfin_serial_tx_int(int irq, void *dev_id)
+{
+	struct bfin_serial_port *uart = dev_id;
+
+	spin_lock(&uart->port.lock);
+	if (UART_GET_LSR(uart) & THRE)
+		bfin_serial_tx_chars(uart);
+	spin_unlock(&uart->port.lock);
+
+	return IRQ_HANDLED;
+}
+#endif
+
+#ifdef CONFIG_SERIAL_BFIN_DMA
+static void bfin_serial_dma_tx_chars(struct bfin_serial_port *uart)
+{
+	struct circ_buf *xmit = &uart->port.state->xmit;
+
+	uart->tx_done = 0;
+
+	if (uart_circ_empty(xmit) || uart_tx_stopped(&uart->port)) {
+		uart->tx_count = 0;
+		uart->tx_done = 1;
+		return;
+	}
+
+	if (uart->port.x_char) {
+		UART_PUT_CHAR(uart, uart->port.x_char);
+		uart->port.icount.tx++;
+		uart->port.x_char = 0;
+	}
+
+	uart->tx_count = CIRC_CNT(xmit->head, xmit->tail, UART_XMIT_SIZE);
+	if (uart->tx_count > (UART_XMIT_SIZE - xmit->tail))
+		uart->tx_count = UART_XMIT_SIZE - xmit->tail;
+	blackfin_dcache_flush_range((unsigned long)(xmit->buf+xmit->tail),
+					(unsigned long)(xmit->buf+xmit->tail+uart->tx_count));
+	set_dma_config(uart->tx_dma_channel,
+		set_bfin_dma_config(DIR_READ, DMA_FLOW_STOP,
+			INTR_ON_BUF,
+			DIMENSION_LINEAR,
+			DATA_SIZE_8,
+			DMA_SYNC_RESTART));
+	set_dma_start_addr(uart->tx_dma_channel, (unsigned long)(xmit->buf+xmit->tail));
+	set_dma_x_count(uart->tx_dma_channel, uart->tx_count);
+	set_dma_x_modify(uart->tx_dma_channel, 1);
+	SSYNC();
+	enable_dma(uart->tx_dma_channel);
+
+	UART_SET_IER(uart, ETBEI);
+}
+
+static void bfin_serial_dma_rx_chars(struct bfin_serial_port *uart)
+{
+	struct tty_struct *tty = uart->port.state->port.tty;
+	int i, flg, status;
+
+	status = UART_GET_LSR(uart);
+	UART_CLEAR_LSR(uart);
+
+	uart->port.icount.rx +=
+		CIRC_CNT(uart->rx_dma_buf.head, uart->rx_dma_buf.tail,
+		UART_XMIT_SIZE);
+
+	if (status & BI) {
+		uart->port.icount.brk++;
+		if (uart_handle_break(&uart->port))
+			goto dma_ignore_char;
+		status &= ~(PE | FE);
+	}
+	if (status & PE)
+		uart->port.icount.parity++;
+	if (status & OE)
+		uart->port.icount.overrun++;
+	if (status & FE)
+		uart->port.icount.frame++;
+
+	status &= uart->port.read_status_mask;
+
+	if (status & BI)
+		flg = TTY_BREAK;
+	else if (status & PE)
+		flg = TTY_PARITY;
+	else if (status & FE)
+		flg = TTY_FRAME;
+	else
+		flg = TTY_NORMAL;
+
+	for (i = uart->rx_dma_buf.tail; ; i++) {
+		if (i >= UART_XMIT_SIZE)
+			i = 0;
+		if (i == uart->rx_dma_buf.head)
+			break;
+		if (!uart_handle_sysrq_char(&uart->port, uart->rx_dma_buf.buf[i]))
+			uart_insert_char(&uart->port, status, OE,
+				uart->rx_dma_buf.buf[i], flg);
+	}
+
+ dma_ignore_char:
+	tty_flip_buffer_push(tty);
+}
+
+void bfin_serial_rx_dma_timeout(struct bfin_serial_port *uart)
+{
+	int x_pos, pos;
+
+	dma_disable_irq_nosync(uart->rx_dma_channel);
+	spin_lock_bh(&uart->rx_lock);
+
+	/* 2D DMA RX buffer ring is used. Because curr_y_count and
+	 * curr_x_count can't be read as an atomic operation,
+	 * curr_y_count should be read before curr_x_count. When
+	 * curr_x_count is read, curr_y_count may already indicate
+	 * next buffer line. But, the position calculated here is
+	 * still indicate the old line. The wrong position data may
+	 * be smaller than current buffer tail, which cause garbages
+	 * are received if it is not prohibit.
+	 */
+	uart->rx_dma_nrows = get_dma_curr_ycount(uart->rx_dma_channel);
+	x_pos = get_dma_curr_xcount(uart->rx_dma_channel);
+	uart->rx_dma_nrows = DMA_RX_YCOUNT - uart->rx_dma_nrows;
+	if (uart->rx_dma_nrows == DMA_RX_YCOUNT || x_pos == 0)
+		uart->rx_dma_nrows = 0;
+	x_pos = DMA_RX_XCOUNT - x_pos;
+	if (x_pos == DMA_RX_XCOUNT)
+		x_pos = 0;
+
+	pos = uart->rx_dma_nrows * DMA_RX_XCOUNT + x_pos;
+	/* Ignore receiving data if new position is in the same line of
+	 * current buffer tail and small.
+	 */
+	if (pos > uart->rx_dma_buf.tail ||
+		uart->rx_dma_nrows < (uart->rx_dma_buf.tail/DMA_RX_XCOUNT)) {
+		uart->rx_dma_buf.head = pos;
+		bfin_serial_dma_rx_chars(uart);
+		uart->rx_dma_buf.tail = uart->rx_dma_buf.head;
+	}
+
+	spin_unlock_bh(&uart->rx_lock);
+	dma_enable_irq(uart->rx_dma_channel);
+
+	mod_timer(&(uart->rx_dma_timer), jiffies + DMA_RX_FLUSH_JIFFIES);
+}
+
+static irqreturn_t bfin_serial_dma_tx_int(int irq, void *dev_id)
+{
+	struct bfin_serial_port *uart = dev_id;
+	struct circ_buf *xmit = &uart->port.state->xmit;
+
+	spin_lock(&uart->port.lock);
+	if (!(get_dma_curr_irqstat(uart->tx_dma_channel)&DMA_RUN)) {
+		disable_dma(uart->tx_dma_channel);
+		clear_dma_irqstat(uart->tx_dma_channel);
+		/* Anomaly notes:
+		 *  05000215 -	we always clear ETBEI within last UART TX
+		 *		interrupt to end a string. It is always set
+		 *		when start a new tx.
+		 */
+		UART_CLEAR_IER(uart, ETBEI);
+		uart->port.icount.tx += uart->tx_count;
+		if (!uart_circ_empty(xmit)) {
+			xmit->tail = (xmit->tail + uart->tx_count) & (UART_XMIT_SIZE - 1);
+
+			if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
+				uart_write_wakeup(&uart->port);
+		}
+
+		bfin_serial_dma_tx_chars(uart);
+	}
+
+	spin_unlock(&uart->port.lock);
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t bfin_serial_dma_rx_int(int irq, void *dev_id)
+{
+	struct bfin_serial_port *uart = dev_id;
+	unsigned short irqstat;
+	int x_pos, pos;
+
+	spin_lock(&uart->rx_lock);
+	irqstat = get_dma_curr_irqstat(uart->rx_dma_channel);
+	clear_dma_irqstat(uart->rx_dma_channel);
+
+	uart->rx_dma_nrows = get_dma_curr_ycount(uart->rx_dma_channel);
+	x_pos = get_dma_curr_xcount(uart->rx_dma_channel);
+	uart->rx_dma_nrows = DMA_RX_YCOUNT - uart->rx_dma_nrows;
+	if (uart->rx_dma_nrows == DMA_RX_YCOUNT || x_pos == 0)
+		uart->rx_dma_nrows = 0;
+
+	pos = uart->rx_dma_nrows * DMA_RX_XCOUNT;
+	if (pos > uart->rx_dma_buf.tail ||
+		uart->rx_dma_nrows < (uart->rx_dma_buf.tail/DMA_RX_XCOUNT)) {
+		uart->rx_dma_buf.head = pos;
+		bfin_serial_dma_rx_chars(uart);
+		uart->rx_dma_buf.tail = uart->rx_dma_buf.head;
+	}
+
+	spin_unlock(&uart->rx_lock);
+
+	return IRQ_HANDLED;
+}
+#endif
+
+/*
+ * Return TIOCSER_TEMT when transmitter is not busy.
+ */
+static unsigned int bfin_serial_tx_empty(struct uart_port *port)
+{
+	struct bfin_serial_port *uart = (struct bfin_serial_port *)port;
+	unsigned short lsr;
+
+	lsr = UART_GET_LSR(uart);
+	if (lsr & TEMT)
+		return TIOCSER_TEMT;
+	else
+		return 0;
+}
+
+static void bfin_serial_break_ctl(struct uart_port *port, int break_state)
+{
+	struct bfin_serial_port *uart = (struct bfin_serial_port *)port;
+	u16 lcr = UART_GET_LCR(uart);
+	if (break_state)
+		lcr |= SB;
+	else
+		lcr &= ~SB;
+	UART_PUT_LCR(uart, lcr);
+	SSYNC();
+}
+
+static int bfin_serial_startup(struct uart_port *port)
+{
+	struct bfin_serial_port *uart = (struct bfin_serial_port *)port;
+
+#ifdef CONFIG_SERIAL_BFIN_DMA
+	dma_addr_t dma_handle;
+
+	if (request_dma(uart->rx_dma_channel, "BFIN_UART_RX") < 0) {
+		printk(KERN_NOTICE "Unable to attach Blackfin UART RX DMA channel\n");
+		return -EBUSY;
+	}
+
+	if (request_dma(uart->tx_dma_channel, "BFIN_UART_TX") < 0) {
+		printk(KERN_NOTICE "Unable to attach Blackfin UART TX DMA channel\n");
+		free_dma(uart->rx_dma_channel);
+		return -EBUSY;
+	}
+
+	set_dma_callback(uart->rx_dma_channel, bfin_serial_dma_rx_int, uart);
+	set_dma_callback(uart->tx_dma_channel, bfin_serial_dma_tx_int, uart);
+
+	uart->rx_dma_buf.buf = (unsigned char *)dma_alloc_coherent(NULL, PAGE_SIZE, &dma_handle, GFP_DMA);
+	uart->rx_dma_buf.head = 0;
+	uart->rx_dma_buf.tail = 0;
+	uart->rx_dma_nrows = 0;
+
+	set_dma_config(uart->rx_dma_channel,
+		set_bfin_dma_config(DIR_WRITE, DMA_FLOW_AUTO,
+				INTR_ON_ROW, DIMENSION_2D,
+				DATA_SIZE_8,
+				DMA_SYNC_RESTART));
+	set_dma_x_count(uart->rx_dma_channel, DMA_RX_XCOUNT);
+	set_dma_x_modify(uart->rx_dma_channel, 1);
+	set_dma_y_count(uart->rx_dma_channel, DMA_RX_YCOUNT);
+	set_dma_y_modify(uart->rx_dma_channel, 1);
+	set_dma_start_addr(uart->rx_dma_channel, (unsigned long)uart->rx_dma_buf.buf);
+	enable_dma(uart->rx_dma_channel);
+
+	uart->rx_dma_timer.data = (unsigned long)(uart);
+	uart->rx_dma_timer.function = (void *)bfin_serial_rx_dma_timeout;
+	uart->rx_dma_timer.expires = jiffies + DMA_RX_FLUSH_JIFFIES;
+	add_timer(&(uart->rx_dma_timer));
+#else
+# if defined(CONFIG_KGDB_SERIAL_CONSOLE) || \
+	defined(CONFIG_KGDB_SERIAL_CONSOLE_MODULE)
+	if (kgdboc_port_line == uart->port.line && kgdboc_break_enabled)
+		kgdboc_break_enabled = 0;
+	else {
+# endif
+	if (request_irq(uart->rx_irq, bfin_serial_rx_int, 0,
+	     "BFIN_UART_RX", uart)) {
+		printk(KERN_NOTICE "Unable to attach BlackFin UART RX interrupt\n");
+		return -EBUSY;
+	}
+
+	if (request_irq
+	    (uart->tx_irq, bfin_serial_tx_int, 0,
+	     "BFIN_UART_TX", uart)) {
+		printk(KERN_NOTICE "Unable to attach BlackFin UART TX interrupt\n");
+		free_irq(uart->rx_irq, uart);
+		return -EBUSY;
+	}
+
+# ifdef CONFIG_BF54x
+	{
+		/*
+		 * UART2 and UART3 on BF548 share interrupt PINs and DMA
+		 * controllers with SPORT2 and SPORT3. UART rx and tx
+		 * interrupts are generated in PIO mode only when configure
+		 * their peripheral mapping registers properly, which means
+		 * request corresponding DMA channels in PIO mode as well.
+		 */
+		unsigned uart_dma_ch_rx, uart_dma_ch_tx;
+
+		switch (uart->rx_irq) {
+		case IRQ_UART3_RX:
+			uart_dma_ch_rx = CH_UART3_RX;
+			uart_dma_ch_tx = CH_UART3_TX;
+			break;
+		case IRQ_UART2_RX:
+			uart_dma_ch_rx = CH_UART2_RX;
+			uart_dma_ch_tx = CH_UART2_TX;
+			break;
+		default:
+			uart_dma_ch_rx = uart_dma_ch_tx = 0;
+			break;
+		};
+
+		if (uart_dma_ch_rx &&
+			request_dma(uart_dma_ch_rx, "BFIN_UART_RX") < 0) {
+			printk(KERN_NOTICE"Fail to attach UART interrupt\n");
+			free_irq(uart->rx_irq, uart);
+			free_irq(uart->tx_irq, uart);
+			return -EBUSY;
+		}
+		if (uart_dma_ch_tx &&
+			request_dma(uart_dma_ch_tx, "BFIN_UART_TX") < 0) {
+			printk(KERN_NOTICE "Fail to attach UART interrupt\n");
+			free_dma(uart_dma_ch_rx);
+			free_irq(uart->rx_irq, uart);
+			free_irq(uart->tx_irq, uart);
+			return -EBUSY;
+		}
+	}
+# endif
+# if defined(CONFIG_KGDB_SERIAL_CONSOLE) || \
+	defined(CONFIG_KGDB_SERIAL_CONSOLE_MODULE)
+	}
+# endif
+#endif
+
+#ifdef CONFIG_SERIAL_BFIN_CTSRTS
+	if (uart->cts_pin >= 0) {
+		if (request_irq(gpio_to_irq(uart->cts_pin),
+			bfin_serial_mctrl_cts_int,
+			IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING |
+			0, "BFIN_UART_CTS", uart)) {
+			uart->cts_pin = -1;
+			pr_info("Unable to attach BlackFin UART CTS interrupt. So, disable it.\n");
+		}
+	}
+	if (uart->rts_pin >= 0) {
+		if (gpio_request(uart->rts_pin, DRIVER_NAME)) {
+			pr_info("fail to request RTS PIN at GPIO_%d\n", uart->rts_pin);
+			uart->rts_pin = -1;
+		} else
+			gpio_direction_output(uart->rts_pin, 0);
+	}
+#endif
+#ifdef CONFIG_SERIAL_BFIN_HARD_CTSRTS
+	if (uart->cts_pin >= 0) {
+		if (request_irq(uart->status_irq, bfin_serial_mctrl_cts_int,
+			IRQF_DISABLED, "BFIN_UART_MODEM_STATUS", uart)) {
+			uart->cts_pin = -1;
+			dev_info(port->dev, "Unable to attach BlackFin UART Modem Status interrupt.\n");
+		}
+
+		/* CTS RTS PINs are negative assertive. */
+		UART_PUT_MCR(uart, ACTS);
+		UART_SET_IER(uart, EDSSI);
+	}
+#endif
+
+	UART_SET_IER(uart, ERBFI);
+	return 0;
+}
+
+static void bfin_serial_shutdown(struct uart_port *port)
+{
+	struct bfin_serial_port *uart = (struct bfin_serial_port *)port;
+
+#ifdef CONFIG_SERIAL_BFIN_DMA
+	disable_dma(uart->tx_dma_channel);
+	free_dma(uart->tx_dma_channel);
+	disable_dma(uart->rx_dma_channel);
+	free_dma(uart->rx_dma_channel);
+	del_timer(&(uart->rx_dma_timer));
+	dma_free_coherent(NULL, PAGE_SIZE, uart->rx_dma_buf.buf, 0);
+#else
+#ifdef CONFIG_BF54x
+	switch (uart->port.irq) {
+	case IRQ_UART3_RX:
+		free_dma(CH_UART3_RX);
+		free_dma(CH_UART3_TX);
+		break;
+	case IRQ_UART2_RX:
+		free_dma(CH_UART2_RX);
+		free_dma(CH_UART2_TX);
+		break;
+	default:
+		break;
+	};
+#endif
+	free_irq(uart->rx_irq, uart);
+	free_irq(uart->tx_irq, uart);
+#endif
+
+#ifdef CONFIG_SERIAL_BFIN_CTSRTS
+	if (uart->cts_pin >= 0)
+		free_irq(gpio_to_irq(uart->cts_pin), uart);
+	if (uart->rts_pin >= 0)
+		gpio_free(uart->rts_pin);
+#endif
+#ifdef CONFIG_SERIAL_BFIN_HARD_CTSRTS
+	if (uart->cts_pin >= 0)
+		free_irq(uart->status_irq, uart);
+#endif
+}
+
+static void
+bfin_serial_set_termios(struct uart_port *port, struct ktermios *termios,
+		   struct ktermios *old)
+{
+	struct bfin_serial_port *uart = (struct bfin_serial_port *)port;
+	unsigned long flags;
+	unsigned int baud, quot;
+	unsigned short val, ier, lcr = 0;
+
+	switch (termios->c_cflag & CSIZE) {
+	case CS8:
+		lcr = WLS(8);
+		break;
+	case CS7:
+		lcr = WLS(7);
+		break;
+	case CS6:
+		lcr = WLS(6);
+		break;
+	case CS5:
+		lcr = WLS(5);
+		break;
+	default:
+		printk(KERN_ERR "%s: word lengh not supported\n",
+			__func__);
+	}
+
+	/* Anomaly notes:
+	 *  05000231 -  STOP bit is always set to 1 whatever the user is set.
+	 */
+	if (termios->c_cflag & CSTOPB) {
+		if (ANOMALY_05000231)
+			printk(KERN_WARNING "STOP bits other than 1 is not "
+				"supported in case of anomaly 05000231.\n");
+		else
+			lcr |= STB;
+	}
+	if (termios->c_cflag & PARENB)
+		lcr |= PEN;
+	if (!(termios->c_cflag & PARODD))
+		lcr |= EPS;
+	if (termios->c_cflag & CMSPAR)
+		lcr |= STP;
+
+	spin_lock_irqsave(&uart->port.lock, flags);
+
+	port->read_status_mask = OE;
+	if (termios->c_iflag & INPCK)
+		port->read_status_mask |= (FE | PE);
+	if (termios->c_iflag & (BRKINT | PARMRK))
+		port->read_status_mask |= BI;
+
+	/*
+	 * Characters to ignore
+	 */
+	port->ignore_status_mask = 0;
+	if (termios->c_iflag & IGNPAR)
+		port->ignore_status_mask |= FE | PE;
+	if (termios->c_iflag & IGNBRK) {
+		port->ignore_status_mask |= BI;
+		/*
+		 * If we're ignoring parity and break indicators,
+		 * ignore overruns too (for real raw support).
+		 */
+		if (termios->c_iflag & IGNPAR)
+			port->ignore_status_mask |= OE;
+	}
+
+	baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk/16);
+	quot = uart_get_divisor(port, baud);
+
+	/* If discipline is not IRDA, apply ANOMALY_05000230 */
+	if (termios->c_line != N_IRDA)
+		quot -= ANOMALY_05000230;
+
+	UART_SET_ANOMALY_THRESHOLD(uart, USEC_PER_SEC / baud * 15);
+
+	/* Disable UART */
+	ier = UART_GET_IER(uart);
+	UART_DISABLE_INTS(uart);
+
+	/* Set DLAB in LCR to Access DLL and DLH */
+	UART_SET_DLAB(uart);
+
+	UART_PUT_DLL(uart, quot & 0xFF);
+	UART_PUT_DLH(uart, (quot >> 8) & 0xFF);
+	SSYNC();
+
+	/* Clear DLAB in LCR to Access THR RBR IER */
+	UART_CLEAR_DLAB(uart);
+
+	UART_PUT_LCR(uart, lcr);
+
+	/* Enable UART */
+	UART_ENABLE_INTS(uart, ier);
+
+	val = UART_GET_GCTL(uart);
+	val |= UCEN;
+	UART_PUT_GCTL(uart, val);
+
+	/* Port speed changed, update the per-port timeout. */
+	uart_update_timeout(port, termios->c_cflag, baud);
+
+	spin_unlock_irqrestore(&uart->port.lock, flags);
+}
+
+static const char *bfin_serial_type(struct uart_port *port)
+{
+	struct bfin_serial_port *uart = (struct bfin_serial_port *)port;
+
+	return uart->port.type == PORT_BFIN ? "BFIN-UART" : NULL;
+}
+
+/*
+ * Release the memory region(s) being used by 'port'.
+ */
+static void bfin_serial_release_port(struct uart_port *port)
+{
+}
+
+/*
+ * Request the memory region(s) being used by 'port'.
+ */
+static int bfin_serial_request_port(struct uart_port *port)
+{
+	return 0;
+}
+
+/*
+ * Configure/autoconfigure the port.
+ */
+static void bfin_serial_config_port(struct uart_port *port, int flags)
+{
+	struct bfin_serial_port *uart = (struct bfin_serial_port *)port;
+
+	if (flags & UART_CONFIG_TYPE &&
+	    bfin_serial_request_port(&uart->port) == 0)
+		uart->port.type = PORT_BFIN;
+}
+
+/*
+ * Verify the new serial_struct (for TIOCSSERIAL).
+ * The only change we allow are to the flags and type, and
+ * even then only between PORT_BFIN and PORT_UNKNOWN
+ */
+static int
+bfin_serial_verify_port(struct uart_port *port, struct serial_struct *ser)
+{
+	return 0;
+}
+
+/*
+ * Enable the IrDA function if tty->ldisc.num is N_IRDA.
+ * In other cases, disable IrDA function.
+ */
+static void bfin_serial_set_ldisc(struct uart_port *port, int ld)
+{
+	struct bfin_serial_port *uart = (struct bfin_serial_port *)port;
+	unsigned short val;
+
+	switch (ld) {
+	case N_IRDA:
+		val = UART_GET_GCTL(uart);
+		val |= (IREN | RPOLC);
+		UART_PUT_GCTL(uart, val);
+		break;
+	default:
+		val = UART_GET_GCTL(uart);
+		val &= ~(IREN | RPOLC);
+		UART_PUT_GCTL(uart, val);
+	}
+}
+
+static void bfin_serial_reset_irda(struct uart_port *port)
+{
+	struct bfin_serial_port *uart = (struct bfin_serial_port *)port;
+	unsigned short val;
+
+	val = UART_GET_GCTL(uart);
+	val &= ~(IREN | RPOLC);
+	UART_PUT_GCTL(uart, val);
+	SSYNC();
+	val |= (IREN | RPOLC);
+	UART_PUT_GCTL(uart, val);
+	SSYNC();
+}
+
+#ifdef CONFIG_CONSOLE_POLL
+/* Anomaly notes:
+ *  05000099 -  Because we only use THRE in poll_put and DR in poll_get,
+ *		losing other bits of UART_LSR is not a problem here.
+ */
+static void bfin_serial_poll_put_char(struct uart_port *port, unsigned char chr)
+{
+	struct bfin_serial_port *uart = (struct bfin_serial_port *)port;
+
+	while (!(UART_GET_LSR(uart) & THRE))
+		cpu_relax();
+
+	UART_CLEAR_DLAB(uart);
+	UART_PUT_CHAR(uart, (unsigned char)chr);
+}
+
+static int bfin_serial_poll_get_char(struct uart_port *port)
+{
+	struct bfin_serial_port *uart = (struct bfin_serial_port *)port;
+	unsigned char chr;
+
+	while (!(UART_GET_LSR(uart) & DR))
+		cpu_relax();
+
+	UART_CLEAR_DLAB(uart);
+	chr = UART_GET_CHAR(uart);
+
+	return chr;
+}
+#endif
+
+#if defined(CONFIG_KGDB_SERIAL_CONSOLE) || \
+	defined(CONFIG_KGDB_SERIAL_CONSOLE_MODULE)
+static void bfin_kgdboc_port_shutdown(struct uart_port *port)
+{
+	if (kgdboc_break_enabled) {
+		kgdboc_break_enabled = 0;
+		bfin_serial_shutdown(port);
+	}
+}
+
+static int bfin_kgdboc_port_startup(struct uart_port *port)
+{
+	kgdboc_port_line = port->line;
+	kgdboc_break_enabled = !bfin_serial_startup(port);
+	return 0;
+}
+#endif
+
+static struct uart_ops bfin_serial_pops = {
+	.tx_empty	= bfin_serial_tx_empty,
+	.set_mctrl	= bfin_serial_set_mctrl,
+	.get_mctrl	= bfin_serial_get_mctrl,
+	.stop_tx	= bfin_serial_stop_tx,
+	.start_tx	= bfin_serial_start_tx,
+	.stop_rx	= bfin_serial_stop_rx,
+	.enable_ms	= bfin_serial_enable_ms,
+	.break_ctl	= bfin_serial_break_ctl,
+	.startup	= bfin_serial_startup,
+	.shutdown	= bfin_serial_shutdown,
+	.set_termios	= bfin_serial_set_termios,
+	.set_ldisc	= bfin_serial_set_ldisc,
+	.type		= bfin_serial_type,
+	.release_port	= bfin_serial_release_port,
+	.request_port	= bfin_serial_request_port,
+	.config_port	= bfin_serial_config_port,
+	.verify_port	= bfin_serial_verify_port,
+#if defined(CONFIG_KGDB_SERIAL_CONSOLE) || \
+	defined(CONFIG_KGDB_SERIAL_CONSOLE_MODULE)
+	.kgdboc_port_startup	= bfin_kgdboc_port_startup,
+	.kgdboc_port_shutdown	= bfin_kgdboc_port_shutdown,
+#endif
+#ifdef CONFIG_CONSOLE_POLL
+	.poll_put_char	= bfin_serial_poll_put_char,
+	.poll_get_char	= bfin_serial_poll_get_char,
+#endif
+};
+
+#if defined(CONFIG_SERIAL_BFIN_CONSOLE) || defined(CONFIG_EARLY_PRINTK)
+/*
+ * If the port was already initialised (eg, by a boot loader),
+ * try to determine the current setup.
+ */
+static void __init
+bfin_serial_console_get_options(struct bfin_serial_port *uart, int *baud,
+			   int *parity, int *bits)
+{
+	unsigned short status;
+
+	status = UART_GET_IER(uart) & (ERBFI | ETBEI);
+	if (status == (ERBFI | ETBEI)) {
+		/* ok, the port was enabled */
+		u16 lcr, dlh, dll;
+
+		lcr = UART_GET_LCR(uart);
+
+		*parity = 'n';
+		if (lcr & PEN) {
+			if (lcr & EPS)
+				*parity = 'e';
+			else
+				*parity = 'o';
+		}
+		switch (lcr & 0x03) {
+		case 0:
+			*bits = 5;
+			break;
+		case 1:
+			*bits = 6;
+			break;
+		case 2:
+			*bits = 7;
+			break;
+		case 3:
+			*bits = 8;
+			break;
+		}
+		/* Set DLAB in LCR to Access DLL and DLH */
+		UART_SET_DLAB(uart);
+
+		dll = UART_GET_DLL(uart);
+		dlh = UART_GET_DLH(uart);
+
+		/* Clear DLAB in LCR to Access THR RBR IER */
+		UART_CLEAR_DLAB(uart);
+
+		*baud = get_sclk() / (16*(dll | dlh << 8));
+	}
+	pr_debug("%s:baud = %d, parity = %c, bits= %d\n", __func__, *baud, *parity, *bits);
+}
+
+static struct uart_driver bfin_serial_reg;
+
+static void bfin_serial_console_putchar(struct uart_port *port, int ch)
+{
+	struct bfin_serial_port *uart = (struct bfin_serial_port *)port;
+	while (!(UART_GET_LSR(uart) & THRE))
+		barrier();
+	UART_PUT_CHAR(uart, ch);
+}
+
+#endif /* defined (CONFIG_SERIAL_BFIN_CONSOLE) ||
+		 defined (CONFIG_EARLY_PRINTK) */
+
+#ifdef CONFIG_SERIAL_BFIN_CONSOLE
+#define CLASS_BFIN_CONSOLE	"bfin-console"
+/*
+ * Interrupts are disabled on entering
+ */
+static void
+bfin_serial_console_write(struct console *co, const char *s, unsigned int count)
+{
+	struct bfin_serial_port *uart = bfin_serial_ports[co->index];
+	unsigned long flags;
+
+	spin_lock_irqsave(&uart->port.lock, flags);
+	uart_console_write(&uart->port, s, count, bfin_serial_console_putchar);
+	spin_unlock_irqrestore(&uart->port.lock, flags);
+
+}
+
+static int __init
+bfin_serial_console_setup(struct console *co, char *options)
+{
+	struct bfin_serial_port *uart;
+	int baud = 57600;
+	int bits = 8;
+	int parity = 'n';
+# if defined(CONFIG_SERIAL_BFIN_CTSRTS) || \
+	defined(CONFIG_SERIAL_BFIN_HARD_CTSRTS)
+	int flow = 'r';
+# else
+	int flow = 'n';
+# endif
+
+	/*
+	 * 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 < 0 || co->index >= BFIN_UART_NR_PORTS)
+		return -ENODEV;
+
+	uart = bfin_serial_ports[co->index];
+	if (!uart)
+		return -ENODEV;
+
+	if (options)
+		uart_parse_options(options, &baud, &parity, &bits, &flow);
+	else
+		bfin_serial_console_get_options(uart, &baud, &parity, &bits);
+
+	return uart_set_options(&uart->port, co, baud, parity, bits, flow);
+}
+
+static struct console bfin_serial_console = {
+	.name		= BFIN_SERIAL_DEV_NAME,
+	.write		= bfin_serial_console_write,
+	.device		= uart_console_device,
+	.setup		= bfin_serial_console_setup,
+	.flags		= CON_PRINTBUFFER,
+	.index		= -1,
+	.data		= &bfin_serial_reg,
+};
+#define BFIN_SERIAL_CONSOLE	(&bfin_serial_console)
+#else
+#define BFIN_SERIAL_CONSOLE	NULL
+#endif /* CONFIG_SERIAL_BFIN_CONSOLE */
+
+#ifdef	CONFIG_EARLY_PRINTK
+static struct bfin_serial_port bfin_earlyprintk_port;
+#define CLASS_BFIN_EARLYPRINTK	"bfin-earlyprintk"
+
+/*
+ * Interrupts are disabled on entering
+ */
+static void
+bfin_earlyprintk_console_write(struct console *co, const char *s, unsigned int count)
+{
+	unsigned long flags;
+
+	if (bfin_earlyprintk_port.port.line != co->index)
+		return;
+
+	spin_lock_irqsave(&bfin_earlyprintk_port.port.lock, flags);
+	uart_console_write(&bfin_earlyprintk_port.port, s, count,
+		bfin_serial_console_putchar);
+	spin_unlock_irqrestore(&bfin_earlyprintk_port.port.lock, flags);
+}
+
+/*
+ * This should have a .setup or .early_setup in it, but then things get called
+ * without the command line options, and the baud rate gets messed up - so
+ * don't let the common infrastructure play with things. (see calls to setup
+ * & earlysetup in ./kernel/printk.c:register_console()
+ */
+static struct __initdata console bfin_early_serial_console = {
+	.name = "early_BFuart",
+	.write = bfin_earlyprintk_console_write,
+	.device = uart_console_device,
+	.flags = CON_PRINTBUFFER,
+	.index = -1,
+	.data  = &bfin_serial_reg,
+};
+#endif
+
+static struct uart_driver bfin_serial_reg = {
+	.owner			= THIS_MODULE,
+	.driver_name		= DRIVER_NAME,
+	.dev_name		= BFIN_SERIAL_DEV_NAME,
+	.major			= BFIN_SERIAL_MAJOR,
+	.minor			= BFIN_SERIAL_MINOR,
+	.nr			= BFIN_UART_NR_PORTS,
+	.cons			= BFIN_SERIAL_CONSOLE,
+};
+
+static int bfin_serial_suspend(struct platform_device *pdev, pm_message_t state)
+{
+	struct bfin_serial_port *uart = platform_get_drvdata(pdev);
+
+	return uart_suspend_port(&bfin_serial_reg, &uart->port);
+}
+
+static int bfin_serial_resume(struct platform_device *pdev)
+{
+	struct bfin_serial_port *uart = platform_get_drvdata(pdev);
+
+	return uart_resume_port(&bfin_serial_reg, &uart->port);
+}
+
+static int bfin_serial_probe(struct platform_device *pdev)
+{
+	struct resource *res;
+	struct bfin_serial_port *uart = NULL;
+	int ret = 0;
+
+	if (pdev->id < 0 || pdev->id >= BFIN_UART_NR_PORTS) {
+		dev_err(&pdev->dev, "Wrong bfin uart platform device id.\n");
+		return -ENOENT;
+	}
+
+	if (bfin_serial_ports[pdev->id] == NULL) {
+
+		uart = kzalloc(sizeof(*uart), GFP_KERNEL);
+		if (!uart) {
+			dev_err(&pdev->dev,
+				"fail to malloc bfin_serial_port\n");
+			return -ENOMEM;
+		}
+		bfin_serial_ports[pdev->id] = uart;
+
+#ifdef CONFIG_EARLY_PRINTK
+		if (!(bfin_earlyprintk_port.port.membase
+			&& bfin_earlyprintk_port.port.line == pdev->id)) {
+			/*
+			 * If the peripheral PINs of current port is allocated
+			 * in earlyprintk probe stage, don't do it again.
+			 */
+#endif
+		ret = peripheral_request_list(
+			(unsigned short *)pdev->dev.platform_data, DRIVER_NAME);
+		if (ret) {
+			dev_err(&pdev->dev,
+				"fail to request bfin serial peripherals\n");
+			goto out_error_free_mem;
+		}
+#ifdef CONFIG_EARLY_PRINTK
+		}
+#endif
+
+		spin_lock_init(&uart->port.lock);
+		uart->port.uartclk   = get_sclk();
+		uart->port.fifosize  = BFIN_UART_TX_FIFO_SIZE;
+		uart->port.ops       = &bfin_serial_pops;
+		uart->port.line      = pdev->id;
+		uart->port.iotype    = UPIO_MEM;
+		uart->port.flags     = UPF_BOOT_AUTOCONF;
+
+		res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+		if (res == NULL) {
+			dev_err(&pdev->dev, "Cannot get IORESOURCE_MEM\n");
+			ret = -ENOENT;
+			goto out_error_free_peripherals;
+		}
+
+		uart->port.membase = ioremap(res->start, resource_size(res));
+		if (!uart->port.membase) {
+			dev_err(&pdev->dev, "Cannot map uart IO\n");
+			ret = -ENXIO;
+			goto out_error_free_peripherals;
+		}
+		uart->port.mapbase = res->start;
+
+		uart->tx_irq = platform_get_irq(pdev, 0);
+		if (uart->tx_irq < 0) {
+			dev_err(&pdev->dev, "No uart TX IRQ specified\n");
+			ret = -ENOENT;
+			goto out_error_unmap;
+		}
+
+		uart->rx_irq = platform_get_irq(pdev, 1);
+		if (uart->rx_irq < 0) {
+			dev_err(&pdev->dev, "No uart RX IRQ specified\n");
+			ret = -ENOENT;
+			goto out_error_unmap;
+		}
+		uart->port.irq = uart->rx_irq;
+
+		uart->status_irq = platform_get_irq(pdev, 2);
+		if (uart->status_irq < 0) {
+			dev_err(&pdev->dev, "No uart status IRQ specified\n");
+			ret = -ENOENT;
+			goto out_error_unmap;
+		}
+
+#ifdef CONFIG_SERIAL_BFIN_DMA
+		spin_lock_init(&uart->rx_lock);
+		uart->tx_done	    = 1;
+		uart->tx_count	    = 0;
+
+		res = platform_get_resource(pdev, IORESOURCE_DMA, 0);
+		if (res == NULL) {
+			dev_err(&pdev->dev, "No uart TX DMA channel specified\n");
+			ret = -ENOENT;
+			goto out_error_unmap;
+		}
+		uart->tx_dma_channel = res->start;
+
+		res = platform_get_resource(pdev, IORESOURCE_DMA, 1);
+		if (res == NULL) {
+			dev_err(&pdev->dev, "No uart RX DMA channel specified\n");
+			ret = -ENOENT;
+			goto out_error_unmap;
+		}
+		uart->rx_dma_channel = res->start;
+
+		init_timer(&(uart->rx_dma_timer));
+#endif
+
+#if defined(CONFIG_SERIAL_BFIN_CTSRTS) || \
+	defined(CONFIG_SERIAL_BFIN_HARD_CTSRTS)
+		res = platform_get_resource(pdev, IORESOURCE_IO, 0);
+		if (res == NULL)
+			uart->cts_pin = -1;
+		else {
+			uart->cts_pin = res->start;
+#ifdef CONFIG_SERIAL_BFIN_CTSRTS
+			uart->port.flags |= ASYNC_CTS_FLOW;
+#endif
+		}
+
+		res = platform_get_resource(pdev, IORESOURCE_IO, 1);
+		if (res == NULL)
+			uart->rts_pin = -1;
+		else
+			uart->rts_pin = res->start;
+#endif
+	}
+
+#ifdef CONFIG_SERIAL_BFIN_CONSOLE
+	if (!is_early_platform_device(pdev)) {
+#endif
+		uart = bfin_serial_ports[pdev->id];
+		uart->port.dev = &pdev->dev;
+		dev_set_drvdata(&pdev->dev, uart);
+		ret = uart_add_one_port(&bfin_serial_reg, &uart->port);
+#ifdef CONFIG_SERIAL_BFIN_CONSOLE
+	}
+#endif
+
+	if (!ret)
+		return 0;
+
+	if (uart) {
+out_error_unmap:
+		iounmap(uart->port.membase);
+out_error_free_peripherals:
+		peripheral_free_list(
+			(unsigned short *)pdev->dev.platform_data);
+out_error_free_mem:
+		kfree(uart);
+		bfin_serial_ports[pdev->id] = NULL;
+	}
+
+	return ret;
+}
+
+static int __devexit bfin_serial_remove(struct platform_device *pdev)
+{
+	struct bfin_serial_port *uart = platform_get_drvdata(pdev);
+
+	dev_set_drvdata(&pdev->dev, NULL);
+
+	if (uart) {
+		uart_remove_one_port(&bfin_serial_reg, &uart->port);
+		iounmap(uart->port.membase);
+		peripheral_free_list(
+			(unsigned short *)pdev->dev.platform_data);
+		kfree(uart);
+		bfin_serial_ports[pdev->id] = NULL;
+	}
+
+	return 0;
+}
+
+static struct platform_driver bfin_serial_driver = {
+	.probe		= bfin_serial_probe,
+	.remove		= __devexit_p(bfin_serial_remove),
+	.suspend	= bfin_serial_suspend,
+	.resume		= bfin_serial_resume,
+	.driver		= {
+		.name	= DRIVER_NAME,
+		.owner	= THIS_MODULE,
+	},
+};
+
+#if defined(CONFIG_SERIAL_BFIN_CONSOLE)
+static __initdata struct early_platform_driver early_bfin_serial_driver = {
+	.class_str = CLASS_BFIN_CONSOLE,
+	.pdrv = &bfin_serial_driver,
+	.requested_id = EARLY_PLATFORM_ID_UNSET,
+};
+
+static int __init bfin_serial_rs_console_init(void)
+{
+	early_platform_driver_register(&early_bfin_serial_driver, DRIVER_NAME);
+
+	early_platform_driver_probe(CLASS_BFIN_CONSOLE, BFIN_UART_NR_PORTS, 0);
+
+	register_console(&bfin_serial_console);
+
+	return 0;
+}
+console_initcall(bfin_serial_rs_console_init);
+#endif
+
+#ifdef CONFIG_EARLY_PRINTK
+/*
+ * Memory can't be allocated dynamically during earlyprink init stage.
+ * So, do individual probe for earlyprink with a static uart port variable.
+ */
+static int bfin_earlyprintk_probe(struct platform_device *pdev)
+{
+	struct resource *res;
+	int ret;
+
+	if (pdev->id < 0 || pdev->id >= BFIN_UART_NR_PORTS) {
+		dev_err(&pdev->dev, "Wrong earlyprintk platform device id.\n");
+		return -ENOENT;
+	}
+
+	ret = peripheral_request_list(
+		(unsigned short *)pdev->dev.platform_data, DRIVER_NAME);
+	if (ret) {
+		dev_err(&pdev->dev,
+				"fail to request bfin serial peripherals\n");
+			return ret;
+	}
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (res == NULL) {
+		dev_err(&pdev->dev, "Cannot get IORESOURCE_MEM\n");
+		ret = -ENOENT;
+		goto out_error_free_peripherals;
+	}
+
+	bfin_earlyprintk_port.port.membase = ioremap(res->start,
+						     resource_size(res));
+	if (!bfin_earlyprintk_port.port.membase) {
+		dev_err(&pdev->dev, "Cannot map uart IO\n");
+		ret = -ENXIO;
+		goto out_error_free_peripherals;
+	}
+	bfin_earlyprintk_port.port.mapbase = res->start;
+	bfin_earlyprintk_port.port.line = pdev->id;
+	bfin_earlyprintk_port.port.uartclk = get_sclk();
+	bfin_earlyprintk_port.port.fifosize  = BFIN_UART_TX_FIFO_SIZE;
+	spin_lock_init(&bfin_earlyprintk_port.port.lock);
+
+	return 0;
+
+out_error_free_peripherals:
+	peripheral_free_list(
+		(unsigned short *)pdev->dev.platform_data);
+
+	return ret;
+}
+
+static struct platform_driver bfin_earlyprintk_driver = {
+	.probe		= bfin_earlyprintk_probe,
+	.driver		= {
+		.name	= DRIVER_NAME,
+		.owner	= THIS_MODULE,
+	},
+};
+
+static __initdata struct early_platform_driver early_bfin_earlyprintk_driver = {
+	.class_str = CLASS_BFIN_EARLYPRINTK,
+	.pdrv = &bfin_earlyprintk_driver,
+	.requested_id = EARLY_PLATFORM_ID_UNSET,
+};
+
+struct console __init *bfin_earlyserial_init(unsigned int port,
+						unsigned int cflag)
+{
+	struct ktermios t;
+	char port_name[20];
+
+	if (port < 0 || port >= BFIN_UART_NR_PORTS)
+		return NULL;
+
+	/*
+	 * Only probe resource of the given port in earlyprintk boot arg.
+	 * The expected port id should be indicated in port name string.
+	 */
+	snprintf(port_name, 20, DRIVER_NAME ".%d", port);
+	early_platform_driver_register(&early_bfin_earlyprintk_driver,
+		port_name);
+	early_platform_driver_probe(CLASS_BFIN_EARLYPRINTK, 1, 0);
+
+	if (!bfin_earlyprintk_port.port.membase)
+		return NULL;
+
+#ifdef CONFIG_SERIAL_BFIN_CONSOLE
+	/*
+	 * If we are using early serial, don't let the normal console rewind
+	 * log buffer, since that causes things to be printed multiple times
+	 */
+	bfin_serial_console.flags &= ~CON_PRINTBUFFER;
+#endif
+
+	bfin_early_serial_console.index = port;
+	t.c_cflag = cflag;
+	t.c_iflag = 0;
+	t.c_oflag = 0;
+	t.c_lflag = ICANON;
+	t.c_line = port;
+	bfin_serial_set_termios(&bfin_earlyprintk_port.port, &t, &t);
+
+	return &bfin_early_serial_console;
+}
+#endif /* CONFIG_EARLY_PRINTK */
+
+static int __init bfin_serial_init(void)
+{
+	int ret;
+
+	pr_info("Blackfin serial driver\n");
+
+	ret = uart_register_driver(&bfin_serial_reg);
+	if (ret) {
+		pr_err("failed to register %s:%d\n",
+			bfin_serial_reg.driver_name, ret);
+	}
+
+	ret = platform_driver_register(&bfin_serial_driver);
+	if (ret) {
+		pr_err("fail to register bfin uart\n");
+		uart_unregister_driver(&bfin_serial_reg);
+	}
+
+	return ret;
+}
+
+static void __exit bfin_serial_exit(void)
+{
+	platform_driver_unregister(&bfin_serial_driver);
+	uart_unregister_driver(&bfin_serial_reg);
+}
+
+
+module_init(bfin_serial_init);
+module_exit(bfin_serial_exit);
+
+MODULE_AUTHOR("Sonic Zhang, Aubrey Li");
+MODULE_DESCRIPTION("Blackfin generic serial port driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS_CHARDEV_MAJOR(BFIN_SERIAL_MAJOR);
+MODULE_ALIAS("platform:bfin-uart");
diff --git a/ap/os/linux/linux-3.4.x/drivers/tty/serial/clps711x.c b/ap/os/linux/linux-3.4.x/drivers/tty/serial/clps711x.c
new file mode 100644
index 0000000..836fe27
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/tty/serial/clps711x.c
@@ -0,0 +1,579 @@
+/*
+ *  Driver for CLPS711x serial ports
+ *
+ *  Based on drivers/char/serial.c, by Linus Torvalds, Theodore Ts'o.
+ *
+ *  Copyright 1999 ARM Limited
+ *  Copyright (C) 2000 Deep Blue Solutions Ltd.
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#if defined(CONFIG_SERIAL_CLPS711X_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ)
+#define SUPPORT_SYSRQ
+#endif
+
+#include <linux/module.h>
+#include <linux/ioport.h>
+#include <linux/init.h>
+#include <linux/console.h>
+#include <linux/sysrq.h>
+#include <linux/spinlock.h>
+#include <linux/device.h>
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
+#include <linux/serial_core.h>
+#include <linux/serial.h>
+#include <linux/io.h>
+
+#include <mach/hardware.h>
+#include <asm/irq.h>
+#include <asm/hardware/clps7111.h>
+
+#define UART_NR		2
+
+#define SERIAL_CLPS711X_MAJOR	204
+#define SERIAL_CLPS711X_MINOR	40
+#define SERIAL_CLPS711X_NR	UART_NR
+
+/*
+ * We use the relevant SYSCON register as a base address for these ports.
+ */
+#define UBRLCR(port)		((port)->iobase + UBRLCR1 - SYSCON1)
+#define UARTDR(port)		((port)->iobase + UARTDR1 - SYSCON1)
+#define SYSFLG(port)		((port)->iobase + SYSFLG1 - SYSCON1)
+#define SYSCON(port)		((port)->iobase + SYSCON1 - SYSCON1)
+
+#define TX_IRQ(port)		((port)->irq)
+#define RX_IRQ(port)		((port)->irq + 1)
+
+#define UART_ANY_ERR		(UARTDR_FRMERR | UARTDR_PARERR | UARTDR_OVERR)
+
+#define tx_enabled(port)	((port)->unused[0])
+
+static void clps711xuart_stop_tx(struct uart_port *port)
+{
+	if (tx_enabled(port)) {
+		disable_irq(TX_IRQ(port));
+		tx_enabled(port) = 0;
+	}
+}
+
+static void clps711xuart_start_tx(struct uart_port *port)
+{
+	if (!tx_enabled(port)) {
+		enable_irq(TX_IRQ(port));
+		tx_enabled(port) = 1;
+	}
+}
+
+static void clps711xuart_stop_rx(struct uart_port *port)
+{
+	disable_irq(RX_IRQ(port));
+}
+
+static void clps711xuart_enable_ms(struct uart_port *port)
+{
+}
+
+static irqreturn_t clps711xuart_int_rx(int irq, void *dev_id)
+{
+	struct uart_port *port = dev_id;
+	struct tty_struct *tty = port->state->port.tty;
+	unsigned int status, ch, flg;
+
+	status = clps_readl(SYSFLG(port));
+	while (!(status & SYSFLG_URXFE)) {
+		ch = clps_readl(UARTDR(port));
+
+		port->icount.rx++;
+
+		flg = TTY_NORMAL;
+
+		/*
+		 * Note that the error handling code is
+		 * out of the main execution path
+		 */
+		if (unlikely(ch & UART_ANY_ERR)) {
+			if (ch & UARTDR_PARERR)
+				port->icount.parity++;
+			else if (ch & UARTDR_FRMERR)
+				port->icount.frame++;
+			if (ch & UARTDR_OVERR)
+				port->icount.overrun++;
+
+			ch &= port->read_status_mask;
+
+			if (ch & UARTDR_PARERR)
+				flg = TTY_PARITY;
+			else if (ch & UARTDR_FRMERR)
+				flg = TTY_FRAME;
+
+#ifdef SUPPORT_SYSRQ
+			port->sysrq = 0;
+#endif
+		}
+
+		if (uart_handle_sysrq_char(port, ch))
+			goto ignore_char;
+
+		/*
+		 * CHECK: does overrun affect the current character?
+		 * ASSUMPTION: it does not.
+		 */
+		uart_insert_char(port, ch, UARTDR_OVERR, ch, flg);
+
+	ignore_char:
+		status = clps_readl(SYSFLG(port));
+	}
+	tty_flip_buffer_push(tty);
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t clps711xuart_int_tx(int irq, void *dev_id)
+{
+	struct uart_port *port = dev_id;
+	struct circ_buf *xmit = &port->state->xmit;
+	int count;
+
+	if (port->x_char) {
+		clps_writel(port->x_char, UARTDR(port));
+		port->icount.tx++;
+		port->x_char = 0;
+		return IRQ_HANDLED;
+	}
+
+	if (uart_circ_empty(xmit) || uart_tx_stopped(port))
+		goto disable_tx_irq;
+
+	count = port->fifosize >> 1;
+	do {
+		clps_writel(xmit->buf[xmit->tail], UARTDR(port));
+		xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
+		port->icount.tx++;
+		if (uart_circ_empty(xmit))
+			break;
+	} while (--count > 0);
+
+	if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
+		uart_write_wakeup(port);
+
+	if (uart_circ_empty(xmit)) {
+	disable_tx_irq:
+		disable_irq_nosync(TX_IRQ(port));
+		tx_enabled(port) = 0;
+	}
+
+	return IRQ_HANDLED;
+}
+
+static unsigned int clps711xuart_tx_empty(struct uart_port *port)
+{
+	unsigned int status = clps_readl(SYSFLG(port));
+	return status & SYSFLG_UBUSY ? 0 : TIOCSER_TEMT;
+}
+
+static unsigned int clps711xuart_get_mctrl(struct uart_port *port)
+{
+	unsigned int port_addr;
+	unsigned int result = 0;
+	unsigned int status;
+
+	port_addr = SYSFLG(port);
+	if (port_addr == SYSFLG1) {
+		status = clps_readl(SYSFLG1);
+		if (status & SYSFLG1_DCD)
+			result |= TIOCM_CAR;
+		if (status & SYSFLG1_DSR)
+			result |= TIOCM_DSR;
+		if (status & SYSFLG1_CTS)
+			result |= TIOCM_CTS;
+	}
+
+	return result;
+}
+
+static void
+clps711xuart_set_mctrl_null(struct uart_port *port, unsigned int mctrl)
+{
+}
+
+static void clps711xuart_break_ctl(struct uart_port *port, int break_state)
+{
+	unsigned long flags;
+	unsigned int ubrlcr;
+
+	spin_lock_irqsave(&port->lock, flags);
+	ubrlcr = clps_readl(UBRLCR(port));
+	if (break_state == -1)
+		ubrlcr |= UBRLCR_BREAK;
+	else
+		ubrlcr &= ~UBRLCR_BREAK;
+	clps_writel(ubrlcr, UBRLCR(port));
+	spin_unlock_irqrestore(&port->lock, flags);
+}
+
+static int clps711xuart_startup(struct uart_port *port)
+{
+	unsigned int syscon;
+	int retval;
+
+	tx_enabled(port) = 1;
+
+	/*
+	 * Allocate the IRQs
+	 */
+	retval = request_irq(TX_IRQ(port), clps711xuart_int_tx, 0,
+			     "clps711xuart_tx", port);
+	if (retval)
+		return retval;
+
+	retval = request_irq(RX_IRQ(port), clps711xuart_int_rx, 0,
+			     "clps711xuart_rx", port);
+	if (retval) {
+		free_irq(TX_IRQ(port), port);
+		return retval;
+	}
+
+	/*
+	 * enable the port
+	 */
+	syscon = clps_readl(SYSCON(port));
+	syscon |= SYSCON_UARTEN;
+	clps_writel(syscon, SYSCON(port));
+
+	return 0;
+}
+
+static void clps711xuart_shutdown(struct uart_port *port)
+{
+	unsigned int ubrlcr, syscon;
+
+	/*
+	 * Free the interrupt
+	 */
+	free_irq(TX_IRQ(port), port);	/* TX interrupt */
+	free_irq(RX_IRQ(port), port);	/* RX interrupt */
+
+	/*
+	 * disable the port
+	 */
+	syscon = clps_readl(SYSCON(port));
+	syscon &= ~SYSCON_UARTEN;
+	clps_writel(syscon, SYSCON(port));
+
+	/*
+	 * disable break condition and fifos
+	 */
+	ubrlcr = clps_readl(UBRLCR(port));
+	ubrlcr &= ~(UBRLCR_FIFOEN | UBRLCR_BREAK);
+	clps_writel(ubrlcr, UBRLCR(port));
+}
+
+static void
+clps711xuart_set_termios(struct uart_port *port, struct ktermios *termios,
+			 struct ktermios *old)
+{
+	unsigned int ubrlcr, baud, quot;
+	unsigned long flags;
+
+	/*
+	 * We don't implement CREAD.
+	 */
+	termios->c_cflag |= CREAD;
+
+	/*
+	 * Ask the core to calculate the divisor for us.
+	 */
+	baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk/16); 
+	quot = uart_get_divisor(port, baud);
+
+	switch (termios->c_cflag & CSIZE) {
+	case CS5:
+		ubrlcr = UBRLCR_WRDLEN5;
+		break;
+	case CS6:
+		ubrlcr = UBRLCR_WRDLEN6;
+		break;
+	case CS7:
+		ubrlcr = UBRLCR_WRDLEN7;
+		break;
+	default: // CS8
+		ubrlcr = UBRLCR_WRDLEN8;
+		break;
+	}
+	if (termios->c_cflag & CSTOPB)
+		ubrlcr |= UBRLCR_XSTOP;
+	if (termios->c_cflag & PARENB) {
+		ubrlcr |= UBRLCR_PRTEN;
+		if (!(termios->c_cflag & PARODD))
+			ubrlcr |= UBRLCR_EVENPRT;
+	}
+	if (port->fifosize > 1)
+		ubrlcr |= UBRLCR_FIFOEN;
+
+	spin_lock_irqsave(&port->lock, flags);
+
+	/*
+	 * Update the per-port timeout.
+	 */
+	uart_update_timeout(port, termios->c_cflag, baud);
+
+	port->read_status_mask = UARTDR_OVERR;
+	if (termios->c_iflag & INPCK)
+		port->read_status_mask |= UARTDR_PARERR | UARTDR_FRMERR;
+
+	/*
+	 * Characters to ignore
+	 */
+	port->ignore_status_mask = 0;
+	if (termios->c_iflag & IGNPAR)
+		port->ignore_status_mask |= UARTDR_FRMERR | UARTDR_PARERR;
+	if (termios->c_iflag & IGNBRK) {
+		/*
+		 * If we're ignoring parity and break indicators,
+		 * ignore overruns to (for real raw support).
+		 */
+		if (termios->c_iflag & IGNPAR)
+			port->ignore_status_mask |= UARTDR_OVERR;
+	}
+
+	quot -= 1;
+
+	clps_writel(ubrlcr | quot, UBRLCR(port));
+
+	spin_unlock_irqrestore(&port->lock, flags);
+}
+
+static const char *clps711xuart_type(struct uart_port *port)
+{
+	return port->type == PORT_CLPS711X ? "CLPS711x" : NULL;
+}
+
+/*
+ * Configure/autoconfigure the port.
+ */
+static void clps711xuart_config_port(struct uart_port *port, int flags)
+{
+	if (flags & UART_CONFIG_TYPE)
+		port->type = PORT_CLPS711X;
+}
+
+static void clps711xuart_release_port(struct uart_port *port)
+{
+}
+
+static int clps711xuart_request_port(struct uart_port *port)
+{
+	return 0;
+}
+
+static struct uart_ops clps711x_pops = {
+	.tx_empty	= clps711xuart_tx_empty,
+	.set_mctrl	= clps711xuart_set_mctrl_null,
+	.get_mctrl	= clps711xuart_get_mctrl,
+	.stop_tx	= clps711xuart_stop_tx,
+	.start_tx	= clps711xuart_start_tx,
+	.stop_rx	= clps711xuart_stop_rx,
+	.enable_ms	= clps711xuart_enable_ms,
+	.break_ctl	= clps711xuart_break_ctl,
+	.startup	= clps711xuart_startup,
+	.shutdown	= clps711xuart_shutdown,
+	.set_termios	= clps711xuart_set_termios,
+	.type		= clps711xuart_type,
+	.config_port	= clps711xuart_config_port,
+	.release_port	= clps711xuart_release_port,
+	.request_port	= clps711xuart_request_port,
+};
+
+static struct uart_port clps711x_ports[UART_NR] = {
+	{
+		.iobase		= SYSCON1,
+		.irq		= IRQ_UTXINT1, /* IRQ_URXINT1, IRQ_UMSINT */
+		.uartclk	= 3686400,
+		.fifosize	= 16,
+		.ops		= &clps711x_pops,
+		.line		= 0,
+		.flags		= UPF_BOOT_AUTOCONF,
+	},
+	{
+		.iobase		= SYSCON2,
+		.irq		= IRQ_UTXINT2, /* IRQ_URXINT2 */
+		.uartclk	= 3686400,
+		.fifosize	= 16,
+		.ops		= &clps711x_pops,
+		.line		= 1,
+		.flags		= UPF_BOOT_AUTOCONF,
+	}
+};
+
+#ifdef CONFIG_SERIAL_CLPS711X_CONSOLE
+static void clps711xuart_console_putchar(struct uart_port *port, int ch)
+{
+	while (clps_readl(SYSFLG(port)) & SYSFLG_UTXFF)
+		barrier();
+	clps_writel(ch, UARTDR(port));
+}
+
+/*
+ *	Print a string to the serial port trying not to disturb
+ *	any possible real use of the port...
+ *
+ *	The console_lock must be held when we get here.
+ *
+ *	Note that this is called with interrupts already disabled
+ */
+static void
+clps711xuart_console_write(struct console *co, const char *s,
+			   unsigned int count)
+{
+	struct uart_port *port = clps711x_ports + co->index;
+	unsigned int status, syscon;
+
+	/*
+	 *	Ensure that the port is enabled.
+	 */
+	syscon = clps_readl(SYSCON(port));
+	clps_writel(syscon | SYSCON_UARTEN, SYSCON(port));
+
+	uart_console_write(port, s, count, clps711xuart_console_putchar);
+
+	/*
+	 *	Finally, wait for transmitter to become empty
+	 *	and restore the uart state.
+	 */
+	do {
+		status = clps_readl(SYSFLG(port));
+	} while (status & SYSFLG_UBUSY);
+
+	clps_writel(syscon, SYSCON(port));
+}
+
+static void __init
+clps711xuart_console_get_options(struct uart_port *port, int *baud,
+				 int *parity, int *bits)
+{
+	if (clps_readl(SYSCON(port)) & SYSCON_UARTEN) {
+		unsigned int ubrlcr, quot;
+
+		ubrlcr = clps_readl(UBRLCR(port));
+
+		*parity = 'n';
+		if (ubrlcr & UBRLCR_PRTEN) {
+			if (ubrlcr & UBRLCR_EVENPRT)
+				*parity = 'e';
+			else
+				*parity = 'o';
+		}
+
+		if ((ubrlcr & UBRLCR_WRDLEN_MASK) == UBRLCR_WRDLEN7)
+			*bits = 7;
+		else
+			*bits = 8;
+
+		quot = ubrlcr & UBRLCR_BAUD_MASK;
+		*baud = port->uartclk / (16 * (quot + 1));
+	}
+}
+
+static int __init clps711xuart_console_setup(struct console *co, char *options)
+{
+	struct uart_port *port;
+	int baud = 38400;
+	int bits = 8;
+	int parity = 'n';
+	int flow = 'n';
+
+	/*
+	 * Check whether an invalid uart number has been specified, and
+	 * if so, search for the first available port that does have
+	 * console support.
+	 */
+	port = uart_get_console(clps711x_ports, UART_NR, co);
+
+	if (options)
+		uart_parse_options(options, &baud, &parity, &bits, &flow);
+	else
+		clps711xuart_console_get_options(port, &baud, &parity, &bits);
+
+	return uart_set_options(port, co, baud, parity, bits, flow);
+}
+
+static struct uart_driver clps711x_reg;
+static struct console clps711x_console = {
+	.name		= "ttyCL",
+	.write		= clps711xuart_console_write,
+	.device		= uart_console_device,
+	.setup		= clps711xuart_console_setup,
+	.flags		= CON_PRINTBUFFER,
+	.index		= -1,
+	.data		= &clps711x_reg,
+};
+
+static int __init clps711xuart_console_init(void)
+{
+	register_console(&clps711x_console);
+	return 0;
+}
+console_initcall(clps711xuart_console_init);
+
+#define CLPS711X_CONSOLE	&clps711x_console
+#else
+#define CLPS711X_CONSOLE	NULL
+#endif
+
+static struct uart_driver clps711x_reg = {
+	.driver_name		= "ttyCL",
+	.dev_name		= "ttyCL",
+	.major			= SERIAL_CLPS711X_MAJOR,
+	.minor			= SERIAL_CLPS711X_MINOR,
+	.nr			= UART_NR,
+
+	.cons			= CLPS711X_CONSOLE,
+};
+
+static int __init clps711xuart_init(void)
+{
+	int ret, i;
+
+	printk(KERN_INFO "Serial: CLPS711x driver\n");
+
+	ret = uart_register_driver(&clps711x_reg);
+	if (ret)
+		return ret;
+
+	for (i = 0; i < UART_NR; i++)
+		uart_add_one_port(&clps711x_reg, &clps711x_ports[i]);
+
+	return 0;
+}
+
+static void __exit clps711xuart_exit(void)
+{
+	int i;
+
+	for (i = 0; i < UART_NR; i++)
+		uart_remove_one_port(&clps711x_reg, &clps711x_ports[i]);
+
+	uart_unregister_driver(&clps711x_reg);
+}
+
+module_init(clps711xuart_init);
+module_exit(clps711xuart_exit);
+
+MODULE_AUTHOR("Deep Blue Solutions Ltd");
+MODULE_DESCRIPTION("CLPS-711x generic serial driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS_CHARDEV(SERIAL_CLPS711X_MAJOR, SERIAL_CLPS711X_MINOR);
diff --git a/ap/os/linux/linux-3.4.x/drivers/tty/serial/cpm_uart/Makefile b/ap/os/linux/linux-3.4.x/drivers/tty/serial/cpm_uart/Makefile
new file mode 100644
index 0000000..e072724
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/tty/serial/cpm_uart/Makefile
@@ -0,0 +1,11 @@
+#
+# Makefile for the Motorola 8xx FEC ethernet controller
+#
+
+obj-$(CONFIG_SERIAL_CPM) += cpm_uart.o
+
+# Select the correct platform objects.
+cpm_uart-objs-$(CONFIG_CPM2)	+= cpm_uart_cpm2.o
+cpm_uart-objs-$(CONFIG_8xx)	+= cpm_uart_cpm1.o
+
+cpm_uart-objs	:= cpm_uart_core.o $(cpm_uart-objs-y)
diff --git a/ap/os/linux/linux-3.4.x/drivers/tty/serial/cpm_uart/cpm_uart.h b/ap/os/linux/linux-3.4.x/drivers/tty/serial/cpm_uart/cpm_uart.h
new file mode 100644
index 0000000..cf34d26
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/tty/serial/cpm_uart/cpm_uart.h
@@ -0,0 +1,145 @@
+/*
+ *  Driver for CPM (SCC/SMC) serial ports
+ *
+ *  Copyright (C) 2004 Freescale Semiconductor, Inc.
+ *
+ *  2006 (c) MontaVista Software, Inc.
+ *	Vitaly Bordug <vbordug@ru.mvista.com>
+ *
+ * This file is licensed under the terms of the GNU General Public License
+ * version 2. This program is licensed "as is" without any warranty of any
+ * kind, whether express or implied.
+ *
+ */
+#ifndef CPM_UART_H
+#define CPM_UART_H
+
+#include <linux/platform_device.h>
+#include <linux/fs_uart_pd.h>
+
+#if defined(CONFIG_CPM2)
+#include "cpm_uart_cpm2.h"
+#elif defined(CONFIG_8xx)
+#include "cpm_uart_cpm1.h"
+#endif
+
+#define SERIAL_CPM_MAJOR	204
+#define SERIAL_CPM_MINOR	46
+
+#define IS_SMC(pinfo)		(pinfo->flags & FLAG_SMC)
+#define IS_DISCARDING(pinfo)	(pinfo->flags & FLAG_DISCARDING)
+#define FLAG_DISCARDING	0x00000004	/* when set, don't discard */
+#define FLAG_SMC	0x00000002
+#define FLAG_CONSOLE	0x00000001
+
+#define UART_SMC1	fsid_smc1_uart
+#define UART_SMC2	fsid_smc2_uart
+#define UART_SCC1	fsid_scc1_uart
+#define UART_SCC2	fsid_scc2_uart
+#define UART_SCC3	fsid_scc3_uart
+#define UART_SCC4	fsid_scc4_uart
+
+#define UART_NR		fs_uart_nr
+
+#define RX_NUM_FIFO	4
+#define RX_BUF_SIZE	32
+#define TX_NUM_FIFO	4
+#define TX_BUF_SIZE	32
+
+#define SCC_WAIT_CLOSING 100
+
+#define GPIO_CTS	0
+#define GPIO_RTS	1
+#define GPIO_DCD	2
+#define GPIO_DSR	3
+#define GPIO_DTR	4
+#define GPIO_RI		5
+
+#define NUM_GPIOS	(GPIO_RI+1)
+
+struct uart_cpm_port {
+	struct uart_port	port;
+	u16			rx_nrfifos;
+	u16			rx_fifosize;
+	u16			tx_nrfifos;
+	u16			tx_fifosize;
+	smc_t __iomem		*smcp;
+	smc_uart_t __iomem	*smcup;
+	scc_t __iomem		*sccp;
+	scc_uart_t __iomem	*sccup;
+	cbd_t __iomem		*rx_bd_base;
+	cbd_t __iomem		*rx_cur;
+	cbd_t __iomem		*tx_bd_base;
+	cbd_t __iomem		*tx_cur;
+	unsigned char		*tx_buf;
+	unsigned char		*rx_buf;
+	u32			flags;
+	struct clk		*clk;
+	u8			brg;
+	uint			 dp_addr;
+	void			*mem_addr;
+	dma_addr_t		 dma_addr;
+	u32			mem_size;
+	/* wait on close if needed */
+	int			wait_closing;
+	/* value to combine with opcode to form cpm command */
+	u32			command;
+	int			gpios[NUM_GPIOS];
+};
+
+extern int cpm_uart_nr;
+extern struct uart_cpm_port cpm_uart_ports[UART_NR];
+
+/* these are located in their respective files */
+void cpm_line_cr_cmd(struct uart_cpm_port *port, int cmd);
+void __iomem *cpm_uart_map_pram(struct uart_cpm_port *port,
+				struct device_node *np);
+void cpm_uart_unmap_pram(struct uart_cpm_port *port, void __iomem *pram);
+int cpm_uart_init_portdesc(void);
+int cpm_uart_allocbuf(struct uart_cpm_port *pinfo, unsigned int is_con);
+void cpm_uart_freebuf(struct uart_cpm_port *pinfo);
+
+void smc1_lineif(struct uart_cpm_port *pinfo);
+void smc2_lineif(struct uart_cpm_port *pinfo);
+void scc1_lineif(struct uart_cpm_port *pinfo);
+void scc2_lineif(struct uart_cpm_port *pinfo);
+void scc3_lineif(struct uart_cpm_port *pinfo);
+void scc4_lineif(struct uart_cpm_port *pinfo);
+
+/*
+   virtual to phys transtalion
+*/
+static inline unsigned long cpu2cpm_addr(void *addr,
+                                         struct uart_cpm_port *pinfo)
+{
+	int offset;
+	u32 val = (u32)addr;
+	u32 mem = (u32)pinfo->mem_addr;
+	/* sane check */
+	if (likely(val >= mem && val < mem + pinfo->mem_size)) {
+		offset = val - mem;
+		return pinfo->dma_addr + offset;
+	}
+	/* something nasty happened */
+	BUG();
+	return 0;
+}
+
+static inline void *cpm2cpu_addr(unsigned long addr,
+                                 struct uart_cpm_port *pinfo)
+{
+	int offset;
+	u32 val = addr;
+	u32 dma = (u32)pinfo->dma_addr;
+	/* sane check */
+	if (likely(val >= dma && val < dma + pinfo->mem_size)) {
+		offset = val - dma;
+		return pinfo->mem_addr + offset;
+	}
+	/* something nasty happened */
+	BUG();
+	return NULL;
+}
+
+
+#endif /* CPM_UART_H */
diff --git a/ap/os/linux/linux-3.4.x/drivers/tty/serial/cpm_uart/cpm_uart_core.c b/ap/os/linux/linux-3.4.x/drivers/tty/serial/cpm_uart/cpm_uart_core.c
new file mode 100644
index 0000000..b418947
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/tty/serial/cpm_uart/cpm_uart_core.c
@@ -0,0 +1,1441 @@
+/*
+ *  Driver for CPM (SCC/SMC) serial ports; core driver
+ *
+ *  Based on arch/ppc/cpm2_io/uart.c by Dan Malek
+ *  Based on ppc8xx.c by Thomas Gleixner
+ *  Based on drivers/serial/amba.c by Russell King
+ *
+ *  Maintainer: Kumar Gala (galak@kernel.crashing.org) (CPM2)
+ *              Pantelis Antoniou (panto@intracom.gr) (CPM1)
+ *
+ *  Copyright (C) 2004, 2007 Freescale Semiconductor, Inc.
+ *            (C) 2004 Intracom, S.A.
+ *            (C) 2005-2006 MontaVista Software, Inc.
+ *		Vitaly Bordug <vbordug@ru.mvista.com>
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#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/bootmem.h>
+#include <linux/dma-mapping.h>
+#include <linux/fs_uart_pd.h>
+#include <linux/of_platform.h>
+#include <linux/gpio.h>
+#include <linux/of_gpio.h>
+#include <linux/clk.h>
+
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/delay.h>
+#include <asm/fs_pd.h>
+#include <asm/udbg.h>
+
+#if defined(CONFIG_SERIAL_CPM_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ)
+#define SUPPORT_SYSRQ
+#endif
+
+#include <linux/serial_core.h>
+#include <linux/kernel.h>
+
+#include "cpm_uart.h"
+
+
+/**************************************************************/
+
+static int  cpm_uart_tx_pump(struct uart_port *port);
+static void cpm_uart_init_smc(struct uart_cpm_port *pinfo);
+static void cpm_uart_init_scc(struct uart_cpm_port *pinfo);
+static void cpm_uart_initbd(struct uart_cpm_port *pinfo);
+
+/**************************************************************/
+
+#define HW_BUF_SPD_THRESHOLD    9600
+
+/*
+ * Check, if transmit buffers are processed
+*/
+static unsigned int cpm_uart_tx_empty(struct uart_port *port)
+{
+	struct uart_cpm_port *pinfo = (struct uart_cpm_port *)port;
+	cbd_t __iomem *bdp = pinfo->tx_bd_base;
+	int ret = 0;
+
+	while (1) {
+		if (in_be16(&bdp->cbd_sc) & BD_SC_READY)
+			break;
+
+		if (in_be16(&bdp->cbd_sc) & BD_SC_WRAP) {
+			ret = TIOCSER_TEMT;
+			break;
+		}
+		bdp++;
+	}
+
+	pr_debug("CPM uart[%d]:tx_empty: %d\n", port->line, ret);
+
+	return ret;
+}
+
+static void cpm_uart_set_mctrl(struct uart_port *port, unsigned int mctrl)
+{
+	struct uart_cpm_port *pinfo = (struct uart_cpm_port *)port;
+
+	if (pinfo->gpios[GPIO_RTS] >= 0)
+		gpio_set_value(pinfo->gpios[GPIO_RTS], !(mctrl & TIOCM_RTS));
+
+	if (pinfo->gpios[GPIO_DTR] >= 0)
+		gpio_set_value(pinfo->gpios[GPIO_DTR], !(mctrl & TIOCM_DTR));
+}
+
+static unsigned int cpm_uart_get_mctrl(struct uart_port *port)
+{
+	struct uart_cpm_port *pinfo = (struct uart_cpm_port *)port;
+	unsigned int mctrl = TIOCM_CTS | TIOCM_DSR | TIOCM_CAR;
+
+	if (pinfo->gpios[GPIO_CTS] >= 0) {
+		if (gpio_get_value(pinfo->gpios[GPIO_CTS]))
+			mctrl &= ~TIOCM_CTS;
+	}
+
+	if (pinfo->gpios[GPIO_DSR] >= 0) {
+		if (gpio_get_value(pinfo->gpios[GPIO_DSR]))
+			mctrl &= ~TIOCM_DSR;
+	}
+
+	if (pinfo->gpios[GPIO_DCD] >= 0) {
+		if (gpio_get_value(pinfo->gpios[GPIO_DCD]))
+			mctrl &= ~TIOCM_CAR;
+	}
+
+	if (pinfo->gpios[GPIO_RI] >= 0) {
+		if (!gpio_get_value(pinfo->gpios[GPIO_RI]))
+			mctrl |= TIOCM_RNG;
+	}
+
+	return mctrl;
+}
+
+/*
+ * Stop transmitter
+ */
+static void cpm_uart_stop_tx(struct uart_port *port)
+{
+	struct uart_cpm_port *pinfo = (struct uart_cpm_port *)port;
+	smc_t __iomem *smcp = pinfo->smcp;
+	scc_t __iomem *sccp = pinfo->sccp;
+
+	pr_debug("CPM uart[%d]:stop tx\n", port->line);
+
+	if (IS_SMC(pinfo))
+		clrbits8(&smcp->smc_smcm, SMCM_TX);
+	else
+		clrbits16(&sccp->scc_sccm, UART_SCCM_TX);
+}
+
+/*
+ * Start transmitter
+ */
+static void cpm_uart_start_tx(struct uart_port *port)
+{
+	struct uart_cpm_port *pinfo = (struct uart_cpm_port *)port;
+	smc_t __iomem *smcp = pinfo->smcp;
+	scc_t __iomem *sccp = pinfo->sccp;
+
+	pr_debug("CPM uart[%d]:start tx\n", port->line);
+
+	if (IS_SMC(pinfo)) {
+		if (in_8(&smcp->smc_smcm) & SMCM_TX)
+			return;
+	} else {
+		if (in_be16(&sccp->scc_sccm) & UART_SCCM_TX)
+			return;
+	}
+
+	if (cpm_uart_tx_pump(port) != 0) {
+		if (IS_SMC(pinfo)) {
+			setbits8(&smcp->smc_smcm, SMCM_TX);
+		} else {
+			setbits16(&sccp->scc_sccm, UART_SCCM_TX);
+		}
+	}
+}
+
+/*
+ * Stop receiver
+ */
+static void cpm_uart_stop_rx(struct uart_port *port)
+{
+	struct uart_cpm_port *pinfo = (struct uart_cpm_port *)port;
+	smc_t __iomem *smcp = pinfo->smcp;
+	scc_t __iomem *sccp = pinfo->sccp;
+
+	pr_debug("CPM uart[%d]:stop rx\n", port->line);
+
+	if (IS_SMC(pinfo))
+		clrbits8(&smcp->smc_smcm, SMCM_RX);
+	else
+		clrbits16(&sccp->scc_sccm, UART_SCCM_RX);
+}
+
+/*
+ * Enable Modem status interrupts
+ */
+static void cpm_uart_enable_ms(struct uart_port *port)
+{
+	pr_debug("CPM uart[%d]:enable ms\n", port->line);
+}
+
+/*
+ * Generate a break.
+ */
+static void cpm_uart_break_ctl(struct uart_port *port, int break_state)
+{
+	struct uart_cpm_port *pinfo = (struct uart_cpm_port *)port;
+
+	pr_debug("CPM uart[%d]:break ctrl, break_state: %d\n", port->line,
+		break_state);
+
+	if (break_state)
+		cpm_line_cr_cmd(pinfo, CPM_CR_STOP_TX);
+	else
+		cpm_line_cr_cmd(pinfo, CPM_CR_RESTART_TX);
+}
+
+/*
+ * Transmit characters, refill buffer descriptor, if possible
+ */
+static void cpm_uart_int_tx(struct uart_port *port)
+{
+	pr_debug("CPM uart[%d]:TX INT\n", port->line);
+
+	cpm_uart_tx_pump(port);
+}
+
+#ifdef CONFIG_CONSOLE_POLL
+static int serial_polled;
+#endif
+
+/*
+ * Receive characters
+ */
+static void cpm_uart_int_rx(struct uart_port *port)
+{
+	int i;
+	unsigned char ch;
+	u8 *cp;
+	struct tty_struct *tty = port->state->port.tty;
+	struct uart_cpm_port *pinfo = (struct uart_cpm_port *)port;
+	cbd_t __iomem *bdp;
+	u16 status;
+	unsigned int flg;
+
+	pr_debug("CPM uart[%d]:RX INT\n", port->line);
+
+	/* Just loop through the closed BDs and copy the characters into
+	 * the buffer.
+	 */
+	bdp = pinfo->rx_cur;
+	for (;;) {
+#ifdef CONFIG_CONSOLE_POLL
+		if (unlikely(serial_polled)) {
+			serial_polled = 0;
+			return;
+		}
+#endif
+		/* get status */
+		status = in_be16(&bdp->cbd_sc);
+		/* If this one is empty, return happy */
+		if (status & BD_SC_EMPTY)
+			break;
+
+		/* get number of characters, and check spce in flip-buffer */
+		i = in_be16(&bdp->cbd_datlen);
+
+		/* If we have not enough room in tty flip buffer, then we try
+		 * later, which will be the next rx-interrupt or a timeout
+		 */
+		if(tty_buffer_request_room(tty, i) < i) {
+			printk(KERN_WARNING "No room in flip buffer\n");
+			return;
+		}
+
+		/* get pointer */
+		cp = cpm2cpu_addr(in_be32(&bdp->cbd_bufaddr), pinfo);
+
+		/* loop through the buffer */
+		while (i-- > 0) {
+			ch = *cp++;
+			port->icount.rx++;
+			flg = TTY_NORMAL;
+
+			if (status &
+			    (BD_SC_BR | BD_SC_FR | BD_SC_PR | BD_SC_OV))
+				goto handle_error;
+			if (uart_handle_sysrq_char(port, ch))
+				continue;
+#ifdef CONFIG_CONSOLE_POLL
+			if (unlikely(serial_polled)) {
+				serial_polled = 0;
+				return;
+			}
+#endif
+		      error_return:
+			tty_insert_flip_char(tty, ch, flg);
+
+		}		/* End while (i--) */
+
+		/* This BD is ready to be used again. Clear status. get next */
+		clrbits16(&bdp->cbd_sc, BD_SC_BR | BD_SC_FR | BD_SC_PR |
+		                        BD_SC_OV | BD_SC_ID);
+		setbits16(&bdp->cbd_sc, BD_SC_EMPTY);
+
+		if (in_be16(&bdp->cbd_sc) & BD_SC_WRAP)
+			bdp = pinfo->rx_bd_base;
+		else
+			bdp++;
+
+	} /* End for (;;) */
+
+	/* Write back buffer pointer */
+	pinfo->rx_cur = bdp;
+
+	/* activate BH processing */
+	tty_flip_buffer_push(tty);
+
+	return;
+
+	/* Error processing */
+
+      handle_error:
+	/* Statistics */
+	if (status & BD_SC_BR)
+		port->icount.brk++;
+	if (status & BD_SC_PR)
+		port->icount.parity++;
+	if (status & BD_SC_FR)
+		port->icount.frame++;
+	if (status & BD_SC_OV)
+		port->icount.overrun++;
+
+	/* Mask out ignored conditions */
+	status &= port->read_status_mask;
+
+	/* Handle the remaining ones */
+	if (status & BD_SC_BR)
+		flg = TTY_BREAK;
+	else if (status & BD_SC_PR)
+		flg = TTY_PARITY;
+	else if (status & BD_SC_FR)
+		flg = TTY_FRAME;
+
+	/* overrun does not affect the current character ! */
+	if (status & BD_SC_OV) {
+		ch = 0;
+		flg = TTY_OVERRUN;
+		/* We skip this buffer */
+		/* CHECK: Is really nothing senseful there */
+		/* ASSUMPTION: it contains nothing valid */
+		i = 0;
+	}
+#ifdef SUPPORT_SYSRQ
+	port->sysrq = 0;
+#endif
+	goto error_return;
+}
+
+/*
+ * Asynchron mode interrupt handler
+ */
+static irqreturn_t cpm_uart_int(int irq, void *data)
+{
+	u8 events;
+	struct uart_port *port = data;
+	struct uart_cpm_port *pinfo = (struct uart_cpm_port *)port;
+	smc_t __iomem *smcp = pinfo->smcp;
+	scc_t __iomem *sccp = pinfo->sccp;
+
+	pr_debug("CPM uart[%d]:IRQ\n", port->line);
+
+	if (IS_SMC(pinfo)) {
+		events = in_8(&smcp->smc_smce);
+		out_8(&smcp->smc_smce, events);
+		if (events & SMCM_BRKE)
+			uart_handle_break(port);
+		if (events & SMCM_RX)
+			cpm_uart_int_rx(port);
+		if (events & SMCM_TX)
+			cpm_uart_int_tx(port);
+	} else {
+		events = in_be16(&sccp->scc_scce);
+		out_be16(&sccp->scc_scce, events);
+		if (events & UART_SCCM_BRKE)
+			uart_handle_break(port);
+		if (events & UART_SCCM_RX)
+			cpm_uart_int_rx(port);
+		if (events & UART_SCCM_TX)
+			cpm_uart_int_tx(port);
+	}
+	return (events) ? IRQ_HANDLED : IRQ_NONE;
+}
+
+static int cpm_uart_startup(struct uart_port *port)
+{
+	int retval;
+	struct uart_cpm_port *pinfo = (struct uart_cpm_port *)port;
+
+	pr_debug("CPM uart[%d]:startup\n", port->line);
+
+	/* If the port is not the console, make sure rx is disabled. */
+	if (!(pinfo->flags & FLAG_CONSOLE)) {
+		/* Disable UART rx */
+		if (IS_SMC(pinfo)) {
+			clrbits16(&pinfo->smcp->smc_smcmr, SMCMR_REN);
+			clrbits8(&pinfo->smcp->smc_smcm, SMCM_RX);
+		} else {
+			clrbits32(&pinfo->sccp->scc_gsmrl, SCC_GSMRL_ENR);
+			clrbits16(&pinfo->sccp->scc_sccm, UART_SCCM_RX);
+		}
+		cpm_line_cr_cmd(pinfo, CPM_CR_INIT_TRX);
+	}
+	/* Install interrupt handler. */
+	retval = request_irq(port->irq, cpm_uart_int, 0, "cpm_uart", port);
+	if (retval)
+		return retval;
+
+	/* Startup rx-int */
+	if (IS_SMC(pinfo)) {
+		setbits8(&pinfo->smcp->smc_smcm, SMCM_RX);
+		setbits16(&pinfo->smcp->smc_smcmr, (SMCMR_REN | SMCMR_TEN));
+	} else {
+		setbits16(&pinfo->sccp->scc_sccm, UART_SCCM_RX);
+		setbits32(&pinfo->sccp->scc_gsmrl, (SCC_GSMRL_ENR | SCC_GSMRL_ENT));
+	}
+
+	return 0;
+}
+
+inline void cpm_uart_wait_until_send(struct uart_cpm_port *pinfo)
+{
+	set_current_state(TASK_UNINTERRUPTIBLE);
+	schedule_timeout(pinfo->wait_closing);
+}
+
+/*
+ * Shutdown the uart
+ */
+static void cpm_uart_shutdown(struct uart_port *port)
+{
+	struct uart_cpm_port *pinfo = (struct uart_cpm_port *)port;
+
+	pr_debug("CPM uart[%d]:shutdown\n", port->line);
+
+	/* free interrupt handler */
+	free_irq(port->irq, port);
+
+	/* If the port is not the console, disable Rx and Tx. */
+	if (!(pinfo->flags & FLAG_CONSOLE)) {
+		/* Wait for all the BDs marked sent */
+		while(!cpm_uart_tx_empty(port)) {
+			set_current_state(TASK_UNINTERRUPTIBLE);
+			schedule_timeout(2);
+		}
+
+		if (pinfo->wait_closing)
+			cpm_uart_wait_until_send(pinfo);
+
+		/* Stop uarts */
+		if (IS_SMC(pinfo)) {
+			smc_t __iomem *smcp = pinfo->smcp;
+			clrbits16(&smcp->smc_smcmr, SMCMR_REN | SMCMR_TEN);
+			clrbits8(&smcp->smc_smcm, SMCM_RX | SMCM_TX);
+		} else {
+			scc_t __iomem *sccp = pinfo->sccp;
+			clrbits32(&sccp->scc_gsmrl, SCC_GSMRL_ENR | SCC_GSMRL_ENT);
+			clrbits16(&sccp->scc_sccm, UART_SCCM_TX | UART_SCCM_RX);
+		}
+
+		/* Shut them really down and reinit buffer descriptors */
+		if (IS_SMC(pinfo)) {
+			out_be16(&pinfo->smcup->smc_brkcr, 0);
+			cpm_line_cr_cmd(pinfo, CPM_CR_STOP_TX);
+		} else {
+			out_be16(&pinfo->sccup->scc_brkcr, 0);
+			cpm_line_cr_cmd(pinfo, CPM_CR_GRA_STOP_TX);
+		}
+
+		cpm_uart_initbd(pinfo);
+	}
+}
+
+static void cpm_uart_set_termios(struct uart_port *port,
+                                 struct ktermios *termios,
+                                 struct ktermios *old)
+{
+	int baud;
+	unsigned long flags;
+	u16 cval, scval, prev_mode;
+	int bits, sbits;
+	struct uart_cpm_port *pinfo = (struct uart_cpm_port *)port;
+	smc_t __iomem *smcp = pinfo->smcp;
+	scc_t __iomem *sccp = pinfo->sccp;
+
+	pr_debug("CPM uart[%d]:set_termios\n", port->line);
+
+	baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk / 16);
+	if (baud <= HW_BUF_SPD_THRESHOLD ||
+	    (pinfo->port.state && pinfo->port.state->port.tty->low_latency))
+		pinfo->rx_fifosize = 1;
+	else
+		pinfo->rx_fifosize = RX_BUF_SIZE;
+
+	/* Character length programmed into the mode register is the
+	 * sum of: 1 start bit, number of data bits, 0 or 1 parity bit,
+	 * 1 or 2 stop bits, minus 1.
+	 * The value 'bits' counts this for us.
+	 */
+	cval = 0;
+	scval = 0;
+
+	/* byte size */
+	switch (termios->c_cflag & CSIZE) {
+	case CS5:
+		bits = 5;
+		break;
+	case CS6:
+		bits = 6;
+		break;
+	case CS7:
+		bits = 7;
+		break;
+	case CS8:
+		bits = 8;
+		break;
+		/* Never happens, but GCC is too dumb to figure it out */
+	default:
+		bits = 8;
+		break;
+	}
+	sbits = bits - 5;
+
+	if (termios->c_cflag & CSTOPB) {
+		cval |= SMCMR_SL;	/* Two stops */
+		scval |= SCU_PSMR_SL;
+		bits++;
+	}
+
+	if (termios->c_cflag & PARENB) {
+		cval |= SMCMR_PEN;
+		scval |= SCU_PSMR_PEN;
+		bits++;
+		if (!(termios->c_cflag & PARODD)) {
+			cval |= SMCMR_PM_EVEN;
+			scval |= (SCU_PSMR_REVP | SCU_PSMR_TEVP);
+		}
+	}
+
+	/*
+	 * Update the timeout
+	 */
+	uart_update_timeout(port, termios->c_cflag, baud);
+
+	/*
+	 * Set up parity check flag
+	 */
+#define RELEVANT_IFLAG(iflag) (iflag & (IGNBRK|BRKINT|IGNPAR|PARMRK|INPCK))
+
+	port->read_status_mask = (BD_SC_EMPTY | BD_SC_OV);
+	if (termios->c_iflag & INPCK)
+		port->read_status_mask |= BD_SC_FR | BD_SC_PR;
+	if ((termios->c_iflag & BRKINT) || (termios->c_iflag & PARMRK))
+		port->read_status_mask |= BD_SC_BR;
+
+	/*
+	 * Characters to ignore
+	 */
+	port->ignore_status_mask = 0;
+	if (termios->c_iflag & IGNPAR)
+		port->ignore_status_mask |= BD_SC_PR | BD_SC_FR;
+	if (termios->c_iflag & IGNBRK) {
+		port->ignore_status_mask |= BD_SC_BR;
+		/*
+		 * If we're ignore parity and break indicators, ignore
+		 * overruns too.  (For real raw support).
+		 */
+		if (termios->c_iflag & IGNPAR)
+			port->ignore_status_mask |= BD_SC_OV;
+	}
+	/*
+	 * !!! ignore all characters if CREAD is not set
+	 */
+	if ((termios->c_cflag & CREAD) == 0)
+		port->read_status_mask &= ~BD_SC_EMPTY;
+
+	spin_lock_irqsave(&port->lock, flags);
+
+	/* Start bit has not been added (so don't, because we would just
+	 * subtract it later), and we need to add one for the number of
+	 * stops bits (there is always at least one).
+	 */
+	bits++;
+	if (IS_SMC(pinfo)) {
+		/*
+		 * MRBLR can be changed while an SMC/SCC is operating only
+		 * if it is done in a single bus cycle with one 16-bit move
+		 * (not two 8-bit bus cycles back-to-back). This occurs when
+		 * the cp shifts control to the next RxBD, so the change does
+		 * not take effect immediately. To guarantee the exact RxBD
+		 * on which the change occurs, change MRBLR only while the
+		 * SMC/SCC receiver is disabled.
+		 */
+		out_be16(&pinfo->smcup->smc_mrblr, pinfo->rx_fifosize);
+
+		/* Set the mode register.  We want to keep a copy of the
+		 * enables, because we want to put them back if they were
+		 * present.
+		 */
+		prev_mode = in_be16(&smcp->smc_smcmr) & (SMCMR_REN | SMCMR_TEN);
+		/* Output in *one* operation, so we don't interrupt RX/TX if they
+		 * were already enabled. */
+		out_be16(&smcp->smc_smcmr, smcr_mk_clen(bits) | cval |
+		    SMCMR_SM_UART | prev_mode);
+	} else {
+		out_be16(&pinfo->sccup->scc_genscc.scc_mrblr, pinfo->rx_fifosize);
+		out_be16(&sccp->scc_psmr, (sbits << 12) | scval);
+	}
+
+	if (pinfo->clk)
+		clk_set_rate(pinfo->clk, baud);
+	else
+		cpm_set_brg(pinfo->brg - 1, baud);
+	spin_unlock_irqrestore(&port->lock, flags);
+}
+
+static const char *cpm_uart_type(struct uart_port *port)
+{
+	pr_debug("CPM uart[%d]:uart_type\n", port->line);
+
+	return port->type == PORT_CPM ? "CPM UART" : NULL;
+}
+
+/*
+ * verify the new serial_struct (for TIOCSSERIAL).
+ */
+static int cpm_uart_verify_port(struct uart_port *port,
+				struct serial_struct *ser)
+{
+	int ret = 0;
+
+	pr_debug("CPM uart[%d]:verify_port\n", port->line);
+
+	if (ser->type != PORT_UNKNOWN && ser->type != PORT_CPM)
+		ret = -EINVAL;
+	if (ser->irq < 0 || ser->irq >= nr_irqs)
+		ret = -EINVAL;
+	if (ser->baud_base < 9600)
+		ret = -EINVAL;
+	return ret;
+}
+
+/*
+ * Transmit characters, refill buffer descriptor, if possible
+ */
+static int cpm_uart_tx_pump(struct uart_port *port)
+{
+	cbd_t __iomem *bdp;
+	u8 *p;
+	int count;
+	struct uart_cpm_port *pinfo = (struct uart_cpm_port *)port;
+	struct circ_buf *xmit = &port->state->xmit;
+
+	/* Handle xon/xoff */
+	if (port->x_char) {
+		/* Pick next descriptor and fill from buffer */
+		bdp = pinfo->tx_cur;
+
+		p = cpm2cpu_addr(in_be32(&bdp->cbd_bufaddr), pinfo);
+
+		*p++ = port->x_char;
+
+		out_be16(&bdp->cbd_datlen, 1);
+		setbits16(&bdp->cbd_sc, BD_SC_READY);
+		/* Get next BD. */
+		if (in_be16(&bdp->cbd_sc) & BD_SC_WRAP)
+			bdp = pinfo->tx_bd_base;
+		else
+			bdp++;
+		pinfo->tx_cur = bdp;
+
+		port->icount.tx++;
+		port->x_char = 0;
+		return 1;
+	}
+
+	if (uart_circ_empty(xmit) || uart_tx_stopped(port)) {
+		cpm_uart_stop_tx(port);
+		return 0;
+	}
+
+	/* Pick next descriptor and fill from buffer */
+	bdp = pinfo->tx_cur;
+
+	while (!(in_be16(&bdp->cbd_sc) & BD_SC_READY) &&
+	       xmit->tail != xmit->head) {
+		count = 0;
+		p = cpm2cpu_addr(in_be32(&bdp->cbd_bufaddr), pinfo);
+		while (count < pinfo->tx_fifosize) {
+			*p++ = xmit->buf[xmit->tail];
+			xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
+			port->icount.tx++;
+			count++;
+			if (xmit->head == xmit->tail)
+				break;
+		}
+		out_be16(&bdp->cbd_datlen, count);
+		setbits16(&bdp->cbd_sc, BD_SC_READY);
+		/* Get next BD. */
+		if (in_be16(&bdp->cbd_sc) & BD_SC_WRAP)
+			bdp = pinfo->tx_bd_base;
+		else
+			bdp++;
+	}
+	pinfo->tx_cur = bdp;
+
+	if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
+		uart_write_wakeup(port);
+
+	if (uart_circ_empty(xmit)) {
+		cpm_uart_stop_tx(port);
+		return 0;
+	}
+
+	return 1;
+}
+
+/*
+ * init buffer descriptors
+ */
+static void cpm_uart_initbd(struct uart_cpm_port *pinfo)
+{
+	int i;
+	u8 *mem_addr;
+	cbd_t __iomem *bdp;
+
+	pr_debug("CPM uart[%d]:initbd\n", pinfo->port.line);
+
+	/* Set the physical address of the host memory
+	 * buffers in the buffer descriptors, and the
+	 * virtual address for us to work with.
+	 */
+	mem_addr = pinfo->mem_addr;
+	bdp = pinfo->rx_cur = pinfo->rx_bd_base;
+	for (i = 0; i < (pinfo->rx_nrfifos - 1); i++, bdp++) {
+		out_be32(&bdp->cbd_bufaddr, cpu2cpm_addr(mem_addr, pinfo));
+		out_be16(&bdp->cbd_sc, BD_SC_EMPTY | BD_SC_INTRPT);
+		mem_addr += pinfo->rx_fifosize;
+	}
+
+	out_be32(&bdp->cbd_bufaddr, cpu2cpm_addr(mem_addr, pinfo));
+	out_be16(&bdp->cbd_sc, BD_SC_WRAP | BD_SC_EMPTY | BD_SC_INTRPT);
+
+	/* Set the physical address of the host memory
+	 * buffers in the buffer descriptors, and the
+	 * virtual address for us to work with.
+	 */
+	mem_addr = pinfo->mem_addr + L1_CACHE_ALIGN(pinfo->rx_nrfifos * pinfo->rx_fifosize);
+	bdp = pinfo->tx_cur = pinfo->tx_bd_base;
+	for (i = 0; i < (pinfo->tx_nrfifos - 1); i++, bdp++) {
+		out_be32(&bdp->cbd_bufaddr, cpu2cpm_addr(mem_addr, pinfo));
+		out_be16(&bdp->cbd_sc, BD_SC_INTRPT);
+		mem_addr += pinfo->tx_fifosize;
+	}
+
+	out_be32(&bdp->cbd_bufaddr, cpu2cpm_addr(mem_addr, pinfo));
+	out_be16(&bdp->cbd_sc, BD_SC_WRAP | BD_SC_INTRPT);
+}
+
+static void cpm_uart_init_scc(struct uart_cpm_port *pinfo)
+{
+	scc_t __iomem *scp;
+	scc_uart_t __iomem *sup;
+
+	pr_debug("CPM uart[%d]:init_scc\n", pinfo->port.line);
+
+	scp = pinfo->sccp;
+	sup = pinfo->sccup;
+
+	/* Store address */
+	out_be16(&pinfo->sccup->scc_genscc.scc_rbase,
+	         (u8 __iomem *)pinfo->rx_bd_base - DPRAM_BASE);
+	out_be16(&pinfo->sccup->scc_genscc.scc_tbase,
+	         (u8 __iomem *)pinfo->tx_bd_base - DPRAM_BASE);
+
+	/* Set up the uart parameters in the
+	 * parameter ram.
+	 */
+
+	cpm_set_scc_fcr(sup);
+
+	out_be16(&sup->scc_genscc.scc_mrblr, pinfo->rx_fifosize);
+	out_be16(&sup->scc_maxidl, pinfo->rx_fifosize);
+	out_be16(&sup->scc_brkcr, 1);
+	out_be16(&sup->scc_parec, 0);
+	out_be16(&sup->scc_frmec, 0);
+	out_be16(&sup->scc_nosec, 0);
+	out_be16(&sup->scc_brkec, 0);
+	out_be16(&sup->scc_uaddr1, 0);
+	out_be16(&sup->scc_uaddr2, 0);
+	out_be16(&sup->scc_toseq, 0);
+	out_be16(&sup->scc_char1, 0x8000);
+	out_be16(&sup->scc_char2, 0x8000);
+	out_be16(&sup->scc_char3, 0x8000);
+	out_be16(&sup->scc_char4, 0x8000);
+	out_be16(&sup->scc_char5, 0x8000);
+	out_be16(&sup->scc_char6, 0x8000);
+	out_be16(&sup->scc_char7, 0x8000);
+	out_be16(&sup->scc_char8, 0x8000);
+	out_be16(&sup->scc_rccm, 0xc0ff);
+
+	/* Send the CPM an initialize command.
+	 */
+	cpm_line_cr_cmd(pinfo, CPM_CR_INIT_TRX);
+
+	/* Set UART mode, 8 bit, no parity, one stop.
+	 * Enable receive and transmit.
+	 */
+	out_be32(&scp->scc_gsmrh, 0);
+	out_be32(&scp->scc_gsmrl,
+	         SCC_GSMRL_MODE_UART | SCC_GSMRL_TDCR_16 | SCC_GSMRL_RDCR_16);
+
+	/* Enable rx interrupts  and clear all pending events.  */
+	out_be16(&scp->scc_sccm, 0);
+	out_be16(&scp->scc_scce, 0xffff);
+	out_be16(&scp->scc_dsr, 0x7e7e);
+	out_be16(&scp->scc_psmr, 0x3000);
+
+	setbits32(&scp->scc_gsmrl, SCC_GSMRL_ENR | SCC_GSMRL_ENT);
+}
+
+static void cpm_uart_init_smc(struct uart_cpm_port *pinfo)
+{
+	smc_t __iomem *sp;
+	smc_uart_t __iomem *up;
+
+	pr_debug("CPM uart[%d]:init_smc\n", pinfo->port.line);
+
+	sp = pinfo->smcp;
+	up = pinfo->smcup;
+
+	/* Store address */
+	out_be16(&pinfo->smcup->smc_rbase,
+	         (u8 __iomem *)pinfo->rx_bd_base - DPRAM_BASE);
+	out_be16(&pinfo->smcup->smc_tbase,
+	         (u8 __iomem *)pinfo->tx_bd_base - DPRAM_BASE);
+
+/*
+ *  In case SMC1 is being relocated...
+ */
+#if defined (CONFIG_I2C_SPI_SMC1_UCODE_PATCH)
+	out_be16(&up->smc_rbptr, in_be16(&pinfo->smcup->smc_rbase));
+	out_be16(&up->smc_tbptr, in_be16(&pinfo->smcup->smc_tbase));
+	out_be32(&up->smc_rstate, 0);
+	out_be32(&up->smc_tstate, 0);
+	out_be16(&up->smc_brkcr, 1);              /* number of break chars */
+	out_be16(&up->smc_brkec, 0);
+#endif
+
+	/* Set up the uart parameters in the
+	 * parameter ram.
+	 */
+	cpm_set_smc_fcr(up);
+
+	/* Using idle character time requires some additional tuning.  */
+	out_be16(&up->smc_mrblr, pinfo->rx_fifosize);
+	out_be16(&up->smc_maxidl, pinfo->rx_fifosize);
+	out_be16(&up->smc_brklen, 0);
+	out_be16(&up->smc_brkec, 0);
+	out_be16(&up->smc_brkcr, 1);
+
+	cpm_line_cr_cmd(pinfo, CPM_CR_INIT_TRX);
+
+	/* Set UART mode, 8 bit, no parity, one stop.
+	 * Enable receive and transmit.
+	 */
+	out_be16(&sp->smc_smcmr, smcr_mk_clen(9) | SMCMR_SM_UART);
+
+	/* Enable only rx interrupts clear all pending events. */
+	out_8(&sp->smc_smcm, 0);
+	out_8(&sp->smc_smce, 0xff);
+
+	setbits16(&sp->smc_smcmr, SMCMR_REN | SMCMR_TEN);
+}
+
+/*
+ * Initialize port. This is called from early_console stuff
+ * so we have to be careful here !
+ */
+static int cpm_uart_request_port(struct uart_port *port)
+{
+	struct uart_cpm_port *pinfo = (struct uart_cpm_port *)port;
+	int ret;
+
+	pr_debug("CPM uart[%d]:request port\n", port->line);
+
+	if (pinfo->flags & FLAG_CONSOLE)
+		return 0;
+
+	if (IS_SMC(pinfo)) {
+		clrbits8(&pinfo->smcp->smc_smcm, SMCM_RX | SMCM_TX);
+		clrbits16(&pinfo->smcp->smc_smcmr, SMCMR_REN | SMCMR_TEN);
+	} else {
+		clrbits16(&pinfo->sccp->scc_sccm, UART_SCCM_TX | UART_SCCM_RX);
+		clrbits32(&pinfo->sccp->scc_gsmrl, SCC_GSMRL_ENR | SCC_GSMRL_ENT);
+	}
+
+	ret = cpm_uart_allocbuf(pinfo, 0);
+
+	if (ret)
+		return ret;
+
+	cpm_uart_initbd(pinfo);
+	if (IS_SMC(pinfo))
+		cpm_uart_init_smc(pinfo);
+	else
+		cpm_uart_init_scc(pinfo);
+
+	return 0;
+}
+
+static void cpm_uart_release_port(struct uart_port *port)
+{
+	struct uart_cpm_port *pinfo = (struct uart_cpm_port *)port;
+
+	if (!(pinfo->flags & FLAG_CONSOLE))
+		cpm_uart_freebuf(pinfo);
+}
+
+/*
+ * Configure/autoconfigure the port.
+ */
+static void cpm_uart_config_port(struct uart_port *port, int flags)
+{
+	pr_debug("CPM uart[%d]:config_port\n", port->line);
+
+	if (flags & UART_CONFIG_TYPE) {
+		port->type = PORT_CPM;
+		cpm_uart_request_port(port);
+	}
+}
+
+#if defined(CONFIG_CONSOLE_POLL) || defined(CONFIG_SERIAL_CPM_CONSOLE)
+/*
+ * Write a string to the serial port
+ * Note that this is called with interrupts already disabled
+ */
+static void cpm_uart_early_write(struct uart_cpm_port *pinfo,
+		const char *string, u_int count)
+{
+	unsigned int i;
+	cbd_t __iomem *bdp, *bdbase;
+	unsigned char *cpm_outp_addr;
+
+	/* Get the address of the host memory buffer.
+	 */
+	bdp = pinfo->tx_cur;
+	bdbase = pinfo->tx_bd_base;
+
+	/*
+	 * Now, do each character.  This is not as bad as it looks
+	 * since this is a holding FIFO and not a transmitting FIFO.
+	 * We could add the complexity of filling the entire transmit
+	 * buffer, but we would just wait longer between accesses......
+	 */
+	for (i = 0; i < count; i++, string++) {
+		/* Wait for transmitter fifo to empty.
+		 * Ready indicates output is ready, and xmt is doing
+		 * that, not that it is ready for us to send.
+		 */
+		while ((in_be16(&bdp->cbd_sc) & BD_SC_READY) != 0)
+			;
+
+		/* Send the character out.
+		 * If the buffer address is in the CPM DPRAM, don't
+		 * convert it.
+		 */
+		cpm_outp_addr = cpm2cpu_addr(in_be32(&bdp->cbd_bufaddr),
+					pinfo);
+		*cpm_outp_addr = *string;
+
+		out_be16(&bdp->cbd_datlen, 1);
+		setbits16(&bdp->cbd_sc, BD_SC_READY);
+
+		if (in_be16(&bdp->cbd_sc) & BD_SC_WRAP)
+			bdp = bdbase;
+		else
+			bdp++;
+
+		/* if a LF, also do CR... */
+		if (*string == 10) {
+			while ((in_be16(&bdp->cbd_sc) & BD_SC_READY) != 0)
+				;
+
+			cpm_outp_addr = cpm2cpu_addr(in_be32(&bdp->cbd_bufaddr),
+						pinfo);
+			*cpm_outp_addr = 13;
+
+			out_be16(&bdp->cbd_datlen, 1);
+			setbits16(&bdp->cbd_sc, BD_SC_READY);
+
+			if (in_be16(&bdp->cbd_sc) & BD_SC_WRAP)
+				bdp = bdbase;
+			else
+				bdp++;
+		}
+	}
+
+	/*
+	 * Finally, Wait for transmitter & holding register to empty
+	 *  and restore the IER
+	 */
+	while ((in_be16(&bdp->cbd_sc) & BD_SC_READY) != 0)
+		;
+
+	pinfo->tx_cur = bdp;
+}
+#endif
+
+#ifdef CONFIG_CONSOLE_POLL
+/* Serial polling routines for writing and reading from the uart while
+ * in an interrupt or debug context.
+ */
+
+#define GDB_BUF_SIZE	512	/* power of 2, please */
+
+static char poll_buf[GDB_BUF_SIZE];
+static char *pollp;
+static int poll_chars;
+
+static int poll_wait_key(char *obuf, struct uart_cpm_port *pinfo)
+{
+	u_char		c, *cp;
+	volatile cbd_t	*bdp;
+	int		i;
+
+	/* Get the address of the host memory buffer.
+	 */
+	bdp = pinfo->rx_cur;
+	while (bdp->cbd_sc & BD_SC_EMPTY)
+		;
+
+	/* If the buffer address is in the CPM DPRAM, don't
+	 * convert it.
+	 */
+	cp = cpm2cpu_addr(bdp->cbd_bufaddr, pinfo);
+
+	if (obuf) {
+		i = c = bdp->cbd_datlen;
+		while (i-- > 0)
+			*obuf++ = *cp++;
+	} else
+		c = *cp;
+	bdp->cbd_sc &= ~(BD_SC_BR | BD_SC_FR | BD_SC_PR | BD_SC_OV | BD_SC_ID);
+	bdp->cbd_sc |= BD_SC_EMPTY;
+
+	if (bdp->cbd_sc & BD_SC_WRAP)
+		bdp = pinfo->rx_bd_base;
+	else
+		bdp++;
+	pinfo->rx_cur = (cbd_t *)bdp;
+
+	return (int)c;
+}
+
+static int cpm_get_poll_char(struct uart_port *port)
+{
+	struct uart_cpm_port *pinfo = (struct uart_cpm_port *)port;
+
+	if (!serial_polled) {
+		serial_polled = 1;
+		poll_chars = 0;
+	}
+	if (poll_chars <= 0) {
+		poll_chars = poll_wait_key(poll_buf, pinfo);
+		pollp = poll_buf;
+	}
+	poll_chars--;
+	return *pollp++;
+}
+
+static void cpm_put_poll_char(struct uart_port *port,
+			 unsigned char c)
+{
+	struct uart_cpm_port *pinfo = (struct uart_cpm_port *)port;
+	static char ch[2];
+
+	ch[0] = (char)c;
+	cpm_uart_early_write(pinfo, ch, 1);
+}
+#endif /* CONFIG_CONSOLE_POLL */
+
+static struct uart_ops cpm_uart_pops = {
+	.tx_empty	= cpm_uart_tx_empty,
+	.set_mctrl	= cpm_uart_set_mctrl,
+	.get_mctrl	= cpm_uart_get_mctrl,
+	.stop_tx	= cpm_uart_stop_tx,
+	.start_tx	= cpm_uart_start_tx,
+	.stop_rx	= cpm_uart_stop_rx,
+	.enable_ms	= cpm_uart_enable_ms,
+	.break_ctl	= cpm_uart_break_ctl,
+	.startup	= cpm_uart_startup,
+	.shutdown	= cpm_uart_shutdown,
+	.set_termios	= cpm_uart_set_termios,
+	.type		= cpm_uart_type,
+	.release_port	= cpm_uart_release_port,
+	.request_port	= cpm_uart_request_port,
+	.config_port	= cpm_uart_config_port,
+	.verify_port	= cpm_uart_verify_port,
+#ifdef CONFIG_CONSOLE_POLL
+	.poll_get_char = cpm_get_poll_char,
+	.poll_put_char = cpm_put_poll_char,
+#endif
+};
+
+struct uart_cpm_port cpm_uart_ports[UART_NR];
+
+static int cpm_uart_init_port(struct device_node *np,
+                              struct uart_cpm_port *pinfo)
+{
+	const u32 *data;
+	void __iomem *mem, *pram;
+	int len;
+	int ret;
+	int i;
+
+	data = of_get_property(np, "clock", NULL);
+	if (data) {
+		struct clk *clk = clk_get(NULL, (const char*)data);
+		if (!IS_ERR(clk))
+			pinfo->clk = clk;
+	}
+	if (!pinfo->clk) {
+		data = of_get_property(np, "fsl,cpm-brg", &len);
+		if (!data || len != 4) {
+			printk(KERN_ERR "CPM UART %s has no/invalid "
+			                "fsl,cpm-brg property.\n", np->name);
+			return -EINVAL;
+		}
+		pinfo->brg = *data;
+	}
+
+	data = of_get_property(np, "fsl,cpm-command", &len);
+	if (!data || len != 4) {
+		printk(KERN_ERR "CPM UART %s has no/invalid "
+		                "fsl,cpm-command property.\n", np->name);
+		return -EINVAL;
+	}
+	pinfo->command = *data;
+
+	mem = of_iomap(np, 0);
+	if (!mem)
+		return -ENOMEM;
+
+	if (of_device_is_compatible(np, "fsl,cpm1-scc-uart") ||
+	    of_device_is_compatible(np, "fsl,cpm2-scc-uart")) {
+		pinfo->sccp = mem;
+		pinfo->sccup = pram = cpm_uart_map_pram(pinfo, np);
+	} else if (of_device_is_compatible(np, "fsl,cpm1-smc-uart") ||
+	           of_device_is_compatible(np, "fsl,cpm2-smc-uart")) {
+		pinfo->flags |= FLAG_SMC;
+		pinfo->smcp = mem;
+		pinfo->smcup = pram = cpm_uart_map_pram(pinfo, np);
+	} else {
+		ret = -ENODEV;
+		goto out_mem;
+	}
+
+	if (!pram) {
+		ret = -ENOMEM;
+		goto out_mem;
+	}
+
+	pinfo->tx_nrfifos = TX_NUM_FIFO;
+	pinfo->tx_fifosize = TX_BUF_SIZE;
+	pinfo->rx_nrfifos = RX_NUM_FIFO;
+	pinfo->rx_fifosize = RX_BUF_SIZE;
+
+	pinfo->port.uartclk = ppc_proc_freq;
+	pinfo->port.mapbase = (unsigned long)mem;
+	pinfo->port.type = PORT_CPM;
+	pinfo->port.ops = &cpm_uart_pops,
+	pinfo->port.iotype = UPIO_MEM;
+	pinfo->port.fifosize = pinfo->tx_nrfifos * pinfo->tx_fifosize;
+	spin_lock_init(&pinfo->port.lock);
+
+	pinfo->port.irq = of_irq_to_resource(np, 0, NULL);
+	if (pinfo->port.irq == NO_IRQ) {
+		ret = -EINVAL;
+		goto out_pram;
+	}
+
+	for (i = 0; i < NUM_GPIOS; i++)
+		pinfo->gpios[i] = of_get_gpio(np, i);
+
+#ifdef CONFIG_PPC_EARLY_DEBUG_CPM
+	udbg_putc = NULL;
+#endif
+
+	return cpm_uart_request_port(&pinfo->port);
+
+out_pram:
+	cpm_uart_unmap_pram(pinfo, pram);
+out_mem:
+	iounmap(mem);
+	return ret;
+}
+
+#ifdef CONFIG_SERIAL_CPM_CONSOLE
+/*
+ *	Print a string to the serial port trying not to disturb
+ *	any possible real use of the port...
+ *
+ *	Note that this is called with interrupts already disabled
+ */
+static void cpm_uart_console_write(struct console *co, const char *s,
+				   u_int count)
+{
+	struct uart_cpm_port *pinfo = &cpm_uart_ports[co->index];
+	unsigned long flags;
+	int nolock = oops_in_progress;
+
+	if (unlikely(nolock)) {
+		local_irq_save(flags);
+	} else {
+		spin_lock_irqsave(&pinfo->port.lock, flags);
+	}
+
+	cpm_uart_early_write(pinfo, s, count);
+
+	if (unlikely(nolock)) {
+		local_irq_restore(flags);
+	} else {
+		spin_unlock_irqrestore(&pinfo->port.lock, flags);
+	}
+}
+
+
+static int __init cpm_uart_console_setup(struct console *co, char *options)
+{
+	int baud = 38400;
+	int bits = 8;
+	int parity = 'n';
+	int flow = 'n';
+	int ret;
+	struct uart_cpm_port *pinfo;
+	struct uart_port *port;
+
+	struct device_node *np = NULL;
+	int i = 0;
+
+	if (co->index >= UART_NR) {
+		printk(KERN_ERR "cpm_uart: console index %d too high\n",
+		       co->index);
+		return -ENODEV;
+	}
+
+	do {
+		np = of_find_node_by_type(np, "serial");
+		if (!np)
+			return -ENODEV;
+
+		if (!of_device_is_compatible(np, "fsl,cpm1-smc-uart") &&
+		    !of_device_is_compatible(np, "fsl,cpm1-scc-uart") &&
+		    !of_device_is_compatible(np, "fsl,cpm2-smc-uart") &&
+		    !of_device_is_compatible(np, "fsl,cpm2-scc-uart"))
+			i--;
+	} while (i++ != co->index);
+
+	pinfo = &cpm_uart_ports[co->index];
+
+	pinfo->flags |= FLAG_CONSOLE;
+	port = &pinfo->port;
+
+	ret = cpm_uart_init_port(np, pinfo);
+	of_node_put(np);
+	if (ret)
+		return ret;
+
+	if (options) {
+		uart_parse_options(options, &baud, &parity, &bits, &flow);
+	} else {
+		if ((baud = uart_baudrate()) == -1)
+			baud = 9600;
+	}
+
+	if (IS_SMC(pinfo)) {
+		out_be16(&pinfo->smcup->smc_brkcr, 0);
+		cpm_line_cr_cmd(pinfo, CPM_CR_STOP_TX);
+		clrbits8(&pinfo->smcp->smc_smcm, SMCM_RX | SMCM_TX);
+		clrbits16(&pinfo->smcp->smc_smcmr, SMCMR_REN | SMCMR_TEN);
+	} else {
+		out_be16(&pinfo->sccup->scc_brkcr, 0);
+		cpm_line_cr_cmd(pinfo, CPM_CR_GRA_STOP_TX);
+		clrbits16(&pinfo->sccp->scc_sccm, UART_SCCM_TX | UART_SCCM_RX);
+		clrbits32(&pinfo->sccp->scc_gsmrl, SCC_GSMRL_ENR | SCC_GSMRL_ENT);
+	}
+
+	ret = cpm_uart_allocbuf(pinfo, 1);
+
+	if (ret)
+		return ret;
+
+	cpm_uart_initbd(pinfo);
+
+	if (IS_SMC(pinfo))
+		cpm_uart_init_smc(pinfo);
+	else
+		cpm_uart_init_scc(pinfo);
+
+	uart_set_options(port, co, baud, parity, bits, flow);
+	cpm_line_cr_cmd(pinfo, CPM_CR_RESTART_TX);
+
+	return 0;
+}
+
+static struct uart_driver cpm_reg;
+static struct console cpm_scc_uart_console = {
+	.name		= "ttyCPM",
+	.write		= cpm_uart_console_write,
+	.device		= uart_console_device,
+	.setup		= cpm_uart_console_setup,
+	.flags		= CON_PRINTBUFFER,
+	.index		= -1,
+	.data		= &cpm_reg,
+};
+
+static int __init cpm_uart_console_init(void)
+{
+	register_console(&cpm_scc_uart_console);
+	return 0;
+}
+
+console_initcall(cpm_uart_console_init);
+
+#define CPM_UART_CONSOLE	&cpm_scc_uart_console
+#else
+#define CPM_UART_CONSOLE	NULL
+#endif
+
+static struct uart_driver cpm_reg = {
+	.owner		= THIS_MODULE,
+	.driver_name	= "ttyCPM",
+	.dev_name	= "ttyCPM",
+	.major		= SERIAL_CPM_MAJOR,
+	.minor		= SERIAL_CPM_MINOR,
+	.cons		= CPM_UART_CONSOLE,
+	.nr		= UART_NR,
+};
+
+static int probe_index;
+
+static int __devinit cpm_uart_probe(struct platform_device *ofdev)
+{
+	int index = probe_index++;
+	struct uart_cpm_port *pinfo = &cpm_uart_ports[index];
+	int ret;
+
+	pinfo->port.line = index;
+
+	if (index >= UART_NR)
+		return -ENODEV;
+
+	dev_set_drvdata(&ofdev->dev, pinfo);
+
+	/* initialize the device pointer for the port */
+	pinfo->port.dev = &ofdev->dev;
+
+	ret = cpm_uart_init_port(ofdev->dev.of_node, pinfo);
+	if (ret)
+		return ret;
+
+	return uart_add_one_port(&cpm_reg, &pinfo->port);
+}
+
+static int __devexit cpm_uart_remove(struct platform_device *ofdev)
+{
+	struct uart_cpm_port *pinfo = dev_get_drvdata(&ofdev->dev);
+	return uart_remove_one_port(&cpm_reg, &pinfo->port);
+}
+
+static struct of_device_id cpm_uart_match[] = {
+	{
+		.compatible = "fsl,cpm1-smc-uart",
+	},
+	{
+		.compatible = "fsl,cpm1-scc-uart",
+	},
+	{
+		.compatible = "fsl,cpm2-smc-uart",
+	},
+	{
+		.compatible = "fsl,cpm2-scc-uart",
+	},
+	{}
+};
+
+static struct platform_driver cpm_uart_driver = {
+	.driver = {
+		.name = "cpm_uart",
+		.owner = THIS_MODULE,
+		.of_match_table = cpm_uart_match,
+	},
+	.probe = cpm_uart_probe,
+	.remove = cpm_uart_remove,
+ };
+
+static int __init cpm_uart_init(void)
+{
+	int ret = uart_register_driver(&cpm_reg);
+	if (ret)
+		return ret;
+
+	ret = platform_driver_register(&cpm_uart_driver);
+	if (ret)
+		uart_unregister_driver(&cpm_reg);
+
+	return ret;
+}
+
+static void __exit cpm_uart_exit(void)
+{
+	platform_driver_unregister(&cpm_uart_driver);
+	uart_unregister_driver(&cpm_reg);
+}
+
+module_init(cpm_uart_init);
+module_exit(cpm_uart_exit);
+
+MODULE_AUTHOR("Kumar Gala/Antoniou Pantelis");
+MODULE_DESCRIPTION("CPM SCC/SMC port driver $Revision: 0.01 $");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS_CHARDEV(SERIAL_CPM_MAJOR, SERIAL_CPM_MINOR);
diff --git a/ap/os/linux/linux-3.4.x/drivers/tty/serial/cpm_uart/cpm_uart_cpm1.c b/ap/os/linux/linux-3.4.x/drivers/tty/serial/cpm_uart/cpm_uart_cpm1.c
new file mode 100644
index 0000000..18f7957
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/tty/serial/cpm_uart/cpm_uart_cpm1.c
@@ -0,0 +1,136 @@
+/*
+ *  Driver for CPM (SCC/SMC) serial ports; CPM1 definitions
+ *
+ *  Maintainer: Kumar Gala (galak@kernel.crashing.org) (CPM2)
+ *              Pantelis Antoniou (panto@intracom.gr) (CPM1)
+ *
+ *  Copyright (C) 2004 Freescale Semiconductor, Inc.
+ *            (C) 2004 Intracom, S.A.
+ *            (C) 2006 MontaVista Software, Inc.
+ *		Vitaly Bordug <vbordug@ru.mvista.com>
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/tty.h>
+#include <linux/gfp.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/bootmem.h>
+#include <linux/dma-mapping.h>
+
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/fs_pd.h>
+
+#include <linux/serial_core.h>
+#include <linux/kernel.h>
+
+#include <linux/of.h>
+
+#include "cpm_uart.h"
+
+/**************************************************************/
+
+void cpm_line_cr_cmd(struct uart_cpm_port *port, int cmd)
+{
+	cpm_command(port->command, cmd);
+}
+
+void __iomem *cpm_uart_map_pram(struct uart_cpm_port *port,
+				struct device_node *np)
+{
+	return of_iomap(np, 1);
+}
+
+void cpm_uart_unmap_pram(struct uart_cpm_port *port, void __iomem *pram)
+{
+	iounmap(pram);
+}
+
+/*
+ * Allocate DP-Ram and memory buffers. We need to allocate a transmit and
+ * receive buffer descriptors from dual port ram, and a character
+ * buffer area from host mem. If we are allocating for the console we need
+ * to do it from bootmem
+ */
+int cpm_uart_allocbuf(struct uart_cpm_port *pinfo, unsigned int is_con)
+{
+	int dpmemsz, memsz;
+	u8 *dp_mem;
+	unsigned long dp_offset;
+	u8 *mem_addr;
+	dma_addr_t dma_addr = 0;
+
+	pr_debug("CPM uart[%d]:allocbuf\n", pinfo->port.line);
+
+	dpmemsz = sizeof(cbd_t) * (pinfo->rx_nrfifos + pinfo->tx_nrfifos);
+	dp_offset = cpm_dpalloc(dpmemsz, 8);
+	if (IS_ERR_VALUE(dp_offset)) {
+		printk(KERN_ERR
+		       "cpm_uart_cpm1.c: could not allocate buffer descriptors\n");
+		return -ENOMEM;
+	}
+	dp_mem = cpm_dpram_addr(dp_offset);
+
+	memsz = L1_CACHE_ALIGN(pinfo->rx_nrfifos * pinfo->rx_fifosize) +
+	    L1_CACHE_ALIGN(pinfo->tx_nrfifos * pinfo->tx_fifosize);
+	if (is_con) {
+		/* was hostalloc but changed cause it blows away the */
+		/* large tlb mapping when pinning the kernel area    */
+		mem_addr = (u8 *) cpm_dpram_addr(cpm_dpalloc(memsz, 8));
+		dma_addr = (u32)cpm_dpram_phys(mem_addr);
+	} else
+		mem_addr = dma_alloc_coherent(pinfo->port.dev, memsz, &dma_addr,
+					      GFP_KERNEL);
+
+	if (mem_addr == NULL) {
+		cpm_dpfree(dp_offset);
+		printk(KERN_ERR
+		       "cpm_uart_cpm1.c: could not allocate coherent memory\n");
+		return -ENOMEM;
+	}
+
+	pinfo->dp_addr = dp_offset;
+	pinfo->mem_addr = mem_addr;             /*  virtual address*/
+	pinfo->dma_addr = dma_addr;             /*  physical address*/
+	pinfo->mem_size = memsz;
+
+	pinfo->rx_buf = mem_addr;
+	pinfo->tx_buf = pinfo->rx_buf + L1_CACHE_ALIGN(pinfo->rx_nrfifos
+						       * pinfo->rx_fifosize);
+
+	pinfo->rx_bd_base = (cbd_t __iomem __force *)dp_mem;
+	pinfo->tx_bd_base = pinfo->rx_bd_base + pinfo->rx_nrfifos;
+
+	return 0;
+}
+
+void cpm_uart_freebuf(struct uart_cpm_port *pinfo)
+{
+	dma_free_coherent(pinfo->port.dev, L1_CACHE_ALIGN(pinfo->rx_nrfifos *
+							  pinfo->rx_fifosize) +
+			  L1_CACHE_ALIGN(pinfo->tx_nrfifos *
+					 pinfo->tx_fifosize), pinfo->mem_addr,
+			  pinfo->dma_addr);
+
+	cpm_dpfree(pinfo->dp_addr);
+}
diff --git a/ap/os/linux/linux-3.4.x/drivers/tty/serial/cpm_uart/cpm_uart_cpm1.h b/ap/os/linux/linux-3.4.x/drivers/tty/serial/cpm_uart/cpm_uart_cpm1.h
new file mode 100644
index 0000000..60c7e94
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/tty/serial/cpm_uart/cpm_uart_cpm1.h
@@ -0,0 +1,32 @@
+/*
+ * Driver for CPM (SCC/SMC) serial ports
+ *
+ * definitions for cpm1
+ *
+ */
+
+#ifndef CPM_UART_CPM1_H
+#define CPM_UART_CPM1_H
+
+#include <asm/cpm1.h>
+
+static inline void cpm_set_brg(int brg, int baud)
+{
+	cpm_setbrg(brg, baud);
+}
+
+static inline void cpm_set_scc_fcr(scc_uart_t __iomem * sup)
+{
+	out_8(&sup->scc_genscc.scc_rfcr, SMC_EB);
+	out_8(&sup->scc_genscc.scc_tfcr, SMC_EB);
+}
+
+static inline void cpm_set_smc_fcr(smc_uart_t __iomem * up)
+{
+	out_8(&up->smc_rfcr, SMC_EB);
+	out_8(&up->smc_tfcr, SMC_EB);
+}
+
+#define DPRAM_BASE	((u8 __iomem __force *)cpm_dpram_addr(0))
+
+#endif
diff --git a/ap/os/linux/linux-3.4.x/drivers/tty/serial/cpm_uart/cpm_uart_cpm2.c b/ap/os/linux/linux-3.4.x/drivers/tty/serial/cpm_uart/cpm_uart_cpm2.c
new file mode 100644
index 0000000..a4927e6
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/tty/serial/cpm_uart/cpm_uart_cpm2.c
@@ -0,0 +1,172 @@
+/*
+ *  Driver for CPM (SCC/SMC) serial ports; CPM2 definitions
+ *
+ *  Maintainer: Kumar Gala (galak@kernel.crashing.org) (CPM2)
+ *              Pantelis Antoniou (panto@intracom.gr) (CPM1)
+ *
+ *  Copyright (C) 2004 Freescale Semiconductor, Inc.
+ *            (C) 2004 Intracom, S.A.
+ *            (C) 2006 MontaVista Software, Inc.
+ *		Vitaly Bordug <vbordug@ru.mvista.com>
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/tty.h>
+#include <linux/ioport.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <linux/serial.h>
+#include <linux/console.h>
+#include <linux/sysrq.h>
+#include <linux/device.h>
+#include <linux/bootmem.h>
+#include <linux/dma-mapping.h>
+
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/fs_pd.h>
+#include <asm/prom.h>
+
+#include <linux/serial_core.h>
+#include <linux/kernel.h>
+
+#include "cpm_uart.h"
+
+/**************************************************************/
+
+void cpm_line_cr_cmd(struct uart_cpm_port *port, int cmd)
+{
+	cpm_command(port->command, cmd);
+}
+
+void __iomem *cpm_uart_map_pram(struct uart_cpm_port *port,
+				struct device_node *np)
+{
+	void __iomem *pram;
+	unsigned long offset;
+	struct resource res;
+	resource_size_t len;
+
+	/* Don't remap parameter RAM if it has already been initialized
+	 * during console setup.
+	 */
+	if (IS_SMC(port) && port->smcup)
+		return port->smcup;
+	else if (!IS_SMC(port) && port->sccup)
+		return port->sccup;
+
+	if (of_address_to_resource(np, 1, &res))
+		return NULL;
+
+	len = resource_size(&res);
+	pram = ioremap(res.start, len);
+	if (!pram)
+		return NULL;
+
+	if (!IS_SMC(port))
+		return pram;
+
+	if (len != 2) {
+		printk(KERN_WARNING "cpm_uart[%d]: device tree references "
+			"SMC pram, using boot loader/wrapper pram mapping. "
+			"Please fix your device tree to reference the pram "
+			"base register instead.\n",
+			port->port.line);
+		return pram;
+	}
+
+	offset = cpm_dpalloc(PROFF_SMC_SIZE, 64);
+	out_be16(pram, offset);
+	iounmap(pram);
+	return cpm_muram_addr(offset);
+}
+
+void cpm_uart_unmap_pram(struct uart_cpm_port *port, void __iomem *pram)
+{
+	if (!IS_SMC(port))
+		iounmap(pram);
+}
+
+/*
+ * Allocate DP-Ram and memory buffers. We need to allocate a transmit and
+ * receive buffer descriptors from dual port ram, and a character
+ * buffer area from host mem. If we are allocating for the console we need
+ * to do it from bootmem
+ */
+int cpm_uart_allocbuf(struct uart_cpm_port *pinfo, unsigned int is_con)
+{
+	int dpmemsz, memsz;
+	u8 __iomem *dp_mem;
+	unsigned long dp_offset;
+	u8 *mem_addr;
+	dma_addr_t dma_addr = 0;
+
+	pr_debug("CPM uart[%d]:allocbuf\n", pinfo->port.line);
+
+	dpmemsz = sizeof(cbd_t) * (pinfo->rx_nrfifos + pinfo->tx_nrfifos);
+	dp_offset = cpm_dpalloc(dpmemsz, 8);
+	if (IS_ERR_VALUE(dp_offset)) {
+		printk(KERN_ERR
+		       "cpm_uart_cpm.c: could not allocate buffer descriptors\n");
+		return -ENOMEM;
+	}
+
+	dp_mem = cpm_dpram_addr(dp_offset);
+
+	memsz = L1_CACHE_ALIGN(pinfo->rx_nrfifos * pinfo->rx_fifosize) +
+	    L1_CACHE_ALIGN(pinfo->tx_nrfifos * pinfo->tx_fifosize);
+	if (is_con) {
+		mem_addr = kzalloc(memsz, GFP_NOWAIT);
+		dma_addr = virt_to_bus(mem_addr);
+	}
+	else
+		mem_addr = dma_alloc_coherent(pinfo->port.dev, memsz, &dma_addr,
+					      GFP_KERNEL);
+
+	if (mem_addr == NULL) {
+		cpm_dpfree(dp_offset);
+		printk(KERN_ERR
+		       "cpm_uart_cpm.c: could not allocate coherent memory\n");
+		return -ENOMEM;
+	}
+
+	pinfo->dp_addr = dp_offset;
+	pinfo->mem_addr = mem_addr;
+	pinfo->dma_addr = dma_addr;
+	pinfo->mem_size = memsz;
+
+	pinfo->rx_buf = mem_addr;
+	pinfo->tx_buf = pinfo->rx_buf + L1_CACHE_ALIGN(pinfo->rx_nrfifos
+						       * pinfo->rx_fifosize);
+
+	pinfo->rx_bd_base = (cbd_t __iomem *)dp_mem;
+	pinfo->tx_bd_base = pinfo->rx_bd_base + pinfo->rx_nrfifos;
+
+	return 0;
+}
+
+void cpm_uart_freebuf(struct uart_cpm_port *pinfo)
+{
+	dma_free_coherent(pinfo->port.dev, L1_CACHE_ALIGN(pinfo->rx_nrfifos *
+							  pinfo->rx_fifosize) +
+			  L1_CACHE_ALIGN(pinfo->tx_nrfifos *
+					 pinfo->tx_fifosize), (void __force *)pinfo->mem_addr,
+			  pinfo->dma_addr);
+
+	cpm_dpfree(pinfo->dp_addr);
+}
diff --git a/ap/os/linux/linux-3.4.x/drivers/tty/serial/cpm_uart/cpm_uart_cpm2.h b/ap/os/linux/linux-3.4.x/drivers/tty/serial/cpm_uart/cpm_uart_cpm2.h
new file mode 100644
index 0000000..51e651a
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/tty/serial/cpm_uart/cpm_uart_cpm2.h
@@ -0,0 +1,32 @@
+/*
+ * Driver for CPM (SCC/SMC) serial ports
+ *
+ * definitions for cpm2
+ *
+ */
+
+#ifndef CPM_UART_CPM2_H
+#define CPM_UART_CPM2_H
+
+#include <asm/cpm2.h>
+
+static inline void cpm_set_brg(int brg, int baud)
+{
+	cpm_setbrg(brg, baud);
+}
+
+static inline void cpm_set_scc_fcr(scc_uart_t __iomem *sup)
+{
+	out_8(&sup->scc_genscc.scc_rfcr, CPMFCR_GBL | CPMFCR_EB);
+	out_8(&sup->scc_genscc.scc_tfcr, CPMFCR_GBL | CPMFCR_EB);
+}
+
+static inline void cpm_set_smc_fcr(smc_uart_t __iomem *up)
+{
+	out_8(&up->smc_rfcr, CPMFCR_GBL | CPMFCR_EB);
+	out_8(&up->smc_tfcr, CPMFCR_GBL | CPMFCR_EB);
+}
+
+#define DPRAM_BASE	((u8 __iomem __force *)cpm_dpram_addr(0))
+
+#endif
diff --git a/ap/os/linux/linux-3.4.x/drivers/tty/serial/crisv10.c b/ap/os/linux/linux-3.4.x/drivers/tty/serial/crisv10.c
new file mode 100644
index 0000000..5b07c0c
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/tty/serial/crisv10.c
@@ -0,0 +1,4557 @@
+/*
+ * Serial port driver for the ETRAX 100LX chip
+ *
+ *    Copyright (C) 1998-2007  Axis Communications AB
+ *
+ *    Many, many authors. Based once upon a time on serial.c for 16x50.
+ *
+ */
+
+static char *serial_version = "$Revision: 1.25 $";
+
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/signal.h>
+#include <linux/sched.h>
+#include <linux/timer.h>
+#include <linux/interrupt.h>
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
+#include <linux/major.h>
+#include <linux/string.h>
+#include <linux/fcntl.h>
+#include <linux/mm.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/mutex.h>
+#include <linux/bitops.h>
+#include <linux/seq_file.h>
+#include <linux/delay.h>
+#include <linux/module.h>
+#include <linux/uaccess.h>
+#include <linux/io.h>
+
+#include <asm/irq.h>
+#include <asm/dma.h>
+
+#include <arch/svinto.h>
+#include <arch/system.h>
+
+/* non-arch dependent serial structures are in linux/serial.h */
+#include <linux/serial.h>
+/* while we keep our own stuff (struct e100_serial) in a local .h file */
+#include "crisv10.h"
+#include <asm/fasttimer.h>
+#include <arch/io_interface_mux.h>
+
+#ifdef CONFIG_ETRAX_SERIAL_FAST_TIMER
+#ifndef CONFIG_ETRAX_FAST_TIMER
+#error "Enable FAST_TIMER to use SERIAL_FAST_TIMER"
+#endif
+#endif
+
+#if defined(CONFIG_ETRAX_SERIAL_RX_TIMEOUT_TICKS) && \
+           (CONFIG_ETRAX_SERIAL_RX_TIMEOUT_TICKS == 0)
+#error "RX_TIMEOUT_TICKS == 0 not allowed, use 1"
+#endif
+
+#if defined(CONFIG_ETRAX_RS485_ON_PA) && defined(CONFIG_ETRAX_RS485_ON_PORT_G)
+#error "Disable either CONFIG_ETRAX_RS485_ON_PA or CONFIG_ETRAX_RS485_ON_PORT_G"
+#endif
+
+/*
+ * All of the compatibilty code so we can compile serial.c against
+ * older kernels is hidden in serial_compat.h
+ */
+#if defined(LOCAL_HEADERS)
+#include "serial_compat.h"
+#endif
+
+struct tty_driver *serial_driver;
+
+/* number of characters left in xmit buffer before we ask for more */
+#define WAKEUP_CHARS 256
+
+//#define SERIAL_DEBUG_INTR
+//#define SERIAL_DEBUG_OPEN
+//#define SERIAL_DEBUG_FLOW
+//#define SERIAL_DEBUG_DATA
+//#define SERIAL_DEBUG_THROTTLE
+//#define SERIAL_DEBUG_IO  /* Debug for Extra control and status pins */
+//#define SERIAL_DEBUG_LINE 0 /* What serport we want to debug */
+
+/* Enable this to use serial interrupts to handle when you
+   expect the first received event on the serial port to
+   be an error, break or similar. Used to be able to flash IRMA
+   from eLinux */
+#define SERIAL_HANDLE_EARLY_ERRORS
+
+/* Currently 16 descriptors x 128 bytes = 2048 bytes */
+#define SERIAL_DESCR_BUF_SIZE 256
+
+#define SERIAL_PRESCALE_BASE 3125000 /* 3.125MHz */
+#define DEF_BAUD_BASE SERIAL_PRESCALE_BASE
+
+/* We don't want to load the system with massive fast timer interrupt
+ * on high baudrates so limit it to 250 us (4kHz) */
+#define MIN_FLUSH_TIME_USEC 250
+
+/* Add an x here to log a lot of timer stuff */
+#define TIMERD(x)
+/* Debug details of interrupt handling */
+#define DINTR1(x)  /* irq on/off, errors */
+#define DINTR2(x)    /* tx and rx */
+/* Debug flip buffer stuff */
+#define DFLIP(x)
+/* Debug flow control and overview of data flow */
+#define DFLOW(x)
+#define DBAUD(x)
+#define DLOG_INT_TRIG(x)
+
+//#define DEBUG_LOG_INCLUDED
+#ifndef DEBUG_LOG_INCLUDED
+#define DEBUG_LOG(line, string, value)
+#else
+struct debug_log_info
+{
+	unsigned long time;
+	unsigned long timer_data;
+//  int line;
+	const char *string;
+	int value;
+};
+#define DEBUG_LOG_SIZE 4096
+
+struct debug_log_info debug_log[DEBUG_LOG_SIZE];
+int debug_log_pos = 0;
+
+#define DEBUG_LOG(_line, _string, _value) do { \
+  if ((_line) == SERIAL_DEBUG_LINE) {\
+    debug_log_func(_line, _string, _value); \
+  }\
+}while(0)
+
+void debug_log_func(int line, const char *string, int value)
+{
+	if (debug_log_pos < DEBUG_LOG_SIZE) {
+		debug_log[debug_log_pos].time = jiffies;
+		debug_log[debug_log_pos].timer_data = *R_TIMER_DATA;
+//    debug_log[debug_log_pos].line = line;
+		debug_log[debug_log_pos].string = string;
+		debug_log[debug_log_pos].value = value;
+		debug_log_pos++;
+	}
+	/*printk(string, value);*/
+}
+#endif
+
+#ifndef CONFIG_ETRAX_SERIAL_RX_TIMEOUT_TICKS
+/* Default number of timer ticks before flushing rx fifo
+ * When using "little data, low latency applications: use 0
+ * When using "much data applications (PPP)" use ~5
+ */
+#define CONFIG_ETRAX_SERIAL_RX_TIMEOUT_TICKS 5
+#endif
+
+unsigned long timer_data_to_ns(unsigned long timer_data);
+
+static void change_speed(struct e100_serial *info);
+static void rs_throttle(struct tty_struct * tty);
+static void rs_wait_until_sent(struct tty_struct *tty, int timeout);
+static int rs_write(struct tty_struct *tty,
+		const unsigned char *buf, int count);
+#ifdef CONFIG_ETRAX_RS485
+static int e100_write_rs485(struct tty_struct *tty,
+		const unsigned char *buf, int count);
+#endif
+static int get_lsr_info(struct e100_serial *info, unsigned int *value);
+
+
+#define DEF_BAUD 115200   /* 115.2 kbit/s */
+#define STD_FLAGS (ASYNC_BOOT_AUTOCONF | ASYNC_SKIP_TEST)
+#define DEF_RX 0x20  /* or SERIAL_CTRL_W >> 8 */
+/* Default value of tx_ctrl register: has txd(bit 7)=1 (idle) as default */
+#define DEF_TX 0x80  /* or SERIAL_CTRL_B */
+
+/* offsets from R_SERIALx_CTRL */
+
+#define REG_DATA 0
+#define REG_DATA_STATUS32 0 /* this is the 32 bit register R_SERIALx_READ */
+#define REG_TR_DATA 0
+#define REG_STATUS 1
+#define REG_TR_CTRL 1
+#define REG_REC_CTRL 2
+#define REG_BAUD 3
+#define REG_XOFF 4  /* this is a 32 bit register */
+
+/* The bitfields are the same for all serial ports */
+#define SER_RXD_MASK         IO_MASK(R_SERIAL0_STATUS, rxd)
+#define SER_DATA_AVAIL_MASK  IO_MASK(R_SERIAL0_STATUS, data_avail)
+#define SER_FRAMING_ERR_MASK IO_MASK(R_SERIAL0_STATUS, framing_err)
+#define SER_PAR_ERR_MASK     IO_MASK(R_SERIAL0_STATUS, par_err)
+#define SER_OVERRUN_MASK     IO_MASK(R_SERIAL0_STATUS, overrun)
+
+#define SER_ERROR_MASK (SER_OVERRUN_MASK | SER_PAR_ERR_MASK | SER_FRAMING_ERR_MASK)
+
+/* Values for info->errorcode */
+#define ERRCODE_SET_BREAK    (TTY_BREAK)
+#define ERRCODE_INSERT        0x100
+#define ERRCODE_INSERT_BREAK (ERRCODE_INSERT | TTY_BREAK)
+
+#define FORCE_EOP(info)  *R_SET_EOP = 1U << info->iseteop;
+
+/*
+ * General note regarding the use of IO_* macros in this file:
+ *
+ * We will use the bits defined for DMA channel 6 when using various
+ * IO_* macros (e.g. IO_STATE, IO_MASK, IO_EXTRACT) and _assume_ they are
+ * the same for all channels (which of course they are).
+ *
+ * We will also use the bits defined for serial port 0 when writing commands
+ * to the different ports, as these bits too are the same for all ports.
+ */
+
+
+/* Mask for the irqs possibly enabled in R_IRQ_MASK1_RD etc. */
+static const unsigned long e100_ser_int_mask = 0
+#ifdef CONFIG_ETRAX_SERIAL_PORT0
+| IO_MASK(R_IRQ_MASK1_RD, ser0_data) | IO_MASK(R_IRQ_MASK1_RD, ser0_ready)
+#endif
+#ifdef CONFIG_ETRAX_SERIAL_PORT1
+| IO_MASK(R_IRQ_MASK1_RD, ser1_data) | IO_MASK(R_IRQ_MASK1_RD, ser1_ready)
+#endif
+#ifdef CONFIG_ETRAX_SERIAL_PORT2
+| IO_MASK(R_IRQ_MASK1_RD, ser2_data) | IO_MASK(R_IRQ_MASK1_RD, ser2_ready)
+#endif
+#ifdef CONFIG_ETRAX_SERIAL_PORT3
+| IO_MASK(R_IRQ_MASK1_RD, ser3_data) | IO_MASK(R_IRQ_MASK1_RD, ser3_ready)
+#endif
+;
+unsigned long r_alt_ser_baudrate_shadow = 0;
+
+/* this is the data for the four serial ports in the etrax100 */
+/*  DMA2(ser2), DMA4(ser3), DMA6(ser0) or DMA8(ser1) */
+/* R_DMA_CHx_CLR_INTR, R_DMA_CHx_FIRST, R_DMA_CHx_CMD */
+
+static struct e100_serial rs_table[] = {
+	{ .baud        = DEF_BAUD,
+	  .ioport        = (unsigned char *)R_SERIAL0_CTRL,
+	  .irq         = 1U << 12, /* uses DMA 6 and 7 */
+	  .oclrintradr = R_DMA_CH6_CLR_INTR,
+	  .ofirstadr   = R_DMA_CH6_FIRST,
+	  .ocmdadr     = R_DMA_CH6_CMD,
+	  .ostatusadr  = R_DMA_CH6_STATUS,
+	  .iclrintradr = R_DMA_CH7_CLR_INTR,
+	  .ifirstadr   = R_DMA_CH7_FIRST,
+	  .icmdadr     = R_DMA_CH7_CMD,
+	  .idescradr   = R_DMA_CH7_DESCR,
+	  .flags       = STD_FLAGS,
+	  .rx_ctrl     = DEF_RX,
+	  .tx_ctrl     = DEF_TX,
+	  .iseteop     = 2,
+	  .dma_owner   = dma_ser0,
+	  .io_if       = if_serial_0,
+#ifdef CONFIG_ETRAX_SERIAL_PORT0
+          .enabled  = 1,
+#ifdef CONFIG_ETRAX_SERIAL_PORT0_DMA6_OUT
+	  .dma_out_enabled = 1,
+	  .dma_out_nbr = SER0_TX_DMA_NBR,
+	  .dma_out_irq_nbr = SER0_DMA_TX_IRQ_NBR,
+	  .dma_out_irq_flags = 0,
+	  .dma_out_irq_description = "serial 0 dma tr",
+#else
+	  .dma_out_enabled = 0,
+	  .dma_out_nbr = UINT_MAX,
+	  .dma_out_irq_nbr = 0,
+	  .dma_out_irq_flags = 0,
+	  .dma_out_irq_description = NULL,
+#endif
+#ifdef CONFIG_ETRAX_SERIAL_PORT0_DMA7_IN
+	  .dma_in_enabled = 1,
+	  .dma_in_nbr = SER0_RX_DMA_NBR,
+	  .dma_in_irq_nbr = SER0_DMA_RX_IRQ_NBR,
+	  .dma_in_irq_flags = 0,
+	  .dma_in_irq_description = "serial 0 dma rec",
+#else
+	  .dma_in_enabled = 0,
+	  .dma_in_nbr = UINT_MAX,
+	  .dma_in_irq_nbr = 0,
+	  .dma_in_irq_flags = 0,
+	  .dma_in_irq_description = NULL,
+#endif
+#else
+          .enabled  = 0,
+	  .io_if_description = NULL,
+	  .dma_out_enabled = 0,
+	  .dma_in_enabled = 0
+#endif
+
+},  /* ttyS0 */
+#ifndef CONFIG_SVINTO_SIM
+	{ .baud        = DEF_BAUD,
+	  .ioport        = (unsigned char *)R_SERIAL1_CTRL,
+	  .irq         = 1U << 16, /* uses DMA 8 and 9 */
+	  .oclrintradr = R_DMA_CH8_CLR_INTR,
+	  .ofirstadr   = R_DMA_CH8_FIRST,
+	  .ocmdadr     = R_DMA_CH8_CMD,
+	  .ostatusadr  = R_DMA_CH8_STATUS,
+	  .iclrintradr = R_DMA_CH9_CLR_INTR,
+	  .ifirstadr   = R_DMA_CH9_FIRST,
+	  .icmdadr     = R_DMA_CH9_CMD,
+	  .idescradr   = R_DMA_CH9_DESCR,
+	  .flags       = STD_FLAGS,
+	  .rx_ctrl     = DEF_RX,
+	  .tx_ctrl     = DEF_TX,
+	  .iseteop     = 3,
+	  .dma_owner   = dma_ser1,
+	  .io_if       = if_serial_1,
+#ifdef CONFIG_ETRAX_SERIAL_PORT1
+          .enabled  = 1,
+	  .io_if_description = "ser1",
+#ifdef CONFIG_ETRAX_SERIAL_PORT1_DMA8_OUT
+	  .dma_out_enabled = 1,
+	  .dma_out_nbr = SER1_TX_DMA_NBR,
+	  .dma_out_irq_nbr = SER1_DMA_TX_IRQ_NBR,
+	  .dma_out_irq_flags = 0,
+	  .dma_out_irq_description = "serial 1 dma tr",
+#else
+	  .dma_out_enabled = 0,
+	  .dma_out_nbr = UINT_MAX,
+	  .dma_out_irq_nbr = 0,
+	  .dma_out_irq_flags = 0,
+	  .dma_out_irq_description = NULL,
+#endif
+#ifdef CONFIG_ETRAX_SERIAL_PORT1_DMA9_IN
+	  .dma_in_enabled = 1,
+	  .dma_in_nbr = SER1_RX_DMA_NBR,
+	  .dma_in_irq_nbr = SER1_DMA_RX_IRQ_NBR,
+	  .dma_in_irq_flags = 0,
+	  .dma_in_irq_description = "serial 1 dma rec",
+#else
+	  .dma_in_enabled = 0,
+	  .dma_in_enabled = 0,
+	  .dma_in_nbr = UINT_MAX,
+	  .dma_in_irq_nbr = 0,
+	  .dma_in_irq_flags = 0,
+	  .dma_in_irq_description = NULL,
+#endif
+#else
+          .enabled  = 0,
+	  .io_if_description = NULL,
+	  .dma_in_irq_nbr = 0,
+	  .dma_out_enabled = 0,
+	  .dma_in_enabled = 0
+#endif
+},  /* ttyS1 */
+
+	{ .baud        = DEF_BAUD,
+	  .ioport        = (unsigned char *)R_SERIAL2_CTRL,
+	  .irq         = 1U << 4,  /* uses DMA 2 and 3 */
+	  .oclrintradr = R_DMA_CH2_CLR_INTR,
+	  .ofirstadr   = R_DMA_CH2_FIRST,
+	  .ocmdadr     = R_DMA_CH2_CMD,
+	  .ostatusadr  = R_DMA_CH2_STATUS,
+	  .iclrintradr = R_DMA_CH3_CLR_INTR,
+	  .ifirstadr   = R_DMA_CH3_FIRST,
+	  .icmdadr     = R_DMA_CH3_CMD,
+	  .idescradr   = R_DMA_CH3_DESCR,
+	  .flags       = STD_FLAGS,
+	  .rx_ctrl     = DEF_RX,
+	  .tx_ctrl     = DEF_TX,
+	  .iseteop     = 0,
+	  .dma_owner   = dma_ser2,
+	  .io_if       = if_serial_2,
+#ifdef CONFIG_ETRAX_SERIAL_PORT2
+          .enabled  = 1,
+	  .io_if_description = "ser2",
+#ifdef CONFIG_ETRAX_SERIAL_PORT2_DMA2_OUT
+	  .dma_out_enabled = 1,
+	  .dma_out_nbr = SER2_TX_DMA_NBR,
+	  .dma_out_irq_nbr = SER2_DMA_TX_IRQ_NBR,
+	  .dma_out_irq_flags = 0,
+	  .dma_out_irq_description = "serial 2 dma tr",
+#else
+	  .dma_out_enabled = 0,
+	  .dma_out_nbr = UINT_MAX,
+	  .dma_out_irq_nbr = 0,
+	  .dma_out_irq_flags = 0,
+	  .dma_out_irq_description = NULL,
+#endif
+#ifdef CONFIG_ETRAX_SERIAL_PORT2_DMA3_IN
+	  .dma_in_enabled = 1,
+	  .dma_in_nbr = SER2_RX_DMA_NBR,
+	  .dma_in_irq_nbr = SER2_DMA_RX_IRQ_NBR,
+	  .dma_in_irq_flags = 0,
+	  .dma_in_irq_description = "serial 2 dma rec",
+#else
+	  .dma_in_enabled = 0,
+	  .dma_in_nbr = UINT_MAX,
+	  .dma_in_irq_nbr = 0,
+	  .dma_in_irq_flags = 0,
+	  .dma_in_irq_description = NULL,
+#endif
+#else
+          .enabled  = 0,
+	  .io_if_description = NULL,
+	  .dma_out_enabled = 0,
+	  .dma_in_enabled = 0
+#endif
+ },  /* ttyS2 */
+
+	{ .baud        = DEF_BAUD,
+	  .ioport        = (unsigned char *)R_SERIAL3_CTRL,
+	  .irq         = 1U << 8,  /* uses DMA 4 and 5 */
+	  .oclrintradr = R_DMA_CH4_CLR_INTR,
+	  .ofirstadr   = R_DMA_CH4_FIRST,
+	  .ocmdadr     = R_DMA_CH4_CMD,
+	  .ostatusadr  = R_DMA_CH4_STATUS,
+	  .iclrintradr = R_DMA_CH5_CLR_INTR,
+	  .ifirstadr   = R_DMA_CH5_FIRST,
+	  .icmdadr     = R_DMA_CH5_CMD,
+	  .idescradr   = R_DMA_CH5_DESCR,
+	  .flags       = STD_FLAGS,
+	  .rx_ctrl     = DEF_RX,
+	  .tx_ctrl     = DEF_TX,
+	  .iseteop     = 1,
+	  .dma_owner   = dma_ser3,
+	  .io_if       = if_serial_3,
+#ifdef CONFIG_ETRAX_SERIAL_PORT3
+          .enabled  = 1,
+	  .io_if_description = "ser3",
+#ifdef CONFIG_ETRAX_SERIAL_PORT3_DMA4_OUT
+	  .dma_out_enabled = 1,
+	  .dma_out_nbr = SER3_TX_DMA_NBR,
+	  .dma_out_irq_nbr = SER3_DMA_TX_IRQ_NBR,
+	  .dma_out_irq_flags = 0,
+	  .dma_out_irq_description = "serial 3 dma tr",
+#else
+	  .dma_out_enabled = 0,
+	  .dma_out_nbr = UINT_MAX,
+	  .dma_out_irq_nbr = 0,
+	  .dma_out_irq_flags = 0,
+	  .dma_out_irq_description = NULL,
+#endif
+#ifdef CONFIG_ETRAX_SERIAL_PORT3_DMA5_IN
+	  .dma_in_enabled = 1,
+	  .dma_in_nbr = SER3_RX_DMA_NBR,
+	  .dma_in_irq_nbr = SER3_DMA_RX_IRQ_NBR,
+	  .dma_in_irq_flags = 0,
+	  .dma_in_irq_description = "serial 3 dma rec",
+#else
+	  .dma_in_enabled = 0,
+	  .dma_in_nbr = UINT_MAX,
+	  .dma_in_irq_nbr = 0,
+	  .dma_in_irq_flags = 0,
+	  .dma_in_irq_description = NULL
+#endif
+#else
+          .enabled  = 0,
+	  .io_if_description = NULL,
+	  .dma_out_enabled = 0,
+	  .dma_in_enabled = 0
+#endif
+ }   /* ttyS3 */
+#endif
+};
+
+
+#define NR_PORTS (sizeof(rs_table)/sizeof(struct e100_serial))
+
+#ifdef CONFIG_ETRAX_SERIAL_FAST_TIMER
+static struct fast_timer fast_timers[NR_PORTS];
+#endif
+
+#ifdef CONFIG_ETRAX_SERIAL_PROC_ENTRY
+#define PROCSTAT(x) x
+struct ser_statistics_type {
+	int overrun_cnt;
+	int early_errors_cnt;
+	int ser_ints_ok_cnt;
+	int errors_cnt;
+	unsigned long int processing_flip;
+	unsigned long processing_flip_still_room;
+	unsigned long int timeout_flush_cnt;
+	int rx_dma_ints;
+	int tx_dma_ints;
+	int rx_tot;
+	int tx_tot;
+};
+
+static struct ser_statistics_type ser_stat[NR_PORTS];
+
+#else
+
+#define PROCSTAT(x)
+
+#endif /* CONFIG_ETRAX_SERIAL_PROC_ENTRY */
+
+/* RS-485 */
+#if defined(CONFIG_ETRAX_RS485)
+#ifdef CONFIG_ETRAX_FAST_TIMER
+static struct fast_timer fast_timers_rs485[NR_PORTS];
+#endif
+#if defined(CONFIG_ETRAX_RS485_ON_PA)
+static int rs485_pa_bit = CONFIG_ETRAX_RS485_ON_PA_BIT;
+#endif
+#if defined(CONFIG_ETRAX_RS485_ON_PORT_G)
+static int rs485_port_g_bit = CONFIG_ETRAX_RS485_ON_PORT_G_BIT;
+#endif
+#endif
+
+/* Info and macros needed for each ports extra control/status signals. */
+#define E100_STRUCT_PORT(line, pinname) \
+ ((CONFIG_ETRAX_SER##line##_##pinname##_ON_PA_BIT >= 0)? \
+		(R_PORT_PA_DATA): ( \
+ (CONFIG_ETRAX_SER##line##_##pinname##_ON_PB_BIT >= 0)? \
+		(R_PORT_PB_DATA):&dummy_ser[line]))
+
+#define E100_STRUCT_SHADOW(line, pinname) \
+ ((CONFIG_ETRAX_SER##line##_##pinname##_ON_PA_BIT >= 0)? \
+		(&port_pa_data_shadow): ( \
+ (CONFIG_ETRAX_SER##line##_##pinname##_ON_PB_BIT >= 0)? \
+		(&port_pb_data_shadow):&dummy_ser[line]))
+#define E100_STRUCT_MASK(line, pinname) \
+ ((CONFIG_ETRAX_SER##line##_##pinname##_ON_PA_BIT >= 0)? \
+		(1<<CONFIG_ETRAX_SER##line##_##pinname##_ON_PA_BIT): ( \
+ (CONFIG_ETRAX_SER##line##_##pinname##_ON_PB_BIT >= 0)? \
+		(1<<CONFIG_ETRAX_SER##line##_##pinname##_ON_PB_BIT):DUMMY_##pinname##_MASK))
+
+#define DUMMY_DTR_MASK 1
+#define DUMMY_RI_MASK  2
+#define DUMMY_DSR_MASK 4
+#define DUMMY_CD_MASK  8
+static unsigned char dummy_ser[NR_PORTS] = {0xFF, 0xFF, 0xFF,0xFF};
+
+/* If not all status pins are used or disabled, use mixed mode */
+#ifdef CONFIG_ETRAX_SERIAL_PORT0
+
+#define SER0_PA_BITSUM (CONFIG_ETRAX_SER0_DTR_ON_PA_BIT+CONFIG_ETRAX_SER0_RI_ON_PA_BIT+CONFIG_ETRAX_SER0_DSR_ON_PA_BIT+CONFIG_ETRAX_SER0_CD_ON_PA_BIT)
+
+#if SER0_PA_BITSUM != -4
+#  if CONFIG_ETRAX_SER0_DTR_ON_PA_BIT == -1
+#    ifndef CONFIG_ETRAX_SER0_DTR_RI_DSR_CD_MIXED
+#      define CONFIG_ETRAX_SER0_DTR_RI_DSR_CD_MIXED 1
+#    endif
+#   endif
+# if CONFIG_ETRAX_SER0_RI_ON_PA_BIT == -1
+#   ifndef CONFIG_ETRAX_SER0_DTR_RI_DSR_CD_MIXED
+#     define CONFIG_ETRAX_SER0_DTR_RI_DSR_CD_MIXED 1
+#   endif
+#  endif
+#  if CONFIG_ETRAX_SER0_DSR_ON_PA_BIT == -1
+#    ifndef CONFIG_ETRAX_SER0_DTR_RI_DSR_CD_MIXED
+#      define CONFIG_ETRAX_SER0_DTR_RI_DSR_CD_MIXED 1
+#    endif
+#  endif
+#  if CONFIG_ETRAX_SER0_CD_ON_PA_BIT == -1
+#    ifndef CONFIG_ETRAX_SER0_DTR_RI_DSR_CD_MIXED
+#      define CONFIG_ETRAX_SER0_DTR_RI_DSR_CD_MIXED 1
+#    endif
+#  endif
+#endif
+
+#define SER0_PB_BITSUM (CONFIG_ETRAX_SER0_DTR_ON_PB_BIT+CONFIG_ETRAX_SER0_RI_ON_PB_BIT+CONFIG_ETRAX_SER0_DSR_ON_PB_BIT+CONFIG_ETRAX_SER0_CD_ON_PB_BIT)
+
+#if SER0_PB_BITSUM != -4
+#  if CONFIG_ETRAX_SER0_DTR_ON_PB_BIT == -1
+#    ifndef CONFIG_ETRAX_SER0_DTR_RI_DSR_CD_MIXED
+#      define CONFIG_ETRAX_SER0_DTR_RI_DSR_CD_MIXED 1
+#    endif
+#   endif
+# if CONFIG_ETRAX_SER0_RI_ON_PB_BIT == -1
+#   ifndef CONFIG_ETRAX_SER0_DTR_RI_DSR_CD_MIXED
+#     define CONFIG_ETRAX_SER0_DTR_RI_DSR_CD_MIXED 1
+#   endif
+#  endif
+#  if CONFIG_ETRAX_SER0_DSR_ON_PB_BIT == -1
+#    ifndef CONFIG_ETRAX_SER0_DTR_RI_DSR_CD_MIXED
+#      define CONFIG_ETRAX_SER0_DTR_RI_DSR_CD_MIXED 1
+#    endif
+#  endif
+#  if CONFIG_ETRAX_SER0_CD_ON_PB_BIT == -1
+#    ifndef CONFIG_ETRAX_SER0_DTR_RI_DSR_CD_MIXED
+#      define CONFIG_ETRAX_SER0_DTR_RI_DSR_CD_MIXED 1
+#    endif
+#  endif
+#endif
+
+#endif /* PORT0 */
+
+
+#ifdef CONFIG_ETRAX_SERIAL_PORT1
+
+#define SER1_PA_BITSUM (CONFIG_ETRAX_SER1_DTR_ON_PA_BIT+CONFIG_ETRAX_SER1_RI_ON_PA_BIT+CONFIG_ETRAX_SER1_DSR_ON_PA_BIT+CONFIG_ETRAX_SER1_CD_ON_PA_BIT)
+
+#if SER1_PA_BITSUM != -4
+#  if CONFIG_ETRAX_SER1_DTR_ON_PA_BIT == -1
+#    ifndef CONFIG_ETRAX_SER1_DTR_RI_DSR_CD_MIXED
+#      define CONFIG_ETRAX_SER1_DTR_RI_DSR_CD_MIXED 1
+#    endif
+#   endif
+# if CONFIG_ETRAX_SER1_RI_ON_PA_BIT == -1
+#   ifndef CONFIG_ETRAX_SER1_DTR_RI_DSR_CD_MIXED
+#     define CONFIG_ETRAX_SER1_DTR_RI_DSR_CD_MIXED 1
+#   endif
+#  endif
+#  if CONFIG_ETRAX_SER1_DSR_ON_PA_BIT == -1
+#    ifndef CONFIG_ETRAX_SER1_DTR_RI_DSR_CD_MIXED
+#      define CONFIG_ETRAX_SER1_DTR_RI_DSR_CD_MIXED 1
+#    endif
+#  endif
+#  if CONFIG_ETRAX_SER1_CD_ON_PA_BIT == -1
+#    ifndef CONFIG_ETRAX_SER1_DTR_RI_DSR_CD_MIXED
+#      define CONFIG_ETRAX_SER1_DTR_RI_DSR_CD_MIXED 1
+#    endif
+#  endif
+#endif
+
+#define SER1_PB_BITSUM (CONFIG_ETRAX_SER1_DTR_ON_PB_BIT+CONFIG_ETRAX_SER1_RI_ON_PB_BIT+CONFIG_ETRAX_SER1_DSR_ON_PB_BIT+CONFIG_ETRAX_SER1_CD_ON_PB_BIT)
+
+#if SER1_PB_BITSUM != -4
+#  if CONFIG_ETRAX_SER1_DTR_ON_PB_BIT == -1
+#    ifndef CONFIG_ETRAX_SER1_DTR_RI_DSR_CD_MIXED
+#      define CONFIG_ETRAX_SER1_DTR_RI_DSR_CD_MIXED 1
+#    endif
+#   endif
+# if CONFIG_ETRAX_SER1_RI_ON_PB_BIT == -1
+#   ifndef CONFIG_ETRAX_SER1_DTR_RI_DSR_CD_MIXED
+#     define CONFIG_ETRAX_SER1_DTR_RI_DSR_CD_MIXED 1
+#   endif
+#  endif
+#  if CONFIG_ETRAX_SER1_DSR_ON_PB_BIT == -1
+#    ifndef CONFIG_ETRAX_SER1_DTR_RI_DSR_CD_MIXED
+#      define CONFIG_ETRAX_SER1_DTR_RI_DSR_CD_MIXED 1
+#    endif
+#  endif
+#  if CONFIG_ETRAX_SER1_CD_ON_PB_BIT == -1
+#    ifndef CONFIG_ETRAX_SER1_DTR_RI_DSR_CD_MIXED
+#      define CONFIG_ETRAX_SER1_DTR_RI_DSR_CD_MIXED 1
+#    endif
+#  endif
+#endif
+
+#endif /* PORT1 */
+
+#ifdef CONFIG_ETRAX_SERIAL_PORT2
+
+#define SER2_PA_BITSUM (CONFIG_ETRAX_SER2_DTR_ON_PA_BIT+CONFIG_ETRAX_SER2_RI_ON_PA_BIT+CONFIG_ETRAX_SER2_DSR_ON_PA_BIT+CONFIG_ETRAX_SER2_CD_ON_PA_BIT)
+
+#if SER2_PA_BITSUM != -4
+#  if CONFIG_ETRAX_SER2_DTR_ON_PA_BIT == -1
+#    ifndef CONFIG_ETRAX_SER2_DTR_RI_DSR_CD_MIXED
+#      define CONFIG_ETRAX_SER2_DTR_RI_DSR_CD_MIXED 1
+#    endif
+#   endif
+# if CONFIG_ETRAX_SER2_RI_ON_PA_BIT == -1
+#   ifndef CONFIG_ETRAX_SER2_DTR_RI_DSR_CD_MIXED
+#     define CONFIG_ETRAX_SER2_DTR_RI_DSR_CD_MIXED 1
+#   endif
+#  endif
+#  if CONFIG_ETRAX_SER2_DSR_ON_PA_BIT == -1
+#    ifndef CONFIG_ETRAX_SER2_DTR_RI_DSR_CD_MIXED
+#      define CONFIG_ETRAX_SER2_DTR_RI_DSR_CD_MIXED 1
+#    endif
+#  endif
+#  if CONFIG_ETRAX_SER2_CD_ON_PA_BIT == -1
+#    ifndef CONFIG_ETRAX_SER2_DTR_RI_DSR_CD_MIXED
+#      define CONFIG_ETRAX_SER2_DTR_RI_DSR_CD_MIXED 1
+#    endif
+#  endif
+#endif
+
+#define SER2_PB_BITSUM (CONFIG_ETRAX_SER2_DTR_ON_PB_BIT+CONFIG_ETRAX_SER2_RI_ON_PB_BIT+CONFIG_ETRAX_SER2_DSR_ON_PB_BIT+CONFIG_ETRAX_SER2_CD_ON_PB_BIT)
+
+#if SER2_PB_BITSUM != -4
+#  if CONFIG_ETRAX_SER2_DTR_ON_PB_BIT == -1
+#    ifndef CONFIG_ETRAX_SER2_DTR_RI_DSR_CD_MIXED
+#      define CONFIG_ETRAX_SER2_DTR_RI_DSR_CD_MIXED 1
+#    endif
+#   endif
+# if CONFIG_ETRAX_SER2_RI_ON_PB_BIT == -1
+#   ifndef CONFIG_ETRAX_SER2_DTR_RI_DSR_CD_MIXED
+#     define CONFIG_ETRAX_SER2_DTR_RI_DSR_CD_MIXED 1
+#   endif
+#  endif
+#  if CONFIG_ETRAX_SER2_DSR_ON_PB_BIT == -1
+#    ifndef CONFIG_ETRAX_SER2_DTR_RI_DSR_CD_MIXED
+#      define CONFIG_ETRAX_SER2_DTR_RI_DSR_CD_MIXED 1
+#    endif
+#  endif
+#  if CONFIG_ETRAX_SER2_CD_ON_PB_BIT == -1
+#    ifndef CONFIG_ETRAX_SER2_DTR_RI_DSR_CD_MIXED
+#      define CONFIG_ETRAX_SER2_DTR_RI_DSR_CD_MIXED 1
+#    endif
+#  endif
+#endif
+
+#endif /* PORT2 */
+
+#ifdef CONFIG_ETRAX_SERIAL_PORT3
+
+#define SER3_PA_BITSUM (CONFIG_ETRAX_SER3_DTR_ON_PA_BIT+CONFIG_ETRAX_SER3_RI_ON_PA_BIT+CONFIG_ETRAX_SER3_DSR_ON_PA_BIT+CONFIG_ETRAX_SER3_CD_ON_PA_BIT)
+
+#if SER3_PA_BITSUM != -4
+#  if CONFIG_ETRAX_SER3_DTR_ON_PA_BIT == -1
+#    ifndef CONFIG_ETRAX_SER3_DTR_RI_DSR_CD_MIXED
+#      define CONFIG_ETRAX_SER3_DTR_RI_DSR_CD_MIXED 1
+#    endif
+#   endif
+# if CONFIG_ETRAX_SER3_RI_ON_PA_BIT == -1
+#   ifndef CONFIG_ETRAX_SER3_DTR_RI_DSR_CD_MIXED
+#     define CONFIG_ETRAX_SER3_DTR_RI_DSR_CD_MIXED 1
+#   endif
+#  endif
+#  if CONFIG_ETRAX_SER3_DSR_ON_PA_BIT == -1
+#    ifndef CONFIG_ETRAX_SER3_DTR_RI_DSR_CD_MIXED
+#      define CONFIG_ETRAX_SER3_DTR_RI_DSR_CD_MIXED 1
+#    endif
+#  endif
+#  if CONFIG_ETRAX_SER3_CD_ON_PA_BIT == -1
+#    ifndef CONFIG_ETRAX_SER3_DTR_RI_DSR_CD_MIXED
+#      define CONFIG_ETRAX_SER3_DTR_RI_DSR_CD_MIXED 1
+#    endif
+#  endif
+#endif
+
+#define SER3_PB_BITSUM (CONFIG_ETRAX_SER3_DTR_ON_PB_BIT+CONFIG_ETRAX_SER3_RI_ON_PB_BIT+CONFIG_ETRAX_SER3_DSR_ON_PB_BIT+CONFIG_ETRAX_SER3_CD_ON_PB_BIT)
+
+#if SER3_PB_BITSUM != -4
+#  if CONFIG_ETRAX_SER3_DTR_ON_PB_BIT == -1
+#    ifndef CONFIG_ETRAX_SER3_DTR_RI_DSR_CD_MIXED
+#      define CONFIG_ETRAX_SER3_DTR_RI_DSR_CD_MIXED 1
+#    endif
+#   endif
+# if CONFIG_ETRAX_SER3_RI_ON_PB_BIT == -1
+#   ifndef CONFIG_ETRAX_SER3_DTR_RI_DSR_CD_MIXED
+#     define CONFIG_ETRAX_SER3_DTR_RI_DSR_CD_MIXED 1
+#   endif
+#  endif
+#  if CONFIG_ETRAX_SER3_DSR_ON_PB_BIT == -1
+#    ifndef CONFIG_ETRAX_SER3_DTR_RI_DSR_CD_MIXED
+#      define CONFIG_ETRAX_SER3_DTR_RI_DSR_CD_MIXED 1
+#    endif
+#  endif
+#  if CONFIG_ETRAX_SER3_CD_ON_PB_BIT == -1
+#    ifndef CONFIG_ETRAX_SER3_DTR_RI_DSR_CD_MIXED
+#      define CONFIG_ETRAX_SER3_DTR_RI_DSR_CD_MIXED 1
+#    endif
+#  endif
+#endif
+
+#endif /* PORT3 */
+
+
+#if defined(CONFIG_ETRAX_SER0_DTR_RI_DSR_CD_MIXED) || \
+    defined(CONFIG_ETRAX_SER1_DTR_RI_DSR_CD_MIXED) || \
+    defined(CONFIG_ETRAX_SER2_DTR_RI_DSR_CD_MIXED) || \
+    defined(CONFIG_ETRAX_SER3_DTR_RI_DSR_CD_MIXED)
+#define CONFIG_ETRAX_SERX_DTR_RI_DSR_CD_MIXED
+#endif
+
+#ifdef CONFIG_ETRAX_SERX_DTR_RI_DSR_CD_MIXED
+/* The pins can be mixed on PA and PB */
+#define CONTROL_PINS_PORT_NOT_USED(line) \
+  &dummy_ser[line], &dummy_ser[line], \
+  &dummy_ser[line], &dummy_ser[line], \
+  &dummy_ser[line], &dummy_ser[line], \
+  &dummy_ser[line], &dummy_ser[line], \
+  DUMMY_DTR_MASK, DUMMY_RI_MASK, DUMMY_DSR_MASK, DUMMY_CD_MASK
+
+
+struct control_pins
+{
+	volatile unsigned char *dtr_port;
+	unsigned char          *dtr_shadow;
+	volatile unsigned char *ri_port;
+	unsigned char          *ri_shadow;
+	volatile unsigned char *dsr_port;
+	unsigned char          *dsr_shadow;
+	volatile unsigned char *cd_port;
+	unsigned char          *cd_shadow;
+
+	unsigned char dtr_mask;
+	unsigned char ri_mask;
+	unsigned char dsr_mask;
+	unsigned char cd_mask;
+};
+
+static const struct control_pins e100_modem_pins[NR_PORTS] =
+{
+	/* Ser 0 */
+	{
+#ifdef CONFIG_ETRAX_SERIAL_PORT0
+	E100_STRUCT_PORT(0,DTR), E100_STRUCT_SHADOW(0,DTR),
+	E100_STRUCT_PORT(0,RI),  E100_STRUCT_SHADOW(0,RI),
+	E100_STRUCT_PORT(0,DSR), E100_STRUCT_SHADOW(0,DSR),
+	E100_STRUCT_PORT(0,CD),  E100_STRUCT_SHADOW(0,CD),
+	E100_STRUCT_MASK(0,DTR),
+	E100_STRUCT_MASK(0,RI),
+	E100_STRUCT_MASK(0,DSR),
+	E100_STRUCT_MASK(0,CD)
+#else
+	CONTROL_PINS_PORT_NOT_USED(0)
+#endif
+	},
+
+	/* Ser 1 */
+	{
+#ifdef CONFIG_ETRAX_SERIAL_PORT1
+	E100_STRUCT_PORT(1,DTR), E100_STRUCT_SHADOW(1,DTR),
+	E100_STRUCT_PORT(1,RI),  E100_STRUCT_SHADOW(1,RI),
+	E100_STRUCT_PORT(1,DSR), E100_STRUCT_SHADOW(1,DSR),
+	E100_STRUCT_PORT(1,CD),  E100_STRUCT_SHADOW(1,CD),
+	E100_STRUCT_MASK(1,DTR),
+	E100_STRUCT_MASK(1,RI),
+	E100_STRUCT_MASK(1,DSR),
+	E100_STRUCT_MASK(1,CD)
+#else
+	CONTROL_PINS_PORT_NOT_USED(1)
+#endif
+	},
+
+	/* Ser 2 */
+	{
+#ifdef CONFIG_ETRAX_SERIAL_PORT2
+	E100_STRUCT_PORT(2,DTR), E100_STRUCT_SHADOW(2,DTR),
+	E100_STRUCT_PORT(2,RI),  E100_STRUCT_SHADOW(2,RI),
+	E100_STRUCT_PORT(2,DSR), E100_STRUCT_SHADOW(2,DSR),
+	E100_STRUCT_PORT(2,CD),  E100_STRUCT_SHADOW(2,CD),
+	E100_STRUCT_MASK(2,DTR),
+	E100_STRUCT_MASK(2,RI),
+	E100_STRUCT_MASK(2,DSR),
+	E100_STRUCT_MASK(2,CD)
+#else
+	CONTROL_PINS_PORT_NOT_USED(2)
+#endif
+	},
+
+	/* Ser 3 */
+	{
+#ifdef CONFIG_ETRAX_SERIAL_PORT3
+	E100_STRUCT_PORT(3,DTR), E100_STRUCT_SHADOW(3,DTR),
+	E100_STRUCT_PORT(3,RI),  E100_STRUCT_SHADOW(3,RI),
+	E100_STRUCT_PORT(3,DSR), E100_STRUCT_SHADOW(3,DSR),
+	E100_STRUCT_PORT(3,CD),  E100_STRUCT_SHADOW(3,CD),
+	E100_STRUCT_MASK(3,DTR),
+	E100_STRUCT_MASK(3,RI),
+	E100_STRUCT_MASK(3,DSR),
+	E100_STRUCT_MASK(3,CD)
+#else
+	CONTROL_PINS_PORT_NOT_USED(3)
+#endif
+	}
+};
+#else  /* CONFIG_ETRAX_SERX_DTR_RI_DSR_CD_MIXED */
+
+/* All pins are on either PA or PB for each serial port */
+#define CONTROL_PINS_PORT_NOT_USED(line) \
+  &dummy_ser[line], &dummy_ser[line], \
+  DUMMY_DTR_MASK, DUMMY_RI_MASK, DUMMY_DSR_MASK, DUMMY_CD_MASK
+
+
+struct control_pins
+{
+	volatile unsigned char *port;
+	unsigned char          *shadow;
+
+	unsigned char dtr_mask;
+	unsigned char ri_mask;
+	unsigned char dsr_mask;
+	unsigned char cd_mask;
+};
+
+#define dtr_port port
+#define dtr_shadow shadow
+#define ri_port port
+#define ri_shadow shadow
+#define dsr_port port
+#define dsr_shadow shadow
+#define cd_port port
+#define cd_shadow shadow
+
+static const struct control_pins e100_modem_pins[NR_PORTS] =
+{
+	/* Ser 0 */
+	{
+#ifdef CONFIG_ETRAX_SERIAL_PORT0
+	E100_STRUCT_PORT(0,DTR), E100_STRUCT_SHADOW(0,DTR),
+	E100_STRUCT_MASK(0,DTR),
+	E100_STRUCT_MASK(0,RI),
+	E100_STRUCT_MASK(0,DSR),
+	E100_STRUCT_MASK(0,CD)
+#else
+	CONTROL_PINS_PORT_NOT_USED(0)
+#endif
+	},
+
+	/* Ser 1 */
+	{
+#ifdef CONFIG_ETRAX_SERIAL_PORT1
+	E100_STRUCT_PORT(1,DTR), E100_STRUCT_SHADOW(1,DTR),
+	E100_STRUCT_MASK(1,DTR),
+	E100_STRUCT_MASK(1,RI),
+	E100_STRUCT_MASK(1,DSR),
+	E100_STRUCT_MASK(1,CD)
+#else
+	CONTROL_PINS_PORT_NOT_USED(1)
+#endif
+	},
+
+	/* Ser 2 */
+	{
+#ifdef CONFIG_ETRAX_SERIAL_PORT2
+	E100_STRUCT_PORT(2,DTR), E100_STRUCT_SHADOW(2,DTR),
+	E100_STRUCT_MASK(2,DTR),
+	E100_STRUCT_MASK(2,RI),
+	E100_STRUCT_MASK(2,DSR),
+	E100_STRUCT_MASK(2,CD)
+#else
+	CONTROL_PINS_PORT_NOT_USED(2)
+#endif
+	},
+
+	/* Ser 3 */
+	{
+#ifdef CONFIG_ETRAX_SERIAL_PORT3
+	E100_STRUCT_PORT(3,DTR), E100_STRUCT_SHADOW(3,DTR),
+	E100_STRUCT_MASK(3,DTR),
+	E100_STRUCT_MASK(3,RI),
+	E100_STRUCT_MASK(3,DSR),
+	E100_STRUCT_MASK(3,CD)
+#else
+	CONTROL_PINS_PORT_NOT_USED(3)
+#endif
+	}
+};
+#endif /* !CONFIG_ETRAX_SERX_DTR_RI_DSR_CD_MIXED */
+
+#define E100_RTS_MASK 0x20
+#define E100_CTS_MASK 0x40
+
+/* All serial port signals are active low:
+ * active   = 0 -> 3.3V to RS-232 driver -> -12V on RS-232 level
+ * inactive = 1 -> 0V   to RS-232 driver -> +12V on RS-232 level
+ *
+ * These macros returns the pin value: 0=0V, >=1 = 3.3V on ETRAX chip
+ */
+
+/* Output */
+#define E100_RTS_GET(info) ((info)->rx_ctrl & E100_RTS_MASK)
+/* Input */
+#define E100_CTS_GET(info) ((info)->ioport[REG_STATUS] & E100_CTS_MASK)
+
+/* These are typically PA or PB and 0 means 0V, 1 means 3.3V */
+/* Is an output */
+#define E100_DTR_GET(info) ((*e100_modem_pins[(info)->line].dtr_shadow) & e100_modem_pins[(info)->line].dtr_mask)
+
+/* Normally inputs */
+#define E100_RI_GET(info) ((*e100_modem_pins[(info)->line].ri_port) & e100_modem_pins[(info)->line].ri_mask)
+#define E100_CD_GET(info) ((*e100_modem_pins[(info)->line].cd_port) & e100_modem_pins[(info)->line].cd_mask)
+
+/* Input */
+#define E100_DSR_GET(info) ((*e100_modem_pins[(info)->line].dsr_port) & e100_modem_pins[(info)->line].dsr_mask)
+
+
+/*
+ * tmp_buf is used as a temporary buffer by serial_write.  We need to
+ * lock it in case the memcpy_fromfs blocks while swapping in a page,
+ * and some other program tries to do a serial write at the same time.
+ * Since the lock will only come under contention when the system is
+ * swapping and available memory is low, it makes sense to share one
+ * buffer across all the serial ports, since it significantly saves
+ * memory if large numbers of serial ports are open.
+ */
+static unsigned char *tmp_buf;
+static DEFINE_MUTEX(tmp_buf_mutex);
+
+/* Calculate the chartime depending on baudrate, numbor of bits etc. */
+static void update_char_time(struct e100_serial * info)
+{
+	tcflag_t cflags = info->port.tty->termios->c_cflag;
+	int bits;
+
+	/* calc. number of bits / data byte */
+	/* databits + startbit and 1 stopbit */
+	if ((cflags & CSIZE) == CS7)
+		bits = 9;
+	else
+		bits = 10;
+
+	if (cflags & CSTOPB)     /* 2 stopbits ? */
+		bits++;
+
+	if (cflags & PARENB)     /* parity bit ? */
+		bits++;
+
+	/* calc timeout */
+	info->char_time_usec = ((bits * 1000000) / info->baud) + 1;
+	info->flush_time_usec = 4*info->char_time_usec;
+	if (info->flush_time_usec < MIN_FLUSH_TIME_USEC)
+		info->flush_time_usec = MIN_FLUSH_TIME_USEC;
+
+}
+
+/*
+ * This function maps from the Bxxxx defines in asm/termbits.h into real
+ * baud rates.
+ */
+
+static int
+cflag_to_baud(unsigned int cflag)
+{
+	static int baud_table[] = {
+		0, 50, 75, 110, 134, 150, 200, 300, 600, 1200, 1800, 2400,
+		4800, 9600, 19200, 38400 };
+
+	static int ext_baud_table[] = {
+		0, 57600, 115200, 230400, 460800, 921600, 1843200, 6250000,
+                0, 0, 0, 0, 0, 0, 0, 0 };
+
+	if (cflag & CBAUDEX)
+		return ext_baud_table[(cflag & CBAUD) & ~CBAUDEX];
+	else
+		return baud_table[cflag & CBAUD];
+}
+
+/* and this maps to an etrax100 hardware baud constant */
+
+static unsigned char
+cflag_to_etrax_baud(unsigned int cflag)
+{
+	char retval;
+
+	static char baud_table[] = {
+		-1, -1, -1, -1, -1, -1, -1, 0, 1, 2, -1, 3, 4, 5, 6, 7 };
+
+	static char ext_baud_table[] = {
+		-1, 8, 9, 10, 11, 12, 13, 14, -1, -1, -1, -1, -1, -1, -1, -1 };
+
+	if (cflag & CBAUDEX)
+		retval = ext_baud_table[(cflag & CBAUD) & ~CBAUDEX];
+	else
+		retval = baud_table[cflag & CBAUD];
+
+	if (retval < 0) {
+		printk(KERN_WARNING "serdriver tried setting invalid baud rate, flags %x.\n", cflag);
+		retval = 5; /* choose default 9600 instead */
+	}
+
+	return retval | (retval << 4); /* choose same for both TX and RX */
+}
+
+
+/* Various static support functions */
+
+/* Functions to set or clear DTR/RTS on the requested line */
+/* It is complicated by the fact that RTS is a serial port register, while
+ * DTR might not be implemented in the HW at all, and if it is, it can be on
+ * any general port.
+ */
+
+
+static inline void
+e100_dtr(struct e100_serial *info, int set)
+{
+#ifndef CONFIG_SVINTO_SIM
+	unsigned char mask = e100_modem_pins[info->line].dtr_mask;
+
+#ifdef SERIAL_DEBUG_IO
+	printk("ser%i dtr %i mask: 0x%02X\n", info->line, set, mask);
+	printk("ser%i shadow before 0x%02X get: %i\n",
+	       info->line, *e100_modem_pins[info->line].dtr_shadow,
+	       E100_DTR_GET(info));
+#endif
+	/* DTR is active low */
+	{
+		unsigned long flags;
+
+		local_irq_save(flags);
+		*e100_modem_pins[info->line].dtr_shadow &= ~mask;
+		*e100_modem_pins[info->line].dtr_shadow |= (set ? 0 : mask);
+		*e100_modem_pins[info->line].dtr_port = *e100_modem_pins[info->line].dtr_shadow;
+		local_irq_restore(flags);
+	}
+
+#ifdef SERIAL_DEBUG_IO
+	printk("ser%i shadow after 0x%02X get: %i\n",
+	       info->line, *e100_modem_pins[info->line].dtr_shadow,
+	       E100_DTR_GET(info));
+#endif
+#endif
+}
+
+/* set = 0 means 3.3V on the pin, bitvalue: 0=active, 1=inactive
+ *                                          0=0V    , 1=3.3V
+ */
+static inline void
+e100_rts(struct e100_serial *info, int set)
+{
+#ifndef CONFIG_SVINTO_SIM
+	unsigned long flags;
+	local_irq_save(flags);
+	info->rx_ctrl &= ~E100_RTS_MASK;
+	info->rx_ctrl |= (set ? 0 : E100_RTS_MASK);  /* RTS is active low */
+	info->ioport[REG_REC_CTRL] = info->rx_ctrl;
+	local_irq_restore(flags);
+#ifdef SERIAL_DEBUG_IO
+	printk("ser%i rts %i\n", info->line, set);
+#endif
+#endif
+}
+
+
+/* If this behaves as a modem, RI and CD is an output */
+static inline void
+e100_ri_out(struct e100_serial *info, int set)
+{
+#ifndef CONFIG_SVINTO_SIM
+	/* RI is active low */
+	{
+		unsigned char mask = e100_modem_pins[info->line].ri_mask;
+		unsigned long flags;
+
+		local_irq_save(flags);
+		*e100_modem_pins[info->line].ri_shadow &= ~mask;
+		*e100_modem_pins[info->line].ri_shadow |= (set ? 0 : mask);
+		*e100_modem_pins[info->line].ri_port = *e100_modem_pins[info->line].ri_shadow;
+		local_irq_restore(flags);
+	}
+#endif
+}
+static inline void
+e100_cd_out(struct e100_serial *info, int set)
+{
+#ifndef CONFIG_SVINTO_SIM
+	/* CD is active low */
+	{
+		unsigned char mask = e100_modem_pins[info->line].cd_mask;
+		unsigned long flags;
+
+		local_irq_save(flags);
+		*e100_modem_pins[info->line].cd_shadow &= ~mask;
+		*e100_modem_pins[info->line].cd_shadow |= (set ? 0 : mask);
+		*e100_modem_pins[info->line].cd_port = *e100_modem_pins[info->line].cd_shadow;
+		local_irq_restore(flags);
+	}
+#endif
+}
+
+static inline void
+e100_disable_rx(struct e100_serial *info)
+{
+#ifndef CONFIG_SVINTO_SIM
+	/* disable the receiver */
+	info->ioport[REG_REC_CTRL] =
+		(info->rx_ctrl &= ~IO_MASK(R_SERIAL0_REC_CTRL, rec_enable));
+#endif
+}
+
+static inline void
+e100_enable_rx(struct e100_serial *info)
+{
+#ifndef CONFIG_SVINTO_SIM
+	/* enable the receiver */
+	info->ioport[REG_REC_CTRL] =
+		(info->rx_ctrl |= IO_MASK(R_SERIAL0_REC_CTRL, rec_enable));
+#endif
+}
+
+/* the rx DMA uses both the dma_descr and the dma_eop interrupts */
+
+static inline void
+e100_disable_rxdma_irq(struct e100_serial *info)
+{
+#ifdef SERIAL_DEBUG_INTR
+	printk("rxdma_irq(%d): 0\n",info->line);
+#endif
+	DINTR1(DEBUG_LOG(info->line,"IRQ disable_rxdma_irq %i\n", info->line));
+	*R_IRQ_MASK2_CLR = (info->irq << 2) | (info->irq << 3);
+}
+
+static inline void
+e100_enable_rxdma_irq(struct e100_serial *info)
+{
+#ifdef SERIAL_DEBUG_INTR
+	printk("rxdma_irq(%d): 1\n",info->line);
+#endif
+	DINTR1(DEBUG_LOG(info->line,"IRQ enable_rxdma_irq %i\n", info->line));
+	*R_IRQ_MASK2_SET = (info->irq << 2) | (info->irq << 3);
+}
+
+/* the tx DMA uses only dma_descr interrupt */
+
+static void e100_disable_txdma_irq(struct e100_serial *info)
+{
+#ifdef SERIAL_DEBUG_INTR
+	printk("txdma_irq(%d): 0\n",info->line);
+#endif
+	DINTR1(DEBUG_LOG(info->line,"IRQ disable_txdma_irq %i\n", info->line));
+	*R_IRQ_MASK2_CLR = info->irq;
+}
+
+static void e100_enable_txdma_irq(struct e100_serial *info)
+{
+#ifdef SERIAL_DEBUG_INTR
+	printk("txdma_irq(%d): 1\n",info->line);
+#endif
+	DINTR1(DEBUG_LOG(info->line,"IRQ enable_txdma_irq %i\n", info->line));
+	*R_IRQ_MASK2_SET = info->irq;
+}
+
+static void e100_disable_txdma_channel(struct e100_serial *info)
+{
+	unsigned long flags;
+
+	/* Disable output DMA channel for the serial port in question
+	 * ( set to something other than serialX)
+	 */
+	local_irq_save(flags);
+	DFLOW(DEBUG_LOG(info->line, "disable_txdma_channel %i\n", info->line));
+	if (info->line == 0) {
+		if ((genconfig_shadow & IO_MASK(R_GEN_CONFIG, dma6)) ==
+		    IO_STATE(R_GEN_CONFIG, dma6, serial0)) {
+			genconfig_shadow &=  ~IO_MASK(R_GEN_CONFIG, dma6);
+			genconfig_shadow |= IO_STATE(R_GEN_CONFIG, dma6, unused);
+		}
+	} else if (info->line == 1) {
+		if ((genconfig_shadow & IO_MASK(R_GEN_CONFIG, dma8)) ==
+		    IO_STATE(R_GEN_CONFIG, dma8, serial1)) {
+			genconfig_shadow &=  ~IO_MASK(R_GEN_CONFIG, dma8);
+			genconfig_shadow |= IO_STATE(R_GEN_CONFIG, dma8, usb);
+		}
+	} else if (info->line == 2) {
+		if ((genconfig_shadow & IO_MASK(R_GEN_CONFIG, dma2)) ==
+		    IO_STATE(R_GEN_CONFIG, dma2, serial2)) {
+			genconfig_shadow &=  ~IO_MASK(R_GEN_CONFIG, dma2);
+			genconfig_shadow |= IO_STATE(R_GEN_CONFIG, dma2, par0);
+		}
+	} else if (info->line == 3) {
+		if ((genconfig_shadow & IO_MASK(R_GEN_CONFIG, dma4)) ==
+		    IO_STATE(R_GEN_CONFIG, dma4, serial3)) {
+			genconfig_shadow &=  ~IO_MASK(R_GEN_CONFIG, dma4);
+			genconfig_shadow |= IO_STATE(R_GEN_CONFIG, dma4, par1);
+		}
+	}
+	*R_GEN_CONFIG = genconfig_shadow;
+	local_irq_restore(flags);
+}
+
+
+static void e100_enable_txdma_channel(struct e100_serial *info)
+{
+	unsigned long flags;
+
+	local_irq_save(flags);
+	DFLOW(DEBUG_LOG(info->line, "enable_txdma_channel %i\n", info->line));
+	/* Enable output DMA channel for the serial port in question */
+	if (info->line == 0) {
+		genconfig_shadow &=  ~IO_MASK(R_GEN_CONFIG, dma6);
+		genconfig_shadow |= IO_STATE(R_GEN_CONFIG, dma6, serial0);
+	} else if (info->line == 1) {
+		genconfig_shadow &=  ~IO_MASK(R_GEN_CONFIG, dma8);
+		genconfig_shadow |= IO_STATE(R_GEN_CONFIG, dma8, serial1);
+	} else if (info->line == 2) {
+		genconfig_shadow &=  ~IO_MASK(R_GEN_CONFIG, dma2);
+		genconfig_shadow |= IO_STATE(R_GEN_CONFIG, dma2, serial2);
+	} else if (info->line == 3) {
+		genconfig_shadow &=  ~IO_MASK(R_GEN_CONFIG, dma4);
+		genconfig_shadow |= IO_STATE(R_GEN_CONFIG, dma4, serial3);
+	}
+	*R_GEN_CONFIG = genconfig_shadow;
+	local_irq_restore(flags);
+}
+
+static void e100_disable_rxdma_channel(struct e100_serial *info)
+{
+	unsigned long flags;
+
+	/* Disable input DMA channel for the serial port in question
+	 * ( set to something other than serialX)
+	 */
+	local_irq_save(flags);
+	if (info->line == 0) {
+		if ((genconfig_shadow & IO_MASK(R_GEN_CONFIG, dma7)) ==
+		    IO_STATE(R_GEN_CONFIG, dma7, serial0)) {
+			genconfig_shadow &=  ~IO_MASK(R_GEN_CONFIG, dma7);
+			genconfig_shadow |= IO_STATE(R_GEN_CONFIG, dma7, unused);
+		}
+	} else if (info->line == 1) {
+		if ((genconfig_shadow & IO_MASK(R_GEN_CONFIG, dma9)) ==
+		    IO_STATE(R_GEN_CONFIG, dma9, serial1)) {
+			genconfig_shadow &=  ~IO_MASK(R_GEN_CONFIG, dma9);
+			genconfig_shadow |= IO_STATE(R_GEN_CONFIG, dma9, usb);
+		}
+	} else if (info->line == 2) {
+		if ((genconfig_shadow & IO_MASK(R_GEN_CONFIG, dma3)) ==
+		    IO_STATE(R_GEN_CONFIG, dma3, serial2)) {
+			genconfig_shadow &=  ~IO_MASK(R_GEN_CONFIG, dma3);
+			genconfig_shadow |= IO_STATE(R_GEN_CONFIG, dma3, par0);
+		}
+	} else if (info->line == 3) {
+		if ((genconfig_shadow & IO_MASK(R_GEN_CONFIG, dma5)) ==
+		    IO_STATE(R_GEN_CONFIG, dma5, serial3)) {
+			genconfig_shadow &=  ~IO_MASK(R_GEN_CONFIG, dma5);
+			genconfig_shadow |= IO_STATE(R_GEN_CONFIG, dma5, par1);
+		}
+	}
+	*R_GEN_CONFIG = genconfig_shadow;
+	local_irq_restore(flags);
+}
+
+
+static void e100_enable_rxdma_channel(struct e100_serial *info)
+{
+	unsigned long flags;
+
+	local_irq_save(flags);
+	/* Enable input DMA channel for the serial port in question */
+	if (info->line == 0) {
+		genconfig_shadow &=  ~IO_MASK(R_GEN_CONFIG, dma7);
+		genconfig_shadow |= IO_STATE(R_GEN_CONFIG, dma7, serial0);
+	} else if (info->line == 1) {
+		genconfig_shadow &=  ~IO_MASK(R_GEN_CONFIG, dma9);
+		genconfig_shadow |= IO_STATE(R_GEN_CONFIG, dma9, serial1);
+	} else if (info->line == 2) {
+		genconfig_shadow &=  ~IO_MASK(R_GEN_CONFIG, dma3);
+		genconfig_shadow |= IO_STATE(R_GEN_CONFIG, dma3, serial2);
+	} else if (info->line == 3) {
+		genconfig_shadow &=  ~IO_MASK(R_GEN_CONFIG, dma5);
+		genconfig_shadow |= IO_STATE(R_GEN_CONFIG, dma5, serial3);
+	}
+	*R_GEN_CONFIG = genconfig_shadow;
+	local_irq_restore(flags);
+}
+
+#ifdef SERIAL_HANDLE_EARLY_ERRORS
+/* in order to detect and fix errors on the first byte
+   we have to use the serial interrupts as well. */
+
+static inline void
+e100_disable_serial_data_irq(struct e100_serial *info)
+{
+#ifdef SERIAL_DEBUG_INTR
+	printk("ser_irq(%d): 0\n",info->line);
+#endif
+	DINTR1(DEBUG_LOG(info->line,"IRQ disable data_irq %i\n", info->line));
+	*R_IRQ_MASK1_CLR = (1U << (8+2*info->line));
+}
+
+static inline void
+e100_enable_serial_data_irq(struct e100_serial *info)
+{
+#ifdef SERIAL_DEBUG_INTR
+	printk("ser_irq(%d): 1\n",info->line);
+	printk("**** %d = %d\n",
+	       (8+2*info->line),
+	       (1U << (8+2*info->line)));
+#endif
+	DINTR1(DEBUG_LOG(info->line,"IRQ enable data_irq %i\n", info->line));
+	*R_IRQ_MASK1_SET = (1U << (8+2*info->line));
+}
+#endif
+
+static inline void
+e100_disable_serial_tx_ready_irq(struct e100_serial *info)
+{
+#ifdef SERIAL_DEBUG_INTR
+	printk("ser_tx_irq(%d): 0\n",info->line);
+#endif
+	DINTR1(DEBUG_LOG(info->line,"IRQ disable ready_irq %i\n", info->line));
+	*R_IRQ_MASK1_CLR = (1U << (8+1+2*info->line));
+}
+
+static inline void
+e100_enable_serial_tx_ready_irq(struct e100_serial *info)
+{
+#ifdef SERIAL_DEBUG_INTR
+	printk("ser_tx_irq(%d): 1\n",info->line);
+	printk("**** %d = %d\n",
+	       (8+1+2*info->line),
+	       (1U << (8+1+2*info->line)));
+#endif
+	DINTR2(DEBUG_LOG(info->line,"IRQ enable ready_irq %i\n", info->line));
+	*R_IRQ_MASK1_SET = (1U << (8+1+2*info->line));
+}
+
+static inline void e100_enable_rx_irq(struct e100_serial *info)
+{
+	if (info->uses_dma_in)
+		e100_enable_rxdma_irq(info);
+	else
+		e100_enable_serial_data_irq(info);
+}
+static inline void e100_disable_rx_irq(struct e100_serial *info)
+{
+	if (info->uses_dma_in)
+		e100_disable_rxdma_irq(info);
+	else
+		e100_disable_serial_data_irq(info);
+}
+
+#if defined(CONFIG_ETRAX_RS485)
+/* Enable RS-485 mode on selected port. This is UGLY. */
+static int
+e100_enable_rs485(struct tty_struct *tty, struct serial_rs485 *r)
+{
+	struct e100_serial * info = (struct e100_serial *)tty->driver_data;
+
+#if defined(CONFIG_ETRAX_RS485_ON_PA)
+	*R_PORT_PA_DATA = port_pa_data_shadow |= (1 << rs485_pa_bit);
+#endif
+#if defined(CONFIG_ETRAX_RS485_ON_PORT_G)
+	REG_SHADOW_SET(R_PORT_G_DATA,  port_g_data_shadow,
+		       rs485_port_g_bit, 1);
+#endif
+#if defined(CONFIG_ETRAX_RS485_LTC1387)
+	REG_SHADOW_SET(R_PORT_G_DATA, port_g_data_shadow,
+		       CONFIG_ETRAX_RS485_LTC1387_DXEN_PORT_G_BIT, 1);
+	REG_SHADOW_SET(R_PORT_G_DATA, port_g_data_shadow,
+		       CONFIG_ETRAX_RS485_LTC1387_RXEN_PORT_G_BIT, 1);
+#endif
+
+	info->rs485 = *r;
+
+	/* Maximum delay before RTS equal to 1000 */
+	if (info->rs485.delay_rts_before_send >= 1000)
+		info->rs485.delay_rts_before_send = 1000;
+
+/*	printk("rts: on send = %i, after = %i, enabled = %i",
+		    info->rs485.rts_on_send,
+		    info->rs485.rts_after_sent,
+		    info->rs485.enabled
+	);
+*/
+	return 0;
+}
+
+static int
+e100_write_rs485(struct tty_struct *tty,
+                 const unsigned char *buf, int count)
+{
+	struct e100_serial * info = (struct e100_serial *)tty->driver_data;
+	int old_value = (info->rs485.flags) & SER_RS485_ENABLED;
+
+	/* rs485 is always implicitly enabled if we're using the ioctl()
+	 * but it doesn't have to be set in the serial_rs485
+	 * (to be backward compatible with old apps)
+	 * So we store, set and restore it.
+	 */
+	info->rs485.flags |= SER_RS485_ENABLED;
+	/* rs_write now deals with RS485 if enabled */
+	count = rs_write(tty, buf, count);
+	if (!old_value)
+		info->rs485.flags &= ~(SER_RS485_ENABLED);
+	return count;
+}
+
+#ifdef CONFIG_ETRAX_FAST_TIMER
+/* Timer function to toggle RTS when using FAST_TIMER */
+static void rs485_toggle_rts_timer_function(unsigned long data)
+{
+	struct e100_serial *info = (struct e100_serial *)data;
+
+	fast_timers_rs485[info->line].function = NULL;
+	e100_rts(info, (info->rs485.flags & SER_RS485_RTS_AFTER_SEND));
+#if defined(CONFIG_ETRAX_RS485_DISABLE_RECEIVER)
+	e100_enable_rx(info);
+	e100_enable_rx_irq(info);
+#endif
+}
+#endif
+#endif /* CONFIG_ETRAX_RS485 */
+
+/*
+ * ------------------------------------------------------------
+ * rs_stop() and rs_start()
+ *
+ * This routines are called before setting or resetting tty->stopped.
+ * They enable or disable transmitter using the XOFF registers, as necessary.
+ * ------------------------------------------------------------
+ */
+
+static void
+rs_stop(struct tty_struct *tty)
+{
+	struct e100_serial *info = (struct e100_serial *)tty->driver_data;
+	if (info) {
+		unsigned long flags;
+		unsigned long xoff;
+
+		local_irq_save(flags);
+		DFLOW(DEBUG_LOG(info->line, "XOFF rs_stop xmit %i\n",
+				CIRC_CNT(info->xmit.head,
+					 info->xmit.tail,SERIAL_XMIT_SIZE)));
+
+		xoff = IO_FIELD(R_SERIAL0_XOFF, xoff_char,
+				STOP_CHAR(info->port.tty));
+		xoff |= IO_STATE(R_SERIAL0_XOFF, tx_stop, stop);
+		if (tty->termios->c_iflag & IXON ) {
+			xoff |= IO_STATE(R_SERIAL0_XOFF, auto_xoff, enable);
+		}
+
+		*((unsigned long *)&info->ioport[REG_XOFF]) = xoff;
+		local_irq_restore(flags);
+	}
+}
+
+static void
+rs_start(struct tty_struct *tty)
+{
+	struct e100_serial *info = (struct e100_serial *)tty->driver_data;
+	if (info) {
+		unsigned long flags;
+		unsigned long xoff;
+
+		local_irq_save(flags);
+		DFLOW(DEBUG_LOG(info->line, "XOFF rs_start xmit %i\n",
+				CIRC_CNT(info->xmit.head,
+					 info->xmit.tail,SERIAL_XMIT_SIZE)));
+		xoff = IO_FIELD(R_SERIAL0_XOFF, xoff_char, STOP_CHAR(tty));
+		xoff |= IO_STATE(R_SERIAL0_XOFF, tx_stop, enable);
+		if (tty->termios->c_iflag & IXON ) {
+			xoff |= IO_STATE(R_SERIAL0_XOFF, auto_xoff, enable);
+		}
+
+		*((unsigned long *)&info->ioport[REG_XOFF]) = xoff;
+		if (!info->uses_dma_out &&
+		    info->xmit.head != info->xmit.tail && info->xmit.buf)
+			e100_enable_serial_tx_ready_irq(info);
+
+		local_irq_restore(flags);
+	}
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * Here starts the interrupt handling routines.  All of the following
+ * subroutines are declared as inline and are folded into
+ * rs_interrupt().  They were separated out for readability's sake.
+ *
+ * Note: rs_interrupt() is a "fast" interrupt, which means that it
+ * runs with interrupts turned off.  People who may want to modify
+ * rs_interrupt() should try to keep the interrupt handler as fast as
+ * possible.  After you are done making modifications, it is not a bad
+ * idea to do:
+ *
+ * gcc -S -DKERNEL -Wall -Wstrict-prototypes -O6 -fomit-frame-pointer serial.c
+ *
+ * and look at the resulting assemble code in serial.s.
+ *
+ * 				- Ted Ts'o (tytso@mit.edu), 7-Mar-93
+ * -----------------------------------------------------------------------
+ */
+
+/*
+ * This routine is used by the interrupt handler to schedule
+ * processing in the software interrupt portion of the driver.
+ */
+static void rs_sched_event(struct e100_serial *info, int event)
+{
+	if (info->event & (1 << event))
+		return;
+	info->event |= 1 << event;
+	schedule_work(&info->work);
+}
+
+/* The output DMA channel is free - use it to send as many chars as possible
+ * NOTES:
+ *   We don't pay attention to info->x_char, which means if the TTY wants to
+ *   use XON/XOFF it will set info->x_char but we won't send any X char!
+ *
+ *   To implement this, we'd just start a DMA send of 1 byte pointing at a
+ *   buffer containing the X char, and skip updating xmit. We'd also have to
+ *   check if the last sent char was the X char when we enter this function
+ *   the next time, to avoid updating xmit with the sent X value.
+ */
+
+static void
+transmit_chars_dma(struct e100_serial *info)
+{
+	unsigned int c, sentl;
+	struct etrax_dma_descr *descr;
+
+#ifdef CONFIG_SVINTO_SIM
+	/* This will output too little if tail is not 0 always since
+	 * we don't reloop to send the other part. Anyway this SHOULD be a
+	 * no-op - transmit_chars_dma would never really be called during sim
+	 * since rs_write does not write into the xmit buffer then.
+	 */
+	if (info->xmit.tail)
+		printk("Error in serial.c:transmit_chars-dma(), tail!=0\n");
+	if (info->xmit.head != info->xmit.tail) {
+		SIMCOUT(info->xmit.buf + info->xmit.tail,
+			CIRC_CNT(info->xmit.head,
+				 info->xmit.tail,
+				 SERIAL_XMIT_SIZE));
+		info->xmit.head = info->xmit.tail;  /* move back head */
+		info->tr_running = 0;
+	}
+	return;
+#endif
+	/* acknowledge both dma_descr and dma_eop irq in R_DMA_CHx_CLR_INTR */
+	*info->oclrintradr =
+		IO_STATE(R_DMA_CH6_CLR_INTR, clr_descr, do) |
+		IO_STATE(R_DMA_CH6_CLR_INTR, clr_eop, do);
+
+#ifdef SERIAL_DEBUG_INTR
+	if (info->line == SERIAL_DEBUG_LINE)
+		printk("tc\n");
+#endif
+	if (!info->tr_running) {
+		/* weirdo... we shouldn't get here! */
+		printk(KERN_WARNING "Achtung: transmit_chars_dma with !tr_running\n");
+		return;
+	}
+
+	descr = &info->tr_descr;
+
+	/* first get the amount of bytes sent during the last DMA transfer,
+	   and update xmit accordingly */
+
+	/* if the stop bit was not set, all data has been sent */
+	if (!(descr->status & d_stop)) {
+		sentl = descr->sw_len;
+	} else
+		/* otherwise we find the amount of data sent here */
+		sentl = descr->hw_len;
+
+	DFLOW(DEBUG_LOG(info->line, "TX %i done\n", sentl));
+
+	/* update stats */
+	info->icount.tx += sentl;
+
+	/* update xmit buffer */
+	info->xmit.tail = (info->xmit.tail + sentl) & (SERIAL_XMIT_SIZE - 1);
+
+	/* if there is only a few chars left in the buf, wake up the blocked
+	   write if any */
+	if (CIRC_CNT(info->xmit.head,
+		     info->xmit.tail,
+		     SERIAL_XMIT_SIZE) < WAKEUP_CHARS)
+		rs_sched_event(info, RS_EVENT_WRITE_WAKEUP);
+
+	/* find out the largest amount of consecutive bytes we want to send now */
+
+	c = CIRC_CNT_TO_END(info->xmit.head, info->xmit.tail, SERIAL_XMIT_SIZE);
+
+	/* Don't send all in one DMA transfer - divide it so we wake up
+	 * application before all is sent
+	 */
+
+	if (c >= 4*WAKEUP_CHARS)
+		c = c/2;
+
+	if (c <= 0) {
+		/* our job here is done, don't schedule any new DMA transfer */
+		info->tr_running = 0;
+
+#if defined(CONFIG_ETRAX_RS485) && defined(CONFIG_ETRAX_FAST_TIMER)
+		if (info->rs485.flags & SER_RS485_ENABLED) {
+			/* Set a short timer to toggle RTS */
+			start_one_shot_timer(&fast_timers_rs485[info->line],
+			                     rs485_toggle_rts_timer_function,
+			                     (unsigned long)info,
+			                     info->char_time_usec*2,
+			                     "RS-485");
+		}
+#endif /* RS485 */
+		return;
+	}
+
+	/* ok we can schedule a dma send of c chars starting at info->xmit.tail */
+	/* set up the descriptor correctly for output */
+	DFLOW(DEBUG_LOG(info->line, "TX %i\n", c));
+	descr->ctrl = d_int | d_eol | d_wait; /* Wait needed for tty_wait_until_sent() */
+	descr->sw_len = c;
+	descr->buf = virt_to_phys(info->xmit.buf + info->xmit.tail);
+	descr->status = 0;
+
+	*info->ofirstadr = virt_to_phys(descr); /* write to R_DMAx_FIRST */
+	*info->ocmdadr = IO_STATE(R_DMA_CH6_CMD, cmd, start);
+
+	/* DMA is now running (hopefully) */
+} /* transmit_chars_dma */
+
+static void
+start_transmit(struct e100_serial *info)
+{
+#if 0
+	if (info->line == SERIAL_DEBUG_LINE)
+		printk("x\n");
+#endif
+
+	info->tr_descr.sw_len = 0;
+	info->tr_descr.hw_len = 0;
+	info->tr_descr.status = 0;
+	info->tr_running = 1;
+	if (info->uses_dma_out)
+		transmit_chars_dma(info);
+	else
+		e100_enable_serial_tx_ready_irq(info);
+} /* start_transmit */
+
+#ifdef CONFIG_ETRAX_SERIAL_FAST_TIMER
+static int serial_fast_timer_started = 0;
+static int serial_fast_timer_expired = 0;
+static void flush_timeout_function(unsigned long data);
+#define START_FLUSH_FAST_TIMER_TIME(info, string, usec) {\
+  unsigned long timer_flags; \
+  local_irq_save(timer_flags); \
+  if (fast_timers[info->line].function == NULL) { \
+    serial_fast_timer_started++; \
+    TIMERD(DEBUG_LOG(info->line, "start_timer %i ", info->line)); \
+    TIMERD(DEBUG_LOG(info->line, "num started: %i\n", serial_fast_timer_started)); \
+    start_one_shot_timer(&fast_timers[info->line], \
+                         flush_timeout_function, \
+                         (unsigned long)info, \
+                         (usec), \
+                         string); \
+  } \
+  else { \
+    TIMERD(DEBUG_LOG(info->line, "timer %i already running\n", info->line)); \
+  } \
+  local_irq_restore(timer_flags); \
+}
+#define START_FLUSH_FAST_TIMER(info, string) START_FLUSH_FAST_TIMER_TIME(info, string, info->flush_time_usec)
+
+#else
+#define START_FLUSH_FAST_TIMER_TIME(info, string, usec)
+#define START_FLUSH_FAST_TIMER(info, string)
+#endif
+
+static struct etrax_recv_buffer *
+alloc_recv_buffer(unsigned int size)
+{
+	struct etrax_recv_buffer *buffer;
+
+	if (!(buffer = kmalloc(sizeof *buffer + size, GFP_ATOMIC)))
+		return NULL;
+
+	buffer->next = NULL;
+	buffer->length = 0;
+	buffer->error = TTY_NORMAL;
+
+	return buffer;
+}
+
+static void
+append_recv_buffer(struct e100_serial *info, struct etrax_recv_buffer *buffer)
+{
+	unsigned long flags;
+
+	local_irq_save(flags);
+
+	if (!info->first_recv_buffer)
+		info->first_recv_buffer = buffer;
+	else
+		info->last_recv_buffer->next = buffer;
+
+	info->last_recv_buffer = buffer;
+
+	info->recv_cnt += buffer->length;
+	if (info->recv_cnt > info->max_recv_cnt)
+		info->max_recv_cnt = info->recv_cnt;
+
+	local_irq_restore(flags);
+}
+
+static int
+add_char_and_flag(struct e100_serial *info, unsigned char data, unsigned char flag)
+{
+	struct etrax_recv_buffer *buffer;
+	if (info->uses_dma_in) {
+		if (!(buffer = alloc_recv_buffer(4)))
+			return 0;
+
+		buffer->length = 1;
+		buffer->error = flag;
+		buffer->buffer[0] = data;
+
+		append_recv_buffer(info, buffer);
+
+		info->icount.rx++;
+	} else {
+		struct tty_struct *tty = info->port.tty;
+		tty_insert_flip_char(tty, data, flag);
+		info->icount.rx++;
+	}
+
+	return 1;
+}
+
+static unsigned int handle_descr_data(struct e100_serial *info,
+				      struct etrax_dma_descr *descr,
+				      unsigned int recvl)
+{
+	struct etrax_recv_buffer *buffer = phys_to_virt(descr->buf) - sizeof *buffer;
+
+	if (info->recv_cnt + recvl > 65536) {
+		printk(KERN_WARNING
+		       "%s: Too much pending incoming serial data! Dropping %u bytes.\n", __func__, recvl);
+		return 0;
+	}
+
+	buffer->length = recvl;
+
+	if (info->errorcode == ERRCODE_SET_BREAK)
+		buffer->error = TTY_BREAK;
+	info->errorcode = 0;
+
+	append_recv_buffer(info, buffer);
+
+	if (!(buffer = alloc_recv_buffer(SERIAL_DESCR_BUF_SIZE)))
+		panic("%s: Failed to allocate memory for receive buffer!\n", __func__);
+
+	descr->buf = virt_to_phys(buffer->buffer);
+
+	return recvl;
+}
+
+static unsigned int handle_all_descr_data(struct e100_serial *info)
+{
+	struct etrax_dma_descr *descr;
+	unsigned int recvl;
+	unsigned int ret = 0;
+
+	while (1)
+	{
+		descr = &info->rec_descr[info->cur_rec_descr];
+
+		if (descr == phys_to_virt(*info->idescradr))
+			break;
+
+		if (++info->cur_rec_descr == SERIAL_RECV_DESCRIPTORS)
+			info->cur_rec_descr = 0;
+
+		/* find out how many bytes were read */
+
+		/* if the eop bit was not set, all data has been received */
+		if (!(descr->status & d_eop)) {
+			recvl = descr->sw_len;
+		} else {
+			/* otherwise we find the amount of data received here */
+			recvl = descr->hw_len;
+		}
+
+		/* Reset the status information */
+		descr->status = 0;
+
+		DFLOW(  DEBUG_LOG(info->line, "RX %lu\n", recvl);
+			if (info->port.tty->stopped) {
+				unsigned char *buf = phys_to_virt(descr->buf);
+				DEBUG_LOG(info->line, "rx 0x%02X\n", buf[0]);
+				DEBUG_LOG(info->line, "rx 0x%02X\n", buf[1]);
+				DEBUG_LOG(info->line, "rx 0x%02X\n", buf[2]);
+			}
+			);
+
+		/* update stats */
+		info->icount.rx += recvl;
+
+		ret += handle_descr_data(info, descr, recvl);
+	}
+
+	return ret;
+}
+
+static void receive_chars_dma(struct e100_serial *info)
+{
+	struct tty_struct *tty;
+	unsigned char rstat;
+
+#ifdef CONFIG_SVINTO_SIM
+	/* No receive in the simulator.  Will probably be when the rest of
+	 * the serial interface works, and this piece will just be removed.
+	 */
+	return;
+#endif
+
+	/* Acknowledge both dma_descr and dma_eop irq in R_DMA_CHx_CLR_INTR */
+	*info->iclrintradr =
+		IO_STATE(R_DMA_CH6_CLR_INTR, clr_descr, do) |
+		IO_STATE(R_DMA_CH6_CLR_INTR, clr_eop, do);
+
+	tty = info->port.tty;
+	if (!tty) /* Something wrong... */
+		return;
+
+#ifdef SERIAL_HANDLE_EARLY_ERRORS
+	if (info->uses_dma_in)
+		e100_enable_serial_data_irq(info);
+#endif
+
+	if (info->errorcode == ERRCODE_INSERT_BREAK)
+		add_char_and_flag(info, '\0', TTY_BREAK);
+
+	handle_all_descr_data(info);
+
+	/* Read the status register to detect errors */
+	rstat = info->ioport[REG_STATUS];
+	if (rstat & IO_MASK(R_SERIAL0_STATUS, xoff_detect) ) {
+		DFLOW(DEBUG_LOG(info->line, "XOFF detect stat %x\n", rstat));
+	}
+
+	if (rstat & SER_ERROR_MASK) {
+		/* If we got an error, we must reset it by reading the
+		 * data_in field
+		 */
+		unsigned char data = info->ioport[REG_DATA];
+
+		PROCSTAT(ser_stat[info->line].errors_cnt++);
+		DEBUG_LOG(info->line, "#dERR: s d 0x%04X\n",
+			  ((rstat & SER_ERROR_MASK) << 8) | data);
+
+		if (rstat & SER_PAR_ERR_MASK)
+			add_char_and_flag(info, data, TTY_PARITY);
+		else if (rstat & SER_OVERRUN_MASK)
+			add_char_and_flag(info, data, TTY_OVERRUN);
+		else if (rstat & SER_FRAMING_ERR_MASK)
+			add_char_and_flag(info, data, TTY_FRAME);
+	}
+
+	START_FLUSH_FAST_TIMER(info, "receive_chars");
+
+	/* Restart the receiving DMA */
+	*info->icmdadr = IO_STATE(R_DMA_CH6_CMD, cmd, restart);
+}
+
+static int start_recv_dma(struct e100_serial *info)
+{
+	struct etrax_dma_descr *descr = info->rec_descr;
+	struct etrax_recv_buffer *buffer;
+        int i;
+
+	/* Set up the receiving descriptors */
+	for (i = 0; i < SERIAL_RECV_DESCRIPTORS; i++) {
+		if (!(buffer = alloc_recv_buffer(SERIAL_DESCR_BUF_SIZE)))
+			panic("%s: Failed to allocate memory for receive buffer!\n", __func__);
+
+		descr[i].ctrl = d_int;
+		descr[i].buf = virt_to_phys(buffer->buffer);
+		descr[i].sw_len = SERIAL_DESCR_BUF_SIZE;
+		descr[i].hw_len = 0;
+		descr[i].status = 0;
+		descr[i].next = virt_to_phys(&descr[i+1]);
+	}
+
+	/* Link the last descriptor to the first */
+	descr[i-1].next = virt_to_phys(&descr[0]);
+
+	/* Start with the first descriptor in the list */
+	info->cur_rec_descr = 0;
+
+	/* Start the DMA */
+	*info->ifirstadr = virt_to_phys(&descr[info->cur_rec_descr]);
+	*info->icmdadr = IO_STATE(R_DMA_CH6_CMD, cmd, start);
+
+	/* Input DMA should be running now */
+	return 1;
+}
+
+static void
+start_receive(struct e100_serial *info)
+{
+#ifdef CONFIG_SVINTO_SIM
+	/* No receive in the simulator.  Will probably be when the rest of
+	 * the serial interface works, and this piece will just be removed.
+	 */
+	return;
+#endif
+	if (info->uses_dma_in) {
+		/* reset the input dma channel to be sure it works */
+
+		*info->icmdadr = IO_STATE(R_DMA_CH6_CMD, cmd, reset);
+		while (IO_EXTRACT(R_DMA_CH6_CMD, cmd, *info->icmdadr) ==
+		       IO_STATE_VALUE(R_DMA_CH6_CMD, cmd, reset));
+
+		start_recv_dma(info);
+	}
+}
+
+
+/* the bits in the MASK2 register are laid out like this:
+   DMAI_EOP DMAI_DESCR DMAO_EOP DMAO_DESCR
+   where I is the input channel and O is the output channel for the port.
+   info->irq is the bit number for the DMAO_DESCR so to check the others we
+   shift info->irq to the left.
+*/
+
+/* dma output channel interrupt handler
+   this interrupt is called from DMA2(ser2), DMA4(ser3), DMA6(ser0) or
+   DMA8(ser1) when they have finished a descriptor with the intr flag set.
+*/
+
+static irqreturn_t
+tr_interrupt(int irq, void *dev_id)
+{
+	struct e100_serial *info;
+	unsigned long ireg;
+	int i;
+	int handled = 0;
+
+#ifdef CONFIG_SVINTO_SIM
+	/* No receive in the simulator.  Will probably be when the rest of
+	 * the serial interface works, and this piece will just be removed.
+	 */
+	{
+		const char *s = "What? tr_interrupt in simulator??\n";
+		SIMCOUT(s,strlen(s));
+	}
+	return IRQ_HANDLED;
+#endif
+
+	/* find out the line that caused this irq and get it from rs_table */
+
+	ireg = *R_IRQ_MASK2_RD;  /* get the active irq bits for the dma channels */
+
+	for (i = 0; i < NR_PORTS; i++) {
+		info = rs_table + i;
+		if (!info->enabled || !info->uses_dma_out)
+			continue;
+		/* check for dma_descr (don't need to check for dma_eop in output dma for serial */
+		if (ireg & info->irq) {
+			handled = 1;
+			/* we can send a new dma bunch. make it so. */
+			DINTR2(DEBUG_LOG(info->line, "tr_interrupt %i\n", i));
+			/* Read jiffies_usec first,
+			 * we want this time to be as late as possible
+			 */
+ 			PROCSTAT(ser_stat[info->line].tx_dma_ints++);
+			info->last_tx_active_usec = GET_JIFFIES_USEC();
+			info->last_tx_active = jiffies;
+			transmit_chars_dma(info);
+		}
+
+		/* FIXME: here we should really check for a change in the
+		   status lines and if so call status_handle(info) */
+	}
+	return IRQ_RETVAL(handled);
+} /* tr_interrupt */
+
+/* dma input channel interrupt handler */
+
+static irqreturn_t
+rec_interrupt(int irq, void *dev_id)
+{
+	struct e100_serial *info;
+	unsigned long ireg;
+	int i;
+	int handled = 0;
+
+#ifdef CONFIG_SVINTO_SIM
+	/* No receive in the simulator.  Will probably be when the rest of
+	 * the serial interface works, and this piece will just be removed.
+	 */
+	{
+		const char *s = "What? rec_interrupt in simulator??\n";
+		SIMCOUT(s,strlen(s));
+	}
+	return IRQ_HANDLED;
+#endif
+
+	/* find out the line that caused this irq and get it from rs_table */
+
+	ireg = *R_IRQ_MASK2_RD;  /* get the active irq bits for the dma channels */
+
+	for (i = 0; i < NR_PORTS; i++) {
+		info = rs_table + i;
+		if (!info->enabled || !info->uses_dma_in)
+			continue;
+		/* check for both dma_eop and dma_descr for the input dma channel */
+		if (ireg & ((info->irq << 2) | (info->irq << 3))) {
+			handled = 1;
+			/* we have received something */
+			receive_chars_dma(info);
+		}
+
+		/* FIXME: here we should really check for a change in the
+		   status lines and if so call status_handle(info) */
+	}
+	return IRQ_RETVAL(handled);
+} /* rec_interrupt */
+
+static int force_eop_if_needed(struct e100_serial *info)
+{
+	/* We check data_avail bit to determine if data has
+	 * arrived since last time
+	 */
+	unsigned char rstat = info->ioport[REG_STATUS];
+
+	/* error or datavail? */
+	if (rstat & SER_ERROR_MASK) {
+		/* Some error has occurred. If there has been valid data, an
+		 * EOP interrupt will be made automatically. If no data, the
+		 * normal ser_interrupt should be enabled and handle it.
+		 * So do nothing!
+		 */
+		DEBUG_LOG(info->line, "timeout err: rstat 0x%03X\n",
+		          rstat | (info->line << 8));
+		return 0;
+	}
+
+	if (rstat & SER_DATA_AVAIL_MASK) {
+		/* Ok data, no error, count it */
+		TIMERD(DEBUG_LOG(info->line, "timeout: rstat 0x%03X\n",
+		          rstat | (info->line << 8)));
+		/* Read data to clear status flags */
+		(void)info->ioport[REG_DATA];
+
+		info->forced_eop = 0;
+		START_FLUSH_FAST_TIMER(info, "magic");
+		return 0;
+	}
+
+	/* hit the timeout, force an EOP for the input
+	 * dma channel if we haven't already
+	 */
+	if (!info->forced_eop) {
+		info->forced_eop = 1;
+		PROCSTAT(ser_stat[info->line].timeout_flush_cnt++);
+		TIMERD(DEBUG_LOG(info->line, "timeout EOP %i\n", info->line));
+		FORCE_EOP(info);
+	}
+
+	return 1;
+}
+
+static void flush_to_flip_buffer(struct e100_serial *info)
+{
+	struct tty_struct *tty;
+	struct etrax_recv_buffer *buffer;
+	unsigned long flags;
+
+	local_irq_save(flags);
+	tty = info->port.tty;
+
+	if (!tty) {
+		local_irq_restore(flags);
+		return;
+	}
+
+	while ((buffer = info->first_recv_buffer) != NULL) {
+		unsigned int count = buffer->length;
+
+		tty_insert_flip_string(tty, buffer->buffer, count);
+		info->recv_cnt -= count;
+
+		if (count == buffer->length) {
+			info->first_recv_buffer = buffer->next;
+			kfree(buffer);
+		} else {
+			buffer->length -= count;
+			memmove(buffer->buffer, buffer->buffer + count, buffer->length);
+			buffer->error = TTY_NORMAL;
+		}
+	}
+
+	if (!info->first_recv_buffer)
+		info->last_recv_buffer = NULL;
+
+	local_irq_restore(flags);
+
+	/* This includes a check for low-latency */
+	tty_flip_buffer_push(tty);
+}
+
+static void check_flush_timeout(struct e100_serial *info)
+{
+	/* Flip what we've got (if we can) */
+	flush_to_flip_buffer(info);
+
+	/* We might need to flip later, but not to fast
+	 * since the system is busy processing input... */
+	if (info->first_recv_buffer)
+		START_FLUSH_FAST_TIMER_TIME(info, "flip", 2000);
+
+	/* Force eop last, since data might have come while we're processing
+	 * and if we started the slow timer above, we won't start a fast
+	 * below.
+	 */
+	force_eop_if_needed(info);
+}
+
+#ifdef CONFIG_ETRAX_SERIAL_FAST_TIMER
+static void flush_timeout_function(unsigned long data)
+{
+	struct e100_serial *info = (struct e100_serial *)data;
+
+	fast_timers[info->line].function = NULL;
+	serial_fast_timer_expired++;
+	TIMERD(DEBUG_LOG(info->line, "flush_timout %i ", info->line));
+	TIMERD(DEBUG_LOG(info->line, "num expired: %i\n", serial_fast_timer_expired));
+	check_flush_timeout(info);
+}
+
+#else
+
+/* dma fifo/buffer timeout handler
+   forces an end-of-packet for the dma input channel if no chars
+   have been received for CONFIG_ETRAX_SERIAL_RX_TIMEOUT_TICKS/100 s.
+*/
+
+static struct timer_list flush_timer;
+
+static void
+timed_flush_handler(unsigned long ptr)
+{
+	struct e100_serial *info;
+	int i;
+
+#ifdef CONFIG_SVINTO_SIM
+	return;
+#endif
+
+	for (i = 0; i < NR_PORTS; i++) {
+		info = rs_table + i;
+		if (info->uses_dma_in)
+			check_flush_timeout(info);
+	}
+
+	/* restart flush timer */
+	mod_timer(&flush_timer, jiffies + CONFIG_ETRAX_SERIAL_RX_TIMEOUT_TICKS);
+}
+#endif
+
+#ifdef SERIAL_HANDLE_EARLY_ERRORS
+
+/* If there is an error (ie break) when the DMA is running and
+ * there are no bytes in the fifo the DMA is stopped and we get no
+ * eop interrupt. Thus we have to monitor the first bytes on a DMA
+ * transfer, and if it is without error we can turn the serial
+ * interrupts off.
+ */
+
+/*
+BREAK handling on ETRAX 100:
+ETRAX will generate interrupt although there is no stop bit between the
+characters.
+
+Depending on how long the break sequence is, the end of the breaksequence
+will look differently:
+| indicates start/end of a character.
+
+B= Break character (0x00) with framing error.
+E= Error byte with parity error received after B characters.
+F= "Faked" valid byte received immediately after B characters.
+V= Valid byte
+
+1.
+    B          BL         ___________________________ V
+.._|__________|__________|                           |valid data |
+
+Multiple frame errors with data == 0x00 (B),
+the timing matches up "perfectly" so no extra ending char is detected.
+The RXD pin is 1 in the last interrupt, in that case
+we set info->errorcode = ERRCODE_INSERT_BREAK, but we can't really
+know if another byte will come and this really is case 2. below
+(e.g F=0xFF or 0xFE)
+If RXD pin is 0 we can expect another character (see 2. below).
+
+
+2.
+
+    B          B          E or F__________________..__ V
+.._|__________|__________|______    |                 |valid data
+                          "valid" or
+                          parity error
+
+Multiple frame errors with data == 0x00 (B),
+but the part of the break trigs is interpreted as a start bit (and possibly
+some 0 bits followed by a number of 1 bits and a stop bit).
+Depending on parity settings etc. this last character can be either
+a fake "valid" char (F) or have a parity error (E).
+
+If the character is valid it will be put in the buffer,
+we set info->errorcode = ERRCODE_SET_BREAK so the receive interrupt
+will set the flags so the tty will handle it,
+if it's an error byte it will not be put in the buffer
+and we set info->errorcode = ERRCODE_INSERT_BREAK.
+
+To distinguish a V byte in 1. from an F byte in 2. we keep a timestamp
+of the last faulty char (B) and compares it with the current time:
+If the time elapsed time is less then 2*char_time_usec we will assume
+it's a faked F char and not a Valid char and set
+info->errorcode = ERRCODE_SET_BREAK.
+
+Flaws in the above solution:
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+We use the timer to distinguish a F character from a V character,
+if a V character is to close after the break we might make the wrong decision.
+
+TODO: The break will be delayed until an F or V character is received.
+
+*/
+
+static
+struct e100_serial * handle_ser_rx_interrupt_no_dma(struct e100_serial *info)
+{
+	unsigned long data_read;
+	struct tty_struct *tty = info->port.tty;
+
+	if (!tty) {
+		printk("!NO TTY!\n");
+		return info;
+	}
+
+	/* Read data and status at the same time */
+	data_read = *((unsigned long *)&info->ioport[REG_DATA_STATUS32]);
+more_data:
+	if (data_read & IO_MASK(R_SERIAL0_READ, xoff_detect) ) {
+		DFLOW(DEBUG_LOG(info->line, "XOFF detect\n", 0));
+	}
+	DINTR2(DEBUG_LOG(info->line, "ser_rx   %c\n", IO_EXTRACT(R_SERIAL0_READ, data_in, data_read)));
+
+	if (data_read & ( IO_MASK(R_SERIAL0_READ, framing_err) |
+			  IO_MASK(R_SERIAL0_READ, par_err) |
+			  IO_MASK(R_SERIAL0_READ, overrun) )) {
+		/* An error */
+		info->last_rx_active_usec = GET_JIFFIES_USEC();
+		info->last_rx_active = jiffies;
+		DINTR1(DEBUG_LOG(info->line, "ser_rx err stat_data %04X\n", data_read));
+		DLOG_INT_TRIG(
+		if (!log_int_trig1_pos) {
+			log_int_trig1_pos = log_int_pos;
+			log_int(rdpc(), 0, 0);
+		}
+		);
+
+
+		if ( ((data_read & IO_MASK(R_SERIAL0_READ, data_in)) == 0) &&
+		     (data_read & IO_MASK(R_SERIAL0_READ, framing_err)) ) {
+			/* Most likely a break, but we get interrupts over and
+			 * over again.
+			 */
+
+			if (!info->break_detected_cnt) {
+				DEBUG_LOG(info->line, "#BRK start\n", 0);
+			}
+			if (data_read & IO_MASK(R_SERIAL0_READ, rxd)) {
+				/* The RX pin is high now, so the break
+				 * must be over, but....
+				 * we can't really know if we will get another
+				 * last byte ending the break or not.
+				 * And we don't know if the byte (if any) will
+				 * have an error or look valid.
+				 */
+				DEBUG_LOG(info->line, "# BL BRK\n", 0);
+				info->errorcode = ERRCODE_INSERT_BREAK;
+			}
+			info->break_detected_cnt++;
+		} else {
+			/* The error does not look like a break, but could be
+			 * the end of one
+			 */
+			if (info->break_detected_cnt) {
+				DEBUG_LOG(info->line, "EBRK %i\n", info->break_detected_cnt);
+				info->errorcode = ERRCODE_INSERT_BREAK;
+			} else {
+				unsigned char data = IO_EXTRACT(R_SERIAL0_READ,
+					data_in, data_read);
+				char flag = TTY_NORMAL;
+				if (info->errorcode == ERRCODE_INSERT_BREAK) {
+					struct tty_struct *tty = info->port.tty;
+					tty_insert_flip_char(tty, 0, flag);
+					info->icount.rx++;
+				}
+
+				if (data_read & IO_MASK(R_SERIAL0_READ, par_err)) {
+					info->icount.parity++;
+					flag = TTY_PARITY;
+				} else if (data_read & IO_MASK(R_SERIAL0_READ, overrun)) {
+					info->icount.overrun++;
+					flag = TTY_OVERRUN;
+				} else if (data_read & IO_MASK(R_SERIAL0_READ, framing_err)) {
+					info->icount.frame++;
+					flag = TTY_FRAME;
+				}
+				tty_insert_flip_char(tty, data, flag);
+				info->errorcode = 0;
+			}
+			info->break_detected_cnt = 0;
+		}
+	} else if (data_read & IO_MASK(R_SERIAL0_READ, data_avail)) {
+		/* No error */
+		DLOG_INT_TRIG(
+		if (!log_int_trig1_pos) {
+			if (log_int_pos >= log_int_size) {
+				log_int_pos = 0;
+			}
+			log_int_trig0_pos = log_int_pos;
+			log_int(rdpc(), 0, 0);
+		}
+		);
+		tty_insert_flip_char(tty,
+			IO_EXTRACT(R_SERIAL0_READ, data_in, data_read),
+			TTY_NORMAL);
+	} else {
+		DEBUG_LOG(info->line, "ser_rx int but no data_avail  %08lX\n", data_read);
+	}
+
+
+	info->icount.rx++;
+	data_read = *((unsigned long *)&info->ioport[REG_DATA_STATUS32]);
+	if (data_read & IO_MASK(R_SERIAL0_READ, data_avail)) {
+		DEBUG_LOG(info->line, "ser_rx   %c in loop\n", IO_EXTRACT(R_SERIAL0_READ, data_in, data_read));
+		goto more_data;
+	}
+
+	tty_flip_buffer_push(info->port.tty);
+	return info;
+}
+
+static struct e100_serial* handle_ser_rx_interrupt(struct e100_serial *info)
+{
+	unsigned char rstat;
+
+#ifdef SERIAL_DEBUG_INTR
+	printk("Interrupt from serport %d\n", i);
+#endif
+/*	DEBUG_LOG(info->line, "ser_interrupt stat %03X\n", rstat | (i << 8)); */
+	if (!info->uses_dma_in) {
+		return handle_ser_rx_interrupt_no_dma(info);
+	}
+	/* DMA is used */
+	rstat = info->ioport[REG_STATUS];
+	if (rstat & IO_MASK(R_SERIAL0_STATUS, xoff_detect) ) {
+		DFLOW(DEBUG_LOG(info->line, "XOFF detect\n", 0));
+	}
+
+	if (rstat & SER_ERROR_MASK) {
+		unsigned char data;
+
+		info->last_rx_active_usec = GET_JIFFIES_USEC();
+		info->last_rx_active = jiffies;
+		/* If we got an error, we must reset it by reading the
+		 * data_in field
+		 */
+		data = info->ioport[REG_DATA];
+		DINTR1(DEBUG_LOG(info->line, "ser_rx!  %c\n", data));
+		DINTR1(DEBUG_LOG(info->line, "ser_rx err stat %02X\n", rstat));
+		if (!data && (rstat & SER_FRAMING_ERR_MASK)) {
+			/* Most likely a break, but we get interrupts over and
+			 * over again.
+			 */
+
+			if (!info->break_detected_cnt) {
+				DEBUG_LOG(info->line, "#BRK start\n", 0);
+			}
+			if (rstat & SER_RXD_MASK) {
+				/* The RX pin is high now, so the break
+				 * must be over, but....
+				 * we can't really know if we will get another
+				 * last byte ending the break or not.
+				 * And we don't know if the byte (if any) will
+				 * have an error or look valid.
+				 */
+				DEBUG_LOG(info->line, "# BL BRK\n", 0);
+				info->errorcode = ERRCODE_INSERT_BREAK;
+			}
+			info->break_detected_cnt++;
+		} else {
+			/* The error does not look like a break, but could be
+			 * the end of one
+			 */
+			if (info->break_detected_cnt) {
+				DEBUG_LOG(info->line, "EBRK %i\n", info->break_detected_cnt);
+				info->errorcode = ERRCODE_INSERT_BREAK;
+			} else {
+				if (info->errorcode == ERRCODE_INSERT_BREAK) {
+					info->icount.brk++;
+					add_char_and_flag(info, '\0', TTY_BREAK);
+				}
+
+				if (rstat & SER_PAR_ERR_MASK) {
+					info->icount.parity++;
+					add_char_and_flag(info, data, TTY_PARITY);
+				} else if (rstat & SER_OVERRUN_MASK) {
+					info->icount.overrun++;
+					add_char_and_flag(info, data, TTY_OVERRUN);
+				} else if (rstat & SER_FRAMING_ERR_MASK) {
+					info->icount.frame++;
+					add_char_and_flag(info, data, TTY_FRAME);
+				}
+
+				info->errorcode = 0;
+			}
+			info->break_detected_cnt = 0;
+			DEBUG_LOG(info->line, "#iERR s d %04X\n",
+			          ((rstat & SER_ERROR_MASK) << 8) | data);
+		}
+		PROCSTAT(ser_stat[info->line].early_errors_cnt++);
+	} else { /* It was a valid byte, now let the DMA do the rest */
+		unsigned long curr_time_u = GET_JIFFIES_USEC();
+		unsigned long curr_time = jiffies;
+
+		if (info->break_detected_cnt) {
+			/* Detect if this character is a new valid char or the
+			 * last char in a break sequence: If LSBits are 0 and
+			 * MSBits are high AND the time is close to the
+			 * previous interrupt we should discard it.
+			 */
+			long elapsed_usec =
+			  (curr_time - info->last_rx_active) * (1000000/HZ) +
+			  curr_time_u - info->last_rx_active_usec;
+			if (elapsed_usec < 2*info->char_time_usec) {
+				DEBUG_LOG(info->line, "FBRK %i\n", info->line);
+				/* Report as BREAK (error) and let
+				 * receive_chars_dma() handle it
+				 */
+				info->errorcode = ERRCODE_SET_BREAK;
+			} else {
+				DEBUG_LOG(info->line, "Not end of BRK (V)%i\n", info->line);
+			}
+			DEBUG_LOG(info->line, "num brk %i\n", info->break_detected_cnt);
+		}
+
+#ifdef SERIAL_DEBUG_INTR
+		printk("** OK, disabling ser_interrupts\n");
+#endif
+		e100_disable_serial_data_irq(info);
+		DINTR2(DEBUG_LOG(info->line, "ser_rx OK %d\n", info->line));
+		info->break_detected_cnt = 0;
+
+		PROCSTAT(ser_stat[info->line].ser_ints_ok_cnt++);
+	}
+	/* Restarting the DMA never hurts */
+	*info->icmdadr = IO_STATE(R_DMA_CH6_CMD, cmd, restart);
+	START_FLUSH_FAST_TIMER(info, "ser_int");
+	return info;
+} /* handle_ser_rx_interrupt */
+
+static void handle_ser_tx_interrupt(struct e100_serial *info)
+{
+	unsigned long flags;
+
+	if (info->x_char) {
+		unsigned char rstat;
+		DFLOW(DEBUG_LOG(info->line, "tx_int: xchar 0x%02X\n", info->x_char));
+		local_irq_save(flags);
+		rstat = info->ioport[REG_STATUS];
+		DFLOW(DEBUG_LOG(info->line, "stat %x\n", rstat));
+
+		info->ioport[REG_TR_DATA] = info->x_char;
+		info->icount.tx++;
+		info->x_char = 0;
+		/* We must enable since it is disabled in ser_interrupt */
+		e100_enable_serial_tx_ready_irq(info);
+		local_irq_restore(flags);
+		return;
+	}
+	if (info->uses_dma_out) {
+		unsigned char rstat;
+		int i;
+		/* We only use normal tx interrupt when sending x_char */
+		DFLOW(DEBUG_LOG(info->line, "tx_int: xchar sent\n", 0));
+		local_irq_save(flags);
+		rstat = info->ioport[REG_STATUS];
+		DFLOW(DEBUG_LOG(info->line, "stat %x\n", rstat));
+		e100_disable_serial_tx_ready_irq(info);
+		if (info->port.tty->stopped)
+			rs_stop(info->port.tty);
+		/* Enable the DMA channel and tell it to continue */
+		e100_enable_txdma_channel(info);
+		/* Wait 12 cycles before doing the DMA command */
+		for(i = 6;  i > 0; i--)
+			nop();
+
+		*info->ocmdadr = IO_STATE(R_DMA_CH6_CMD, cmd, continue);
+		local_irq_restore(flags);
+		return;
+	}
+	/* Normal char-by-char interrupt */
+	if (info->xmit.head == info->xmit.tail
+	    || info->port.tty->stopped
+	    || info->port.tty->hw_stopped) {
+		DFLOW(DEBUG_LOG(info->line, "tx_int: stopped %i\n",
+				info->port.tty->stopped));
+		e100_disable_serial_tx_ready_irq(info);
+		info->tr_running = 0;
+		return;
+	}
+	DINTR2(DEBUG_LOG(info->line, "tx_int %c\n", info->xmit.buf[info->xmit.tail]));
+	/* Send a byte, rs485 timing is critical so turn of ints */
+	local_irq_save(flags);
+	info->ioport[REG_TR_DATA] = info->xmit.buf[info->xmit.tail];
+	info->xmit.tail = (info->xmit.tail + 1) & (SERIAL_XMIT_SIZE-1);
+	info->icount.tx++;
+	if (info->xmit.head == info->xmit.tail) {
+#if defined(CONFIG_ETRAX_RS485) && defined(CONFIG_ETRAX_FAST_TIMER)
+		if (info->rs485.flags & SER_RS485_ENABLED) {
+			/* Set a short timer to toggle RTS */
+			start_one_shot_timer(&fast_timers_rs485[info->line],
+			                     rs485_toggle_rts_timer_function,
+			                     (unsigned long)info,
+			                     info->char_time_usec*2,
+			                     "RS-485");
+		}
+#endif /* RS485 */
+		info->last_tx_active_usec = GET_JIFFIES_USEC();
+		info->last_tx_active = jiffies;
+		e100_disable_serial_tx_ready_irq(info);
+		info->tr_running = 0;
+		DFLOW(DEBUG_LOG(info->line, "tx_int: stop2\n", 0));
+	} else {
+		/* We must enable since it is disabled in ser_interrupt */
+		e100_enable_serial_tx_ready_irq(info);
+	}
+	local_irq_restore(flags);
+
+	if (CIRC_CNT(info->xmit.head,
+		     info->xmit.tail,
+		     SERIAL_XMIT_SIZE) < WAKEUP_CHARS)
+		rs_sched_event(info, RS_EVENT_WRITE_WAKEUP);
+
+} /* handle_ser_tx_interrupt */
+
+/* result of time measurements:
+ * RX duration 54-60 us when doing something, otherwise 6-9 us
+ * ser_int duration: just sending: 8-15 us normally, up to 73 us
+ */
+static irqreturn_t
+ser_interrupt(int irq, void *dev_id)
+{
+	static volatile int tx_started = 0;
+	struct e100_serial *info;
+	int i;
+	unsigned long flags;
+	unsigned long irq_mask1_rd;
+	unsigned long data_mask = (1 << (8+2*0)); /* ser0 data_avail */
+	int handled = 0;
+	static volatile unsigned long reentered_ready_mask = 0;
+
+	local_irq_save(flags);
+	irq_mask1_rd = *R_IRQ_MASK1_RD;
+	/* First handle all rx interrupts with ints disabled */
+	info = rs_table;
+	irq_mask1_rd &= e100_ser_int_mask;
+	for (i = 0; i < NR_PORTS; i++) {
+		/* Which line caused the data irq? */
+		if (irq_mask1_rd & data_mask) {
+			handled = 1;
+			handle_ser_rx_interrupt(info);
+		}
+		info += 1;
+		data_mask <<= 2;
+	}
+	/* Handle tx interrupts with interrupts enabled so we
+	 * can take care of new data interrupts while transmitting
+	 * We protect the tx part with the tx_started flag.
+	 * We disable the tr_ready interrupts we are about to handle and
+	 * unblock the serial interrupt so new serial interrupts may come.
+	 *
+	 * If we get a new interrupt:
+	 *  - it migth be due to synchronous serial ports.
+	 *  - serial irq will be blocked by general irq handler.
+	 *  - async data will be handled above (sync will be ignored).
+	 *  - tx_started flag will prevent us from trying to send again and
+	 *    we will exit fast - no need to unblock serial irq.
+	 *  - Next (sync) serial interrupt handler will be runned with
+	 *    disabled interrupt due to restore_flags() at end of function,
+	 *    so sync handler will not be preempted or reentered.
+	 */
+	if (!tx_started) {
+		unsigned long ready_mask;
+		unsigned long
+		tx_started = 1;
+		/* Only the tr_ready interrupts left */
+		irq_mask1_rd &= (IO_MASK(R_IRQ_MASK1_RD, ser0_ready) |
+				 IO_MASK(R_IRQ_MASK1_RD, ser1_ready) |
+				 IO_MASK(R_IRQ_MASK1_RD, ser2_ready) |
+				 IO_MASK(R_IRQ_MASK1_RD, ser3_ready));
+		while (irq_mask1_rd) {
+			/* Disable those we are about to handle */
+			*R_IRQ_MASK1_CLR = irq_mask1_rd;
+			/* Unblock the serial interrupt */
+			*R_VECT_MASK_SET = IO_STATE(R_VECT_MASK_SET, serial, set);
+
+			local_irq_enable();
+			ready_mask = (1 << (8+1+2*0)); /* ser0 tr_ready */
+			info = rs_table;
+			for (i = 0; i < NR_PORTS; i++) {
+				/* Which line caused the ready irq? */
+				if (irq_mask1_rd & ready_mask) {
+					handled = 1;
+					handle_ser_tx_interrupt(info);
+				}
+				info += 1;
+				ready_mask <<= 2;
+			}
+			/* handle_ser_tx_interrupt enables tr_ready interrupts */
+			local_irq_disable();
+			/* Handle reentered TX interrupt */
+			irq_mask1_rd = reentered_ready_mask;
+		}
+		local_irq_disable();
+		tx_started = 0;
+	} else {
+		unsigned long ready_mask;
+		ready_mask = irq_mask1_rd & (IO_MASK(R_IRQ_MASK1_RD, ser0_ready) |
+					     IO_MASK(R_IRQ_MASK1_RD, ser1_ready) |
+					     IO_MASK(R_IRQ_MASK1_RD, ser2_ready) |
+					     IO_MASK(R_IRQ_MASK1_RD, ser3_ready));
+		if (ready_mask) {
+			reentered_ready_mask |= ready_mask;
+			/* Disable those we are about to handle */
+			*R_IRQ_MASK1_CLR = ready_mask;
+			DFLOW(DEBUG_LOG(SERIAL_DEBUG_LINE, "ser_int reentered with TX %X\n", ready_mask));
+		}
+	}
+
+	local_irq_restore(flags);
+	return IRQ_RETVAL(handled);
+} /* ser_interrupt */
+#endif
+
+/*
+ * -------------------------------------------------------------------
+ * Here ends the serial interrupt routines.
+ * -------------------------------------------------------------------
+ */
+
+/*
+ * This routine is used to handle the "bottom half" processing for the
+ * serial driver, known also the "software interrupt" processing.
+ * This processing is done at the kernel interrupt level, after the
+ * rs_interrupt() has returned, BUT WITH INTERRUPTS TURNED ON.  This
+ * is where time-consuming activities which can not be done in the
+ * interrupt driver proper are done; the interrupt driver schedules
+ * them using rs_sched_event(), and they get done here.
+ */
+static void
+do_softint(struct work_struct *work)
+{
+	struct e100_serial	*info;
+	struct tty_struct	*tty;
+
+	info = container_of(work, struct e100_serial, work);
+
+	tty = info->port.tty;
+	if (!tty)
+		return;
+
+	if (test_and_clear_bit(RS_EVENT_WRITE_WAKEUP, &info->event))
+		tty_wakeup(tty);
+}
+
+static int
+startup(struct e100_serial * info)
+{
+	unsigned long flags;
+	unsigned long xmit_page;
+	int i;
+
+	xmit_page = get_zeroed_page(GFP_KERNEL);
+	if (!xmit_page)
+		return -ENOMEM;
+
+	local_irq_save(flags);
+
+	/* if it was already initialized, skip this */
+
+	if (info->flags & ASYNC_INITIALIZED) {
+		local_irq_restore(flags);
+		free_page(xmit_page);
+		return 0;
+	}
+
+	if (info->xmit.buf)
+		free_page(xmit_page);
+	else
+		info->xmit.buf = (unsigned char *) xmit_page;
+
+#ifdef SERIAL_DEBUG_OPEN
+	printk("starting up ttyS%d (xmit_buf 0x%p)...\n", info->line, info->xmit.buf);
+#endif
+
+#ifdef CONFIG_SVINTO_SIM
+	/* Bits and pieces collected from below.  Better to have them
+	   in one ifdef:ed clause than to mix in a lot of ifdefs,
+	   right? */
+	if (info->port.tty)
+		clear_bit(TTY_IO_ERROR, &info->port.tty->flags);
+
+	info->xmit.head = info->xmit.tail = 0;
+	info->first_recv_buffer = info->last_recv_buffer = NULL;
+	info->recv_cnt = info->max_recv_cnt = 0;
+
+	for (i = 0; i < SERIAL_RECV_DESCRIPTORS; i++)
+		info->rec_descr[i].buf = NULL;
+
+	/* No real action in the simulator, but may set info important
+	   to ioctl. */
+	change_speed(info);
+#else
+
+	/*
+	 * Clear the FIFO buffers and disable them
+	 * (they will be reenabled in change_speed())
+	 */
+
+	/*
+	 * Reset the DMA channels and make sure their interrupts are cleared
+	 */
+
+	if (info->dma_in_enabled) {
+		info->uses_dma_in = 1;
+		e100_enable_rxdma_channel(info);
+
+		*info->icmdadr = IO_STATE(R_DMA_CH6_CMD, cmd, reset);
+
+		/* Wait until reset cycle is complete */
+		while (IO_EXTRACT(R_DMA_CH6_CMD, cmd, *info->icmdadr) ==
+		       IO_STATE_VALUE(R_DMA_CH6_CMD, cmd, reset));
+
+		/* Make sure the irqs are cleared */
+		*info->iclrintradr =
+			IO_STATE(R_DMA_CH6_CLR_INTR, clr_descr, do) |
+			IO_STATE(R_DMA_CH6_CLR_INTR, clr_eop, do);
+	} else {
+		e100_disable_rxdma_channel(info);
+	}
+
+	if (info->dma_out_enabled) {
+		info->uses_dma_out = 1;
+		e100_enable_txdma_channel(info);
+		*info->ocmdadr = IO_STATE(R_DMA_CH6_CMD, cmd, reset);
+
+		while (IO_EXTRACT(R_DMA_CH6_CMD, cmd, *info->ocmdadr) ==
+		       IO_STATE_VALUE(R_DMA_CH6_CMD, cmd, reset));
+
+		/* Make sure the irqs are cleared */
+		*info->oclrintradr =
+			IO_STATE(R_DMA_CH6_CLR_INTR, clr_descr, do) |
+			IO_STATE(R_DMA_CH6_CLR_INTR, clr_eop, do);
+	} else {
+		e100_disable_txdma_channel(info);
+	}
+
+	if (info->port.tty)
+		clear_bit(TTY_IO_ERROR, &info->port.tty->flags);
+
+	info->xmit.head = info->xmit.tail = 0;
+	info->first_recv_buffer = info->last_recv_buffer = NULL;
+	info->recv_cnt = info->max_recv_cnt = 0;
+
+	for (i = 0; i < SERIAL_RECV_DESCRIPTORS; i++)
+		info->rec_descr[i].buf = 0;
+
+	/*
+	 * and set the speed and other flags of the serial port
+	 * this will start the rx/tx as well
+	 */
+#ifdef SERIAL_HANDLE_EARLY_ERRORS
+	e100_enable_serial_data_irq(info);
+#endif
+	change_speed(info);
+
+	/* dummy read to reset any serial errors */
+
+	(void)info->ioport[REG_DATA];
+
+	/* enable the interrupts */
+	if (info->uses_dma_out)
+		e100_enable_txdma_irq(info);
+
+	e100_enable_rx_irq(info);
+
+	info->tr_running = 0; /* to be sure we don't lock up the transmitter */
+
+	/* setup the dma input descriptor and start dma */
+
+	start_receive(info);
+
+	/* for safety, make sure the descriptors last result is 0 bytes written */
+
+	info->tr_descr.sw_len = 0;
+	info->tr_descr.hw_len = 0;
+	info->tr_descr.status = 0;
+
+	/* enable RTS/DTR last */
+
+	e100_rts(info, 1);
+	e100_dtr(info, 1);
+
+#endif /* CONFIG_SVINTO_SIM */
+
+	info->flags |= ASYNC_INITIALIZED;
+
+	local_irq_restore(flags);
+	return 0;
+}
+
+/*
+ * This routine will shutdown a serial port; interrupts are disabled, and
+ * DTR is dropped if the hangup on close termio flag is on.
+ */
+static void
+shutdown(struct e100_serial * info)
+{
+	unsigned long flags;
+	struct etrax_dma_descr *descr = info->rec_descr;
+	struct etrax_recv_buffer *buffer;
+	int i;
+
+#ifndef CONFIG_SVINTO_SIM
+	/* shut down the transmitter and receiver */
+	DFLOW(DEBUG_LOG(info->line, "shutdown %i\n", info->line));
+	e100_disable_rx(info);
+	info->ioport[REG_TR_CTRL] = (info->tx_ctrl &= ~0x40);
+
+	/* disable interrupts, reset dma channels */
+	if (info->uses_dma_in) {
+		e100_disable_rxdma_irq(info);
+		*info->icmdadr = IO_STATE(R_DMA_CH6_CMD, cmd, reset);
+		info->uses_dma_in = 0;
+	} else {
+		e100_disable_serial_data_irq(info);
+	}
+
+	if (info->uses_dma_out) {
+		e100_disable_txdma_irq(info);
+		info->tr_running = 0;
+		*info->ocmdadr = IO_STATE(R_DMA_CH6_CMD, cmd, reset);
+		info->uses_dma_out = 0;
+	} else {
+		e100_disable_serial_tx_ready_irq(info);
+		info->tr_running = 0;
+	}
+
+#endif /* CONFIG_SVINTO_SIM */
+
+	if (!(info->flags & ASYNC_INITIALIZED))
+		return;
+
+#ifdef SERIAL_DEBUG_OPEN
+	printk("Shutting down serial port %d (irq %d)....\n", info->line,
+	       info->irq);
+#endif
+
+	local_irq_save(flags);
+
+	if (info->xmit.buf) {
+		free_page((unsigned long)info->xmit.buf);
+		info->xmit.buf = NULL;
+	}
+
+	for (i = 0; i < SERIAL_RECV_DESCRIPTORS; i++)
+		if (descr[i].buf) {
+			buffer = phys_to_virt(descr[i].buf) - sizeof *buffer;
+			kfree(buffer);
+			descr[i].buf = 0;
+		}
+
+	if (!info->port.tty || (info->port.tty->termios->c_cflag & HUPCL)) {
+		/* hang up DTR and RTS if HUPCL is enabled */
+		e100_dtr(info, 0);
+		e100_rts(info, 0); /* could check CRTSCTS before doing this */
+	}
+
+	if (info->port.tty)
+		set_bit(TTY_IO_ERROR, &info->port.tty->flags);
+
+	info->flags &= ~ASYNC_INITIALIZED;
+	local_irq_restore(flags);
+}
+
+
+/* change baud rate and other assorted parameters */
+
+static void
+change_speed(struct e100_serial *info)
+{
+	unsigned int cflag;
+	unsigned long xoff;
+	unsigned long flags;
+	/* first some safety checks */
+
+	if (!info->port.tty || !info->port.tty->termios)
+		return;
+	if (!info->ioport)
+		return;
+
+	cflag = info->port.tty->termios->c_cflag;
+
+	/* possibly, the tx/rx should be disabled first to do this safely */
+
+	/* change baud-rate and write it to the hardware */
+	if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_CUST) {
+		/* Special baudrate */
+		u32 mask = 0xFF << (info->line*8); /* Each port has 8 bits */
+		unsigned long alt_source =
+				IO_STATE(R_ALT_SER_BAUDRATE, ser0_rec, normal) |
+				IO_STATE(R_ALT_SER_BAUDRATE, ser0_tr, normal);
+		/* R_ALT_SER_BAUDRATE selects the source */
+		DBAUD(printk("Custom baudrate: baud_base/divisor %lu/%i\n",
+		       (unsigned long)info->baud_base, info->custom_divisor));
+		if (info->baud_base == SERIAL_PRESCALE_BASE) {
+			/* 0, 2-65535 (0=65536) */
+			u16 divisor = info->custom_divisor;
+			/* R_SERIAL_PRESCALE (upper 16 bits of R_CLOCK_PRESCALE) */
+			/* baudrate is 3.125MHz/custom_divisor */
+			alt_source =
+				IO_STATE(R_ALT_SER_BAUDRATE, ser0_rec, prescale) |
+				IO_STATE(R_ALT_SER_BAUDRATE, ser0_tr, prescale);
+			alt_source = 0x11;
+			DBAUD(printk("Writing SERIAL_PRESCALE: divisor %i\n", divisor));
+			*R_SERIAL_PRESCALE = divisor;
+			info->baud = SERIAL_PRESCALE_BASE/divisor;
+		}
+#ifdef CONFIG_ETRAX_EXTERN_PB6CLK_ENABLED
+		else if ((info->baud_base==CONFIG_ETRAX_EXTERN_PB6CLK_FREQ/8 &&
+			  info->custom_divisor == 1) ||
+			 (info->baud_base==CONFIG_ETRAX_EXTERN_PB6CLK_FREQ &&
+			  info->custom_divisor == 8)) {
+				/* ext_clk selected */
+				alt_source =
+					IO_STATE(R_ALT_SER_BAUDRATE, ser0_rec, extern) |
+					IO_STATE(R_ALT_SER_BAUDRATE, ser0_tr, extern);
+				DBAUD(printk("using external baudrate: %lu\n", CONFIG_ETRAX_EXTERN_PB6CLK_FREQ/8));
+				info->baud = CONFIG_ETRAX_EXTERN_PB6CLK_FREQ/8;
+			}
+#endif
+		else
+		{
+			/* Bad baudbase, we don't support using timer0
+			 * for baudrate.
+			 */
+			printk(KERN_WARNING "Bad baud_base/custom_divisor: %lu/%i\n",
+			       (unsigned long)info->baud_base, info->custom_divisor);
+		}
+		r_alt_ser_baudrate_shadow &= ~mask;
+		r_alt_ser_baudrate_shadow |= (alt_source << (info->line*8));
+		*R_ALT_SER_BAUDRATE = r_alt_ser_baudrate_shadow;
+	} else {
+		/* Normal baudrate */
+		/* Make sure we use normal baudrate */
+		u32 mask = 0xFF << (info->line*8); /* Each port has 8 bits */
+		unsigned long alt_source =
+			IO_STATE(R_ALT_SER_BAUDRATE, ser0_rec, normal) |
+			IO_STATE(R_ALT_SER_BAUDRATE, ser0_tr, normal);
+		r_alt_ser_baudrate_shadow &= ~mask;
+		r_alt_ser_baudrate_shadow |= (alt_source << (info->line*8));
+#ifndef CONFIG_SVINTO_SIM
+		*R_ALT_SER_BAUDRATE = r_alt_ser_baudrate_shadow;
+#endif /* CONFIG_SVINTO_SIM */
+
+		info->baud = cflag_to_baud(cflag);
+#ifndef CONFIG_SVINTO_SIM
+		info->ioport[REG_BAUD] = cflag_to_etrax_baud(cflag);
+#endif /* CONFIG_SVINTO_SIM */
+	}
+
+#ifndef CONFIG_SVINTO_SIM
+	/* start with default settings and then fill in changes */
+	local_irq_save(flags);
+	/* 8 bit, no/even parity */
+	info->rx_ctrl &= ~(IO_MASK(R_SERIAL0_REC_CTRL, rec_bitnr) |
+			   IO_MASK(R_SERIAL0_REC_CTRL, rec_par_en) |
+			   IO_MASK(R_SERIAL0_REC_CTRL, rec_par));
+
+	/* 8 bit, no/even parity, 1 stop bit, no cts */
+	info->tx_ctrl &= ~(IO_MASK(R_SERIAL0_TR_CTRL, tr_bitnr) |
+			   IO_MASK(R_SERIAL0_TR_CTRL, tr_par_en) |
+			   IO_MASK(R_SERIAL0_TR_CTRL, tr_par) |
+			   IO_MASK(R_SERIAL0_TR_CTRL, stop_bits) |
+			   IO_MASK(R_SERIAL0_TR_CTRL, auto_cts));
+
+	if ((cflag & CSIZE) == CS7) {
+		/* set 7 bit mode */
+		info->tx_ctrl |= IO_STATE(R_SERIAL0_TR_CTRL, tr_bitnr, tr_7bit);
+		info->rx_ctrl |= IO_STATE(R_SERIAL0_REC_CTRL, rec_bitnr, rec_7bit);
+	}
+
+	if (cflag & CSTOPB) {
+		/* set 2 stop bit mode */
+		info->tx_ctrl |= IO_STATE(R_SERIAL0_TR_CTRL, stop_bits, two_bits);
+	}
+
+	if (cflag & PARENB) {
+		/* enable parity */
+		info->tx_ctrl |= IO_STATE(R_SERIAL0_TR_CTRL, tr_par_en, enable);
+		info->rx_ctrl |= IO_STATE(R_SERIAL0_REC_CTRL, rec_par_en, enable);
+	}
+
+	if (cflag & CMSPAR) {
+		/* enable stick parity, PARODD mean Mark which matches ETRAX */
+		info->tx_ctrl |= IO_STATE(R_SERIAL0_TR_CTRL, tr_stick_par, stick);
+		info->rx_ctrl |= IO_STATE(R_SERIAL0_REC_CTRL, rec_stick_par, stick);
+	}
+	if (cflag & PARODD) {
+		/* set odd parity (or Mark if CMSPAR) */
+		info->tx_ctrl |= IO_STATE(R_SERIAL0_TR_CTRL, tr_par, odd);
+		info->rx_ctrl |= IO_STATE(R_SERIAL0_REC_CTRL, rec_par, odd);
+	}
+
+	if (cflag & CRTSCTS) {
+		/* enable automatic CTS handling */
+		DFLOW(DEBUG_LOG(info->line, "FLOW auto_cts enabled\n", 0));
+		info->tx_ctrl |= IO_STATE(R_SERIAL0_TR_CTRL, auto_cts, active);
+	}
+
+	/* make sure the tx and rx are enabled */
+
+	info->tx_ctrl |= IO_STATE(R_SERIAL0_TR_CTRL, tr_enable, enable);
+	info->rx_ctrl |= IO_STATE(R_SERIAL0_REC_CTRL, rec_enable, enable);
+
+	/* actually write the control regs to the hardware */
+
+	info->ioport[REG_TR_CTRL] = info->tx_ctrl;
+	info->ioport[REG_REC_CTRL] = info->rx_ctrl;
+	xoff = IO_FIELD(R_SERIAL0_XOFF, xoff_char, STOP_CHAR(info->port.tty));
+	xoff |= IO_STATE(R_SERIAL0_XOFF, tx_stop, enable);
+	if (info->port.tty->termios->c_iflag & IXON ) {
+		DFLOW(DEBUG_LOG(info->line, "FLOW XOFF enabled 0x%02X\n",
+				STOP_CHAR(info->port.tty)));
+		xoff |= IO_STATE(R_SERIAL0_XOFF, auto_xoff, enable);
+	}
+
+	*((unsigned long *)&info->ioport[REG_XOFF]) = xoff;
+	local_irq_restore(flags);
+#endif /* !CONFIG_SVINTO_SIM */
+
+	update_char_time(info);
+
+} /* change_speed */
+
+/* start transmitting chars NOW */
+
+static void
+rs_flush_chars(struct tty_struct *tty)
+{
+	struct e100_serial *info = (struct e100_serial *)tty->driver_data;
+	unsigned long flags;
+
+	if (info->tr_running ||
+	    info->xmit.head == info->xmit.tail ||
+	    tty->stopped ||
+	    tty->hw_stopped ||
+	    !info->xmit.buf)
+		return;
+
+#ifdef SERIAL_DEBUG_FLOW
+	printk("rs_flush_chars\n");
+#endif
+
+	/* this protection might not exactly be necessary here */
+
+	local_irq_save(flags);
+	start_transmit(info);
+	local_irq_restore(flags);
+}
+
+static int rs_raw_write(struct tty_struct *tty,
+			const unsigned char *buf, int count)
+{
+	int	c, ret = 0;
+	struct e100_serial *info = (struct e100_serial *)tty->driver_data;
+	unsigned long flags;
+
+	/* first some sanity checks */
+
+	if (!tty || !info->xmit.buf || !tmp_buf)
+		return 0;
+
+#ifdef SERIAL_DEBUG_DATA
+	if (info->line == SERIAL_DEBUG_LINE)
+		printk("rs_raw_write (%d), status %d\n",
+		       count, info->ioport[REG_STATUS]);
+#endif
+
+#ifdef CONFIG_SVINTO_SIM
+	/* Really simple.  The output is here and now. */
+	SIMCOUT(buf, count);
+	return count;
+#endif
+	local_save_flags(flags);
+	DFLOW(DEBUG_LOG(info->line, "write count %i ", count));
+	DFLOW(DEBUG_LOG(info->line, "ldisc %i\n", tty->ldisc.chars_in_buffer(tty)));
+
+
+	/* The local_irq_disable/restore_flags pairs below are needed
+	 * because the DMA interrupt handler moves the info->xmit values.
+	 * the memcpy needs to be in the critical region unfortunately,
+	 * because we need to read xmit values, memcpy, write xmit values
+	 * in one atomic operation... this could perhaps be avoided by
+	 * more clever design.
+	 */
+	local_irq_disable();
+		while (count) {
+			c = CIRC_SPACE_TO_END(info->xmit.head,
+					      info->xmit.tail,
+					      SERIAL_XMIT_SIZE);
+
+			if (count < c)
+				c = count;
+			if (c <= 0)
+				break;
+
+			memcpy(info->xmit.buf + info->xmit.head, buf, c);
+			info->xmit.head = (info->xmit.head + c) &
+				(SERIAL_XMIT_SIZE-1);
+			buf += c;
+			count -= c;
+			ret += c;
+		}
+	local_irq_restore(flags);
+
+	/* enable transmitter if not running, unless the tty is stopped
+	 * this does not need IRQ protection since if tr_running == 0
+	 * the IRQ's are not running anyway for this port.
+	 */
+	DFLOW(DEBUG_LOG(info->line, "write ret %i\n", ret));
+
+	if (info->xmit.head != info->xmit.tail &&
+	    !tty->stopped &&
+	    !tty->hw_stopped &&
+	    !info->tr_running) {
+		start_transmit(info);
+	}
+
+	return ret;
+} /* raw_raw_write() */
+
+static int
+rs_write(struct tty_struct *tty,
+	 const unsigned char *buf, int count)
+{
+#if defined(CONFIG_ETRAX_RS485)
+	struct e100_serial *info = (struct e100_serial *)tty->driver_data;
+
+	if (info->rs485.flags & SER_RS485_ENABLED)
+	{
+		/* If we are in RS-485 mode, we need to toggle RTS and disable
+		 * the receiver before initiating a DMA transfer
+		 */
+#ifdef CONFIG_ETRAX_FAST_TIMER
+		/* Abort any started timer */
+		fast_timers_rs485[info->line].function = NULL;
+		del_fast_timer(&fast_timers_rs485[info->line]);
+#endif
+		e100_rts(info, (info->rs485.flags & SER_RS485_RTS_ON_SEND));
+#if defined(CONFIG_ETRAX_RS485_DISABLE_RECEIVER)
+		e100_disable_rx(info);
+		e100_enable_rx_irq(info);
+#endif
+		if (info->rs485.delay_rts_before_send > 0)
+			msleep(info->rs485.delay_rts_before_send);
+	}
+#endif /* CONFIG_ETRAX_RS485 */
+
+	count = rs_raw_write(tty, buf, count);
+
+#if defined(CONFIG_ETRAX_RS485)
+	if (info->rs485.flags & SER_RS485_ENABLED)
+	{
+		unsigned int val;
+		/* If we are in RS-485 mode the following has to be done:
+		 * wait until DMA is ready
+		 * wait on transmit shift register
+		 * toggle RTS
+		 * enable the receiver
+		 */
+
+		/* Sleep until all sent */
+		tty_wait_until_sent(tty, 0);
+#ifdef CONFIG_ETRAX_FAST_TIMER
+		/* Now sleep a little more so that shift register is empty */
+		schedule_usleep(info->char_time_usec * 2);
+#endif
+		/* wait on transmit shift register */
+		do{
+			get_lsr_info(info, &val);
+		}while (!(val & TIOCSER_TEMT));
+
+		e100_rts(info, (info->rs485.flags & SER_RS485_RTS_AFTER_SEND));
+
+#if defined(CONFIG_ETRAX_RS485_DISABLE_RECEIVER)
+		e100_enable_rx(info);
+		e100_enable_rxdma_irq(info);
+#endif
+	}
+#endif /* CONFIG_ETRAX_RS485 */
+
+	return count;
+} /* rs_write */
+
+
+/* how much space is available in the xmit buffer? */
+
+static int
+rs_write_room(struct tty_struct *tty)
+{
+	struct e100_serial *info = (struct e100_serial *)tty->driver_data;
+
+	return CIRC_SPACE(info->xmit.head, info->xmit.tail, SERIAL_XMIT_SIZE);
+}
+
+/* How many chars are in the xmit buffer?
+ * This does not include any chars in the transmitter FIFO.
+ * Use wait_until_sent for waiting for FIFO drain.
+ */
+
+static int
+rs_chars_in_buffer(struct tty_struct *tty)
+{
+	struct e100_serial *info = (struct e100_serial *)tty->driver_data;
+
+	return CIRC_CNT(info->xmit.head, info->xmit.tail, SERIAL_XMIT_SIZE);
+}
+
+/* discard everything in the xmit buffer */
+
+static void
+rs_flush_buffer(struct tty_struct *tty)
+{
+	struct e100_serial *info = (struct e100_serial *)tty->driver_data;
+	unsigned long flags;
+
+	local_irq_save(flags);
+	info->xmit.head = info->xmit.tail = 0;
+	local_irq_restore(flags);
+
+	tty_wakeup(tty);
+}
+
+/*
+ * This function is used to send a high-priority XON/XOFF character to
+ * the device
+ *
+ * Since we use DMA we don't check for info->x_char in transmit_chars_dma(),
+ * but we do it in handle_ser_tx_interrupt().
+ * We disable DMA channel and enable tx ready interrupt and write the
+ * character when possible.
+ */
+static void rs_send_xchar(struct tty_struct *tty, char ch)
+{
+	struct e100_serial *info = (struct e100_serial *)tty->driver_data;
+	unsigned long flags;
+	local_irq_save(flags);
+	if (info->uses_dma_out) {
+		/* Put the DMA on hold and disable the channel */
+		*info->ocmdadr = IO_STATE(R_DMA_CH6_CMD, cmd, hold);
+		while (IO_EXTRACT(R_DMA_CH6_CMD, cmd, *info->ocmdadr) !=
+		       IO_STATE_VALUE(R_DMA_CH6_CMD, cmd, hold));
+		e100_disable_txdma_channel(info);
+	}
+
+	/* Must make sure transmitter is not stopped before we can transmit */
+	if (tty->stopped)
+		rs_start(tty);
+
+	/* Enable manual transmit interrupt and send from there */
+	DFLOW(DEBUG_LOG(info->line, "rs_send_xchar 0x%02X\n", ch));
+	info->x_char = ch;
+	e100_enable_serial_tx_ready_irq(info);
+	local_irq_restore(flags);
+}
+
+/*
+ * ------------------------------------------------------------
+ * rs_throttle()
+ *
+ * This routine is called by the upper-layer tty layer to signal that
+ * incoming characters should be throttled.
+ * ------------------------------------------------------------
+ */
+static void
+rs_throttle(struct tty_struct * tty)
+{
+	struct e100_serial *info = (struct e100_serial *)tty->driver_data;
+#ifdef SERIAL_DEBUG_THROTTLE
+	char	buf[64];
+
+	printk("throttle %s: %lu....\n", tty_name(tty, buf),
+	       (unsigned long)tty->ldisc.chars_in_buffer(tty));
+#endif
+	DFLOW(DEBUG_LOG(info->line,"rs_throttle %lu\n", tty->ldisc.chars_in_buffer(tty)));
+
+	/* Do RTS before XOFF since XOFF might take some time */
+	if (tty->termios->c_cflag & CRTSCTS) {
+		/* Turn off RTS line */
+		e100_rts(info, 0);
+	}
+	if (I_IXOFF(tty))
+		rs_send_xchar(tty, STOP_CHAR(tty));
+
+}
+
+static void
+rs_unthrottle(struct tty_struct * tty)
+{
+	struct e100_serial *info = (struct e100_serial *)tty->driver_data;
+#ifdef SERIAL_DEBUG_THROTTLE
+	char	buf[64];
+
+	printk("unthrottle %s: %lu....\n", tty_name(tty, buf),
+	       (unsigned long)tty->ldisc.chars_in_buffer(tty));
+#endif
+	DFLOW(DEBUG_LOG(info->line,"rs_unthrottle ldisc %d\n", tty->ldisc.chars_in_buffer(tty)));
+	DFLOW(DEBUG_LOG(info->line,"rs_unthrottle flip.count: %i\n", tty->flip.count));
+	/* Do RTS before XOFF since XOFF might take some time */
+	if (tty->termios->c_cflag & CRTSCTS) {
+		/* Assert RTS line  */
+		e100_rts(info, 1);
+	}
+
+	if (I_IXOFF(tty)) {
+		if (info->x_char)
+			info->x_char = 0;
+		else
+			rs_send_xchar(tty, START_CHAR(tty));
+	}
+
+}
+
+/*
+ * ------------------------------------------------------------
+ * rs_ioctl() and friends
+ * ------------------------------------------------------------
+ */
+
+static int
+get_serial_info(struct e100_serial * info,
+		struct serial_struct * retinfo)
+{
+	struct serial_struct tmp;
+
+	/* this is all probably wrong, there are a lot of fields
+	 * here that we don't have in e100_serial and maybe we
+	 * should set them to something else than 0.
+	 */
+
+	if (!retinfo)
+		return -EFAULT;
+	memset(&tmp, 0, sizeof(tmp));
+	tmp.type = info->type;
+	tmp.line = info->line;
+	tmp.port = (int)info->ioport;
+	tmp.irq = info->irq;
+	tmp.flags = info->flags;
+	tmp.baud_base = info->baud_base;
+	tmp.close_delay = info->close_delay;
+	tmp.closing_wait = info->closing_wait;
+	tmp.custom_divisor = info->custom_divisor;
+	if (copy_to_user(retinfo, &tmp, sizeof(*retinfo)))
+		return -EFAULT;
+	return 0;
+}
+
+static int
+set_serial_info(struct e100_serial *info,
+		struct serial_struct *new_info)
+{
+	struct serial_struct new_serial;
+	struct e100_serial old_info;
+	int retval = 0;
+
+	if (copy_from_user(&new_serial, new_info, sizeof(new_serial)))
+		return -EFAULT;
+
+	old_info = *info;
+
+	if (!capable(CAP_SYS_ADMIN)) {
+		if ((new_serial.type != info->type) ||
+		    (new_serial.close_delay != info->close_delay) ||
+		    ((new_serial.flags & ~ASYNC_USR_MASK) !=
+		     (info->flags & ~ASYNC_USR_MASK)))
+			return -EPERM;
+		info->flags = ((info->flags & ~ASYNC_USR_MASK) |
+			       (new_serial.flags & ASYNC_USR_MASK));
+		goto check_and_exit;
+	}
+
+	if (info->count > 1)
+		return -EBUSY;
+
+	/*
+	 * OK, past this point, all the error checking has been done.
+	 * At this point, we start making changes.....
+	 */
+
+	info->baud_base = new_serial.baud_base;
+	info->flags = ((info->flags & ~ASYNC_FLAGS) |
+		       (new_serial.flags & ASYNC_FLAGS));
+	info->custom_divisor = new_serial.custom_divisor;
+	info->type = new_serial.type;
+	info->close_delay = new_serial.close_delay;
+	info->closing_wait = new_serial.closing_wait;
+	info->port.tty->low_latency = (info->flags & ASYNC_LOW_LATENCY) ? 1 : 0;
+
+ check_and_exit:
+	if (info->flags & ASYNC_INITIALIZED) {
+		change_speed(info);
+	} else
+		retval = startup(info);
+	return retval;
+}
+
+/*
+ * get_lsr_info - get line status register info
+ *
+ * Purpose: Let user call ioctl() to get info when the UART physically
+ * 	    is emptied.  On bus types like RS485, the transmitter must
+ * 	    release the bus after transmitting. This must be done when
+ * 	    the transmit shift register is empty, not be done when the
+ * 	    transmit holding register is empty.  This functionality
+ * 	    allows an RS485 driver to be written in user space.
+ */
+static int
+get_lsr_info(struct e100_serial * info, unsigned int *value)
+{
+	unsigned int result = TIOCSER_TEMT;
+#ifndef CONFIG_SVINTO_SIM
+	unsigned long curr_time = jiffies;
+	unsigned long curr_time_usec = GET_JIFFIES_USEC();
+	unsigned long elapsed_usec =
+		(curr_time - info->last_tx_active) * 1000000/HZ +
+		curr_time_usec - info->last_tx_active_usec;
+
+	if (info->xmit.head != info->xmit.tail ||
+	    elapsed_usec < 2*info->char_time_usec) {
+		result = 0;
+	}
+#endif
+
+	if (copy_to_user(value, &result, sizeof(int)))
+		return -EFAULT;
+	return 0;
+}
+
+#ifdef SERIAL_DEBUG_IO
+struct state_str
+{
+	int state;
+	const char *str;
+};
+
+const struct state_str control_state_str[] = {
+	{TIOCM_DTR, "DTR" },
+	{TIOCM_RTS, "RTS"},
+	{TIOCM_ST, "ST?" },
+	{TIOCM_SR, "SR?" },
+	{TIOCM_CTS, "CTS" },
+	{TIOCM_CD, "CD" },
+	{TIOCM_RI, "RI" },
+	{TIOCM_DSR, "DSR" },
+	{0, NULL }
+};
+
+char *get_control_state_str(int MLines, char *s)
+{
+	int i = 0;
+
+	s[0]='\0';
+	while (control_state_str[i].str != NULL) {
+		if (MLines & control_state_str[i].state) {
+			if (s[0] != '\0') {
+				strcat(s, ", ");
+			}
+			strcat(s, control_state_str[i].str);
+		}
+		i++;
+	}
+	return s;
+}
+#endif
+
+static int
+rs_break(struct tty_struct *tty, int break_state)
+{
+	struct e100_serial *info = (struct e100_serial *)tty->driver_data;
+	unsigned long flags;
+
+	if (!info->ioport)
+		return -EIO;
+
+	local_irq_save(flags);
+	if (break_state == -1) {
+		/* Go to manual mode and set the txd pin to 0 */
+		/* Clear bit 7 (txd) and 6 (tr_enable) */
+		info->tx_ctrl &= 0x3F;
+	} else {
+		/* Set bit 7 (txd) and 6 (tr_enable) */
+		info->tx_ctrl |= (0x80 | 0x40);
+	}
+	info->ioport[REG_TR_CTRL] = info->tx_ctrl;
+	local_irq_restore(flags);
+	return 0;
+}
+
+static int
+rs_tiocmset(struct tty_struct *tty, unsigned int set, unsigned int clear)
+{
+	struct e100_serial *info = (struct e100_serial *)tty->driver_data;
+	unsigned long flags;
+
+	local_irq_save(flags);
+
+	if (clear & TIOCM_RTS)
+		e100_rts(info, 0);
+	if (clear & TIOCM_DTR)
+		e100_dtr(info, 0);
+	/* Handle FEMALE behaviour */
+	if (clear & TIOCM_RI)
+		e100_ri_out(info, 0);
+	if (clear & TIOCM_CD)
+		e100_cd_out(info, 0);
+
+	if (set & TIOCM_RTS)
+		e100_rts(info, 1);
+	if (set & TIOCM_DTR)
+		e100_dtr(info, 1);
+	/* Handle FEMALE behaviour */
+	if (set & TIOCM_RI)
+		e100_ri_out(info, 1);
+	if (set & TIOCM_CD)
+		e100_cd_out(info, 1);
+
+	local_irq_restore(flags);
+	return 0;
+}
+
+static int
+rs_tiocmget(struct tty_struct *tty)
+{
+	struct e100_serial *info = (struct e100_serial *)tty->driver_data;
+	unsigned int result;
+	unsigned long flags;
+
+	local_irq_save(flags);
+
+	result =
+		(!E100_RTS_GET(info) ? TIOCM_RTS : 0)
+		| (!E100_DTR_GET(info) ? TIOCM_DTR : 0)
+		| (!E100_RI_GET(info) ? TIOCM_RNG : 0)
+		| (!E100_DSR_GET(info) ? TIOCM_DSR : 0)
+		| (!E100_CD_GET(info) ? TIOCM_CAR : 0)
+		| (!E100_CTS_GET(info) ? TIOCM_CTS : 0);
+
+	local_irq_restore(flags);
+
+#ifdef SERIAL_DEBUG_IO
+	printk(KERN_DEBUG "ser%i: modem state: %i 0x%08X\n",
+		info->line, result, result);
+	{
+		char s[100];
+
+		get_control_state_str(result, s);
+		printk(KERN_DEBUG "state: %s\n", s);
+	}
+#endif
+	return result;
+
+}
+
+
+static int
+rs_ioctl(struct tty_struct *tty,
+	 unsigned int cmd, unsigned long arg)
+{
+	struct e100_serial * info = (struct e100_serial *)tty->driver_data;
+
+	if ((cmd != TIOCGSERIAL) && (cmd != TIOCSSERIAL) &&
+	    (cmd != TIOCSERCONFIG) && (cmd != TIOCSERGWILD)  &&
+	    (cmd != TIOCSERSWILD) && (cmd != TIOCSERGSTRUCT)) {
+		if (tty->flags & (1 << TTY_IO_ERROR))
+			return -EIO;
+	}
+
+	switch (cmd) {
+	case TIOCGSERIAL:
+		return get_serial_info(info,
+				       (struct serial_struct *) arg);
+	case TIOCSSERIAL:
+		return set_serial_info(info,
+				       (struct serial_struct *) arg);
+	case TIOCSERGETLSR: /* Get line status register */
+		return get_lsr_info(info, (unsigned int *) arg);
+
+	case TIOCSERGSTRUCT:
+		if (copy_to_user((struct e100_serial *) arg,
+				 info, sizeof(struct e100_serial)))
+			return -EFAULT;
+		return 0;
+
+#if defined(CONFIG_ETRAX_RS485)
+	case TIOCSERSETRS485:
+	{
+		/* In this ioctl we still use the old structure
+		 * rs485_control for backward compatibility
+		 * (if we use serial_rs485, then old user-level code
+		 * wouldn't work anymore...).
+		 * The use of this ioctl is deprecated: use TIOCSRS485
+		 * instead.*/
+		struct rs485_control rs485ctrl;
+		struct serial_rs485 rs485data;
+		printk(KERN_DEBUG "The use of this ioctl is deprecated. Use TIOCSRS485 instead\n");
+		if (copy_from_user(&rs485ctrl, (struct rs485_control *)arg,
+				sizeof(rs485ctrl)))
+			return -EFAULT;
+
+		rs485data.delay_rts_before_send = rs485ctrl.delay_rts_before_send;
+		rs485data.flags = 0;
+
+		if (rs485ctrl.enabled)
+			rs485data.flags |= SER_RS485_ENABLED;
+		else
+			rs485data.flags &= ~(SER_RS485_ENABLED);
+
+		if (rs485ctrl.rts_on_send)
+			rs485data.flags |= SER_RS485_RTS_ON_SEND;
+		else
+			rs485data.flags &= ~(SER_RS485_RTS_ON_SEND);
+
+		if (rs485ctrl.rts_after_sent)
+			rs485data.flags |= SER_RS485_RTS_AFTER_SEND;
+		else
+			rs485data.flags &= ~(SER_RS485_RTS_AFTER_SEND);
+
+		return e100_enable_rs485(tty, &rs485data);
+	}
+
+	case TIOCSRS485:
+	{
+		/* This is the new version of TIOCSRS485, with new
+		 * data structure serial_rs485 */
+		struct serial_rs485 rs485data;
+		if (copy_from_user(&rs485data, (struct rs485_control *)arg,
+				sizeof(rs485data)))
+			return -EFAULT;
+
+		return e100_enable_rs485(tty, &rs485data);
+	}
+
+	case TIOCGRS485:
+	{
+		struct serial_rs485 *rs485data =
+			&(((struct e100_serial *)tty->driver_data)->rs485);
+		/* This is the ioctl to get RS485 data from user-space */
+		if (copy_to_user((struct serial_rs485 *) arg,
+					rs485data,
+					sizeof(struct serial_rs485)))
+			return -EFAULT;
+		break;
+	}
+
+	case TIOCSERWRRS485:
+	{
+		struct rs485_write rs485wr;
+		if (copy_from_user(&rs485wr, (struct rs485_write *)arg,
+				sizeof(rs485wr)))
+			return -EFAULT;
+
+		return e100_write_rs485(tty, rs485wr.outc, rs485wr.outc_size);
+	}
+#endif
+
+	default:
+		return -ENOIOCTLCMD;
+	}
+	return 0;
+}
+
+static void
+rs_set_termios(struct tty_struct *tty, struct ktermios *old_termios)
+{
+	struct e100_serial *info = (struct e100_serial *)tty->driver_data;
+
+	change_speed(info);
+
+	/* Handle turning off CRTSCTS */
+	if ((old_termios->c_cflag & CRTSCTS) &&
+	    !(tty->termios->c_cflag & CRTSCTS)) {
+		tty->hw_stopped = 0;
+		rs_start(tty);
+	}
+
+}
+
+/*
+ * ------------------------------------------------------------
+ * rs_close()
+ *
+ * This routine is called when the serial port gets closed.  First, we
+ * wait for the last remaining data to be sent.  Then, we unlink its
+ * S structure from the interrupt chain if necessary, and we free
+ * that IRQ if nothing is left in the chain.
+ * ------------------------------------------------------------
+ */
+static void
+rs_close(struct tty_struct *tty, struct file * filp)
+{
+	struct e100_serial * info = (struct e100_serial *)tty->driver_data;
+	unsigned long flags;
+
+	if (!info)
+		return;
+
+	/* interrupts are disabled for this entire function */
+
+	local_irq_save(flags);
+
+	if (tty_hung_up_p(filp)) {
+		local_irq_restore(flags);
+		return;
+	}
+
+#ifdef SERIAL_DEBUG_OPEN
+	printk("[%d] rs_close ttyS%d, count = %d\n", current->pid,
+	       info->line, info->count);
+#endif
+	if ((tty->count == 1) && (info->count != 1)) {
+		/*
+		 * Uh, oh.  tty->count is 1, which means that the tty
+		 * structure will be freed.  Info->count should always
+		 * be one in these conditions.  If it's greater than
+		 * one, we've got real problems, since it means the
+		 * serial port won't be shutdown.
+		 */
+		printk(KERN_ERR
+		       "rs_close: bad serial port count; tty->count is 1, "
+		       "info->count is %d\n", info->count);
+		info->count = 1;
+	}
+	if (--info->count < 0) {
+		printk(KERN_ERR "rs_close: bad serial port count for ttyS%d: %d\n",
+		       info->line, info->count);
+		info->count = 0;
+	}
+	if (info->count) {
+		local_irq_restore(flags);
+		return;
+	}
+	info->flags |= ASYNC_CLOSING;
+	/*
+	 * Save the termios structure, since this port may have
+	 * separate termios for callout and dialin.
+	 */
+	if (info->flags & ASYNC_NORMAL_ACTIVE)
+		info->normal_termios = *tty->termios;
+	/*
+	 * Now we wait for the transmit buffer to clear; and we notify
+	 * the line discipline to only process XON/XOFF characters.
+	 */
+	tty->closing = 1;
+	if (info->closing_wait != ASYNC_CLOSING_WAIT_NONE)
+		tty_wait_until_sent(tty, info->closing_wait);
+	/*
+	 * At this point we stop accepting input.  To do this, we
+	 * disable the serial receiver and the DMA receive interrupt.
+	 */
+#ifdef SERIAL_HANDLE_EARLY_ERRORS
+	e100_disable_serial_data_irq(info);
+#endif
+
+#ifndef CONFIG_SVINTO_SIM
+	e100_disable_rx(info);
+	e100_disable_rx_irq(info);
+
+	if (info->flags & ASYNC_INITIALIZED) {
+		/*
+		 * Before we drop DTR, make sure the UART transmitter
+		 * has completely drained; this is especially
+		 * important as we have a transmit FIFO!
+		 */
+		rs_wait_until_sent(tty, HZ);
+	}
+#endif
+
+	shutdown(info);
+	rs_flush_buffer(tty);
+	tty_ldisc_flush(tty);
+	tty->closing = 0;
+	info->event = 0;
+	info->port.tty = NULL;
+	if (info->blocked_open) {
+		if (info->close_delay)
+			schedule_timeout_interruptible(info->close_delay);
+		wake_up_interruptible(&info->open_wait);
+	}
+	info->flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CLOSING);
+	wake_up_interruptible(&info->close_wait);
+	local_irq_restore(flags);
+
+	/* port closed */
+
+#if defined(CONFIG_ETRAX_RS485)
+	if (info->rs485.flags & SER_RS485_ENABLED) {
+		info->rs485.flags &= ~(SER_RS485_ENABLED);
+#if defined(CONFIG_ETRAX_RS485_ON_PA)
+		*R_PORT_PA_DATA = port_pa_data_shadow &= ~(1 << rs485_pa_bit);
+#endif
+#if defined(CONFIG_ETRAX_RS485_ON_PORT_G)
+		REG_SHADOW_SET(R_PORT_G_DATA, port_g_data_shadow,
+			       rs485_port_g_bit, 0);
+#endif
+#if defined(CONFIG_ETRAX_RS485_LTC1387)
+		REG_SHADOW_SET(R_PORT_G_DATA, port_g_data_shadow,
+			       CONFIG_ETRAX_RS485_LTC1387_DXEN_PORT_G_BIT, 0);
+		REG_SHADOW_SET(R_PORT_G_DATA, port_g_data_shadow,
+			       CONFIG_ETRAX_RS485_LTC1387_RXEN_PORT_G_BIT, 0);
+#endif
+	}
+#endif
+
+	/*
+	 * Release any allocated DMA irq's.
+	 */
+	if (info->dma_in_enabled) {
+		free_irq(info->dma_in_irq_nbr, info);
+		cris_free_dma(info->dma_in_nbr, info->dma_in_irq_description);
+		info->uses_dma_in = 0;
+#ifdef SERIAL_DEBUG_OPEN
+		printk(KERN_DEBUG "DMA irq '%s' freed\n",
+			info->dma_in_irq_description);
+#endif
+	}
+	if (info->dma_out_enabled) {
+		free_irq(info->dma_out_irq_nbr, info);
+		cris_free_dma(info->dma_out_nbr, info->dma_out_irq_description);
+		info->uses_dma_out = 0;
+#ifdef SERIAL_DEBUG_OPEN
+		printk(KERN_DEBUG "DMA irq '%s' freed\n",
+			info->dma_out_irq_description);
+#endif
+	}
+}
+
+/*
+ * rs_wait_until_sent() --- wait until the transmitter is empty
+ */
+static void rs_wait_until_sent(struct tty_struct *tty, int timeout)
+{
+	unsigned long orig_jiffies;
+	struct e100_serial *info = (struct e100_serial *)tty->driver_data;
+	unsigned long curr_time = jiffies;
+	unsigned long curr_time_usec = GET_JIFFIES_USEC();
+	long elapsed_usec =
+		(curr_time - info->last_tx_active) * (1000000/HZ) +
+		curr_time_usec - info->last_tx_active_usec;
+
+	/*
+	 * Check R_DMA_CHx_STATUS bit 0-6=number of available bytes in FIFO
+	 * R_DMA_CHx_HWSW bit 31-16=nbr of bytes left in DMA buffer (0=64k)
+	 */
+	orig_jiffies = jiffies;
+	while (info->xmit.head != info->xmit.tail || /* More in send queue */
+	       (*info->ostatusadr & 0x007f) ||  /* more in FIFO */
+	       (elapsed_usec < 2*info->char_time_usec)) {
+		schedule_timeout_interruptible(1);
+		if (signal_pending(current))
+			break;
+		if (timeout && time_after(jiffies, orig_jiffies + timeout))
+			break;
+		curr_time = jiffies;
+		curr_time_usec = GET_JIFFIES_USEC();
+		elapsed_usec =
+			(curr_time - info->last_tx_active) * (1000000/HZ) +
+			curr_time_usec - info->last_tx_active_usec;
+	}
+	set_current_state(TASK_RUNNING);
+}
+
+/*
+ * rs_hangup() --- called by tty_hangup() when a hangup is signaled.
+ */
+void
+rs_hangup(struct tty_struct *tty)
+{
+	struct e100_serial * info = (struct e100_serial *)tty->driver_data;
+
+	rs_flush_buffer(tty);
+	shutdown(info);
+	info->event = 0;
+	info->count = 0;
+	info->flags &= ~ASYNC_NORMAL_ACTIVE;
+	info->port.tty = NULL;
+	wake_up_interruptible(&info->open_wait);
+}
+
+/*
+ * ------------------------------------------------------------
+ * rs_open() and friends
+ * ------------------------------------------------------------
+ */
+static int
+block_til_ready(struct tty_struct *tty, struct file * filp,
+		struct e100_serial *info)
+{
+	DECLARE_WAITQUEUE(wait, current);
+	unsigned long	flags;
+	int		retval;
+	int		do_clocal = 0, extra_count = 0;
+
+	/*
+	 * If the device is in the middle of being closed, then block
+	 * until it's done, and then try again.
+	 */
+	if (tty_hung_up_p(filp) ||
+	    (info->flags & ASYNC_CLOSING)) {
+		wait_event_interruptible_tty(info->close_wait,
+			!(info->flags & ASYNC_CLOSING));
+#ifdef SERIAL_DO_RESTART
+		if (info->flags & ASYNC_HUP_NOTIFY)
+			return -EAGAIN;
+		else
+			return -ERESTARTSYS;
+#else
+		return -EAGAIN;
+#endif
+	}
+
+	/*
+	 * If non-blocking mode is set, or the port is not enabled,
+	 * then make the check up front and then exit.
+	 */
+	if ((filp->f_flags & O_NONBLOCK) ||
+	    (tty->flags & (1 << TTY_IO_ERROR))) {
+		info->flags |= ASYNC_NORMAL_ACTIVE;
+		return 0;
+	}
+
+	if (tty->termios->c_cflag & CLOCAL) {
+			do_clocal = 1;
+	}
+
+	/*
+	 * Block waiting for the carrier detect and the line to become
+	 * free (i.e., not in use by the callout).  While we are in
+	 * this loop, info->count is dropped by one, so that
+	 * rs_close() knows when to free things.  We restore it upon
+	 * exit, either normal or abnormal.
+	 */
+	retval = 0;
+	add_wait_queue(&info->open_wait, &wait);
+#ifdef SERIAL_DEBUG_OPEN
+	printk("block_til_ready before block: ttyS%d, count = %d\n",
+	       info->line, info->count);
+#endif
+	local_irq_save(flags);
+	if (!tty_hung_up_p(filp)) {
+		extra_count++;
+		info->count--;
+	}
+	local_irq_restore(flags);
+	info->blocked_open++;
+	while (1) {
+		local_irq_save(flags);
+		/* assert RTS and DTR */
+		e100_rts(info, 1);
+		e100_dtr(info, 1);
+		local_irq_restore(flags);
+		set_current_state(TASK_INTERRUPTIBLE);
+		if (tty_hung_up_p(filp) ||
+		    !(info->flags & ASYNC_INITIALIZED)) {
+#ifdef SERIAL_DO_RESTART
+			if (info->flags & ASYNC_HUP_NOTIFY)
+				retval = -EAGAIN;
+			else
+				retval = -ERESTARTSYS;
+#else
+			retval = -EAGAIN;
+#endif
+			break;
+		}
+		if (!(info->flags & ASYNC_CLOSING) && do_clocal)
+			/* && (do_clocal || DCD_IS_ASSERTED) */
+			break;
+		if (signal_pending(current)) {
+			retval = -ERESTARTSYS;
+			break;
+		}
+#ifdef SERIAL_DEBUG_OPEN
+		printk("block_til_ready blocking: ttyS%d, count = %d\n",
+		       info->line, info->count);
+#endif
+		tty_unlock();
+		schedule();
+		tty_lock();
+	}
+	set_current_state(TASK_RUNNING);
+	remove_wait_queue(&info->open_wait, &wait);
+	if (extra_count)
+		info->count++;
+	info->blocked_open--;
+#ifdef SERIAL_DEBUG_OPEN
+	printk("block_til_ready after blocking: ttyS%d, count = %d\n",
+	       info->line, info->count);
+#endif
+	if (retval)
+		return retval;
+	info->flags |= ASYNC_NORMAL_ACTIVE;
+	return 0;
+}
+
+static void
+deinit_port(struct e100_serial *info)
+{
+	if (info->dma_out_enabled) {
+		cris_free_dma(info->dma_out_nbr, info->dma_out_irq_description);
+		free_irq(info->dma_out_irq_nbr, info);
+	}
+	if (info->dma_in_enabled) {
+		cris_free_dma(info->dma_in_nbr, info->dma_in_irq_description);
+		free_irq(info->dma_in_irq_nbr, info);
+	}
+}
+
+/*
+ * This routine is called whenever a serial port is opened.
+ * It performs the serial-specific initialization for the tty structure.
+ */
+static int
+rs_open(struct tty_struct *tty, struct file * filp)
+{
+	struct e100_serial	*info;
+	int 			retval;
+	unsigned long           page;
+	int                     allocated_resources = 0;
+
+	info = rs_table + tty->index;
+	if (!info->enabled)
+		return -ENODEV;
+
+#ifdef SERIAL_DEBUG_OPEN
+        printk("[%d] rs_open %s, count = %d\n", current->pid, tty->name,
+ 	       info->count);
+#endif
+
+	info->count++;
+	tty->driver_data = info;
+	info->port.tty = tty;
+
+	tty->low_latency = !!(info->flags & ASYNC_LOW_LATENCY);
+
+	if (!tmp_buf) {
+		page = get_zeroed_page(GFP_KERNEL);
+		if (!page) {
+			return -ENOMEM;
+		}
+		if (tmp_buf)
+			free_page(page);
+		else
+			tmp_buf = (unsigned char *) page;
+	}
+
+	/*
+	 * If the port is in the middle of closing, bail out now
+	 */
+	if (tty_hung_up_p(filp) ||
+	    (info->flags & ASYNC_CLOSING)) {
+		wait_event_interruptible_tty(info->close_wait,
+			!(info->flags & ASYNC_CLOSING));
+#ifdef SERIAL_DO_RESTART
+		return ((info->flags & ASYNC_HUP_NOTIFY) ?
+			-EAGAIN : -ERESTARTSYS);
+#else
+		return -EAGAIN;
+#endif
+	}
+
+	/*
+	 * If DMA is enabled try to allocate the irq's.
+	 */
+	if (info->count == 1) {
+		allocated_resources = 1;
+		if (info->dma_in_enabled) {
+			if (request_irq(info->dma_in_irq_nbr,
+					rec_interrupt,
+					info->dma_in_irq_flags,
+					info->dma_in_irq_description,
+					info)) {
+				printk(KERN_WARNING "DMA irq '%s' busy; "
+					"falling back to non-DMA mode\n",
+					info->dma_in_irq_description);
+				/* Make sure we never try to use DMA in */
+				/* for the port again. */
+				info->dma_in_enabled = 0;
+			} else if (cris_request_dma(info->dma_in_nbr,
+					info->dma_in_irq_description,
+					DMA_VERBOSE_ON_ERROR,
+					info->dma_owner)) {
+				free_irq(info->dma_in_irq_nbr, info);
+				printk(KERN_WARNING "DMA '%s' busy; "
+					"falling back to non-DMA mode\n",
+					info->dma_in_irq_description);
+				/* Make sure we never try to use DMA in */
+				/* for the port again. */
+				info->dma_in_enabled = 0;
+			}
+#ifdef SERIAL_DEBUG_OPEN
+			else
+				printk(KERN_DEBUG "DMA irq '%s' allocated\n",
+					info->dma_in_irq_description);
+#endif
+		}
+		if (info->dma_out_enabled) {
+			if (request_irq(info->dma_out_irq_nbr,
+					       tr_interrupt,
+					       info->dma_out_irq_flags,
+					       info->dma_out_irq_description,
+					       info)) {
+				printk(KERN_WARNING "DMA irq '%s' busy; "
+					"falling back to non-DMA mode\n",
+					info->dma_out_irq_description);
+				/* Make sure we never try to use DMA out */
+				/* for the port again. */
+				info->dma_out_enabled = 0;
+			} else if (cris_request_dma(info->dma_out_nbr,
+					     info->dma_out_irq_description,
+					     DMA_VERBOSE_ON_ERROR,
+					     info->dma_owner)) {
+				free_irq(info->dma_out_irq_nbr, info);
+				printk(KERN_WARNING "DMA '%s' busy; "
+					"falling back to non-DMA mode\n",
+					info->dma_out_irq_description);
+				/* Make sure we never try to use DMA out */
+				/* for the port again. */
+				info->dma_out_enabled = 0;
+			}
+#ifdef SERIAL_DEBUG_OPEN
+			else
+				printk(KERN_DEBUG "DMA irq '%s' allocated\n",
+					info->dma_out_irq_description);
+#endif
+		}
+	}
+
+	/*
+	 * Start up the serial port
+	 */
+
+	retval = startup(info);
+	if (retval) {
+		if (allocated_resources)
+			deinit_port(info);
+
+		/* FIXME Decrease count info->count here too? */
+		return retval;
+	}
+
+
+	retval = block_til_ready(tty, filp, info);
+	if (retval) {
+#ifdef SERIAL_DEBUG_OPEN
+		printk("rs_open returning after block_til_ready with %d\n",
+		       retval);
+#endif
+		if (allocated_resources)
+			deinit_port(info);
+
+		return retval;
+	}
+
+	if ((info->count == 1) && (info->flags & ASYNC_SPLIT_TERMIOS)) {
+		*tty->termios = info->normal_termios;
+		change_speed(info);
+	}
+
+#ifdef SERIAL_DEBUG_OPEN
+	printk("rs_open ttyS%d successful...\n", info->line);
+#endif
+	DLOG_INT_TRIG( log_int_pos = 0);
+
+	DFLIP(	if (info->line == SERIAL_DEBUG_LINE) {
+			info->icount.rx = 0;
+		} );
+
+	return 0;
+}
+
+#ifdef CONFIG_PROC_FS
+/*
+ * /proc fs routines....
+ */
+
+static void seq_line_info(struct seq_file *m, struct e100_serial *info)
+{
+	unsigned long tmp;
+
+	seq_printf(m, "%d: uart:E100 port:%lX irq:%d",
+		   info->line, (unsigned long)info->ioport, info->irq);
+
+	if (!info->ioport || (info->type == PORT_UNKNOWN)) {
+		seq_printf(m, "\n");
+		return;
+	}
+
+	seq_printf(m, " baud:%d", info->baud);
+	seq_printf(m, " tx:%lu rx:%lu",
+		       (unsigned long)info->icount.tx,
+		       (unsigned long)info->icount.rx);
+	tmp = CIRC_CNT(info->xmit.head, info->xmit.tail, SERIAL_XMIT_SIZE);
+	if (tmp)
+		seq_printf(m, " tx_pend:%lu/%lu",
+			   (unsigned long)tmp,
+			   (unsigned long)SERIAL_XMIT_SIZE);
+
+	seq_printf(m, " rx_pend:%lu/%lu",
+		   (unsigned long)info->recv_cnt,
+		   (unsigned long)info->max_recv_cnt);
+
+#if 1
+	if (info->port.tty) {
+		if (info->port.tty->stopped)
+			seq_printf(m, " stopped:%i",
+				   (int)info->port.tty->stopped);
+		if (info->port.tty->hw_stopped)
+			seq_printf(m, " hw_stopped:%i",
+				   (int)info->port.tty->hw_stopped);
+	}
+
+	{
+		unsigned char rstat = info->ioport[REG_STATUS];
+		if (rstat & IO_MASK(R_SERIAL0_STATUS, xoff_detect))
+			seq_printf(m, " xoff_detect:1");
+	}
+
+#endif
+
+	if (info->icount.frame)
+		seq_printf(m, " fe:%lu", (unsigned long)info->icount.frame);
+
+	if (info->icount.parity)
+		seq_printf(m, " pe:%lu", (unsigned long)info->icount.parity);
+
+	if (info->icount.brk)
+		seq_printf(m, " brk:%lu", (unsigned long)info->icount.brk);
+
+	if (info->icount.overrun)
+		seq_printf(m, " oe:%lu", (unsigned long)info->icount.overrun);
+
+	/*
+	 * Last thing is the RS-232 status lines
+	 */
+	if (!E100_RTS_GET(info))
+		seq_puts(m, "|RTS");
+	if (!E100_CTS_GET(info))
+		seq_puts(m, "|CTS");
+	if (!E100_DTR_GET(info))
+		seq_puts(m, "|DTR");
+	if (!E100_DSR_GET(info))
+		seq_puts(m, "|DSR");
+	if (!E100_CD_GET(info))
+		seq_puts(m, "|CD");
+	if (!E100_RI_GET(info))
+		seq_puts(m, "|RI");
+	seq_puts(m, "\n");
+}
+
+
+static int crisv10_proc_show(struct seq_file *m, void *v)
+{
+	int i;
+
+	seq_printf(m, "serinfo:1.0 driver:%s\n", serial_version);
+
+	for (i = 0; i < NR_PORTS; i++) {
+		if (!rs_table[i].enabled)
+			continue;
+		seq_line_info(m, &rs_table[i]);
+	}
+#ifdef DEBUG_LOG_INCLUDED
+	for (i = 0; i < debug_log_pos; i++) {
+		seq_printf(m, "%-4i %lu.%lu ",
+			 i, debug_log[i].time,
+			 timer_data_to_ns(debug_log[i].timer_data));
+		seq_printf(m, debug_log[i].string, debug_log[i].value);
+	}
+	seq_printf(m, "debug_log %i/%i\n", i, DEBUG_LOG_SIZE);
+	debug_log_pos = 0;
+#endif
+	return 0;
+}
+
+static int crisv10_proc_open(struct inode *inode, struct file *file)
+{
+	return single_open(file, crisv10_proc_show, NULL);
+}
+
+static const struct file_operations crisv10_proc_fops = {
+	.owner		= THIS_MODULE,
+	.open		= crisv10_proc_open,
+	.read		= seq_read,
+	.llseek		= seq_lseek,
+	.release	= single_release,
+};
+#endif
+
+
+/* Finally, routines used to initialize the serial driver. */
+
+static void show_serial_version(void)
+{
+	printk(KERN_INFO
+	       "ETRAX 100LX serial-driver %s, "
+	       "(c) 2000-2004 Axis Communications AB\r\n",
+	       &serial_version[11]); /* "$Revision: x.yy" */
+}
+
+/* rs_init inits the driver at boot (using the module_init chain) */
+
+static const struct tty_operations rs_ops = {
+	.open = rs_open,
+	.close = rs_close,
+	.write = rs_write,
+	.flush_chars = rs_flush_chars,
+	.write_room = rs_write_room,
+	.chars_in_buffer = rs_chars_in_buffer,
+	.flush_buffer = rs_flush_buffer,
+	.ioctl = rs_ioctl,
+	.throttle = rs_throttle,
+        .unthrottle = rs_unthrottle,
+	.set_termios = rs_set_termios,
+	.stop = rs_stop,
+	.start = rs_start,
+	.hangup = rs_hangup,
+	.break_ctl = rs_break,
+	.send_xchar = rs_send_xchar,
+	.wait_until_sent = rs_wait_until_sent,
+	.tiocmget = rs_tiocmget,
+	.tiocmset = rs_tiocmset,
+#ifdef CONFIG_PROC_FS
+	.proc_fops = &crisv10_proc_fops,
+#endif
+};
+
+static int __init rs_init(void)
+{
+	int i;
+	struct e100_serial *info;
+	struct tty_driver *driver = alloc_tty_driver(NR_PORTS);
+
+	if (!driver)
+		return -ENOMEM;
+
+	show_serial_version();
+
+	/* Setup the timed flush handler system */
+
+#if !defined(CONFIG_ETRAX_SERIAL_FAST_TIMER)
+	setup_timer(&flush_timer, timed_flush_handler, 0);
+	mod_timer(&flush_timer, jiffies + 5);
+#endif
+
+#if defined(CONFIG_ETRAX_RS485)
+#if defined(CONFIG_ETRAX_RS485_ON_PA)
+	if (cris_io_interface_allocate_pins(if_serial_0, 'a', rs485_pa_bit,
+			rs485_pa_bit)) {
+		printk(KERN_ERR "ETRAX100LX serial: Could not allocate "
+			"RS485 pin\n");
+		put_tty_driver(driver);
+		return -EBUSY;
+	}
+#endif
+#if defined(CONFIG_ETRAX_RS485_ON_PORT_G)
+	if (cris_io_interface_allocate_pins(if_serial_0, 'g', rs485_pa_bit,
+			rs485_port_g_bit)) {
+		printk(KERN_ERR "ETRAX100LX serial: Could not allocate "
+			"RS485 pin\n");
+		put_tty_driver(driver);
+		return -EBUSY;
+	}
+#endif
+#endif
+
+	/* Initialize the tty_driver structure */
+
+	driver->driver_name = "serial";
+	driver->name = "ttyS";
+	driver->major = TTY_MAJOR;
+	driver->minor_start = 64;
+	driver->type = TTY_DRIVER_TYPE_SERIAL;
+	driver->subtype = SERIAL_TYPE_NORMAL;
+	driver->init_termios = tty_std_termios;
+	driver->init_termios.c_cflag =
+		B115200 | CS8 | CREAD | HUPCL | CLOCAL; /* is normally B9600 default... */
+	driver->init_termios.c_ispeed = 115200;
+	driver->init_termios.c_ospeed = 115200;
+	driver->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV;
+
+	tty_set_operations(driver, &rs_ops);
+        serial_driver = driver;
+	if (tty_register_driver(driver))
+		panic("Couldn't register serial driver\n");
+	/* do some initializing for the separate ports */
+
+	for (i = 0, info = rs_table; i < NR_PORTS; i++,info++) {
+		if (info->enabled) {
+			if (cris_request_io_interface(info->io_if,
+					info->io_if_description)) {
+				printk(KERN_ERR "ETRAX100LX async serial: "
+					"Could not allocate IO pins for "
+					"%s, port %d\n",
+					info->io_if_description, i);
+				info->enabled = 0;
+			}
+		}
+		info->uses_dma_in = 0;
+		info->uses_dma_out = 0;
+		info->line = i;
+		info->port.tty = NULL;
+		info->type = PORT_ETRAX;
+		info->tr_running = 0;
+		info->forced_eop = 0;
+		info->baud_base = DEF_BAUD_BASE;
+		info->custom_divisor = 0;
+		info->flags = 0;
+		info->close_delay = 5*HZ/10;
+		info->closing_wait = 30*HZ;
+		info->x_char = 0;
+		info->event = 0;
+		info->count = 0;
+		info->blocked_open = 0;
+		info->normal_termios = driver->init_termios;
+		init_waitqueue_head(&info->open_wait);
+		init_waitqueue_head(&info->close_wait);
+		info->xmit.buf = NULL;
+		info->xmit.tail = info->xmit.head = 0;
+		info->first_recv_buffer = info->last_recv_buffer = NULL;
+		info->recv_cnt = info->max_recv_cnt = 0;
+		info->last_tx_active_usec = 0;
+		info->last_tx_active = 0;
+
+#if defined(CONFIG_ETRAX_RS485)
+		/* Set sane defaults */
+		info->rs485.flags &= ~(SER_RS485_RTS_ON_SEND);
+		info->rs485.flags |= SER_RS485_RTS_AFTER_SEND;
+		info->rs485.delay_rts_before_send = 0;
+		info->rs485.flags &= ~(SER_RS485_ENABLED);
+#endif
+		INIT_WORK(&info->work, do_softint);
+
+		if (info->enabled) {
+			printk(KERN_INFO "%s%d at %p is a builtin UART with DMA\n",
+			       serial_driver->name, info->line, info->ioport);
+		}
+	}
+#ifdef CONFIG_ETRAX_FAST_TIMER
+#ifdef CONFIG_ETRAX_SERIAL_FAST_TIMER
+	memset(fast_timers, 0, sizeof(fast_timers));
+#endif
+#ifdef CONFIG_ETRAX_RS485
+	memset(fast_timers_rs485, 0, sizeof(fast_timers_rs485));
+#endif
+	fast_timer_init();
+#endif
+
+#ifndef CONFIG_SVINTO_SIM
+#ifndef CONFIG_ETRAX_KGDB
+	/* Not needed in simulator.  May only complicate stuff. */
+	/* hook the irq's for DMA channel 6 and 7, serial output and input, and some more... */
+
+	if (request_irq(SERIAL_IRQ_NBR, ser_interrupt,
+			IRQF_SHARED, "serial ", driver))
+		panic("%s: Failed to request irq8", __func__);
+
+#endif
+#endif /* CONFIG_SVINTO_SIM */
+
+	return 0;
+}
+
+/* this makes sure that rs_init is called during kernel boot */
+
+module_init(rs_init);
diff --git a/ap/os/linux/linux-3.4.x/drivers/tty/serial/crisv10.h b/ap/os/linux/linux-3.4.x/drivers/tty/serial/crisv10.h
new file mode 100644
index 0000000..ea0beb4
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/tty/serial/crisv10.h
@@ -0,0 +1,147 @@
+/*
+ * serial.h: Arch-dep definitions for the Etrax100 serial driver.
+ *
+ * Copyright (C) 1998-2007 Axis Communications AB
+ */
+
+#ifndef _ETRAX_SERIAL_H
+#define _ETRAX_SERIAL_H
+
+#include <linux/circ_buf.h>
+#include <asm/termios.h>
+#include <asm/dma.h>
+#include <arch/io_interface_mux.h>
+
+/* Software state per channel */
+
+#ifdef __KERNEL__
+/*
+ * This is our internal structure for each serial port's state.
+ *
+ * Many fields are paralleled by the structure used by the serial_struct
+ * structure.
+ *
+ * For definitions of the flags field, see tty.h
+ */
+
+#define SERIAL_RECV_DESCRIPTORS 8
+
+struct etrax_recv_buffer {
+	struct etrax_recv_buffer *next;
+	unsigned short length;
+	unsigned char error;
+	unsigned char pad;
+
+	unsigned char buffer[0];
+};
+
+struct e100_serial {
+	struct tty_port port;
+	int baud;
+	volatile u8	*ioport;	/* R_SERIALx_CTRL */
+	u32		irq;	/* bitnr in R_IRQ_MASK2 for dmaX_descr */
+
+	/* Output registers */
+	volatile u8 *oclrintradr;	/* adr to R_DMA_CHx_CLR_INTR */
+	volatile u32 *ofirstadr;	/* adr to R_DMA_CHx_FIRST */
+	volatile u8 *ocmdadr;		/* adr to R_DMA_CHx_CMD */
+	const volatile u8 *ostatusadr;	/* adr to R_DMA_CHx_STATUS */
+
+	/* Input registers */
+	volatile u8 *iclrintradr;	/* adr to R_DMA_CHx_CLR_INTR */
+	volatile u32 *ifirstadr;	/* adr to R_DMA_CHx_FIRST */
+	volatile u8 *icmdadr;		/* adr to R_DMA_CHx_CMD */
+	volatile u32 *idescradr;	/* adr to R_DMA_CHx_DESCR */
+
+	int flags;	/* defined in tty.h */
+
+	u8 rx_ctrl;	/* shadow for R_SERIALx_REC_CTRL */
+	u8 tx_ctrl;	/* shadow for R_SERIALx_TR_CTRL */
+	u8 iseteop;	/* bit number for R_SET_EOP for the input dma */
+	int enabled;	/* Set to 1 if the port is enabled in HW config */
+
+	u8 dma_out_enabled;	/* Set to 1 if DMA should be used */
+	u8 dma_in_enabled;	/* Set to 1 if DMA should be used */
+
+	/* end of fields defined in rs_table[] in .c-file */
+	int		dma_owner;
+	unsigned int	dma_in_nbr;
+	unsigned int	dma_out_nbr;
+	unsigned int	dma_in_irq_nbr;
+	unsigned int	dma_out_irq_nbr;
+	unsigned long	dma_in_irq_flags;
+	unsigned long	dma_out_irq_flags;
+	char		*dma_in_irq_description;
+	char		*dma_out_irq_description;
+
+	enum cris_io_interface io_if;
+	char            *io_if_description;
+
+	u8		uses_dma_in;  /* Set to 1 if DMA is used */
+	u8		uses_dma_out; /* Set to 1 if DMA is used */
+	u8		forced_eop;   /* a fifo eop has been forced */
+	int			baud_base;     /* For special baudrates */
+	int			custom_divisor; /* For special baudrates */
+	struct etrax_dma_descr	tr_descr;
+	struct etrax_dma_descr	rec_descr[SERIAL_RECV_DESCRIPTORS];
+	int			cur_rec_descr;
+
+	volatile int		tr_running; /* 1 if output is running */
+
+	struct tty_struct	*tty;
+	int			read_status_mask;
+	int			ignore_status_mask;
+	int			x_char;	/* xon/xoff character */
+	int			close_delay;
+	unsigned short		closing_wait;
+	unsigned short		closing_wait2;
+	unsigned long		event;
+	unsigned long		last_active;
+	int			line;
+	int			type;  /* PORT_ETRAX */
+	int			count;	    /* # of fd on device */
+	int			blocked_open; /* # of blocked opens */
+	struct circ_buf		xmit;
+	struct etrax_recv_buffer *first_recv_buffer;
+	struct etrax_recv_buffer *last_recv_buffer;
+	unsigned int		recv_cnt;
+	unsigned int		max_recv_cnt;
+
+	struct work_struct	work;
+	struct async_icount	icount;   /* error-statistics etc.*/
+	struct ktermios		normal_termios;
+	struct ktermios		callout_termios;
+	wait_queue_head_t	open_wait;
+	wait_queue_head_t	close_wait;
+
+	unsigned long char_time_usec;       /* The time for 1 char, in usecs */
+	unsigned long flush_time_usec;      /* How often we should flush */
+	unsigned long last_tx_active_usec;  /* Last tx usec in the jiffies */
+	unsigned long last_tx_active;       /* Last tx time in jiffies */
+	unsigned long last_rx_active_usec;  /* Last rx usec in the jiffies */
+	unsigned long last_rx_active;       /* Last rx time in jiffies */
+
+	int break_detected_cnt;
+	int errorcode;
+
+#ifdef CONFIG_ETRAX_RS485
+	struct serial_rs485	rs485;  /* RS-485 support */
+#endif
+};
+
+/* this PORT is not in the standard serial.h. it's not actually used for
+ * anything since we only have one type of async serial-port anyway in this
+ * system.
+ */
+
+#define PORT_ETRAX 1
+
+/*
+ * Events are used to schedule things to happen at timer-interrupt
+ * time, instead of at rs interrupt time.
+ */
+#define RS_EVENT_WRITE_WAKEUP	0
+
+#endif /* __KERNEL__ */
+
+#endif /* !_ETRAX_SERIAL_H */
diff --git a/ap/os/linux/linux-3.4.x/drivers/tty/serial/dz.c b/ap/os/linux/linux-3.4.x/drivers/tty/serial/dz.c
new file mode 100644
index 0000000..6491b86
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/tty/serial/dz.c
@@ -0,0 +1,955 @@
+/*
+ * dz.c: Serial port driver for DECstations equipped
+ *       with the DZ chipset.
+ *
+ * Copyright (C) 1998 Olivier A. D. Lebaillif
+ *
+ * Email: olivier.lebaillif@ifrsys.com
+ *
+ * Copyright (C) 2004, 2006, 2007  Maciej W. Rozycki
+ *
+ * [31-AUG-98] triemer
+ * Changed IRQ to use Harald's dec internals interrupts.h
+ * removed base_addr code - moving address assignment to setup.c
+ * Changed name of dz_init to rs_init to be consistent with tc code
+ * [13-NOV-98] triemer fixed code to receive characters
+ *    after patches by harald to irq code.
+ * [09-JAN-99] triemer minor fix for schedule - due to removal of timeout
+ *            field from "current" - somewhere between 2.1.121 and 2.1.131
+ Qua Jun 27 15:02:26 BRT 2001
+ * [27-JUN-2001] Arnaldo Carvalho de Melo <acme@conectiva.com.br> - cleanups
+ *
+ * Parts (C) 1999 David Airlie, airlied@linux.ie
+ * [07-SEP-99] Bugfixes
+ *
+ * [06-Jan-2002] Russell King <rmk@arm.linux.org.uk>
+ * Converted to new serial core
+ */
+
+#undef DEBUG_DZ
+
+#if defined(CONFIG_SERIAL_DZ_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ)
+#define SUPPORT_SYSRQ
+#endif
+
+#include <linux/bitops.h>
+#include <linux/compiler.h>
+#include <linux/console.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/ioport.h>
+#include <linux/kernel.h>
+#include <linux/major.h>
+#include <linux/module.h>
+#include <linux/serial.h>
+#include <linux/serial_core.h>
+#include <linux/sysrq.h>
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
+
+#include <linux/atomic.h>
+#include <asm/bootinfo.h>
+#include <asm/io.h>
+
+#include <asm/dec/interrupts.h>
+#include <asm/dec/kn01.h>
+#include <asm/dec/kn02.h>
+#include <asm/dec/machtype.h>
+#include <asm/dec/prom.h>
+#include <asm/dec/system.h>
+
+#include "dz.h"
+
+
+MODULE_DESCRIPTION("DECstation DZ serial driver");
+MODULE_LICENSE("GPL");
+
+
+static char dz_name[] __initdata = "DECstation DZ serial driver version ";
+static char dz_version[] __initdata = "1.04";
+
+struct dz_port {
+	struct dz_mux		*mux;
+	struct uart_port	port;
+	unsigned int		cflag;
+};
+
+struct dz_mux {
+	struct dz_port		dport[DZ_NB_PORT];
+	atomic_t		map_guard;
+	atomic_t		irq_guard;
+	int			initialised;
+};
+
+static struct dz_mux dz_mux;
+
+static inline struct dz_port *to_dport(struct uart_port *uport)
+{
+	return container_of(uport, struct dz_port, port);
+}
+
+/*
+ * ------------------------------------------------------------
+ * dz_in () and dz_out ()
+ *
+ * These routines are used to access the registers of the DZ
+ * chip, hiding relocation differences between implementation.
+ * ------------------------------------------------------------
+ */
+
+static u16 dz_in(struct dz_port *dport, unsigned offset)
+{
+	void __iomem *addr = dport->port.membase + offset;
+
+	return readw(addr);
+}
+
+static void dz_out(struct dz_port *dport, unsigned offset, u16 value)
+{
+	void __iomem *addr = dport->port.membase + offset;
+
+	writew(value, addr);
+}
+
+/*
+ * ------------------------------------------------------------
+ * rs_stop () and rs_start ()
+ *
+ * These routines are called before setting or resetting
+ * tty->stopped. They enable or disable transmitter interrupts,
+ * as necessary.
+ * ------------------------------------------------------------
+ */
+
+static void dz_stop_tx(struct uart_port *uport)
+{
+	struct dz_port *dport = to_dport(uport);
+	u16 tmp, mask = 1 << dport->port.line;
+
+	tmp = dz_in(dport, DZ_TCR);	/* read the TX flag */
+	tmp &= ~mask;			/* clear the TX flag */
+	dz_out(dport, DZ_TCR, tmp);
+}
+
+static void dz_start_tx(struct uart_port *uport)
+{
+	struct dz_port *dport = to_dport(uport);
+	u16 tmp, mask = 1 << dport->port.line;
+
+	tmp = dz_in(dport, DZ_TCR);	/* read the TX flag */
+	tmp |= mask;			/* set the TX flag */
+	dz_out(dport, DZ_TCR, tmp);
+}
+
+static void dz_stop_rx(struct uart_port *uport)
+{
+	struct dz_port *dport = to_dport(uport);
+
+	dport->cflag &= ~DZ_RXENAB;
+	dz_out(dport, DZ_LPR, dport->cflag);
+}
+
+static void dz_enable_ms(struct uart_port *uport)
+{
+	/* nothing to do */
+}
+
+/*
+ * ------------------------------------------------------------
+ *
+ * Here start the interrupt handling routines.  All of the following
+ * subroutines are declared as inline and are folded into
+ * dz_interrupt.  They were separated out for readability's sake.
+ *
+ * Note: dz_interrupt() is a "fast" interrupt, which means that it
+ * runs with interrupts turned off.  People who may want to modify
+ * dz_interrupt() should try to keep the interrupt handler as fast as
+ * possible.  After you are done making modifications, it is not a bad
+ * idea to do:
+ *
+ *	make drivers/serial/dz.s
+ *
+ * and look at the resulting assemble code in dz.s.
+ *
+ * ------------------------------------------------------------
+ */
+
+/*
+ * ------------------------------------------------------------
+ * receive_char ()
+ *
+ * This routine deals with inputs from any lines.
+ * ------------------------------------------------------------
+ */
+static inline void dz_receive_chars(struct dz_mux *mux)
+{
+	struct uart_port *uport;
+	struct dz_port *dport = &mux->dport[0];
+	struct tty_struct *tty = NULL;
+	struct uart_icount *icount;
+	int lines_rx[DZ_NB_PORT] = { [0 ... DZ_NB_PORT - 1] = 0 };
+	unsigned char ch, flag;
+	u16 status;
+	int i;
+
+	while ((status = dz_in(dport, DZ_RBUF)) & DZ_DVAL) {
+		dport = &mux->dport[LINE(status)];
+		uport = &dport->port;
+		tty = uport->state->port.tty;	/* point to the proper dev */
+
+		ch = UCHAR(status);		/* grab the char */
+		flag = TTY_NORMAL;
+
+		icount = &uport->icount;
+		icount->rx++;
+
+		if (unlikely(status & (DZ_OERR | DZ_FERR | DZ_PERR))) {
+
+			/*
+			 * There is no separate BREAK status bit, so treat
+			 * null characters with framing errors as BREAKs;
+			 * normally, otherwise.  For this move the Framing
+			 * Error bit to a simulated BREAK bit.
+			 */
+			if (!ch) {
+				status |= (status & DZ_FERR) >>
+					  (ffs(DZ_FERR) - ffs(DZ_BREAK));
+				status &= ~DZ_FERR;
+			}
+
+			/* Handle SysRq/SAK & keep track of the statistics. */
+			if (status & DZ_BREAK) {
+				icount->brk++;
+				if (uart_handle_break(uport))
+					continue;
+			} else if (status & DZ_FERR)
+				icount->frame++;
+			else if (status & DZ_PERR)
+				icount->parity++;
+			if (status & DZ_OERR)
+				icount->overrun++;
+
+			status &= uport->read_status_mask;
+			if (status & DZ_BREAK)
+				flag = TTY_BREAK;
+			else if (status & DZ_FERR)
+				flag = TTY_FRAME;
+			else if (status & DZ_PERR)
+				flag = TTY_PARITY;
+
+		}
+
+		if (uart_handle_sysrq_char(uport, ch))
+			continue;
+
+		uart_insert_char(uport, status, DZ_OERR, ch, flag);
+		lines_rx[LINE(status)] = 1;
+	}
+	for (i = 0; i < DZ_NB_PORT; i++)
+		if (lines_rx[i])
+			tty_flip_buffer_push(mux->dport[i].port.state->port.tty);
+}
+
+/*
+ * ------------------------------------------------------------
+ * transmit_char ()
+ *
+ * This routine deals with outputs to any lines.
+ * ------------------------------------------------------------
+ */
+static inline void dz_transmit_chars(struct dz_mux *mux)
+{
+	struct dz_port *dport = &mux->dport[0];
+	struct circ_buf *xmit;
+	unsigned char tmp;
+	u16 status;
+
+	status = dz_in(dport, DZ_CSR);
+	dport = &mux->dport[LINE(status)];
+	xmit = &dport->port.state->xmit;
+
+	if (dport->port.x_char) {		/* XON/XOFF chars */
+		dz_out(dport, DZ_TDR, dport->port.x_char);
+		dport->port.icount.tx++;
+		dport->port.x_char = 0;
+		return;
+	}
+	/* If nothing to do or stopped or hardware stopped. */
+	if (uart_circ_empty(xmit) || uart_tx_stopped(&dport->port)) {
+		spin_lock(&dport->port.lock);
+		dz_stop_tx(&dport->port);
+		spin_unlock(&dport->port.lock);
+		return;
+	}
+
+	/*
+	 * If something to do... (remember the dz has no output fifo,
+	 * so we go one char at a time) :-<
+	 */
+	tmp = xmit->buf[xmit->tail];
+	xmit->tail = (xmit->tail + 1) & (DZ_XMIT_SIZE - 1);
+	dz_out(dport, DZ_TDR, tmp);
+	dport->port.icount.tx++;
+
+	if (uart_circ_chars_pending(xmit) < DZ_WAKEUP_CHARS)
+		uart_write_wakeup(&dport->port);
+
+	/* Are we are done. */
+	if (uart_circ_empty(xmit)) {
+		spin_lock(&dport->port.lock);
+		dz_stop_tx(&dport->port);
+		spin_unlock(&dport->port.lock);
+	}
+}
+
+/*
+ * ------------------------------------------------------------
+ * check_modem_status()
+ *
+ * DS 3100 & 5100: Only valid for the MODEM line, duh!
+ * DS 5000/200: Valid for the MODEM and PRINTER line.
+ * ------------------------------------------------------------
+ */
+static inline void check_modem_status(struct dz_port *dport)
+{
+	/*
+	 * FIXME:
+	 * 1. No status change interrupt; use a timer.
+	 * 2. Handle the 3100/5000 as appropriate. --macro
+	 */
+	u16 status;
+
+	/* If not the modem line just return.  */
+	if (dport->port.line != DZ_MODEM)
+		return;
+
+	status = dz_in(dport, DZ_MSR);
+
+	/* it's easy, since DSR2 is the only bit in the register */
+	if (status)
+		dport->port.icount.dsr++;
+}
+
+/*
+ * ------------------------------------------------------------
+ * dz_interrupt ()
+ *
+ * this is the main interrupt routine for the DZ chip.
+ * It deals with the multiple ports.
+ * ------------------------------------------------------------
+ */
+static irqreturn_t dz_interrupt(int irq, void *dev_id)
+{
+	struct dz_mux *mux = dev_id;
+	struct dz_port *dport = &mux->dport[0];
+	u16 status;
+
+	/* get the reason why we just got an irq */
+	status = dz_in(dport, DZ_CSR);
+
+	if ((status & (DZ_RDONE | DZ_RIE)) == (DZ_RDONE | DZ_RIE))
+		dz_receive_chars(mux);
+
+	if ((status & (DZ_TRDY | DZ_TIE)) == (DZ_TRDY | DZ_TIE))
+		dz_transmit_chars(mux);
+
+	return IRQ_HANDLED;
+}
+
+/*
+ * -------------------------------------------------------------------
+ * Here ends the DZ interrupt routines.
+ * -------------------------------------------------------------------
+ */
+
+static unsigned int dz_get_mctrl(struct uart_port *uport)
+{
+	/*
+	 * FIXME: Handle the 3100/5000 as appropriate. --macro
+	 */
+	struct dz_port *dport = to_dport(uport);
+	unsigned int mctrl = TIOCM_CAR | TIOCM_DSR | TIOCM_CTS;
+
+	if (dport->port.line == DZ_MODEM) {
+		if (dz_in(dport, DZ_MSR) & DZ_MODEM_DSR)
+			mctrl &= ~TIOCM_DSR;
+	}
+
+	return mctrl;
+}
+
+static void dz_set_mctrl(struct uart_port *uport, unsigned int mctrl)
+{
+	/*
+	 * FIXME: Handle the 3100/5000 as appropriate. --macro
+	 */
+	struct dz_port *dport = to_dport(uport);
+	u16 tmp;
+
+	if (dport->port.line == DZ_MODEM) {
+		tmp = dz_in(dport, DZ_TCR);
+		if (mctrl & TIOCM_DTR)
+			tmp &= ~DZ_MODEM_DTR;
+		else
+			tmp |= DZ_MODEM_DTR;
+		dz_out(dport, DZ_TCR, tmp);
+	}
+}
+
+/*
+ * -------------------------------------------------------------------
+ * startup ()
+ *
+ * various initialization tasks
+ * -------------------------------------------------------------------
+ */
+static int dz_startup(struct uart_port *uport)
+{
+	struct dz_port *dport = to_dport(uport);
+	struct dz_mux *mux = dport->mux;
+	unsigned long flags;
+	int irq_guard;
+	int ret;
+	u16 tmp;
+
+	irq_guard = atomic_add_return(1, &mux->irq_guard);
+	if (irq_guard != 1)
+		return 0;
+
+	ret = request_irq(dport->port.irq, dz_interrupt,
+			  IRQF_SHARED, "dz", mux);
+	if (ret) {
+		atomic_add(-1, &mux->irq_guard);
+		printk(KERN_ERR "dz: Cannot get IRQ %d!\n", dport->port.irq);
+		return ret;
+	}
+
+	spin_lock_irqsave(&dport->port.lock, flags);
+
+	/* Enable interrupts.  */
+	tmp = dz_in(dport, DZ_CSR);
+	tmp |= DZ_RIE | DZ_TIE;
+	dz_out(dport, DZ_CSR, tmp);
+
+	spin_unlock_irqrestore(&dport->port.lock, flags);
+
+	return 0;
+}
+
+/*
+ * -------------------------------------------------------------------
+ * shutdown ()
+ *
+ * This routine will shutdown a serial port; interrupts are disabled, and
+ * DTR is dropped if the hangup on close termio flag is on.
+ * -------------------------------------------------------------------
+ */
+static void dz_shutdown(struct uart_port *uport)
+{
+	struct dz_port *dport = to_dport(uport);
+	struct dz_mux *mux = dport->mux;
+	unsigned long flags;
+	int irq_guard;
+	u16 tmp;
+
+	spin_lock_irqsave(&dport->port.lock, flags);
+	dz_stop_tx(&dport->port);
+	spin_unlock_irqrestore(&dport->port.lock, flags);
+
+	irq_guard = atomic_add_return(-1, &mux->irq_guard);
+	if (!irq_guard) {
+		/* Disable interrupts.  */
+		tmp = dz_in(dport, DZ_CSR);
+		tmp &= ~(DZ_RIE | DZ_TIE);
+		dz_out(dport, DZ_CSR, tmp);
+
+		free_irq(dport->port.irq, mux);
+	}
+}
+
+/*
+ * -------------------------------------------------------------------
+ * dz_tx_empty() -- get the transmitter empty status
+ *
+ * Purpose: Let user call ioctl() to get info when the UART physically
+ *          is emptied.  On bus types like RS485, the transmitter must
+ *          release the bus after transmitting. This must be done when
+ *          the transmit shift register is empty, not be done when the
+ *          transmit holding register is empty.  This functionality
+ *          allows an RS485 driver to be written in user space.
+ * -------------------------------------------------------------------
+ */
+static unsigned int dz_tx_empty(struct uart_port *uport)
+{
+	struct dz_port *dport = to_dport(uport);
+	unsigned short tmp, mask = 1 << dport->port.line;
+
+	tmp = dz_in(dport, DZ_TCR);
+	tmp &= mask;
+
+	return tmp ? 0 : TIOCSER_TEMT;
+}
+
+static void dz_break_ctl(struct uart_port *uport, int break_state)
+{
+	/*
+	 * FIXME: Can't access BREAK bits in TDR easily;
+	 * reuse the code for polled TX. --macro
+	 */
+	struct dz_port *dport = to_dport(uport);
+	unsigned long flags;
+	unsigned short tmp, mask = 1 << dport->port.line;
+
+	spin_lock_irqsave(&uport->lock, flags);
+	tmp = dz_in(dport, DZ_TCR);
+	if (break_state)
+		tmp |= mask;
+	else
+		tmp &= ~mask;
+	dz_out(dport, DZ_TCR, tmp);
+	spin_unlock_irqrestore(&uport->lock, flags);
+}
+
+static int dz_encode_baud_rate(unsigned int baud)
+{
+	switch (baud) {
+	case 50:
+		return DZ_B50;
+	case 75:
+		return DZ_B75;
+	case 110:
+		return DZ_B110;
+	case 134:
+		return DZ_B134;
+	case 150:
+		return DZ_B150;
+	case 300:
+		return DZ_B300;
+	case 600:
+		return DZ_B600;
+	case 1200:
+		return DZ_B1200;
+	case 1800:
+		return DZ_B1800;
+	case 2000:
+		return DZ_B2000;
+	case 2400:
+		return DZ_B2400;
+	case 3600:
+		return DZ_B3600;
+	case 4800:
+		return DZ_B4800;
+	case 7200:
+		return DZ_B7200;
+	case 9600:
+		return DZ_B9600;
+	default:
+		return -1;
+	}
+}
+
+
+static void dz_reset(struct dz_port *dport)
+{
+	struct dz_mux *mux = dport->mux;
+
+	if (mux->initialised)
+		return;
+
+	dz_out(dport, DZ_CSR, DZ_CLR);
+	while (dz_in(dport, DZ_CSR) & DZ_CLR);
+	iob();
+
+	/* Enable scanning.  */
+	dz_out(dport, DZ_CSR, DZ_MSE);
+
+	mux->initialised = 1;
+}
+
+static void dz_set_termios(struct uart_port *uport, struct ktermios *termios,
+			   struct ktermios *old_termios)
+{
+	struct dz_port *dport = to_dport(uport);
+	unsigned long flags;
+	unsigned int cflag, baud;
+	int bflag;
+
+	cflag = dport->port.line;
+
+	switch (termios->c_cflag & CSIZE) {
+	case CS5:
+		cflag |= DZ_CS5;
+		break;
+	case CS6:
+		cflag |= DZ_CS6;
+		break;
+	case CS7:
+		cflag |= DZ_CS7;
+		break;
+	case CS8:
+	default:
+		cflag |= DZ_CS8;
+	}
+
+	if (termios->c_cflag & CSTOPB)
+		cflag |= DZ_CSTOPB;
+	if (termios->c_cflag & PARENB)
+		cflag |= DZ_PARENB;
+	if (termios->c_cflag & PARODD)
+		cflag |= DZ_PARODD;
+
+	baud = uart_get_baud_rate(uport, termios, old_termios, 50, 9600);
+	bflag = dz_encode_baud_rate(baud);
+	if (bflag < 0)	{			/* Try to keep unchanged.  */
+		baud = uart_get_baud_rate(uport, old_termios, NULL, 50, 9600);
+		bflag = dz_encode_baud_rate(baud);
+		if (bflag < 0)	{		/* Resort to 9600.  */
+			baud = 9600;
+			bflag = DZ_B9600;
+		}
+		tty_termios_encode_baud_rate(termios, baud, baud);
+	}
+	cflag |= bflag;
+
+	if (termios->c_cflag & CREAD)
+		cflag |= DZ_RXENAB;
+
+	spin_lock_irqsave(&dport->port.lock, flags);
+
+	uart_update_timeout(uport, termios->c_cflag, baud);
+
+	dz_out(dport, DZ_LPR, cflag);
+	dport->cflag = cflag;
+
+	/* setup accept flag */
+	dport->port.read_status_mask = DZ_OERR;
+	if (termios->c_iflag & INPCK)
+		dport->port.read_status_mask |= DZ_FERR | DZ_PERR;
+	if (termios->c_iflag & (BRKINT | PARMRK))
+		dport->port.read_status_mask |= DZ_BREAK;
+
+	/* characters to ignore */
+	uport->ignore_status_mask = 0;
+	if ((termios->c_iflag & (IGNPAR | IGNBRK)) == (IGNPAR | IGNBRK))
+		dport->port.ignore_status_mask |= DZ_OERR;
+	if (termios->c_iflag & IGNPAR)
+		dport->port.ignore_status_mask |= DZ_FERR | DZ_PERR;
+	if (termios->c_iflag & IGNBRK)
+		dport->port.ignore_status_mask |= DZ_BREAK;
+
+	spin_unlock_irqrestore(&dport->port.lock, flags);
+}
+
+/*
+ * Hack alert!
+ * Required solely so that the initial PROM-based console
+ * works undisturbed in parallel with this one.
+ */
+static void dz_pm(struct uart_port *uport, unsigned int state,
+		  unsigned int oldstate)
+{
+	struct dz_port *dport = to_dport(uport);
+	unsigned long flags;
+
+	spin_lock_irqsave(&dport->port.lock, flags);
+	if (state < 3)
+		dz_start_tx(&dport->port);
+	else
+		dz_stop_tx(&dport->port);
+	spin_unlock_irqrestore(&dport->port.lock, flags);
+}
+
+
+static const char *dz_type(struct uart_port *uport)
+{
+	return "DZ";
+}
+
+static void dz_release_port(struct uart_port *uport)
+{
+	struct dz_mux *mux = to_dport(uport)->mux;
+	int map_guard;
+
+	iounmap(uport->membase);
+	uport->membase = NULL;
+
+	map_guard = atomic_add_return(-1, &mux->map_guard);
+	if (!map_guard)
+		release_mem_region(uport->mapbase, dec_kn_slot_size);
+}
+
+static int dz_map_port(struct uart_port *uport)
+{
+	if (!uport->membase)
+		uport->membase = ioremap_nocache(uport->mapbase,
+						 dec_kn_slot_size);
+	if (!uport->membase) {
+		printk(KERN_ERR "dz: Cannot map MMIO\n");
+		return -ENOMEM;
+	}
+	return 0;
+}
+
+static int dz_request_port(struct uart_port *uport)
+{
+	struct dz_mux *mux = to_dport(uport)->mux;
+	int map_guard;
+	int ret;
+
+	map_guard = atomic_add_return(1, &mux->map_guard);
+	if (map_guard == 1) {
+		if (!request_mem_region(uport->mapbase, dec_kn_slot_size,
+					"dz")) {
+			atomic_add(-1, &mux->map_guard);
+			printk(KERN_ERR
+			       "dz: Unable to reserve MMIO resource\n");
+			return -EBUSY;
+		}
+	}
+	ret = dz_map_port(uport);
+	if (ret) {
+		map_guard = atomic_add_return(-1, &mux->map_guard);
+		if (!map_guard)
+			release_mem_region(uport->mapbase, dec_kn_slot_size);
+		return ret;
+	}
+	return 0;
+}
+
+static void dz_config_port(struct uart_port *uport, int flags)
+{
+	struct dz_port *dport = to_dport(uport);
+
+	if (flags & UART_CONFIG_TYPE) {
+		if (dz_request_port(uport))
+			return;
+
+		uport->type = PORT_DZ;
+
+		dz_reset(dport);
+	}
+}
+
+/*
+ * Verify the new serial_struct (for TIOCSSERIAL).
+ */
+static int dz_verify_port(struct uart_port *uport, struct serial_struct *ser)
+{
+	int ret = 0;
+
+	if (ser->type != PORT_UNKNOWN && ser->type != PORT_DZ)
+		ret = -EINVAL;
+	if (ser->irq != uport->irq)
+		ret = -EINVAL;
+	return ret;
+}
+
+static struct uart_ops dz_ops = {
+	.tx_empty	= dz_tx_empty,
+	.get_mctrl	= dz_get_mctrl,
+	.set_mctrl	= dz_set_mctrl,
+	.stop_tx	= dz_stop_tx,
+	.start_tx	= dz_start_tx,
+	.stop_rx	= dz_stop_rx,
+	.enable_ms	= dz_enable_ms,
+	.break_ctl	= dz_break_ctl,
+	.startup	= dz_startup,
+	.shutdown	= dz_shutdown,
+	.set_termios	= dz_set_termios,
+	.pm		= dz_pm,
+	.type		= dz_type,
+	.release_port	= dz_release_port,
+	.request_port	= dz_request_port,
+	.config_port	= dz_config_port,
+	.verify_port	= dz_verify_port,
+};
+
+static void __init dz_init_ports(void)
+{
+	static int first = 1;
+	unsigned long base;
+	int line;
+
+	if (!first)
+		return;
+	first = 0;
+
+	if (mips_machtype == MACH_DS23100 || mips_machtype == MACH_DS5100)
+		base = dec_kn_slot_base + KN01_DZ11;
+	else
+		base = dec_kn_slot_base + KN02_DZ11;
+
+	for (line = 0; line < DZ_NB_PORT; line++) {
+		struct dz_port *dport = &dz_mux.dport[line];
+		struct uart_port *uport = &dport->port;
+
+		dport->mux	= &dz_mux;
+
+		uport->irq	= dec_interrupt[DEC_IRQ_DZ11];
+		uport->fifosize	= 1;
+		uport->iotype	= UPIO_MEM;
+		uport->flags	= UPF_BOOT_AUTOCONF;
+		uport->ops	= &dz_ops;
+		uport->line	= line;
+		uport->mapbase	= base;
+	}
+}
+
+#ifdef CONFIG_SERIAL_DZ_CONSOLE
+/*
+ * -------------------------------------------------------------------
+ * dz_console_putchar() -- transmit a character
+ *
+ * Polled transmission.  This is tricky.  We need to mask transmit
+ * interrupts so that they do not interfere, enable the transmitter
+ * for the line requested and then wait till the transmit scanner
+ * requests data for this line.  But it may request data for another
+ * line first, in which case we have to disable its transmitter and
+ * repeat waiting till our line pops up.  Only then the character may
+ * be transmitted.  Finally, the state of the transmitter mask is
+ * restored.  Welcome to the world of PDP-11!
+ * -------------------------------------------------------------------
+ */
+static void dz_console_putchar(struct uart_port *uport, int ch)
+{
+	struct dz_port *dport = to_dport(uport);
+	unsigned long flags;
+	unsigned short csr, tcr, trdy, mask;
+	int loops = 10000;
+
+	spin_lock_irqsave(&dport->port.lock, flags);
+	csr = dz_in(dport, DZ_CSR);
+	dz_out(dport, DZ_CSR, csr & ~DZ_TIE);
+	tcr = dz_in(dport, DZ_TCR);
+	tcr |= 1 << dport->port.line;
+	mask = tcr;
+	dz_out(dport, DZ_TCR, mask);
+	iob();
+	spin_unlock_irqrestore(&dport->port.lock, flags);
+
+	do {
+		trdy = dz_in(dport, DZ_CSR);
+		if (!(trdy & DZ_TRDY))
+			continue;
+		trdy = (trdy & DZ_TLINE) >> 8;
+		if (trdy == dport->port.line)
+			break;
+		mask &= ~(1 << trdy);
+		dz_out(dport, DZ_TCR, mask);
+		iob();
+		udelay(2);
+	} while (--loops);
+
+	if (loops)				/* Cannot send otherwise. */
+		dz_out(dport, DZ_TDR, ch);
+
+	dz_out(dport, DZ_TCR, tcr);
+	dz_out(dport, DZ_CSR, csr);
+}
+
+/*
+ * -------------------------------------------------------------------
+ * dz_console_print ()
+ *
+ * dz_console_print is registered for printk.
+ * The console must be locked when we get here.
+ * -------------------------------------------------------------------
+ */
+static void dz_console_print(struct console *co,
+			     const char *str,
+			     unsigned int count)
+{
+	struct dz_port *dport = &dz_mux.dport[co->index];
+#ifdef DEBUG_DZ
+	prom_printf((char *) str);
+#endif
+	uart_console_write(&dport->port, str, count, dz_console_putchar);
+}
+
+static int __init dz_console_setup(struct console *co, char *options)
+{
+	struct dz_port *dport = &dz_mux.dport[co->index];
+	struct uart_port *uport = &dport->port;
+	int baud = 9600;
+	int bits = 8;
+	int parity = 'n';
+	int flow = 'n';
+	int ret;
+
+	ret = dz_map_port(uport);
+	if (ret)
+		return ret;
+
+	spin_lock_init(&dport->port.lock);	/* For dz_pm().  */
+
+	dz_reset(dport);
+	dz_pm(uport, 0, -1);
+
+	if (options)
+		uart_parse_options(options, &baud, &parity, &bits, &flow);
+
+	return uart_set_options(&dport->port, co, baud, parity, bits, flow);
+}
+
+static struct uart_driver dz_reg;
+static struct console dz_console = {
+	.name	= "ttyS",
+	.write	= dz_console_print,
+	.device	= uart_console_device,
+	.setup	= dz_console_setup,
+	.flags	= CON_PRINTBUFFER,
+	.index	= -1,
+	.data	= &dz_reg,
+};
+
+static int __init dz_serial_console_init(void)
+{
+	if (!IOASIC) {
+		dz_init_ports();
+		register_console(&dz_console);
+		return 0;
+	} else
+		return -ENXIO;
+}
+
+console_initcall(dz_serial_console_init);
+
+#define SERIAL_DZ_CONSOLE	&dz_console
+#else
+#define SERIAL_DZ_CONSOLE	NULL
+#endif /* CONFIG_SERIAL_DZ_CONSOLE */
+
+static struct uart_driver dz_reg = {
+	.owner			= THIS_MODULE,
+	.driver_name		= "serial",
+	.dev_name		= "ttyS",
+	.major			= TTY_MAJOR,
+	.minor			= 64,
+	.nr			= DZ_NB_PORT,
+	.cons			= SERIAL_DZ_CONSOLE,
+};
+
+static int __init dz_init(void)
+{
+	int ret, i;
+
+	if (IOASIC)
+		return -ENXIO;
+
+	printk("%s%s\n", dz_name, dz_version);
+
+	dz_init_ports();
+
+	ret = uart_register_driver(&dz_reg);
+	if (ret)
+		return ret;
+
+	for (i = 0; i < DZ_NB_PORT; i++)
+		uart_add_one_port(&dz_reg, &dz_mux.dport[i].port);
+
+	return 0;
+}
+
+module_init(dz_init);
diff --git a/ap/os/linux/linux-3.4.x/drivers/tty/serial/dz.h b/ap/os/linux/linux-3.4.x/drivers/tty/serial/dz.h
new file mode 100644
index 0000000..faf169e
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/tty/serial/dz.h
@@ -0,0 +1,129 @@
+/*
+ * dz.h: Serial port driver for DECstations equipped
+ *       with the DZ chipset.
+ *
+ * Copyright (C) 1998 Olivier A. D. Lebaillif 
+ *             
+ * Email: olivier.lebaillif@ifrsys.com
+ *
+ * Copyright (C) 2004, 2006  Maciej W. Rozycki
+ */
+#ifndef DZ_SERIAL_H
+#define DZ_SERIAL_H
+
+/*
+ * Definitions for the Control and Status Register.
+ */
+#define DZ_TRDY        0x8000                 /* Transmitter empty */
+#define DZ_TIE         0x4000                 /* Transmitter Interrupt Enbl */
+#define DZ_TLINE       0x0300                 /* Transmitter Line Number */
+#define DZ_RDONE       0x0080                 /* Receiver data ready */
+#define DZ_RIE         0x0040                 /* Receive Interrupt Enable */
+#define DZ_MSE         0x0020                 /* Master Scan Enable */
+#define DZ_CLR         0x0010                 /* Master reset */
+#define DZ_MAINT       0x0008                 /* Loop Back Mode */
+
+/*
+ * Definitions for the Receiver Buffer Register.
+ */
+#define DZ_RBUF_MASK   0x00FF                 /* Data Mask */
+#define DZ_LINE_MASK   0x0300                 /* Line Mask */
+#define DZ_DVAL        0x8000                 /* Valid Data indicator */
+#define DZ_OERR        0x4000                 /* Overrun error indicator */
+#define DZ_FERR        0x2000                 /* Frame error indicator */
+#define DZ_PERR        0x1000                 /* Parity error indicator */
+
+#define DZ_BREAK       0x0800                 /* BREAK event software flag */
+
+#define LINE(x) ((x & DZ_LINE_MASK) >> 8)     /* Get the line number
+                                                 from the input buffer */
+#define UCHAR(x) ((unsigned char)(x & DZ_RBUF_MASK))
+
+/*
+ * Definitions for the Transmit Control Register.
+ */
+#define DZ_LINE_KEYBOARD 0x0001
+#define DZ_LINE_MOUSE    0x0002
+#define DZ_LINE_MODEM    0x0004
+#define DZ_LINE_PRINTER  0x0008
+
+#define DZ_MODEM_RTS     0x0800               /* RTS for the modem line (2) */
+#define DZ_MODEM_DTR     0x0400               /* DTR for the modem line (2) */
+#define DZ_PRINT_RTS     0x0200               /* RTS for the prntr line (3) */
+#define DZ_PRINT_DTR     0x0100               /* DTR for the prntr line (3) */
+#define DZ_LNENB         0x000f               /* Transmitter Line Enable */
+
+/*
+ * Definitions for the Modem Status Register.
+ */
+#define DZ_MODEM_RI      0x0800               /* RI for the modem line (2) */
+#define DZ_MODEM_CD      0x0400               /* CD for the modem line (2) */
+#define DZ_MODEM_DSR     0x0200               /* DSR for the modem line (2) */
+#define DZ_MODEM_CTS     0x0100               /* CTS for the modem line (2) */
+#define DZ_PRINT_RI      0x0008               /* RI for the printer line (3) */
+#define DZ_PRINT_CD      0x0004               /* CD for the printer line (3) */
+#define DZ_PRINT_DSR     0x0002               /* DSR for the prntr line (3) */
+#define DZ_PRINT_CTS     0x0001               /* CTS for the prntr line (3) */
+
+/*
+ * Definitions for the Transmit Data Register.
+ */
+#define DZ_BRK0          0x0100               /* Break assertion for line 0 */
+#define DZ_BRK1          0x0200               /* Break assertion for line 1 */
+#define DZ_BRK2          0x0400               /* Break assertion for line 2 */
+#define DZ_BRK3          0x0800               /* Break assertion for line 3 */
+
+/*
+ * Definitions for the Line Parameter Register.
+ */
+#define DZ_KEYBOARD      0x0000               /* line 0 = keyboard */
+#define DZ_MOUSE         0x0001               /* line 1 = mouse */
+#define DZ_MODEM         0x0002               /* line 2 = modem */
+#define DZ_PRINTER       0x0003               /* line 3 = printer */
+
+#define DZ_CSIZE         0x0018               /* Number of bits per byte (mask) */
+#define DZ_CS5           0x0000               /* 5 bits per byte */
+#define DZ_CS6           0x0008               /* 6 bits per byte */
+#define DZ_CS7           0x0010               /* 7 bits per byte */
+#define DZ_CS8           0x0018               /* 8 bits per byte */
+
+#define DZ_CSTOPB        0x0020               /* 2 stop bits instead of one */ 
+
+#define DZ_PARENB        0x0040               /* Parity enable */
+#define DZ_PARODD        0x0080               /* Odd parity instead of even */
+
+#define DZ_CBAUD         0x0E00               /* Baud Rate (mask) */
+#define DZ_B50           0x0000
+#define DZ_B75           0x0100
+#define DZ_B110          0x0200
+#define DZ_B134          0x0300
+#define DZ_B150          0x0400
+#define DZ_B300          0x0500
+#define DZ_B600          0x0600
+#define DZ_B1200         0x0700 
+#define DZ_B1800         0x0800
+#define DZ_B2000         0x0900
+#define DZ_B2400         0x0A00
+#define DZ_B3600         0x0B00
+#define DZ_B4800         0x0C00
+#define DZ_B7200         0x0D00
+#define DZ_B9600         0x0E00
+
+#define DZ_RXENAB        0x1000               /* Receiver Enable */
+
+/*
+ * Addresses for the DZ registers
+ */
+#define DZ_CSR       0x00            /* Control and Status Register */
+#define DZ_RBUF      0x08            /* Receive Buffer */
+#define DZ_LPR       0x08            /* Line Parameters Register */
+#define DZ_TCR       0x10            /* Transmitter Control Register */
+#define DZ_MSR       0x18            /* Modem Status Register */
+#define DZ_TDR       0x18            /* Transmit Data Register */
+
+#define DZ_NB_PORT 4
+
+#define DZ_XMIT_SIZE   4096                 /* buffer size */
+#define DZ_WAKEUP_CHARS   DZ_XMIT_SIZE/4
+
+#endif /* DZ_SERIAL_H */
diff --git a/ap/os/linux/linux-3.4.x/drivers/tty/serial/efm32-uart.c b/ap/os/linux/linux-3.4.x/drivers/tty/serial/efm32-uart.c
new file mode 100644
index 0000000..615e464
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/tty/serial/efm32-uart.c
@@ -0,0 +1,830 @@
+#if defined(CONFIG_SERIAL_EFM32_UART_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ)
+#define SUPPORT_SYSRQ
+#endif
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/io.h>
+#include <linux/platform_device.h>
+#include <linux/console.h>
+#include <linux/sysrq.h>
+#include <linux/serial_core.h>
+#include <linux/tty_flip.h>
+#include <linux/slab.h>
+#include <linux/clk.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+
+#include <linux/platform_data/efm32-uart.h>
+
+#define DRIVER_NAME "efm32-uart"
+#define DEV_NAME "ttyefm"
+
+#define UARTn_CTRL		0x00
+#define UARTn_CTRL_SYNC		0x0001
+#define UARTn_CTRL_TXBIL		0x1000
+
+#define UARTn_FRAME		0x04
+#define UARTn_FRAME_DATABITS__MASK	0x000f
+#define UARTn_FRAME_DATABITS(n)		((n) - 3)
+#define UARTn_FRAME_PARITY_NONE		0x0000
+#define UARTn_FRAME_PARITY_EVEN		0x0200
+#define UARTn_FRAME_PARITY_ODD		0x0300
+#define UARTn_FRAME_STOPBITS_HALF	0x0000
+#define UARTn_FRAME_STOPBITS_ONE	0x1000
+#define UARTn_FRAME_STOPBITS_TWO	0x3000
+
+#define UARTn_CMD		0x0c
+#define UARTn_CMD_RXEN			0x0001
+#define UARTn_CMD_RXDIS		0x0002
+#define UARTn_CMD_TXEN			0x0004
+#define UARTn_CMD_TXDIS		0x0008
+
+#define UARTn_STATUS		0x10
+#define UARTn_STATUS_TXENS		0x0002
+#define UARTn_STATUS_TXC		0x0020
+#define UARTn_STATUS_TXBL		0x0040
+#define UARTn_STATUS_RXDATAV		0x0080
+
+#define UARTn_CLKDIV		0x14
+
+#define UARTn_RXDATAX		0x18
+#define UARTn_RXDATAX_RXDATA__MASK	0x01ff
+#define UARTn_RXDATAX_PERR		0x4000
+#define UARTn_RXDATAX_FERR		0x8000
+/*
+ * This is a software only flag used for ignore_status_mask and
+ * read_status_mask! It's used for breaks that the hardware doesn't report
+ * explicitly.
+ */
+#define SW_UARTn_RXDATAX_BERR		0x2000
+
+#define UARTn_TXDATA		0x34
+
+#define UARTn_IF		0x40
+#define UARTn_IF_TXC			0x0001
+#define UARTn_IF_TXBL			0x0002
+#define UARTn_IF_RXDATAV		0x0004
+#define UARTn_IF_RXOF			0x0010
+
+#define UARTn_IFS		0x44
+#define UARTn_IFC		0x48
+#define UARTn_IEN		0x4c
+
+#define UARTn_ROUTE		0x54
+#define UARTn_ROUTE_LOCATION__MASK	0x0700
+#define UARTn_ROUTE_LOCATION(n)		(((n) << 8) & UARTn_ROUTE_LOCATION__MASK)
+#define UARTn_ROUTE_RXPEN		0x0001
+#define UARTn_ROUTE_TXPEN		0x0002
+
+struct efm32_uart_port {
+	struct uart_port port;
+	unsigned int txirq;
+	struct clk *clk;
+};
+#define to_efm_port(_port) container_of(_port, struct efm32_uart_port, port)
+#define efm_debug(efm_port, format, arg...)			\
+	dev_dbg(efm_port->port.dev, format, ##arg)
+
+static void efm32_uart_write32(struct efm32_uart_port *efm_port,
+		u32 value, unsigned offset)
+{
+	writel_relaxed(value, efm_port->port.membase + offset);
+}
+
+static u32 efm32_uart_read32(struct efm32_uart_port *efm_port,
+		unsigned offset)
+{
+	return readl_relaxed(efm_port->port.membase + offset);
+}
+
+static unsigned int efm32_uart_tx_empty(struct uart_port *port)
+{
+	struct efm32_uart_port *efm_port = to_efm_port(port);
+	u32 status = efm32_uart_read32(efm_port, UARTn_STATUS);
+
+	if (status & UARTn_STATUS_TXC)
+		return TIOCSER_TEMT;
+	else
+		return 0;
+}
+
+static void efm32_uart_set_mctrl(struct uart_port *port, unsigned int mctrl)
+{
+	/* sorry, neither handshaking lines nor loop functionallity */
+}
+
+static unsigned int efm32_uart_get_mctrl(struct uart_port *port)
+{
+	/* sorry, no handshaking lines available */
+	return TIOCM_CAR | TIOCM_CTS | TIOCM_DSR;
+}
+
+static void efm32_uart_stop_tx(struct uart_port *port)
+{
+	struct efm32_uart_port *efm_port = to_efm_port(port);
+	u32 ien = efm32_uart_read32(efm_port,  UARTn_IEN);
+
+	efm32_uart_write32(efm_port, UARTn_CMD_TXDIS, UARTn_CMD);
+	ien &= ~(UARTn_IF_TXC | UARTn_IF_TXBL);
+	efm32_uart_write32(efm_port, ien, UARTn_IEN);
+}
+
+static void efm32_uart_tx_chars(struct efm32_uart_port *efm_port)
+{
+	struct uart_port *port = &efm_port->port;
+	struct circ_buf *xmit = &port->state->xmit;
+
+	while (efm32_uart_read32(efm_port, UARTn_STATUS) &
+			UARTn_STATUS_TXBL) {
+		if (port->x_char) {
+			port->icount.tx++;
+			efm32_uart_write32(efm_port, port->x_char,
+					UARTn_TXDATA);
+			port->x_char = 0;
+			continue;
+		}
+		if (!uart_circ_empty(xmit) && !uart_tx_stopped(port)) {
+			port->icount.tx++;
+			efm32_uart_write32(efm_port, xmit->buf[xmit->tail],
+					UARTn_TXDATA);
+			xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
+		} else
+			break;
+	}
+
+	if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
+		uart_write_wakeup(port);
+
+	if (!port->x_char && uart_circ_empty(xmit) &&
+			efm32_uart_read32(efm_port, UARTn_STATUS) &
+				UARTn_STATUS_TXC)
+		efm32_uart_stop_tx(port);
+}
+
+static void efm32_uart_start_tx(struct uart_port *port)
+{
+	struct efm32_uart_port *efm_port = to_efm_port(port);
+	u32 ien;
+
+	efm32_uart_write32(efm_port,
+			UARTn_IF_TXBL | UARTn_IF_TXC, UARTn_IFC);
+	ien = efm32_uart_read32(efm_port, UARTn_IEN);
+	efm32_uart_write32(efm_port,
+			ien | UARTn_IF_TXBL | UARTn_IF_TXC, UARTn_IEN);
+	efm32_uart_write32(efm_port, UARTn_CMD_TXEN, UARTn_CMD);
+
+	efm32_uart_tx_chars(efm_port);
+}
+
+static void efm32_uart_stop_rx(struct uart_port *port)
+{
+	struct efm32_uart_port *efm_port = to_efm_port(port);
+
+	efm32_uart_write32(efm_port, UARTn_CMD_RXDIS, UARTn_CMD);
+}
+
+static void efm32_uart_enable_ms(struct uart_port *port)
+{
+	/* no handshake lines, no modem status interrupts */
+}
+
+static void efm32_uart_break_ctl(struct uart_port *port, int ctl)
+{
+	/* not possible without fiddling with gpios */
+}
+
+static void efm32_uart_rx_chars(struct efm32_uart_port *efm_port,
+		struct tty_struct *tty)
+{
+	struct uart_port *port = &efm_port->port;
+
+	while (efm32_uart_read32(efm_port, UARTn_STATUS) &
+			UARTn_STATUS_RXDATAV) {
+		u32 rxdata = efm32_uart_read32(efm_port, UARTn_RXDATAX);
+		int flag = 0;
+
+		/*
+		 * This is a reserved bit and I only saw it read as 0. But to be
+		 * sure not to be confused too much by new devices adhere to the
+		 * warning in the reference manual that reserverd bits might
+		 * read as 1 in the future.
+		 */
+		rxdata &= ~SW_UARTn_RXDATAX_BERR;
+
+		port->icount.rx++;
+
+		if ((rxdata & UARTn_RXDATAX_FERR) &&
+				!(rxdata & UARTn_RXDATAX_RXDATA__MASK)) {
+			rxdata |= SW_UARTn_RXDATAX_BERR;
+			port->icount.brk++;
+			if (uart_handle_break(port))
+				continue;
+		} else if (rxdata & UARTn_RXDATAX_PERR)
+			port->icount.parity++;
+		else if (rxdata & UARTn_RXDATAX_FERR)
+			port->icount.frame++;
+
+		rxdata &= port->read_status_mask;
+
+		if (rxdata & SW_UARTn_RXDATAX_BERR)
+			flag = TTY_BREAK;
+		else if (rxdata & UARTn_RXDATAX_PERR)
+			flag = TTY_PARITY;
+		else if (rxdata & UARTn_RXDATAX_FERR)
+			flag = TTY_FRAME;
+		else if (uart_handle_sysrq_char(port,
+					rxdata & UARTn_RXDATAX_RXDATA__MASK))
+			continue;
+
+		if (tty && (rxdata & port->ignore_status_mask) == 0)
+			tty_insert_flip_char(tty,
+					rxdata & UARTn_RXDATAX_RXDATA__MASK, flag);
+	}
+}
+
+static irqreturn_t efm32_uart_rxirq(int irq, void *data)
+{
+	struct efm32_uart_port *efm_port = data;
+	u32 irqflag = efm32_uart_read32(efm_port, UARTn_IF);
+	int handled = IRQ_NONE;
+	struct uart_port *port = &efm_port->port;
+	struct tty_struct *tty;
+
+	spin_lock(&port->lock);
+
+	tty = tty_kref_get(port->state->port.tty);
+
+	if (irqflag & UARTn_IF_RXDATAV) {
+		efm32_uart_write32(efm_port, UARTn_IF_RXDATAV, UARTn_IFC);
+		efm32_uart_rx_chars(efm_port, tty);
+
+		handled = IRQ_HANDLED;
+	}
+
+	if (irqflag & UARTn_IF_RXOF) {
+		efm32_uart_write32(efm_port, UARTn_IF_RXOF, UARTn_IFC);
+		port->icount.overrun++;
+		if (tty)
+			tty_insert_flip_char(tty, 0, TTY_OVERRUN);
+
+		handled = IRQ_HANDLED;
+	}
+
+	if (tty) {
+		tty_flip_buffer_push(tty);
+		tty_kref_put(tty);
+	}
+
+	spin_unlock(&port->lock);
+
+	return handled;
+}
+
+static irqreturn_t efm32_uart_txirq(int irq, void *data)
+{
+	struct efm32_uart_port *efm_port = data;
+	u32 irqflag = efm32_uart_read32(efm_port, UARTn_IF);
+
+	/* TXBL doesn't need to be cleared */
+	if (irqflag & UARTn_IF_TXC)
+		efm32_uart_write32(efm_port, UARTn_IF_TXC, UARTn_IFC);
+
+	if (irqflag & (UARTn_IF_TXC | UARTn_IF_TXBL)) {
+		efm32_uart_tx_chars(efm_port);
+		return IRQ_HANDLED;
+	} else
+		return IRQ_NONE;
+}
+
+static int efm32_uart_startup(struct uart_port *port)
+{
+	struct efm32_uart_port *efm_port = to_efm_port(port);
+	u32 location = 0;
+	struct efm32_uart_pdata *pdata = dev_get_platdata(port->dev);
+	int ret;
+
+	if (pdata)
+		location = UARTn_ROUTE_LOCATION(pdata->location);
+
+	ret = clk_enable(efm_port->clk);
+	if (ret) {
+		efm_debug(efm_port, "failed to enable clk\n");
+		goto err_clk_enable;
+	}
+	port->uartclk = clk_get_rate(efm_port->clk);
+
+	/* Enable pins at configured location */
+	efm32_uart_write32(efm_port, location | UARTn_ROUTE_RXPEN | UARTn_ROUTE_TXPEN,
+			UARTn_ROUTE);
+
+	ret = request_irq(port->irq, efm32_uart_rxirq, 0,
+			DRIVER_NAME, efm_port);
+	if (ret) {
+		efm_debug(efm_port, "failed to register rxirq\n");
+		goto err_request_irq_rx;
+	}
+
+	/* disable all irqs */
+	efm32_uart_write32(efm_port, 0, UARTn_IEN);
+
+	ret = request_irq(efm_port->txirq, efm32_uart_txirq, 0,
+			DRIVER_NAME, efm_port);
+	if (ret) {
+		efm_debug(efm_port, "failed to register txirq\n");
+		free_irq(port->irq, efm_port);
+err_request_irq_rx:
+
+		clk_disable(efm_port->clk);
+	} else {
+		efm32_uart_write32(efm_port,
+				UARTn_IF_RXDATAV | UARTn_IF_RXOF, UARTn_IEN);
+		efm32_uart_write32(efm_port, UARTn_CMD_RXEN, UARTn_CMD);
+	}
+
+err_clk_enable:
+	return ret;
+}
+
+static void efm32_uart_shutdown(struct uart_port *port)
+{
+	struct efm32_uart_port *efm_port = to_efm_port(port);
+
+	efm32_uart_write32(efm_port, 0, UARTn_IEN);
+	free_irq(port->irq, efm_port);
+
+	clk_disable(efm_port->clk);
+}
+
+static void efm32_uart_set_termios(struct uart_port *port,
+		struct ktermios *new, struct ktermios *old)
+{
+	struct efm32_uart_port *efm_port = to_efm_port(port);
+	unsigned long flags;
+	unsigned baud;
+	u32 clkdiv;
+	u32 frame = 0;
+
+	/* no modem control lines */
+	new->c_cflag &= ~(CRTSCTS | CMSPAR);
+
+	baud = uart_get_baud_rate(port, new, old,
+			DIV_ROUND_CLOSEST(port->uartclk, 16 * 8192),
+			DIV_ROUND_CLOSEST(port->uartclk, 16));
+
+	switch (new->c_cflag & CSIZE) {
+	case CS5:
+		frame |= UARTn_FRAME_DATABITS(5);
+		break;
+	case CS6:
+		frame |= UARTn_FRAME_DATABITS(6);
+		break;
+	case CS7:
+		frame |= UARTn_FRAME_DATABITS(7);
+		break;
+	case CS8:
+		frame |= UARTn_FRAME_DATABITS(8);
+		break;
+	}
+
+	if (new->c_cflag & CSTOPB)
+		/* the receiver only verifies the first stop bit */
+		frame |= UARTn_FRAME_STOPBITS_TWO;
+	else
+		frame |= UARTn_FRAME_STOPBITS_ONE;
+
+	if (new->c_cflag & PARENB) {
+		if (new->c_cflag & PARODD)
+			frame |= UARTn_FRAME_PARITY_ODD;
+		else
+			frame |= UARTn_FRAME_PARITY_EVEN;
+	} else
+		frame |= UARTn_FRAME_PARITY_NONE;
+
+	/*
+	 * the 6 lowest bits of CLKDIV are dc, bit 6 has value 0.25.
+	 * port->uartclk <= 14e6, so 4 * port->uartclk doesn't overflow.
+	 */
+	clkdiv = (DIV_ROUND_CLOSEST(4 * port->uartclk, 16 * baud) - 4) << 6;
+
+	spin_lock_irqsave(&port->lock, flags);
+
+	efm32_uart_write32(efm_port,
+			UARTn_CMD_TXDIS | UARTn_CMD_RXDIS, UARTn_CMD);
+
+	port->read_status_mask = UARTn_RXDATAX_RXDATA__MASK;
+	if (new->c_iflag & INPCK)
+		port->read_status_mask |=
+			UARTn_RXDATAX_FERR | UARTn_RXDATAX_PERR;
+	if (new->c_iflag & (BRKINT | PARMRK))
+		port->read_status_mask |= SW_UARTn_RXDATAX_BERR;
+
+	port->ignore_status_mask = 0;
+	if (new->c_iflag & IGNPAR)
+		port->ignore_status_mask |=
+			UARTn_RXDATAX_FERR | UARTn_RXDATAX_PERR;
+	if (new->c_iflag & IGNBRK)
+		port->ignore_status_mask |= SW_UARTn_RXDATAX_BERR;
+
+	uart_update_timeout(port, new->c_cflag, baud);
+
+	efm32_uart_write32(efm_port, UARTn_CTRL_TXBIL, UARTn_CTRL);
+	efm32_uart_write32(efm_port, frame, UARTn_FRAME);
+	efm32_uart_write32(efm_port, clkdiv, UARTn_CLKDIV);
+
+	efm32_uart_write32(efm_port, UARTn_CMD_TXEN | UARTn_CMD_RXEN,
+			UARTn_CMD);
+
+	spin_unlock_irqrestore(&port->lock, flags);
+}
+
+static const char *efm32_uart_type(struct uart_port *port)
+{
+	return port->type == PORT_EFMUART ? "efm32-uart" : NULL;
+}
+
+static void efm32_uart_release_port(struct uart_port *port)
+{
+	struct efm32_uart_port *efm_port = to_efm_port(port);
+
+	clk_unprepare(efm_port->clk);
+	clk_put(efm_port->clk);
+	iounmap(port->membase);
+}
+
+static int efm32_uart_request_port(struct uart_port *port)
+{
+	struct efm32_uart_port *efm_port = to_efm_port(port);
+	int ret;
+
+	port->membase = ioremap(port->mapbase, 60);
+	if (!efm_port->port.membase) {
+		ret = -ENOMEM;
+		efm_debug(efm_port, "failed to remap\n");
+		goto err_ioremap;
+	}
+
+	efm_port->clk = clk_get(port->dev, NULL);
+	if (IS_ERR(efm_port->clk)) {
+		ret = PTR_ERR(efm_port->clk);
+		efm_debug(efm_port, "failed to get clock\n");
+		goto err_clk_get;
+	}
+
+	ret = clk_prepare(efm_port->clk);
+	if (ret) {
+		clk_put(efm_port->clk);
+err_clk_get:
+
+		iounmap(port->membase);
+err_ioremap:
+		return ret;
+	}
+	return 0;
+}
+
+static void efm32_uart_config_port(struct uart_port *port, int type)
+{
+	if (type & UART_CONFIG_TYPE &&
+			!efm32_uart_request_port(port))
+		port->type = PORT_EFMUART;
+}
+
+static int efm32_uart_verify_port(struct uart_port *port,
+		struct serial_struct *serinfo)
+{
+	int ret = 0;
+
+	if (serinfo->type != PORT_UNKNOWN && serinfo->type != PORT_EFMUART)
+		ret = -EINVAL;
+
+	return ret;
+}
+
+static struct uart_ops efm32_uart_pops = {
+	.tx_empty = efm32_uart_tx_empty,
+	.set_mctrl = efm32_uart_set_mctrl,
+	.get_mctrl = efm32_uart_get_mctrl,
+	.stop_tx = efm32_uart_stop_tx,
+	.start_tx = efm32_uart_start_tx,
+	.stop_rx = efm32_uart_stop_rx,
+	.enable_ms = efm32_uart_enable_ms,
+	.break_ctl = efm32_uart_break_ctl,
+	.startup = efm32_uart_startup,
+	.shutdown = efm32_uart_shutdown,
+	.set_termios = efm32_uart_set_termios,
+	.type = efm32_uart_type,
+	.release_port = efm32_uart_release_port,
+	.request_port = efm32_uart_request_port,
+	.config_port = efm32_uart_config_port,
+	.verify_port = efm32_uart_verify_port,
+};
+
+static struct efm32_uart_port *efm32_uart_ports[5];
+
+#ifdef CONFIG_SERIAL_EFM32_UART_CONSOLE
+static void efm32_uart_console_putchar(struct uart_port *port, int ch)
+{
+	struct efm32_uart_port *efm_port = to_efm_port(port);
+	unsigned int timeout = 0x400;
+	u32 status;
+
+	while (1) {
+		status = efm32_uart_read32(efm_port, UARTn_STATUS);
+
+		if (status & UARTn_STATUS_TXBL)
+			break;
+		if (!timeout--)
+			return;
+	}
+	efm32_uart_write32(efm_port, ch, UARTn_TXDATA);
+}
+
+static void efm32_uart_console_write(struct console *co, const char *s,
+		unsigned int count)
+{
+	struct efm32_uart_port *efm_port = efm32_uart_ports[co->index];
+	u32 status = efm32_uart_read32(efm_port, UARTn_STATUS);
+	unsigned int timeout = 0x400;
+
+	if (!(status & UARTn_STATUS_TXENS))
+		efm32_uart_write32(efm_port, UARTn_CMD_TXEN, UARTn_CMD);
+
+	uart_console_write(&efm_port->port, s, count,
+			efm32_uart_console_putchar);
+
+	/* Wait for the transmitter to become empty */
+	while (1) {
+		u32 status = efm32_uart_read32(efm_port, UARTn_STATUS);
+		if (status & UARTn_STATUS_TXC)
+			break;
+		if (!timeout--)
+			break;
+	}
+
+	if (!(status & UARTn_STATUS_TXENS))
+		efm32_uart_write32(efm_port, UARTn_CMD_TXDIS, UARTn_CMD);
+}
+
+static void efm32_uart_console_get_options(struct efm32_uart_port *efm_port,
+		int *baud, int *parity, int *bits)
+{
+	u32 ctrl = efm32_uart_read32(efm_port, UARTn_CTRL);
+	u32 route, clkdiv, frame;
+
+	if (ctrl & UARTn_CTRL_SYNC)
+		/* not operating in async mode */
+		return;
+
+	route = efm32_uart_read32(efm_port, UARTn_ROUTE);
+	if (!(route & UARTn_ROUTE_TXPEN))
+		/* tx pin not routed */
+		return;
+
+	clkdiv = efm32_uart_read32(efm_port, UARTn_CLKDIV);
+
+	*baud = DIV_ROUND_CLOSEST(4 * efm_port->port.uartclk,
+			16 * (4 + (clkdiv >> 6)));
+
+	frame = efm32_uart_read32(efm_port, UARTn_FRAME);
+	if (frame & UARTn_FRAME_PARITY_ODD)
+		*parity = 'o';
+	else if (frame & UARTn_FRAME_PARITY_EVEN)
+		*parity = 'e';
+	else
+		*parity = 'n';
+
+	*bits = (frame & UARTn_FRAME_DATABITS__MASK) -
+			UARTn_FRAME_DATABITS(4) + 4;
+
+	efm_debug(efm_port, "get_opts: options=%d%c%d\n",
+			*baud, *parity, *bits);
+}
+
+static int efm32_uart_console_setup(struct console *co, char *options)
+{
+	struct efm32_uart_port *efm_port;
+	int baud = 115200;
+	int bits = 8;
+	int parity = 'n';
+	int flow = 'n';
+	int ret;
+
+	if (co->index < 0 || co->index >= ARRAY_SIZE(efm32_uart_ports)) {
+		unsigned i;
+		for (i = 0; i < ARRAY_SIZE(efm32_uart_ports); ++i) {
+			if (efm32_uart_ports[i]) {
+				pr_warn("efm32-console: fall back to console index %u (from %hhi)\n",
+						i, co->index);
+				co->index = i;
+				break;
+			}
+		}
+	}
+
+	efm_port = efm32_uart_ports[co->index];
+	if (!efm_port) {
+		pr_warn("efm32-console: No port at %d\n", co->index);
+		return -ENODEV;
+	}
+
+	ret = clk_prepare(efm_port->clk);
+	if (ret) {
+		dev_warn(efm_port->port.dev,
+				"console: clk_prepare failed: %d\n", ret);
+		return ret;
+	}
+
+	efm_port->port.uartclk = clk_get_rate(efm_port->clk);
+
+	if (options)
+		uart_parse_options(options, &baud, &parity, &bits, &flow);
+	else
+		efm32_uart_console_get_options(efm_port,
+				&baud, &parity, &bits);
+
+	return uart_set_options(&efm_port->port, co, baud, parity, bits, flow);
+}
+
+static struct uart_driver efm32_uart_reg;
+
+static struct console efm32_uart_console = {
+	.name = DEV_NAME,
+	.write = efm32_uart_console_write,
+	.device = uart_console_device,
+	.setup = efm32_uart_console_setup,
+	.flags = CON_PRINTBUFFER,
+	.index = -1,
+	.data = &efm32_uart_reg,
+};
+
+#else
+#define efm32_uart_console (*(struct console *)NULL)
+#endif /* ifdef CONFIG_SERIAL_EFM32_UART_CONSOLE / else */
+
+static struct uart_driver efm32_uart_reg = {
+	.owner = THIS_MODULE,
+	.driver_name = DRIVER_NAME,
+	.dev_name = DEV_NAME,
+	.nr = ARRAY_SIZE(efm32_uart_ports),
+	.cons = &efm32_uart_console,
+};
+
+static int efm32_uart_probe_dt(struct platform_device *pdev,
+		struct efm32_uart_port *efm_port)
+{
+	struct device_node *np = pdev->dev.of_node;
+	int ret;
+
+	if (!np)
+		return 1;
+
+	ret = of_alias_get_id(np, "serial");
+	if (ret < 0) {
+		dev_err(&pdev->dev, "failed to get alias id: %d\n", ret);
+		return ret;
+	} else {
+		efm_port->port.line = ret;
+		return 0;
+	}
+
+}
+
+static int __devinit efm32_uart_probe(struct platform_device *pdev)
+{
+	struct efm32_uart_port *efm_port;
+	struct resource *res;
+	int ret;
+
+	efm_port = kzalloc(sizeof(*efm_port), GFP_KERNEL);
+	if (!efm_port) {
+		dev_dbg(&pdev->dev, "failed to allocate private data\n");
+		return -ENOMEM;
+	}
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!res) {
+		ret = -ENODEV;
+		dev_dbg(&pdev->dev, "failed to determine base address\n");
+		goto err_get_base;
+	}
+
+	if (resource_size(res) < 60) {
+		ret = -EINVAL;
+		dev_dbg(&pdev->dev, "memory resource too small\n");
+		goto err_too_small;
+	}
+
+	ret = platform_get_irq(pdev, 0);
+	if (ret <= 0) {
+		dev_dbg(&pdev->dev, "failed to get rx irq\n");
+		goto err_get_rxirq;
+	}
+
+	efm_port->port.irq = ret;
+
+	ret = platform_get_irq(pdev, 1);
+	if (ret <= 0)
+		ret = efm_port->port.irq + 1;
+
+	efm_port->txirq = ret;
+
+	efm_port->port.dev = &pdev->dev;
+	efm_port->port.mapbase = res->start;
+	efm_port->port.type = PORT_EFMUART;
+	efm_port->port.iotype = UPIO_MEM32;
+	efm_port->port.fifosize = 2;
+	efm_port->port.ops = &efm32_uart_pops;
+	efm_port->port.flags = UPF_BOOT_AUTOCONF;
+
+	ret = efm32_uart_probe_dt(pdev, efm_port);
+	if (ret > 0)
+		/* not created by device tree */
+		efm_port->port.line = pdev->id;
+
+	if (efm_port->port.line >= 0 &&
+			efm_port->port.line < ARRAY_SIZE(efm32_uart_ports))
+		efm32_uart_ports[efm_port->port.line] = efm_port;
+
+	ret = uart_add_one_port(&efm32_uart_reg, &efm_port->port);
+	if (ret) {
+		dev_dbg(&pdev->dev, "failed to add port: %d\n", ret);
+
+		if (pdev->id >= 0 && pdev->id < ARRAY_SIZE(efm32_uart_ports))
+			efm32_uart_ports[pdev->id] = NULL;
+err_get_rxirq:
+err_too_small:
+err_get_base:
+		kfree(efm_port);
+	} else {
+		platform_set_drvdata(pdev, efm_port);
+		dev_dbg(&pdev->dev, "\\o/\n");
+	}
+
+	return ret;
+}
+
+static int __devexit efm32_uart_remove(struct platform_device *pdev)
+{
+	struct efm32_uart_port *efm_port = platform_get_drvdata(pdev);
+
+	platform_set_drvdata(pdev, NULL);
+
+	uart_remove_one_port(&efm32_uart_reg, &efm_port->port);
+
+	if (pdev->id >= 0 && pdev->id < ARRAY_SIZE(efm32_uart_ports))
+		efm32_uart_ports[pdev->id] = NULL;
+
+	kfree(efm_port);
+
+	return 0;
+}
+
+static struct of_device_id efm32_uart_dt_ids[] = {
+	{
+		.compatible = "efm32,uart",
+	}, {
+		/* sentinel */
+	}
+};
+MODULE_DEVICE_TABLE(of, efm32_uart_dt_ids);
+
+static struct platform_driver efm32_uart_driver = {
+	.probe = efm32_uart_probe,
+	.remove = __devexit_p(efm32_uart_remove),
+
+	.driver = {
+		.name = DRIVER_NAME,
+		.owner = THIS_MODULE,
+		.of_match_table = efm32_uart_dt_ids,
+	},
+};
+
+static int __init efm32_uart_init(void)
+{
+	int ret;
+
+	ret = uart_register_driver(&efm32_uart_reg);
+	if (ret)
+		return ret;
+
+	ret = platform_driver_register(&efm32_uart_driver);
+	if (ret)
+		uart_unregister_driver(&efm32_uart_reg);
+
+	pr_info("EFM32 UART/USART driver\n");
+
+	return ret;
+}
+module_init(efm32_uart_init);
+
+static void __exit efm32_uart_exit(void)
+{
+	platform_driver_unregister(&efm32_uart_driver);
+	uart_unregister_driver(&efm32_uart_reg);
+}
+
+MODULE_AUTHOR("Uwe Kleine-Koenig <u.kleine-koenig@pengutronix.de>");
+MODULE_DESCRIPTION("EFM32 UART/USART driver");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:" DRIVER_NAME);
diff --git a/ap/os/linux/linux-3.4.x/drivers/tty/serial/icom.c b/ap/os/linux/linux-3.4.x/drivers/tty/serial/icom.c
new file mode 100644
index 0000000..defc4e3
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/tty/serial/icom.c
@@ -0,0 +1,1657 @@
+/*
+  * icom.c
+  *
+  * Copyright (C) 2001 IBM Corporation. All rights reserved.
+  *
+  * Serial device driver.
+  *
+  * Based on code from serial.c
+  *
+  * 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, write to the Free Software
+  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+  *
+  */
+#define SERIAL_DO_RESTART
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/signal.h>
+#include <linux/timer.h>
+#include <linux/interrupt.h>
+#include <linux/tty.h>
+#include <linux/termios.h>
+#include <linux/fs.h>
+#include <linux/tty_flip.h>
+#include <linux/serial.h>
+#include <linux/serial_reg.h>
+#include <linux/major.h>
+#include <linux/string.h>
+#include <linux/fcntl.h>
+#include <linux/ptrace.h>
+#include <linux/ioport.h>
+#include <linux/mm.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/pci.h>
+#include <linux/vmalloc.h>
+#include <linux/smp.h>
+#include <linux/spinlock.h>
+#include <linux/kref.h>
+#include <linux/firmware.h>
+#include <linux/bitops.h>
+
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/uaccess.h>
+
+#include "icom.h"
+
+/*#define ICOM_TRACE		 enable port trace capabilities */
+
+#define ICOM_DRIVER_NAME "icom"
+#define ICOM_VERSION_STR "1.3.1"
+#define NR_PORTS	       128
+#define ICOM_PORT ((struct icom_port *)port)
+#define to_icom_adapter(d) container_of(d, struct icom_adapter, kref)
+
+static const struct pci_device_id icom_pci_table[] = {
+	{
+		.vendor = PCI_VENDOR_ID_IBM,
+		.device = PCI_DEVICE_ID_IBM_ICOM_DEV_ID_1,
+		.subvendor = PCI_ANY_ID,
+		.subdevice = PCI_ANY_ID,
+		.driver_data = ADAPTER_V1,
+	},
+	{
+		.vendor = PCI_VENDOR_ID_IBM,
+		.device = PCI_DEVICE_ID_IBM_ICOM_DEV_ID_2,
+		.subvendor = PCI_VENDOR_ID_IBM,
+		.subdevice = PCI_DEVICE_ID_IBM_ICOM_V2_TWO_PORTS_RVX,
+		.driver_data = ADAPTER_V2,
+	},
+	{
+		.vendor = PCI_VENDOR_ID_IBM,
+		.device = PCI_DEVICE_ID_IBM_ICOM_DEV_ID_2,
+		.subvendor = PCI_VENDOR_ID_IBM,
+		.subdevice = PCI_DEVICE_ID_IBM_ICOM_V2_ONE_PORT_RVX_ONE_PORT_MDM,
+		.driver_data = ADAPTER_V2,
+	},
+	{
+		.vendor = PCI_VENDOR_ID_IBM,
+		.device = PCI_DEVICE_ID_IBM_ICOM_DEV_ID_2,
+		.subvendor = PCI_VENDOR_ID_IBM,
+		.subdevice = PCI_DEVICE_ID_IBM_ICOM_FOUR_PORT_MODEL,
+		.driver_data = ADAPTER_V2,
+	},
+	{
+		.vendor = PCI_VENDOR_ID_IBM,
+		.device = PCI_DEVICE_ID_IBM_ICOM_DEV_ID_2,
+		.subvendor = PCI_VENDOR_ID_IBM,
+		.subdevice = PCI_DEVICE_ID_IBM_ICOM_V2_ONE_PORT_RVX_ONE_PORT_MDM_PCIE,
+		.driver_data = ADAPTER_V2,
+	},
+	{}
+};
+
+struct lookup_proc_table start_proc[4] = {
+	{NULL, ICOM_CONTROL_START_A},
+	{NULL, ICOM_CONTROL_START_B},
+	{NULL, ICOM_CONTROL_START_C},
+	{NULL, ICOM_CONTROL_START_D}
+};
+
+
+struct lookup_proc_table stop_proc[4] = {
+	{NULL, ICOM_CONTROL_STOP_A},
+	{NULL, ICOM_CONTROL_STOP_B},
+	{NULL, ICOM_CONTROL_STOP_C},
+	{NULL, ICOM_CONTROL_STOP_D}
+};
+
+struct lookup_int_table int_mask_tbl[4] = {
+	{NULL, ICOM_INT_MASK_PRC_A},
+	{NULL, ICOM_INT_MASK_PRC_B},
+	{NULL, ICOM_INT_MASK_PRC_C},
+	{NULL, ICOM_INT_MASK_PRC_D},
+};
+
+
+MODULE_DEVICE_TABLE(pci, icom_pci_table);
+
+static LIST_HEAD(icom_adapter_head);
+
+/* spinlock for adapter initialization and changing adapter operations */
+static spinlock_t icom_lock;
+
+#ifdef ICOM_TRACE
+static inline void trace(struct icom_port *icom_port, char *trace_pt,
+			unsigned long trace_data)
+{
+	dev_info(&icom_port->adapter->pci_dev->dev, ":%d:%s - %lx\n",
+	icom_port->port, trace_pt, trace_data);
+}
+#else
+static inline void trace(struct icom_port *icom_port, char *trace_pt, unsigned long trace_data) {};
+#endif
+static void icom_kref_release(struct kref *kref);
+
+static void free_port_memory(struct icom_port *icom_port)
+{
+	struct pci_dev *dev = icom_port->adapter->pci_dev;
+
+	trace(icom_port, "RET_PORT_MEM", 0);
+	if (icom_port->recv_buf) {
+		pci_free_consistent(dev, 4096, icom_port->recv_buf,
+				    icom_port->recv_buf_pci);
+		icom_port->recv_buf = NULL;
+	}
+	if (icom_port->xmit_buf) {
+		pci_free_consistent(dev, 4096, icom_port->xmit_buf,
+				    icom_port->xmit_buf_pci);
+		icom_port->xmit_buf = NULL;
+	}
+	if (icom_port->statStg) {
+		pci_free_consistent(dev, 4096, icom_port->statStg,
+				    icom_port->statStg_pci);
+		icom_port->statStg = NULL;
+	}
+
+	if (icom_port->xmitRestart) {
+		pci_free_consistent(dev, 4096, icom_port->xmitRestart,
+				    icom_port->xmitRestart_pci);
+		icom_port->xmitRestart = NULL;
+	}
+}
+
+static int __devinit get_port_memory(struct icom_port *icom_port)
+{
+	int index;
+	unsigned long stgAddr;
+	unsigned long startStgAddr;
+	unsigned long offset;
+	struct pci_dev *dev = icom_port->adapter->pci_dev;
+
+	icom_port->xmit_buf =
+	    pci_alloc_consistent(dev, 4096, &icom_port->xmit_buf_pci);
+	if (!icom_port->xmit_buf) {
+		dev_err(&dev->dev, "Can not allocate Transmit buffer\n");
+		return -ENOMEM;
+	}
+
+	trace(icom_port, "GET_PORT_MEM",
+	      (unsigned long) icom_port->xmit_buf);
+
+	icom_port->recv_buf =
+	    pci_alloc_consistent(dev, 4096, &icom_port->recv_buf_pci);
+	if (!icom_port->recv_buf) {
+		dev_err(&dev->dev, "Can not allocate Receive buffer\n");
+		free_port_memory(icom_port);
+		return -ENOMEM;
+	}
+	trace(icom_port, "GET_PORT_MEM",
+	      (unsigned long) icom_port->recv_buf);
+
+	icom_port->statStg =
+	    pci_alloc_consistent(dev, 4096, &icom_port->statStg_pci);
+	if (!icom_port->statStg) {
+		dev_err(&dev->dev, "Can not allocate Status buffer\n");
+		free_port_memory(icom_port);
+		return -ENOMEM;
+	}
+	trace(icom_port, "GET_PORT_MEM",
+	      (unsigned long) icom_port->statStg);
+
+	icom_port->xmitRestart =
+	    pci_alloc_consistent(dev, 4096, &icom_port->xmitRestart_pci);
+	if (!icom_port->xmitRestart) {
+		dev_err(&dev->dev,
+			"Can not allocate xmit Restart buffer\n");
+		free_port_memory(icom_port);
+		return -ENOMEM;
+	}
+
+	memset(icom_port->statStg, 0, 4096);
+
+	/* FODs: Frame Out Descriptor Queue, this is a FIFO queue that
+           indicates that frames are to be transmitted
+	*/
+
+	stgAddr = (unsigned long) icom_port->statStg;
+	for (index = 0; index < NUM_XBUFFS; index++) {
+		trace(icom_port, "FOD_ADDR", stgAddr);
+		stgAddr = stgAddr + sizeof(icom_port->statStg->xmit[0]);
+		if (index < (NUM_XBUFFS - 1)) {
+			memset(&icom_port->statStg->xmit[index], 0, sizeof(struct xmit_status_area));
+			icom_port->statStg->xmit[index].leLengthASD =
+			    (unsigned short int) cpu_to_le16(XMIT_BUFF_SZ);
+			trace(icom_port, "FOD_ADDR", stgAddr);
+			trace(icom_port, "FOD_XBUFF",
+			      (unsigned long) icom_port->xmit_buf);
+			icom_port->statStg->xmit[index].leBuffer =
+			    cpu_to_le32(icom_port->xmit_buf_pci);
+		} else if (index == (NUM_XBUFFS - 1)) {
+			memset(&icom_port->statStg->xmit[index], 0, sizeof(struct xmit_status_area));
+			icom_port->statStg->xmit[index].leLengthASD =
+			    (unsigned short int) cpu_to_le16(XMIT_BUFF_SZ);
+			trace(icom_port, "FOD_XBUFF",
+			      (unsigned long) icom_port->xmit_buf);
+			icom_port->statStg->xmit[index].leBuffer =
+			    cpu_to_le32(icom_port->xmit_buf_pci);
+		} else {
+			memset(&icom_port->statStg->xmit[index], 0, sizeof(struct xmit_status_area));
+		}
+	}
+	/* FIDs */
+	startStgAddr = stgAddr;
+
+	/* fill in every entry, even if no buffer */
+	for (index = 0; index <  NUM_RBUFFS; index++) {
+		trace(icom_port, "FID_ADDR", stgAddr);
+		stgAddr = stgAddr + sizeof(icom_port->statStg->rcv[0]);
+		icom_port->statStg->rcv[index].leLength = 0;
+		icom_port->statStg->rcv[index].WorkingLength =
+		    (unsigned short int) cpu_to_le16(RCV_BUFF_SZ);
+		if (index < (NUM_RBUFFS - 1) ) {
+			offset = stgAddr - (unsigned long) icom_port->statStg;
+			icom_port->statStg->rcv[index].leNext =
+			      cpu_to_le32(icom_port-> statStg_pci + offset);
+			trace(icom_port, "FID_RBUFF",
+			      (unsigned long) icom_port->recv_buf);
+			icom_port->statStg->rcv[index].leBuffer =
+			    cpu_to_le32(icom_port->recv_buf_pci);
+		} else if (index == (NUM_RBUFFS -1) ) {
+			offset = startStgAddr - (unsigned long) icom_port->statStg;
+			icom_port->statStg->rcv[index].leNext =
+			    cpu_to_le32(icom_port-> statStg_pci + offset);
+			trace(icom_port, "FID_RBUFF",
+			      (unsigned long) icom_port->recv_buf + 2048);
+			icom_port->statStg->rcv[index].leBuffer =
+			    cpu_to_le32(icom_port->recv_buf_pci + 2048);
+		} else {
+			icom_port->statStg->rcv[index].leNext = 0;
+			icom_port->statStg->rcv[index].leBuffer = 0;
+		}
+	}
+
+	return 0;
+}
+
+static void stop_processor(struct icom_port *icom_port)
+{
+	unsigned long temp;
+	unsigned long flags;
+	int port;
+
+	spin_lock_irqsave(&icom_lock, flags);
+
+	port = icom_port->port;
+	if (port == 0 || port == 1)
+		stop_proc[port].global_control_reg = &icom_port->global_reg->control;
+	else
+		stop_proc[port].global_control_reg = &icom_port->global_reg->control_2;
+
+
+	if (port < 4) {
+		temp = readl(stop_proc[port].global_control_reg);
+		temp =
+			(temp & ~start_proc[port].processor_id) | stop_proc[port].processor_id;
+		writel(temp, stop_proc[port].global_control_reg);
+
+		/* write flush */
+		readl(stop_proc[port].global_control_reg);
+	} else {
+		dev_err(&icom_port->adapter->pci_dev->dev,
+                        "Invalid port assignment\n");
+	}
+
+	spin_unlock_irqrestore(&icom_lock, flags);
+}
+
+static void start_processor(struct icom_port *icom_port)
+{
+	unsigned long temp;
+	unsigned long flags;
+	int port;
+
+	spin_lock_irqsave(&icom_lock, flags);
+
+	port = icom_port->port;
+	if (port == 0 || port == 1)
+		start_proc[port].global_control_reg = &icom_port->global_reg->control;
+	else
+		start_proc[port].global_control_reg = &icom_port->global_reg->control_2;
+	if (port < 4) {
+		temp = readl(start_proc[port].global_control_reg);
+		temp =
+			(temp & ~stop_proc[port].processor_id) | start_proc[port].processor_id;
+		writel(temp, start_proc[port].global_control_reg);
+
+		/* write flush */
+		readl(start_proc[port].global_control_reg);
+	} else {
+		dev_err(&icom_port->adapter->pci_dev->dev,
+                        "Invalid port assignment\n");
+	}
+
+	spin_unlock_irqrestore(&icom_lock, flags);
+}
+
+static void load_code(struct icom_port *icom_port)
+{
+	const struct firmware *fw;
+	char __iomem *iram_ptr;
+	int index;
+	int status = 0;
+	void __iomem *dram_ptr = icom_port->dram;
+	dma_addr_t temp_pci;
+	unsigned char *new_page = NULL;
+	unsigned char cable_id = NO_CABLE;
+	struct pci_dev *dev = icom_port->adapter->pci_dev;
+
+	/* Clear out any pending interrupts */
+	writew(0x3FFF, icom_port->int_reg);
+
+	trace(icom_port, "CLEAR_INTERRUPTS", 0);
+
+	/* Stop processor */
+	stop_processor(icom_port);
+
+	/* Zero out DRAM */
+	memset_io(dram_ptr, 0, 512);
+
+	/* Load Call Setup into Adapter */
+	if (request_firmware(&fw, "icom_call_setup.bin", &dev->dev) < 0) {
+		dev_err(&dev->dev,"Unable to load icom_call_setup.bin firmware image\n");
+		status = -1;
+		goto load_code_exit;
+	}
+
+	if (fw->size > ICOM_DCE_IRAM_OFFSET) {
+		dev_err(&dev->dev, "Invalid firmware image for icom_call_setup.bin found.\n");
+		release_firmware(fw);
+		status = -1;
+		goto load_code_exit;
+	}
+
+	iram_ptr = (char __iomem *)icom_port->dram + ICOM_IRAM_OFFSET;
+	for (index = 0; index < fw->size; index++)
+		writeb(fw->data[index], &iram_ptr[index]);
+
+	release_firmware(fw);
+
+	/* Load Resident DCE portion of Adapter */
+	if (request_firmware(&fw, "icom_res_dce.bin", &dev->dev) < 0) {
+		dev_err(&dev->dev,"Unable to load icom_res_dce.bin firmware image\n");
+		status = -1;
+		goto load_code_exit;
+	}
+
+	if (fw->size > ICOM_IRAM_SIZE) {
+		dev_err(&dev->dev, "Invalid firmware image for icom_res_dce.bin found.\n");
+		release_firmware(fw);
+		status = -1;
+		goto load_code_exit;
+	}
+
+	iram_ptr = (char __iomem *) icom_port->dram + ICOM_IRAM_OFFSET;
+	for (index = ICOM_DCE_IRAM_OFFSET; index < fw->size; index++)
+		writeb(fw->data[index], &iram_ptr[index]);
+
+	release_firmware(fw);
+
+	/* Set Hardware level */
+	if (icom_port->adapter->version == ADAPTER_V2)
+		writeb(V2_HARDWARE, &(icom_port->dram->misc_flags));
+
+	/* Start the processor in Adapter */
+	start_processor(icom_port);
+
+	writeb((HDLC_PPP_PURE_ASYNC | HDLC_FF_FILL),
+	       &(icom_port->dram->HDLCConfigReg));
+	writeb(0x04, &(icom_port->dram->FlagFillIdleTimer));	/* 0.5 seconds */
+	writeb(0x00, &(icom_port->dram->CmdReg));
+	writeb(0x10, &(icom_port->dram->async_config3));
+	writeb((ICOM_ACFG_DRIVE1 | ICOM_ACFG_NO_PARITY | ICOM_ACFG_8BPC |
+		ICOM_ACFG_1STOP_BIT), &(icom_port->dram->async_config2));
+
+	/*Set up data in icom DRAM to indicate where personality
+	 *code is located and its length.
+	 */
+	new_page = pci_alloc_consistent(dev, 4096, &temp_pci);
+
+	if (!new_page) {
+		dev_err(&dev->dev, "Can not allocate DMA buffer\n");
+		status = -1;
+		goto load_code_exit;
+	}
+
+	if (request_firmware(&fw, "icom_asc.bin", &dev->dev) < 0) {
+		dev_err(&dev->dev,"Unable to load icom_asc.bin firmware image\n");
+		status = -1;
+		goto load_code_exit;
+	}
+
+	if (fw->size > ICOM_DCE_IRAM_OFFSET) {
+		dev_err(&dev->dev, "Invalid firmware image for icom_asc.bin found.\n");
+		release_firmware(fw);
+		status = -1;
+		goto load_code_exit;
+	}
+
+	for (index = 0; index < fw->size; index++)
+		new_page[index] = fw->data[index];
+
+	release_firmware(fw);
+
+	writeb((char) ((fw->size + 16)/16), &icom_port->dram->mac_length);
+	writel(temp_pci, &icom_port->dram->mac_load_addr);
+
+	/*Setting the syncReg to 0x80 causes adapter to start downloading
+	   the personality code into adapter instruction RAM.
+	   Once code is loaded, it will begin executing and, based on
+	   information provided above, will start DMAing data from
+	   shared memory to adapter DRAM.
+	 */
+	/* the wait loop below verifies this write operation has been done
+	   and processed
+	*/
+	writeb(START_DOWNLOAD, &icom_port->dram->sync);
+
+	/* Wait max 1 Sec for data download and processor to start */
+	for (index = 0; index < 10; index++) {
+		msleep(100);
+		if (readb(&icom_port->dram->misc_flags) & ICOM_HDW_ACTIVE)
+			break;
+	}
+
+	if (index == 10)
+		status = -1;
+
+	/*
+	 * check Cable ID
+	 */
+	cable_id = readb(&icom_port->dram->cable_id);
+
+	if (cable_id & ICOM_CABLE_ID_VALID) {
+		/* Get cable ID into the lower 4 bits (standard form) */
+		cable_id = (cable_id & ICOM_CABLE_ID_MASK) >> 4;
+		icom_port->cable_id = cable_id;
+	} else {
+		dev_err(&dev->dev,"Invalid or no cable attached\n");
+		icom_port->cable_id = NO_CABLE;
+	}
+
+      load_code_exit:
+
+	if (status != 0) {
+		/* Clear out any pending interrupts */
+		writew(0x3FFF, icom_port->int_reg);
+
+		/* Turn off port */
+		writeb(ICOM_DISABLE, &(icom_port->dram->disable));
+
+		/* Stop processor */
+		stop_processor(icom_port);
+
+		dev_err(&icom_port->adapter->pci_dev->dev,"Port not opertional\n");
+	}
+
+	if (new_page != NULL)
+		pci_free_consistent(dev, 4096, new_page, temp_pci);
+}
+
+static int startup(struct icom_port *icom_port)
+{
+	unsigned long temp;
+	unsigned char cable_id, raw_cable_id;
+	unsigned long flags;
+	int port;
+
+	trace(icom_port, "STARTUP", 0);
+
+	if (!icom_port->dram) {
+		/* should NEVER be NULL */
+		dev_err(&icom_port->adapter->pci_dev->dev,
+			"Unusable Port, port configuration missing\n");
+		return -ENODEV;
+	}
+
+	/*
+	 * check Cable ID
+	 */
+	raw_cable_id = readb(&icom_port->dram->cable_id);
+	trace(icom_port, "CABLE_ID", raw_cable_id);
+
+	/* Get cable ID into the lower 4 bits (standard form) */
+	cable_id = (raw_cable_id & ICOM_CABLE_ID_MASK) >> 4;
+
+	/* Check for valid Cable ID */
+	if (!(raw_cable_id & ICOM_CABLE_ID_VALID) ||
+	    (cable_id != icom_port->cable_id)) {
+
+		/* reload adapter code, pick up any potential changes in cable id */
+		load_code(icom_port);
+
+		/* still no sign of cable, error out */
+		raw_cable_id = readb(&icom_port->dram->cable_id);
+		cable_id = (raw_cable_id & ICOM_CABLE_ID_MASK) >> 4;
+		if (!(raw_cable_id & ICOM_CABLE_ID_VALID) ||
+		    (icom_port->cable_id == NO_CABLE))
+			return -EIO;
+	}
+
+	/*
+	 * Finally, clear and  enable interrupts
+	 */
+	spin_lock_irqsave(&icom_lock, flags);
+	port = icom_port->port;
+	if (port == 0 || port == 1)
+		int_mask_tbl[port].global_int_mask = &icom_port->global_reg->int_mask;
+	else
+		int_mask_tbl[port].global_int_mask = &icom_port->global_reg->int_mask_2;
+
+	if (port == 0 || port == 2)
+		writew(0x00FF, icom_port->int_reg);
+	else
+		writew(0x3F00, icom_port->int_reg);
+	if (port < 4) {
+		temp = readl(int_mask_tbl[port].global_int_mask);
+		writel(temp & ~int_mask_tbl[port].processor_id, int_mask_tbl[port].global_int_mask);
+
+		/* write flush */
+		readl(int_mask_tbl[port].global_int_mask);
+	} else {
+		dev_err(&icom_port->adapter->pci_dev->dev,
+                        "Invalid port assignment\n");
+	}
+
+	spin_unlock_irqrestore(&icom_lock, flags);
+	return 0;
+}
+
+static void shutdown(struct icom_port *icom_port)
+{
+	unsigned long temp;
+	unsigned char cmdReg;
+	unsigned long flags;
+	int port;
+
+	spin_lock_irqsave(&icom_lock, flags);
+	trace(icom_port, "SHUTDOWN", 0);
+
+	/*
+	 * disable all interrupts
+	 */
+	port = icom_port->port;
+	if (port == 0 || port == 1)
+		int_mask_tbl[port].global_int_mask = &icom_port->global_reg->int_mask;
+	else
+		int_mask_tbl[port].global_int_mask = &icom_port->global_reg->int_mask_2;
+
+	if (port < 4) {
+		temp = readl(int_mask_tbl[port].global_int_mask);
+		writel(temp | int_mask_tbl[port].processor_id, int_mask_tbl[port].global_int_mask);
+
+		/* write flush */
+		readl(int_mask_tbl[port].global_int_mask);
+	} else {
+		dev_err(&icom_port->adapter->pci_dev->dev,
+                        "Invalid port assignment\n");
+	}
+	spin_unlock_irqrestore(&icom_lock, flags);
+
+	/*
+	 * disable break condition
+	 */
+	cmdReg = readb(&icom_port->dram->CmdReg);
+	if (cmdReg & CMD_SND_BREAK) {
+		writeb(cmdReg & ~CMD_SND_BREAK, &icom_port->dram->CmdReg);
+	}
+}
+
+static int icom_write(struct uart_port *port)
+{
+	unsigned long data_count;
+	unsigned char cmdReg;
+	unsigned long offset;
+	int temp_tail = port->state->xmit.tail;
+
+	trace(ICOM_PORT, "WRITE", 0);
+
+	if (cpu_to_le16(ICOM_PORT->statStg->xmit[0].flags) &
+	    SA_FLAGS_READY_TO_XMIT) {
+		trace(ICOM_PORT, "WRITE_FULL", 0);
+		return 0;
+	}
+
+	data_count = 0;
+	while ((port->state->xmit.head != temp_tail) &&
+	       (data_count <= XMIT_BUFF_SZ)) {
+
+		ICOM_PORT->xmit_buf[data_count++] =
+		    port->state->xmit.buf[temp_tail];
+
+		temp_tail++;
+		temp_tail &= (UART_XMIT_SIZE - 1);
+	}
+
+	if (data_count) {
+		ICOM_PORT->statStg->xmit[0].flags =
+		    cpu_to_le16(SA_FLAGS_READY_TO_XMIT);
+		ICOM_PORT->statStg->xmit[0].leLength =
+		    cpu_to_le16(data_count);
+		offset =
+		    (unsigned long) &ICOM_PORT->statStg->xmit[0] -
+		    (unsigned long) ICOM_PORT->statStg;
+		*ICOM_PORT->xmitRestart =
+		    cpu_to_le32(ICOM_PORT->statStg_pci + offset);
+		cmdReg = readb(&ICOM_PORT->dram->CmdReg);
+		writeb(cmdReg | CMD_XMIT_RCV_ENABLE,
+		       &ICOM_PORT->dram->CmdReg);
+		writeb(START_XMIT, &ICOM_PORT->dram->StartXmitCmd);
+		trace(ICOM_PORT, "WRITE_START", data_count);
+		/* write flush */
+		readb(&ICOM_PORT->dram->StartXmitCmd);
+	}
+
+	return data_count;
+}
+
+static inline void check_modem_status(struct icom_port *icom_port)
+{
+	static char old_status = 0;
+	char delta_status;
+	unsigned char status;
+
+	spin_lock(&icom_port->uart_port.lock);
+
+	/*modem input register */
+	status = readb(&icom_port->dram->isr);
+	trace(icom_port, "CHECK_MODEM", status);
+	delta_status = status ^ old_status;
+	if (delta_status) {
+		if (delta_status & ICOM_RI)
+			icom_port->uart_port.icount.rng++;
+		if (delta_status & ICOM_DSR)
+			icom_port->uart_port.icount.dsr++;
+		if (delta_status & ICOM_DCD)
+			uart_handle_dcd_change(&icom_port->uart_port,
+					       delta_status & ICOM_DCD);
+		if (delta_status & ICOM_CTS)
+			uart_handle_cts_change(&icom_port->uart_port,
+					       delta_status & ICOM_CTS);
+
+		wake_up_interruptible(&icom_port->uart_port.state->
+				      port.delta_msr_wait);
+		old_status = status;
+	}
+	spin_unlock(&icom_port->uart_port.lock);
+}
+
+static void xmit_interrupt(u16 port_int_reg, struct icom_port *icom_port)
+{
+	unsigned short int count;
+	int i;
+
+	if (port_int_reg & (INT_XMIT_COMPLETED)) {
+		trace(icom_port, "XMIT_COMPLETE", 0);
+
+		/* clear buffer in use bit */
+		icom_port->statStg->xmit[0].flags &=
+			cpu_to_le16(~SA_FLAGS_READY_TO_XMIT);
+
+		count = (unsigned short int)
+			cpu_to_le16(icom_port->statStg->xmit[0].leLength);
+		icom_port->uart_port.icount.tx += count;
+
+		for (i=0; i<count &&
+			!uart_circ_empty(&icom_port->uart_port.state->xmit); i++) {
+
+			icom_port->uart_port.state->xmit.tail++;
+			icom_port->uart_port.state->xmit.tail &=
+				(UART_XMIT_SIZE - 1);
+		}
+
+		if (!icom_write(&icom_port->uart_port))
+			/* activate write queue */
+			uart_write_wakeup(&icom_port->uart_port);
+	} else
+		trace(icom_port, "XMIT_DISABLED", 0);
+}
+
+static void recv_interrupt(u16 port_int_reg, struct icom_port *icom_port)
+{
+	short int count, rcv_buff;
+	struct tty_struct *tty = icom_port->uart_port.state->port.tty;
+	unsigned short int status;
+	struct uart_icount *icount;
+	unsigned long offset;
+	unsigned char flag;
+
+	trace(icom_port, "RCV_COMPLETE", 0);
+	rcv_buff = icom_port->next_rcv;
+
+	status = cpu_to_le16(icom_port->statStg->rcv[rcv_buff].flags);
+	while (status & SA_FL_RCV_DONE) {
+		int first = -1;
+
+		trace(icom_port, "FID_STATUS", status);
+		count = cpu_to_le16(icom_port->statStg->rcv[rcv_buff].leLength);
+
+		trace(icom_port, "RCV_COUNT", count);
+
+		trace(icom_port, "REAL_COUNT", count);
+
+		offset =
+			cpu_to_le32(icom_port->statStg->rcv[rcv_buff].leBuffer) -
+			icom_port->recv_buf_pci;
+
+		/* Block copy all but the last byte as this may have status */
+		if (count > 0) {
+			first = icom_port->recv_buf[offset];
+			tty_insert_flip_string(tty, icom_port->recv_buf + offset, count - 1);
+		}
+
+		icount = &icom_port->uart_port.icount;
+		icount->rx += count;
+
+		/* Break detect logic */
+		if ((status & SA_FLAGS_FRAME_ERROR)
+		    && first == 0) {
+			status &= ~SA_FLAGS_FRAME_ERROR;
+			status |= SA_FLAGS_BREAK_DET;
+			trace(icom_port, "BREAK_DET", 0);
+		}
+
+		flag = TTY_NORMAL;
+
+		if (status &
+		    (SA_FLAGS_BREAK_DET | SA_FLAGS_PARITY_ERROR |
+		     SA_FLAGS_FRAME_ERROR | SA_FLAGS_OVERRUN)) {
+
+			if (status & SA_FLAGS_BREAK_DET)
+				icount->brk++;
+			if (status & SA_FLAGS_PARITY_ERROR)
+				icount->parity++;
+			if (status & SA_FLAGS_FRAME_ERROR)
+				icount->frame++;
+			if (status & SA_FLAGS_OVERRUN)
+				icount->overrun++;
+
+			/*
+			 * Now check to see if character should be
+			 * ignored, and mask off conditions which
+			 * should be ignored.
+			 */
+			if (status & icom_port->ignore_status_mask) {
+				trace(icom_port, "IGNORE_CHAR", 0);
+				goto ignore_char;
+			}
+
+			status &= icom_port->read_status_mask;
+
+			if (status & SA_FLAGS_BREAK_DET) {
+				flag = TTY_BREAK;
+			} else if (status & SA_FLAGS_PARITY_ERROR) {
+				trace(icom_port, "PARITY_ERROR", 0);
+				flag = TTY_PARITY;
+			} else if (status & SA_FLAGS_FRAME_ERROR)
+				flag = TTY_FRAME;
+
+		}
+
+		tty_insert_flip_char(tty, *(icom_port->recv_buf + offset + count - 1), flag);
+
+		if (status & SA_FLAGS_OVERRUN)
+			/*
+			 * Overrun is special, since it's
+			 * reported immediately, and doesn't
+			 * affect the current character
+			 */
+			tty_insert_flip_char(tty, 0, TTY_OVERRUN);
+ignore_char:
+		icom_port->statStg->rcv[rcv_buff].flags = 0;
+		icom_port->statStg->rcv[rcv_buff].leLength = 0;
+		icom_port->statStg->rcv[rcv_buff].WorkingLength =
+			(unsigned short int) cpu_to_le16(RCV_BUFF_SZ);
+
+		rcv_buff++;
+		if (rcv_buff == NUM_RBUFFS)
+			rcv_buff = 0;
+
+		status = cpu_to_le16(icom_port->statStg->rcv[rcv_buff].flags);
+	}
+	icom_port->next_rcv = rcv_buff;
+	tty_flip_buffer_push(tty);
+}
+
+static void process_interrupt(u16 port_int_reg,
+			      struct icom_port *icom_port)
+{
+
+	spin_lock(&icom_port->uart_port.lock);
+	trace(icom_port, "INTERRUPT", port_int_reg);
+
+	if (port_int_reg & (INT_XMIT_COMPLETED | INT_XMIT_DISABLED))
+		xmit_interrupt(port_int_reg, icom_port);
+
+	if (port_int_reg & INT_RCV_COMPLETED)
+		recv_interrupt(port_int_reg, icom_port);
+
+	spin_unlock(&icom_port->uart_port.lock);
+}
+
+static irqreturn_t icom_interrupt(int irq, void *dev_id)
+{
+	void __iomem * int_reg;
+	u32 adapter_interrupts;
+	u16 port_int_reg;
+	struct icom_adapter *icom_adapter;
+	struct icom_port *icom_port;
+
+	/* find icom_port for this interrupt */
+	icom_adapter = (struct icom_adapter *) dev_id;
+
+	if (icom_adapter->version == ADAPTER_V2) {
+		int_reg = icom_adapter->base_addr + 0x8024;
+
+		adapter_interrupts = readl(int_reg);
+
+		if (adapter_interrupts & 0x00003FFF) {
+			/* port 2 interrupt,  NOTE:  for all ADAPTER_V2, port 2 will be active */
+			icom_port = &icom_adapter->port_info[2];
+			port_int_reg = (u16) adapter_interrupts;
+			process_interrupt(port_int_reg, icom_port);
+			check_modem_status(icom_port);
+		}
+		if (adapter_interrupts & 0x3FFF0000) {
+			/* port 3 interrupt */
+			icom_port = &icom_adapter->port_info[3];
+			if (icom_port->status == ICOM_PORT_ACTIVE) {
+				port_int_reg =
+				    (u16) (adapter_interrupts >> 16);
+				process_interrupt(port_int_reg, icom_port);
+				check_modem_status(icom_port);
+			}
+		}
+
+		/* Clear out any pending interrupts */
+		writel(adapter_interrupts, int_reg);
+
+		int_reg = icom_adapter->base_addr + 0x8004;
+	} else {
+		int_reg = icom_adapter->base_addr + 0x4004;
+	}
+
+	adapter_interrupts = readl(int_reg);
+
+	if (adapter_interrupts & 0x00003FFF) {
+		/* port 0 interrupt, NOTE:  for all adapters, port 0 will be active */
+		icom_port = &icom_adapter->port_info[0];
+		port_int_reg = (u16) adapter_interrupts;
+		process_interrupt(port_int_reg, icom_port);
+		check_modem_status(icom_port);
+	}
+	if (adapter_interrupts & 0x3FFF0000) {
+		/* port 1 interrupt */
+		icom_port = &icom_adapter->port_info[1];
+		if (icom_port->status == ICOM_PORT_ACTIVE) {
+			port_int_reg = (u16) (adapter_interrupts >> 16);
+			process_interrupt(port_int_reg, icom_port);
+			check_modem_status(icom_port);
+		}
+	}
+
+	/* Clear out any pending interrupts */
+	writel(adapter_interrupts, int_reg);
+
+	/* flush the write */
+	adapter_interrupts = readl(int_reg);
+
+	return IRQ_HANDLED;
+}
+
+/*
+ * ------------------------------------------------------------------
+ * Begin serial-core API
+ * ------------------------------------------------------------------
+ */
+static unsigned int icom_tx_empty(struct uart_port *port)
+{
+	int ret;
+	unsigned long flags;
+
+	spin_lock_irqsave(&port->lock, flags);
+	if (cpu_to_le16(ICOM_PORT->statStg->xmit[0].flags) &
+	    SA_FLAGS_READY_TO_XMIT)
+		ret = TIOCSER_TEMT;
+	else
+		ret = 0;
+
+	spin_unlock_irqrestore(&port->lock, flags);
+	return ret;
+}
+
+static void icom_set_mctrl(struct uart_port *port, unsigned int mctrl)
+{
+	unsigned char local_osr;
+
+	trace(ICOM_PORT, "SET_MODEM", 0);
+	local_osr = readb(&ICOM_PORT->dram->osr);
+
+	if (mctrl & TIOCM_RTS) {
+		trace(ICOM_PORT, "RAISE_RTS", 0);
+		local_osr |= ICOM_RTS;
+	} else {
+		trace(ICOM_PORT, "LOWER_RTS", 0);
+		local_osr &= ~ICOM_RTS;
+	}
+
+	if (mctrl & TIOCM_DTR) {
+		trace(ICOM_PORT, "RAISE_DTR", 0);
+		local_osr |= ICOM_DTR;
+	} else {
+		trace(ICOM_PORT, "LOWER_DTR", 0);
+		local_osr &= ~ICOM_DTR;
+	}
+
+	writeb(local_osr, &ICOM_PORT->dram->osr);
+}
+
+static unsigned int icom_get_mctrl(struct uart_port *port)
+{
+	unsigned char status;
+	unsigned int result;
+
+	trace(ICOM_PORT, "GET_MODEM", 0);
+
+	status = readb(&ICOM_PORT->dram->isr);
+
+	result = ((status & ICOM_DCD) ? TIOCM_CAR : 0)
+	    | ((status & ICOM_RI) ? TIOCM_RNG : 0)
+	    | ((status & ICOM_DSR) ? TIOCM_DSR : 0)
+	    | ((status & ICOM_CTS) ? TIOCM_CTS : 0);
+	return result;
+}
+
+static void icom_stop_tx(struct uart_port *port)
+{
+	unsigned char cmdReg;
+
+	trace(ICOM_PORT, "STOP", 0);
+	cmdReg = readb(&ICOM_PORT->dram->CmdReg);
+	writeb(cmdReg | CMD_HOLD_XMIT, &ICOM_PORT->dram->CmdReg);
+}
+
+static void icom_start_tx(struct uart_port *port)
+{
+	unsigned char cmdReg;
+
+	trace(ICOM_PORT, "START", 0);
+	cmdReg = readb(&ICOM_PORT->dram->CmdReg);
+	if ((cmdReg & CMD_HOLD_XMIT) == CMD_HOLD_XMIT)
+		writeb(cmdReg & ~CMD_HOLD_XMIT,
+		       &ICOM_PORT->dram->CmdReg);
+
+	icom_write(port);
+}
+
+static void icom_send_xchar(struct uart_port *port, char ch)
+{
+	unsigned char xdata;
+	int index;
+	unsigned long flags;
+
+	trace(ICOM_PORT, "SEND_XCHAR", ch);
+
+	/* wait .1 sec to send char */
+	for (index = 0; index < 10; index++) {
+		spin_lock_irqsave(&port->lock, flags);
+		xdata = readb(&ICOM_PORT->dram->xchar);
+		if (xdata == 0x00) {
+			trace(ICOM_PORT, "QUICK_WRITE", 0);
+			writeb(ch, &ICOM_PORT->dram->xchar);
+
+			/* flush write operation */
+			xdata = readb(&ICOM_PORT->dram->xchar);
+			spin_unlock_irqrestore(&port->lock, flags);
+			break;
+		}
+		spin_unlock_irqrestore(&port->lock, flags);
+		msleep(10);
+	}
+}
+
+static void icom_stop_rx(struct uart_port *port)
+{
+	unsigned char cmdReg;
+
+	cmdReg = readb(&ICOM_PORT->dram->CmdReg);
+	writeb(cmdReg & ~CMD_RCV_ENABLE, &ICOM_PORT->dram->CmdReg);
+}
+
+static void icom_enable_ms(struct uart_port *port)
+{
+	/* no-op */
+}
+
+static void icom_break(struct uart_port *port, int break_state)
+{
+	unsigned char cmdReg;
+	unsigned long flags;
+
+	spin_lock_irqsave(&port->lock, flags);
+	trace(ICOM_PORT, "BREAK", 0);
+	cmdReg = readb(&ICOM_PORT->dram->CmdReg);
+	if (break_state == -1) {
+		writeb(cmdReg | CMD_SND_BREAK, &ICOM_PORT->dram->CmdReg);
+	} else {
+		writeb(cmdReg & ~CMD_SND_BREAK, &ICOM_PORT->dram->CmdReg);
+	}
+	spin_unlock_irqrestore(&port->lock, flags);
+}
+
+static int icom_open(struct uart_port *port)
+{
+	int retval;
+
+	kref_get(&ICOM_PORT->adapter->kref);
+	retval = startup(ICOM_PORT);
+
+	if (retval) {
+		kref_put(&ICOM_PORT->adapter->kref, icom_kref_release);
+		trace(ICOM_PORT, "STARTUP_ERROR", 0);
+		return retval;
+	}
+
+	return 0;
+}
+
+static void icom_close(struct uart_port *port)
+{
+	unsigned char cmdReg;
+
+	trace(ICOM_PORT, "CLOSE", 0);
+
+	/* stop receiver */
+	cmdReg = readb(&ICOM_PORT->dram->CmdReg);
+	writeb(cmdReg & (unsigned char) ~CMD_RCV_ENABLE,
+	       &ICOM_PORT->dram->CmdReg);
+
+	shutdown(ICOM_PORT);
+
+	kref_put(&ICOM_PORT->adapter->kref, icom_kref_release);
+}
+
+static void icom_set_termios(struct uart_port *port,
+			     struct ktermios *termios,
+			     struct ktermios *old_termios)
+{
+	int baud;
+	unsigned cflag, iflag;
+	char new_config2;
+	char new_config3 = 0;
+	char tmp_byte;
+	int index;
+	int rcv_buff, xmit_buff;
+	unsigned long offset;
+	unsigned long flags;
+
+	spin_lock_irqsave(&port->lock, flags);
+	trace(ICOM_PORT, "CHANGE_SPEED", 0);
+
+	cflag = termios->c_cflag;
+	iflag = termios->c_iflag;
+
+	new_config2 = ICOM_ACFG_DRIVE1;
+
+	/* byte size and parity */
+	switch (cflag & CSIZE) {
+	case CS5:		/* 5 bits/char */
+		new_config2 |= ICOM_ACFG_5BPC;
+		break;
+	case CS6:		/* 6 bits/char */
+		new_config2 |= ICOM_ACFG_6BPC;
+		break;
+	case CS7:		/* 7 bits/char */
+		new_config2 |= ICOM_ACFG_7BPC;
+		break;
+	case CS8:		/* 8 bits/char */
+		new_config2 |= ICOM_ACFG_8BPC;
+		break;
+	default:
+		break;
+	}
+	if (cflag & CSTOPB) {
+		/* 2 stop bits */
+		new_config2 |= ICOM_ACFG_2STOP_BIT;
+	}
+	if (cflag & PARENB) {
+		/* parity bit enabled */
+		new_config2 |= ICOM_ACFG_PARITY_ENAB;
+		trace(ICOM_PORT, "PARENB", 0);
+	}
+	if (cflag & PARODD) {
+		/* odd parity */
+		new_config2 |= ICOM_ACFG_PARITY_ODD;
+		trace(ICOM_PORT, "PARODD", 0);
+	}
+
+	/* Determine divisor based on baud rate */
+	baud = uart_get_baud_rate(port, termios, old_termios,
+				  icom_acfg_baud[0],
+				  icom_acfg_baud[BAUD_TABLE_LIMIT]);
+	if (!baud)
+		baud = 9600;	/* B0 transition handled in rs_set_termios */
+
+	for (index = 0; index < BAUD_TABLE_LIMIT; index++) {
+		if (icom_acfg_baud[index] == baud) {
+			new_config3 = index;
+			break;
+		}
+	}
+
+	uart_update_timeout(port, cflag, baud);
+
+	/* CTS flow control flag and modem status interrupts */
+	tmp_byte = readb(&(ICOM_PORT->dram->HDLCConfigReg));
+	if (cflag & CRTSCTS)
+		tmp_byte |= HDLC_HDW_FLOW;
+	else
+		tmp_byte &= ~HDLC_HDW_FLOW;
+	writeb(tmp_byte, &(ICOM_PORT->dram->HDLCConfigReg));
+
+	/*
+	 * Set up parity check flag
+	 */
+	ICOM_PORT->read_status_mask = SA_FLAGS_OVERRUN | SA_FL_RCV_DONE;
+	if (iflag & INPCK)
+		ICOM_PORT->read_status_mask |=
+		    SA_FLAGS_FRAME_ERROR | SA_FLAGS_PARITY_ERROR;
+
+	if ((iflag & BRKINT) || (iflag & PARMRK))
+		ICOM_PORT->read_status_mask |= SA_FLAGS_BREAK_DET;
+
+	/*
+	 * Characters to ignore
+	 */
+	ICOM_PORT->ignore_status_mask = 0;
+	if (iflag & IGNPAR)
+		ICOM_PORT->ignore_status_mask |=
+		    SA_FLAGS_PARITY_ERROR | SA_FLAGS_FRAME_ERROR;
+	if (iflag & IGNBRK) {
+		ICOM_PORT->ignore_status_mask |= SA_FLAGS_BREAK_DET;
+		/*
+		 * If we're ignore parity and break indicators, ignore
+		 * overruns too.  (For real raw support).
+		 */
+		if (iflag & IGNPAR)
+			ICOM_PORT->ignore_status_mask |= SA_FLAGS_OVERRUN;
+	}
+
+	/*
+	 * !!! ignore all characters if CREAD is not set
+	 */
+	if ((cflag & CREAD) == 0)
+		ICOM_PORT->ignore_status_mask |= SA_FL_RCV_DONE;
+
+	/* Turn off Receiver to prepare for reset */
+	writeb(CMD_RCV_DISABLE, &ICOM_PORT->dram->CmdReg);
+
+	for (index = 0; index < 10; index++) {
+		if (readb(&ICOM_PORT->dram->PrevCmdReg) == 0x00) {
+			break;
+		}
+	}
+
+	/* clear all current buffers of data */
+	for (rcv_buff = 0; rcv_buff < NUM_RBUFFS; rcv_buff++) {
+		ICOM_PORT->statStg->rcv[rcv_buff].flags = 0;
+		ICOM_PORT->statStg->rcv[rcv_buff].leLength = 0;
+		ICOM_PORT->statStg->rcv[rcv_buff].WorkingLength =
+		    (unsigned short int) cpu_to_le16(RCV_BUFF_SZ);
+	}
+
+	for (xmit_buff = 0; xmit_buff < NUM_XBUFFS; xmit_buff++) {
+		ICOM_PORT->statStg->xmit[xmit_buff].flags = 0;
+	}
+
+	/* activate changes and start xmit and receiver here */
+	/* Enable the receiver */
+	writeb(new_config3, &(ICOM_PORT->dram->async_config3));
+	writeb(new_config2, &(ICOM_PORT->dram->async_config2));
+	tmp_byte = readb(&(ICOM_PORT->dram->HDLCConfigReg));
+	tmp_byte |= HDLC_PPP_PURE_ASYNC | HDLC_FF_FILL;
+	writeb(tmp_byte, &(ICOM_PORT->dram->HDLCConfigReg));
+	writeb(0x04, &(ICOM_PORT->dram->FlagFillIdleTimer));	/* 0.5 seconds */
+	writeb(0xFF, &(ICOM_PORT->dram->ier));	/* enable modem signal interrupts */
+
+	/* reset processor */
+	writeb(CMD_RESTART, &ICOM_PORT->dram->CmdReg);
+
+	for (index = 0; index < 10; index++) {
+		if (readb(&ICOM_PORT->dram->CmdReg) == 0x00) {
+			break;
+		}
+	}
+
+	/* Enable Transmitter and Receiver */
+	offset =
+	    (unsigned long) &ICOM_PORT->statStg->rcv[0] -
+	    (unsigned long) ICOM_PORT->statStg;
+	writel(ICOM_PORT->statStg_pci + offset,
+	       &ICOM_PORT->dram->RcvStatusAddr);
+	ICOM_PORT->next_rcv = 0;
+	ICOM_PORT->put_length = 0;
+	*ICOM_PORT->xmitRestart = 0;
+	writel(ICOM_PORT->xmitRestart_pci,
+	       &ICOM_PORT->dram->XmitStatusAddr);
+	trace(ICOM_PORT, "XR_ENAB", 0);
+	writeb(CMD_XMIT_RCV_ENABLE, &ICOM_PORT->dram->CmdReg);
+
+	spin_unlock_irqrestore(&port->lock, flags);
+}
+
+static const char *icom_type(struct uart_port *port)
+{
+	return "icom";
+}
+
+static void icom_release_port(struct uart_port *port)
+{
+}
+
+static int icom_request_port(struct uart_port *port)
+{
+	return 0;
+}
+
+static void icom_config_port(struct uart_port *port, int flags)
+{
+	port->type = PORT_ICOM;
+}
+
+static struct uart_ops icom_ops = {
+	.tx_empty = icom_tx_empty,
+	.set_mctrl = icom_set_mctrl,
+	.get_mctrl = icom_get_mctrl,
+	.stop_tx = icom_stop_tx,
+	.start_tx = icom_start_tx,
+	.send_xchar = icom_send_xchar,
+	.stop_rx = icom_stop_rx,
+	.enable_ms = icom_enable_ms,
+	.break_ctl = icom_break,
+	.startup = icom_open,
+	.shutdown = icom_close,
+	.set_termios = icom_set_termios,
+	.type = icom_type,
+	.release_port = icom_release_port,
+	.request_port = icom_request_port,
+	.config_port = icom_config_port,
+};
+
+#define ICOM_CONSOLE NULL
+
+static struct uart_driver icom_uart_driver = {
+	.owner = THIS_MODULE,
+	.driver_name = ICOM_DRIVER_NAME,
+	.dev_name = "ttyA",
+	.major = ICOM_MAJOR,
+	.minor = ICOM_MINOR_START,
+	.nr = NR_PORTS,
+	.cons = ICOM_CONSOLE,
+};
+
+static int __devinit icom_init_ports(struct icom_adapter *icom_adapter)
+{
+	u32 subsystem_id = icom_adapter->subsystem_id;
+	int i;
+	struct icom_port *icom_port;
+
+	if (icom_adapter->version == ADAPTER_V1) {
+		icom_adapter->numb_ports = 2;
+
+		for (i = 0; i < 2; i++) {
+			icom_port = &icom_adapter->port_info[i];
+			icom_port->port = i;
+			icom_port->status = ICOM_PORT_ACTIVE;
+			icom_port->imbed_modem = ICOM_UNKNOWN;
+		}
+	} else {
+		if (subsystem_id == PCI_DEVICE_ID_IBM_ICOM_FOUR_PORT_MODEL) {
+			icom_adapter->numb_ports = 4;
+
+			for (i = 0; i < 4; i++) {
+				icom_port = &icom_adapter->port_info[i];
+
+				icom_port->port = i;
+				icom_port->status = ICOM_PORT_ACTIVE;
+				icom_port->imbed_modem = ICOM_IMBED_MODEM;
+			}
+		} else {
+			icom_adapter->numb_ports = 4;
+
+			icom_adapter->port_info[0].port = 0;
+			icom_adapter->port_info[0].status = ICOM_PORT_ACTIVE;
+
+			if (subsystem_id ==
+			    PCI_DEVICE_ID_IBM_ICOM_V2_ONE_PORT_RVX_ONE_PORT_MDM) {
+				icom_adapter->port_info[0].imbed_modem = ICOM_IMBED_MODEM;
+			} else {
+				icom_adapter->port_info[0].imbed_modem = ICOM_RVX;
+			}
+
+			icom_adapter->port_info[1].status = ICOM_PORT_OFF;
+
+			icom_adapter->port_info[2].port = 2;
+			icom_adapter->port_info[2].status = ICOM_PORT_ACTIVE;
+			icom_adapter->port_info[2].imbed_modem = ICOM_RVX;
+			icom_adapter->port_info[3].status = ICOM_PORT_OFF;
+		}
+	}
+
+	return 0;
+}
+
+static void icom_port_active(struct icom_port *icom_port, struct icom_adapter *icom_adapter, int port_num)
+{
+	if (icom_adapter->version == ADAPTER_V1) {
+		icom_port->global_reg = icom_adapter->base_addr + 0x4000;
+		icom_port->int_reg = icom_adapter->base_addr +
+		    0x4004 + 2 - 2 * port_num;
+	} else {
+		icom_port->global_reg = icom_adapter->base_addr + 0x8000;
+		if (icom_port->port < 2)
+			icom_port->int_reg = icom_adapter->base_addr +
+			    0x8004 + 2 - 2 * icom_port->port;
+		else
+			icom_port->int_reg = icom_adapter->base_addr +
+			    0x8024 + 2 - 2 * (icom_port->port - 2);
+	}
+}
+static int __devinit icom_load_ports(struct icom_adapter *icom_adapter)
+{
+	struct icom_port *icom_port;
+	int port_num;
+
+	for (port_num = 0; port_num < icom_adapter->numb_ports; port_num++) {
+
+		icom_port = &icom_adapter->port_info[port_num];
+
+		if (icom_port->status == ICOM_PORT_ACTIVE) {
+			icom_port_active(icom_port, icom_adapter, port_num);
+			icom_port->dram = icom_adapter->base_addr +
+					0x2000 * icom_port->port;
+
+			icom_port->adapter = icom_adapter;
+
+			/* get port memory */
+			if (get_port_memory(icom_port) != 0) {
+				dev_err(&icom_port->adapter->pci_dev->dev,
+					"Memory allocation for port FAILED\n");
+			}
+		}
+	}
+	return 0;
+}
+
+static int __devinit icom_alloc_adapter(struct icom_adapter
+					**icom_adapter_ref)
+{
+	int adapter_count = 0;
+	struct icom_adapter *icom_adapter;
+	struct icom_adapter *cur_adapter_entry;
+	struct list_head *tmp;
+
+	icom_adapter = (struct icom_adapter *)
+	    kzalloc(sizeof(struct icom_adapter), GFP_KERNEL);
+
+	if (!icom_adapter) {
+		return -ENOMEM;
+	}
+
+	list_for_each(tmp, &icom_adapter_head) {
+		cur_adapter_entry =
+		    list_entry(tmp, struct icom_adapter,
+			       icom_adapter_entry);
+		if (cur_adapter_entry->index != adapter_count) {
+			break;
+		}
+		adapter_count++;
+	}
+
+	icom_adapter->index = adapter_count;
+	list_add_tail(&icom_adapter->icom_adapter_entry, tmp);
+
+	*icom_adapter_ref = icom_adapter;
+	return 0;
+}
+
+static void icom_free_adapter(struct icom_adapter *icom_adapter)
+{
+	list_del(&icom_adapter->icom_adapter_entry);
+	kfree(icom_adapter);
+}
+
+static void icom_remove_adapter(struct icom_adapter *icom_adapter)
+{
+	struct icom_port *icom_port;
+	int index;
+
+	for (index = 0; index < icom_adapter->numb_ports; index++) {
+		icom_port = &icom_adapter->port_info[index];
+
+		if (icom_port->status == ICOM_PORT_ACTIVE) {
+			dev_info(&icom_adapter->pci_dev->dev,
+				 "Device removed\n");
+
+			uart_remove_one_port(&icom_uart_driver,
+					     &icom_port->uart_port);
+
+			/* be sure that DTR and RTS are dropped */
+			writeb(0x00, &icom_port->dram->osr);
+
+			/* Wait 0.1 Sec for simple Init to complete */
+			msleep(100);
+
+			/* Stop proccessor */
+			stop_processor(icom_port);
+
+			free_port_memory(icom_port);
+		}
+	}
+
+	free_irq(icom_adapter->pci_dev->irq, (void *) icom_adapter);
+	iounmap(icom_adapter->base_addr);
+	pci_release_regions(icom_adapter->pci_dev);
+	icom_free_adapter(icom_adapter);
+}
+
+static void icom_kref_release(struct kref *kref)
+{
+	struct icom_adapter *icom_adapter;
+
+	icom_adapter = to_icom_adapter(kref);
+	icom_remove_adapter(icom_adapter);
+}
+
+static int __devinit icom_probe(struct pci_dev *dev,
+				const struct pci_device_id *ent)
+{
+	int index;
+	unsigned int command_reg;
+	int retval;
+	struct icom_adapter *icom_adapter;
+	struct icom_port *icom_port;
+
+	retval = pci_enable_device(dev);
+	if (retval) {
+		dev_err(&dev->dev, "Device enable FAILED\n");
+		return retval;
+	}
+
+	if ( (retval = pci_request_regions(dev, "icom"))) {
+		 dev_err(&dev->dev, "pci_request_regions FAILED\n");
+		 pci_disable_device(dev);
+		 return retval;
+	 }
+
+	pci_set_master(dev);
+
+	if ( (retval = pci_read_config_dword(dev, PCI_COMMAND, &command_reg))) {
+		dev_err(&dev->dev, "PCI Config read FAILED\n");
+		return retval;
+	}
+
+	pci_write_config_dword(dev, PCI_COMMAND,
+		command_reg | PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER
+ 		| PCI_COMMAND_PARITY | PCI_COMMAND_SERR);
+
+	if (ent->driver_data == ADAPTER_V1) {
+		pci_write_config_dword(dev, 0x44, 0x8300830A);
+	} else {
+		pci_write_config_dword(dev, 0x44, 0x42004200);
+		pci_write_config_dword(dev, 0x48, 0x42004200);
+	}
+
+
+	retval = icom_alloc_adapter(&icom_adapter);
+	if (retval) {
+		 dev_err(&dev->dev, "icom_alloc_adapter FAILED\n");
+		 retval = -EIO;
+		 goto probe_exit0;
+	}
+
+	icom_adapter->base_addr_pci = pci_resource_start(dev, 0);
+	icom_adapter->pci_dev = dev;
+	icom_adapter->version = ent->driver_data;
+	icom_adapter->subsystem_id = ent->subdevice;
+
+
+	retval = icom_init_ports(icom_adapter);
+	if (retval) {
+		dev_err(&dev->dev, "Port configuration failed\n");
+		goto probe_exit1;
+	}
+
+	icom_adapter->base_addr = pci_ioremap_bar(dev, 0);
+
+	if (!icom_adapter->base_addr)
+		goto probe_exit1;
+
+	 /* save off irq and request irq line */
+	 if ( (retval = request_irq(dev->irq, icom_interrupt,
+				   IRQF_SHARED, ICOM_DRIVER_NAME,
+				   (void *) icom_adapter))) {
+		  goto probe_exit2;
+	 }
+
+	retval = icom_load_ports(icom_adapter);
+
+	for (index = 0; index < icom_adapter->numb_ports; index++) {
+		icom_port = &icom_adapter->port_info[index];
+
+		if (icom_port->status == ICOM_PORT_ACTIVE) {
+			icom_port->uart_port.irq = icom_port->adapter->pci_dev->irq;
+			icom_port->uart_port.type = PORT_ICOM;
+			icom_port->uart_port.iotype = UPIO_MEM;
+			icom_port->uart_port.membase =
+					       (char *) icom_adapter->base_addr_pci;
+			icom_port->uart_port.fifosize = 16;
+			icom_port->uart_port.ops = &icom_ops;
+			icom_port->uart_port.line =
+		        icom_port->port + icom_adapter->index * 4;
+			if (uart_add_one_port (&icom_uart_driver, &icom_port->uart_port)) {
+				icom_port->status = ICOM_PORT_OFF;
+				dev_err(&dev->dev, "Device add failed\n");
+			 } else
+				dev_info(&dev->dev, "Device added\n");
+		}
+	}
+
+	kref_init(&icom_adapter->kref);
+	return 0;
+
+probe_exit2:
+	iounmap(icom_adapter->base_addr);
+probe_exit1:
+	icom_free_adapter(icom_adapter);
+
+probe_exit0:
+	pci_release_regions(dev);
+	pci_disable_device(dev);
+
+	return retval;
+}
+
+static void __devexit icom_remove(struct pci_dev *dev)
+{
+	struct icom_adapter *icom_adapter;
+	struct list_head *tmp;
+
+	list_for_each(tmp, &icom_adapter_head) {
+		icom_adapter = list_entry(tmp, struct icom_adapter,
+					  icom_adapter_entry);
+		if (icom_adapter->pci_dev == dev) {
+			kref_put(&icom_adapter->kref, icom_kref_release);
+			return;
+		}
+	}
+
+	dev_err(&dev->dev, "Unable to find device to remove\n");
+}
+
+static struct pci_driver icom_pci_driver = {
+	.name = ICOM_DRIVER_NAME,
+	.id_table = icom_pci_table,
+	.probe = icom_probe,
+	.remove = __devexit_p(icom_remove),
+};
+
+static int __init icom_init(void)
+{
+	int ret;
+
+	spin_lock_init(&icom_lock);
+
+	ret = uart_register_driver(&icom_uart_driver);
+	if (ret)
+		return ret;
+
+	ret = pci_register_driver(&icom_pci_driver);
+
+	if (ret < 0)
+		uart_unregister_driver(&icom_uart_driver);
+
+	return ret;
+}
+
+static void __exit icom_exit(void)
+{
+	pci_unregister_driver(&icom_pci_driver);
+	uart_unregister_driver(&icom_uart_driver);
+}
+
+module_init(icom_init);
+module_exit(icom_exit);
+
+MODULE_AUTHOR("Michael Anderson <mjanders@us.ibm.com>");
+MODULE_DESCRIPTION("IBM iSeries Serial IOA driver");
+MODULE_SUPPORTED_DEVICE
+    ("IBM iSeries 2745, 2771, 2772, 2742, 2793 and 2805 Communications adapters");
+MODULE_LICENSE("GPL");
+MODULE_FIRMWARE("icom_call_setup.bin");
+MODULE_FIRMWARE("icom_res_dce.bin");
+MODULE_FIRMWARE("icom_asc.bin");
diff --git a/ap/os/linux/linux-3.4.x/drivers/tty/serial/icom.h b/ap/os/linux/linux-3.4.x/drivers/tty/serial/icom.h
new file mode 100644
index 0000000..c8029e0
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/tty/serial/icom.h
@@ -0,0 +1,287 @@
+/*
+ * icom.h
+ *
+ * Copyright (C) 2001 Michael Anderson, IBM Corporation
+ *
+ * Serial device driver include file.
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ */
+
+#include <linux/serial_core.h>
+
+#define BAUD_TABLE_LIMIT	((sizeof(icom_acfg_baud)/sizeof(int)) - 1)
+static int icom_acfg_baud[] = {
+	300,
+	600,
+	900,
+	1200,
+	1800,
+	2400,
+	3600,
+	4800,
+	7200,
+	9600,
+	14400,
+	19200,
+	28800,
+	38400,
+	57600,
+	76800,
+	115200,
+	153600,
+	230400,
+	307200,
+	460800,
+};
+
+struct icom_regs {
+	u32 control;		/* Adapter Control Register     */
+	u32 interrupt;		/* Adapter Interrupt Register   */
+	u32 int_mask;		/* Adapter Interrupt Mask Reg   */
+	u32 int_pri;		/* Adapter Interrupt Priority r */
+	u32 int_reg_b;		/* Adapter non-masked Interrupt */
+	u32 resvd01;
+	u32 resvd02;
+	u32 resvd03;
+	u32 control_2;		/* Adapter Control Register 2   */
+	u32 interrupt_2;	/* Adapter Interrupt Register 2 */
+	u32 int_mask_2;		/* Adapter Interrupt Mask 2     */
+	u32 int_pri_2;		/* Adapter Interrupt Prior 2    */
+	u32 int_reg_2b;		/* Adapter non-masked 2         */
+};
+
+struct func_dram {
+	u32 reserved[108];	/* 0-1B0   reserved by personality code */
+	u32 RcvStatusAddr;	/* 1B0-1B3 Status Address for Next rcv */
+	u8 RcvStnAddr;		/* 1B4     Receive Station Addr */
+	u8 IdleState;		/* 1B5     Idle State */
+	u8 IdleMonitor;		/* 1B6     Idle Monitor */
+	u8 FlagFillIdleTimer;	/* 1B7     Flag Fill Idle Timer */
+	u32 XmitStatusAddr;	/* 1B8-1BB Transmit Status Address */
+	u8 StartXmitCmd;	/* 1BC     Start Xmit Command */
+	u8 HDLCConfigReg;	/* 1BD     Reserved */
+	u8 CauseCode;		/* 1BE     Cause code for fatal error */
+	u8 xchar;		/* 1BF     High priority send */
+	u32 reserved3;		/* 1C0-1C3 Reserved */
+	u8 PrevCmdReg;		/* 1C4     Reserved */
+	u8 CmdReg;		/* 1C5     Command Register */
+	u8 async_config2;	/* 1C6     Async Config Byte 2 */
+	u8 async_config3;	/* 1C7     Async Config Byte 3 */
+	u8 dce_resvd[20];	/* 1C8-1DB DCE Rsvd           */
+	u8 dce_resvd21;		/* 1DC     DCE Rsvd (21st byte */
+	u8 misc_flags;		/* 1DD     misc flags         */
+#define V2_HARDWARE     0x40
+#define ICOM_HDW_ACTIVE 0x01
+	u8 call_length;		/* 1DE     Phone #/CFI buff ln */
+	u8 call_length2;	/* 1DF     Upper byte (unused) */
+	u32 call_addr;		/* 1E0-1E3 Phn #/CFI buff addr */
+	u16 timer_value;	/* 1E4-1E5 general timer value */
+	u8 timer_command;	/* 1E6     general timer cmd  */
+	u8 dce_command;		/* 1E7     dce command reg    */
+	u8 dce_cmd_status;	/* 1E8     dce command stat   */
+	u8 x21_r1_ioff;		/* 1E9     dce ready counter  */
+	u8 x21_r0_ioff;		/* 1EA     dce not ready ctr  */
+	u8 x21_ralt_ioff;	/* 1EB     dce CNR counter    */
+	u8 x21_r1_ion;		/* 1EC     dce ready I on ctr */
+	u8 rsvd_ier;		/* 1ED     Rsvd for IER (if ne */
+	u8 ier;			/* 1EE     Interrupt Enable   */
+	u8 isr;			/* 1EF     Input Signal Reg   */
+	u8 osr;			/* 1F0     Output Signal Reg  */
+	u8 reset;		/* 1F1     Reset/Reload Reg   */
+	u8 disable;		/* 1F2     Disable Reg        */
+	u8 sync;		/* 1F3     Sync Reg           */
+	u8 error_stat;		/* 1F4     Error Status       */
+	u8 cable_id;		/* 1F5     Cable ID           */
+	u8 cs_length;		/* 1F6     CS Load Length     */
+	u8 mac_length;		/* 1F7     Mac Load Length    */
+	u32 cs_load_addr;	/* 1F8-1FB Call Load PCI Addr */
+	u32 mac_load_addr;	/* 1FC-1FF Mac Load PCI Addr  */
+};
+
+/*
+ * adapter defines and structures
+ */
+#define ICOM_CONTROL_START_A         0x00000008
+#define ICOM_CONTROL_STOP_A          0x00000004
+#define ICOM_CONTROL_START_B         0x00000002
+#define ICOM_CONTROL_STOP_B          0x00000001
+#define ICOM_CONTROL_START_C         0x00000008
+#define ICOM_CONTROL_STOP_C          0x00000004
+#define ICOM_CONTROL_START_D         0x00000002
+#define ICOM_CONTROL_STOP_D          0x00000001
+#define ICOM_IRAM_OFFSET             0x1000
+#define ICOM_IRAM_SIZE               0x0C00
+#define ICOM_DCE_IRAM_OFFSET         0x0A00
+#define ICOM_CABLE_ID_VALID          0x01
+#define ICOM_CABLE_ID_MASK           0xF0
+#define ICOM_DISABLE                 0x80
+#define CMD_XMIT_RCV_ENABLE          0xC0
+#define CMD_XMIT_ENABLE              0x40
+#define CMD_RCV_DISABLE              0x00
+#define CMD_RCV_ENABLE               0x80
+#define CMD_RESTART                  0x01
+#define CMD_HOLD_XMIT                0x02
+#define CMD_SND_BREAK                0x04
+#define RS232_CABLE                  0x06
+#define V24_CABLE                    0x0E
+#define V35_CABLE                    0x0C
+#define V36_CABLE                    0x02
+#define NO_CABLE                     0x00
+#define START_DOWNLOAD               0x80
+#define ICOM_INT_MASK_PRC_A          0x00003FFF
+#define ICOM_INT_MASK_PRC_B          0x3FFF0000
+#define ICOM_INT_MASK_PRC_C          0x00003FFF
+#define ICOM_INT_MASK_PRC_D          0x3FFF0000
+#define INT_RCV_COMPLETED            0x1000
+#define INT_XMIT_COMPLETED           0x2000
+#define INT_IDLE_DETECT              0x0800
+#define INT_RCV_DISABLED             0x0400
+#define INT_XMIT_DISABLED            0x0200
+#define INT_RCV_XMIT_SHUTDOWN        0x0100
+#define INT_FATAL_ERROR              0x0080
+#define INT_CABLE_PULL               0x0020
+#define INT_SIGNAL_CHANGE            0x0010
+#define HDLC_PPP_PURE_ASYNC          0x02
+#define HDLC_FF_FILL                 0x00
+#define HDLC_HDW_FLOW                0x01
+#define START_XMIT                   0x80
+#define ICOM_ACFG_DRIVE1             0x20
+#define ICOM_ACFG_NO_PARITY          0x00
+#define ICOM_ACFG_PARITY_ENAB        0x02
+#define ICOM_ACFG_PARITY_ODD         0x01
+#define ICOM_ACFG_8BPC               0x00
+#define ICOM_ACFG_7BPC               0x04
+#define ICOM_ACFG_6BPC               0x08
+#define ICOM_ACFG_5BPC               0x0C
+#define ICOM_ACFG_1STOP_BIT          0x00
+#define ICOM_ACFG_2STOP_BIT          0x10
+#define ICOM_DTR                     0x80
+#define ICOM_RTS                     0x40
+#define ICOM_RI                      0x08
+#define ICOM_DSR                     0x80
+#define ICOM_DCD                     0x20
+#define ICOM_CTS                     0x40
+
+#define NUM_XBUFFS 1
+#define NUM_RBUFFS 2
+#define RCV_BUFF_SZ 0x0200
+#define XMIT_BUFF_SZ 0x1000
+struct statusArea {
+    /**********************************************/
+	/* Transmit Status Area                       */
+    /**********************************************/
+	struct xmit_status_area{
+		u32 leNext;	/* Next entry in Little Endian on Adapter */
+		u32 leNextASD;
+		u32 leBuffer;	/* Buffer for entry in LE for Adapter */
+		u16 leLengthASD;
+		u16 leOffsetASD;
+		u16 leLength;	/* Length of data in segment */
+		u16 flags;
+#define SA_FLAGS_DONE           0x0080	/* Done with Segment */
+#define SA_FLAGS_CONTINUED      0x8000	/* More Segments */
+#define SA_FLAGS_IDLE           0x4000	/* Mark IDLE after frm */
+#define SA_FLAGS_READY_TO_XMIT  0x0800
+#define SA_FLAGS_STAT_MASK      0x007F
+	} xmit[NUM_XBUFFS];
+
+    /**********************************************/
+	/* Receive Status Area                        */
+    /**********************************************/
+	struct {
+		u32 leNext;	/* Next entry in Little Endian on Adapter */
+		u32 leNextASD;
+		u32 leBuffer;	/* Buffer for entry in LE for Adapter */
+		u16 WorkingLength;	/* size of segment */
+		u16 reserv01;
+		u16 leLength;	/* Length of data in segment */
+		u16 flags;
+#define SA_FL_RCV_DONE           0x0010	/* Data ready */
+#define SA_FLAGS_OVERRUN         0x0040
+#define SA_FLAGS_PARITY_ERROR    0x0080
+#define SA_FLAGS_FRAME_ERROR     0x0001
+#define SA_FLAGS_FRAME_TRUNC     0x0002
+#define SA_FLAGS_BREAK_DET       0x0004	/* set conditionally by device driver, not hardware */
+#define SA_FLAGS_RCV_MASK        0xFFE6
+	} rcv[NUM_RBUFFS];
+};
+
+struct icom_adapter;
+
+
+#define ICOM_MAJOR       243
+#define ICOM_MINOR_START 0
+
+struct icom_port {
+	struct uart_port uart_port;
+	u8 imbed_modem;
+#define ICOM_UNKNOWN		1
+#define ICOM_RVX		2
+#define ICOM_IMBED_MODEM	3
+	unsigned char cable_id;
+	unsigned char read_status_mask;
+	unsigned char ignore_status_mask;
+	void __iomem * int_reg;
+	struct icom_regs __iomem *global_reg;
+	struct func_dram __iomem *dram;
+	int port;
+	struct statusArea *statStg;
+	dma_addr_t statStg_pci;
+	u32 *xmitRestart;
+	dma_addr_t xmitRestart_pci;
+	unsigned char *xmit_buf;
+	dma_addr_t xmit_buf_pci;
+	unsigned char *recv_buf;
+	dma_addr_t recv_buf_pci;
+	int next_rcv;
+	int put_length;
+	int status;
+#define ICOM_PORT_ACTIVE	1	/* Port exists. */
+#define ICOM_PORT_OFF		0	/* Port does not exist. */
+	int load_in_progress;
+	struct icom_adapter *adapter;
+};
+
+struct icom_adapter {
+	void __iomem * base_addr;
+	unsigned long base_addr_pci;
+	struct pci_dev *pci_dev;
+	struct icom_port port_info[4];
+	int index;
+	int version;
+#define ADAPTER_V1	0x0001
+#define ADAPTER_V2	0x0002
+	u32 subsystem_id;
+#define FOUR_PORT_MODEL				0x0252
+#define V2_TWO_PORTS_RVX			0x021A
+#define V2_ONE_PORT_RVX_ONE_PORT_IMBED_MDM	0x0251
+	int numb_ports;
+	struct list_head icom_adapter_entry;
+	struct kref kref;
+};
+
+/* prototype */
+extern void iCom_sercons_init(void);
+
+struct lookup_proc_table {
+	u32	__iomem *global_control_reg;
+	unsigned long	processor_id;
+};
+
+struct lookup_int_table {
+	u32	__iomem *global_int_mask;
+	unsigned long	processor_id;
+};
diff --git a/ap/os/linux/linux-3.4.x/drivers/tty/serial/ifx6x60.c b/ap/os/linux/linux-3.4.x/drivers/tty/serial/ifx6x60.c
new file mode 100644
index 0000000..17f587c
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/tty/serial/ifx6x60.c
@@ -0,0 +1,1412 @@
+/****************************************************************************
+ *
+ * Driver for the IFX 6x60 spi modem.
+ *
+ * Copyright (C) 2008 Option International
+ * Copyright (C) 2008 Filip Aben <f.aben@option.com>
+ *		      Denis Joseph Barrow <d.barow@option.com>
+ *		      Jan Dumon <j.dumon@option.com>
+ *
+ * Copyright (C) 2009, 2010 Intel Corp
+ * Russ Gorby <russ.gorby@intel.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301,
+ * USA
+ *
+ * Driver modified by Intel from Option gtm501l_spi.c
+ *
+ * Notes
+ * o	The driver currently assumes a single device only. If you need to
+ *	change this then look for saved_ifx_dev and add a device lookup
+ * o	The driver is intended to be big-endian safe but has never been
+ *	tested that way (no suitable hardware). There are a couple of FIXME
+ *	notes by areas that may need addressing
+ * o	Some of the GPIO naming/setup assumptions may need revisiting if
+ *	you need to use this driver for another platform.
+ *
+ *****************************************************************************/
+#include <linux/dma-mapping.h>
+#include <linux/module.h>
+#include <linux/termios.h>
+#include <linux/tty.h>
+#include <linux/device.h>
+#include <linux/spi/spi.h>
+#include <linux/kfifo.h>
+#include <linux/tty_flip.h>
+#include <linux/timer.h>
+#include <linux/serial.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/rfkill.h>
+#include <linux/fs.h>
+#include <linux/ip.h>
+#include <linux/dmapool.h>
+#include <linux/gpio.h>
+#include <linux/sched.h>
+#include <linux/time.h>
+#include <linux/wait.h>
+#include <linux/pm.h>
+#include <linux/pm_runtime.h>
+#include <linux/spi/ifx_modem.h>
+#include <linux/delay.h>
+
+#include "ifx6x60.h"
+
+#define IFX_SPI_MORE_MASK		0x10
+#define IFX_SPI_MORE_BIT		12	/* bit position in u16 */
+#define IFX_SPI_CTS_BIT			13	/* bit position in u16 */
+#define IFX_SPI_MODE			SPI_MODE_1
+#define IFX_SPI_TTY_ID			0
+#define IFX_SPI_TIMEOUT_SEC		2
+#define IFX_SPI_HEADER_0		(-1)
+#define IFX_SPI_HEADER_F		(-2)
+
+/* forward reference */
+static void ifx_spi_handle_srdy(struct ifx_spi_device *ifx_dev);
+
+/* local variables */
+static int spi_bpw = 16;		/* 8, 16 or 32 bit word length */
+static struct tty_driver *tty_drv;
+static struct ifx_spi_device *saved_ifx_dev;
+static struct lock_class_key ifx_spi_key;
+
+/* GPIO/GPE settings */
+
+/**
+ *	mrdy_set_high		-	set MRDY GPIO
+ *	@ifx: device we are controlling
+ *
+ */
+static inline void mrdy_set_high(struct ifx_spi_device *ifx)
+{
+	gpio_set_value(ifx->gpio.mrdy, 1);
+}
+
+/**
+ *	mrdy_set_low		-	clear MRDY GPIO
+ *	@ifx: device we are controlling
+ *
+ */
+static inline void mrdy_set_low(struct ifx_spi_device *ifx)
+{
+	gpio_set_value(ifx->gpio.mrdy, 0);
+}
+
+/**
+ *	ifx_spi_power_state_set
+ *	@ifx_dev: our SPI device
+ *	@val: bits to set
+ *
+ *	Set bit in power status and signal power system if status becomes non-0
+ */
+static void
+ifx_spi_power_state_set(struct ifx_spi_device *ifx_dev, unsigned char val)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&ifx_dev->power_lock, flags);
+
+	/*
+	 * if power status is already non-0, just update, else
+	 * tell power system
+	 */
+	if (!ifx_dev->power_status)
+		pm_runtime_get(&ifx_dev->spi_dev->dev);
+	ifx_dev->power_status |= val;
+
+	spin_unlock_irqrestore(&ifx_dev->power_lock, flags);
+}
+
+/**
+ *	ifx_spi_power_state_clear	-	clear power bit
+ *	@ifx_dev: our SPI device
+ *	@val: bits to clear
+ *
+ *	clear bit in power status and signal power system if status becomes 0
+ */
+static void
+ifx_spi_power_state_clear(struct ifx_spi_device *ifx_dev, unsigned char val)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&ifx_dev->power_lock, flags);
+
+	if (ifx_dev->power_status) {
+		ifx_dev->power_status &= ~val;
+		if (!ifx_dev->power_status)
+			pm_runtime_put(&ifx_dev->spi_dev->dev);
+	}
+
+	spin_unlock_irqrestore(&ifx_dev->power_lock, flags);
+}
+
+/**
+ *	swap_buf
+ *	@buf: our buffer
+ *	@len : number of bytes (not words) in the buffer
+ *	@end: end of buffer
+ *
+ *	Swap the contents of a buffer into big endian format
+ */
+static inline void swap_buf(u16 *buf, int len, void *end)
+{
+	int n;
+
+	len = ((len + 1) >> 1);
+	if ((void *)&buf[len] > end) {
+		pr_err("swap_buf: swap exceeds boundary (%p > %p)!",
+		       &buf[len], end);
+		return;
+	}
+	for (n = 0; n < len; n++) {
+		*buf = cpu_to_be16(*buf);
+		buf++;
+	}
+}
+
+/**
+ *	mrdy_assert		-	assert MRDY line
+ *	@ifx_dev: our SPI device
+ *
+ *	Assert mrdy and set timer to wait for SRDY interrupt, if SRDY is low
+ *	now.
+ *
+ *	FIXME: Can SRDY even go high as we are running this code ?
+ */
+static void mrdy_assert(struct ifx_spi_device *ifx_dev)
+{
+	int val = gpio_get_value(ifx_dev->gpio.srdy);
+	if (!val) {
+		if (!test_and_set_bit(IFX_SPI_STATE_TIMER_PENDING,
+				      &ifx_dev->flags)) {
+			ifx_dev->spi_timer.expires =
+				jiffies + IFX_SPI_TIMEOUT_SEC*HZ;
+			add_timer(&ifx_dev->spi_timer);
+
+		}
+	}
+	ifx_spi_power_state_set(ifx_dev, IFX_SPI_POWER_DATA_PENDING);
+	mrdy_set_high(ifx_dev);
+}
+
+/**
+ *	ifx_spi_hangup		-	hang up an IFX device
+ *	@ifx_dev: our SPI device
+ *
+ *	Hang up the tty attached to the IFX device if one is currently
+ *	open. If not take no action
+ */
+static void ifx_spi_ttyhangup(struct ifx_spi_device *ifx_dev)
+{
+	struct tty_port *pport = &ifx_dev->tty_port;
+	struct tty_struct *tty = tty_port_tty_get(pport);
+	if (tty) {
+		tty_hangup(tty);
+		tty_kref_put(tty);
+	}
+}
+
+/**
+ *	ifx_spi_timeout		-	SPI timeout
+ *	@arg: our SPI device
+ *
+ *	The SPI has timed out: hang up the tty. Users will then see a hangup
+ *	and error events.
+ */
+static void ifx_spi_timeout(unsigned long arg)
+{
+	struct ifx_spi_device *ifx_dev = (struct ifx_spi_device *)arg;
+
+	dev_warn(&ifx_dev->spi_dev->dev, "*** SPI Timeout ***");
+	ifx_spi_ttyhangup(ifx_dev);
+	mrdy_set_low(ifx_dev);
+	clear_bit(IFX_SPI_STATE_TIMER_PENDING, &ifx_dev->flags);
+}
+
+/* char/tty operations */
+
+/**
+ *	ifx_spi_tiocmget	-	get modem lines
+ *	@tty: our tty device
+ *	@filp: file handle issuing the request
+ *
+ *	Map the signal state into Linux modem flags and report the value
+ *	in Linux terms
+ */
+static int ifx_spi_tiocmget(struct tty_struct *tty)
+{
+	unsigned int value;
+	struct ifx_spi_device *ifx_dev = tty->driver_data;
+
+	value =
+	(test_bit(IFX_SPI_RTS, &ifx_dev->signal_state) ? TIOCM_RTS : 0) |
+	(test_bit(IFX_SPI_DTR, &ifx_dev->signal_state) ? TIOCM_DTR : 0) |
+	(test_bit(IFX_SPI_CTS, &ifx_dev->signal_state) ? TIOCM_CTS : 0) |
+	(test_bit(IFX_SPI_DSR, &ifx_dev->signal_state) ? TIOCM_DSR : 0) |
+	(test_bit(IFX_SPI_DCD, &ifx_dev->signal_state) ? TIOCM_CAR : 0) |
+	(test_bit(IFX_SPI_RI, &ifx_dev->signal_state) ? TIOCM_RNG : 0);
+	return value;
+}
+
+/**
+ *	ifx_spi_tiocmset	-	set modem bits
+ *	@tty: the tty structure
+ *	@set: bits to set
+ *	@clear: bits to clear
+ *
+ *	The IFX6x60 only supports DTR and RTS. Set them accordingly
+ *	and flag that an update to the modem is needed.
+ *
+ *	FIXME: do we need to kick the tranfers when we do this ?
+ */
+static int ifx_spi_tiocmset(struct tty_struct *tty,
+			    unsigned int set, unsigned int clear)
+{
+	struct ifx_spi_device *ifx_dev = tty->driver_data;
+
+	if (set & TIOCM_RTS)
+		set_bit(IFX_SPI_RTS, &ifx_dev->signal_state);
+	if (set & TIOCM_DTR)
+		set_bit(IFX_SPI_DTR, &ifx_dev->signal_state);
+	if (clear & TIOCM_RTS)
+		clear_bit(IFX_SPI_RTS, &ifx_dev->signal_state);
+	if (clear & TIOCM_DTR)
+		clear_bit(IFX_SPI_DTR, &ifx_dev->signal_state);
+
+	set_bit(IFX_SPI_UPDATE, &ifx_dev->signal_state);
+	return 0;
+}
+
+/**
+ *	ifx_spi_open	-	called on tty open
+ *	@tty: our tty device
+ *	@filp: file handle being associated with the tty
+ *
+ *	Open the tty interface. We let the tty_port layer do all the work
+ *	for us.
+ *
+ *	FIXME: Remove single device assumption and saved_ifx_dev
+ */
+static int ifx_spi_open(struct tty_struct *tty, struct file *filp)
+{
+	return tty_port_open(&saved_ifx_dev->tty_port, tty, filp);
+}
+
+/**
+ *	ifx_spi_close	-	called when our tty closes
+ *	@tty: the tty being closed
+ *	@filp: the file handle being closed
+ *
+ *	Perform the close of the tty. We use the tty_port layer to do all
+ *	our hard work.
+ */
+static void ifx_spi_close(struct tty_struct *tty, struct file *filp)
+{
+	struct ifx_spi_device *ifx_dev = tty->driver_data;
+	tty_port_close(&ifx_dev->tty_port, tty, filp);
+	/* FIXME: should we do an ifx_spi_reset here ? */
+}
+
+/**
+ *	ifx_decode_spi_header	-	decode received header
+ *	@buffer: the received data
+ *	@length: decoded length
+ *	@more: decoded more flag
+ *	@received_cts: status of cts we received
+ *
+ *	Note how received_cts is handled -- if header is all F it is left
+ *	the same as it was, if header is all 0 it is set to 0 otherwise it is
+ *	taken from the incoming header.
+ *
+ *	FIXME: endianness
+ */
+static int ifx_spi_decode_spi_header(unsigned char *buffer, int *length,
+			unsigned char *more, unsigned char *received_cts)
+{
+	u16 h1;
+	u16 h2;
+	u16 *in_buffer = (u16 *)buffer;
+
+	h1 = *in_buffer;
+	h2 = *(in_buffer+1);
+
+	if (h1 == 0 && h2 == 0) {
+		*received_cts = 0;
+		return IFX_SPI_HEADER_0;
+	} else if (h1 == 0xffff && h2 == 0xffff) {
+		/* spi_slave_cts remains as it was */
+		return IFX_SPI_HEADER_F;
+	}
+
+	*length = h1 & 0xfff;	/* upper bits of byte are flags */
+	*more = (buffer[1] >> IFX_SPI_MORE_BIT) & 1;
+	*received_cts = (buffer[3] >> IFX_SPI_CTS_BIT) & 1;
+	return 0;
+}
+
+/**
+ *	ifx_setup_spi_header	-	set header fields
+ *	@txbuffer: pointer to start of SPI buffer
+ *	@tx_count: bytes
+ *	@more: indicate if more to follow
+ *
+ *	Format up an SPI header for a transfer
+ *
+ *	FIXME: endianness?
+ */
+static void ifx_spi_setup_spi_header(unsigned char *txbuffer, int tx_count,
+					unsigned char more)
+{
+	*(u16 *)(txbuffer) = tx_count;
+	*(u16 *)(txbuffer+2) = IFX_SPI_PAYLOAD_SIZE;
+	txbuffer[1] |= (more << IFX_SPI_MORE_BIT) & IFX_SPI_MORE_MASK;
+}
+
+/**
+ *	ifx_spi_wakeup_serial	-	SPI space made
+ *	@port_data: our SPI device
+ *
+ *	We have emptied the FIFO enough that we want to get more data
+ *	queued into it. Poke the line discipline via tty_wakeup so that
+ *	it will feed us more bits
+ */
+static void ifx_spi_wakeup_serial(struct ifx_spi_device *ifx_dev)
+{
+	struct tty_struct *tty;
+
+	tty = tty_port_tty_get(&ifx_dev->tty_port);
+	if (!tty)
+		return;
+	tty_wakeup(tty);
+	tty_kref_put(tty);
+}
+
+/**
+ *	ifx_spi_prepare_tx_buffer	-	prepare transmit frame
+ *	@ifx_dev: our SPI device
+ *
+ *	The transmit buffr needs a header and various other bits of
+ *	information followed by as much data as we can pull from the FIFO
+ *	and transfer. This function formats up a suitable buffer in the
+ *	ifx_dev->tx_buffer
+ *
+ *	FIXME: performance - should we wake the tty when the queue is half
+ *			     empty ?
+ */
+static int ifx_spi_prepare_tx_buffer(struct ifx_spi_device *ifx_dev)
+{
+	int temp_count;
+	int queue_length;
+	int tx_count;
+	unsigned char *tx_buffer;
+
+	tx_buffer = ifx_dev->tx_buffer;
+	memset(tx_buffer, 0, IFX_SPI_TRANSFER_SIZE);
+
+	/* make room for required SPI header */
+	tx_buffer += IFX_SPI_HEADER_OVERHEAD;
+	tx_count = IFX_SPI_HEADER_OVERHEAD;
+
+	/* clear to signal no more data if this turns out to be the
+	 * last buffer sent in a sequence */
+	ifx_dev->spi_more = 0;
+
+	/* if modem cts is set, just send empty buffer */
+	if (!ifx_dev->spi_slave_cts) {
+		/* see if there's tx data */
+		queue_length = kfifo_len(&ifx_dev->tx_fifo);
+		if (queue_length != 0) {
+			/* data to mux -- see if there's room for it */
+			temp_count = min(queue_length, IFX_SPI_PAYLOAD_SIZE);
+			temp_count = kfifo_out_locked(&ifx_dev->tx_fifo,
+					tx_buffer, temp_count,
+					&ifx_dev->fifo_lock);
+
+			/* update buffer pointer and data count in message */
+			tx_buffer += temp_count;
+			tx_count += temp_count;
+			if (temp_count == queue_length)
+				/* poke port to get more data */
+				ifx_spi_wakeup_serial(ifx_dev);
+			else /* more data in port, use next SPI message */
+				ifx_dev->spi_more = 1;
+		}
+	}
+	/* have data and info for header -- set up SPI header in buffer */
+	/* spi header needs payload size, not entire buffer size */
+	ifx_spi_setup_spi_header(ifx_dev->tx_buffer,
+					tx_count-IFX_SPI_HEADER_OVERHEAD,
+					ifx_dev->spi_more);
+	/* swap actual data in the buffer */
+	swap_buf((u16 *)(ifx_dev->tx_buffer), tx_count,
+		&ifx_dev->tx_buffer[IFX_SPI_TRANSFER_SIZE]);
+	return tx_count;
+}
+
+/**
+ *	ifx_spi_write		-	line discipline write
+ *	@tty: our tty device
+ *	@buf: pointer to buffer to write (kernel space)
+ *	@count: size of buffer
+ *
+ *	Write the characters we have been given into the FIFO. If the device
+ *	is not active then activate it, when the SRDY line is asserted back
+ *	this will commence I/O
+ */
+static int ifx_spi_write(struct tty_struct *tty, const unsigned char *buf,
+			 int count)
+{
+	struct ifx_spi_device *ifx_dev = tty->driver_data;
+	unsigned char *tmp_buf = (unsigned char *)buf;
+	int tx_count = kfifo_in_locked(&ifx_dev->tx_fifo, tmp_buf, count,
+				   &ifx_dev->fifo_lock);
+	mrdy_assert(ifx_dev);
+	return tx_count;
+}
+
+/**
+ *	ifx_spi_chars_in_buffer	-	line discipline helper
+ *	@tty: our tty device
+ *
+ *	Report how much data we can accept before we drop bytes. As we use
+ *	a simple FIFO this is nice and easy.
+ */
+static int ifx_spi_write_room(struct tty_struct *tty)
+{
+	struct ifx_spi_device *ifx_dev = tty->driver_data;
+	return IFX_SPI_FIFO_SIZE - kfifo_len(&ifx_dev->tx_fifo);
+}
+
+/**
+ *	ifx_spi_chars_in_buffer	-	line discipline helper
+ *	@tty: our tty device
+ *
+ *	Report how many characters we have buffered. In our case this is the
+ *	number of bytes sitting in our transmit FIFO.
+ */
+static int ifx_spi_chars_in_buffer(struct tty_struct *tty)
+{
+	struct ifx_spi_device *ifx_dev = tty->driver_data;
+	return kfifo_len(&ifx_dev->tx_fifo);
+}
+
+/**
+ *	ifx_port_hangup
+ *	@port: our tty port
+ *
+ *	tty port hang up. Called when tty_hangup processing is invoked either
+ *	by loss of carrier, or by software (eg vhangup). Serialized against
+ *	activate/shutdown by the tty layer.
+ */
+static void ifx_spi_hangup(struct tty_struct *tty)
+{
+	struct ifx_spi_device *ifx_dev = tty->driver_data;
+	tty_port_hangup(&ifx_dev->tty_port);
+}
+
+/**
+ *	ifx_port_activate
+ *	@port: our tty port
+ *
+ *	tty port activate method - called for first open. Serialized
+ *	with hangup and shutdown by the tty layer.
+ */
+static int ifx_port_activate(struct tty_port *port, struct tty_struct *tty)
+{
+	struct ifx_spi_device *ifx_dev =
+		container_of(port, struct ifx_spi_device, tty_port);
+
+	/* clear any old data; can't do this in 'close' */
+	kfifo_reset(&ifx_dev->tx_fifo);
+
+	/* put port data into this tty */
+	tty->driver_data = ifx_dev;
+
+	/* allows flip string push from int context */
+	tty->low_latency = 1;
+
+	return 0;
+}
+
+/**
+ *	ifx_port_shutdown
+ *	@port: our tty port
+ *
+ *	tty port shutdown method - called for last port close. Serialized
+ *	with hangup and activate by the tty layer.
+ */
+static void ifx_port_shutdown(struct tty_port *port)
+{
+	struct ifx_spi_device *ifx_dev =
+		container_of(port, struct ifx_spi_device, tty_port);
+
+	mrdy_set_low(ifx_dev);
+	del_timer(&ifx_dev->spi_timer);
+	clear_bit(IFX_SPI_STATE_TIMER_PENDING, &ifx_dev->flags);
+	tasklet_kill(&ifx_dev->io_work_tasklet);
+}
+
+static const struct tty_port_operations ifx_tty_port_ops = {
+	.activate = ifx_port_activate,
+	.shutdown = ifx_port_shutdown,
+};
+
+static const struct tty_operations ifx_spi_serial_ops = {
+	.open = ifx_spi_open,
+	.close = ifx_spi_close,
+	.write = ifx_spi_write,
+	.hangup = ifx_spi_hangup,
+	.write_room = ifx_spi_write_room,
+	.chars_in_buffer = ifx_spi_chars_in_buffer,
+	.tiocmget = ifx_spi_tiocmget,
+	.tiocmset = ifx_spi_tiocmset,
+};
+
+/**
+ *	ifx_spi_insert_fip_string	-	queue received data
+ *	@ifx_ser: our SPI device
+ *	@chars: buffer we have received
+ *	@size: number of chars reeived
+ *
+ *	Queue bytes to the tty assuming the tty side is currently open. If
+ *	not the discard the data.
+ */
+static void ifx_spi_insert_flip_string(struct ifx_spi_device *ifx_dev,
+				    unsigned char *chars, size_t size)
+{
+	struct tty_struct *tty = tty_port_tty_get(&ifx_dev->tty_port);
+	if (!tty)
+		return;
+	tty_insert_flip_string(tty, chars, size);
+	tty_flip_buffer_push(tty);
+	tty_kref_put(tty);
+}
+
+/**
+ *	ifx_spi_complete	-	SPI transfer completed
+ *	@ctx: our SPI device
+ *
+ *	An SPI transfer has completed. Process any received data and kick off
+ *	any further transmits we can commence.
+ */
+static void ifx_spi_complete(void *ctx)
+{
+	struct ifx_spi_device *ifx_dev = ctx;
+	struct tty_struct *tty;
+	struct tty_ldisc *ldisc = NULL;
+	int length;
+	int actual_length;
+	unsigned char more;
+	unsigned char cts;
+	int local_write_pending = 0;
+	int queue_length;
+	int srdy;
+	int decode_result;
+
+	mrdy_set_low(ifx_dev);
+
+	if (!ifx_dev->spi_msg.status) {
+		/* check header validity, get comm flags */
+		swap_buf((u16 *)ifx_dev->rx_buffer, IFX_SPI_HEADER_OVERHEAD,
+			&ifx_dev->rx_buffer[IFX_SPI_HEADER_OVERHEAD]);
+		decode_result = ifx_spi_decode_spi_header(ifx_dev->rx_buffer,
+				&length, &more, &cts);
+		if (decode_result == IFX_SPI_HEADER_0) {
+			dev_dbg(&ifx_dev->spi_dev->dev,
+				"ignore input: invalid header 0");
+			ifx_dev->spi_slave_cts = 0;
+			goto complete_exit;
+		} else if (decode_result == IFX_SPI_HEADER_F) {
+			dev_dbg(&ifx_dev->spi_dev->dev,
+				"ignore input: invalid header F");
+			goto complete_exit;
+		}
+
+		ifx_dev->spi_slave_cts = cts;
+
+		actual_length = min((unsigned int)length,
+					ifx_dev->spi_msg.actual_length);
+		swap_buf((u16 *)(ifx_dev->rx_buffer + IFX_SPI_HEADER_OVERHEAD),
+			 actual_length,
+			 &ifx_dev->rx_buffer[IFX_SPI_TRANSFER_SIZE]);
+		ifx_spi_insert_flip_string(
+			ifx_dev,
+			ifx_dev->rx_buffer + IFX_SPI_HEADER_OVERHEAD,
+			(size_t)actual_length);
+	} else {
+		dev_dbg(&ifx_dev->spi_dev->dev, "SPI transfer error %d",
+		       ifx_dev->spi_msg.status);
+	}
+
+complete_exit:
+	if (ifx_dev->write_pending) {
+		ifx_dev->write_pending = 0;
+		local_write_pending = 1;
+	}
+
+	clear_bit(IFX_SPI_STATE_IO_IN_PROGRESS, &(ifx_dev->flags));
+
+	queue_length = kfifo_len(&ifx_dev->tx_fifo);
+	srdy = gpio_get_value(ifx_dev->gpio.srdy);
+	if (!srdy)
+		ifx_spi_power_state_clear(ifx_dev, IFX_SPI_POWER_SRDY);
+
+	/* schedule output if there is more to do */
+	if (test_and_clear_bit(IFX_SPI_STATE_IO_READY, &ifx_dev->flags))
+		tasklet_schedule(&ifx_dev->io_work_tasklet);
+	else {
+		if (more || ifx_dev->spi_more || queue_length > 0 ||
+			local_write_pending) {
+			if (ifx_dev->spi_slave_cts) {
+				if (more)
+					mrdy_assert(ifx_dev);
+			} else
+				mrdy_assert(ifx_dev);
+		} else {
+			/*
+			 * poke line discipline driver if any for more data
+			 * may or may not get more data to write
+			 * for now, say not busy
+			 */
+			ifx_spi_power_state_clear(ifx_dev,
+						  IFX_SPI_POWER_DATA_PENDING);
+			tty = tty_port_tty_get(&ifx_dev->tty_port);
+			if (tty) {
+				ldisc = tty_ldisc_ref(tty);
+				if (ldisc) {
+					ldisc->ops->write_wakeup(tty);
+					tty_ldisc_deref(ldisc);
+				}
+				tty_kref_put(tty);
+			}
+		}
+	}
+}
+
+/**
+ *	ifx_spio_io		-	I/O tasklet
+ *	@data: our SPI device
+ *
+ *	Queue data for transmission if possible and then kick off the
+ *	transfer.
+ */
+static void ifx_spi_io(unsigned long data)
+{
+	int retval;
+	struct ifx_spi_device *ifx_dev = (struct ifx_spi_device *) data;
+
+	if (!test_and_set_bit(IFX_SPI_STATE_IO_IN_PROGRESS, &ifx_dev->flags)) {
+		if (ifx_dev->gpio.unack_srdy_int_nb > 0)
+			ifx_dev->gpio.unack_srdy_int_nb--;
+
+		ifx_spi_prepare_tx_buffer(ifx_dev);
+
+		spi_message_init(&ifx_dev->spi_msg);
+		INIT_LIST_HEAD(&ifx_dev->spi_msg.queue);
+
+		ifx_dev->spi_msg.context = ifx_dev;
+		ifx_dev->spi_msg.complete = ifx_spi_complete;
+
+		/* set up our spi transfer */
+		/* note len is BYTES, not transfers */
+		ifx_dev->spi_xfer.len = IFX_SPI_TRANSFER_SIZE;
+		ifx_dev->spi_xfer.cs_change = 0;
+		ifx_dev->spi_xfer.speed_hz = ifx_dev->spi_dev->max_speed_hz;
+		/* ifx_dev->spi_xfer.speed_hz = 390625; */
+		ifx_dev->spi_xfer.bits_per_word = spi_bpw;
+
+		ifx_dev->spi_xfer.tx_buf = ifx_dev->tx_buffer;
+		ifx_dev->spi_xfer.rx_buf = ifx_dev->rx_buffer;
+
+		/*
+		 * setup dma pointers
+		 */
+		if (ifx_dev->use_dma) {
+			ifx_dev->spi_msg.is_dma_mapped = 1;
+			ifx_dev->tx_dma = ifx_dev->tx_bus;
+			ifx_dev->rx_dma = ifx_dev->rx_bus;
+			ifx_dev->spi_xfer.tx_dma = ifx_dev->tx_dma;
+			ifx_dev->spi_xfer.rx_dma = ifx_dev->rx_dma;
+		} else {
+			ifx_dev->spi_msg.is_dma_mapped = 0;
+			ifx_dev->tx_dma = (dma_addr_t)0;
+			ifx_dev->rx_dma = (dma_addr_t)0;
+			ifx_dev->spi_xfer.tx_dma = (dma_addr_t)0;
+			ifx_dev->spi_xfer.rx_dma = (dma_addr_t)0;
+		}
+
+		spi_message_add_tail(&ifx_dev->spi_xfer, &ifx_dev->spi_msg);
+
+		/* Assert MRDY. This may have already been done by the write
+		 * routine.
+		 */
+		mrdy_assert(ifx_dev);
+
+		retval = spi_async(ifx_dev->spi_dev, &ifx_dev->spi_msg);
+		if (retval) {
+			clear_bit(IFX_SPI_STATE_IO_IN_PROGRESS,
+				  &ifx_dev->flags);
+			tasklet_schedule(&ifx_dev->io_work_tasklet);
+			return;
+		}
+	} else
+		ifx_dev->write_pending = 1;
+}
+
+/**
+ *	ifx_spi_free_port	-	free up the tty side
+ *	@ifx_dev: IFX device going away
+ *
+ *	Unregister and free up a port when the device goes away
+ */
+static void ifx_spi_free_port(struct ifx_spi_device *ifx_dev)
+{
+	if (ifx_dev->tty_dev)
+		tty_unregister_device(tty_drv, ifx_dev->minor);
+	kfifo_free(&ifx_dev->tx_fifo);
+}
+
+/**
+ *	ifx_spi_create_port	-	create a new port
+ *	@ifx_dev: our spi device
+ *
+ *	Allocate and initialise the tty port that goes with this interface
+ *	and add it to the tty layer so that it can be opened.
+ */
+static int ifx_spi_create_port(struct ifx_spi_device *ifx_dev)
+{
+	int ret = 0;
+	struct tty_port *pport = &ifx_dev->tty_port;
+
+	spin_lock_init(&ifx_dev->fifo_lock);
+	lockdep_set_class_and_subclass(&ifx_dev->fifo_lock,
+		&ifx_spi_key, 0);
+
+	if (kfifo_alloc(&ifx_dev->tx_fifo, IFX_SPI_FIFO_SIZE, GFP_KERNEL)) {
+		ret = -ENOMEM;
+		goto error_ret;
+	}
+
+	tty_port_init(pport);
+	pport->ops = &ifx_tty_port_ops;
+	ifx_dev->minor = IFX_SPI_TTY_ID;
+	ifx_dev->tty_dev = tty_register_device(tty_drv, ifx_dev->minor,
+					       &ifx_dev->spi_dev->dev);
+	if (IS_ERR(ifx_dev->tty_dev)) {
+		dev_dbg(&ifx_dev->spi_dev->dev,
+			"%s: registering tty device failed", __func__);
+		ret = PTR_ERR(ifx_dev->tty_dev);
+		goto error_ret;
+	}
+	return 0;
+
+error_ret:
+	ifx_spi_free_port(ifx_dev);
+	return ret;
+}
+
+/**
+ *	ifx_spi_handle_srdy		-	handle SRDY
+ *	@ifx_dev: device asserting SRDY
+ *
+ *	Check our device state and see what we need to kick off when SRDY
+ *	is asserted. This usually means killing the timer and firing off the
+ *	I/O processing.
+ */
+static void ifx_spi_handle_srdy(struct ifx_spi_device *ifx_dev)
+{
+	if (test_bit(IFX_SPI_STATE_TIMER_PENDING, &ifx_dev->flags)) {
+		del_timer_sync(&ifx_dev->spi_timer);
+		clear_bit(IFX_SPI_STATE_TIMER_PENDING, &ifx_dev->flags);
+	}
+
+	ifx_spi_power_state_set(ifx_dev, IFX_SPI_POWER_SRDY);
+
+	if (!test_bit(IFX_SPI_STATE_IO_IN_PROGRESS, &ifx_dev->flags))
+		tasklet_schedule(&ifx_dev->io_work_tasklet);
+	else
+		set_bit(IFX_SPI_STATE_IO_READY, &ifx_dev->flags);
+}
+
+/**
+ *	ifx_spi_srdy_interrupt	-	SRDY asserted
+ *	@irq: our IRQ number
+ *	@dev: our ifx device
+ *
+ *	The modem asserted SRDY. Handle the srdy event
+ */
+static irqreturn_t ifx_spi_srdy_interrupt(int irq, void *dev)
+{
+	struct ifx_spi_device *ifx_dev = dev;
+	ifx_dev->gpio.unack_srdy_int_nb++;
+	ifx_spi_handle_srdy(ifx_dev);
+	return IRQ_HANDLED;
+}
+
+/**
+ *	ifx_spi_reset_interrupt	-	Modem has changed reset state
+ *	@irq: interrupt number
+ *	@dev: our device pointer
+ *
+ *	The modem has either entered or left reset state. Check the GPIO
+ *	line to see which.
+ *
+ *	FIXME: review locking on MR_INPROGRESS versus
+ *	parallel unsolicited reset/solicited reset
+ */
+static irqreturn_t ifx_spi_reset_interrupt(int irq, void *dev)
+{
+	struct ifx_spi_device *ifx_dev = dev;
+	int val = gpio_get_value(ifx_dev->gpio.reset_out);
+	int solreset = test_bit(MR_START, &ifx_dev->mdm_reset_state);
+
+	if (val == 0) {
+		/* entered reset */
+		set_bit(MR_INPROGRESS, &ifx_dev->mdm_reset_state);
+		if (!solreset) {
+			/* unsolicited reset  */
+			ifx_spi_ttyhangup(ifx_dev);
+		}
+	} else {
+		/* exited reset */
+		clear_bit(MR_INPROGRESS, &ifx_dev->mdm_reset_state);
+		if (solreset) {
+			set_bit(MR_COMPLETE, &ifx_dev->mdm_reset_state);
+			wake_up(&ifx_dev->mdm_reset_wait);
+		}
+	}
+	return IRQ_HANDLED;
+}
+
+/**
+ *	ifx_spi_free_device - free device
+ *	@ifx_dev: device to free
+ *
+ *	Free the IFX device
+ */
+static void ifx_spi_free_device(struct ifx_spi_device *ifx_dev)
+{
+	ifx_spi_free_port(ifx_dev);
+	dma_free_coherent(&ifx_dev->spi_dev->dev,
+				IFX_SPI_TRANSFER_SIZE,
+				ifx_dev->tx_buffer,
+				ifx_dev->tx_bus);
+	dma_free_coherent(&ifx_dev->spi_dev->dev,
+				IFX_SPI_TRANSFER_SIZE,
+				ifx_dev->rx_buffer,
+				ifx_dev->rx_bus);
+}
+
+/**
+ *	ifx_spi_reset	-	reset modem
+ *	@ifx_dev: modem to reset
+ *
+ *	Perform a reset on the modem
+ */
+static int ifx_spi_reset(struct ifx_spi_device *ifx_dev)
+{
+	int ret;
+	/*
+	 * set up modem power, reset
+	 *
+	 * delays are required on some platforms for the modem
+	 * to reset properly
+	 */
+	set_bit(MR_START, &ifx_dev->mdm_reset_state);
+	gpio_set_value(ifx_dev->gpio.po, 0);
+	gpio_set_value(ifx_dev->gpio.reset, 0);
+	msleep(25);
+	gpio_set_value(ifx_dev->gpio.reset, 1);
+	msleep(1);
+	gpio_set_value(ifx_dev->gpio.po, 1);
+	msleep(1);
+	gpio_set_value(ifx_dev->gpio.po, 0);
+	ret = wait_event_timeout(ifx_dev->mdm_reset_wait,
+				 test_bit(MR_COMPLETE,
+					  &ifx_dev->mdm_reset_state),
+				 IFX_RESET_TIMEOUT);
+	if (!ret)
+		dev_warn(&ifx_dev->spi_dev->dev, "Modem reset timeout: (state:%lx)",
+			 ifx_dev->mdm_reset_state);
+
+	ifx_dev->mdm_reset_state = 0;
+	return ret;
+}
+
+/**
+ *	ifx_spi_spi_probe	-	probe callback
+ *	@spi: our possible matching SPI device
+ *
+ *	Probe for a 6x60 modem on SPI bus. Perform any needed device and
+ *	GPIO setup.
+ *
+ *	FIXME:
+ *	-	Support for multiple devices
+ *	-	Split out MID specific GPIO handling eventually
+ */
+
+static int ifx_spi_spi_probe(struct spi_device *spi)
+{
+	int ret;
+	int srdy;
+	struct ifx_modem_platform_data *pl_data;
+	struct ifx_spi_device *ifx_dev;
+
+	if (saved_ifx_dev) {
+		dev_dbg(&spi->dev, "ignoring subsequent detection");
+		return -ENODEV;
+	}
+
+	pl_data = (struct ifx_modem_platform_data *)spi->dev.platform_data;
+	if (!pl_data) {
+		dev_err(&spi->dev, "missing platform data!");
+		return -ENODEV;
+	}
+
+	/* initialize structure to hold our device variables */
+	ifx_dev = kzalloc(sizeof(struct ifx_spi_device), GFP_KERNEL);
+	if (!ifx_dev) {
+		dev_err(&spi->dev, "spi device allocation failed");
+		return -ENOMEM;
+	}
+	saved_ifx_dev = ifx_dev;
+	ifx_dev->spi_dev = spi;
+	clear_bit(IFX_SPI_STATE_IO_IN_PROGRESS, &ifx_dev->flags);
+	spin_lock_init(&ifx_dev->write_lock);
+	spin_lock_init(&ifx_dev->power_lock);
+	ifx_dev->power_status = 0;
+	init_timer(&ifx_dev->spi_timer);
+	ifx_dev->spi_timer.function = ifx_spi_timeout;
+	ifx_dev->spi_timer.data = (unsigned long)ifx_dev;
+	ifx_dev->modem = pl_data->modem_type;
+	ifx_dev->use_dma = pl_data->use_dma;
+	ifx_dev->max_hz = pl_data->max_hz;
+	/* initialize spi mode, etc */
+	spi->max_speed_hz = ifx_dev->max_hz;
+	spi->mode = IFX_SPI_MODE | (SPI_LOOP & spi->mode);
+	spi->bits_per_word = spi_bpw;
+	ret = spi_setup(spi);
+	if (ret) {
+		dev_err(&spi->dev, "SPI setup wasn't successful %d", ret);
+		return -ENODEV;
+	}
+
+	/* ensure SPI protocol flags are initialized to enable transfer */
+	ifx_dev->spi_more = 0;
+	ifx_dev->spi_slave_cts = 0;
+
+	/*initialize transfer and dma buffers */
+	ifx_dev->tx_buffer = dma_alloc_coherent(ifx_dev->spi_dev->dev.parent,
+				IFX_SPI_TRANSFER_SIZE,
+				&ifx_dev->tx_bus,
+				GFP_KERNEL);
+	if (!ifx_dev->tx_buffer) {
+		dev_err(&spi->dev, "DMA-TX buffer allocation failed");
+		ret = -ENOMEM;
+		goto error_ret;
+	}
+	ifx_dev->rx_buffer = dma_alloc_coherent(ifx_dev->spi_dev->dev.parent,
+				IFX_SPI_TRANSFER_SIZE,
+				&ifx_dev->rx_bus,
+				GFP_KERNEL);
+	if (!ifx_dev->rx_buffer) {
+		dev_err(&spi->dev, "DMA-RX buffer allocation failed");
+		ret = -ENOMEM;
+		goto error_ret;
+	}
+
+	/* initialize waitq for modem reset */
+	init_waitqueue_head(&ifx_dev->mdm_reset_wait);
+
+	spi_set_drvdata(spi, ifx_dev);
+	tasklet_init(&ifx_dev->io_work_tasklet, ifx_spi_io,
+						(unsigned long)ifx_dev);
+
+	set_bit(IFX_SPI_STATE_PRESENT, &ifx_dev->flags);
+
+	/* create our tty port */
+	ret = ifx_spi_create_port(ifx_dev);
+	if (ret != 0) {
+		dev_err(&spi->dev, "create default tty port failed");
+		goto error_ret;
+	}
+
+	ifx_dev->gpio.reset = pl_data->rst_pmu;
+	ifx_dev->gpio.po = pl_data->pwr_on;
+	ifx_dev->gpio.mrdy = pl_data->mrdy;
+	ifx_dev->gpio.srdy = pl_data->srdy;
+	ifx_dev->gpio.reset_out = pl_data->rst_out;
+
+	dev_info(&spi->dev, "gpios %d, %d, %d, %d, %d",
+		 ifx_dev->gpio.reset, ifx_dev->gpio.po, ifx_dev->gpio.mrdy,
+		 ifx_dev->gpio.srdy, ifx_dev->gpio.reset_out);
+
+	/* Configure gpios */
+	ret = gpio_request(ifx_dev->gpio.reset, "ifxModem");
+	if (ret < 0) {
+		dev_err(&spi->dev, "Unable to allocate GPIO%d (RESET)",
+			ifx_dev->gpio.reset);
+		goto error_ret;
+	}
+	ret += gpio_direction_output(ifx_dev->gpio.reset, 0);
+	ret += gpio_export(ifx_dev->gpio.reset, 1);
+	if (ret) {
+		dev_err(&spi->dev, "Unable to configure GPIO%d (RESET)",
+			ifx_dev->gpio.reset);
+		ret = -EBUSY;
+		goto error_ret2;
+	}
+
+	ret = gpio_request(ifx_dev->gpio.po, "ifxModem");
+	ret += gpio_direction_output(ifx_dev->gpio.po, 0);
+	ret += gpio_export(ifx_dev->gpio.po, 1);
+	if (ret) {
+		dev_err(&spi->dev, "Unable to configure GPIO%d (ON)",
+			ifx_dev->gpio.po);
+		ret = -EBUSY;
+		goto error_ret3;
+	}
+
+	ret = gpio_request(ifx_dev->gpio.mrdy, "ifxModem");
+	if (ret < 0) {
+		dev_err(&spi->dev, "Unable to allocate GPIO%d (MRDY)",
+			ifx_dev->gpio.mrdy);
+		goto error_ret3;
+	}
+	ret += gpio_export(ifx_dev->gpio.mrdy, 1);
+	ret += gpio_direction_output(ifx_dev->gpio.mrdy, 0);
+	if (ret) {
+		dev_err(&spi->dev, "Unable to configure GPIO%d (MRDY)",
+			ifx_dev->gpio.mrdy);
+		ret = -EBUSY;
+		goto error_ret4;
+	}
+
+	ret = gpio_request(ifx_dev->gpio.srdy, "ifxModem");
+	if (ret < 0) {
+		dev_err(&spi->dev, "Unable to allocate GPIO%d (SRDY)",
+			ifx_dev->gpio.srdy);
+		ret = -EBUSY;
+		goto error_ret4;
+	}
+	ret += gpio_export(ifx_dev->gpio.srdy, 1);
+	ret += gpio_direction_input(ifx_dev->gpio.srdy);
+	if (ret) {
+		dev_err(&spi->dev, "Unable to configure GPIO%d (SRDY)",
+			ifx_dev->gpio.srdy);
+		ret = -EBUSY;
+		goto error_ret5;
+	}
+
+	ret = gpio_request(ifx_dev->gpio.reset_out, "ifxModem");
+	if (ret < 0) {
+		dev_err(&spi->dev, "Unable to allocate GPIO%d (RESET_OUT)",
+			ifx_dev->gpio.reset_out);
+		goto error_ret5;
+	}
+	ret += gpio_export(ifx_dev->gpio.reset_out, 1);
+	ret += gpio_direction_input(ifx_dev->gpio.reset_out);
+	if (ret) {
+		dev_err(&spi->dev, "Unable to configure GPIO%d (RESET_OUT)",
+			ifx_dev->gpio.reset_out);
+		ret = -EBUSY;
+		goto error_ret6;
+	}
+
+	ret = request_irq(gpio_to_irq(ifx_dev->gpio.reset_out),
+			  ifx_spi_reset_interrupt,
+			  IRQF_TRIGGER_RISING|IRQF_TRIGGER_FALLING, DRVNAME,
+		(void *)ifx_dev);
+	if (ret) {
+		dev_err(&spi->dev, "Unable to get irq %x\n",
+			gpio_to_irq(ifx_dev->gpio.reset_out));
+		goto error_ret6;
+	}
+
+	ret = ifx_spi_reset(ifx_dev);
+
+	ret = request_irq(gpio_to_irq(ifx_dev->gpio.srdy),
+			  ifx_spi_srdy_interrupt,
+			  IRQF_TRIGGER_RISING, DRVNAME,
+			  (void *)ifx_dev);
+	if (ret) {
+		dev_err(&spi->dev, "Unable to get irq %x",
+			gpio_to_irq(ifx_dev->gpio.srdy));
+		goto error_ret7;
+	}
+
+	/* set pm runtime power state and register with power system */
+	pm_runtime_set_active(&spi->dev);
+	pm_runtime_enable(&spi->dev);
+
+	/* handle case that modem is already signaling SRDY */
+	/* no outgoing tty open at this point, this just satisfies the
+	 * modem's read and should reset communication properly
+	 */
+	srdy = gpio_get_value(ifx_dev->gpio.srdy);
+
+	if (srdy) {
+		mrdy_assert(ifx_dev);
+		ifx_spi_handle_srdy(ifx_dev);
+	} else
+		mrdy_set_low(ifx_dev);
+	return 0;
+
+error_ret7:
+	free_irq(gpio_to_irq(ifx_dev->gpio.reset_out), (void *)ifx_dev);
+error_ret6:
+	gpio_free(ifx_dev->gpio.srdy);
+error_ret5:
+	gpio_free(ifx_dev->gpio.mrdy);
+error_ret4:
+	gpio_free(ifx_dev->gpio.reset);
+error_ret3:
+	gpio_free(ifx_dev->gpio.po);
+error_ret2:
+	gpio_free(ifx_dev->gpio.reset_out);
+error_ret:
+	ifx_spi_free_device(ifx_dev);
+	saved_ifx_dev = NULL;
+	return ret;
+}
+
+/**
+ *	ifx_spi_spi_remove	-	SPI device was removed
+ *	@spi: SPI device
+ *
+ *	FIXME: We should be shutting the device down here not in
+ *	the module unload path.
+ */
+
+static int ifx_spi_spi_remove(struct spi_device *spi)
+{
+	struct ifx_spi_device *ifx_dev = spi_get_drvdata(spi);
+	/* stop activity */
+	tasklet_kill(&ifx_dev->io_work_tasklet);
+	/* free irq */
+	free_irq(gpio_to_irq(ifx_dev->gpio.reset_out), (void *)ifx_dev);
+	free_irq(gpio_to_irq(ifx_dev->gpio.srdy), (void *)ifx_dev);
+
+	gpio_free(ifx_dev->gpio.srdy);
+	gpio_free(ifx_dev->gpio.mrdy);
+	gpio_free(ifx_dev->gpio.reset);
+	gpio_free(ifx_dev->gpio.po);
+	gpio_free(ifx_dev->gpio.reset_out);
+
+	/* free allocations */
+	ifx_spi_free_device(ifx_dev);
+
+	saved_ifx_dev = NULL;
+	return 0;
+}
+
+/**
+ *	ifx_spi_spi_shutdown	-	called on SPI shutdown
+ *	@spi: SPI device
+ *
+ *	No action needs to be taken here
+ */
+
+static void ifx_spi_spi_shutdown(struct spi_device *spi)
+{
+}
+
+/*
+ * various suspends and resumes have nothing to do
+ * no hardware to save state for
+ */
+
+/**
+ *	ifx_spi_spi_suspend	-	suspend SPI on system suspend
+ *	@dev: device being suspended
+ *
+ *	Suspend the SPI side. No action needed on Intel MID platforms, may
+ *	need extending for other systems.
+ */
+static int ifx_spi_spi_suspend(struct spi_device *spi, pm_message_t msg)
+{
+	return 0;
+}
+
+/**
+ *	ifx_spi_spi_resume	-	resume SPI side on system resume
+ *	@dev: device being suspended
+ *
+ *	Suspend the SPI side. No action needed on Intel MID platforms, may
+ *	need extending for other systems.
+ */
+static int ifx_spi_spi_resume(struct spi_device *spi)
+{
+	return 0;
+}
+
+/**
+ *	ifx_spi_pm_suspend	-	suspend modem on system suspend
+ *	@dev: device being suspended
+ *
+ *	Suspend the modem. No action needed on Intel MID platforms, may
+ *	need extending for other systems.
+ */
+static int ifx_spi_pm_suspend(struct device *dev)
+{
+	return 0;
+}
+
+/**
+ *	ifx_spi_pm_resume	-	resume modem on system resume
+ *	@dev: device being suspended
+ *
+ *	Allow the modem to resume. No action needed.
+ *
+ *	FIXME: do we need to reset anything here ?
+ */
+static int ifx_spi_pm_resume(struct device *dev)
+{
+	return 0;
+}
+
+/**
+ *	ifx_spi_pm_runtime_resume	-	suspend modem
+ *	@dev: device being suspended
+ *
+ *	Allow the modem to resume. No action needed.
+ */
+static int ifx_spi_pm_runtime_resume(struct device *dev)
+{
+	return 0;
+}
+
+/**
+ *	ifx_spi_pm_runtime_suspend	-	suspend modem
+ *	@dev: device being suspended
+ *
+ *	Allow the modem to suspend and thus suspend to continue up the
+ *	device tree.
+ */
+static int ifx_spi_pm_runtime_suspend(struct device *dev)
+{
+	return 0;
+}
+
+/**
+ *	ifx_spi_pm_runtime_idle		-	check if modem idle
+ *	@dev: our device
+ *
+ *	Check conditions and queue runtime suspend if idle.
+ */
+static int ifx_spi_pm_runtime_idle(struct device *dev)
+{
+	struct spi_device *spi = to_spi_device(dev);
+	struct ifx_spi_device *ifx_dev = spi_get_drvdata(spi);
+
+	if (!ifx_dev->power_status)
+		pm_runtime_suspend(dev);
+
+	return 0;
+}
+
+static const struct dev_pm_ops ifx_spi_pm = {
+	.resume = ifx_spi_pm_resume,
+	.suspend = ifx_spi_pm_suspend,
+	.runtime_resume = ifx_spi_pm_runtime_resume,
+	.runtime_suspend = ifx_spi_pm_runtime_suspend,
+	.runtime_idle = ifx_spi_pm_runtime_idle
+};
+
+static const struct spi_device_id ifx_id_table[] = {
+	{"ifx6160", 0},
+	{"ifx6260", 0},
+	{ }
+};
+MODULE_DEVICE_TABLE(spi, ifx_id_table);
+
+/* spi operations */
+static const struct spi_driver ifx_spi_driver = {
+	.driver = {
+		.name = DRVNAME,
+		.pm = &ifx_spi_pm,
+		.owner = THIS_MODULE},
+	.probe = ifx_spi_spi_probe,
+	.shutdown = ifx_spi_spi_shutdown,
+	.remove = __devexit_p(ifx_spi_spi_remove),
+	.suspend = ifx_spi_spi_suspend,
+	.resume = ifx_spi_spi_resume,
+	.id_table = ifx_id_table
+};
+
+/**
+ *	ifx_spi_exit	-	module exit
+ *
+ *	Unload the module.
+ */
+
+static void __exit ifx_spi_exit(void)
+{
+	/* unregister */
+	tty_unregister_driver(tty_drv);
+	spi_unregister_driver((void *)&ifx_spi_driver);
+}
+
+/**
+ *	ifx_spi_init		-	module entry point
+ *
+ *	Initialise the SPI and tty interfaces for the IFX SPI driver
+ *	We need to initialize upper-edge spi driver after the tty
+ *	driver because otherwise the spi probe will race
+ */
+
+static int __init ifx_spi_init(void)
+{
+	int result;
+
+	tty_drv = alloc_tty_driver(1);
+	if (!tty_drv) {
+		pr_err("%s: alloc_tty_driver failed", DRVNAME);
+		return -ENOMEM;
+	}
+
+	tty_drv->driver_name = DRVNAME;
+	tty_drv->name = TTYNAME;
+	tty_drv->minor_start = IFX_SPI_TTY_ID;
+	tty_drv->type = TTY_DRIVER_TYPE_SERIAL;
+	tty_drv->subtype = SERIAL_TYPE_NORMAL;
+	tty_drv->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV;
+	tty_drv->init_termios = tty_std_termios;
+
+	tty_set_operations(tty_drv, &ifx_spi_serial_ops);
+
+	result = tty_register_driver(tty_drv);
+	if (result) {
+		pr_err("%s: tty_register_driver failed(%d)",
+			DRVNAME, result);
+		put_tty_driver(tty_drv);
+		return result;
+	}
+
+	result = spi_register_driver((void *)&ifx_spi_driver);
+	if (result) {
+		pr_err("%s: spi_register_driver failed(%d)",
+			DRVNAME, result);
+		tty_unregister_driver(tty_drv);
+	}
+	return result;
+}
+
+module_init(ifx_spi_init);
+module_exit(ifx_spi_exit);
+
+MODULE_AUTHOR("Intel");
+MODULE_DESCRIPTION("IFX6x60 spi driver");
+MODULE_LICENSE("GPL");
+MODULE_INFO(Version, "0.1-IFX6x60");
diff --git a/ap/os/linux/linux-3.4.x/drivers/tty/serial/ifx6x60.h b/ap/os/linux/linux-3.4.x/drivers/tty/serial/ifx6x60.h
new file mode 100644
index 0000000..e8464ba
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/tty/serial/ifx6x60.h
@@ -0,0 +1,129 @@
+/****************************************************************************
+ *
+ * Driver for the IFX spi modem.
+ *
+ * Copyright (C) 2009, 2010 Intel Corp
+ * Jim Stanley <jim.stanley@intel.com>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301,
+ * USA
+ *
+ *
+ *
+ *****************************************************************************/
+#ifndef _IFX6X60_H
+#define _IFX6X60_H
+
+#define DRVNAME				"ifx6x60"
+#define TTYNAME				"ttyIFX"
+
+#define IFX_SPI_MAX_MINORS		1
+#define IFX_SPI_TRANSFER_SIZE		2048
+#define IFX_SPI_FIFO_SIZE		4096
+
+#define IFX_SPI_HEADER_OVERHEAD		4
+#define IFX_RESET_TIMEOUT		msecs_to_jiffies(50)
+
+/* device flags bitfield definitions */
+#define IFX_SPI_STATE_PRESENT		0
+#define IFX_SPI_STATE_IO_IN_PROGRESS	1
+#define IFX_SPI_STATE_IO_READY		2
+#define IFX_SPI_STATE_TIMER_PENDING	3
+
+/* flow control bitfields */
+#define IFX_SPI_DCD			0
+#define IFX_SPI_CTS			1
+#define IFX_SPI_DSR			2
+#define IFX_SPI_RI			3
+#define IFX_SPI_DTR			4
+#define IFX_SPI_RTS			5
+#define IFX_SPI_TX_FC			6
+#define IFX_SPI_RX_FC			7
+#define IFX_SPI_UPDATE			8
+
+#define IFX_SPI_PAYLOAD_SIZE		(IFX_SPI_TRANSFER_SIZE - \
+						IFX_SPI_HEADER_OVERHEAD)
+
+#define IFX_SPI_IRQ_TYPE		DETECT_EDGE_RISING
+#define IFX_SPI_GPIO_TARGET		0
+#define IFX_SPI_GPIO0			0x105
+
+#define IFX_SPI_STATUS_TIMEOUT		(2000*HZ)
+
+/* values for bits in power status byte */
+#define IFX_SPI_POWER_DATA_PENDING	1
+#define IFX_SPI_POWER_SRDY		2
+
+struct ifx_spi_device {
+	/* Our SPI device */
+	struct spi_device *spi_dev;
+
+	/* Port specific data */
+	struct kfifo tx_fifo;
+	spinlock_t fifo_lock;
+	unsigned long signal_state;
+
+	/* TTY Layer logic */
+	struct tty_port tty_port;
+	struct device *tty_dev;
+	int minor;
+
+	/* Low level I/O work */
+	struct tasklet_struct io_work_tasklet;
+	unsigned long flags;
+	dma_addr_t rx_dma;
+	dma_addr_t tx_dma;
+
+	int modem;		/* Modem type */
+	int use_dma;		/* provide dma-able addrs in SPI msg */
+	long max_hz;		/* max SPI frequency */
+
+	spinlock_t write_lock;
+	int write_pending;
+	spinlock_t power_lock;
+	unsigned char power_status;
+
+	unsigned char *rx_buffer;
+	unsigned char *tx_buffer;
+	dma_addr_t rx_bus;
+	dma_addr_t tx_bus;
+	unsigned char spi_more;
+	unsigned char spi_slave_cts;
+
+	struct timer_list spi_timer;
+
+	struct spi_message spi_msg;
+	struct spi_transfer spi_xfer;
+
+	struct {
+		/* gpio lines */
+		unsigned short srdy;		/* slave-ready gpio */
+		unsigned short mrdy;		/* master-ready gpio */
+		unsigned short reset;		/* modem-reset gpio */
+		unsigned short po;		/* modem-on gpio */
+		unsigned short reset_out;	/* modem-in-reset gpio */
+		/* state/stats */
+		int unack_srdy_int_nb;
+	} gpio;
+
+	/* modem reset */
+	unsigned long mdm_reset_state;
+#define MR_START	0
+#define MR_INPROGRESS	1
+#define MR_COMPLETE	2
+	wait_queue_head_t mdm_reset_wait;
+};
+
+#endif /* _IFX6X60_H */
diff --git a/ap/os/linux/linux-3.4.x/drivers/tty/serial/imx.c b/ap/os/linux/linux-3.4.x/drivers/tty/serial/imx.c
new file mode 100644
index 0000000..2fdd52d
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/tty/serial/imx.c
@@ -0,0 +1,1618 @@
+/*
+ *  Driver for Motorola IMX serial ports
+ *
+ *  Based on drivers/char/serial.c, by Linus Torvalds, Theodore Ts'o.
+ *
+ *  Author: Sascha Hauer <sascha@saschahauer.de>
+ *  Copyright (C) 2004 Pengutronix
+ *
+ *  Copyright (C) 2009 emlix GmbH
+ *  Author: Fabian Godehardt (added IrDA support for iMX)
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ * [29-Mar-2005] Mike Lee
+ * Added hardware handshake
+ */
+
+#if defined(CONFIG_SERIAL_IMX_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ)
+#define SUPPORT_SYSRQ
+#endif
+
+#include <linux/module.h>
+#include <linux/ioport.h>
+#include <linux/init.h>
+#include <linux/console.h>
+#include <linux/sysrq.h>
+#include <linux/platform_device.h>
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
+#include <linux/serial_core.h>
+#include <linux/serial.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/rational.h>
+#include <linux/slab.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <mach/imx-uart.h>
+
+/* Register definitions */
+#define URXD0 0x0  /* Receiver Register */
+#define URTX0 0x40 /* Transmitter Register */
+#define UCR1  0x80 /* Control Register 1 */
+#define UCR2  0x84 /* Control Register 2 */
+#define UCR3  0x88 /* Control Register 3 */
+#define UCR4  0x8c /* Control Register 4 */
+#define UFCR  0x90 /* FIFO Control Register */
+#define USR1  0x94 /* Status Register 1 */
+#define USR2  0x98 /* Status Register 2 */
+#define UESC  0x9c /* Escape Character Register */
+#define UTIM  0xa0 /* Escape Timer Register */
+#define UBIR  0xa4 /* BRM Incremental Register */
+#define UBMR  0xa8 /* BRM Modulator Register */
+#define UBRC  0xac /* Baud Rate Count Register */
+#define IMX21_ONEMS 0xb0 /* One Millisecond register */
+#define IMX1_UTS 0xd0 /* UART Test Register on i.mx1 */
+#define IMX21_UTS 0xb4 /* UART Test Register on all other i.mx*/
+
+/* UART Control Register Bit Fields.*/
+#define  URXD_CHARRDY    (1<<15)
+#define  URXD_ERR        (1<<14)
+#define  URXD_OVRRUN     (1<<13)
+#define  URXD_FRMERR     (1<<12)
+#define  URXD_BRK        (1<<11)
+#define  URXD_PRERR      (1<<10)
+#define  UCR1_ADEN       (1<<15) /* Auto detect interrupt */
+#define  UCR1_ADBR       (1<<14) /* Auto detect baud rate */
+#define  UCR1_TRDYEN     (1<<13) /* Transmitter ready interrupt enable */
+#define  UCR1_IDEN       (1<<12) /* Idle condition interrupt */
+#define  UCR1_RRDYEN     (1<<9)	 /* Recv ready interrupt enable */
+#define  UCR1_RDMAEN     (1<<8)	 /* Recv ready DMA enable */
+#define  UCR1_IREN       (1<<7)	 /* Infrared interface enable */
+#define  UCR1_TXMPTYEN   (1<<6)	 /* Transimitter empty interrupt enable */
+#define  UCR1_RTSDEN     (1<<5)	 /* RTS delta interrupt enable */
+#define  UCR1_SNDBRK     (1<<4)	 /* Send break */
+#define  UCR1_TDMAEN     (1<<3)	 /* Transmitter ready DMA enable */
+#define  IMX1_UCR1_UARTCLKEN  (1<<2)  /* UART clock enabled, i.mx1 only */
+#define  UCR1_DOZE       (1<<1)	 /* Doze */
+#define  UCR1_UARTEN     (1<<0)	 /* UART enabled */
+#define  UCR2_ESCI     	 (1<<15) /* Escape seq interrupt enable */
+#define  UCR2_IRTS  	 (1<<14) /* Ignore RTS pin */
+#define  UCR2_CTSC  	 (1<<13) /* CTS pin control */
+#define  UCR2_CTS        (1<<12) /* Clear to send */
+#define  UCR2_ESCEN      (1<<11) /* Escape enable */
+#define  UCR2_PREN       (1<<8)  /* Parity enable */
+#define  UCR2_PROE       (1<<7)  /* Parity odd/even */
+#define  UCR2_STPB       (1<<6)	 /* Stop */
+#define  UCR2_WS         (1<<5)	 /* Word size */
+#define  UCR2_RTSEN      (1<<4)	 /* Request to send interrupt enable */
+#define  UCR2_ATEN       (1<<3)  /* Aging Timer Enable */
+#define  UCR2_TXEN       (1<<2)	 /* Transmitter enabled */
+#define  UCR2_RXEN       (1<<1)	 /* Receiver enabled */
+#define  UCR2_SRST 	 (1<<0)	 /* SW reset */
+#define  UCR3_DTREN 	 (1<<13) /* DTR interrupt enable */
+#define  UCR3_PARERREN   (1<<12) /* Parity enable */
+#define  UCR3_FRAERREN   (1<<11) /* Frame error interrupt enable */
+#define  UCR3_DSR        (1<<10) /* Data set ready */
+#define  UCR3_DCD        (1<<9)  /* Data carrier detect */
+#define  UCR3_RI         (1<<8)  /* Ring indicator */
+#define  UCR3_TIMEOUTEN  (1<<7)  /* Timeout interrupt enable */
+#define  UCR3_RXDSEN	 (1<<6)  /* Receive status interrupt enable */
+#define  UCR3_AIRINTEN   (1<<5)  /* Async IR wake interrupt enable */
+#define  UCR3_AWAKEN	 (1<<4)  /* Async wake interrupt enable */
+#define  IMX21_UCR3_RXDMUXSEL	 (1<<2)  /* RXD Muxed Input Select */
+#define  UCR3_INVT  	 (1<<1)  /* Inverted Infrared transmission */
+#define  UCR3_BPEN  	 (1<<0)  /* Preset registers enable */
+#define  UCR4_CTSTL_SHF  10      /* CTS trigger level shift */
+#define  UCR4_CTSTL_MASK 0x3F    /* CTS trigger is 6 bits wide */
+#define  UCR4_INVR  	 (1<<9)  /* Inverted infrared reception */
+#define  UCR4_ENIRI 	 (1<<8)  /* Serial infrared interrupt enable */
+#define  UCR4_WKEN  	 (1<<7)  /* Wake interrupt enable */
+#define  UCR4_REF16 	 (1<<6)  /* Ref freq 16 MHz */
+#define  UCR4_IRSC  	 (1<<5)  /* IR special case */
+#define  UCR4_TCEN  	 (1<<3)  /* Transmit complete interrupt enable */
+#define  UCR4_BKEN  	 (1<<2)  /* Break condition interrupt enable */
+#define  UCR4_OREN  	 (1<<1)  /* Receiver overrun interrupt enable */
+#define  UCR4_DREN  	 (1<<0)  /* Recv data ready interrupt enable */
+#define  UFCR_RXTL_SHF   0       /* Receiver trigger level shift */
+#define  UFCR_DCEDTE	 (1<<6)  /* DCE/DTE mode select */
+#define  UFCR_RFDIV      (7<<7)  /* Reference freq divider mask */
+#define  UFCR_RFDIV_REG(x)	(((x) < 7 ? 6 - (x) : 6) << 7)
+#define  UFCR_TXTL_SHF   10      /* Transmitter trigger level shift */
+#define  USR1_PARITYERR  (1<<15) /* Parity error interrupt flag */
+#define  USR1_RTSS  	 (1<<14) /* RTS pin status */
+#define  USR1_TRDY  	 (1<<13) /* Transmitter ready interrupt/dma flag */
+#define  USR1_RTSD  	 (1<<12) /* RTS delta */
+#define  USR1_ESCF  	 (1<<11) /* Escape seq interrupt flag */
+#define  USR1_FRAMERR    (1<<10) /* Frame error interrupt flag */
+#define  USR1_RRDY       (1<<9)	 /* Receiver ready interrupt/dma flag */
+#define  USR1_TIMEOUT    (1<<7)	 /* Receive timeout interrupt status */
+#define  USR1_RXDS  	 (1<<6)	 /* Receiver idle interrupt flag */
+#define  USR1_AIRINT	 (1<<5)	 /* Async IR wake interrupt flag */
+#define  USR1_AWAKE 	 (1<<4)	 /* Aysnc wake interrupt flag */
+#define  USR2_ADET  	 (1<<15) /* Auto baud rate detect complete */
+#define  USR2_TXFE  	 (1<<14) /* Transmit buffer FIFO empty */
+#define  USR2_DTRF  	 (1<<13) /* DTR edge interrupt flag */
+#define  USR2_IDLE  	 (1<<12) /* Idle condition */
+#define  USR2_IRINT 	 (1<<8)	 /* Serial infrared interrupt flag */
+#define  USR2_WAKE  	 (1<<7)	 /* Wake */
+#define  USR2_RTSF  	 (1<<4)	 /* RTS edge interrupt flag */
+#define  USR2_TXDC  	 (1<<3)	 /* Transmitter complete */
+#define  USR2_BRCD  	 (1<<2)	 /* Break condition */
+#define  USR2_ORE        (1<<1)	 /* Overrun error */
+#define  USR2_RDR        (1<<0)	 /* Recv data ready */
+#define  UTS_FRCPERR	 (1<<13) /* Force parity error */
+#define  UTS_LOOP        (1<<12) /* Loop tx and rx */
+#define  UTS_TXEMPTY	 (1<<6)	 /* TxFIFO empty */
+#define  UTS_RXEMPTY	 (1<<5)	 /* RxFIFO empty */
+#define  UTS_TXFULL 	 (1<<4)	 /* TxFIFO full */
+#define  UTS_RXFULL 	 (1<<3)	 /* RxFIFO full */
+#define  UTS_SOFTRST	 (1<<0)	 /* Software reset */
+
+/* We've been assigned a range on the "Low-density serial ports" major */
+#define SERIAL_IMX_MAJOR        207
+#define MINOR_START	        16
+#define DEV_NAME		"ttymxc"
+#define MAX_INTERNAL_IRQ	MXC_INTERNAL_IRQS
+
+/*
+ * 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)
+
+#define DRIVER_NAME "IMX-uart"
+
+#define UART_NR 8
+
+/* i.mx21 type uart runs on all i.mx except i.mx1 */
+enum imx_uart_type {
+	IMX1_UART,
+	IMX21_UART,
+};
+
+/* device type dependent stuff */
+struct imx_uart_data {
+	unsigned uts_reg;
+	enum imx_uart_type devtype;
+};
+
+struct imx_port {
+	struct uart_port	port;
+	struct timer_list	timer;
+	unsigned int		old_status;
+	int			txirq,rxirq,rtsirq;
+	unsigned int		have_rtscts:1;
+	unsigned int		use_irda:1;
+	unsigned int		irda_inv_rx:1;
+	unsigned int		irda_inv_tx:1;
+	unsigned short		trcv_delay; /* transceiver delay */
+	struct clk		*clk;
+	struct imx_uart_data	*devdata;
+};
+
+struct imx_port_ucrs {
+	unsigned int	ucr1;
+	unsigned int	ucr2;
+	unsigned int	ucr3;
+};
+
+#ifdef CONFIG_IRDA
+#define USE_IRDA(sport)	((sport)->use_irda)
+#else
+#define USE_IRDA(sport)	(0)
+#endif
+
+static struct imx_uart_data imx_uart_devdata[] = {
+	[IMX1_UART] = {
+		.uts_reg = IMX1_UTS,
+		.devtype = IMX1_UART,
+	},
+	[IMX21_UART] = {
+		.uts_reg = IMX21_UTS,
+		.devtype = IMX21_UART,
+	},
+};
+
+static struct platform_device_id imx_uart_devtype[] = {
+	{
+		.name = "imx1-uart",
+		.driver_data = (kernel_ulong_t) &imx_uart_devdata[IMX1_UART],
+	}, {
+		.name = "imx21-uart",
+		.driver_data = (kernel_ulong_t) &imx_uart_devdata[IMX21_UART],
+	}, {
+		/* sentinel */
+	}
+};
+MODULE_DEVICE_TABLE(platform, imx_uart_devtype);
+
+static struct of_device_id imx_uart_dt_ids[] = {
+	{ .compatible = "fsl,imx1-uart", .data = &imx_uart_devdata[IMX1_UART], },
+	{ .compatible = "fsl,imx21-uart", .data = &imx_uart_devdata[IMX21_UART], },
+	{ /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, imx_uart_dt_ids);
+
+static inline unsigned uts_reg(struct imx_port *sport)
+{
+	return sport->devdata->uts_reg;
+}
+
+static inline int is_imx1_uart(struct imx_port *sport)
+{
+	return sport->devdata->devtype == IMX1_UART;
+}
+
+static inline int is_imx21_uart(struct imx_port *sport)
+{
+	return sport->devdata->devtype == IMX21_UART;
+}
+
+/*
+ * Save and restore functions for UCR1, UCR2 and UCR3 registers
+ */
+static void imx_port_ucrs_save(struct uart_port *port,
+			       struct imx_port_ucrs *ucr)
+{
+	/* save control registers */
+	ucr->ucr1 = readl(port->membase + UCR1);
+	ucr->ucr2 = readl(port->membase + UCR2);
+	ucr->ucr3 = readl(port->membase + UCR3);
+}
+
+static void imx_port_ucrs_restore(struct uart_port *port,
+				  struct imx_port_ucrs *ucr)
+{
+	/* restore control registers */
+	writel(ucr->ucr1, port->membase + UCR1);
+	writel(ucr->ucr2, port->membase + UCR2);
+	writel(ucr->ucr3, port->membase + UCR3);
+}
+
+/*
+ * Handle any change of modem status signal since we were last called.
+ */
+static void imx_mctrl_check(struct imx_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 imx_timeout(unsigned long data)
+{
+	struct imx_port *sport = (struct imx_port *)data;
+	unsigned long flags;
+
+	if (sport->port.state) {
+		spin_lock_irqsave(&sport->port.lock, flags);
+		imx_mctrl_check(sport);
+		spin_unlock_irqrestore(&sport->port.lock, flags);
+
+		mod_timer(&sport->timer, jiffies + MCTRL_TIMEOUT);
+	}
+}
+
+/*
+ * interrupts disabled on entry
+ */
+static void imx_stop_tx(struct uart_port *port)
+{
+	struct imx_port *sport = (struct imx_port *)port;
+	unsigned long temp;
+
+	if (USE_IRDA(sport)) {
+		/* half duplex - wait for end of transmission */
+		int n = 256;
+		while ((--n > 0) &&
+		      !(readl(sport->port.membase + USR2) & USR2_TXDC)) {
+			udelay(5);
+			barrier();
+		}
+		/*
+		 * irda transceiver - wait a bit more to avoid
+		 * cutoff, hardware dependent
+		 */
+		udelay(sport->trcv_delay);
+
+		/*
+		 * half duplex - reactivate receive mode,
+		 * flush receive pipe echo crap
+		 */
+		if (readl(sport->port.membase + USR2) & USR2_TXDC) {
+			temp = readl(sport->port.membase + UCR1);
+			temp &= ~(UCR1_TXMPTYEN | UCR1_TRDYEN);
+			writel(temp, sport->port.membase + UCR1);
+
+			temp = readl(sport->port.membase + UCR4);
+			temp &= ~(UCR4_TCEN);
+			writel(temp, sport->port.membase + UCR4);
+
+			while (readl(sport->port.membase + URXD0) &
+			       URXD_CHARRDY)
+				barrier();
+
+			temp = readl(sport->port.membase + UCR1);
+			temp |= UCR1_RRDYEN;
+			writel(temp, sport->port.membase + UCR1);
+
+			temp = readl(sport->port.membase + UCR4);
+			temp |= UCR4_DREN;
+			writel(temp, sport->port.membase + UCR4);
+		}
+		return;
+	}
+
+	temp = readl(sport->port.membase + UCR1);
+	writel(temp & ~UCR1_TXMPTYEN, sport->port.membase + UCR1);
+}
+
+/*
+ * interrupts disabled on entry
+ */
+static void imx_stop_rx(struct uart_port *port)
+{
+	struct imx_port *sport = (struct imx_port *)port;
+	unsigned long temp;
+
+	temp = readl(sport->port.membase + UCR2);
+	writel(temp &~ UCR2_RXEN, sport->port.membase + UCR2);
+}
+
+/*
+ * Set the modem control timer to fire immediately.
+ */
+static void imx_enable_ms(struct uart_port *port)
+{
+	struct imx_port *sport = (struct imx_port *)port;
+
+	mod_timer(&sport->timer, jiffies);
+}
+
+static inline void imx_transmit_buffer(struct imx_port *sport)
+{
+	struct circ_buf *xmit = &sport->port.state->xmit;
+
+	while (!uart_circ_empty(xmit) &&
+			!(readl(sport->port.membase + uts_reg(sport))
+				& UTS_TXFULL)) {
+		/* send xmit->buf[xmit->tail]
+		 * out the port here */
+		writel(xmit->buf[xmit->tail], sport->port.membase + URTX0);
+		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))
+		imx_stop_tx(&sport->port);
+}
+
+/*
+ * interrupts disabled on entry
+ */
+static void imx_start_tx(struct uart_port *port)
+{
+	struct imx_port *sport = (struct imx_port *)port;
+	unsigned long temp;
+
+	if (USE_IRDA(sport)) {
+		/* half duplex in IrDA mode; have to disable receive mode */
+		temp = readl(sport->port.membase + UCR4);
+		temp &= ~(UCR4_DREN);
+		writel(temp, sport->port.membase + UCR4);
+
+		temp = readl(sport->port.membase + UCR1);
+		temp &= ~(UCR1_RRDYEN);
+		writel(temp, sport->port.membase + UCR1);
+	}
+
+	temp = readl(sport->port.membase + UCR1);
+	writel(temp | UCR1_TXMPTYEN, sport->port.membase + UCR1);
+
+	if (USE_IRDA(sport)) {
+		temp = readl(sport->port.membase + UCR1);
+		temp |= UCR1_TRDYEN;
+		writel(temp, sport->port.membase + UCR1);
+
+		temp = readl(sport->port.membase + UCR4);
+		temp |= UCR4_TCEN;
+		writel(temp, sport->port.membase + UCR4);
+	}
+
+	if (readl(sport->port.membase + uts_reg(sport)) & UTS_TXEMPTY)
+		imx_transmit_buffer(sport);
+}
+
+static irqreturn_t imx_rtsint(int irq, void *dev_id)
+{
+	struct imx_port *sport = dev_id;
+	unsigned int val;
+	unsigned long flags;
+
+	spin_lock_irqsave(&sport->port.lock, flags);
+
+	writel(USR1_RTSD, sport->port.membase + USR1);
+	val = readl(sport->port.membase + USR1) & USR1_RTSS;
+	uart_handle_cts_change(&sport->port, !!val);
+	wake_up_interruptible(&sport->port.state->port.delta_msr_wait);
+
+	spin_unlock_irqrestore(&sport->port.lock, flags);
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t imx_txint(int irq, void *dev_id)
+{
+	struct imx_port *sport = dev_id;
+	struct circ_buf *xmit = &sport->port.state->xmit;
+	unsigned long flags;
+
+	spin_lock_irqsave(&sport->port.lock,flags);
+	if (sport->port.x_char)
+	{
+		/* Send next char */
+		writel(sport->port.x_char, sport->port.membase + URTX0);
+		goto out;
+	}
+
+	if (uart_circ_empty(xmit) || uart_tx_stopped(&sport->port)) {
+		imx_stop_tx(&sport->port);
+		goto out;
+	}
+
+	imx_transmit_buffer(sport);
+
+	if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
+		uart_write_wakeup(&sport->port);
+
+out:
+	spin_unlock_irqrestore(&sport->port.lock,flags);
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t imx_rxint(int irq, void *dev_id)
+{
+	struct imx_port *sport = dev_id;
+	unsigned int rx,flg,ignored = 0;
+	struct tty_struct *tty = sport->port.state->port.tty;
+	unsigned long flags, temp;
+
+	spin_lock_irqsave(&sport->port.lock,flags);
+
+	while (readl(sport->port.membase + USR2) & USR2_RDR) {
+		flg = TTY_NORMAL;
+		sport->port.icount.rx++;
+
+		rx = readl(sport->port.membase + URXD0);
+
+		temp = readl(sport->port.membase + USR2);
+		if (temp & USR2_BRCD) {
+			writel(USR2_BRCD, sport->port.membase + USR2);
+			if (uart_handle_break(&sport->port))
+				continue;
+		}
+
+		if (uart_handle_sysrq_char(&sport->port, (unsigned char)rx))
+			continue;
+
+		if (unlikely(rx & URXD_ERR)) {
+			if (rx & URXD_BRK)
+				sport->port.icount.brk++;
+			else if (rx & URXD_PRERR)
+				sport->port.icount.parity++;
+			else if (rx & URXD_FRMERR)
+				sport->port.icount.frame++;
+			if (rx & URXD_OVRRUN)
+				sport->port.icount.overrun++;
+
+			if (rx & sport->port.ignore_status_mask) {
+				if (++ignored > 100)
+					goto out;
+				continue;
+			}
+
+			rx &= sport->port.read_status_mask;
+
+			if (rx & URXD_BRK)
+				flg = TTY_BREAK;
+			else if (rx & URXD_PRERR)
+				flg = TTY_PARITY;
+			else if (rx & URXD_FRMERR)
+				flg = TTY_FRAME;
+			if (rx & URXD_OVRRUN)
+				flg = TTY_OVERRUN;
+
+#ifdef SUPPORT_SYSRQ
+			sport->port.sysrq = 0;
+#endif
+		}
+
+		tty_insert_flip_char(tty, rx, flg);
+	}
+
+out:
+	spin_unlock_irqrestore(&sport->port.lock,flags);
+	tty_flip_buffer_push(tty);
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t imx_int(int irq, void *dev_id)
+{
+	struct imx_port *sport = dev_id;
+	unsigned int sts;
+
+	sts = readl(sport->port.membase + USR1);
+
+	if (sts & USR1_RRDY)
+		imx_rxint(irq, dev_id);
+
+	if (sts & USR1_TRDY &&
+			readl(sport->port.membase + UCR1) & UCR1_TXMPTYEN)
+		imx_txint(irq, dev_id);
+
+	if (sts & USR1_RTSD)
+		imx_rtsint(irq, dev_id);
+
+	if (sts & USR1_AWAKE)
+		writel(USR1_AWAKE, sport->port.membase + USR1);
+
+	return IRQ_HANDLED;
+}
+
+/*
+ * Return TIOCSER_TEMT when transmitter is not busy.
+ */
+static unsigned int imx_tx_empty(struct uart_port *port)
+{
+	struct imx_port *sport = (struct imx_port *)port;
+
+	return (readl(sport->port.membase + USR2) & USR2_TXDC) ?  TIOCSER_TEMT : 0;
+}
+
+/*
+ * We have a modem side uart, so the meanings of RTS and CTS are inverted.
+ */
+static unsigned int imx_get_mctrl(struct uart_port *port)
+{
+	struct imx_port *sport = (struct imx_port *)port;
+	unsigned int tmp = TIOCM_DSR | TIOCM_CAR;
+
+	if (readl(sport->port.membase + USR1) & USR1_RTSS)
+		tmp |= TIOCM_CTS;
+
+	if (readl(sport->port.membase + UCR2) & UCR2_CTS)
+		tmp |= TIOCM_RTS;
+
+	return tmp;
+}
+
+static void imx_set_mctrl(struct uart_port *port, unsigned int mctrl)
+{
+	struct imx_port *sport = (struct imx_port *)port;
+	unsigned long temp;
+
+	temp = readl(sport->port.membase + UCR2) & ~UCR2_CTS;
+
+	if (mctrl & TIOCM_RTS)
+		temp |= UCR2_CTS;
+
+	writel(temp, sport->port.membase + UCR2);
+}
+
+/*
+ * Interrupts always disabled.
+ */
+static void imx_break_ctl(struct uart_port *port, int break_state)
+{
+	struct imx_port *sport = (struct imx_port *)port;
+	unsigned long flags, temp;
+
+	spin_lock_irqsave(&sport->port.lock, flags);
+
+	temp = readl(sport->port.membase + UCR1) & ~UCR1_SNDBRK;
+
+	if ( break_state != 0 )
+		temp |= UCR1_SNDBRK;
+
+	writel(temp, sport->port.membase + UCR1);
+
+	spin_unlock_irqrestore(&sport->port.lock, flags);
+}
+
+#define TXTL 2 /* reset default */
+#define RXTL 1 /* reset default */
+
+static int imx_setup_ufcr(struct imx_port *sport, unsigned int mode)
+{
+	unsigned int val;
+
+	/* set receiver / transmitter trigger level */
+	val = readl(sport->port.membase + UFCR) & (UFCR_RFDIV | UFCR_DCEDTE);
+	val |= TXTL << UFCR_TXTL_SHF | RXTL;
+	writel(val, sport->port.membase + UFCR);
+	return 0;
+}
+
+/* half the RX buffer size */
+#define CTSTL 16
+
+static int imx_startup(struct uart_port *port)
+{
+	struct imx_port *sport = (struct imx_port *)port;
+	int retval;
+	unsigned long flags, temp;
+
+	imx_setup_ufcr(sport, 0);
+
+	/* disable the DREN bit (Data Ready interrupt enable) before
+	 * requesting IRQs
+	 */
+	temp = readl(sport->port.membase + UCR4);
+
+	if (USE_IRDA(sport))
+		temp |= UCR4_IRSC;
+
+	/* set the trigger level for CTS */
+	temp &= ~(UCR4_CTSTL_MASK<<  UCR4_CTSTL_SHF);
+	temp |= CTSTL<<  UCR4_CTSTL_SHF;
+
+	writel(temp & ~UCR4_DREN, sport->port.membase + UCR4);
+
+	if (USE_IRDA(sport)) {
+		/* reset fifo's and state machines */
+		int i = 100;
+		temp = readl(sport->port.membase + UCR2);
+		temp &= ~UCR2_SRST;
+		writel(temp, sport->port.membase + UCR2);
+		while (!(readl(sport->port.membase + UCR2) & UCR2_SRST) &&
+		    (--i > 0)) {
+			udelay(1);
+		}
+	}
+
+	/*
+	 * Allocate the IRQ(s) i.MX1 has three interrupts whereas later
+	 * chips only have one interrupt.
+	 */
+	if (sport->txirq > 0) {
+		retval = request_irq(sport->rxirq, imx_rxint, 0,
+				DRIVER_NAME, sport);
+		if (retval)
+			goto error_out1;
+
+		retval = request_irq(sport->txirq, imx_txint, 0,
+				DRIVER_NAME, sport);
+		if (retval)
+			goto error_out2;
+
+		/* do not use RTS IRQ on IrDA */
+		if (!USE_IRDA(sport)) {
+			retval = request_irq(sport->rtsirq, imx_rtsint,
+				     (sport->rtsirq < MAX_INTERNAL_IRQ) ? 0 :
+				       IRQF_TRIGGER_FALLING |
+				       IRQF_TRIGGER_RISING,
+					DRIVER_NAME, sport);
+			if (retval)
+				goto error_out3;
+		}
+	} else {
+		retval = request_irq(sport->port.irq, imx_int, 0,
+				DRIVER_NAME, sport);
+		if (retval) {
+			free_irq(sport->port.irq, sport);
+			goto error_out1;
+		}
+	}
+
+	spin_lock_irqsave(&sport->port.lock, flags);
+	/*
+	 * Finally, clear and enable interrupts
+	 */
+	writel(USR1_RTSD, sport->port.membase + USR1);
+
+	temp = readl(sport->port.membase + UCR1);
+	temp |= UCR1_RRDYEN | UCR1_RTSDEN | UCR1_UARTEN;
+
+	if (USE_IRDA(sport)) {
+		temp |= UCR1_IREN;
+		temp &= ~(UCR1_RTSDEN);
+	}
+
+	writel(temp, sport->port.membase + UCR1);
+
+	temp = readl(sport->port.membase + UCR2);
+	temp |= (UCR2_RXEN | UCR2_TXEN);
+	writel(temp, sport->port.membase + UCR2);
+
+	if (USE_IRDA(sport)) {
+		/* clear RX-FIFO */
+		int i = 64;
+		while ((--i > 0) &&
+			(readl(sport->port.membase + URXD0) & URXD_CHARRDY)) {
+			barrier();
+		}
+	}
+
+	if (is_imx21_uart(sport)) {
+		temp = readl(sport->port.membase + UCR3);
+		temp |= IMX21_UCR3_RXDMUXSEL;
+		writel(temp, sport->port.membase + UCR3);
+	}
+
+	if (USE_IRDA(sport)) {
+		temp = readl(sport->port.membase + UCR4);
+		if (sport->irda_inv_rx)
+			temp |= UCR4_INVR;
+		else
+			temp &= ~(UCR4_INVR);
+		writel(temp | UCR4_DREN, sport->port.membase + UCR4);
+
+		temp = readl(sport->port.membase + UCR3);
+		if (sport->irda_inv_tx)
+			temp |= UCR3_INVT;
+		else
+			temp &= ~(UCR3_INVT);
+		writel(temp, sport->port.membase + UCR3);
+	}
+
+	/*
+	 * Enable modem status interrupts
+	 */
+	imx_enable_ms(&sport->port);
+	spin_unlock_irqrestore(&sport->port.lock,flags);
+
+	if (USE_IRDA(sport)) {
+		struct imxuart_platform_data *pdata;
+		pdata = sport->port.dev->platform_data;
+		sport->irda_inv_rx = pdata->irda_inv_rx;
+		sport->irda_inv_tx = pdata->irda_inv_tx;
+		sport->trcv_delay = pdata->transceiver_delay;
+		if (pdata->irda_enable)
+			pdata->irda_enable(1);
+	}
+
+	return 0;
+
+error_out3:
+	if (sport->txirq)
+		free_irq(sport->txirq, sport);
+error_out2:
+	if (sport->rxirq)
+		free_irq(sport->rxirq, sport);
+error_out1:
+	return retval;
+}
+
+static void imx_shutdown(struct uart_port *port)
+{
+	struct imx_port *sport = (struct imx_port *)port;
+	unsigned long temp;
+	unsigned long flags;
+
+	spin_lock_irqsave(&sport->port.lock, flags);
+	temp = readl(sport->port.membase + UCR2);
+	temp &= ~(UCR2_TXEN);
+	writel(temp, sport->port.membase + UCR2);
+	spin_unlock_irqrestore(&sport->port.lock, flags);
+
+	if (USE_IRDA(sport)) {
+		struct imxuart_platform_data *pdata;
+		pdata = sport->port.dev->platform_data;
+		if (pdata->irda_enable)
+			pdata->irda_enable(0);
+	}
+
+	/*
+	 * Stop our timer.
+	 */
+	del_timer_sync(&sport->timer);
+
+	/*
+	 * Free the interrupts
+	 */
+	if (sport->txirq > 0) {
+		if (!USE_IRDA(sport))
+			free_irq(sport->rtsirq, sport);
+		free_irq(sport->txirq, sport);
+		free_irq(sport->rxirq, sport);
+	} else
+		free_irq(sport->port.irq, sport);
+
+	/*
+	 * Disable all interrupts, port and break condition.
+	 */
+
+	spin_lock_irqsave(&sport->port.lock, flags);
+	temp = readl(sport->port.membase + UCR1);
+	temp &= ~(UCR1_TXMPTYEN | UCR1_RRDYEN | UCR1_RTSDEN | UCR1_UARTEN);
+	if (USE_IRDA(sport))
+		temp &= ~(UCR1_IREN);
+
+	writel(temp, sport->port.membase + UCR1);
+	spin_unlock_irqrestore(&sport->port.lock, flags);
+}
+
+static void
+imx_set_termios(struct uart_port *port, struct ktermios *termios,
+		   struct ktermios *old)
+{
+	struct imx_port *sport = (struct imx_port *)port;
+	unsigned long flags;
+	unsigned int ucr2, old_ucr1, old_txrxen, baud, quot;
+	unsigned int old_csize = old ? old->c_cflag & CSIZE : CS8;
+	unsigned int div, ufcr;
+	unsigned long num, denom;
+	uint64_t tdiv64;
+
+	/*
+	 * 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;
+	}
+
+	/*
+	 * We 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)
+		ucr2 = UCR2_WS | UCR2_SRST | UCR2_IRTS;
+	else
+		ucr2 = UCR2_SRST | UCR2_IRTS;
+
+	if (termios->c_cflag & CRTSCTS) {
+		if( sport->have_rtscts ) {
+			ucr2 &= ~UCR2_IRTS;
+			ucr2 |= UCR2_CTSC;
+		} else {
+			termios->c_cflag &= ~CRTSCTS;
+		}
+	}
+
+	if (termios->c_cflag & CSTOPB)
+		ucr2 |= UCR2_STPB;
+	if (termios->c_cflag & PARENB) {
+		ucr2 |= UCR2_PREN;
+		if (termios->c_cflag & PARODD)
+			ucr2 |= UCR2_PROE;
+	}
+
+	del_timer_sync(&sport->timer);
+
+	/*
+	 * Ask the core to calculate the divisor for us.
+	 */
+	baud = uart_get_baud_rate(port, termios, old, 50, port->uartclk / 16);
+	quot = uart_get_divisor(port, baud);
+
+	spin_lock_irqsave(&sport->port.lock, flags);
+
+	sport->port.read_status_mask = 0;
+	if (termios->c_iflag & INPCK)
+		sport->port.read_status_mask |= (URXD_FRMERR | URXD_PRERR);
+	if (termios->c_iflag & (BRKINT | PARMRK))
+		sport->port.read_status_mask |= URXD_BRK;
+
+	/*
+	 * Characters to ignore
+	 */
+	sport->port.ignore_status_mask = 0;
+	if (termios->c_iflag & IGNPAR)
+		sport->port.ignore_status_mask |= URXD_PRERR;
+	if (termios->c_iflag & IGNBRK) {
+		sport->port.ignore_status_mask |= URXD_BRK;
+		/*
+		 * 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 |= URXD_OVRRUN;
+	}
+
+	/*
+	 * Update the per-port timeout.
+	 */
+	uart_update_timeout(port, termios->c_cflag, baud);
+
+	/*
+	 * disable interrupts and drain transmitter
+	 */
+	old_ucr1 = readl(sport->port.membase + UCR1);
+	writel(old_ucr1 & ~(UCR1_TXMPTYEN | UCR1_RRDYEN | UCR1_RTSDEN),
+			sport->port.membase + UCR1);
+
+	while ( !(readl(sport->port.membase + USR2) & USR2_TXDC))
+		barrier();
+
+	/* then, disable everything */
+	old_txrxen = readl(sport->port.membase + UCR2);
+	writel(old_txrxen & ~( UCR2_TXEN | UCR2_RXEN),
+			sport->port.membase + UCR2);
+	old_txrxen &= (UCR2_TXEN | UCR2_RXEN);
+
+	if (USE_IRDA(sport)) {
+		/*
+		 * use maximum available submodule frequency to
+		 * avoid missing short pulses due to low sampling rate
+		 */
+		div = 1;
+	} else {
+		div = sport->port.uartclk / (baud * 16);
+		if (div > 7)
+			div = 7;
+		if (!div)
+			div = 1;
+	}
+
+	rational_best_approximation(16 * div * baud, sport->port.uartclk,
+		1 << 16, 1 << 16, &num, &denom);
+
+	tdiv64 = sport->port.uartclk;
+	tdiv64 *= num;
+	do_div(tdiv64, denom * 16 * div);
+	tty_termios_encode_baud_rate(termios,
+				(speed_t)tdiv64, (speed_t)tdiv64);
+
+	num -= 1;
+	denom -= 1;
+
+	ufcr = readl(sport->port.membase + UFCR);
+	ufcr = (ufcr & (~UFCR_RFDIV)) | UFCR_RFDIV_REG(div);
+	writel(ufcr, sport->port.membase + UFCR);
+
+	writel(num, sport->port.membase + UBIR);
+	writel(denom, sport->port.membase + UBMR);
+
+	if (is_imx21_uart(sport))
+		writel(sport->port.uartclk / div / 1000,
+				sport->port.membase + IMX21_ONEMS);
+
+	writel(old_ucr1, sport->port.membase + UCR1);
+
+	/* set the parity, stop bits and data size */
+	writel(ucr2 | old_txrxen, sport->port.membase + UCR2);
+
+	if (UART_ENABLE_MS(&sport->port, termios->c_cflag))
+		imx_enable_ms(&sport->port);
+
+	spin_unlock_irqrestore(&sport->port.lock, flags);
+}
+
+static const char *imx_type(struct uart_port *port)
+{
+	struct imx_port *sport = (struct imx_port *)port;
+
+	return sport->port.type == PORT_IMX ? "IMX" : NULL;
+}
+
+/*
+ * Release the memory region(s) being used by 'port'.
+ */
+static void imx_release_port(struct uart_port *port)
+{
+	struct platform_device *pdev = to_platform_device(port->dev);
+	struct resource *mmres;
+
+	mmres = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	release_mem_region(mmres->start, resource_size(mmres));
+}
+
+/*
+ * Request the memory region(s) being used by 'port'.
+ */
+static int imx_request_port(struct uart_port *port)
+{
+	struct platform_device *pdev = to_platform_device(port->dev);
+	struct resource *mmres;
+	void *ret;
+
+	mmres = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!mmres)
+		return -ENODEV;
+
+	ret = request_mem_region(mmres->start, resource_size(mmres), "imx-uart");
+
+	return  ret ? 0 : -EBUSY;
+}
+
+/*
+ * Configure/autoconfigure the port.
+ */
+static void imx_config_port(struct uart_port *port, int flags)
+{
+	struct imx_port *sport = (struct imx_port *)port;
+
+	if (flags & UART_CONFIG_TYPE &&
+	    imx_request_port(&sport->port) == 0)
+		sport->port.type = PORT_IMX;
+}
+
+/*
+ * Verify the new serial_struct (for TIOCSSERIAL).
+ * The only change we allow are to the flags and type, and
+ * even then only between PORT_IMX and PORT_UNKNOWN
+ */
+static int
+imx_verify_port(struct uart_port *port, struct serial_struct *ser)
+{
+	struct imx_port *sport = (struct imx_port *)port;
+	int ret = 0;
+
+	if (ser->type != PORT_UNKNOWN && ser->type != PORT_IMX)
+		ret = -EINVAL;
+	if (sport->port.irq != ser->irq)
+		ret = -EINVAL;
+	if (ser->io_type != UPIO_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;
+}
+
+#if defined(CONFIG_CONSOLE_POLL)
+static int imx_poll_get_char(struct uart_port *port)
+{
+	struct imx_port_ucrs old_ucr;
+	unsigned int status;
+	unsigned char c;
+
+	/* save control registers */
+	imx_port_ucrs_save(port, &old_ucr);
+
+	/* disable interrupts */
+	writel(UCR1_UARTEN, port->membase + UCR1);
+	writel(old_ucr.ucr2 & ~(UCR2_ATEN | UCR2_RTSEN | UCR2_ESCI),
+	       port->membase + UCR2);
+	writel(old_ucr.ucr3 & ~(UCR3_DCD | UCR3_RI | UCR3_DTREN),
+	       port->membase + UCR3);
+
+	/* poll */
+	do {
+		status = readl(port->membase + USR2);
+	} while (~status & USR2_RDR);
+
+	/* read */
+	c = readl(port->membase + URXD0);
+
+	/* restore control registers */
+	imx_port_ucrs_restore(port, &old_ucr);
+
+	return c;
+}
+
+static void imx_poll_put_char(struct uart_port *port, unsigned char c)
+{
+	struct imx_port_ucrs old_ucr;
+	unsigned int status;
+
+	/* save control registers */
+	imx_port_ucrs_save(port, &old_ucr);
+
+	/* disable interrupts */
+	writel(UCR1_UARTEN, port->membase + UCR1);
+	writel(old_ucr.ucr2 & ~(UCR2_ATEN | UCR2_RTSEN | UCR2_ESCI),
+	       port->membase + UCR2);
+	writel(old_ucr.ucr3 & ~(UCR3_DCD | UCR3_RI | UCR3_DTREN),
+	       port->membase + UCR3);
+
+	/* drain */
+	do {
+		status = readl(port->membase + USR1);
+	} while (~status & USR1_TRDY);
+
+	/* write */
+	writel(c, port->membase + URTX0);
+
+	/* flush */
+	do {
+		status = readl(port->membase + USR2);
+	} while (~status & USR2_TXDC);
+
+	/* restore control registers */
+	imx_port_ucrs_restore(port, &old_ucr);
+}
+#endif
+
+static struct uart_ops imx_pops = {
+	.tx_empty	= imx_tx_empty,
+	.set_mctrl	= imx_set_mctrl,
+	.get_mctrl	= imx_get_mctrl,
+	.stop_tx	= imx_stop_tx,
+	.start_tx	= imx_start_tx,
+	.stop_rx	= imx_stop_rx,
+	.enable_ms	= imx_enable_ms,
+	.break_ctl	= imx_break_ctl,
+	.startup	= imx_startup,
+	.shutdown	= imx_shutdown,
+	.set_termios	= imx_set_termios,
+	.type		= imx_type,
+	.release_port	= imx_release_port,
+	.request_port	= imx_request_port,
+	.config_port	= imx_config_port,
+	.verify_port	= imx_verify_port,
+#if defined(CONFIG_CONSOLE_POLL)
+	.poll_get_char  = imx_poll_get_char,
+	.poll_put_char  = imx_poll_put_char,
+#endif
+};
+
+static struct imx_port *imx_ports[UART_NR];
+
+#ifdef CONFIG_SERIAL_IMX_CONSOLE
+static void imx_console_putchar(struct uart_port *port, int ch)
+{
+	struct imx_port *sport = (struct imx_port *)port;
+
+	while (readl(sport->port.membase + uts_reg(sport)) & UTS_TXFULL)
+		barrier();
+
+	writel(ch, sport->port.membase + URTX0);
+}
+
+/*
+ * Interrupts are disabled on entering
+ */
+static void
+imx_console_write(struct console *co, const char *s, unsigned int count)
+{
+	struct imx_port *sport = imx_ports[co->index];
+	struct imx_port_ucrs old_ucr;
+	unsigned int ucr1;
+	unsigned long flags;
+	int locked = 1;
+
+	if (sport->port.sysrq)
+		locked = 0;
+	else if (oops_in_progress)
+		locked = spin_trylock_irqsave(&sport->port.lock, flags);
+	else
+		spin_lock_irqsave(&sport->port.lock, flags);
+
+	/*
+	 *	First, save UCR1/2/3 and then disable interrupts
+	 */
+	imx_port_ucrs_save(&sport->port, &old_ucr);
+	ucr1 = old_ucr.ucr1;
+
+	if (is_imx1_uart(sport))
+		ucr1 |= IMX1_UCR1_UARTCLKEN;
+	ucr1 |= UCR1_UARTEN;
+	ucr1 &= ~(UCR1_TXMPTYEN | UCR1_RRDYEN | UCR1_RTSDEN);
+
+	writel(ucr1, sport->port.membase + UCR1);
+
+	writel(old_ucr.ucr2 | UCR2_TXEN, sport->port.membase + UCR2);
+
+	uart_console_write(&sport->port, s, count, imx_console_putchar);
+
+	/*
+	 *	Finally, wait for transmitter to become empty
+	 *	and restore UCR1/2/3
+	 */
+	while (!(readl(sport->port.membase + USR2) & USR2_TXDC));
+
+	imx_port_ucrs_restore(&sport->port, &old_ucr);
+
+	if (locked)
+		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
+imx_console_get_options(struct imx_port *sport, int *baud,
+			   int *parity, int *bits)
+{
+
+	if (readl(sport->port.membase + UCR1) & UCR1_UARTEN) {
+		/* ok, the port was enabled */
+		unsigned int ucr2, ubir,ubmr, uartclk;
+		unsigned int baud_raw;
+		unsigned int ucfr_rfdiv;
+
+		ucr2 = readl(sport->port.membase + UCR2);
+
+		*parity = 'n';
+		if (ucr2 & UCR2_PREN) {
+			if (ucr2 & UCR2_PROE)
+				*parity = 'o';
+			else
+				*parity = 'e';
+		}
+
+		if (ucr2 & UCR2_WS)
+			*bits = 8;
+		else
+			*bits = 7;
+
+		ubir = readl(sport->port.membase + UBIR) & 0xffff;
+		ubmr = readl(sport->port.membase + UBMR) & 0xffff;
+
+		ucfr_rfdiv = (readl(sport->port.membase + UFCR) & UFCR_RFDIV) >> 7;
+		if (ucfr_rfdiv == 6)
+			ucfr_rfdiv = 7;
+		else
+			ucfr_rfdiv = 6 - ucfr_rfdiv;
+
+		uartclk = clk_get_rate(sport->clk);
+		uartclk /= ucfr_rfdiv;
+
+		{	/*
+			 * The next code provides exact computation of
+			 *   baud_raw = round(((uartclk/16) * (ubir + 1)) / (ubmr + 1))
+			 * without need of float support or long long division,
+			 * which would be required to prevent 32bit arithmetic overflow
+			 */
+			unsigned int mul = ubir + 1;
+			unsigned int div = 16 * (ubmr + 1);
+			unsigned int rem = uartclk % div;
+
+			baud_raw = (uartclk / div) * mul;
+			baud_raw += (rem * mul + div / 2) / div;
+			*baud = (baud_raw + 50) / 100 * 100;
+		}
+
+		if(*baud != baud_raw)
+			printk(KERN_INFO "Serial: Console IMX rounded baud rate from %d to %d\n",
+				baud_raw, *baud);
+	}
+}
+
+static int __init
+imx_console_setup(struct console *co, char *options)
+{
+	struct imx_port *sport;
+	int baud = 9600;
+	int bits = 8;
+	int parity = 'n';
+	int flow = 'n';
+
+	/*
+	 * 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 >= ARRAY_SIZE(imx_ports))
+		co->index = 0;
+	sport = imx_ports[co->index];
+	if(sport == NULL)
+		return -ENODEV;
+
+	if (options)
+		uart_parse_options(options, &baud, &parity, &bits, &flow);
+	else
+		imx_console_get_options(sport, &baud, &parity, &bits);
+
+	imx_setup_ufcr(sport, 0);
+
+	return uart_set_options(&sport->port, co, baud, parity, bits, flow);
+}
+
+static struct uart_driver imx_reg;
+static struct console imx_console = {
+	.name		= DEV_NAME,
+	.write		= imx_console_write,
+	.device		= uart_console_device,
+	.setup		= imx_console_setup,
+	.flags		= CON_PRINTBUFFER,
+	.index		= -1,
+	.data		= &imx_reg,
+};
+
+#define IMX_CONSOLE	&imx_console
+#else
+#define IMX_CONSOLE	NULL
+#endif
+
+static struct uart_driver imx_reg = {
+	.owner          = THIS_MODULE,
+	.driver_name    = DRIVER_NAME,
+	.dev_name       = DEV_NAME,
+	.major          = SERIAL_IMX_MAJOR,
+	.minor          = MINOR_START,
+	.nr             = ARRAY_SIZE(imx_ports),
+	.cons           = IMX_CONSOLE,
+};
+
+static int serial_imx_suspend(struct platform_device *dev, pm_message_t state)
+{
+	struct imx_port *sport = platform_get_drvdata(dev);
+	unsigned int val;
+
+	/* enable wakeup from i.MX UART */
+	val = readl(sport->port.membase + UCR3);
+	val |= UCR3_AWAKEN;
+	writel(val, sport->port.membase + UCR3);
+
+	if (sport)
+		uart_suspend_port(&imx_reg, &sport->port);
+
+	return 0;
+}
+
+static int serial_imx_resume(struct platform_device *dev)
+{
+	struct imx_port *sport = platform_get_drvdata(dev);
+	unsigned int val;
+
+	/* disable wakeup from i.MX UART */
+	val = readl(sport->port.membase + UCR3);
+	val &= ~UCR3_AWAKEN;
+	writel(val, sport->port.membase + UCR3);
+
+	if (sport)
+		uart_resume_port(&imx_reg, &sport->port);
+
+	return 0;
+}
+
+#ifdef CONFIG_OF
+/*
+ * This function returns 1 iff pdev isn't a device instatiated by dt, 0 iff it
+ * could successfully get all information from dt or a negative errno.
+ */
+static int serial_imx_probe_dt(struct imx_port *sport,
+		struct platform_device *pdev)
+{
+	struct device_node *np = pdev->dev.of_node;
+	const struct of_device_id *of_id =
+			of_match_device(imx_uart_dt_ids, &pdev->dev);
+	int ret;
+
+	if (!np)
+		/* no device tree device */
+		return 1;
+
+	ret = of_alias_get_id(np, "serial");
+	if (ret < 0) {
+		dev_err(&pdev->dev, "failed to get alias id, errno %d\n", ret);
+		return ret;
+	}
+	sport->port.line = ret;
+
+	if (of_get_property(np, "fsl,uart-has-rtscts", NULL))
+		sport->have_rtscts = 1;
+
+	if (of_get_property(np, "fsl,irda-mode", NULL))
+		sport->use_irda = 1;
+
+	sport->devdata = of_id->data;
+
+	return 0;
+}
+#else
+static inline int serial_imx_probe_dt(struct imx_port *sport,
+		struct platform_device *pdev)
+{
+	return 1;
+}
+#endif
+
+static void serial_imx_probe_pdata(struct imx_port *sport,
+		struct platform_device *pdev)
+{
+	struct imxuart_platform_data *pdata = pdev->dev.platform_data;
+
+	sport->port.line = pdev->id;
+	sport->devdata = (struct imx_uart_data	*) pdev->id_entry->driver_data;
+
+	if (!pdata)
+		return;
+
+	if (pdata->flags & IMXUART_HAVE_RTSCTS)
+		sport->have_rtscts = 1;
+
+	if (pdata->flags & IMXUART_IRDA)
+		sport->use_irda = 1;
+}
+
+static int serial_imx_probe(struct platform_device *pdev)
+{
+	struct imx_port *sport;
+	struct imxuart_platform_data *pdata;
+	void __iomem *base;
+	int ret = 0;
+	struct resource *res;
+
+	sport = kzalloc(sizeof(*sport), GFP_KERNEL);
+	if (!sport)
+		return -ENOMEM;
+
+	ret = serial_imx_probe_dt(sport, pdev);
+	if (ret > 0)
+		serial_imx_probe_pdata(sport, pdev);
+	else if (ret < 0)
+		goto free;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!res) {
+		ret = -ENODEV;
+		goto free;
+	}
+
+	base = ioremap(res->start, PAGE_SIZE);
+	if (!base) {
+		ret = -ENOMEM;
+		goto free;
+	}
+
+	sport->port.dev = &pdev->dev;
+	sport->port.mapbase = res->start;
+	sport->port.membase = base;
+	sport->port.type = PORT_IMX,
+	sport->port.iotype = UPIO_MEM;
+	sport->port.irq = platform_get_irq(pdev, 0);
+	sport->rxirq = platform_get_irq(pdev, 0);
+	sport->txirq = platform_get_irq(pdev, 1);
+	sport->rtsirq = platform_get_irq(pdev, 2);
+	sport->port.fifosize = 32;
+	sport->port.ops = &imx_pops;
+	sport->port.flags = UPF_BOOT_AUTOCONF;
+	init_timer(&sport->timer);
+	sport->timer.function = imx_timeout;
+	sport->timer.data     = (unsigned long)sport;
+
+	sport->clk = clk_get(&pdev->dev, "uart");
+	if (IS_ERR(sport->clk)) {
+		ret = PTR_ERR(sport->clk);
+		goto unmap;
+	}
+	clk_prepare_enable(sport->clk);
+
+	sport->port.uartclk = clk_get_rate(sport->clk);
+
+	imx_ports[sport->port.line] = sport;
+
+	pdata = pdev->dev.platform_data;
+	if (pdata && pdata->init) {
+		ret = pdata->init(pdev);
+		if (ret)
+			goto clkput;
+	}
+
+	ret = uart_add_one_port(&imx_reg, &sport->port);
+	if (ret)
+		goto deinit;
+	platform_set_drvdata(pdev, &sport->port);
+
+	return 0;
+deinit:
+	if (pdata && pdata->exit)
+		pdata->exit(pdev);
+clkput:
+	clk_disable_unprepare(sport->clk);
+	clk_put(sport->clk);
+unmap:
+	iounmap(sport->port.membase);
+free:
+	kfree(sport);
+
+	return ret;
+}
+
+static int serial_imx_remove(struct platform_device *pdev)
+{
+	struct imxuart_platform_data *pdata;
+	struct imx_port *sport = platform_get_drvdata(pdev);
+
+	pdata = pdev->dev.platform_data;
+
+	platform_set_drvdata(pdev, NULL);
+
+	if (sport) {
+		uart_remove_one_port(&imx_reg, &sport->port);
+		clk_disable_unprepare(sport->clk);
+		clk_put(sport->clk);
+	}
+
+	if (pdata && pdata->exit)
+		pdata->exit(pdev);
+
+	iounmap(sport->port.membase);
+	kfree(sport);
+
+	return 0;
+}
+
+static struct platform_driver serial_imx_driver = {
+	.probe		= serial_imx_probe,
+	.remove		= serial_imx_remove,
+
+	.suspend	= serial_imx_suspend,
+	.resume		= serial_imx_resume,
+	.id_table	= imx_uart_devtype,
+	.driver		= {
+		.name	= "imx-uart",
+		.owner	= THIS_MODULE,
+		.of_match_table = imx_uart_dt_ids,
+	},
+};
+
+static int __init imx_serial_init(void)
+{
+	int ret;
+
+	printk(KERN_INFO "Serial: IMX driver\n");
+
+	ret = uart_register_driver(&imx_reg);
+	if (ret)
+		return ret;
+
+	ret = platform_driver_register(&serial_imx_driver);
+	if (ret != 0)
+		uart_unregister_driver(&imx_reg);
+
+	return ret;
+}
+
+static void __exit imx_serial_exit(void)
+{
+	platform_driver_unregister(&serial_imx_driver);
+	uart_unregister_driver(&imx_reg);
+}
+
+module_init(imx_serial_init);
+module_exit(imx_serial_exit);
+
+MODULE_AUTHOR("Sascha Hauer");
+MODULE_DESCRIPTION("IMX generic serial port driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:imx-uart");
diff --git a/ap/os/linux/linux-3.4.x/drivers/tty/serial/ioc3_serial.c b/ap/os/linux/linux-3.4.x/drivers/tty/serial/ioc3_serial.c
new file mode 100644
index 0000000..758ff31
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/tty/serial/ioc3_serial.c
@@ -0,0 +1,2200 @@
+/*
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License.  See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * Copyright (C) 2005 Silicon Graphics, Inc.  All Rights Reserved.
+ */
+
+/*
+ * This file contains a module version of the ioc3 serial driver. This
+ * includes all the support functions needed (support functions, etc.)
+ * and the serial driver itself.
+ */
+#include <linux/errno.h>
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
+#include <linux/serial.h>
+#include <linux/circ_buf.h>
+#include <linux/serial_reg.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/serial_core.h>
+#include <linux/ioc3.h>
+#include <linux/slab.h>
+
+/*
+ * Interesting things about the ioc3
+ */
+
+#define LOGICAL_PORTS		2	/* rs232(0) and rs422(1) */
+#define PORTS_PER_CARD		2
+#define LOGICAL_PORTS_PER_CARD (PORTS_PER_CARD * LOGICAL_PORTS)
+#define MAX_CARDS		8
+#define MAX_LOGICAL_PORTS	(LOGICAL_PORTS_PER_CARD * MAX_CARDS)
+
+/* determine given the sio_ir what port it applies to */
+#define GET_PORT_FROM_SIO_IR(_x)	(_x & SIO_IR_SA) ? 0 : 1
+
+
+/*
+ * we have 2 logical ports (rs232, rs422) for each physical port
+ * evens are rs232, odds are rs422
+ */
+#define GET_PHYSICAL_PORT(_x)	((_x) >> 1)
+#define GET_LOGICAL_PORT(_x)	((_x) & 1)
+#define IS_PHYSICAL_PORT(_x)	!((_x) & 1)
+#define IS_RS232(_x)		!((_x) & 1)
+
+static unsigned int Num_of_ioc3_cards;
+static unsigned int Submodule_slot;
+
+/* defining this will get you LOTS of great debug info */
+//#define DEBUG_INTERRUPTS
+#define DPRINT_CONFIG(_x...)	;
+//#define DPRINT_CONFIG(_x...)  printk _x
+#define NOT_PROGRESS()	;
+//#define NOT_PROGRESS()	printk("%s : fails %d\n", __func__, __LINE__)
+
+/* number of characters we want to transmit to the lower level at a time */
+#define MAX_CHARS		256
+#define FIFO_SIZE		(MAX_CHARS-1)	/* it's a uchar */
+
+/* Device name we're using */
+#define DEVICE_NAME		"ttySIOC"
+#define DEVICE_MAJOR		204
+#define DEVICE_MINOR		116
+
+/* flags for next_char_state */
+#define NCS_BREAK		0x1
+#define NCS_PARITY		0x2
+#define NCS_FRAMING		0x4
+#define NCS_OVERRUN		0x8
+
+/* cause we need SOME parameters ... */
+#define MIN_BAUD_SUPPORTED	1200
+#define MAX_BAUD_SUPPORTED	115200
+
+/* protocol types supported */
+#define PROTO_RS232		0
+#define PROTO_RS422		1
+
+/* Notification types */
+#define N_DATA_READY		0x01
+#define N_OUTPUT_LOWAT		0x02
+#define N_BREAK			0x04
+#define N_PARITY_ERROR		0x08
+#define N_FRAMING_ERROR		0x10
+#define N_OVERRUN_ERROR		0x20
+#define N_DDCD			0x40
+#define N_DCTS			0x80
+
+#define N_ALL_INPUT		(N_DATA_READY | N_BREAK			   \
+					| N_PARITY_ERROR | N_FRAMING_ERROR \
+					| N_OVERRUN_ERROR | N_DDCD | N_DCTS)
+
+#define N_ALL_OUTPUT		N_OUTPUT_LOWAT
+
+#define N_ALL_ERRORS		(N_PARITY_ERROR | N_FRAMING_ERROR \
+						| N_OVERRUN_ERROR)
+
+#define N_ALL			(N_DATA_READY | N_OUTPUT_LOWAT | N_BREAK    \
+					| N_PARITY_ERROR | N_FRAMING_ERROR  \
+					| N_OVERRUN_ERROR | N_DDCD | N_DCTS)
+
+#define SER_CLK_SPEED(prediv)	((22000000 << 1) / prediv)
+#define SER_DIVISOR(x, clk)	(((clk) + (x) * 8) / ((x) * 16))
+#define DIVISOR_TO_BAUD(div, clk) ((clk) / 16 / (div))
+
+/* Some masks */
+#define LCR_MASK_BITS_CHAR	(UART_LCR_WLEN5 | UART_LCR_WLEN6 \
+					| UART_LCR_WLEN7 | UART_LCR_WLEN8)
+#define LCR_MASK_STOP_BITS	(UART_LCR_STOP)
+
+#define PENDING(_a, _p)		(readl(&(_p)->vma->sio_ir) & (_a)->ic_enable)
+
+#define RING_BUF_SIZE		4096
+#define BUF_SIZE_BIT		SBBR_L_SIZE
+#define PROD_CONS_MASK		PROD_CONS_PTR_4K
+
+#define TOTAL_RING_BUF_SIZE	(RING_BUF_SIZE * 4)
+
+/* driver specific - one per card */
+struct ioc3_card {
+	struct {
+		/* uart ports are allocated here */
+		struct uart_port icp_uart_port[LOGICAL_PORTS];
+		/* the ioc3_port used for this port */
+		struct ioc3_port *icp_port;
+	} ic_port[PORTS_PER_CARD];
+	/* currently enabled interrupts */
+	uint32_t ic_enable;
+};
+
+/* Local port info for each IOC3 serial port */
+struct ioc3_port {
+	/* handy reference material */
+	struct uart_port *ip_port;
+	struct ioc3_card *ip_card;
+	struct ioc3_driver_data *ip_idd;
+	struct ioc3_submodule *ip_is;
+
+	/* pci mem addresses for this port */
+	struct ioc3_serialregs __iomem *ip_serial_regs;
+	struct ioc3_uartregs __iomem *ip_uart_regs;
+
+	/* Ring buffer page for this port */
+	dma_addr_t ip_dma_ringbuf;
+	/* vaddr of ring buffer */
+	struct ring_buffer *ip_cpu_ringbuf;
+
+	/* Rings for this port */
+	struct ring *ip_inring;
+	struct ring *ip_outring;
+
+	/* Hook to port specific values */
+	struct port_hooks *ip_hooks;
+
+	spinlock_t ip_lock;
+
+	/* Various rx/tx parameters */
+	int ip_baud;
+	int ip_tx_lowat;
+	int ip_rx_timeout;
+
+	/* Copy of notification bits */
+	int ip_notify;
+
+	/* Shadow copies of various registers so we don't need to PIO
+	 * read them constantly
+	 */
+	uint32_t ip_sscr;
+	uint32_t ip_tx_prod;
+	uint32_t ip_rx_cons;
+	unsigned char ip_flags;
+};
+
+/* tx low water mark.  We need to notify the driver whenever tx is getting
+ * close to empty so it can refill the tx buffer and keep things going.
+ * Let's assume that if we interrupt 1 ms before the tx goes idle, we'll
+ * have no trouble getting in more chars in time (I certainly hope so).
+ */
+#define TX_LOWAT_LATENCY      1000
+#define TX_LOWAT_HZ          (1000000 / TX_LOWAT_LATENCY)
+#define TX_LOWAT_CHARS(baud) (baud / 10 / TX_LOWAT_HZ)
+
+/* Flags per port */
+#define INPUT_HIGH		0x01
+	/* used to signify that we have turned off the rx_high
+	 * temporarily - we need to drain the fifo and don't
+	 * want to get blasted with interrupts.
+	 */
+#define DCD_ON			0x02
+	/* DCD state is on */
+#define LOWAT_WRITTEN		0x04
+#define READ_ABORTED		0x08
+	/* the read was aborted - used to avaoid infinate looping
+	 * in the interrupt handler
+	 */
+#define INPUT_ENABLE		0x10
+
+/* Since each port has different register offsets and bitmasks
+ * for everything, we'll store those that we need in tables so we
+ * don't have to be constantly checking the port we are dealing with.
+ */
+struct port_hooks {
+	uint32_t intr_delta_dcd;
+	uint32_t intr_delta_cts;
+	uint32_t intr_tx_mt;
+	uint32_t intr_rx_timer;
+	uint32_t intr_rx_high;
+	uint32_t intr_tx_explicit;
+	uint32_t intr_clear;
+	uint32_t intr_all;
+	char rs422_select_pin;
+};
+
+static struct port_hooks hooks_array[PORTS_PER_CARD] = {
+	/* values for port A */
+	{
+	.intr_delta_dcd = SIO_IR_SA_DELTA_DCD,
+	.intr_delta_cts = SIO_IR_SA_DELTA_CTS,
+	.intr_tx_mt = SIO_IR_SA_TX_MT,
+	.intr_rx_timer = SIO_IR_SA_RX_TIMER,
+	.intr_rx_high = SIO_IR_SA_RX_HIGH,
+	.intr_tx_explicit = SIO_IR_SA_TX_EXPLICIT,
+	.intr_clear = (SIO_IR_SA_TX_MT | SIO_IR_SA_RX_FULL
+				| SIO_IR_SA_RX_HIGH
+				| SIO_IR_SA_RX_TIMER
+				| SIO_IR_SA_DELTA_DCD
+				| SIO_IR_SA_DELTA_CTS
+				| SIO_IR_SA_INT
+				| SIO_IR_SA_TX_EXPLICIT
+				| SIO_IR_SA_MEMERR),
+	.intr_all =  SIO_IR_SA,
+	.rs422_select_pin = GPPR_UARTA_MODESEL_PIN,
+	 },
+
+	/* values for port B */
+	{
+	.intr_delta_dcd = SIO_IR_SB_DELTA_DCD,
+	.intr_delta_cts = SIO_IR_SB_DELTA_CTS,
+	.intr_tx_mt = SIO_IR_SB_TX_MT,
+	.intr_rx_timer = SIO_IR_SB_RX_TIMER,
+	.intr_rx_high = SIO_IR_SB_RX_HIGH,
+	.intr_tx_explicit = SIO_IR_SB_TX_EXPLICIT,
+	.intr_clear = (SIO_IR_SB_TX_MT | SIO_IR_SB_RX_FULL
+				| SIO_IR_SB_RX_HIGH
+				| SIO_IR_SB_RX_TIMER
+				| SIO_IR_SB_DELTA_DCD
+				| SIO_IR_SB_DELTA_CTS
+				| SIO_IR_SB_INT
+				| SIO_IR_SB_TX_EXPLICIT
+				| SIO_IR_SB_MEMERR),
+	.intr_all = SIO_IR_SB,
+	.rs422_select_pin = GPPR_UARTB_MODESEL_PIN,
+	 }
+};
+
+struct ring_entry {
+	union {
+		struct {
+			uint32_t alldata;
+			uint32_t allsc;
+		} all;
+		struct {
+			char data[4];	/* data bytes */
+			char sc[4];	/* status/control */
+		} s;
+	} u;
+};
+
+/* Test the valid bits in any of the 4 sc chars using "allsc" member */
+#define RING_ANY_VALID \
+	((uint32_t)(RXSB_MODEM_VALID | RXSB_DATA_VALID) * 0x01010101)
+
+#define ring_sc		u.s.sc
+#define ring_data	u.s.data
+#define ring_allsc	u.all.allsc
+
+/* Number of entries per ring buffer. */
+#define ENTRIES_PER_RING (RING_BUF_SIZE / (int) sizeof(struct ring_entry))
+
+/* An individual ring */
+struct ring {
+	struct ring_entry entries[ENTRIES_PER_RING];
+};
+
+/* The whole enchilada */
+struct ring_buffer {
+	struct ring TX_A;
+	struct ring RX_A;
+	struct ring TX_B;
+	struct ring RX_B;
+};
+
+/* Get a ring from a port struct */
+#define RING(_p, _wh)	&(((struct ring_buffer *)((_p)->ip_cpu_ringbuf))->_wh)
+
+/* for Infinite loop detection  */
+#define MAXITER		10000000
+
+
+/**
+ * set_baud - Baud rate setting code
+ * @port: port to set
+ * @baud: baud rate to use
+ */
+static int set_baud(struct ioc3_port *port, int baud)
+{
+	int divisor;
+	int actual_baud;
+	int diff;
+	int lcr, prediv;
+	struct ioc3_uartregs __iomem *uart;
+
+	for (prediv = 6; prediv < 64; prediv++) {
+		divisor = SER_DIVISOR(baud, SER_CLK_SPEED(prediv));
+		if (!divisor)
+			continue;	/* invalid divisor */
+		actual_baud = DIVISOR_TO_BAUD(divisor, SER_CLK_SPEED(prediv));
+
+		diff = actual_baud - baud;
+		if (diff < 0)
+			diff = -diff;
+
+		/* if we're within 1% we've found a match */
+		if (diff * 100 <= actual_baud)
+			break;
+	}
+
+	/* if the above loop completed, we didn't match
+	 * the baud rate.  give up.
+	 */
+	if (prediv == 64) {
+		NOT_PROGRESS();
+		return 1;
+	}
+
+	uart = port->ip_uart_regs;
+	lcr = readb(&uart->iu_lcr);
+
+	writeb(lcr | UART_LCR_DLAB, &uart->iu_lcr);
+	writeb((unsigned char)divisor, &uart->iu_dll);
+	writeb((unsigned char)(divisor >> 8), &uart->iu_dlm);
+	writeb((unsigned char)prediv, &uart->iu_scr);
+	writeb((unsigned char)lcr, &uart->iu_lcr);
+
+	return 0;
+}
+
+/**
+ * get_ioc3_port - given a uart port, return the control structure
+ * @the_port: uart port to find
+ */
+static struct ioc3_port *get_ioc3_port(struct uart_port *the_port)
+{
+	struct ioc3_driver_data *idd = dev_get_drvdata(the_port->dev);
+	struct ioc3_card *card_ptr = idd->data[Submodule_slot];
+	int ii, jj;
+
+	if (!card_ptr) {
+		NOT_PROGRESS();
+		return NULL;
+	}
+	for (ii = 0; ii < PORTS_PER_CARD; ii++) {
+		for (jj = 0; jj < LOGICAL_PORTS; jj++) {
+			if (the_port == &card_ptr->ic_port[ii].icp_uart_port[jj])
+				return card_ptr->ic_port[ii].icp_port;
+		}
+	}
+	NOT_PROGRESS();
+	return NULL;
+}
+
+/**
+ * port_init - Initialize the sio and ioc3 hardware for a given port
+ *			called per port from attach...
+ * @port: port to initialize
+ */
+static int inline port_init(struct ioc3_port *port)
+{
+	uint32_t sio_cr;
+	struct port_hooks *hooks = port->ip_hooks;
+	struct ioc3_uartregs __iomem *uart;
+	int reset_loop_counter = 0xfffff;
+	struct ioc3_driver_data *idd = port->ip_idd;
+
+	/* Idle the IOC3 serial interface */
+	writel(SSCR_RESET, &port->ip_serial_regs->sscr);
+
+	/* Wait until any pending bus activity for this port has ceased */
+	do {
+		sio_cr = readl(&idd->vma->sio_cr);
+		if (reset_loop_counter-- <= 0) {
+			printk(KERN_WARNING
+			       "IOC3 unable to come out of reset"
+				" scr 0x%x\n", sio_cr);
+			return -1;
+		}
+	} while (!(sio_cr & SIO_CR_ARB_DIAG_IDLE) &&
+	       (((sio_cr &= SIO_CR_ARB_DIAG) == SIO_CR_ARB_DIAG_TXA)
+		|| sio_cr == SIO_CR_ARB_DIAG_TXB
+		|| sio_cr == SIO_CR_ARB_DIAG_RXA
+		|| sio_cr == SIO_CR_ARB_DIAG_RXB));
+
+	/* Finish reset sequence */
+	writel(0, &port->ip_serial_regs->sscr);
+
+	/* Once RESET is done, reload cached tx_prod and rx_cons values
+	 * and set rings to empty by making prod == cons
+	 */
+	port->ip_tx_prod = readl(&port->ip_serial_regs->stcir) & PROD_CONS_MASK;
+	writel(port->ip_tx_prod, &port->ip_serial_regs->stpir);
+	port->ip_rx_cons = readl(&port->ip_serial_regs->srpir) & PROD_CONS_MASK;
+	writel(port->ip_rx_cons | SRCIR_ARM, &port->ip_serial_regs->srcir);
+
+	/* Disable interrupts for this 16550 */
+	uart = port->ip_uart_regs;
+	writeb(0, &uart->iu_lcr);
+	writeb(0, &uart->iu_ier);
+
+	/* Set the default baud */
+	set_baud(port, port->ip_baud);
+
+	/* Set line control to 8 bits no parity */
+	writeb(UART_LCR_WLEN8 | 0, &uart->iu_lcr);
+	/* UART_LCR_STOP == 1 stop */
+
+	/* Enable the FIFOs */
+	writeb(UART_FCR_ENABLE_FIFO, &uart->iu_fcr);
+	/* then reset 16550 FIFOs */
+	writeb(UART_FCR_ENABLE_FIFO | UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT,
+	       &uart->iu_fcr);
+
+	/* Clear modem control register */
+	writeb(0, &uart->iu_mcr);
+
+	/* Clear deltas in modem status register */
+	writel(0, &port->ip_serial_regs->shadow);
+
+	/* Only do this once per port pair */
+	if (port->ip_hooks == &hooks_array[0]) {
+		unsigned long ring_pci_addr;
+		uint32_t __iomem *sbbr_l, *sbbr_h;
+
+		sbbr_l = &idd->vma->sbbr_l;
+		sbbr_h = &idd->vma->sbbr_h;
+		ring_pci_addr = (unsigned long __iomem)port->ip_dma_ringbuf;
+		DPRINT_CONFIG(("%s: ring_pci_addr 0x%p\n",
+			       __func__, (void *)ring_pci_addr));
+
+		writel((unsigned int)((uint64_t) ring_pci_addr >> 32), sbbr_h);
+		writel((unsigned int)ring_pci_addr | BUF_SIZE_BIT, sbbr_l);
+	}
+
+	/* Set the receive timeout value to 10 msec */
+	writel(SRTR_HZ / 100, &port->ip_serial_regs->srtr);
+
+	/* Set rx threshold, enable DMA */
+	/* Set high water mark at 3/4 of full ring */
+	port->ip_sscr = (ENTRIES_PER_RING * 3 / 4);
+
+	/* uart experiences pauses at high baud rate reducing actual
+	 * throughput by 10% or so unless we enable high speed polling
+	 * XXX when this hardware bug is resolved we should revert to
+	 * normal polling speed
+	 */
+	port->ip_sscr |= SSCR_HIGH_SPD;
+
+	writel(port->ip_sscr, &port->ip_serial_regs->sscr);
+
+	/* Disable and clear all serial related interrupt bits */
+	port->ip_card->ic_enable &= ~hooks->intr_clear;
+	ioc3_disable(port->ip_is, idd, hooks->intr_clear);
+	ioc3_ack(port->ip_is, idd, hooks->intr_clear);
+	return 0;
+}
+
+/**
+ * enable_intrs - enable interrupts
+ * @port: port to enable
+ * @mask: mask to use
+ */
+static void enable_intrs(struct ioc3_port *port, uint32_t mask)
+{
+	if ((port->ip_card->ic_enable & mask) != mask) {
+		port->ip_card->ic_enable |= mask;
+		ioc3_enable(port->ip_is, port->ip_idd, mask);
+	}
+}
+
+/**
+ * local_open - local open a port
+ * @port: port to open
+ */
+static inline int local_open(struct ioc3_port *port)
+{
+	int spiniter = 0;
+
+	port->ip_flags = INPUT_ENABLE;
+
+	/* Pause the DMA interface if necessary */
+	if (port->ip_sscr & SSCR_DMA_EN) {
+		writel(port->ip_sscr | SSCR_DMA_PAUSE,
+		       &port->ip_serial_regs->sscr);
+		while ((readl(&port->ip_serial_regs->sscr)
+			& SSCR_PAUSE_STATE) == 0) {
+			spiniter++;
+			if (spiniter > MAXITER) {
+				NOT_PROGRESS();
+				return -1;
+			}
+		}
+	}
+
+	/* Reset the input fifo.  If the uart received chars while the port
+	 * was closed and DMA is not enabled, the uart may have a bunch of
+	 * chars hanging around in its rx fifo which will not be discarded
+	 * by rclr in the upper layer. We must get rid of them here.
+	 */
+	writeb(UART_FCR_ENABLE_FIFO | UART_FCR_CLEAR_RCVR,
+	       &port->ip_uart_regs->iu_fcr);
+
+	writeb(UART_LCR_WLEN8, &port->ip_uart_regs->iu_lcr);
+	/* UART_LCR_STOP == 1 stop */
+
+	/* Re-enable DMA, set default threshold to intr whenever there is
+	 * data available.
+	 */
+	port->ip_sscr &= ~SSCR_RX_THRESHOLD;
+	port->ip_sscr |= 1;	/* default threshold */
+
+	/* Plug in the new sscr.  This implicitly clears the DMA_PAUSE
+	 * flag if it was set above
+	 */
+	writel(port->ip_sscr, &port->ip_serial_regs->sscr);
+	port->ip_tx_lowat = 1;
+	return 0;
+}
+
+/**
+ * set_rx_timeout - Set rx timeout and threshold values.
+ * @port: port to use
+ * @timeout: timeout value in ticks
+ */
+static inline int set_rx_timeout(struct ioc3_port *port, int timeout)
+{
+	int threshold;
+
+	port->ip_rx_timeout = timeout;
+
+	/* Timeout is in ticks.  Let's figure out how many chars we
+	 * can receive at the current baud rate in that interval
+	 * and set the rx threshold to that amount.  There are 4 chars
+	 * per ring entry, so we'll divide the number of chars that will
+	 * arrive in timeout by 4.
+	 * So .... timeout * baud / 10 / HZ / 4, with HZ = 100.
+	 */
+	threshold = timeout * port->ip_baud / 4000;
+	if (threshold == 0)
+		threshold = 1;	/* otherwise we'll intr all the time! */
+
+	if ((unsigned)threshold > (unsigned)SSCR_RX_THRESHOLD)
+		return 1;
+
+	port->ip_sscr &= ~SSCR_RX_THRESHOLD;
+	port->ip_sscr |= threshold;
+	writel(port->ip_sscr, &port->ip_serial_regs->sscr);
+
+	/* Now set the rx timeout to the given value
+	 * again timeout * SRTR_HZ / HZ
+	 */
+	timeout = timeout * SRTR_HZ / 100;
+	if (timeout > SRTR_CNT)
+		timeout = SRTR_CNT;
+	writel(timeout, &port->ip_serial_regs->srtr);
+	return 0;
+}
+
+/**
+ * config_port - config the hardware
+ * @port: port to config
+ * @baud: baud rate for the port
+ * @byte_size: data size
+ * @stop_bits: number of stop bits
+ * @parenb: parity enable ?
+ * @parodd: odd parity ?
+ */
+static inline int
+config_port(struct ioc3_port *port,
+	    int baud, int byte_size, int stop_bits, int parenb, int parodd)
+{
+	char lcr, sizebits;
+	int spiniter = 0;
+
+	DPRINT_CONFIG(("%s: line %d baud %d byte_size %d stop %d parenb %d "
+			"parodd %d\n",
+		       __func__, ((struct uart_port *)port->ip_port)->line,
+			baud, byte_size, stop_bits, parenb, parodd));
+
+	if (set_baud(port, baud))
+		return 1;
+
+	switch (byte_size) {
+	case 5:
+		sizebits = UART_LCR_WLEN5;
+		break;
+	case 6:
+		sizebits = UART_LCR_WLEN6;
+		break;
+	case 7:
+		sizebits = UART_LCR_WLEN7;
+		break;
+	case 8:
+		sizebits = UART_LCR_WLEN8;
+		break;
+	default:
+		return 1;
+	}
+
+	/* Pause the DMA interface if necessary */
+	if (port->ip_sscr & SSCR_DMA_EN) {
+		writel(port->ip_sscr | SSCR_DMA_PAUSE,
+		       &port->ip_serial_regs->sscr);
+		while ((readl(&port->ip_serial_regs->sscr)
+			& SSCR_PAUSE_STATE) == 0) {
+			spiniter++;
+			if (spiniter > MAXITER)
+				return -1;
+		}
+	}
+
+	/* Clear relevant fields in lcr */
+	lcr = readb(&port->ip_uart_regs->iu_lcr);
+	lcr &= ~(LCR_MASK_BITS_CHAR | UART_LCR_EPAR |
+		 UART_LCR_PARITY | LCR_MASK_STOP_BITS);
+
+	/* Set byte size in lcr */
+	lcr |= sizebits;
+
+	/* Set parity */
+	if (parenb) {
+		lcr |= UART_LCR_PARITY;
+		if (!parodd)
+			lcr |= UART_LCR_EPAR;
+	}
+
+	/* Set stop bits */
+	if (stop_bits)
+		lcr |= UART_LCR_STOP /* 2 stop bits */ ;
+
+	writeb(lcr, &port->ip_uart_regs->iu_lcr);
+
+	/* Re-enable the DMA interface if necessary */
+	if (port->ip_sscr & SSCR_DMA_EN) {
+		writel(port->ip_sscr, &port->ip_serial_regs->sscr);
+	}
+	port->ip_baud = baud;
+
+	/* When we get within this number of ring entries of filling the
+	 * entire ring on tx, place an EXPLICIT intr to generate a lowat
+	 * notification when output has drained.
+	 */
+	port->ip_tx_lowat = (TX_LOWAT_CHARS(baud) + 3) / 4;
+	if (port->ip_tx_lowat == 0)
+		port->ip_tx_lowat = 1;
+
+	set_rx_timeout(port, 2);
+	return 0;
+}
+
+/**
+ * do_write - Write bytes to the port.  Returns the number of bytes
+ *			actually written. Called from transmit_chars
+ * @port: port to use
+ * @buf: the stuff to write
+ * @len: how many bytes in 'buf'
+ */
+static inline int do_write(struct ioc3_port *port, char *buf, int len)
+{
+	int prod_ptr, cons_ptr, total = 0;
+	struct ring *outring;
+	struct ring_entry *entry;
+	struct port_hooks *hooks = port->ip_hooks;
+
+	BUG_ON(!(len >= 0));
+
+	prod_ptr = port->ip_tx_prod;
+	cons_ptr = readl(&port->ip_serial_regs->stcir) & PROD_CONS_MASK;
+	outring = port->ip_outring;
+
+	/* Maintain a 1-entry red-zone.  The ring buffer is full when
+	 * (cons - prod) % ring_size is 1.  Rather than do this subtraction
+	 * in the body of the loop, I'll do it now.
+	 */
+	cons_ptr = (cons_ptr - (int)sizeof(struct ring_entry)) & PROD_CONS_MASK;
+
+	/* Stuff the bytes into the output */
+	while ((prod_ptr != cons_ptr) && (len > 0)) {
+		int xx;
+
+		/* Get 4 bytes (one ring entry) at a time */
+		entry = (struct ring_entry *)((caddr_t) outring + prod_ptr);
+
+		/* Invalidate all entries */
+		entry->ring_allsc = 0;
+
+		/* Copy in some bytes */
+		for (xx = 0; (xx < 4) && (len > 0); xx++) {
+			entry->ring_data[xx] = *buf++;
+			entry->ring_sc[xx] = TXCB_VALID;
+			len--;
+			total++;
+		}
+
+		/* If we are within some small threshold of filling up the
+		 * entire ring buffer, we must place an EXPLICIT intr here
+		 * to generate a lowat interrupt in case we subsequently
+		 * really do fill up the ring and the caller goes to sleep.
+		 * No need to place more than one though.
+		 */
+		if (!(port->ip_flags & LOWAT_WRITTEN) &&
+		    ((cons_ptr - prod_ptr) & PROD_CONS_MASK)
+		    <= port->ip_tx_lowat * (int)sizeof(struct ring_entry)) {
+			port->ip_flags |= LOWAT_WRITTEN;
+			entry->ring_sc[0] |= TXCB_INT_WHEN_DONE;
+		}
+
+		/* Go on to next entry */
+		prod_ptr += sizeof(struct ring_entry);
+		prod_ptr &= PROD_CONS_MASK;
+	}
+
+	/* If we sent something, start DMA if necessary */
+	if (total > 0 && !(port->ip_sscr & SSCR_DMA_EN)) {
+		port->ip_sscr |= SSCR_DMA_EN;
+		writel(port->ip_sscr, &port->ip_serial_regs->sscr);
+	}
+
+	/* Store the new producer pointer.  If tx is disabled, we stuff the
+	 * data into the ring buffer, but we don't actually start tx.
+	 */
+	if (!uart_tx_stopped(port->ip_port)) {
+		writel(prod_ptr, &port->ip_serial_regs->stpir);
+
+		/* If we are now transmitting, enable tx_mt interrupt so we
+		 * can disable DMA if necessary when the tx finishes.
+		 */
+		if (total > 0)
+			enable_intrs(port, hooks->intr_tx_mt);
+	}
+	port->ip_tx_prod = prod_ptr;
+
+	return total;
+}
+
+/**
+ * disable_intrs - disable interrupts
+ * @port: port to enable
+ * @mask: mask to use
+ */
+static inline void disable_intrs(struct ioc3_port *port, uint32_t mask)
+{
+	if (port->ip_card->ic_enable & mask) {
+		ioc3_disable(port->ip_is, port->ip_idd, mask);
+		port->ip_card->ic_enable &= ~mask;
+	}
+}
+
+/**
+ * set_notification - Modify event notification
+ * @port: port to use
+ * @mask: events mask
+ * @set_on: set ?
+ */
+static int set_notification(struct ioc3_port *port, int mask, int set_on)
+{
+	struct port_hooks *hooks = port->ip_hooks;
+	uint32_t intrbits, sscrbits;
+
+	BUG_ON(!mask);
+
+	intrbits = sscrbits = 0;
+
+	if (mask & N_DATA_READY)
+		intrbits |= (hooks->intr_rx_timer | hooks->intr_rx_high);
+	if (mask & N_OUTPUT_LOWAT)
+		intrbits |= hooks->intr_tx_explicit;
+	if (mask & N_DDCD) {
+		intrbits |= hooks->intr_delta_dcd;
+		sscrbits |= SSCR_RX_RING_DCD;
+	}
+	if (mask & N_DCTS)
+		intrbits |= hooks->intr_delta_cts;
+
+	if (set_on) {
+		enable_intrs(port, intrbits);
+		port->ip_notify |= mask;
+		port->ip_sscr |= sscrbits;
+	} else {
+		disable_intrs(port, intrbits);
+		port->ip_notify &= ~mask;
+		port->ip_sscr &= ~sscrbits;
+	}
+
+	/* We require DMA if either DATA_READY or DDCD notification is
+	 * currently requested. If neither of these is requested and
+	 * there is currently no tx in progress, DMA may be disabled.
+	 */
+	if (port->ip_notify & (N_DATA_READY | N_DDCD))
+		port->ip_sscr |= SSCR_DMA_EN;
+	else if (!(port->ip_card->ic_enable & hooks->intr_tx_mt))
+		port->ip_sscr &= ~SSCR_DMA_EN;
+
+	writel(port->ip_sscr, &port->ip_serial_regs->sscr);
+	return 0;
+}
+
+/**
+ * set_mcr - set the master control reg
+ * @the_port: port to use
+ * @mask1: mcr mask
+ * @mask2: shadow mask
+ */
+static inline int set_mcr(struct uart_port *the_port,
+			  int mask1, int mask2)
+{
+	struct ioc3_port *port = get_ioc3_port(the_port);
+	uint32_t shadow;
+	int spiniter = 0;
+	char mcr;
+
+	if (!port)
+		return -1;
+
+	/* Pause the DMA interface if necessary */
+	if (port->ip_sscr & SSCR_DMA_EN) {
+		writel(port->ip_sscr | SSCR_DMA_PAUSE,
+		       &port->ip_serial_regs->sscr);
+		while ((readl(&port->ip_serial_regs->sscr)
+			& SSCR_PAUSE_STATE) == 0) {
+			spiniter++;
+			if (spiniter > MAXITER)
+				return -1;
+		}
+	}
+	shadow = readl(&port->ip_serial_regs->shadow);
+	mcr = (shadow & 0xff000000) >> 24;
+
+	/* Set new value */
+	mcr |= mask1;
+	shadow |= mask2;
+	writeb(mcr, &port->ip_uart_regs->iu_mcr);
+	writel(shadow, &port->ip_serial_regs->shadow);
+
+	/* Re-enable the DMA interface if necessary */
+	if (port->ip_sscr & SSCR_DMA_EN) {
+		writel(port->ip_sscr, &port->ip_serial_regs->sscr);
+	}
+	return 0;
+}
+
+/**
+ * ioc3_set_proto - set the protocol for the port
+ * @port: port to use
+ * @proto: protocol to use
+ */
+static int ioc3_set_proto(struct ioc3_port *port, int proto)
+{
+	struct port_hooks *hooks = port->ip_hooks;
+
+	switch (proto) {
+	default:
+	case PROTO_RS232:
+		/* Clear the appropriate GIO pin */
+		DPRINT_CONFIG(("%s: rs232\n", __func__));
+		writel(0, (&port->ip_idd->vma->gppr[0]
+					+ hooks->rs422_select_pin));
+		break;
+
+	case PROTO_RS422:
+		/* Set the appropriate GIO pin */
+		DPRINT_CONFIG(("%s: rs422\n", __func__));
+		writel(1, (&port->ip_idd->vma->gppr[0]
+					+ hooks->rs422_select_pin));
+		break;
+	}
+	return 0;
+}
+
+/**
+ * transmit_chars - upper level write, called with the_port->lock
+ * @the_port: port to write
+ */
+static void transmit_chars(struct uart_port *the_port)
+{
+	int xmit_count, tail, head;
+	int result;
+	char *start;
+	struct tty_struct *tty;
+	struct ioc3_port *port = get_ioc3_port(the_port);
+	struct uart_state *state;
+
+	if (!the_port)
+		return;
+	if (!port)
+		return;
+
+	state = the_port->state;
+	tty = state->port.tty;
+
+	if (uart_circ_empty(&state->xmit) || uart_tx_stopped(the_port)) {
+		/* Nothing to do or hw stopped */
+		set_notification(port, N_ALL_OUTPUT, 0);
+		return;
+	}
+
+	head = state->xmit.head;
+	tail = state->xmit.tail;
+	start = (char *)&state->xmit.buf[tail];
+
+	/* write out all the data or until the end of the buffer */
+	xmit_count = (head < tail) ? (UART_XMIT_SIZE - tail) : (head - tail);
+	if (xmit_count > 0) {
+		result = do_write(port, start, xmit_count);
+		if (result > 0) {
+			/* booking */
+			xmit_count -= result;
+			the_port->icount.tx += result;
+			/* advance the pointers */
+			tail += result;
+			tail &= UART_XMIT_SIZE - 1;
+			state->xmit.tail = tail;
+			start = (char *)&state->xmit.buf[tail];
+		}
+	}
+	if (uart_circ_chars_pending(&state->xmit) < WAKEUP_CHARS)
+		uart_write_wakeup(the_port);
+
+	if (uart_circ_empty(&state->xmit)) {
+		set_notification(port, N_OUTPUT_LOWAT, 0);
+	} else {
+		set_notification(port, N_OUTPUT_LOWAT, 1);
+	}
+}
+
+/**
+ * ioc3_change_speed - change the speed of the port
+ * @the_port: port to change
+ * @new_termios: new termios settings
+ * @old_termios: old termios settings
+ */
+static void
+ioc3_change_speed(struct uart_port *the_port,
+		  struct ktermios *new_termios, struct ktermios *old_termios)
+{
+	struct ioc3_port *port = get_ioc3_port(the_port);
+	unsigned int cflag, iflag;
+	int baud;
+	int new_parity = 0, new_parity_enable = 0, new_stop = 0, new_data = 8;
+	struct uart_state *state = the_port->state;
+
+	cflag = new_termios->c_cflag;
+	iflag = new_termios->c_iflag;
+
+	switch (cflag & CSIZE) {
+	case CS5:
+		new_data = 5;
+		break;
+	case CS6:
+		new_data = 6;
+		break;
+	case CS7:
+		new_data = 7;
+		break;
+	case CS8:
+		new_data = 8;
+		break;
+	default:
+		/* cuz we always need a default ... */
+		new_data = 5;
+		break;
+	}
+	if (cflag & CSTOPB) {
+		new_stop = 1;
+	}
+	if (cflag & PARENB) {
+		new_parity_enable = 1;
+		if (cflag & PARODD)
+			new_parity = 1;
+	}
+	baud = uart_get_baud_rate(the_port, new_termios, old_termios,
+				  MIN_BAUD_SUPPORTED, MAX_BAUD_SUPPORTED);
+	DPRINT_CONFIG(("%s: returned baud %d for line %d\n", __func__, baud,
+				the_port->line));
+
+	if (!the_port->fifosize)
+		the_port->fifosize = FIFO_SIZE;
+	uart_update_timeout(the_port, cflag, baud);
+
+	the_port->ignore_status_mask = N_ALL_INPUT;
+
+	state->port.tty->low_latency = 1;
+
+	if (iflag & IGNPAR)
+		the_port->ignore_status_mask &= ~(N_PARITY_ERROR
+						  | N_FRAMING_ERROR);
+	if (iflag & IGNBRK) {
+		the_port->ignore_status_mask &= ~N_BREAK;
+		if (iflag & IGNPAR)
+			the_port->ignore_status_mask &= ~N_OVERRUN_ERROR;
+	}
+	if (!(cflag & CREAD)) {
+		/* ignore everything */
+		the_port->ignore_status_mask &= ~N_DATA_READY;
+	}
+
+	if (cflag & CRTSCTS) {
+		/* enable hardware flow control */
+		port->ip_sscr |= SSCR_HFC_EN;
+	}
+	else {
+		/* disable hardware flow control */
+		port->ip_sscr &= ~SSCR_HFC_EN;
+	}
+	writel(port->ip_sscr, &port->ip_serial_regs->sscr);
+
+	/* Set the configuration and proper notification call */
+	DPRINT_CONFIG(("%s : port 0x%p line %d cflag 0%o "
+		       "config_port(baud %d data %d stop %d penable %d "
+			" parity %d), notification 0x%x\n",
+		       __func__, (void *)port, the_port->line, cflag, baud,
+		       new_data, new_stop, new_parity_enable, new_parity,
+		       the_port->ignore_status_mask));
+
+	if ((config_port(port, baud,	/* baud */
+			 new_data,	/* byte size */
+			 new_stop,	/* stop bits */
+			 new_parity_enable,	/* set parity */
+			 new_parity)) >= 0) {	/* parity 1==odd */
+		set_notification(port, the_port->ignore_status_mask, 1);
+	}
+}
+
+/**
+ * ic3_startup_local - Start up the serial port - returns >= 0 if no errors
+ * @the_port: Port to operate on
+ */
+static inline int ic3_startup_local(struct uart_port *the_port)
+{
+	struct ioc3_port *port;
+
+	if (!the_port) {
+		NOT_PROGRESS();
+		return -1;
+	}
+
+	port = get_ioc3_port(the_port);
+	if (!port) {
+		NOT_PROGRESS();
+		return -1;
+	}
+
+	local_open(port);
+
+	/* set the protocol */
+	ioc3_set_proto(port, IS_RS232(the_port->line) ? PROTO_RS232 :
+							PROTO_RS422);
+	return 0;
+}
+
+/*
+ * ioc3_cb_output_lowat - called when the output low water mark is hit
+ * @port: port to output
+ */
+static void ioc3_cb_output_lowat(struct ioc3_port *port)
+{
+	unsigned long pflags;
+
+	/* the_port->lock is set on the call here */
+	if (port->ip_port) {
+		spin_lock_irqsave(&port->ip_port->lock, pflags);
+		transmit_chars(port->ip_port);
+		spin_unlock_irqrestore(&port->ip_port->lock, pflags);
+	}
+}
+
+/*
+ * ioc3_cb_post_ncs - called for some basic errors
+ * @port: port to use
+ * @ncs: event
+ */
+static void ioc3_cb_post_ncs(struct uart_port *the_port, int ncs)
+{
+	struct uart_icount *icount;
+
+	icount = &the_port->icount;
+
+	if (ncs & NCS_BREAK)
+		icount->brk++;
+	if (ncs & NCS_FRAMING)
+		icount->frame++;
+	if (ncs & NCS_OVERRUN)
+		icount->overrun++;
+	if (ncs & NCS_PARITY)
+		icount->parity++;
+}
+
+/**
+ * do_read - Read in bytes from the port.  Return the number of bytes
+ *			actually read.
+ * @the_port: port to use
+ * @buf: place to put the stuff we read
+ * @len: how big 'buf' is
+ */
+
+static inline int do_read(struct uart_port *the_port, char *buf, int len)
+{
+	int prod_ptr, cons_ptr, total;
+	struct ioc3_port *port = get_ioc3_port(the_port);
+	struct ring *inring;
+	struct ring_entry *entry;
+	struct port_hooks *hooks = port->ip_hooks;
+	int byte_num;
+	char *sc;
+	int loop_counter;
+
+	BUG_ON(!(len >= 0));
+	BUG_ON(!port);
+
+	/* There is a nasty timing issue in the IOC3. When the rx_timer
+	 * expires or the rx_high condition arises, we take an interrupt.
+	 * At some point while servicing the interrupt, we read bytes from
+	 * the ring buffer and re-arm the rx_timer.  However the rx_timer is
+	 * not started until the first byte is received *after* it is armed,
+	 * and any bytes pending in the rx construction buffers are not drained
+	 * to memory until either there are 4 bytes available or the rx_timer
+	 * expires.  This leads to a potential situation where data is left
+	 * in the construction buffers forever - 1 to 3 bytes were received
+	 * after the interrupt was generated but before the rx_timer was
+	 * re-armed. At that point as long as no subsequent bytes are received
+	 * the timer will never be started and the bytes will remain in the
+	 * construction buffer forever.  The solution is to execute a DRAIN
+	 * command after rearming the timer.  This way any bytes received before
+	 * the DRAIN will be drained to memory, and any bytes received after
+	 * the DRAIN will start the TIMER and be drained when it expires.
+	 * Luckily, this only needs to be done when the DMA buffer is empty
+	 * since there is no requirement that this function return all
+	 * available data as long as it returns some.
+	 */
+	/* Re-arm the timer */
+
+	writel(port->ip_rx_cons | SRCIR_ARM, &port->ip_serial_regs->srcir);
+
+	prod_ptr = readl(&port->ip_serial_regs->srpir) & PROD_CONS_MASK;
+	cons_ptr = port->ip_rx_cons;
+
+	if (prod_ptr == cons_ptr) {
+		int reset_dma = 0;
+
+		/* Input buffer appears empty, do a flush. */
+
+		/* DMA must be enabled for this to work. */
+		if (!(port->ip_sscr & SSCR_DMA_EN)) {
+			port->ip_sscr |= SSCR_DMA_EN;
+			reset_dma = 1;
+		}
+
+		/* Potential race condition: we must reload the srpir after
+		 * issuing the drain command, otherwise we could think the rx
+		 * buffer is empty, then take a very long interrupt, and when
+		 * we come back it's full and we wait forever for the drain to
+		 * complete.
+		 */
+		writel(port->ip_sscr | SSCR_RX_DRAIN,
+		       &port->ip_serial_regs->sscr);
+		prod_ptr = readl(&port->ip_serial_regs->srpir) & PROD_CONS_MASK;
+
+		/* We must not wait for the DRAIN to complete unless there are
+		 * at least 8 bytes (2 ring entries) available to receive the
+		 * data otherwise the DRAIN will never complete and we'll
+		 * deadlock here.
+		 * In fact, to make things easier, I'll just ignore the flush if
+		 * there is any data at all now available.
+		 */
+		if (prod_ptr == cons_ptr) {
+			loop_counter = 0;
+			while (readl(&port->ip_serial_regs->sscr) &
+			       SSCR_RX_DRAIN) {
+				loop_counter++;
+				if (loop_counter > MAXITER)
+					return -1;
+			}
+
+			/* SIGH. We have to reload the prod_ptr *again* since
+			 * the drain may have caused it to change
+			 */
+			prod_ptr = readl(&port->ip_serial_regs->srpir)
+			    & PROD_CONS_MASK;
+		}
+		if (reset_dma) {
+			port->ip_sscr &= ~SSCR_DMA_EN;
+			writel(port->ip_sscr, &port->ip_serial_regs->sscr);
+		}
+	}
+	inring = port->ip_inring;
+	port->ip_flags &= ~READ_ABORTED;
+
+	total = 0;
+	loop_counter = 0xfffff;	/* to avoid hangs */
+
+	/* Grab bytes from the hardware */
+	while ((prod_ptr != cons_ptr) && (len > 0)) {
+		entry = (struct ring_entry *)((caddr_t) inring + cons_ptr);
+
+		if (loop_counter-- <= 0) {
+			printk(KERN_WARNING "IOC3 serial: "
+			       "possible hang condition/"
+			       "port stuck on read (line %d).\n",
+				the_port->line);
+			break;
+		}
+
+		/* According to the producer pointer, this ring entry
+		 * must contain some data.  But if the PIO happened faster
+		 * than the DMA, the data may not be available yet, so let's
+		 * wait until it arrives.
+		 */
+		if ((entry->ring_allsc & RING_ANY_VALID) == 0) {
+			/* Indicate the read is aborted so we don't disable
+			 * the interrupt thinking that the consumer is
+			 * congested.
+			 */
+			port->ip_flags |= READ_ABORTED;
+			len = 0;
+			break;
+		}
+
+		/* Load the bytes/status out of the ring entry */
+		for (byte_num = 0; byte_num < 4 && len > 0; byte_num++) {
+			sc = &(entry->ring_sc[byte_num]);
+
+			/* Check for change in modem state or overrun */
+			if ((*sc & RXSB_MODEM_VALID)
+			    && (port->ip_notify & N_DDCD)) {
+				/* Notify upper layer if DCD dropped */
+				if ((port->ip_flags & DCD_ON)
+				    && !(*sc & RXSB_DCD)) {
+					/* If we have already copied some data,
+					 * return it.  We'll pick up the carrier
+					 * drop on the next pass.  That way we
+					 * don't throw away the data that has
+					 * already been copied back to
+					 * the caller's buffer.
+					 */
+					if (total > 0) {
+						len = 0;
+						break;
+					}
+					port->ip_flags &= ~DCD_ON;
+
+					/* Turn off this notification so the
+					 * carrier drop protocol won't see it
+					 * again when it does a read.
+					 */
+					*sc &= ~RXSB_MODEM_VALID;
+
+					/* To keep things consistent, we need
+					 * to update the consumer pointer so
+					 * the next reader won't come in and
+					 * try to read the same ring entries
+					 * again. This must be done here before
+					 * the dcd change.
+					 */
+
+					if ((entry->ring_allsc & RING_ANY_VALID)
+					    == 0) {
+						cons_ptr += (int)sizeof
+						    (struct ring_entry);
+						cons_ptr &= PROD_CONS_MASK;
+					}
+					writel(cons_ptr,
+					       &port->ip_serial_regs->srcir);
+					port->ip_rx_cons = cons_ptr;
+
+					/* Notify upper layer of carrier drop */
+					if ((port->ip_notify & N_DDCD)
+					    && port->ip_port) {
+						uart_handle_dcd_change
+							(port->ip_port, 0);
+						wake_up_interruptible
+						    (&the_port->state->
+						     port.delta_msr_wait);
+					}
+
+					/* If we had any data to return, we
+					 * would have returned it above.
+					 */
+					return 0;
+				}
+			}
+			if (*sc & RXSB_MODEM_VALID) {
+				/* Notify that an input overrun occurred */
+				if ((*sc & RXSB_OVERRUN)
+				    && (port->ip_notify & N_OVERRUN_ERROR)) {
+					ioc3_cb_post_ncs(the_port, NCS_OVERRUN);
+				}
+				/* Don't look at this byte again */
+				*sc &= ~RXSB_MODEM_VALID;
+			}
+
+			/* Check for valid data or RX errors */
+			if ((*sc & RXSB_DATA_VALID) &&
+			    ((*sc & (RXSB_PAR_ERR
+				     | RXSB_FRAME_ERR | RXSB_BREAK))
+			     && (port->ip_notify & (N_PARITY_ERROR
+						    | N_FRAMING_ERROR
+						    | N_BREAK)))) {
+				/* There is an error condition on the next byte.
+				 * If we have already transferred some bytes,
+				 * we'll stop here. Otherwise if this is the
+				 * first byte to be read, we'll just transfer
+				 * it alone after notifying the
+				 * upper layer of its status.
+				 */
+				if (total > 0) {
+					len = 0;
+					break;
+				} else {
+					if ((*sc & RXSB_PAR_ERR) &&
+					    (port->
+					     ip_notify & N_PARITY_ERROR)) {
+						ioc3_cb_post_ncs(the_port,
+								 NCS_PARITY);
+					}
+					if ((*sc & RXSB_FRAME_ERR) &&
+					    (port->
+					     ip_notify & N_FRAMING_ERROR)) {
+						ioc3_cb_post_ncs(the_port,
+								 NCS_FRAMING);
+					}
+					if ((*sc & RXSB_BREAK)
+					    && (port->ip_notify & N_BREAK)) {
+						ioc3_cb_post_ncs
+						    (the_port, NCS_BREAK);
+					}
+					len = 1;
+				}
+			}
+			if (*sc & RXSB_DATA_VALID) {
+				*sc &= ~RXSB_DATA_VALID;
+				*buf = entry->ring_data[byte_num];
+				buf++;
+				len--;
+				total++;
+			}
+		}
+
+		/* If we used up this entry entirely, go on to the next one,
+		 * otherwise we must have run out of buffer space, so
+		 * leave the consumer pointer here for the next read in case
+		 * there are still unread bytes in this entry.
+		 */
+		if ((entry->ring_allsc & RING_ANY_VALID) == 0) {
+			cons_ptr += (int)sizeof(struct ring_entry);
+			cons_ptr &= PROD_CONS_MASK;
+		}
+	}
+
+	/* Update consumer pointer and re-arm rx timer interrupt */
+	writel(cons_ptr, &port->ip_serial_regs->srcir);
+	port->ip_rx_cons = cons_ptr;
+
+	/* If we have now dipped below the rx high water mark and we have
+	 * rx_high interrupt turned off, we can now turn it back on again.
+	 */
+	if ((port->ip_flags & INPUT_HIGH) && (((prod_ptr - cons_ptr)
+					       & PROD_CONS_MASK) <
+					      ((port->
+						ip_sscr &
+						SSCR_RX_THRESHOLD)
+					       << PROD_CONS_PTR_OFF))) {
+		port->ip_flags &= ~INPUT_HIGH;
+		enable_intrs(port, hooks->intr_rx_high);
+	}
+	return total;
+}
+
+/**
+ * receive_chars - upper level read.
+ * @the_port: port to read from
+ */
+static int receive_chars(struct uart_port *the_port)
+{
+	struct tty_struct *tty;
+	unsigned char ch[MAX_CHARS];
+	int read_count = 0, read_room, flip = 0;
+	struct uart_state *state = the_port->state;
+	struct ioc3_port *port = get_ioc3_port(the_port);
+	unsigned long pflags;
+
+	/* Make sure all the pointers are "good" ones */
+	if (!state)
+		return 0;
+	if (!state->port.tty)
+		return 0;
+
+	if (!(port->ip_flags & INPUT_ENABLE))
+		return 0;
+
+	spin_lock_irqsave(&the_port->lock, pflags);
+	tty = state->port.tty;
+
+	read_count = do_read(the_port, ch, MAX_CHARS);
+	if (read_count > 0) {
+		flip = 1;
+		read_room = tty_insert_flip_string(tty, ch, read_count);
+		the_port->icount.rx += read_count;
+	}
+	spin_unlock_irqrestore(&the_port->lock, pflags);
+
+	if (flip)
+		tty_flip_buffer_push(tty);
+
+	return read_count;
+}
+
+/**
+ * ioc3uart_intr_one - lowest level (per port) interrupt handler.
+ * @is : submodule
+ * @idd: driver data
+ * @pending: interrupts to handle
+ */
+
+static int inline
+ioc3uart_intr_one(struct ioc3_submodule *is,
+			struct ioc3_driver_data *idd,
+			unsigned int pending)
+{
+	int port_num = GET_PORT_FROM_SIO_IR(pending);
+	struct port_hooks *hooks;
+	unsigned int rx_high_rd_aborted = 0;
+	unsigned long flags;
+	struct uart_port *the_port;
+	struct ioc3_port *port;
+	int loop_counter;
+	struct ioc3_card *card_ptr;
+	unsigned int sio_ir;
+
+	card_ptr = idd->data[is->id];
+	port = card_ptr->ic_port[port_num].icp_port;
+	hooks = port->ip_hooks;
+
+	/* Possible race condition here: The tx_mt interrupt bit may be
+	 * cleared without the intervention of the interrupt handler,
+	 * e.g. by a write.  If the top level interrupt handler reads a
+	 * tx_mt, then some other processor does a write, starting up
+	 * output, then we come in here, see the tx_mt and stop DMA, the
+	 * output started by the other processor will hang.  Thus we can
+	 * only rely on tx_mt being legitimate if it is read while the
+	 * port lock is held.  Therefore this bit must be ignored in the
+	 * passed in interrupt mask which was read by the top level
+	 * interrupt handler since the port lock was not held at the time
+	 * it was read.  We can only rely on this bit being accurate if it
+	 * is read while the port lock is held.  So we'll clear it for now,
+	 * and reload it later once we have the port lock.
+	 */
+
+	sio_ir = pending & ~(hooks->intr_tx_mt);
+	spin_lock_irqsave(&port->ip_lock, flags);
+
+	loop_counter = MAXITER;	/* to avoid hangs */
+
+	do {
+		uint32_t shadow;
+
+		if (loop_counter-- <= 0) {
+			printk(KERN_WARNING "IOC3 serial: "
+			       "possible hang condition/"
+			       "port stuck on interrupt (line %d).\n",
+				((struct uart_port *)port->ip_port)->line);
+			break;
+		}
+		/* Handle a DCD change */
+		if (sio_ir & hooks->intr_delta_dcd) {
+			ioc3_ack(is, idd, hooks->intr_delta_dcd);
+			shadow = readl(&port->ip_serial_regs->shadow);
+
+			if ((port->ip_notify & N_DDCD)
+			    && (shadow & SHADOW_DCD)
+			    && (port->ip_port)) {
+				the_port = port->ip_port;
+				uart_handle_dcd_change(the_port,
+						shadow & SHADOW_DCD);
+				wake_up_interruptible
+				    (&the_port->state->port.delta_msr_wait);
+			} else if ((port->ip_notify & N_DDCD)
+				   && !(shadow & SHADOW_DCD)) {
+				/* Flag delta DCD/no DCD */
+				uart_handle_dcd_change(port->ip_port,
+						shadow & SHADOW_DCD);
+				port->ip_flags |= DCD_ON;
+			}
+		}
+
+		/* Handle a CTS change */
+		if (sio_ir & hooks->intr_delta_cts) {
+			ioc3_ack(is, idd, hooks->intr_delta_cts);
+			shadow = readl(&port->ip_serial_regs->shadow);
+
+			if ((port->ip_notify & N_DCTS) && (port->ip_port)) {
+				the_port = port->ip_port;
+				uart_handle_cts_change(the_port, shadow
+						& SHADOW_CTS);
+				wake_up_interruptible
+				    (&the_port->state->port.delta_msr_wait);
+			}
+		}
+
+		/* rx timeout interrupt.  Must be some data available.  Put this
+		 * before the check for rx_high since servicing this condition
+		 * may cause that condition to clear.
+		 */
+		if (sio_ir & hooks->intr_rx_timer) {
+			ioc3_ack(is, idd, hooks->intr_rx_timer);
+			if ((port->ip_notify & N_DATA_READY)
+						&& (port->ip_port)) {
+				receive_chars(port->ip_port);
+			}
+		}
+
+		/* rx high interrupt. Must be after rx_timer.  */
+		else if (sio_ir & hooks->intr_rx_high) {
+			/* Data available, notify upper layer */
+			if ((port->ip_notify & N_DATA_READY) && port->ip_port) {
+				receive_chars(port->ip_port);
+			}
+
+			/* We can't ACK this interrupt.  If receive_chars didn't
+			 * cause the condition to clear, we'll have to disable
+			 * the interrupt until the data is drained.
+			 * If the read was aborted, don't disable the interrupt
+			 * as this may cause us to hang indefinitely.  An
+			 * aborted read generally means that this interrupt
+			 * hasn't been delivered to the cpu yet anyway, even
+			 * though we see it as asserted when we read the sio_ir.
+			 */
+			if ((sio_ir = PENDING(card_ptr, idd))
+					& hooks->intr_rx_high) {
+				if (port->ip_flags & READ_ABORTED) {
+					rx_high_rd_aborted++;
+				}
+				else {
+					card_ptr->ic_enable &= ~hooks->intr_rx_high;
+					port->ip_flags |= INPUT_HIGH;
+				}
+			}
+		}
+
+		/* We got a low water interrupt: notify upper layer to
+		 * send more data.  Must come before tx_mt since servicing
+		 * this condition may cause that condition to clear.
+		 */
+		if (sio_ir & hooks->intr_tx_explicit) {
+			port->ip_flags &= ~LOWAT_WRITTEN;
+			ioc3_ack(is, idd, hooks->intr_tx_explicit);
+			if (port->ip_notify & N_OUTPUT_LOWAT)
+				ioc3_cb_output_lowat(port);
+		}
+
+		/* Handle tx_mt.  Must come after tx_explicit.  */
+		else if (sio_ir & hooks->intr_tx_mt) {
+			/* If we are expecting a lowat notification
+			 * and we get to this point it probably means that for
+			 * some reason the tx_explicit didn't work as expected
+			 * (that can legitimately happen if the output buffer is
+			 * filled up in just the right way).
+			 * So send the notification now.
+			 */
+			if (port->ip_notify & N_OUTPUT_LOWAT) {
+				ioc3_cb_output_lowat(port);
+
+				/* We need to reload the sio_ir since the lowat
+				 * call may have caused another write to occur,
+				 * clearing the tx_mt condition.
+				 */
+				sio_ir = PENDING(card_ptr, idd);
+			}
+
+			/* If the tx_mt condition still persists even after the
+			 * lowat call, we've got some work to do.
+			 */
+			if (sio_ir & hooks->intr_tx_mt) {
+				/* If we are not currently expecting DMA input,
+				 * and the transmitter has just gone idle,
+				 * there is no longer any reason for DMA, so
+				 * disable it.
+				 */
+				if (!(port->ip_notify
+				      & (N_DATA_READY | N_DDCD))) {
+					BUG_ON(!(port->ip_sscr
+						 & SSCR_DMA_EN));
+					port->ip_sscr &= ~SSCR_DMA_EN;
+					writel(port->ip_sscr,
+					       &port->ip_serial_regs->sscr);
+				}
+				/* Prevent infinite tx_mt interrupt */
+				card_ptr->ic_enable &= ~hooks->intr_tx_mt;
+			}
+		}
+		sio_ir = PENDING(card_ptr, idd);
+
+		/* if the read was aborted and only hooks->intr_rx_high,
+		 * clear hooks->intr_rx_high, so we do not loop forever.
+		 */
+
+		if (rx_high_rd_aborted && (sio_ir == hooks->intr_rx_high)) {
+			sio_ir &= ~hooks->intr_rx_high;
+		}
+	} while (sio_ir & hooks->intr_all);
+
+	spin_unlock_irqrestore(&port->ip_lock, flags);
+	ioc3_enable(is, idd, card_ptr->ic_enable);
+	return 0;
+}
+
+/**
+ * ioc3uart_intr - field all serial interrupts
+ * @is : submodule
+ * @idd: driver data
+ * @pending: interrupts to handle
+ *
+ */
+
+static int ioc3uart_intr(struct ioc3_submodule *is,
+			struct ioc3_driver_data *idd,
+			unsigned int pending)
+{
+	int ret = 0;
+
+	/*
+	 * The upper level interrupt handler sends interrupts for both ports
+	 * here. So we need to call for each port with its interrupts.
+	 */
+
+	if (pending & SIO_IR_SA)
+		ret |= ioc3uart_intr_one(is, idd, pending & SIO_IR_SA);
+	if (pending & SIO_IR_SB)
+		ret |= ioc3uart_intr_one(is, idd, pending & SIO_IR_SB);
+
+	return ret;
+}
+
+/**
+ * ic3_type
+ * @port: Port to operate with (we ignore since we only have one port)
+ *
+ */
+static const char *ic3_type(struct uart_port *the_port)
+{
+	if (IS_RS232(the_port->line))
+		return "SGI IOC3 Serial [rs232]";
+	else
+		return "SGI IOC3 Serial [rs422]";
+}
+
+/**
+ * ic3_tx_empty - Is the transmitter empty?
+ * @port: Port to operate on
+ *
+ */
+static unsigned int ic3_tx_empty(struct uart_port *the_port)
+{
+	unsigned int ret = 0;
+	struct ioc3_port *port = get_ioc3_port(the_port);
+
+	if (readl(&port->ip_serial_regs->shadow) & SHADOW_TEMT)
+		ret = TIOCSER_TEMT;
+	return ret;
+}
+
+/**
+ * ic3_stop_tx - stop the transmitter
+ * @port: Port to operate on
+ *
+ */
+static void ic3_stop_tx(struct uart_port *the_port)
+{
+	struct ioc3_port *port = get_ioc3_port(the_port);
+
+	if (port)
+		set_notification(port, N_OUTPUT_LOWAT, 0);
+}
+
+/**
+ * ic3_stop_rx - stop the receiver
+ * @port: Port to operate on
+ *
+ */
+static void ic3_stop_rx(struct uart_port *the_port)
+{
+	struct ioc3_port *port = get_ioc3_port(the_port);
+
+	if (port)
+		port->ip_flags &= ~INPUT_ENABLE;
+}
+
+/**
+ * null_void_function
+ * @port: Port to operate on
+ *
+ */
+static void null_void_function(struct uart_port *the_port)
+{
+}
+
+/**
+ * ic3_shutdown - shut down the port - free irq and disable
+ * @port: port to shut down
+ *
+ */
+static void ic3_shutdown(struct uart_port *the_port)
+{
+	unsigned long port_flags;
+	struct ioc3_port *port;
+	struct uart_state *state;
+
+	port = get_ioc3_port(the_port);
+	if (!port)
+		return;
+
+	state = the_port->state;
+	wake_up_interruptible(&state->port.delta_msr_wait);
+
+	spin_lock_irqsave(&the_port->lock, port_flags);
+	set_notification(port, N_ALL, 0);
+	spin_unlock_irqrestore(&the_port->lock, port_flags);
+}
+
+/**
+ * ic3_set_mctrl - set control lines (dtr, rts, etc)
+ * @port: Port to operate on
+ * @mctrl: Lines to set/unset
+ *
+ */
+static void ic3_set_mctrl(struct uart_port *the_port, unsigned int mctrl)
+{
+	unsigned char mcr = 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;
+
+	set_mcr(the_port, mcr, SHADOW_DTR);
+}
+
+/**
+ * ic3_get_mctrl - get control line info
+ * @port: port to operate on
+ *
+ */
+static unsigned int ic3_get_mctrl(struct uart_port *the_port)
+{
+	struct ioc3_port *port = get_ioc3_port(the_port);
+	uint32_t shadow;
+	unsigned int ret = 0;
+
+	if (!port)
+		return 0;
+
+	shadow = readl(&port->ip_serial_regs->shadow);
+	if (shadow & SHADOW_DCD)
+		ret |= TIOCM_CD;
+	if (shadow & SHADOW_DR)
+		ret |= TIOCM_DSR;
+	if (shadow & SHADOW_CTS)
+		ret |= TIOCM_CTS;
+	return ret;
+}
+
+/**
+ * ic3_start_tx - Start transmitter. Called with the_port->lock
+ * @port: Port to operate on
+ *
+ */
+static void ic3_start_tx(struct uart_port *the_port)
+{
+	struct ioc3_port *port = get_ioc3_port(the_port);
+
+	if (port) {
+		set_notification(port, N_OUTPUT_LOWAT, 1);
+		enable_intrs(port, port->ip_hooks->intr_tx_mt);
+	}
+}
+
+/**
+ * ic3_break_ctl - handle breaks
+ * @port: Port to operate on
+ * @break_state: Break state
+ *
+ */
+static void ic3_break_ctl(struct uart_port *the_port, int break_state)
+{
+}
+
+/**
+ * ic3_startup - Start up the serial port - always return 0 (We're always on)
+ * @port: Port to operate on
+ *
+ */
+static int ic3_startup(struct uart_port *the_port)
+{
+	int retval;
+	struct ioc3_port *port;
+	struct ioc3_card *card_ptr;
+	unsigned long port_flags;
+
+	if (!the_port) {
+		NOT_PROGRESS();
+		return -ENODEV;
+	}
+	port = get_ioc3_port(the_port);
+	if (!port) {
+		NOT_PROGRESS();
+		return -ENODEV;
+	}
+	card_ptr = port->ip_card;
+	port->ip_port = the_port;
+
+	if (!card_ptr) {
+		NOT_PROGRESS();
+		return -ENODEV;
+	}
+
+	/* Start up the serial port */
+	spin_lock_irqsave(&the_port->lock, port_flags);
+	retval = ic3_startup_local(the_port);
+	spin_unlock_irqrestore(&the_port->lock, port_flags);
+	return retval;
+}
+
+/**
+ * ic3_set_termios - set termios stuff
+ * @port: port to operate on
+ * @termios: New settings
+ * @termios: Old
+ *
+ */
+static void
+ic3_set_termios(struct uart_port *the_port,
+		struct ktermios *termios, struct ktermios *old_termios)
+{
+	unsigned long port_flags;
+
+	spin_lock_irqsave(&the_port->lock, port_flags);
+	ioc3_change_speed(the_port, termios, old_termios);
+	spin_unlock_irqrestore(&the_port->lock, port_flags);
+}
+
+/**
+ * ic3_request_port - allocate resources for port - no op....
+ * @port: port to operate on
+ *
+ */
+static int ic3_request_port(struct uart_port *port)
+{
+	return 0;
+}
+
+/* Associate the uart functions above - given to serial core */
+static struct uart_ops ioc3_ops = {
+	.tx_empty = ic3_tx_empty,
+	.set_mctrl = ic3_set_mctrl,
+	.get_mctrl = ic3_get_mctrl,
+	.stop_tx = ic3_stop_tx,
+	.start_tx = ic3_start_tx,
+	.stop_rx = ic3_stop_rx,
+	.enable_ms = null_void_function,
+	.break_ctl = ic3_break_ctl,
+	.startup = ic3_startup,
+	.shutdown = ic3_shutdown,
+	.set_termios = ic3_set_termios,
+	.type = ic3_type,
+	.release_port = null_void_function,
+	.request_port = ic3_request_port,
+};
+
+/*
+ * Boot-time initialization code
+ */
+
+static struct uart_driver ioc3_uart = {
+	.owner = THIS_MODULE,
+	.driver_name = "ioc3_serial",
+	.dev_name = DEVICE_NAME,
+	.major = DEVICE_MAJOR,
+	.minor = DEVICE_MINOR,
+	.nr = MAX_LOGICAL_PORTS
+};
+
+/**
+ * ioc3_serial_core_attach - register with serial core
+ *		This is done during pci probing
+ * @is: submodule struct for this
+ * @idd: handle for this card
+ */
+static inline int ioc3_serial_core_attach( struct ioc3_submodule *is,
+				struct ioc3_driver_data *idd)
+{
+	struct ioc3_port *port;
+	struct uart_port *the_port;
+	struct ioc3_card *card_ptr = idd->data[is->id];
+	int ii, phys_port;
+	struct pci_dev *pdev = idd->pdev;
+
+	DPRINT_CONFIG(("%s: attach pdev 0x%p - card_ptr 0x%p\n",
+		       __func__, pdev, (void *)card_ptr));
+
+	if (!card_ptr)
+		return -ENODEV;
+
+	/* once around for each logical port on this card */
+	for (ii = 0; ii < LOGICAL_PORTS_PER_CARD; ii++) {
+		phys_port = GET_PHYSICAL_PORT(ii);
+		the_port = &card_ptr->ic_port[phys_port].
+				icp_uart_port[GET_LOGICAL_PORT(ii)];
+		port = card_ptr->ic_port[phys_port].icp_port;
+		port->ip_port = the_port;
+
+		DPRINT_CONFIG(("%s: attach the_port 0x%p / port 0x%p [%d/%d]\n",
+			__func__, (void *)the_port, (void *)port,
+				phys_port, ii));
+
+		/* membase, iobase and mapbase just need to be non-0 */
+		the_port->membase = (unsigned char __iomem *)1;
+		the_port->iobase = (pdev->bus->number << 16) |  ii;
+		the_port->line = (Num_of_ioc3_cards << 2) | ii;
+		the_port->mapbase = 1;
+		the_port->type = PORT_16550A;
+		the_port->fifosize = FIFO_SIZE;
+		the_port->ops = &ioc3_ops;
+		the_port->irq = idd->irq_io;
+		the_port->dev = &pdev->dev;
+
+		if (uart_add_one_port(&ioc3_uart, the_port) < 0) {
+			printk(KERN_WARNING
+		          "%s: unable to add port %d bus %d\n",
+			       __func__, the_port->line, pdev->bus->number);
+		} else {
+			DPRINT_CONFIG(("IOC3 serial port %d irq %d bus %d\n",
+		          the_port->line, the_port->irq, pdev->bus->number));
+		}
+
+		/* all ports are rs232 for now */
+		if (IS_PHYSICAL_PORT(ii))
+			ioc3_set_proto(port, PROTO_RS232);
+	}
+	return 0;
+}
+
+/**
+ * ioc3uart_remove - register detach function
+ * @is: submodule struct for this submodule
+ * @idd: ioc3 driver data for this submodule
+ */
+
+static int ioc3uart_remove(struct ioc3_submodule *is,
+			struct ioc3_driver_data *idd)
+{
+	struct ioc3_card *card_ptr = idd->data[is->id];
+	struct uart_port *the_port;
+	struct ioc3_port *port;
+	int ii;
+
+	if (card_ptr) {
+		for (ii = 0; ii < LOGICAL_PORTS_PER_CARD; ii++) {
+			the_port = &card_ptr->ic_port[GET_PHYSICAL_PORT(ii)].
+					icp_uart_port[GET_LOGICAL_PORT(ii)];
+			if (the_port)
+				uart_remove_one_port(&ioc3_uart, the_port);
+			port = card_ptr->ic_port[GET_PHYSICAL_PORT(ii)].icp_port;
+			if (port && IS_PHYSICAL_PORT(ii)
+					&& (GET_PHYSICAL_PORT(ii) == 0)) {
+				pci_free_consistent(port->ip_idd->pdev,
+					TOTAL_RING_BUF_SIZE,
+					(void *)port->ip_cpu_ringbuf,
+					port->ip_dma_ringbuf);
+				kfree(port);
+				card_ptr->ic_port[GET_PHYSICAL_PORT(ii)].
+							icp_port = NULL;
+			}
+		}
+		kfree(card_ptr);
+		idd->data[is->id] = NULL;
+	}
+	return 0;
+}
+
+/**
+ * ioc3uart_probe - card probe function called from shim driver
+ * @is: submodule struct for this submodule
+ * @idd: ioc3 driver data for this card
+ */
+
+static int __devinit
+ioc3uart_probe(struct ioc3_submodule *is, struct ioc3_driver_data *idd)
+{
+	struct pci_dev *pdev = idd->pdev;
+	struct ioc3_card *card_ptr;
+	int ret = 0;
+	struct ioc3_port *port;
+	struct ioc3_port *ports[PORTS_PER_CARD];
+	int phys_port;
+	int cnt;
+
+	DPRINT_CONFIG(("%s (0x%p, 0x%p)\n", __func__, is, idd));
+
+	card_ptr = kzalloc(sizeof(struct ioc3_card), GFP_KERNEL);
+	if (!card_ptr) {
+		printk(KERN_WARNING "ioc3_attach_one"
+		       ": unable to get memory for the IOC3\n");
+		return -ENOMEM;
+	}
+	idd->data[is->id] = card_ptr;
+	Submodule_slot = is->id;
+
+	writel(((UARTA_BASE >> 3) << SIO_CR_SER_A_BASE_SHIFT) |
+		((UARTB_BASE >> 3) << SIO_CR_SER_B_BASE_SHIFT) |
+		(0xf << SIO_CR_CMD_PULSE_SHIFT), &idd->vma->sio_cr);
+
+	pci_write_config_dword(pdev, PCI_LAT, 0xff00);
+
+	/* Enable serial port mode select generic PIO pins as outputs */
+	ioc3_gpcr_set(idd, GPCR_UARTA_MODESEL | GPCR_UARTB_MODESEL);
+
+	/* Create port structures for each port */
+	for (phys_port = 0; phys_port < PORTS_PER_CARD; phys_port++) {
+		port = kzalloc(sizeof(struct ioc3_port), GFP_KERNEL);
+		if (!port) {
+			printk(KERN_WARNING
+			       "IOC3 serial memory not available for port\n");
+			ret = -ENOMEM;
+			goto out4;
+		}
+		spin_lock_init(&port->ip_lock);
+
+		/* we need to remember the previous ones, to point back to
+		 * them farther down - setting up the ring buffers.
+		 */
+		ports[phys_port] = port;
+
+		/* init to something useful */
+		card_ptr->ic_port[phys_port].icp_port = port;
+		port->ip_is = is;
+		port->ip_idd = idd;
+		port->ip_baud = 9600;
+		port->ip_card = card_ptr;
+		port->ip_hooks = &hooks_array[phys_port];
+
+		/* Setup each port */
+		if (phys_port == 0) {
+			port->ip_serial_regs = &idd->vma->port_a;
+			port->ip_uart_regs = &idd->vma->sregs.uarta;
+
+			DPRINT_CONFIG(("%s : Port A ip_serial_regs 0x%p "
+				       "ip_uart_regs 0x%p\n",
+				       __func__,
+				       (void *)port->ip_serial_regs,
+				       (void *)port->ip_uart_regs));
+
+			/* setup ring buffers */
+			port->ip_cpu_ringbuf = pci_alloc_consistent(pdev,
+				TOTAL_RING_BUF_SIZE, &port->ip_dma_ringbuf);
+
+			BUG_ON(!((((int64_t) port->ip_dma_ringbuf) &
+				  (TOTAL_RING_BUF_SIZE - 1)) == 0));
+			port->ip_inring = RING(port, RX_A);
+			port->ip_outring = RING(port, TX_A);
+			DPRINT_CONFIG(("%s : Port A ip_cpu_ringbuf 0x%p "
+				       "ip_dma_ringbuf 0x%p, ip_inring 0x%p "
+					"ip_outring 0x%p\n",
+				       __func__,
+				       (void *)port->ip_cpu_ringbuf,
+				       (void *)port->ip_dma_ringbuf,
+				       (void *)port->ip_inring,
+				       (void *)port->ip_outring));
+		}
+		else {
+			port->ip_serial_regs = &idd->vma->port_b;
+			port->ip_uart_regs = &idd->vma->sregs.uartb;
+
+			DPRINT_CONFIG(("%s : Port B ip_serial_regs 0x%p "
+				       "ip_uart_regs 0x%p\n",
+				       __func__,
+				       (void *)port->ip_serial_regs,
+				       (void *)port->ip_uart_regs));
+
+			/* share the ring buffers */
+			port->ip_dma_ringbuf =
+			    ports[phys_port - 1]->ip_dma_ringbuf;
+			port->ip_cpu_ringbuf =
+			    ports[phys_port - 1]->ip_cpu_ringbuf;
+			port->ip_inring = RING(port, RX_B);
+			port->ip_outring = RING(port, TX_B);
+			DPRINT_CONFIG(("%s : Port B ip_cpu_ringbuf 0x%p "
+				       "ip_dma_ringbuf 0x%p, ip_inring 0x%p "
+					"ip_outring 0x%p\n",
+				       __func__,
+				       (void *)port->ip_cpu_ringbuf,
+				       (void *)port->ip_dma_ringbuf,
+				       (void *)port->ip_inring,
+				       (void *)port->ip_outring));
+		}
+
+		DPRINT_CONFIG(("%s : port %d [addr 0x%p] card_ptr 0x%p",
+			       __func__,
+			       phys_port, (void *)port, (void *)card_ptr));
+		DPRINT_CONFIG((" ip_serial_regs 0x%p ip_uart_regs 0x%p\n",
+			       (void *)port->ip_serial_regs,
+			       (void *)port->ip_uart_regs));
+
+		/* Initialize the hardware for IOC3 */
+		port_init(port);
+
+		DPRINT_CONFIG(("%s: phys_port %d port 0x%p inring 0x%p "
+			       "outring 0x%p\n",
+			       __func__,
+			       phys_port, (void *)port,
+			       (void *)port->ip_inring,
+			       (void *)port->ip_outring));
+
+	}
+
+	/* register port with the serial core */
+
+	if ((ret = ioc3_serial_core_attach(is, idd)))
+		goto out4;
+
+	Num_of_ioc3_cards++;
+
+	return ret;
+
+	/* error exits that give back resources */
+out4:
+	for (cnt = 0; cnt < phys_port; cnt++)
+		kfree(ports[cnt]);
+
+	kfree(card_ptr);
+	return ret;
+}
+
+static struct ioc3_submodule ioc3uart_ops = {
+	.name = "IOC3uart",
+	.probe = ioc3uart_probe,
+	.remove = ioc3uart_remove,
+	/* call .intr for both ports initially */
+	.irq_mask = SIO_IR_SA | SIO_IR_SB,
+	.intr = ioc3uart_intr,
+	.owner = THIS_MODULE,
+};
+
+/**
+ * ioc3_detect - module init called,
+ */
+static int __init ioc3uart_init(void)
+{
+	int ret;
+
+	/* register with serial core */
+	if ((ret = uart_register_driver(&ioc3_uart)) < 0) {
+		printk(KERN_WARNING
+		       "%s: Couldn't register IOC3 uart serial driver\n",
+		       __func__);
+		return ret;
+	}
+	ret = ioc3_register_submodule(&ioc3uart_ops);
+	if (ret)
+		uart_unregister_driver(&ioc3_uart);
+	return ret;
+}
+
+static void __exit ioc3uart_exit(void)
+{
+	ioc3_unregister_submodule(&ioc3uart_ops);
+	uart_unregister_driver(&ioc3_uart);
+}
+
+module_init(ioc3uart_init);
+module_exit(ioc3uart_exit);
+
+MODULE_AUTHOR("Pat Gefre - Silicon Graphics Inc. (SGI) <pfg@sgi.com>");
+MODULE_DESCRIPTION("Serial PCI driver module for SGI IOC3 card");
+MODULE_LICENSE("GPL");
diff --git a/ap/os/linux/linux-3.4.x/drivers/tty/serial/ioc4_serial.c b/ap/os/linux/linux-3.4.x/drivers/tty/serial/ioc4_serial.c
new file mode 100644
index 0000000..e16894f
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/tty/serial/ioc4_serial.c
@@ -0,0 +1,2953 @@
+/*
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License.  See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * Copyright (C) 2003-2006 Silicon Graphics, Inc.  All Rights Reserved.
+ */
+
+
+/*
+ * This file contains a module version of the ioc4 serial driver. This
+ * includes all the support functions needed (support functions, etc.)
+ * and the serial driver itself.
+ */
+#include <linux/errno.h>
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
+#include <linux/serial.h>
+#include <linux/circ_buf.h>
+#include <linux/serial_reg.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/ioc4.h>
+#include <linux/serial_core.h>
+#include <linux/slab.h>
+
+/*
+ * interesting things about the ioc4
+ */
+
+#define IOC4_NUM_SERIAL_PORTS	4	/* max ports per card */
+#define IOC4_NUM_CARDS		8	/* max cards per partition */
+
+#define	GET_SIO_IR(_n)	(_n == 0) ? (IOC4_SIO_IR_S0) : \
+				(_n == 1) ? (IOC4_SIO_IR_S1) : \
+				(_n == 2) ? (IOC4_SIO_IR_S2) : \
+				(IOC4_SIO_IR_S3)
+
+#define	GET_OTHER_IR(_n)  (_n == 0) ? (IOC4_OTHER_IR_S0_MEMERR) : \
+				(_n == 1) ? (IOC4_OTHER_IR_S1_MEMERR) : \
+				(_n == 2) ? (IOC4_OTHER_IR_S2_MEMERR) : \
+				(IOC4_OTHER_IR_S3_MEMERR)
+
+
+/*
+ * All IOC4 registers are 32 bits wide.
+ */
+
+/*
+ * PCI Memory Space Map
+ */
+#define IOC4_PCI_ERR_ADDR_L     0x000	/* Low Error Address */
+#define IOC4_PCI_ERR_ADDR_VLD	        (0x1 << 0)
+#define IOC4_PCI_ERR_ADDR_MST_ID_MSK    (0xf << 1)
+#define IOC4_PCI_ERR_ADDR_MST_NUM_MSK   (0xe << 1)
+#define IOC4_PCI_ERR_ADDR_MST_TYP_MSK   (0x1 << 1)
+#define IOC4_PCI_ERR_ADDR_MUL_ERR       (0x1 << 5)
+#define IOC4_PCI_ERR_ADDR_ADDR_MSK      (0x3ffffff << 6)
+
+/* Interrupt types */
+#define	IOC4_SIO_INTR_TYPE	0
+#define	IOC4_OTHER_INTR_TYPE	1
+#define	IOC4_NUM_INTR_TYPES	2
+
+/* Bitmasks for IOC4_SIO_IR, IOC4_SIO_IEC, and IOC4_SIO_IES  */
+#define IOC4_SIO_IR_S0_TX_MT	   0x00000001	/* Serial port 0 TX empty */
+#define IOC4_SIO_IR_S0_RX_FULL	   0x00000002	/* Port 0 RX buf full */
+#define IOC4_SIO_IR_S0_RX_HIGH	   0x00000004	/* Port 0 RX hiwat */
+#define IOC4_SIO_IR_S0_RX_TIMER	   0x00000008	/* Port 0 RX timeout */
+#define IOC4_SIO_IR_S0_DELTA_DCD   0x00000010	/* Port 0 delta DCD */
+#define IOC4_SIO_IR_S0_DELTA_CTS   0x00000020	/* Port 0 delta CTS */
+#define IOC4_SIO_IR_S0_INT	   0x00000040	/* Port 0 pass-thru intr */
+#define IOC4_SIO_IR_S0_TX_EXPLICIT 0x00000080	/* Port 0 explicit TX thru */
+#define IOC4_SIO_IR_S1_TX_MT	   0x00000100	/* Serial port 1 */
+#define IOC4_SIO_IR_S1_RX_FULL	   0x00000200	/* */
+#define IOC4_SIO_IR_S1_RX_HIGH	   0x00000400	/* */
+#define IOC4_SIO_IR_S1_RX_TIMER	   0x00000800	/* */
+#define IOC4_SIO_IR_S1_DELTA_DCD   0x00001000	/* */
+#define IOC4_SIO_IR_S1_DELTA_CTS   0x00002000	/* */
+#define IOC4_SIO_IR_S1_INT	   0x00004000	/* */
+#define IOC4_SIO_IR_S1_TX_EXPLICIT 0x00008000	/* */
+#define IOC4_SIO_IR_S2_TX_MT	   0x00010000	/* Serial port 2 */
+#define IOC4_SIO_IR_S2_RX_FULL	   0x00020000	/* */
+#define IOC4_SIO_IR_S2_RX_HIGH	   0x00040000	/* */
+#define IOC4_SIO_IR_S2_RX_TIMER	   0x00080000	/* */
+#define IOC4_SIO_IR_S2_DELTA_DCD   0x00100000	/* */
+#define IOC4_SIO_IR_S2_DELTA_CTS   0x00200000	/* */
+#define IOC4_SIO_IR_S2_INT	   0x00400000	/* */
+#define IOC4_SIO_IR_S2_TX_EXPLICIT 0x00800000	/* */
+#define IOC4_SIO_IR_S3_TX_MT	   0x01000000	/* Serial port 3 */
+#define IOC4_SIO_IR_S3_RX_FULL	   0x02000000	/* */
+#define IOC4_SIO_IR_S3_RX_HIGH	   0x04000000	/* */
+#define IOC4_SIO_IR_S3_RX_TIMER	   0x08000000	/* */
+#define IOC4_SIO_IR_S3_DELTA_DCD   0x10000000	/* */
+#define IOC4_SIO_IR_S3_DELTA_CTS   0x20000000	/* */
+#define IOC4_SIO_IR_S3_INT	   0x40000000	/* */
+#define IOC4_SIO_IR_S3_TX_EXPLICIT 0x80000000	/* */
+
+/* Per device interrupt masks */
+#define IOC4_SIO_IR_S0		(IOC4_SIO_IR_S0_TX_MT | \
+				 IOC4_SIO_IR_S0_RX_FULL | \
+				 IOC4_SIO_IR_S0_RX_HIGH | \
+				 IOC4_SIO_IR_S0_RX_TIMER | \
+				 IOC4_SIO_IR_S0_DELTA_DCD | \
+				 IOC4_SIO_IR_S0_DELTA_CTS | \
+				 IOC4_SIO_IR_S0_INT | \
+				 IOC4_SIO_IR_S0_TX_EXPLICIT)
+#define IOC4_SIO_IR_S1		(IOC4_SIO_IR_S1_TX_MT | \
+				 IOC4_SIO_IR_S1_RX_FULL | \
+				 IOC4_SIO_IR_S1_RX_HIGH | \
+				 IOC4_SIO_IR_S1_RX_TIMER | \
+				 IOC4_SIO_IR_S1_DELTA_DCD | \
+				 IOC4_SIO_IR_S1_DELTA_CTS | \
+				 IOC4_SIO_IR_S1_INT | \
+				 IOC4_SIO_IR_S1_TX_EXPLICIT)
+#define IOC4_SIO_IR_S2		(IOC4_SIO_IR_S2_TX_MT | \
+				 IOC4_SIO_IR_S2_RX_FULL | \
+				 IOC4_SIO_IR_S2_RX_HIGH | \
+				 IOC4_SIO_IR_S2_RX_TIMER | \
+				 IOC4_SIO_IR_S2_DELTA_DCD | \
+				 IOC4_SIO_IR_S2_DELTA_CTS | \
+				 IOC4_SIO_IR_S2_INT | \
+				 IOC4_SIO_IR_S2_TX_EXPLICIT)
+#define IOC4_SIO_IR_S3		(IOC4_SIO_IR_S3_TX_MT | \
+				 IOC4_SIO_IR_S3_RX_FULL | \
+				 IOC4_SIO_IR_S3_RX_HIGH | \
+				 IOC4_SIO_IR_S3_RX_TIMER | \
+				 IOC4_SIO_IR_S3_DELTA_DCD | \
+				 IOC4_SIO_IR_S3_DELTA_CTS | \
+				 IOC4_SIO_IR_S3_INT | \
+				 IOC4_SIO_IR_S3_TX_EXPLICIT)
+
+/* Bitmasks for IOC4_OTHER_IR, IOC4_OTHER_IEC, and IOC4_OTHER_IES  */
+#define IOC4_OTHER_IR_ATA_INT		0x00000001  /* ATAPI intr pass-thru */
+#define IOC4_OTHER_IR_ATA_MEMERR	0x00000002  /* ATAPI DMA PCI error */
+#define IOC4_OTHER_IR_S0_MEMERR		0x00000004  /* Port 0 PCI error */
+#define IOC4_OTHER_IR_S1_MEMERR		0x00000008  /* Port 1 PCI error */
+#define IOC4_OTHER_IR_S2_MEMERR		0x00000010  /* Port 2 PCI error */
+#define IOC4_OTHER_IR_S3_MEMERR		0x00000020  /* Port 3 PCI error */
+#define IOC4_OTHER_IR_KBD_INT		0x00000040  /* Keyboard/mouse */
+#define IOC4_OTHER_IR_RESERVED		0x007fff80  /* Reserved */
+#define IOC4_OTHER_IR_RT_INT		0x00800000  /* INT_OUT section output */
+#define IOC4_OTHER_IR_GEN_INT		0xff000000  /* Generic pins */
+
+#define IOC4_OTHER_IR_SER_MEMERR (IOC4_OTHER_IR_S0_MEMERR | IOC4_OTHER_IR_S1_MEMERR | \
+				  IOC4_OTHER_IR_S2_MEMERR | IOC4_OTHER_IR_S3_MEMERR)
+
+/* Bitmasks for IOC4_SIO_CR */
+#define IOC4_SIO_CR_CMD_PULSE_SHIFT              0  /* byte bus strobe shift */
+#define IOC4_SIO_CR_ARB_DIAG_TX0	0x00000000
+#define IOC4_SIO_CR_ARB_DIAG_RX0	0x00000010
+#define IOC4_SIO_CR_ARB_DIAG_TX1	0x00000020
+#define IOC4_SIO_CR_ARB_DIAG_RX1	0x00000030
+#define IOC4_SIO_CR_ARB_DIAG_TX2	0x00000040
+#define IOC4_SIO_CR_ARB_DIAG_RX2	0x00000050
+#define IOC4_SIO_CR_ARB_DIAG_TX3	0x00000060
+#define IOC4_SIO_CR_ARB_DIAG_RX3	0x00000070
+#define IOC4_SIO_CR_SIO_DIAG_IDLE	0x00000080  /* 0 -> active request among
+							   serial ports (ro) */
+/* Defs for some of the generic I/O pins */
+#define IOC4_GPCR_UART0_MODESEL	   0x10	/* Pin is output to port 0
+						   mode sel */
+#define IOC4_GPCR_UART1_MODESEL	   0x20	/* Pin is output to port 1
+						   mode sel */
+#define IOC4_GPCR_UART2_MODESEL	   0x40	/* Pin is output to port 2
+						   mode sel */
+#define IOC4_GPCR_UART3_MODESEL	   0x80	/* Pin is output to port 3
+						   mode sel */
+
+#define IOC4_GPPR_UART0_MODESEL_PIN   4	/* GIO pin controlling
+					   uart 0 mode select */
+#define IOC4_GPPR_UART1_MODESEL_PIN   5	/* GIO pin controlling
+					   uart 1 mode select */
+#define IOC4_GPPR_UART2_MODESEL_PIN   6	/* GIO pin controlling
+					   uart 2 mode select */
+#define IOC4_GPPR_UART3_MODESEL_PIN   7	/* GIO pin controlling
+					   uart 3 mode select */
+
+/* Bitmasks for serial RX status byte */
+#define IOC4_RXSB_OVERRUN       0x01	/* Char(s) lost */
+#define IOC4_RXSB_PAR_ERR	0x02	/* Parity error */
+#define IOC4_RXSB_FRAME_ERR	0x04	/* Framing error */
+#define IOC4_RXSB_BREAK	        0x08	/* Break character */
+#define IOC4_RXSB_CTS	        0x10	/* State of CTS */
+#define IOC4_RXSB_DCD	        0x20	/* State of DCD */
+#define IOC4_RXSB_MODEM_VALID   0x40	/* DCD, CTS, and OVERRUN are valid */
+#define IOC4_RXSB_DATA_VALID    0x80	/* Data byte, FRAME_ERR PAR_ERR
+					 * & BREAK valid */
+
+/* Bitmasks for serial TX control byte */
+#define IOC4_TXCB_INT_WHEN_DONE 0x20	/* Interrupt after this byte is sent */
+#define IOC4_TXCB_INVALID	0x00	/* Byte is invalid */
+#define IOC4_TXCB_VALID	        0x40	/* Byte is valid */
+#define IOC4_TXCB_MCR	        0x80	/* Data<7:0> to modem control reg */
+#define IOC4_TXCB_DELAY	        0xc0	/* Delay data<7:0> mSec */
+
+/* Bitmasks for IOC4_SBBR_L */
+#define IOC4_SBBR_L_SIZE	0x00000001  /* 0 == 1KB rings, 1 == 4KB rings */
+
+/* Bitmasks for IOC4_SSCR_<3:0> */
+#define IOC4_SSCR_RX_THRESHOLD  0x000001ff  /* Hiwater mark */
+#define IOC4_SSCR_TX_TIMER_BUSY 0x00010000  /* TX timer in progress */
+#define IOC4_SSCR_HFC_EN	0x00020000  /* Hardware flow control enabled */
+#define IOC4_SSCR_RX_RING_DCD   0x00040000  /* Post RX record on delta-DCD */
+#define IOC4_SSCR_RX_RING_CTS   0x00080000  /* Post RX record on delta-CTS */
+#define IOC4_SSCR_DIAG	        0x00200000  /* Bypass clock divider for sim */
+#define IOC4_SSCR_RX_DRAIN	0x08000000  /* Drain RX buffer to memory */
+#define IOC4_SSCR_DMA_EN	0x10000000  /* Enable ring buffer DMA */
+#define IOC4_SSCR_DMA_PAUSE	0x20000000  /* Pause DMA */
+#define IOC4_SSCR_PAUSE_STATE   0x40000000  /* Sets when PAUSE takes effect */
+#define IOC4_SSCR_RESET	        0x80000000  /* Reset DMA channels */
+
+/* All producer/comsumer pointers are the same bitfield */
+#define IOC4_PROD_CONS_PTR_4K   0x00000ff8	/* For 4K buffers */
+#define IOC4_PROD_CONS_PTR_1K   0x000003f8	/* For 1K buffers */
+#define IOC4_PROD_CONS_PTR_OFF           3
+
+/* Bitmasks for IOC4_SRCIR_<3:0> */
+#define IOC4_SRCIR_ARM	        0x80000000	/* Arm RX timer */
+
+/* Bitmasks for IOC4_SHADOW_<3:0> */
+#define IOC4_SHADOW_DR	 0x00000001	/* Data ready */
+#define IOC4_SHADOW_OE	 0x00000002	/* Overrun error */
+#define IOC4_SHADOW_PE	 0x00000004	/* Parity error */
+#define IOC4_SHADOW_FE	 0x00000008	/* Framing error */
+#define IOC4_SHADOW_BI	 0x00000010	/* Break interrupt */
+#define IOC4_SHADOW_THRE 0x00000020	/* Xmit holding register empty */
+#define IOC4_SHADOW_TEMT 0x00000040	/* Xmit shift register empty */
+#define IOC4_SHADOW_RFCE 0x00000080	/* Char in RX fifo has an error */
+#define IOC4_SHADOW_DCTS 0x00010000	/* Delta clear to send */
+#define IOC4_SHADOW_DDCD 0x00080000	/* Delta data carrier detect */
+#define IOC4_SHADOW_CTS	 0x00100000	/* Clear to send */
+#define IOC4_SHADOW_DCD	 0x00800000	/* Data carrier detect */
+#define IOC4_SHADOW_DTR	 0x01000000	/* Data terminal ready */
+#define IOC4_SHADOW_RTS	 0x02000000	/* Request to send */
+#define IOC4_SHADOW_OUT1 0x04000000	/* 16550 OUT1 bit */
+#define IOC4_SHADOW_OUT2 0x08000000	/* 16550 OUT2 bit */
+#define IOC4_SHADOW_LOOP 0x10000000	/* Loopback enabled */
+
+/* Bitmasks for IOC4_SRTR_<3:0> */
+#define IOC4_SRTR_CNT	        0x00000fff	/* Reload value for RX timer */
+#define IOC4_SRTR_CNT_VAL	0x0fff0000	/* Current value of RX timer */
+#define IOC4_SRTR_CNT_VAL_SHIFT         16
+#define IOC4_SRTR_HZ                 16000	/* SRTR clock frequency */
+
+/* Serial port register map used for DMA and PIO serial I/O */
+struct ioc4_serialregs {
+	uint32_t sscr;
+	uint32_t stpir;
+	uint32_t stcir;
+	uint32_t srpir;
+	uint32_t srcir;
+	uint32_t srtr;
+	uint32_t shadow;
+};
+
+/* IOC4 UART register map */
+struct ioc4_uartregs {
+	char i4u_lcr;
+	union {
+		char iir;	/* read only */
+		char fcr;	/* write only */
+	} u3;
+	union {
+		char ier;	/* DLAB == 0 */
+		char dlm;	/* DLAB == 1 */
+	} u2;
+	union {
+		char rbr;	/* read only, DLAB == 0 */
+		char thr;	/* write only, DLAB == 0 */
+		char dll;	/* DLAB == 1 */
+	} u1;
+	char i4u_scr;
+	char i4u_msr;
+	char i4u_lsr;
+	char i4u_mcr;
+};
+
+/* short names */
+#define i4u_dll u1.dll
+#define i4u_ier u2.ier
+#define i4u_dlm u2.dlm
+#define i4u_fcr u3.fcr
+
+/* Serial port registers used for DMA serial I/O */
+struct ioc4_serial {
+	uint32_t sbbr01_l;
+	uint32_t sbbr01_h;
+	uint32_t sbbr23_l;
+	uint32_t sbbr23_h;
+
+	struct ioc4_serialregs port_0;
+	struct ioc4_serialregs port_1;
+	struct ioc4_serialregs port_2;
+	struct ioc4_serialregs port_3;
+	struct ioc4_uartregs uart_0;
+	struct ioc4_uartregs uart_1;
+	struct ioc4_uartregs uart_2;
+	struct ioc4_uartregs uart_3;
+} ioc4_serial;
+
+/* UART clock speed */
+#define IOC4_SER_XIN_CLK_66     66666667
+#define IOC4_SER_XIN_CLK_33     33333333
+
+#define IOC4_W_IES		0
+#define IOC4_W_IEC		1
+
+typedef void ioc4_intr_func_f(void *, uint32_t);
+typedef ioc4_intr_func_f *ioc4_intr_func_t;
+
+static unsigned int Num_of_ioc4_cards;
+
+/* defining this will get you LOTS of great debug info */
+//#define DEBUG_INTERRUPTS
+#define DPRINT_CONFIG(_x...)	;
+//#define DPRINT_CONFIG(_x...)	printk _x
+
+/* number of characters left in xmit buffer before we ask for more */
+#define WAKEUP_CHARS	256
+
+/* number of characters we want to transmit to the lower level at a time */
+#define IOC4_MAX_CHARS	256
+#define IOC4_FIFO_CHARS	255
+
+/* Device name we're using */
+#define DEVICE_NAME_RS232  "ttyIOC"
+#define DEVICE_NAME_RS422  "ttyAIOC"
+#define DEVICE_MAJOR	   204
+#define DEVICE_MINOR_RS232 50
+#define DEVICE_MINOR_RS422 84
+
+
+/* register offsets */
+#define IOC4_SERIAL_OFFSET	0x300
+
+/* flags for next_char_state */
+#define NCS_BREAK	0x1
+#define NCS_PARITY	0x2
+#define NCS_FRAMING	0x4
+#define NCS_OVERRUN	0x8
+
+/* cause we need SOME parameters ... */
+#define MIN_BAUD_SUPPORTED	1200
+#define MAX_BAUD_SUPPORTED	115200
+
+/* protocol types supported */
+#define PROTO_RS232	3
+#define PROTO_RS422	7
+
+/* Notification types */
+#define N_DATA_READY	0x01
+#define N_OUTPUT_LOWAT	0x02
+#define N_BREAK		0x04
+#define N_PARITY_ERROR	0x08
+#define N_FRAMING_ERROR	0x10
+#define N_OVERRUN_ERROR	0x20
+#define N_DDCD		0x40
+#define N_DCTS		0x80
+
+#define N_ALL_INPUT	(N_DATA_READY | N_BREAK |			\
+			 N_PARITY_ERROR | N_FRAMING_ERROR |		\
+			 N_OVERRUN_ERROR | N_DDCD | N_DCTS)
+
+#define N_ALL_OUTPUT	N_OUTPUT_LOWAT
+
+#define N_ALL_ERRORS	(N_PARITY_ERROR | N_FRAMING_ERROR | N_OVERRUN_ERROR)
+
+#define N_ALL		(N_DATA_READY | N_OUTPUT_LOWAT | N_BREAK |	\
+			 N_PARITY_ERROR | N_FRAMING_ERROR |		\
+			 N_OVERRUN_ERROR | N_DDCD | N_DCTS)
+
+#define SER_DIVISOR(_x, clk)		(((clk) + (_x) * 8) / ((_x) * 16))
+#define DIVISOR_TO_BAUD(div, clk)	((clk) / 16 / (div))
+
+/* Some masks */
+#define LCR_MASK_BITS_CHAR	(UART_LCR_WLEN5 | UART_LCR_WLEN6 \
+					| UART_LCR_WLEN7 | UART_LCR_WLEN8)
+#define LCR_MASK_STOP_BITS	(UART_LCR_STOP)
+
+#define PENDING(_p)	(readl(&(_p)->ip_mem->sio_ir.raw) & _p->ip_ienb)
+#define READ_SIO_IR(_p) readl(&(_p)->ip_mem->sio_ir.raw)
+
+/* Default to 4k buffers */
+#ifdef IOC4_1K_BUFFERS
+#define RING_BUF_SIZE 1024
+#define IOC4_BUF_SIZE_BIT 0
+#define PROD_CONS_MASK IOC4_PROD_CONS_PTR_1K
+#else
+#define RING_BUF_SIZE 4096
+#define IOC4_BUF_SIZE_BIT IOC4_SBBR_L_SIZE
+#define PROD_CONS_MASK IOC4_PROD_CONS_PTR_4K
+#endif
+
+#define TOTAL_RING_BUF_SIZE (RING_BUF_SIZE * 4)
+
+/*
+ * This is the entry saved by the driver - one per card
+ */
+
+#define UART_PORT_MIN		0
+#define UART_PORT_RS232		UART_PORT_MIN
+#define UART_PORT_RS422		1
+#define UART_PORT_COUNT		2	/* one for each mode */
+
+struct ioc4_control {
+	int ic_irq;
+	struct {
+		/* uart ports are allocated here - 1 for rs232, 1 for rs422 */
+		struct uart_port icp_uart_port[UART_PORT_COUNT];
+		/* Handy reference material */
+		struct ioc4_port *icp_port;
+	} ic_port[IOC4_NUM_SERIAL_PORTS];
+	struct ioc4_soft *ic_soft;
+};
+
+/*
+ * per-IOC4 data structure
+ */
+#define MAX_IOC4_INTR_ENTS	(8 * sizeof(uint32_t))
+struct ioc4_soft {
+	struct ioc4_misc_regs __iomem *is_ioc4_misc_addr;
+	struct ioc4_serial __iomem *is_ioc4_serial_addr;
+
+	/* Each interrupt type has an entry in the array */
+	struct ioc4_intr_type {
+
+		/*
+		 * Each in-use entry in this array contains at least
+		 * one nonzero bit in sd_bits; no two entries in this
+		 * array have overlapping sd_bits values.
+		 */
+		struct ioc4_intr_info {
+			uint32_t sd_bits;
+			ioc4_intr_func_f *sd_intr;
+			void *sd_info;
+		} is_intr_info[MAX_IOC4_INTR_ENTS];
+
+		/* Number of entries active in the above array */
+		atomic_t is_num_intrs;
+	} is_intr_type[IOC4_NUM_INTR_TYPES];
+
+	/* is_ir_lock must be held while
+	 * modifying sio_ie values, so
+	 * we can be sure that sio_ie is
+	 * not changing when we read it
+	 * along with sio_ir.
+	 */
+	spinlock_t is_ir_lock;	/* SIO_IE[SC] mod lock */
+};
+
+/* Local port info for each IOC4 serial ports */
+struct ioc4_port {
+	struct uart_port *ip_port;	/* current active port ptr */
+	/* Ptrs for all ports */
+	struct uart_port *ip_all_ports[UART_PORT_COUNT];
+	/* Back ptrs for this port */
+	struct ioc4_control *ip_control;
+	struct pci_dev *ip_pdev;
+	struct ioc4_soft *ip_ioc4_soft;
+
+	/* pci mem addresses */
+	struct ioc4_misc_regs __iomem *ip_mem;
+	struct ioc4_serial __iomem *ip_serial;
+	struct ioc4_serialregs __iomem *ip_serial_regs;
+	struct ioc4_uartregs __iomem *ip_uart_regs;
+
+	/* Ring buffer page for this port */
+	dma_addr_t ip_dma_ringbuf;
+	/* vaddr of ring buffer */
+	struct ring_buffer *ip_cpu_ringbuf;
+
+	/* Rings for this port */
+	struct ring *ip_inring;
+	struct ring *ip_outring;
+
+	/* Hook to port specific values */
+	struct hooks *ip_hooks;
+
+	spinlock_t ip_lock;
+
+	/* Various rx/tx parameters */
+	int ip_baud;
+	int ip_tx_lowat;
+	int ip_rx_timeout;
+
+	/* Copy of notification bits */
+	int ip_notify;
+
+	/* Shadow copies of various registers so we don't need to PIO
+	 * read them constantly
+	 */
+	uint32_t ip_ienb;	/* Enabled interrupts */
+	uint32_t ip_sscr;
+	uint32_t ip_tx_prod;
+	uint32_t ip_rx_cons;
+	int ip_pci_bus_speed;
+	unsigned char ip_flags;
+};
+
+/* tx low water mark.  We need to notify the driver whenever tx is getting
+ * close to empty so it can refill the tx buffer and keep things going.
+ * Let's assume that if we interrupt 1 ms before the tx goes idle, we'll
+ * have no trouble getting in more chars in time (I certainly hope so).
+ */
+#define TX_LOWAT_LATENCY      1000
+#define TX_LOWAT_HZ          (1000000 / TX_LOWAT_LATENCY)
+#define TX_LOWAT_CHARS(baud) (baud / 10 / TX_LOWAT_HZ)
+
+/* Flags per port */
+#define INPUT_HIGH	0x01
+#define DCD_ON		0x02
+#define LOWAT_WRITTEN	0x04
+#define READ_ABORTED	0x08
+#define PORT_ACTIVE	0x10
+#define PORT_INACTIVE	0	/* This is the value when "off" */
+
+
+/* Since each port has different register offsets and bitmasks
+ * for everything, we'll store those that we need in tables so we
+ * don't have to be constantly checking the port we are dealing with.
+ */
+struct hooks {
+	uint32_t intr_delta_dcd;
+	uint32_t intr_delta_cts;
+	uint32_t intr_tx_mt;
+	uint32_t intr_rx_timer;
+	uint32_t intr_rx_high;
+	uint32_t intr_tx_explicit;
+	uint32_t intr_dma_error;
+	uint32_t intr_clear;
+	uint32_t intr_all;
+	int rs422_select_pin;
+};
+
+static struct hooks hooks_array[IOC4_NUM_SERIAL_PORTS] = {
+	/* Values for port 0 */
+	{
+	 IOC4_SIO_IR_S0_DELTA_DCD, IOC4_SIO_IR_S0_DELTA_CTS,
+	 IOC4_SIO_IR_S0_TX_MT, IOC4_SIO_IR_S0_RX_TIMER,
+	 IOC4_SIO_IR_S0_RX_HIGH, IOC4_SIO_IR_S0_TX_EXPLICIT,
+	 IOC4_OTHER_IR_S0_MEMERR,
+	 (IOC4_SIO_IR_S0_TX_MT | IOC4_SIO_IR_S0_RX_FULL |
+	  IOC4_SIO_IR_S0_RX_HIGH | IOC4_SIO_IR_S0_RX_TIMER |
+	  IOC4_SIO_IR_S0_DELTA_DCD | IOC4_SIO_IR_S0_DELTA_CTS |
+	  IOC4_SIO_IR_S0_INT | IOC4_SIO_IR_S0_TX_EXPLICIT),
+	 IOC4_SIO_IR_S0, IOC4_GPPR_UART0_MODESEL_PIN,
+	 },
+
+	/* Values for port 1 */
+	{
+	 IOC4_SIO_IR_S1_DELTA_DCD, IOC4_SIO_IR_S1_DELTA_CTS,
+	 IOC4_SIO_IR_S1_TX_MT, IOC4_SIO_IR_S1_RX_TIMER,
+	 IOC4_SIO_IR_S1_RX_HIGH, IOC4_SIO_IR_S1_TX_EXPLICIT,
+	 IOC4_OTHER_IR_S1_MEMERR,
+	 (IOC4_SIO_IR_S1_TX_MT | IOC4_SIO_IR_S1_RX_FULL |
+	  IOC4_SIO_IR_S1_RX_HIGH | IOC4_SIO_IR_S1_RX_TIMER |
+	  IOC4_SIO_IR_S1_DELTA_DCD | IOC4_SIO_IR_S1_DELTA_CTS |
+	  IOC4_SIO_IR_S1_INT | IOC4_SIO_IR_S1_TX_EXPLICIT),
+	 IOC4_SIO_IR_S1, IOC4_GPPR_UART1_MODESEL_PIN,
+	 },
+
+	/* Values for port 2 */
+	{
+	 IOC4_SIO_IR_S2_DELTA_DCD, IOC4_SIO_IR_S2_DELTA_CTS,
+	 IOC4_SIO_IR_S2_TX_MT, IOC4_SIO_IR_S2_RX_TIMER,
+	 IOC4_SIO_IR_S2_RX_HIGH, IOC4_SIO_IR_S2_TX_EXPLICIT,
+	 IOC4_OTHER_IR_S2_MEMERR,
+	 (IOC4_SIO_IR_S2_TX_MT | IOC4_SIO_IR_S2_RX_FULL |
+	  IOC4_SIO_IR_S2_RX_HIGH | IOC4_SIO_IR_S2_RX_TIMER |
+	  IOC4_SIO_IR_S2_DELTA_DCD | IOC4_SIO_IR_S2_DELTA_CTS |
+	  IOC4_SIO_IR_S2_INT | IOC4_SIO_IR_S2_TX_EXPLICIT),
+	 IOC4_SIO_IR_S2, IOC4_GPPR_UART2_MODESEL_PIN,
+	 },
+
+	/* Values for port 3 */
+	{
+	 IOC4_SIO_IR_S3_DELTA_DCD, IOC4_SIO_IR_S3_DELTA_CTS,
+	 IOC4_SIO_IR_S3_TX_MT, IOC4_SIO_IR_S3_RX_TIMER,
+	 IOC4_SIO_IR_S3_RX_HIGH, IOC4_SIO_IR_S3_TX_EXPLICIT,
+	 IOC4_OTHER_IR_S3_MEMERR,
+	 (IOC4_SIO_IR_S3_TX_MT | IOC4_SIO_IR_S3_RX_FULL |
+	  IOC4_SIO_IR_S3_RX_HIGH | IOC4_SIO_IR_S3_RX_TIMER |
+	  IOC4_SIO_IR_S3_DELTA_DCD | IOC4_SIO_IR_S3_DELTA_CTS |
+	  IOC4_SIO_IR_S3_INT | IOC4_SIO_IR_S3_TX_EXPLICIT),
+	 IOC4_SIO_IR_S3, IOC4_GPPR_UART3_MODESEL_PIN,
+	 }
+};
+
+/* A ring buffer entry */
+struct ring_entry {
+	union {
+		struct {
+			uint32_t alldata;
+			uint32_t allsc;
+		} all;
+		struct {
+			char data[4];	/* data bytes */
+			char sc[4];	/* status/control */
+		} s;
+	} u;
+};
+
+/* Test the valid bits in any of the 4 sc chars using "allsc" member */
+#define RING_ANY_VALID \
+	((uint32_t)(IOC4_RXSB_MODEM_VALID | IOC4_RXSB_DATA_VALID) * 0x01010101)
+
+#define ring_sc     u.s.sc
+#define ring_data   u.s.data
+#define ring_allsc  u.all.allsc
+
+/* Number of entries per ring buffer. */
+#define ENTRIES_PER_RING (RING_BUF_SIZE / (int) sizeof(struct ring_entry))
+
+/* An individual ring */
+struct ring {
+	struct ring_entry entries[ENTRIES_PER_RING];
+};
+
+/* The whole enchilada */
+struct ring_buffer {
+	struct ring TX_0_OR_2;
+	struct ring RX_0_OR_2;
+	struct ring TX_1_OR_3;
+	struct ring RX_1_OR_3;
+};
+
+/* Get a ring from a port struct */
+#define RING(_p, _wh)	&(((struct ring_buffer *)((_p)->ip_cpu_ringbuf))->_wh)
+
+/* Infinite loop detection.
+ */
+#define MAXITER 10000000
+
+/* Prototypes */
+static void receive_chars(struct uart_port *);
+static void handle_intr(void *arg, uint32_t sio_ir);
+
+/*
+ * port_is_active - determines if this port is currently active
+ * @port: ptr to soft struct for this port
+ * @uart_port: uart port to test for
+ */
+static inline int port_is_active(struct ioc4_port *port,
+		struct uart_port *uart_port)
+{
+	if (port) {
+		if ((port->ip_flags & PORT_ACTIVE)
+					&& (port->ip_port == uart_port))
+			return 1;
+	}
+	return 0;
+}
+
+
+/**
+ * write_ireg - write the interrupt regs
+ * @ioc4_soft: ptr to soft struct for this port
+ * @val: value to write
+ * @which: which register
+ * @type: which ireg set
+ */
+static inline void
+write_ireg(struct ioc4_soft *ioc4_soft, uint32_t val, int which, int type)
+{
+	struct ioc4_misc_regs __iomem *mem = ioc4_soft->is_ioc4_misc_addr;
+	unsigned long flags;
+
+	spin_lock_irqsave(&ioc4_soft->is_ir_lock, flags);
+
+	switch (type) {
+	case IOC4_SIO_INTR_TYPE:
+		switch (which) {
+		case IOC4_W_IES:
+			writel(val, &mem->sio_ies.raw);
+			break;
+
+		case IOC4_W_IEC:
+			writel(val, &mem->sio_iec.raw);
+			break;
+		}
+		break;
+
+	case IOC4_OTHER_INTR_TYPE:
+		switch (which) {
+		case IOC4_W_IES:
+			writel(val, &mem->other_ies.raw);
+			break;
+
+		case IOC4_W_IEC:
+			writel(val, &mem->other_iec.raw);
+			break;
+		}
+		break;
+
+	default:
+		break;
+	}
+	spin_unlock_irqrestore(&ioc4_soft->is_ir_lock, flags);
+}
+
+/**
+ * set_baud - Baud rate setting code
+ * @port: port to set
+ * @baud: baud rate to use
+ */
+static int set_baud(struct ioc4_port *port, int baud)
+{
+	int actual_baud;
+	int diff;
+	int lcr;
+	unsigned short divisor;
+	struct ioc4_uartregs __iomem *uart;
+
+	divisor = SER_DIVISOR(baud, port->ip_pci_bus_speed);
+	if (!divisor)
+		return 1;
+	actual_baud = DIVISOR_TO_BAUD(divisor, port->ip_pci_bus_speed);
+
+	diff = actual_baud - baud;
+	if (diff < 0)
+		diff = -diff;
+
+	/* If we're within 1%, we've found a match */
+	if (diff * 100 > actual_baud)
+		return 1;
+
+	uart = port->ip_uart_regs;
+	lcr = readb(&uart->i4u_lcr);
+	writeb(lcr | UART_LCR_DLAB, &uart->i4u_lcr);
+	writeb((unsigned char)divisor, &uart->i4u_dll);
+	writeb((unsigned char)(divisor >> 8), &uart->i4u_dlm);
+	writeb(lcr, &uart->i4u_lcr);
+	return 0;
+}
+
+
+/**
+ * get_ioc4_port - given a uart port, return the control structure
+ * @port: uart port
+ * @set: set this port as current
+ */
+static struct ioc4_port *get_ioc4_port(struct uart_port *the_port, int set)
+{
+	struct ioc4_driver_data *idd = dev_get_drvdata(the_port->dev);
+	struct ioc4_control *control = idd->idd_serial_data;
+	struct ioc4_port *port;
+	int port_num, port_type;
+
+	if (control) {
+		for ( port_num = 0; port_num < IOC4_NUM_SERIAL_PORTS;
+							port_num++ ) {
+			port = control->ic_port[port_num].icp_port;
+			if (!port)
+				continue;
+			for (port_type = UART_PORT_MIN;
+						port_type < UART_PORT_COUNT;
+						port_type++) {
+				if (the_port == port->ip_all_ports
+							[port_type]) {
+					/* set local copy */
+					if (set) {
+						port->ip_port = the_port;
+					}
+					return port;
+				}
+			}
+		}
+	}
+	return NULL;
+}
+
+/* The IOC4 hardware provides no atomic way to determine if interrupts
+ * are pending since two reads are required to do so.  The handler must
+ * read the SIO_IR and the SIO_IES, and take the logical and of the
+ * two.  When this value is zero, all interrupts have been serviced and
+ * the handler may return.
+ *
+ * This has the unfortunate "hole" that, if some other CPU or
+ * some other thread or some higher level interrupt manages to
+ * modify SIO_IE between our reads of SIO_IR and SIO_IE, we may
+ * think we have observed SIO_IR&SIO_IE==0 when in fact this
+ * condition never really occurred.
+ *
+ * To solve this, we use a simple spinlock that must be held
+ * whenever modifying SIO_IE; holding this lock while observing
+ * both SIO_IR and SIO_IE guarantees that we do not falsely
+ * conclude that no enabled interrupts are pending.
+ */
+
+static inline uint32_t
+pending_intrs(struct ioc4_soft *soft, int type)
+{
+	struct ioc4_misc_regs __iomem *mem = soft->is_ioc4_misc_addr;
+	unsigned long flag;
+	uint32_t intrs = 0;
+
+	BUG_ON(!((type == IOC4_SIO_INTR_TYPE)
+	       || (type == IOC4_OTHER_INTR_TYPE)));
+
+	spin_lock_irqsave(&soft->is_ir_lock, flag);
+
+	switch (type) {
+	case IOC4_SIO_INTR_TYPE:
+		intrs = readl(&mem->sio_ir.raw) & readl(&mem->sio_ies.raw);
+		break;
+
+	case IOC4_OTHER_INTR_TYPE:
+		intrs = readl(&mem->other_ir.raw) & readl(&mem->other_ies.raw);
+
+		/* Don't process any ATA interrupte */
+		intrs &= ~(IOC4_OTHER_IR_ATA_INT | IOC4_OTHER_IR_ATA_MEMERR);
+		break;
+
+	default:
+		break;
+	}
+	spin_unlock_irqrestore(&soft->is_ir_lock, flag);
+	return intrs;
+}
+
+/**
+ * port_init - Initialize the sio and ioc4 hardware for a given port
+ *			called per port from attach...
+ * @port: port to initialize
+ */
+static int inline port_init(struct ioc4_port *port)
+{
+	uint32_t sio_cr;
+	struct hooks *hooks = port->ip_hooks;
+	struct ioc4_uartregs __iomem *uart;
+
+	/* Idle the IOC4 serial interface */
+	writel(IOC4_SSCR_RESET, &port->ip_serial_regs->sscr);
+
+	/* Wait until any pending bus activity for this port has ceased */
+	do
+		sio_cr = readl(&port->ip_mem->sio_cr.raw);
+	while (!(sio_cr & IOC4_SIO_CR_SIO_DIAG_IDLE));
+
+	/* Finish reset sequence */
+	writel(0, &port->ip_serial_regs->sscr);
+
+	/* Once RESET is done, reload cached tx_prod and rx_cons values
+	 * and set rings to empty by making prod == cons
+	 */
+	port->ip_tx_prod = readl(&port->ip_serial_regs->stcir) & PROD_CONS_MASK;
+	writel(port->ip_tx_prod, &port->ip_serial_regs->stpir);
+	port->ip_rx_cons = readl(&port->ip_serial_regs->srpir) & PROD_CONS_MASK;
+	writel(port->ip_rx_cons | IOC4_SRCIR_ARM, &port->ip_serial_regs->srcir);
+
+	/* Disable interrupts for this 16550 */
+	uart = port->ip_uart_regs;
+	writeb(0, &uart->i4u_lcr);
+	writeb(0, &uart->i4u_ier);
+
+	/* Set the default baud */
+	set_baud(port, port->ip_baud);
+
+	/* Set line control to 8 bits no parity */
+	writeb(UART_LCR_WLEN8 | 0, &uart->i4u_lcr);
+					/* UART_LCR_STOP == 1 stop */
+
+	/* Enable the FIFOs */
+	writeb(UART_FCR_ENABLE_FIFO, &uart->i4u_fcr);
+	/* then reset 16550 FIFOs */
+	writeb(UART_FCR_ENABLE_FIFO | UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT,
+			&uart->i4u_fcr);
+
+	/* Clear modem control register */
+	writeb(0, &uart->i4u_mcr);
+
+	/* Clear deltas in modem status register */
+	readb(&uart->i4u_msr);
+
+	/* Only do this once per port pair */
+	if (port->ip_hooks == &hooks_array[0]
+			    || port->ip_hooks == &hooks_array[2]) {
+		unsigned long ring_pci_addr;
+		uint32_t __iomem *sbbr_l;
+		uint32_t __iomem *sbbr_h;
+
+		if (port->ip_hooks == &hooks_array[0]) {
+			sbbr_l = &port->ip_serial->sbbr01_l;
+			sbbr_h = &port->ip_serial->sbbr01_h;
+		} else {
+			sbbr_l = &port->ip_serial->sbbr23_l;
+			sbbr_h = &port->ip_serial->sbbr23_h;
+		}
+
+		ring_pci_addr = (unsigned long __iomem)port->ip_dma_ringbuf;
+		DPRINT_CONFIG(("%s: ring_pci_addr 0x%lx\n",
+					__func__, ring_pci_addr));
+
+		writel((unsigned int)((uint64_t)ring_pci_addr >> 32), sbbr_h);
+		writel((unsigned int)ring_pci_addr | IOC4_BUF_SIZE_BIT, sbbr_l);
+	}
+
+	/* Set the receive timeout value to 10 msec */
+	writel(IOC4_SRTR_HZ / 100, &port->ip_serial_regs->srtr);
+
+	/* Set rx threshold, enable DMA */
+	/* Set high water mark at 3/4 of full ring */
+	port->ip_sscr = (ENTRIES_PER_RING * 3 / 4);
+	writel(port->ip_sscr, &port->ip_serial_regs->sscr);
+
+	/* Disable and clear all serial related interrupt bits */
+	write_ireg(port->ip_ioc4_soft, hooks->intr_clear,
+		       IOC4_W_IEC, IOC4_SIO_INTR_TYPE);
+	port->ip_ienb &= ~hooks->intr_clear;
+	writel(hooks->intr_clear, &port->ip_mem->sio_ir.raw);
+	return 0;
+}
+
+/**
+ * handle_dma_error_intr - service any pending DMA error interrupts for the
+ *			given port - 2nd level called via sd_intr
+ * @arg: handler arg
+ * @other_ir: ioc4regs
+ */
+static void handle_dma_error_intr(void *arg, uint32_t other_ir)
+{
+	struct ioc4_port *port = (struct ioc4_port *)arg;
+	struct hooks *hooks = port->ip_hooks;
+	unsigned long flags;
+
+	spin_lock_irqsave(&port->ip_lock, flags);
+
+	/* ACK the interrupt */
+	writel(hooks->intr_dma_error, &port->ip_mem->other_ir.raw);
+
+	if (readl(&port->ip_mem->pci_err_addr_l.raw) & IOC4_PCI_ERR_ADDR_VLD) {
+		printk(KERN_ERR
+			"PCI error address is 0x%llx, "
+				"master is serial port %c %s\n",
+		     (((uint64_t)readl(&port->ip_mem->pci_err_addr_h)
+							 << 32)
+				| readl(&port->ip_mem->pci_err_addr_l.raw))
+					& IOC4_PCI_ERR_ADDR_ADDR_MSK, '1' +
+		     ((char)(readl(&port->ip_mem->pci_err_addr_l.raw) &
+			     IOC4_PCI_ERR_ADDR_MST_NUM_MSK) >> 1),
+		     (readl(&port->ip_mem->pci_err_addr_l.raw)
+				& IOC4_PCI_ERR_ADDR_MST_TYP_MSK)
+				? "RX" : "TX");
+
+		if (readl(&port->ip_mem->pci_err_addr_l.raw)
+						& IOC4_PCI_ERR_ADDR_MUL_ERR) {
+			printk(KERN_ERR
+				"Multiple errors occurred\n");
+		}
+	}
+	spin_unlock_irqrestore(&port->ip_lock, flags);
+
+	/* Re-enable DMA error interrupts */
+	write_ireg(port->ip_ioc4_soft, hooks->intr_dma_error, IOC4_W_IES,
+						IOC4_OTHER_INTR_TYPE);
+}
+
+/**
+ * intr_connect - interrupt connect function
+ * @soft: soft struct for this card
+ * @type: interrupt type
+ * @intrbits: bit pattern to set
+ * @intr: handler function
+ * @info: handler arg
+ */
+static void
+intr_connect(struct ioc4_soft *soft, int type,
+		  uint32_t intrbits, ioc4_intr_func_f * intr, void *info)
+{
+	int i;
+	struct ioc4_intr_info *intr_ptr;
+
+	BUG_ON(!((type == IOC4_SIO_INTR_TYPE)
+	       || (type == IOC4_OTHER_INTR_TYPE)));
+
+	i = atomic_inc_return(&soft-> is_intr_type[type].is_num_intrs) - 1;
+	BUG_ON(!(i < MAX_IOC4_INTR_ENTS || (printk("i %d\n", i), 0)));
+
+	/* Save off the lower level interrupt handler */
+	intr_ptr = &soft->is_intr_type[type].is_intr_info[i];
+	intr_ptr->sd_bits = intrbits;
+	intr_ptr->sd_intr = intr;
+	intr_ptr->sd_info = info;
+}
+
+/**
+ * ioc4_intr - Top level IOC4 interrupt handler.
+ * @irq: irq value
+ * @arg: handler arg
+ */
+
+static irqreturn_t ioc4_intr(int irq, void *arg)
+{
+	struct ioc4_soft *soft;
+	uint32_t this_ir, this_mir;
+	int xx, num_intrs = 0;
+	int intr_type;
+	int handled = 0;
+	struct ioc4_intr_info *intr_info;
+
+	soft = arg;
+	for (intr_type = 0; intr_type < IOC4_NUM_INTR_TYPES; intr_type++) {
+		num_intrs = (int)atomic_read(
+				&soft->is_intr_type[intr_type].is_num_intrs);
+
+		this_mir = this_ir = pending_intrs(soft, intr_type);
+
+		/* Farm out the interrupt to the various drivers depending on
+		 * which interrupt bits are set.
+		 */
+		for (xx = 0; xx < num_intrs; xx++) {
+			intr_info = &soft->is_intr_type[intr_type].is_intr_info[xx];
+			if ((this_mir = this_ir & intr_info->sd_bits)) {
+				/* Disable owned interrupts, call handler */
+				handled++;
+				write_ireg(soft, intr_info->sd_bits, IOC4_W_IEC,
+								intr_type);
+				intr_info->sd_intr(intr_info->sd_info, this_mir);
+				this_ir &= ~this_mir;
+			}
+		}
+	}
+#ifdef DEBUG_INTERRUPTS
+	{
+		struct ioc4_misc_regs __iomem *mem = soft->is_ioc4_misc_addr;
+		unsigned long flag;
+
+		spin_lock_irqsave(&soft->is_ir_lock, flag);
+		printk ("%s : %d : mem 0x%p sio_ir 0x%x sio_ies 0x%x "
+				"other_ir 0x%x other_ies 0x%x mask 0x%x\n",
+		     __func__, __LINE__,
+		     (void *)mem, readl(&mem->sio_ir.raw),
+		     readl(&mem->sio_ies.raw),
+		     readl(&mem->other_ir.raw),
+		     readl(&mem->other_ies.raw),
+		     IOC4_OTHER_IR_ATA_INT | IOC4_OTHER_IR_ATA_MEMERR);
+		spin_unlock_irqrestore(&soft->is_ir_lock, flag);
+	}
+#endif
+	return handled ? IRQ_HANDLED : IRQ_NONE;
+}
+
+/**
+ * ioc4_attach_local - Device initialization.
+ *			Called at *_attach() time for each
+ *			IOC4 with serial ports in the system.
+ * @idd: Master module data for this IOC4
+ */
+static int inline ioc4_attach_local(struct ioc4_driver_data *idd)
+{
+	struct ioc4_port *port;
+	struct ioc4_port *ports[IOC4_NUM_SERIAL_PORTS];
+	int port_number;
+	uint16_t ioc4_revid_min = 62;
+	uint16_t ioc4_revid;
+	struct pci_dev *pdev = idd->idd_pdev;
+	struct ioc4_control* control = idd->idd_serial_data;
+	struct ioc4_soft *soft = control->ic_soft;
+	void __iomem *ioc4_misc = idd->idd_misc_regs;
+	void __iomem *ioc4_serial = soft->is_ioc4_serial_addr;
+
+	/* IOC4 firmware must be at least rev 62 */
+	pci_read_config_word(pdev, PCI_COMMAND_SPECIAL, &ioc4_revid);
+
+	printk(KERN_INFO "IOC4 firmware revision %d\n", ioc4_revid);
+	if (ioc4_revid < ioc4_revid_min) {
+		printk(KERN_WARNING
+		    "IOC4 serial not supported on firmware rev %d, "
+				"please upgrade to rev %d or higher\n",
+				ioc4_revid, ioc4_revid_min);
+		return -EPERM;
+	}
+	BUG_ON(ioc4_misc == NULL);
+	BUG_ON(ioc4_serial == NULL);
+
+	/* Create port structures for each port */
+	for (port_number = 0; port_number < IOC4_NUM_SERIAL_PORTS;
+							port_number++) {
+		port = kzalloc(sizeof(struct ioc4_port), GFP_KERNEL);
+		if (!port) {
+			printk(KERN_WARNING
+				"IOC4 serial memory not available for port\n");
+			return -ENOMEM;
+		}
+		spin_lock_init(&port->ip_lock);
+
+		/* we need to remember the previous ones, to point back to
+		 * them farther down - setting up the ring buffers.
+		 */
+		ports[port_number] = port;
+
+		/* Allocate buffers and jumpstart the hardware.  */
+		control->ic_port[port_number].icp_port = port;
+		port->ip_ioc4_soft = soft;
+		port->ip_pdev = pdev;
+		port->ip_ienb = 0;
+		/* Use baud rate calculations based on detected PCI
+		 * bus speed.  Simply test whether the PCI clock is
+		 * running closer to 66MHz or 33MHz.
+		 */
+		if (idd->count_period/IOC4_EXTINT_COUNT_DIVISOR < 20) {
+			port->ip_pci_bus_speed = IOC4_SER_XIN_CLK_66;
+		} else {
+			port->ip_pci_bus_speed = IOC4_SER_XIN_CLK_33;
+		}
+		port->ip_baud = 9600;
+		port->ip_control = control;
+		port->ip_mem = ioc4_misc;
+		port->ip_serial = ioc4_serial;
+
+		/* point to the right hook */
+		port->ip_hooks = &hooks_array[port_number];
+
+		/* Get direct hooks to the serial regs and uart regs
+		 * for this port
+		 */
+		switch (port_number) {
+		case 0:
+			port->ip_serial_regs = &(port->ip_serial->port_0);
+			port->ip_uart_regs = &(port->ip_serial->uart_0);
+			break;
+		case 1:
+			port->ip_serial_regs = &(port->ip_serial->port_1);
+			port->ip_uart_regs = &(port->ip_serial->uart_1);
+			break;
+		case 2:
+			port->ip_serial_regs = &(port->ip_serial->port_2);
+			port->ip_uart_regs = &(port->ip_serial->uart_2);
+			break;
+		default:
+		case 3:
+			port->ip_serial_regs = &(port->ip_serial->port_3);
+			port->ip_uart_regs = &(port->ip_serial->uart_3);
+			break;
+		}
+
+		/* ring buffers are 1 to a pair of ports */
+		if (port_number && (port_number & 1)) {
+			/* odd use the evens buffer */
+			port->ip_dma_ringbuf =
+					ports[port_number - 1]->ip_dma_ringbuf;
+			port->ip_cpu_ringbuf =
+					ports[port_number - 1]->ip_cpu_ringbuf;
+			port->ip_inring = RING(port, RX_1_OR_3);
+			port->ip_outring = RING(port, TX_1_OR_3);
+
+		} else {
+			if (port->ip_dma_ringbuf == 0) {
+				port->ip_cpu_ringbuf = pci_alloc_consistent
+					(pdev, TOTAL_RING_BUF_SIZE,
+					&port->ip_dma_ringbuf);
+
+			}
+			BUG_ON(!((((int64_t)port->ip_dma_ringbuf) &
+				(TOTAL_RING_BUF_SIZE - 1)) == 0));
+			DPRINT_CONFIG(("%s : ip_cpu_ringbuf 0x%p "
+						"ip_dma_ringbuf 0x%p\n",
+					__func__,
+					(void *)port->ip_cpu_ringbuf,
+					(void *)port->ip_dma_ringbuf));
+			port->ip_inring = RING(port, RX_0_OR_2);
+			port->ip_outring = RING(port, TX_0_OR_2);
+		}
+		DPRINT_CONFIG(("%s : port %d [addr 0x%p] control 0x%p",
+				__func__,
+				port_number, (void *)port, (void *)control));
+		DPRINT_CONFIG((" ip_serial_regs 0x%p ip_uart_regs 0x%p\n",
+				(void *)port->ip_serial_regs,
+				(void *)port->ip_uart_regs));
+
+		/* Initialize the hardware for IOC4 */
+		port_init(port);
+
+		DPRINT_CONFIG(("%s: port_number %d port 0x%p inring 0x%p "
+						"outring 0x%p\n",
+				__func__,
+				port_number, (void *)port,
+				(void *)port->ip_inring,
+				(void *)port->ip_outring));
+
+		/* Attach interrupt handlers */
+		intr_connect(soft, IOC4_SIO_INTR_TYPE,
+				GET_SIO_IR(port_number),
+				handle_intr, port);
+
+		intr_connect(soft, IOC4_OTHER_INTR_TYPE,
+				GET_OTHER_IR(port_number),
+				handle_dma_error_intr, port);
+	}
+	return 0;
+}
+
+/**
+ * enable_intrs - enable interrupts
+ * @port: port to enable
+ * @mask: mask to use
+ */
+static void enable_intrs(struct ioc4_port *port, uint32_t mask)
+{
+	struct hooks *hooks = port->ip_hooks;
+
+	if ((port->ip_ienb & mask) != mask) {
+		write_ireg(port->ip_ioc4_soft, mask, IOC4_W_IES,
+						IOC4_SIO_INTR_TYPE);
+		port->ip_ienb |= mask;
+	}
+
+	if (port->ip_ienb)
+		write_ireg(port->ip_ioc4_soft, hooks->intr_dma_error,
+				IOC4_W_IES, IOC4_OTHER_INTR_TYPE);
+}
+
+/**
+ * local_open - local open a port
+ * @port: port to open
+ */
+static inline int local_open(struct ioc4_port *port)
+{
+	int spiniter = 0;
+
+	port->ip_flags = PORT_ACTIVE;
+
+	/* Pause the DMA interface if necessary */
+	if (port->ip_sscr & IOC4_SSCR_DMA_EN) {
+		writel(port->ip_sscr | IOC4_SSCR_DMA_PAUSE,
+			&port->ip_serial_regs->sscr);
+		while((readl(&port->ip_serial_regs-> sscr)
+				& IOC4_SSCR_PAUSE_STATE) == 0) {
+			spiniter++;
+			if (spiniter > MAXITER) {
+				port->ip_flags = PORT_INACTIVE;
+				return -1;
+			}
+		}
+	}
+
+	/* Reset the input fifo.  If the uart received chars while the port
+	 * was closed and DMA is not enabled, the uart may have a bunch of
+	 * chars hanging around in its rx fifo which will not be discarded
+	 * by rclr in the upper layer. We must get rid of them here.
+	 */
+	writeb(UART_FCR_ENABLE_FIFO | UART_FCR_CLEAR_RCVR,
+				&port->ip_uart_regs->i4u_fcr);
+
+	writeb(UART_LCR_WLEN8, &port->ip_uart_regs->i4u_lcr);
+					/* UART_LCR_STOP == 1 stop */
+
+	/* Re-enable DMA, set default threshold to intr whenever there is
+	 * data available.
+	 */
+	port->ip_sscr &= ~IOC4_SSCR_RX_THRESHOLD;
+	port->ip_sscr |= 1;	/* default threshold */
+
+	/* Plug in the new sscr.  This implicitly clears the DMA_PAUSE
+	 * flag if it was set above
+	 */
+	writel(port->ip_sscr, &port->ip_serial_regs->sscr);
+	port->ip_tx_lowat = 1;
+	return 0;
+}
+
+/**
+ * set_rx_timeout - Set rx timeout and threshold values.
+ * @port: port to use
+ * @timeout: timeout value in ticks
+ */
+static inline int set_rx_timeout(struct ioc4_port *port, int timeout)
+{
+	int threshold;
+
+	port->ip_rx_timeout = timeout;
+
+	/* Timeout is in ticks.  Let's figure out how many chars we
+	 * can receive at the current baud rate in that interval
+	 * and set the rx threshold to that amount.  There are 4 chars
+	 * per ring entry, so we'll divide the number of chars that will
+	 * arrive in timeout by 4.
+	 * So .... timeout * baud / 10 / HZ / 4, with HZ = 100.
+	 */
+	threshold = timeout * port->ip_baud / 4000;
+	if (threshold == 0)
+		threshold = 1;	/* otherwise we'll intr all the time! */
+
+	if ((unsigned)threshold > (unsigned)IOC4_SSCR_RX_THRESHOLD)
+		return 1;
+
+	port->ip_sscr &= ~IOC4_SSCR_RX_THRESHOLD;
+	port->ip_sscr |= threshold;
+
+	writel(port->ip_sscr, &port->ip_serial_regs->sscr);
+
+	/* Now set the rx timeout to the given value
+	 * again timeout * IOC4_SRTR_HZ / HZ
+	 */
+	timeout = timeout * IOC4_SRTR_HZ / 100;
+	if (timeout > IOC4_SRTR_CNT)
+		timeout = IOC4_SRTR_CNT;
+
+	writel(timeout, &port->ip_serial_regs->srtr);
+	return 0;
+}
+
+/**
+ * config_port - config the hardware
+ * @port: port to config
+ * @baud: baud rate for the port
+ * @byte_size: data size
+ * @stop_bits: number of stop bits
+ * @parenb: parity enable ?
+ * @parodd: odd parity ?
+ */
+static inline int
+config_port(struct ioc4_port *port,
+	    int baud, int byte_size, int stop_bits, int parenb, int parodd)
+{
+	char lcr, sizebits;
+	int spiniter = 0;
+
+	DPRINT_CONFIG(("%s: baud %d byte_size %d stop %d parenb %d parodd %d\n",
+		__func__, baud, byte_size, stop_bits, parenb, parodd));
+
+	if (set_baud(port, baud))
+		return 1;
+
+	switch (byte_size) {
+	case 5:
+		sizebits = UART_LCR_WLEN5;
+		break;
+	case 6:
+		sizebits = UART_LCR_WLEN6;
+		break;
+	case 7:
+		sizebits = UART_LCR_WLEN7;
+		break;
+	case 8:
+		sizebits = UART_LCR_WLEN8;
+		break;
+	default:
+		return 1;
+	}
+
+	/* Pause the DMA interface if necessary */
+	if (port->ip_sscr & IOC4_SSCR_DMA_EN) {
+		writel(port->ip_sscr | IOC4_SSCR_DMA_PAUSE,
+			&port->ip_serial_regs->sscr);
+		while((readl(&port->ip_serial_regs->sscr)
+						& IOC4_SSCR_PAUSE_STATE) == 0) {
+			spiniter++;
+			if (spiniter > MAXITER)
+				return -1;
+		}
+	}
+
+	/* Clear relevant fields in lcr */
+	lcr = readb(&port->ip_uart_regs->i4u_lcr);
+	lcr &= ~(LCR_MASK_BITS_CHAR | UART_LCR_EPAR |
+		 UART_LCR_PARITY | LCR_MASK_STOP_BITS);
+
+	/* Set byte size in lcr */
+	lcr |= sizebits;
+
+	/* Set parity */
+	if (parenb) {
+		lcr |= UART_LCR_PARITY;
+		if (!parodd)
+			lcr |= UART_LCR_EPAR;
+	}
+
+	/* Set stop bits */
+	if (stop_bits)
+		lcr |= UART_LCR_STOP /* 2 stop bits */ ;
+
+	writeb(lcr, &port->ip_uart_regs->i4u_lcr);
+
+	/* Re-enable the DMA interface if necessary */
+	if (port->ip_sscr & IOC4_SSCR_DMA_EN) {
+		writel(port->ip_sscr, &port->ip_serial_regs->sscr);
+	}
+	port->ip_baud = baud;
+
+	/* When we get within this number of ring entries of filling the
+	 * entire ring on tx, place an EXPLICIT intr to generate a lowat
+	 * notification when output has drained.
+	 */
+	port->ip_tx_lowat = (TX_LOWAT_CHARS(baud) + 3) / 4;
+	if (port->ip_tx_lowat == 0)
+		port->ip_tx_lowat = 1;
+
+	set_rx_timeout(port, 2);
+
+	return 0;
+}
+
+/**
+ * do_write - Write bytes to the port.  Returns the number of bytes
+ *			actually written. Called from transmit_chars
+ * @port: port to use
+ * @buf: the stuff to write
+ * @len: how many bytes in 'buf'
+ */
+static inline int do_write(struct ioc4_port *port, char *buf, int len)
+{
+	int prod_ptr, cons_ptr, total = 0;
+	struct ring *outring;
+	struct ring_entry *entry;
+	struct hooks *hooks = port->ip_hooks;
+
+	BUG_ON(!(len >= 0));
+
+	prod_ptr = port->ip_tx_prod;
+	cons_ptr = readl(&port->ip_serial_regs->stcir) & PROD_CONS_MASK;
+	outring = port->ip_outring;
+
+	/* Maintain a 1-entry red-zone.  The ring buffer is full when
+	 * (cons - prod) % ring_size is 1.  Rather than do this subtraction
+	 * in the body of the loop, I'll do it now.
+	 */
+	cons_ptr = (cons_ptr - (int)sizeof(struct ring_entry)) & PROD_CONS_MASK;
+
+	/* Stuff the bytes into the output */
+	while ((prod_ptr != cons_ptr) && (len > 0)) {
+		int xx;
+
+		/* Get 4 bytes (one ring entry) at a time */
+		entry = (struct ring_entry *)((caddr_t) outring + prod_ptr);
+
+		/* Invalidate all entries */
+		entry->ring_allsc = 0;
+
+		/* Copy in some bytes */
+		for (xx = 0; (xx < 4) && (len > 0); xx++) {
+			entry->ring_data[xx] = *buf++;
+			entry->ring_sc[xx] = IOC4_TXCB_VALID;
+			len--;
+			total++;
+		}
+
+		/* If we are within some small threshold of filling up the
+		 * entire ring buffer, we must place an EXPLICIT intr here
+		 * to generate a lowat interrupt in case we subsequently
+		 * really do fill up the ring and the caller goes to sleep.
+		 * No need to place more than one though.
+		 */
+		if (!(port->ip_flags & LOWAT_WRITTEN) &&
+			((cons_ptr - prod_ptr) & PROD_CONS_MASK)
+				<= port->ip_tx_lowat
+					* (int)sizeof(struct ring_entry)) {
+			port->ip_flags |= LOWAT_WRITTEN;
+			entry->ring_sc[0] |= IOC4_TXCB_INT_WHEN_DONE;
+		}
+
+		/* Go on to next entry */
+		prod_ptr += sizeof(struct ring_entry);
+		prod_ptr &= PROD_CONS_MASK;
+	}
+
+	/* If we sent something, start DMA if necessary */
+	if (total > 0 && !(port->ip_sscr & IOC4_SSCR_DMA_EN)) {
+		port->ip_sscr |= IOC4_SSCR_DMA_EN;
+		writel(port->ip_sscr, &port->ip_serial_regs->sscr);
+	}
+
+	/* Store the new producer pointer.  If tx is disabled, we stuff the
+	 * data into the ring buffer, but we don't actually start tx.
+	 */
+	if (!uart_tx_stopped(port->ip_port)) {
+		writel(prod_ptr, &port->ip_serial_regs->stpir);
+
+		/* If we are now transmitting, enable tx_mt interrupt so we
+		 * can disable DMA if necessary when the tx finishes.
+		 */
+		if (total > 0)
+			enable_intrs(port, hooks->intr_tx_mt);
+	}
+	port->ip_tx_prod = prod_ptr;
+	return total;
+}
+
+/**
+ * disable_intrs - disable interrupts
+ * @port: port to enable
+ * @mask: mask to use
+ */
+static void disable_intrs(struct ioc4_port *port, uint32_t mask)
+{
+	struct hooks *hooks = port->ip_hooks;
+
+	if (port->ip_ienb & mask) {
+		write_ireg(port->ip_ioc4_soft, mask, IOC4_W_IEC,
+					IOC4_SIO_INTR_TYPE);
+		port->ip_ienb &= ~mask;
+	}
+
+	if (!port->ip_ienb)
+		write_ireg(port->ip_ioc4_soft, hooks->intr_dma_error,
+				IOC4_W_IEC, IOC4_OTHER_INTR_TYPE);
+}
+
+/**
+ * set_notification - Modify event notification
+ * @port: port to use
+ * @mask: events mask
+ * @set_on: set ?
+ */
+static int set_notification(struct ioc4_port *port, int mask, int set_on)
+{
+	struct hooks *hooks = port->ip_hooks;
+	uint32_t intrbits, sscrbits;
+
+	BUG_ON(!mask);
+
+	intrbits = sscrbits = 0;
+
+	if (mask & N_DATA_READY)
+		intrbits |= (hooks->intr_rx_timer | hooks->intr_rx_high);
+	if (mask & N_OUTPUT_LOWAT)
+		intrbits |= hooks->intr_tx_explicit;
+	if (mask & N_DDCD) {
+		intrbits |= hooks->intr_delta_dcd;
+		sscrbits |= IOC4_SSCR_RX_RING_DCD;
+	}
+	if (mask & N_DCTS)
+		intrbits |= hooks->intr_delta_cts;
+
+	if (set_on) {
+		enable_intrs(port, intrbits);
+		port->ip_notify |= mask;
+		port->ip_sscr |= sscrbits;
+	} else {
+		disable_intrs(port, intrbits);
+		port->ip_notify &= ~mask;
+		port->ip_sscr &= ~sscrbits;
+	}
+
+	/* We require DMA if either DATA_READY or DDCD notification is
+	 * currently requested. If neither of these is requested and
+	 * there is currently no tx in progress, DMA may be disabled.
+	 */
+	if (port->ip_notify & (N_DATA_READY | N_DDCD))
+		port->ip_sscr |= IOC4_SSCR_DMA_EN;
+	else if (!(port->ip_ienb & hooks->intr_tx_mt))
+		port->ip_sscr &= ~IOC4_SSCR_DMA_EN;
+
+	writel(port->ip_sscr, &port->ip_serial_regs->sscr);
+	return 0;
+}
+
+/**
+ * set_mcr - set the master control reg
+ * @the_port: port to use
+ * @mask1: mcr mask
+ * @mask2: shadow mask
+ */
+static inline int set_mcr(struct uart_port *the_port,
+		int mask1, int mask2)
+{
+	struct ioc4_port *port = get_ioc4_port(the_port, 0);
+	uint32_t shadow;
+	int spiniter = 0;
+	char mcr;
+
+	if (!port)
+		return -1;
+
+	/* Pause the DMA interface if necessary */
+	if (port->ip_sscr & IOC4_SSCR_DMA_EN) {
+		writel(port->ip_sscr | IOC4_SSCR_DMA_PAUSE,
+			&port->ip_serial_regs->sscr);
+		while ((readl(&port->ip_serial_regs->sscr)
+					& IOC4_SSCR_PAUSE_STATE) == 0) {
+			spiniter++;
+			if (spiniter > MAXITER)
+				return -1;
+		}
+	}
+	shadow = readl(&port->ip_serial_regs->shadow);
+	mcr = (shadow & 0xff000000) >> 24;
+
+	/* Set new value */
+	mcr |= mask1;
+	shadow |= mask2;
+
+	writeb(mcr, &port->ip_uart_regs->i4u_mcr);
+	writel(shadow, &port->ip_serial_regs->shadow);
+
+	/* Re-enable the DMA interface if necessary */
+	if (port->ip_sscr & IOC4_SSCR_DMA_EN) {
+		writel(port->ip_sscr, &port->ip_serial_regs->sscr);
+	}
+	return 0;
+}
+
+/**
+ * ioc4_set_proto - set the protocol for the port
+ * @port: port to use
+ * @proto: protocol to use
+ */
+static int ioc4_set_proto(struct ioc4_port *port, int proto)
+{
+	struct hooks *hooks = port->ip_hooks;
+
+	switch (proto) {
+	case PROTO_RS232:
+		/* Clear the appropriate GIO pin */
+		writel(0, (&port->ip_mem->gppr[hooks->rs422_select_pin].raw));
+		break;
+
+	case PROTO_RS422:
+		/* Set the appropriate GIO pin */
+		writel(1, (&port->ip_mem->gppr[hooks->rs422_select_pin].raw));
+		break;
+
+	default:
+		return 1;
+	}
+	return 0;
+}
+
+/**
+ * transmit_chars - upper level write, called with ip_lock
+ * @the_port: port to write
+ */
+static void transmit_chars(struct uart_port *the_port)
+{
+	int xmit_count, tail, head;
+	int result;
+	char *start;
+	struct tty_struct *tty;
+	struct ioc4_port *port = get_ioc4_port(the_port, 0);
+	struct uart_state *state;
+
+	if (!the_port)
+		return;
+	if (!port)
+		return;
+
+	state = the_port->state;
+	tty = state->port.tty;
+
+	if (uart_circ_empty(&state->xmit) || uart_tx_stopped(the_port)) {
+		/* Nothing to do or hw stopped */
+		set_notification(port, N_ALL_OUTPUT, 0);
+		return;
+	}
+
+	head = state->xmit.head;
+	tail = state->xmit.tail;
+	start = (char *)&state->xmit.buf[tail];
+
+	/* write out all the data or until the end of the buffer */
+	xmit_count = (head < tail) ? (UART_XMIT_SIZE - tail) : (head - tail);
+	if (xmit_count > 0) {
+		result = do_write(port, start, xmit_count);
+		if (result > 0) {
+			/* booking */
+			xmit_count -= result;
+			the_port->icount.tx += result;
+			/* advance the pointers */
+			tail += result;
+			tail &= UART_XMIT_SIZE - 1;
+			state->xmit.tail = tail;
+			start = (char *)&state->xmit.buf[tail];
+		}
+	}
+	if (uart_circ_chars_pending(&state->xmit) < WAKEUP_CHARS)
+		uart_write_wakeup(the_port);
+
+	if (uart_circ_empty(&state->xmit)) {
+		set_notification(port, N_OUTPUT_LOWAT, 0);
+	} else {
+		set_notification(port, N_OUTPUT_LOWAT, 1);
+	}
+}
+
+/**
+ * ioc4_change_speed - change the speed of the port
+ * @the_port: port to change
+ * @new_termios: new termios settings
+ * @old_termios: old termios settings
+ */
+static void
+ioc4_change_speed(struct uart_port *the_port,
+		  struct ktermios *new_termios, struct ktermios *old_termios)
+{
+	struct ioc4_port *port = get_ioc4_port(the_port, 0);
+	int baud, bits;
+	unsigned cflag, iflag;
+	int new_parity = 0, new_parity_enable = 0, new_stop = 0, new_data = 8;
+	struct uart_state *state = the_port->state;
+
+	cflag = new_termios->c_cflag;
+	iflag = new_termios->c_iflag;
+
+	switch (cflag & CSIZE) {
+	case CS5:
+		new_data = 5;
+		bits = 7;
+		break;
+	case CS6:
+		new_data = 6;
+		bits = 8;
+		break;
+	case CS7:
+		new_data = 7;
+		bits = 9;
+		break;
+	case CS8:
+		new_data = 8;
+		bits = 10;
+		break;
+	default:
+		/* cuz we always need a default ... */
+		new_data = 5;
+		bits = 7;
+		break;
+	}
+	if (cflag & CSTOPB) {
+		bits++;
+		new_stop = 1;
+	}
+	if (cflag & PARENB) {
+		bits++;
+		new_parity_enable = 1;
+		if (cflag & PARODD)
+			new_parity = 1;
+	}
+	baud = uart_get_baud_rate(the_port, new_termios, old_termios,
+				MIN_BAUD_SUPPORTED, MAX_BAUD_SUPPORTED);
+	DPRINT_CONFIG(("%s: returned baud %d\n", __func__, baud));
+
+	/* default is 9600 */
+	if (!baud)
+		baud = 9600;
+
+	if (!the_port->fifosize)
+		the_port->fifosize = IOC4_FIFO_CHARS;
+	the_port->timeout = ((the_port->fifosize * HZ * bits) / (baud / 10));
+	the_port->timeout += HZ / 50;	/* Add .02 seconds of slop */
+
+	the_port->ignore_status_mask = N_ALL_INPUT;
+
+	state->port.tty->low_latency = 1;
+
+	if (iflag & IGNPAR)
+		the_port->ignore_status_mask &= ~(N_PARITY_ERROR
+						| N_FRAMING_ERROR);
+	if (iflag & IGNBRK) {
+		the_port->ignore_status_mask &= ~N_BREAK;
+		if (iflag & IGNPAR)
+			the_port->ignore_status_mask &= ~N_OVERRUN_ERROR;
+	}
+	if (!(cflag & CREAD)) {
+		/* ignore everything */
+		the_port->ignore_status_mask &= ~N_DATA_READY;
+	}
+
+	if (cflag & CRTSCTS) {
+		port->ip_sscr |= IOC4_SSCR_HFC_EN;
+	}
+	else {
+		port->ip_sscr &= ~IOC4_SSCR_HFC_EN;
+	}
+	writel(port->ip_sscr, &port->ip_serial_regs->sscr);
+
+	/* Set the configuration and proper notification call */
+	DPRINT_CONFIG(("%s : port 0x%p cflag 0%o "
+		"config_port(baud %d data %d stop %d p enable %d parity %d),"
+		" notification 0x%x\n",
+	     __func__, (void *)port, cflag, baud, new_data, new_stop,
+	     new_parity_enable, new_parity, the_port->ignore_status_mask));
+
+	if ((config_port(port, baud,		/* baud */
+			 new_data,		/* byte size */
+			 new_stop,		/* stop bits */
+			 new_parity_enable,	/* set parity */
+			 new_parity)) >= 0) {	/* parity 1==odd */
+		set_notification(port, the_port->ignore_status_mask, 1);
+	}
+}
+
+/**
+ * ic4_startup_local - Start up the serial port - returns >= 0 if no errors
+ * @the_port: Port to operate on
+ */
+static inline int ic4_startup_local(struct uart_port *the_port)
+{
+	struct ioc4_port *port;
+	struct uart_state *state;
+
+	if (!the_port)
+		return -1;
+
+	port = get_ioc4_port(the_port, 0);
+	if (!port)
+		return -1;
+
+	state = the_port->state;
+
+	local_open(port);
+
+	/* set the protocol - mapbase has the port type */
+	ioc4_set_proto(port, the_port->mapbase);
+
+	/* set the speed of the serial port */
+	ioc4_change_speed(the_port, state->port.tty->termios,
+			  (struct ktermios *)0);
+
+	return 0;
+}
+
+/*
+ * ioc4_cb_output_lowat - called when the output low water mark is hit
+ * @the_port: port to output
+ */
+static void ioc4_cb_output_lowat(struct uart_port *the_port)
+{
+	unsigned long pflags;
+
+	/* ip_lock is set on the call here */
+	if (the_port) {
+		spin_lock_irqsave(&the_port->lock, pflags);
+		transmit_chars(the_port);
+		spin_unlock_irqrestore(&the_port->lock, pflags);
+	}
+}
+
+/**
+ * handle_intr - service any interrupts for the given port - 2nd level
+ *			called via sd_intr
+ * @arg: handler arg
+ * @sio_ir: ioc4regs
+ */
+static void handle_intr(void *arg, uint32_t sio_ir)
+{
+	struct ioc4_port *port = (struct ioc4_port *)arg;
+	struct hooks *hooks = port->ip_hooks;
+	unsigned int rx_high_rd_aborted = 0;
+	unsigned long flags;
+	struct uart_port *the_port;
+	int loop_counter;
+
+	/* Possible race condition here: The tx_mt interrupt bit may be
+	 * cleared without the intervention of the interrupt handler,
+	 * e.g. by a write.  If the top level interrupt handler reads a
+	 * tx_mt, then some other processor does a write, starting up
+	 * output, then we come in here, see the tx_mt and stop DMA, the
+	 * output started by the other processor will hang.  Thus we can
+	 * only rely on tx_mt being legitimate if it is read while the
+	 * port lock is held.  Therefore this bit must be ignored in the
+	 * passed in interrupt mask which was read by the top level
+	 * interrupt handler since the port lock was not held at the time
+	 * it was read.  We can only rely on this bit being accurate if it
+	 * is read while the port lock is held.  So we'll clear it for now,
+	 * and reload it later once we have the port lock.
+	 */
+	sio_ir &= ~(hooks->intr_tx_mt);
+
+	spin_lock_irqsave(&port->ip_lock, flags);
+
+	loop_counter = MAXITER;	/* to avoid hangs */
+
+	do {
+		uint32_t shadow;
+
+		if ( loop_counter-- <= 0 ) {
+			printk(KERN_WARNING "IOC4 serial: "
+					"possible hang condition/"
+					"port stuck on interrupt.\n");
+			break;
+		}
+
+		/* Handle a DCD change */
+		if (sio_ir & hooks->intr_delta_dcd) {
+			/* ACK the interrupt */
+			writel(hooks->intr_delta_dcd,
+				&port->ip_mem->sio_ir.raw);
+
+			shadow = readl(&port->ip_serial_regs->shadow);
+
+			if ((port->ip_notify & N_DDCD)
+					&& (shadow & IOC4_SHADOW_DCD)
+					&& (port->ip_port)) {
+				the_port = port->ip_port;
+				the_port->icount.dcd = 1;
+				wake_up_interruptible
+					    (&the_port->state->port.delta_msr_wait);
+			} else if ((port->ip_notify & N_DDCD)
+					&& !(shadow & IOC4_SHADOW_DCD)) {
+				/* Flag delta DCD/no DCD */
+				port->ip_flags |= DCD_ON;
+			}
+		}
+
+		/* Handle a CTS change */
+		if (sio_ir & hooks->intr_delta_cts) {
+			/* ACK the interrupt */
+			writel(hooks->intr_delta_cts,
+					&port->ip_mem->sio_ir.raw);
+
+			shadow = readl(&port->ip_serial_regs->shadow);
+
+			if ((port->ip_notify & N_DCTS)
+					&& (port->ip_port)) {
+				the_port = port->ip_port;
+				the_port->icount.cts =
+					(shadow & IOC4_SHADOW_CTS) ? 1 : 0;
+				wake_up_interruptible
+					(&the_port->state->port.delta_msr_wait);
+			}
+		}
+
+		/* rx timeout interrupt.  Must be some data available.  Put this
+		 * before the check for rx_high since servicing this condition
+		 * may cause that condition to clear.
+		 */
+		if (sio_ir & hooks->intr_rx_timer) {
+			/* ACK the interrupt */
+			writel(hooks->intr_rx_timer,
+				&port->ip_mem->sio_ir.raw);
+
+			if ((port->ip_notify & N_DATA_READY)
+					&& (port->ip_port)) {
+				/* ip_lock is set on call here */
+				receive_chars(port->ip_port);
+			}
+		}
+
+		/* rx high interrupt. Must be after rx_timer.  */
+		else if (sio_ir & hooks->intr_rx_high) {
+			/* Data available, notify upper layer */
+			if ((port->ip_notify & N_DATA_READY)
+						&& port->ip_port) {
+				/* ip_lock is set on call here */
+				receive_chars(port->ip_port);
+			}
+
+			/* We can't ACK this interrupt.  If receive_chars didn't
+			 * cause the condition to clear, we'll have to disable
+			 * the interrupt until the data is drained.
+			 * If the read was aborted, don't disable the interrupt
+			 * as this may cause us to hang indefinitely.  An
+			 * aborted read generally means that this interrupt
+			 * hasn't been delivered to the cpu yet anyway, even
+			 * though we see it as asserted when we read the sio_ir.
+			 */
+			if ((sio_ir = PENDING(port)) & hooks->intr_rx_high) {
+				if ((port->ip_flags & READ_ABORTED) == 0) {
+					port->ip_ienb &= ~hooks->intr_rx_high;
+					port->ip_flags |= INPUT_HIGH;
+				} else {
+					rx_high_rd_aborted++;
+				}
+			}
+		}
+
+		/* We got a low water interrupt: notify upper layer to
+		 * send more data.  Must come before tx_mt since servicing
+		 * this condition may cause that condition to clear.
+		 */
+		if (sio_ir & hooks->intr_tx_explicit) {
+			port->ip_flags &= ~LOWAT_WRITTEN;
+
+			/* ACK the interrupt */
+			writel(hooks->intr_tx_explicit,
+					&port->ip_mem->sio_ir.raw);
+
+			if (port->ip_notify & N_OUTPUT_LOWAT)
+				ioc4_cb_output_lowat(port->ip_port);
+		}
+
+		/* Handle tx_mt.  Must come after tx_explicit.  */
+		else if (sio_ir & hooks->intr_tx_mt) {
+			/* If we are expecting a lowat notification
+			 * and we get to this point it probably means that for
+			 * some reason the tx_explicit didn't work as expected
+			 * (that can legitimately happen if the output buffer is
+			 * filled up in just the right way).
+			 * So send the notification now.
+			 */
+			if (port->ip_notify & N_OUTPUT_LOWAT) {
+				ioc4_cb_output_lowat(port->ip_port);
+
+				/* We need to reload the sio_ir since the lowat
+				 * call may have caused another write to occur,
+				 * clearing the tx_mt condition.
+				 */
+				sio_ir = PENDING(port);
+			}
+
+			/* If the tx_mt condition still persists even after the
+			 * lowat call, we've got some work to do.
+			 */
+			if (sio_ir & hooks->intr_tx_mt) {
+
+				/* If we are not currently expecting DMA input,
+				 * and the transmitter has just gone idle,
+				 * there is no longer any reason for DMA, so
+				 * disable it.
+				 */
+				if (!(port->ip_notify
+						& (N_DATA_READY | N_DDCD))) {
+					BUG_ON(!(port->ip_sscr
+							& IOC4_SSCR_DMA_EN));
+					port->ip_sscr &= ~IOC4_SSCR_DMA_EN;
+					writel(port->ip_sscr,
+					   &port->ip_serial_regs->sscr);
+				}
+
+				/* Prevent infinite tx_mt interrupt */
+				port->ip_ienb &= ~hooks->intr_tx_mt;
+			}
+		}
+		sio_ir = PENDING(port);
+
+		/* if the read was aborted and only hooks->intr_rx_high,
+		 * clear hooks->intr_rx_high, so we do not loop forever.
+		 */
+
+		if (rx_high_rd_aborted && (sio_ir == hooks->intr_rx_high)) {
+			sio_ir &= ~hooks->intr_rx_high;
+		}
+	} while (sio_ir & hooks->intr_all);
+
+	spin_unlock_irqrestore(&port->ip_lock, flags);
+
+	/* Re-enable interrupts before returning from interrupt handler.
+	 * Getting interrupted here is okay.  It'll just v() our semaphore, and
+	 * we'll come through the loop again.
+	 */
+
+	write_ireg(port->ip_ioc4_soft, port->ip_ienb, IOC4_W_IES,
+							IOC4_SIO_INTR_TYPE);
+}
+
+/*
+ * ioc4_cb_post_ncs - called for some basic errors
+ * @port: port to use
+ * @ncs: event
+ */
+static void ioc4_cb_post_ncs(struct uart_port *the_port, int ncs)
+{
+	struct uart_icount *icount;
+
+	icount = &the_port->icount;
+
+	if (ncs & NCS_BREAK)
+		icount->brk++;
+	if (ncs & NCS_FRAMING)
+		icount->frame++;
+	if (ncs & NCS_OVERRUN)
+		icount->overrun++;
+	if (ncs & NCS_PARITY)
+		icount->parity++;
+}
+
+/**
+ * do_read - Read in bytes from the port.  Return the number of bytes
+ *			actually read.
+ * @the_port: port to use
+ * @buf: place to put the stuff we read
+ * @len: how big 'buf' is
+ */
+
+static inline int do_read(struct uart_port *the_port, unsigned char *buf,
+				int len)
+{
+	int prod_ptr, cons_ptr, total;
+	struct ioc4_port *port = get_ioc4_port(the_port, 0);
+	struct ring *inring;
+	struct ring_entry *entry;
+	struct hooks *hooks = port->ip_hooks;
+	int byte_num;
+	char *sc;
+	int loop_counter;
+
+	BUG_ON(!(len >= 0));
+	BUG_ON(!port);
+
+	/* There is a nasty timing issue in the IOC4. When the rx_timer
+	 * expires or the rx_high condition arises, we take an interrupt.
+	 * At some point while servicing the interrupt, we read bytes from
+	 * the ring buffer and re-arm the rx_timer.  However the rx_timer is
+	 * not started until the first byte is received *after* it is armed,
+	 * and any bytes pending in the rx construction buffers are not drained
+	 * to memory until either there are 4 bytes available or the rx_timer
+	 * expires.  This leads to a potential situation where data is left
+	 * in the construction buffers forever - 1 to 3 bytes were received
+	 * after the interrupt was generated but before the rx_timer was
+	 * re-armed. At that point as long as no subsequent bytes are received
+	 * the timer will never be started and the bytes will remain in the
+	 * construction buffer forever.  The solution is to execute a DRAIN
+	 * command after rearming the timer.  This way any bytes received before
+	 * the DRAIN will be drained to memory, and any bytes received after
+	 * the DRAIN will start the TIMER and be drained when it expires.
+	 * Luckily, this only needs to be done when the DMA buffer is empty
+	 * since there is no requirement that this function return all
+	 * available data as long as it returns some.
+	 */
+	/* Re-arm the timer */
+	writel(port->ip_rx_cons | IOC4_SRCIR_ARM, &port->ip_serial_regs->srcir);
+
+	prod_ptr = readl(&port->ip_serial_regs->srpir) & PROD_CONS_MASK;
+	cons_ptr = port->ip_rx_cons;
+
+	if (prod_ptr == cons_ptr) {
+		int reset_dma = 0;
+
+		/* Input buffer appears empty, do a flush. */
+
+		/* DMA must be enabled for this to work. */
+		if (!(port->ip_sscr & IOC4_SSCR_DMA_EN)) {
+			port->ip_sscr |= IOC4_SSCR_DMA_EN;
+			reset_dma = 1;
+		}
+
+		/* Potential race condition: we must reload the srpir after
+		 * issuing the drain command, otherwise we could think the rx
+		 * buffer is empty, then take a very long interrupt, and when
+		 * we come back it's full and we wait forever for the drain to
+		 * complete.
+		 */
+		writel(port->ip_sscr | IOC4_SSCR_RX_DRAIN,
+				&port->ip_serial_regs->sscr);
+		prod_ptr = readl(&port->ip_serial_regs->srpir)
+				& PROD_CONS_MASK;
+
+		/* We must not wait for the DRAIN to complete unless there are
+		 * at least 8 bytes (2 ring entries) available to receive the
+		 * data otherwise the DRAIN will never complete and we'll
+		 * deadlock here.
+		 * In fact, to make things easier, I'll just ignore the flush if
+		 * there is any data at all now available.
+		 */
+		if (prod_ptr == cons_ptr) {
+			loop_counter = 0;
+			while (readl(&port->ip_serial_regs->sscr) &
+						IOC4_SSCR_RX_DRAIN) {
+				loop_counter++;
+				if (loop_counter > MAXITER)
+					return -1;
+			}
+
+			/* SIGH. We have to reload the prod_ptr *again* since
+			 * the drain may have caused it to change
+			 */
+			prod_ptr = readl(&port->ip_serial_regs->srpir)
+							& PROD_CONS_MASK;
+		}
+		if (reset_dma) {
+			port->ip_sscr &= ~IOC4_SSCR_DMA_EN;
+			writel(port->ip_sscr, &port->ip_serial_regs->sscr);
+		}
+	}
+	inring = port->ip_inring;
+	port->ip_flags &= ~READ_ABORTED;
+
+	total = 0;
+	loop_counter = 0xfffff;	/* to avoid hangs */
+
+	/* Grab bytes from the hardware */
+	while ((prod_ptr != cons_ptr) && (len > 0)) {
+		entry = (struct ring_entry *)((caddr_t)inring + cons_ptr);
+
+		if ( loop_counter-- <= 0 ) {
+			printk(KERN_WARNING "IOC4 serial: "
+					"possible hang condition/"
+					"port stuck on read.\n");
+			break;
+		}
+
+		/* According to the producer pointer, this ring entry
+		 * must contain some data.  But if the PIO happened faster
+		 * than the DMA, the data may not be available yet, so let's
+		 * wait until it arrives.
+		 */
+		if ((entry->ring_allsc & RING_ANY_VALID) == 0) {
+			/* Indicate the read is aborted so we don't disable
+			 * the interrupt thinking that the consumer is
+			 * congested.
+			 */
+			port->ip_flags |= READ_ABORTED;
+			len = 0;
+			break;
+		}
+
+		/* Load the bytes/status out of the ring entry */
+		for (byte_num = 0; byte_num < 4 && len > 0; byte_num++) {
+			sc = &(entry->ring_sc[byte_num]);
+
+			/* Check for change in modem state or overrun */
+			if ((*sc & IOC4_RXSB_MODEM_VALID)
+						&& (port->ip_notify & N_DDCD)) {
+				/* Notify upper layer if DCD dropped */
+
+				if ((port->ip_flags & DCD_ON)
+						&& !(*sc & IOC4_RXSB_DCD)) {
+
+					/* If we have already copied some data,
+					 * return it.  We'll pick up the carrier
+					 * drop on the next pass.  That way we
+					 * don't throw away the data that has
+					 * already been copied back to
+					 * the caller's buffer.
+					 */
+					if (total > 0) {
+						len = 0;
+						break;
+					}
+					port->ip_flags &= ~DCD_ON;
+
+					/* Turn off this notification so the
+					 * carrier drop protocol won't see it
+					 * again when it does a read.
+					 */
+					*sc &= ~IOC4_RXSB_MODEM_VALID;
+
+					/* To keep things consistent, we need
+					 * to update the consumer pointer so
+					 * the next reader won't come in and
+					 * try to read the same ring entries
+					 * again. This must be done here before
+					 * the dcd change.
+					 */
+
+					if ((entry->ring_allsc & RING_ANY_VALID)
+									== 0) {
+						cons_ptr += (int)sizeof
+							(struct ring_entry);
+						cons_ptr &= PROD_CONS_MASK;
+					}
+					writel(cons_ptr,
+						&port->ip_serial_regs->srcir);
+					port->ip_rx_cons = cons_ptr;
+
+					/* Notify upper layer of carrier drop */
+					if ((port->ip_notify & N_DDCD)
+						   && port->ip_port) {
+						the_port->icount.dcd = 0;
+						wake_up_interruptible
+						    (&the_port->state->
+							port.delta_msr_wait);
+					}
+
+					/* If we had any data to return, we
+					 * would have returned it above.
+					 */
+					return 0;
+				}
+			}
+			if (*sc & IOC4_RXSB_MODEM_VALID) {
+				/* Notify that an input overrun occurred */
+				if ((*sc & IOC4_RXSB_OVERRUN)
+				    && (port->ip_notify & N_OVERRUN_ERROR)) {
+					ioc4_cb_post_ncs(the_port, NCS_OVERRUN);
+				}
+				/* Don't look at this byte again */
+				*sc &= ~IOC4_RXSB_MODEM_VALID;
+			}
+
+			/* Check for valid data or RX errors */
+			if ((*sc & IOC4_RXSB_DATA_VALID) &&
+					((*sc & (IOC4_RXSB_PAR_ERR
+							| IOC4_RXSB_FRAME_ERR
+							| IOC4_RXSB_BREAK))
+					&& (port->ip_notify & (N_PARITY_ERROR
+							| N_FRAMING_ERROR
+							| N_BREAK)))) {
+				/* There is an error condition on the next byte.
+				 * If we have already transferred some bytes,
+				 * we'll stop here. Otherwise if this is the
+				 * first byte to be read, we'll just transfer
+				 * it alone after notifying the
+				 * upper layer of its status.
+				 */
+				if (total > 0) {
+					len = 0;
+					break;
+				} else {
+					if ((*sc & IOC4_RXSB_PAR_ERR) &&
+					   (port->ip_notify & N_PARITY_ERROR)) {
+						ioc4_cb_post_ncs(the_port,
+								NCS_PARITY);
+					}
+					if ((*sc & IOC4_RXSB_FRAME_ERR) &&
+					   (port->ip_notify & N_FRAMING_ERROR)){
+						ioc4_cb_post_ncs(the_port,
+								NCS_FRAMING);
+					}
+					if ((*sc & IOC4_RXSB_BREAK)
+					    && (port->ip_notify & N_BREAK)) {
+							ioc4_cb_post_ncs
+								    (the_port,
+								     NCS_BREAK);
+					}
+					len = 1;
+				}
+			}
+			if (*sc & IOC4_RXSB_DATA_VALID) {
+				*sc &= ~IOC4_RXSB_DATA_VALID;
+				*buf = entry->ring_data[byte_num];
+				buf++;
+				len--;
+				total++;
+			}
+		}
+
+		/* If we used up this entry entirely, go on to the next one,
+		 * otherwise we must have run out of buffer space, so
+		 * leave the consumer pointer here for the next read in case
+		 * there are still unread bytes in this entry.
+		 */
+		if ((entry->ring_allsc & RING_ANY_VALID) == 0) {
+			cons_ptr += (int)sizeof(struct ring_entry);
+			cons_ptr &= PROD_CONS_MASK;
+		}
+	}
+
+	/* Update consumer pointer and re-arm rx timer interrupt */
+	writel(cons_ptr, &port->ip_serial_regs->srcir);
+	port->ip_rx_cons = cons_ptr;
+
+	/* If we have now dipped below the rx high water mark and we have
+	 * rx_high interrupt turned off, we can now turn it back on again.
+	 */
+	if ((port->ip_flags & INPUT_HIGH) && (((prod_ptr - cons_ptr)
+			& PROD_CONS_MASK) < ((port->ip_sscr &
+				IOC4_SSCR_RX_THRESHOLD)
+					<< IOC4_PROD_CONS_PTR_OFF))) {
+		port->ip_flags &= ~INPUT_HIGH;
+		enable_intrs(port, hooks->intr_rx_high);
+	}
+	return total;
+}
+
+/**
+ * receive_chars - upper level read. Called with ip_lock.
+ * @the_port: port to read from
+ */
+static void receive_chars(struct uart_port *the_port)
+{
+	struct tty_struct *tty;
+	unsigned char ch[IOC4_MAX_CHARS];
+	int read_count, request_count = IOC4_MAX_CHARS;
+	struct uart_icount *icount;
+	struct uart_state *state = the_port->state;
+	unsigned long pflags;
+
+	/* Make sure all the pointers are "good" ones */
+	if (!state)
+		return;
+	if (!state->port.tty)
+		return;
+
+	spin_lock_irqsave(&the_port->lock, pflags);
+	tty = state->port.tty;
+
+	request_count = tty_buffer_request_room(tty, IOC4_MAX_CHARS);
+
+	if (request_count > 0) {
+		icount = &the_port->icount;
+		read_count = do_read(the_port, ch, request_count);
+		if (read_count > 0) {
+			tty_insert_flip_string(tty, ch, read_count);
+			icount->rx += read_count;
+		}
+	}
+
+	spin_unlock_irqrestore(&the_port->lock, pflags);
+
+	tty_flip_buffer_push(tty);
+}
+
+/**
+ * ic4_type - What type of console are we?
+ * @port: Port to operate with (we ignore since we only have one port)
+ *
+ */
+static const char *ic4_type(struct uart_port *the_port)
+{
+	if (the_port->mapbase == PROTO_RS232)
+		return "SGI IOC4 Serial [rs232]";
+	else
+		return "SGI IOC4 Serial [rs422]";
+}
+
+/**
+ * ic4_tx_empty - Is the transmitter empty?
+ * @port: Port to operate on
+ *
+ */
+static unsigned int ic4_tx_empty(struct uart_port *the_port)
+{
+	struct ioc4_port *port = get_ioc4_port(the_port, 0);
+	unsigned int ret = 0;
+
+	if (port_is_active(port, the_port)) {
+		if (readl(&port->ip_serial_regs->shadow) & IOC4_SHADOW_TEMT)
+			ret = TIOCSER_TEMT;
+	}
+	return ret;
+}
+
+/**
+ * ic4_stop_tx - stop the transmitter
+ * @port: Port to operate on
+ *
+ */
+static void ic4_stop_tx(struct uart_port *the_port)
+{
+	struct ioc4_port *port = get_ioc4_port(the_port, 0);
+
+	if (port_is_active(port, the_port))
+		set_notification(port, N_OUTPUT_LOWAT, 0);
+}
+
+/**
+ * null_void_function -
+ * @port: Port to operate on
+ *
+ */
+static void null_void_function(struct uart_port *the_port)
+{
+}
+
+/**
+ * ic4_shutdown - shut down the port - free irq and disable
+ * @port: Port to shut down
+ *
+ */
+static void ic4_shutdown(struct uart_port *the_port)
+{
+	unsigned long port_flags;
+	struct ioc4_port *port;
+	struct uart_state *state;
+
+	port = get_ioc4_port(the_port, 0);
+	if (!port)
+		return;
+
+	state = the_port->state;
+	port->ip_port = NULL;
+
+	wake_up_interruptible(&state->port.delta_msr_wait);
+
+	if (state->port.tty)
+		set_bit(TTY_IO_ERROR, &state->port.tty->flags);
+
+	spin_lock_irqsave(&the_port->lock, port_flags);
+	set_notification(port, N_ALL, 0);
+	port->ip_flags = PORT_INACTIVE;
+	spin_unlock_irqrestore(&the_port->lock, port_flags);
+}
+
+/**
+ * ic4_set_mctrl - set control lines (dtr, rts, etc)
+ * @port: Port to operate on
+ * @mctrl: Lines to set/unset
+ *
+ */
+static void ic4_set_mctrl(struct uart_port *the_port, unsigned int mctrl)
+{
+	unsigned char mcr = 0;
+	struct ioc4_port *port;
+
+	port = get_ioc4_port(the_port, 0);
+	if (!port_is_active(port, the_port))
+		return;
+
+	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;
+
+	set_mcr(the_port, mcr, IOC4_SHADOW_DTR);
+}
+
+/**
+ * ic4_get_mctrl - get control line info
+ * @port: port to operate on
+ *
+ */
+static unsigned int ic4_get_mctrl(struct uart_port *the_port)
+{
+	struct ioc4_port *port = get_ioc4_port(the_port, 0);
+	uint32_t shadow;
+	unsigned int ret = 0;
+
+	if (!port_is_active(port, the_port))
+		return 0;
+
+	shadow = readl(&port->ip_serial_regs->shadow);
+	if (shadow & IOC4_SHADOW_DCD)
+		ret |= TIOCM_CAR;
+	if (shadow & IOC4_SHADOW_DR)
+		ret |= TIOCM_DSR;
+	if (shadow & IOC4_SHADOW_CTS)
+		ret |= TIOCM_CTS;
+	return ret;
+}
+
+/**
+ * ic4_start_tx - Start transmitter, flush any output
+ * @port: Port to operate on
+ *
+ */
+static void ic4_start_tx(struct uart_port *the_port)
+{
+	struct ioc4_port *port = get_ioc4_port(the_port, 0);
+
+	if (port_is_active(port, the_port)) {
+		set_notification(port, N_OUTPUT_LOWAT, 1);
+		enable_intrs(port, port->ip_hooks->intr_tx_mt);
+	}
+}
+
+/**
+ * ic4_break_ctl - handle breaks
+ * @port: Port to operate on
+ * @break_state: Break state
+ *
+ */
+static void ic4_break_ctl(struct uart_port *the_port, int break_state)
+{
+}
+
+/**
+ * ic4_startup - Start up the serial port
+ * @port: Port to operate on
+ *
+ */
+static int ic4_startup(struct uart_port *the_port)
+{
+	int retval;
+	struct ioc4_port *port;
+	struct ioc4_control *control;
+	struct uart_state *state;
+	unsigned long port_flags;
+
+	if (!the_port)
+		return -ENODEV;
+	port = get_ioc4_port(the_port, 1);
+	if (!port)
+		return -ENODEV;
+	state = the_port->state;
+
+	control = port->ip_control;
+	if (!control) {
+		port->ip_port = NULL;
+		return -ENODEV;
+	}
+
+	/* Start up the serial port */
+	spin_lock_irqsave(&the_port->lock, port_flags);
+	retval = ic4_startup_local(the_port);
+	spin_unlock_irqrestore(&the_port->lock, port_flags);
+	return retval;
+}
+
+/**
+ * ic4_set_termios - set termios stuff
+ * @port: port to operate on
+ * @termios: New settings
+ * @termios: Old
+ *
+ */
+static void
+ic4_set_termios(struct uart_port *the_port,
+		struct ktermios *termios, struct ktermios *old_termios)
+{
+	unsigned long port_flags;
+
+	spin_lock_irqsave(&the_port->lock, port_flags);
+	ioc4_change_speed(the_port, termios, old_termios);
+	spin_unlock_irqrestore(&the_port->lock, port_flags);
+}
+
+/**
+ * ic4_request_port - allocate resources for port - no op....
+ * @port: port to operate on
+ *
+ */
+static int ic4_request_port(struct uart_port *port)
+{
+	return 0;
+}
+
+/* Associate the uart functions above - given to serial core */
+
+static struct uart_ops ioc4_ops = {
+	.tx_empty	= ic4_tx_empty,
+	.set_mctrl	= ic4_set_mctrl,
+	.get_mctrl	= ic4_get_mctrl,
+	.stop_tx	= ic4_stop_tx,
+	.start_tx	= ic4_start_tx,
+	.stop_rx	= null_void_function,
+	.enable_ms	= null_void_function,
+	.break_ctl	= ic4_break_ctl,
+	.startup	= ic4_startup,
+	.shutdown	= ic4_shutdown,
+	.set_termios	= ic4_set_termios,
+	.type		= ic4_type,
+	.release_port	= null_void_function,
+	.request_port	= ic4_request_port,
+};
+
+/*
+ * Boot-time initialization code
+ */
+
+static struct uart_driver ioc4_uart_rs232 = {
+	.owner		= THIS_MODULE,
+	.driver_name	= "ioc4_serial_rs232",
+	.dev_name	= DEVICE_NAME_RS232,
+	.major		= DEVICE_MAJOR,
+	.minor		= DEVICE_MINOR_RS232,
+	.nr		= IOC4_NUM_CARDS * IOC4_NUM_SERIAL_PORTS,
+};
+
+static struct uart_driver ioc4_uart_rs422 = {
+	.owner		= THIS_MODULE,
+	.driver_name	= "ioc4_serial_rs422",
+	.dev_name	= DEVICE_NAME_RS422,
+	.major		= DEVICE_MAJOR,
+	.minor		= DEVICE_MINOR_RS422,
+	.nr		= IOC4_NUM_CARDS * IOC4_NUM_SERIAL_PORTS,
+};
+
+
+/**
+ * ioc4_serial_remove_one - detach function
+ *
+ * @idd: IOC4 master module data for this IOC4
+ */
+
+static int ioc4_serial_remove_one(struct ioc4_driver_data *idd)
+{
+	int port_num, port_type;
+	struct ioc4_control *control;
+	struct uart_port *the_port;
+	struct ioc4_port *port;
+	struct ioc4_soft *soft;
+
+	/* If serial driver did not attach, don't try to detach */
+	control = idd->idd_serial_data;
+	if (!control)
+		return 0;
+
+	for (port_num = 0; port_num < IOC4_NUM_SERIAL_PORTS; port_num++) {
+		for (port_type = UART_PORT_MIN;
+					port_type < UART_PORT_COUNT;
+					port_type++) {
+			the_port = &control->ic_port[port_num].icp_uart_port
+							[port_type];
+			if (the_port) {
+				switch (port_type) {
+				case UART_PORT_RS422:
+					uart_remove_one_port(&ioc4_uart_rs422,
+							the_port);
+					break;
+				default:
+				case UART_PORT_RS232:
+					uart_remove_one_port(&ioc4_uart_rs232,
+							the_port);
+					break;
+				}
+			}
+		}
+		port = control->ic_port[port_num].icp_port;
+		/* we allocate in pairs */
+		if (!(port_num & 1) && port) {
+			pci_free_consistent(port->ip_pdev,
+					TOTAL_RING_BUF_SIZE,
+					port->ip_cpu_ringbuf,
+					port->ip_dma_ringbuf);
+			kfree(port);
+		}
+	}
+	soft = control->ic_soft;
+	if (soft) {
+		free_irq(control->ic_irq, soft);
+		if (soft->is_ioc4_serial_addr) {
+			iounmap(soft->is_ioc4_serial_addr);
+			release_mem_region((unsigned long)
+			     soft->is_ioc4_serial_addr,
+				sizeof(struct ioc4_serial));
+		}
+		kfree(soft);
+	}
+	kfree(control);
+	idd->idd_serial_data = NULL;
+
+	return 0;
+}
+
+
+/**
+ * ioc4_serial_core_attach_rs232 - register with serial core
+ *		This is done during pci probing
+ * @pdev: handle for this card
+ */
+static inline int
+ioc4_serial_core_attach(struct pci_dev *pdev, int port_type)
+{
+	struct ioc4_port *port;
+	struct uart_port *the_port;
+	struct ioc4_driver_data *idd = pci_get_drvdata(pdev);
+	struct ioc4_control *control = idd->idd_serial_data;
+	int port_num;
+	int port_type_idx;
+	struct uart_driver *u_driver;
+
+
+	DPRINT_CONFIG(("%s: attach pdev 0x%p - control 0x%p\n",
+			__func__, pdev, (void *)control));
+
+	if (!control)
+		return -ENODEV;
+
+	port_type_idx = (port_type == PROTO_RS232) ? UART_PORT_RS232
+						: UART_PORT_RS422;
+
+	u_driver = (port_type == PROTO_RS232)	? &ioc4_uart_rs232
+						: &ioc4_uart_rs422;
+
+	/* once around for each port on this card */
+	for (port_num = 0; port_num < IOC4_NUM_SERIAL_PORTS; port_num++) {
+		the_port = &control->ic_port[port_num].icp_uart_port
+							[port_type_idx];
+		port = control->ic_port[port_num].icp_port;
+		port->ip_all_ports[port_type_idx] = the_port;
+
+		DPRINT_CONFIG(("%s: attach the_port 0x%p / port 0x%p : type %s\n",
+				__func__, (void *)the_port,
+				(void *)port,
+				port_type == PROTO_RS232 ? "rs232" : "rs422"));
+
+		/* membase, iobase and mapbase just need to be non-0 */
+		the_port->membase = (unsigned char __iomem *)1;
+		the_port->iobase = (pdev->bus->number << 16) |  port_num;
+		the_port->line = (Num_of_ioc4_cards << 2) | port_num;
+		the_port->mapbase = port_type;
+		the_port->type = PORT_16550A;
+		the_port->fifosize = IOC4_FIFO_CHARS;
+		the_port->ops = &ioc4_ops;
+		the_port->irq = control->ic_irq;
+		the_port->dev = &pdev->dev;
+		spin_lock_init(&the_port->lock);
+		if (uart_add_one_port(u_driver, the_port) < 0) {
+			printk(KERN_WARNING
+		           "%s: unable to add port %d bus %d\n",
+			       __func__, the_port->line, pdev->bus->number);
+		} else {
+			DPRINT_CONFIG(
+			    ("IOC4 serial port %d irq = %d, bus %d\n",
+			       the_port->line, the_port->irq, pdev->bus->number));
+		}
+	}
+	return 0;
+}
+
+/**
+ * ioc4_serial_attach_one - register attach function
+ *		called per card found from IOC4 master module.
+ * @idd: Master module data for this IOC4
+ */
+int
+ioc4_serial_attach_one(struct ioc4_driver_data *idd)
+{
+	unsigned long tmp_addr1;
+	struct ioc4_serial __iomem *serial;
+	struct ioc4_soft *soft;
+	struct ioc4_control *control;
+	int ret = 0;
+
+
+	DPRINT_CONFIG(("%s (0x%p, 0x%p)\n", __func__, idd->idd_pdev,
+							idd->idd_pci_id));
+
+	/* PCI-RT does not bring out serial connections.
+	 * Do not attach to this particular IOC4.
+	 */
+	if (idd->idd_variant == IOC4_VARIANT_PCI_RT)
+		return 0;
+
+	/* request serial registers */
+	tmp_addr1 = idd->idd_bar0 + IOC4_SERIAL_OFFSET;
+
+	if (!request_mem_region(tmp_addr1, sizeof(struct ioc4_serial),
+					"sioc4_uart")) {
+		printk(KERN_WARNING
+			"ioc4 (%p): unable to get request region for "
+				"uart space\n", (void *)idd->idd_pdev);
+		ret = -ENODEV;
+		goto out1;
+	}
+	serial = ioremap(tmp_addr1, sizeof(struct ioc4_serial));
+	if (!serial) {
+		printk(KERN_WARNING
+			 "ioc4 (%p) : unable to remap ioc4 serial register\n",
+				(void *)idd->idd_pdev);
+		ret = -ENODEV;
+		goto out2;
+	}
+	DPRINT_CONFIG(("%s : mem 0x%p, serial 0x%p\n",
+				__func__, (void *)idd->idd_misc_regs,
+				(void *)serial));
+
+	/* Get memory for the new card */
+	control = kzalloc(sizeof(struct ioc4_control), GFP_KERNEL);
+
+	if (!control) {
+		printk(KERN_WARNING "ioc4_attach_one"
+		       ": unable to get memory for the IOC4\n");
+		ret = -ENOMEM;
+		goto out2;
+	}
+	idd->idd_serial_data = control;
+
+	/* Allocate the soft structure */
+	soft = kzalloc(sizeof(struct ioc4_soft), GFP_KERNEL);
+	if (!soft) {
+		printk(KERN_WARNING
+		       "ioc4 (%p): unable to get memory for the soft struct\n",
+		       (void *)idd->idd_pdev);
+		ret = -ENOMEM;
+		goto out3;
+	}
+
+	spin_lock_init(&soft->is_ir_lock);
+	soft->is_ioc4_misc_addr = idd->idd_misc_regs;
+	soft->is_ioc4_serial_addr = serial;
+
+	/* Init the IOC4 */
+	writel(0xf << IOC4_SIO_CR_CMD_PULSE_SHIFT,
+	       &idd->idd_misc_regs->sio_cr.raw);
+
+	/* Enable serial port mode select generic PIO pins as outputs */
+	writel(IOC4_GPCR_UART0_MODESEL | IOC4_GPCR_UART1_MODESEL
+		| IOC4_GPCR_UART2_MODESEL | IOC4_GPCR_UART3_MODESEL,
+		&idd->idd_misc_regs->gpcr_s.raw);
+
+	/* Clear and disable all serial interrupts */
+	write_ireg(soft, ~0, IOC4_W_IEC, IOC4_SIO_INTR_TYPE);
+	writel(~0, &idd->idd_misc_regs->sio_ir.raw);
+	write_ireg(soft, IOC4_OTHER_IR_SER_MEMERR, IOC4_W_IEC,
+		   IOC4_OTHER_INTR_TYPE);
+	writel(IOC4_OTHER_IR_SER_MEMERR, &idd->idd_misc_regs->other_ir.raw);
+	control->ic_soft = soft;
+
+	/* Hook up interrupt handler */
+	if (!request_irq(idd->idd_pdev->irq, ioc4_intr, IRQF_SHARED,
+				"sgi-ioc4serial", soft)) {
+		control->ic_irq = idd->idd_pdev->irq;
+	} else {
+		printk(KERN_WARNING
+		    "%s : request_irq fails for IRQ 0x%x\n ",
+			__func__, idd->idd_pdev->irq);
+	}
+	ret = ioc4_attach_local(idd);
+	if (ret)
+		goto out4;
+
+	/* register port with the serial core - 1 rs232, 1 rs422 */
+
+	if ((ret = ioc4_serial_core_attach(idd->idd_pdev, PROTO_RS232)))
+		goto out4;
+
+	if ((ret = ioc4_serial_core_attach(idd->idd_pdev, PROTO_RS422)))
+		goto out5;
+
+	Num_of_ioc4_cards++;
+
+	return ret;
+
+	/* error exits that give back resources */
+out5:
+	ioc4_serial_remove_one(idd);
+out4:
+	kfree(soft);
+out3:
+	kfree(control);
+out2:
+	if (serial)
+		iounmap(serial);
+	release_mem_region(tmp_addr1, sizeof(struct ioc4_serial));
+out1:
+
+	return ret;
+}
+
+
+static struct ioc4_submodule ioc4_serial_submodule = {
+	.is_name = "IOC4_serial",
+	.is_owner = THIS_MODULE,
+	.is_probe = ioc4_serial_attach_one,
+	.is_remove = ioc4_serial_remove_one,
+};
+
+/**
+ * ioc4_serial_init - module init
+ */
+static int __init ioc4_serial_init(void)
+{
+	int ret;
+
+	/* register with serial core */
+	if ((ret = uart_register_driver(&ioc4_uart_rs232)) < 0) {
+		printk(KERN_WARNING
+			"%s: Couldn't register rs232 IOC4 serial driver\n",
+			__func__);
+		goto out;
+	}
+	if ((ret = uart_register_driver(&ioc4_uart_rs422)) < 0) {
+		printk(KERN_WARNING
+			"%s: Couldn't register rs422 IOC4 serial driver\n",
+			__func__);
+		goto out_uart_rs232;
+	}
+
+	/* register with IOC4 main module */
+	ret = ioc4_register_submodule(&ioc4_serial_submodule);
+	if (ret)
+		goto out_uart_rs422;
+	return 0;
+
+out_uart_rs422:
+	uart_unregister_driver(&ioc4_uart_rs422);
+out_uart_rs232:
+	uart_unregister_driver(&ioc4_uart_rs232);
+out:
+	return ret;
+}
+
+static void __exit ioc4_serial_exit(void)
+{
+	ioc4_unregister_submodule(&ioc4_serial_submodule);
+	uart_unregister_driver(&ioc4_uart_rs232);
+	uart_unregister_driver(&ioc4_uart_rs422);
+}
+
+late_initcall(ioc4_serial_init); /* Call only after tty init is done */
+module_exit(ioc4_serial_exit);
+
+MODULE_AUTHOR("Pat Gefre - Silicon Graphics Inc. (SGI) <pfg@sgi.com>");
+MODULE_DESCRIPTION("Serial PCI driver module for SGI IOC4 Base-IO Card");
+MODULE_LICENSE("GPL");
diff --git a/ap/os/linux/linux-3.4.x/drivers/tty/serial/ip22zilog.c b/ap/os/linux/linux-3.4.x/drivers/tty/serial/ip22zilog.c
new file mode 100644
index 0000000..7b1cda5
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/tty/serial/ip22zilog.c
@@ -0,0 +1,1221 @@
+/*
+ * Driver for Zilog serial chips found on SGI workstations and
+ * servers.  This driver could actually be made more generic.
+ *
+ * This is based on the drivers/serial/sunzilog.c code as of 2.6.0-test7 and the
+ * old drivers/sgi/char/sgiserial.c code which itself is based of the original
+ * drivers/sbus/char/zs.c code.  A lot of code has been simply moved over
+ * directly from there but much has been rewritten.  Credits therefore go out
+ * to David S. Miller, Eddie C. Dost, Pete Zaitcev, Ted Ts'o and Alex Buell
+ * for their work there.
+ *
+ *  Copyright (C) 2002 Ralf Baechle (ralf@linux-mips.org)
+ *  Copyright (C) 2002 David S. Miller (davem@redhat.com)
+ */
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/delay.h>
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
+#include <linux/major.h>
+#include <linux/string.h>
+#include <linux/ptrace.h>
+#include <linux/ioport.h>
+#include <linux/slab.h>
+#include <linux/circ_buf.h>
+#include <linux/serial.h>
+#include <linux/sysrq.h>
+#include <linux/console.h>
+#include <linux/spinlock.h>
+#include <linux/init.h>
+
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/sgialib.h>
+#include <asm/sgi/ioc.h>
+#include <asm/sgi/hpc3.h>
+#include <asm/sgi/ip22.h>
+
+#if defined(CONFIG_SERIAL_IP22_ZILOG_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ)
+#define SUPPORT_SYSRQ
+#endif
+
+#include <linux/serial_core.h>
+
+#include "ip22zilog.h"
+
+/*
+ * On IP22 we need to delay after register accesses but we do not need to
+ * flush writes.
+ */
+#define ZSDELAY()		udelay(5)
+#define ZSDELAY_LONG()		udelay(20)
+#define ZS_WSYNC(channel)	do { } while (0)
+
+#define NUM_IP22ZILOG		1
+#define NUM_CHANNELS		(NUM_IP22ZILOG * 2)
+
+#define ZS_CLOCK		3672000	/* Zilog input clock rate. */
+#define ZS_CLOCK_DIVISOR	16      /* Divisor this driver uses. */
+
+/*
+ * We wrap our port structure around the generic uart_port.
+ */
+struct uart_ip22zilog_port {
+	struct uart_port		port;
+
+	/* IRQ servicing chain.  */
+	struct uart_ip22zilog_port	*next;
+
+	/* Current values of Zilog write registers.  */
+	unsigned char			curregs[NUM_ZSREGS];
+
+	unsigned int			flags;
+#define IP22ZILOG_FLAG_IS_CONS		0x00000004
+#define IP22ZILOG_FLAG_IS_KGDB		0x00000008
+#define IP22ZILOG_FLAG_MODEM_STATUS	0x00000010
+#define IP22ZILOG_FLAG_IS_CHANNEL_A	0x00000020
+#define IP22ZILOG_FLAG_REGS_HELD	0x00000040
+#define IP22ZILOG_FLAG_TX_STOPPED	0x00000080
+#define IP22ZILOG_FLAG_TX_ACTIVE	0x00000100
+#define IP22ZILOG_FLAG_RESET_DONE	0x00000200
+
+	unsigned int			tty_break;
+
+	unsigned char			parity_mask;
+	unsigned char			prev_status;
+};
+
+#define ZILOG_CHANNEL_FROM_PORT(PORT)	((struct zilog_channel *)((PORT)->membase))
+#define UART_ZILOG(PORT)		((struct uart_ip22zilog_port *)(PORT))
+#define IP22ZILOG_GET_CURR_REG(PORT, REGNUM)		\
+	(UART_ZILOG(PORT)->curregs[REGNUM])
+#define IP22ZILOG_SET_CURR_REG(PORT, REGNUM, REGVAL)	\
+	((UART_ZILOG(PORT)->curregs[REGNUM]) = (REGVAL))
+#define ZS_IS_CONS(UP)	((UP)->flags & IP22ZILOG_FLAG_IS_CONS)
+#define ZS_IS_KGDB(UP)	((UP)->flags & IP22ZILOG_FLAG_IS_KGDB)
+#define ZS_WANTS_MODEM_STATUS(UP)	((UP)->flags & IP22ZILOG_FLAG_MODEM_STATUS)
+#define ZS_IS_CHANNEL_A(UP)	((UP)->flags & IP22ZILOG_FLAG_IS_CHANNEL_A)
+#define ZS_REGS_HELD(UP)	((UP)->flags & IP22ZILOG_FLAG_REGS_HELD)
+#define ZS_TX_STOPPED(UP)	((UP)->flags & IP22ZILOG_FLAG_TX_STOPPED)
+#define ZS_TX_ACTIVE(UP)	((UP)->flags & IP22ZILOG_FLAG_TX_ACTIVE)
+
+/* Reading and writing Zilog8530 registers.  The delays are to make this
+ * driver work on the IP22 which needs a settling delay after each chip
+ * register access, other machines handle this in hardware via auxiliary
+ * flip-flops which implement the settle time we do in software.
+ *
+ * The port lock must be held and local IRQs must be disabled
+ * when {read,write}_zsreg is invoked.
+ */
+static unsigned char read_zsreg(struct zilog_channel *channel,
+				unsigned char reg)
+{
+	unsigned char retval;
+
+	writeb(reg, &channel->control);
+	ZSDELAY();
+	retval = readb(&channel->control);
+	ZSDELAY();
+
+	return retval;
+}
+
+static void write_zsreg(struct zilog_channel *channel,
+			unsigned char reg, unsigned char value)
+{
+	writeb(reg, &channel->control);
+	ZSDELAY();
+	writeb(value, &channel->control);
+	ZSDELAY();
+}
+
+static void ip22zilog_clear_fifo(struct zilog_channel *channel)
+{
+	int i;
+
+	for (i = 0; i < 32; i++) {
+		unsigned char regval;
+
+		regval = readb(&channel->control);
+		ZSDELAY();
+		if (regval & Rx_CH_AV)
+			break;
+
+		regval = read_zsreg(channel, R1);
+		readb(&channel->data);
+		ZSDELAY();
+
+		if (regval & (PAR_ERR | Rx_OVR | CRC_ERR)) {
+			writeb(ERR_RES, &channel->control);
+			ZSDELAY();
+			ZS_WSYNC(channel);
+		}
+	}
+}
+
+/* This function must only be called when the TX is not busy.  The UART
+ * port lock must be held and local interrupts disabled.
+ */
+static void __load_zsregs(struct zilog_channel *channel, unsigned char *regs)
+{
+	int i;
+
+	/* Let pending transmits finish.  */
+	for (i = 0; i < 1000; i++) {
+		unsigned char stat = read_zsreg(channel, R1);
+		if (stat & ALL_SNT)
+			break;
+		udelay(100);
+	}
+
+	writeb(ERR_RES, &channel->control);
+	ZSDELAY();
+	ZS_WSYNC(channel);
+
+	ip22zilog_clear_fifo(channel);
+
+	/* Disable all interrupts.  */
+	write_zsreg(channel, R1,
+		    regs[R1] & ~(RxINT_MASK | TxINT_ENAB | EXT_INT_ENAB));
+
+	/* Set parity, sync config, stop bits, and clock divisor.  */
+	write_zsreg(channel, R4, regs[R4]);
+
+	/* Set misc. TX/RX control bits.  */
+	write_zsreg(channel, R10, regs[R10]);
+
+	/* Set TX/RX controls sans the enable bits.  */
+	write_zsreg(channel, R3, regs[R3] & ~RxENAB);
+	write_zsreg(channel, R5, regs[R5] & ~TxENAB);
+
+	/* Synchronous mode config.  */
+	write_zsreg(channel, R6, regs[R6]);
+	write_zsreg(channel, R7, regs[R7]);
+
+	/* Don't mess with the interrupt vector (R2, unused by us) and
+	 * master interrupt control (R9).  We make sure this is setup
+	 * properly at probe time then never touch it again.
+	 */
+
+	/* Disable baud generator.  */
+	write_zsreg(channel, R14, regs[R14] & ~BRENAB);
+
+	/* Clock mode control.  */
+	write_zsreg(channel, R11, regs[R11]);
+
+	/* Lower and upper byte of baud rate generator divisor.  */
+	write_zsreg(channel, R12, regs[R12]);
+	write_zsreg(channel, R13, regs[R13]);
+
+	/* Now rewrite R14, with BRENAB (if set).  */
+	write_zsreg(channel, R14, regs[R14]);
+
+	/* External status interrupt control.  */
+	write_zsreg(channel, R15, regs[R15]);
+
+	/* Reset external status interrupts.  */
+	write_zsreg(channel, R0, RES_EXT_INT);
+	write_zsreg(channel, R0, RES_EXT_INT);
+
+	/* Rewrite R3/R5, this time without enables masked.  */
+	write_zsreg(channel, R3, regs[R3]);
+	write_zsreg(channel, R5, regs[R5]);
+
+	/* Rewrite R1, this time without IRQ enabled masked.  */
+	write_zsreg(channel, R1, regs[R1]);
+}
+
+/* Reprogram the Zilog channel HW registers with the copies found in the
+ * software state struct.  If the transmitter is busy, we defer this update
+ * until the next TX complete interrupt.  Else, we do it right now.
+ *
+ * The UART port lock must be held and local interrupts disabled.
+ */
+static void ip22zilog_maybe_update_regs(struct uart_ip22zilog_port *up,
+				       struct zilog_channel *channel)
+{
+	if (!ZS_REGS_HELD(up)) {
+		if (ZS_TX_ACTIVE(up)) {
+			up->flags |= IP22ZILOG_FLAG_REGS_HELD;
+		} else {
+			__load_zsregs(channel, up->curregs);
+		}
+	}
+}
+
+#define Rx_BRK 0x0100                   /* BREAK event software flag.  */
+#define Rx_SYS 0x0200                   /* SysRq event software flag.  */
+
+static struct tty_struct *ip22zilog_receive_chars(struct uart_ip22zilog_port *up,
+						  struct zilog_channel *channel)
+{
+	struct tty_struct *tty;
+	unsigned char ch, flag;
+	unsigned int r1;
+
+	tty = NULL;
+	if (up->port.state != NULL &&
+	    up->port.state->port.tty != NULL)
+		tty = up->port.state->port.tty;
+
+	for (;;) {
+		ch = readb(&channel->control);
+		ZSDELAY();
+		if (!(ch & Rx_CH_AV))
+			break;
+
+		r1 = read_zsreg(channel, R1);
+		if (r1 & (PAR_ERR | Rx_OVR | CRC_ERR)) {
+			writeb(ERR_RES, &channel->control);
+			ZSDELAY();
+			ZS_WSYNC(channel);
+		}
+
+		ch = readb(&channel->data);
+		ZSDELAY();
+
+		ch &= up->parity_mask;
+
+		/* Handle the null char got when BREAK is removed.  */
+		if (!ch)
+			r1 |= up->tty_break;
+
+		/* A real serial line, record the character and status.  */
+		flag = TTY_NORMAL;
+		up->port.icount.rx++;
+		if (r1 & (PAR_ERR | Rx_OVR | CRC_ERR | Rx_SYS | Rx_BRK)) {
+			up->tty_break = 0;
+
+			if (r1 & (Rx_SYS | Rx_BRK)) {
+				up->port.icount.brk++;
+				if (r1 & Rx_SYS)
+					continue;
+				r1 &= ~(PAR_ERR | CRC_ERR);
+			}
+			else if (r1 & PAR_ERR)
+				up->port.icount.parity++;
+			else if (r1 & CRC_ERR)
+				up->port.icount.frame++;
+			if (r1 & Rx_OVR)
+				up->port.icount.overrun++;
+			r1 &= up->port.read_status_mask;
+			if (r1 & Rx_BRK)
+				flag = TTY_BREAK;
+			else if (r1 & PAR_ERR)
+				flag = TTY_PARITY;
+			else if (r1 & CRC_ERR)
+				flag = TTY_FRAME;
+		}
+
+		if (uart_handle_sysrq_char(&up->port, ch))
+			continue;
+
+		if (tty)
+			uart_insert_char(&up->port, r1, Rx_OVR, ch, flag);
+	}
+	return tty;
+}
+
+static void ip22zilog_status_handle(struct uart_ip22zilog_port *up,
+				   struct zilog_channel *channel)
+{
+	unsigned char status;
+
+	status = readb(&channel->control);
+	ZSDELAY();
+
+	writeb(RES_EXT_INT, &channel->control);
+	ZSDELAY();
+	ZS_WSYNC(channel);
+
+	if (up->curregs[R15] & BRKIE) {
+		if ((status & BRK_ABRT) && !(up->prev_status & BRK_ABRT)) {
+			if (uart_handle_break(&up->port))
+				up->tty_break = Rx_SYS;
+			else
+				up->tty_break = Rx_BRK;
+		}
+	}
+
+	if (ZS_WANTS_MODEM_STATUS(up)) {
+		if (status & SYNC)
+			up->port.icount.dsr++;
+
+		/* The Zilog just gives us an interrupt when DCD/CTS/etc. change.
+		 * But it does not tell us which bit has changed, we have to keep
+		 * track of this ourselves.
+		 */
+		if ((status ^ up->prev_status) ^ DCD)
+			uart_handle_dcd_change(&up->port,
+					       (status & DCD));
+		if ((status ^ up->prev_status) ^ CTS)
+			uart_handle_cts_change(&up->port,
+					       (status & CTS));
+
+		wake_up_interruptible(&up->port.state->port.delta_msr_wait);
+	}
+
+	up->prev_status = status;
+}
+
+static void ip22zilog_transmit_chars(struct uart_ip22zilog_port *up,
+				    struct zilog_channel *channel)
+{
+	struct circ_buf *xmit;
+
+	if (ZS_IS_CONS(up)) {
+		unsigned char status = readb(&channel->control);
+		ZSDELAY();
+
+		/* TX still busy?  Just wait for the next TX done interrupt.
+		 *
+		 * It can occur because of how we do serial console writes.  It would
+		 * be nice to transmit console writes just like we normally would for
+		 * a TTY line. (ie. buffered and TX interrupt driven).  That is not
+		 * easy because console writes cannot sleep.  One solution might be
+		 * to poll on enough port->xmit space becoming free.  -DaveM
+		 */
+		if (!(status & Tx_BUF_EMP))
+			return;
+	}
+
+	up->flags &= ~IP22ZILOG_FLAG_TX_ACTIVE;
+
+	if (ZS_REGS_HELD(up)) {
+		__load_zsregs(channel, up->curregs);
+		up->flags &= ~IP22ZILOG_FLAG_REGS_HELD;
+	}
+
+	if (ZS_TX_STOPPED(up)) {
+		up->flags &= ~IP22ZILOG_FLAG_TX_STOPPED;
+		goto ack_tx_int;
+	}
+
+	if (up->port.x_char) {
+		up->flags |= IP22ZILOG_FLAG_TX_ACTIVE;
+		writeb(up->port.x_char, &channel->data);
+		ZSDELAY();
+		ZS_WSYNC(channel);
+
+		up->port.icount.tx++;
+		up->port.x_char = 0;
+		return;
+	}
+
+	if (up->port.state == NULL)
+		goto ack_tx_int;
+	xmit = &up->port.state->xmit;
+	if (uart_circ_empty(xmit))
+		goto ack_tx_int;
+	if (uart_tx_stopped(&up->port))
+		goto ack_tx_int;
+
+	up->flags |= IP22ZILOG_FLAG_TX_ACTIVE;
+	writeb(xmit->buf[xmit->tail], &channel->data);
+	ZSDELAY();
+	ZS_WSYNC(channel);
+
+	xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
+	up->port.icount.tx++;
+
+	if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
+		uart_write_wakeup(&up->port);
+
+	return;
+
+ack_tx_int:
+	writeb(RES_Tx_P, &channel->control);
+	ZSDELAY();
+	ZS_WSYNC(channel);
+}
+
+static irqreturn_t ip22zilog_interrupt(int irq, void *dev_id)
+{
+	struct uart_ip22zilog_port *up = dev_id;
+
+	while (up) {
+		struct zilog_channel *channel
+			= ZILOG_CHANNEL_FROM_PORT(&up->port);
+		struct tty_struct *tty;
+		unsigned char r3;
+
+		spin_lock(&up->port.lock);
+		r3 = read_zsreg(channel, R3);
+
+		/* Channel A */
+		tty = NULL;
+		if (r3 & (CHAEXT | CHATxIP | CHARxIP)) {
+			writeb(RES_H_IUS, &channel->control);
+			ZSDELAY();
+			ZS_WSYNC(channel);
+
+			if (r3 & CHARxIP)
+				tty = ip22zilog_receive_chars(up, channel);
+			if (r3 & CHAEXT)
+				ip22zilog_status_handle(up, channel);
+			if (r3 & CHATxIP)
+				ip22zilog_transmit_chars(up, channel);
+		}
+		spin_unlock(&up->port.lock);
+
+		if (tty)
+			tty_flip_buffer_push(tty);
+
+		/* Channel B */
+		up = up->next;
+		channel = ZILOG_CHANNEL_FROM_PORT(&up->port);
+
+		spin_lock(&up->port.lock);
+		tty = NULL;
+		if (r3 & (CHBEXT | CHBTxIP | CHBRxIP)) {
+			writeb(RES_H_IUS, &channel->control);
+			ZSDELAY();
+			ZS_WSYNC(channel);
+
+			if (r3 & CHBRxIP)
+				tty = ip22zilog_receive_chars(up, channel);
+			if (r3 & CHBEXT)
+				ip22zilog_status_handle(up, channel);
+			if (r3 & CHBTxIP)
+				ip22zilog_transmit_chars(up, channel);
+		}
+		spin_unlock(&up->port.lock);
+
+		if (tty)
+			tty_flip_buffer_push(tty);
+
+		up = up->next;
+	}
+
+	return IRQ_HANDLED;
+}
+
+/* A convenient way to quickly get R0 status.  The caller must _not_ hold the
+ * port lock, it is acquired here.
+ */
+static __inline__ unsigned char ip22zilog_read_channel_status(struct uart_port *port)
+{
+	struct zilog_channel *channel;
+	unsigned char status;
+
+	channel = ZILOG_CHANNEL_FROM_PORT(port);
+	status = readb(&channel->control);
+	ZSDELAY();
+
+	return status;
+}
+
+/* The port lock is not held.  */
+static unsigned int ip22zilog_tx_empty(struct uart_port *port)
+{
+	unsigned long flags;
+	unsigned char status;
+	unsigned int ret;
+
+	spin_lock_irqsave(&port->lock, flags);
+
+	status = ip22zilog_read_channel_status(port);
+
+	spin_unlock_irqrestore(&port->lock, flags);
+
+	if (status & Tx_BUF_EMP)
+		ret = TIOCSER_TEMT;
+	else
+		ret = 0;
+
+	return ret;
+}
+
+/* The port lock is held and interrupts are disabled.  */
+static unsigned int ip22zilog_get_mctrl(struct uart_port *port)
+{
+	unsigned char status;
+	unsigned int ret;
+
+	status = ip22zilog_read_channel_status(port);
+
+	ret = 0;
+	if (status & DCD)
+		ret |= TIOCM_CAR;
+	if (status & SYNC)
+		ret |= TIOCM_DSR;
+	if (status & CTS)
+		ret |= TIOCM_CTS;
+
+	return ret;
+}
+
+/* The port lock is held and interrupts are disabled.  */
+static void ip22zilog_set_mctrl(struct uart_port *port, unsigned int mctrl)
+{
+	struct uart_ip22zilog_port *up = (struct uart_ip22zilog_port *) port;
+	struct zilog_channel *channel = ZILOG_CHANNEL_FROM_PORT(port);
+	unsigned char set_bits, clear_bits;
+
+	set_bits = clear_bits = 0;
+
+	if (mctrl & TIOCM_RTS)
+		set_bits |= RTS;
+	else
+		clear_bits |= RTS;
+	if (mctrl & TIOCM_DTR)
+		set_bits |= DTR;
+	else
+		clear_bits |= DTR;
+
+	/* NOTE: Not subject to 'transmitter active' rule.  */
+	up->curregs[R5] |= set_bits;
+	up->curregs[R5] &= ~clear_bits;
+	write_zsreg(channel, R5, up->curregs[R5]);
+}
+
+/* The port lock is held and interrupts are disabled.  */
+static void ip22zilog_stop_tx(struct uart_port *port)
+{
+	struct uart_ip22zilog_port *up = (struct uart_ip22zilog_port *) port;
+
+	up->flags |= IP22ZILOG_FLAG_TX_STOPPED;
+}
+
+/* The port lock is held and interrupts are disabled.  */
+static void ip22zilog_start_tx(struct uart_port *port)
+{
+	struct uart_ip22zilog_port *up = (struct uart_ip22zilog_port *) port;
+	struct zilog_channel *channel = ZILOG_CHANNEL_FROM_PORT(port);
+	unsigned char status;
+
+	up->flags |= IP22ZILOG_FLAG_TX_ACTIVE;
+	up->flags &= ~IP22ZILOG_FLAG_TX_STOPPED;
+
+	status = readb(&channel->control);
+	ZSDELAY();
+
+	/* TX busy?  Just wait for the TX done interrupt.  */
+	if (!(status & Tx_BUF_EMP))
+		return;
+
+	/* Send the first character to jump-start the TX done
+	 * IRQ sending engine.
+	 */
+	if (port->x_char) {
+		writeb(port->x_char, &channel->data);
+		ZSDELAY();
+		ZS_WSYNC(channel);
+
+		port->icount.tx++;
+		port->x_char = 0;
+	} else {
+		struct circ_buf *xmit = &port->state->xmit;
+
+		writeb(xmit->buf[xmit->tail], &channel->data);
+		ZSDELAY();
+		ZS_WSYNC(channel);
+
+		xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
+		port->icount.tx++;
+
+		if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
+			uart_write_wakeup(&up->port);
+	}
+}
+
+/* The port lock is held and interrupts are disabled.  */
+static void ip22zilog_stop_rx(struct uart_port *port)
+{
+	struct uart_ip22zilog_port *up = UART_ZILOG(port);
+	struct zilog_channel *channel;
+
+	if (ZS_IS_CONS(up))
+		return;
+
+	channel = ZILOG_CHANNEL_FROM_PORT(port);
+
+	/* Disable all RX interrupts.  */
+	up->curregs[R1] &= ~RxINT_MASK;
+	ip22zilog_maybe_update_regs(up, channel);
+}
+
+/* The port lock is held.  */
+static void ip22zilog_enable_ms(struct uart_port *port)
+{
+	struct uart_ip22zilog_port *up = (struct uart_ip22zilog_port *) port;
+	struct zilog_channel *channel = ZILOG_CHANNEL_FROM_PORT(port);
+	unsigned char new_reg;
+
+	new_reg = up->curregs[R15] | (DCDIE | SYNCIE | CTSIE);
+	if (new_reg != up->curregs[R15]) {
+		up->curregs[R15] = new_reg;
+
+		/* NOTE: Not subject to 'transmitter active' rule.  */
+		write_zsreg(channel, R15, up->curregs[R15]);
+	}
+}
+
+/* The port lock is not held.  */
+static void ip22zilog_break_ctl(struct uart_port *port, int break_state)
+{
+	struct uart_ip22zilog_port *up = (struct uart_ip22zilog_port *) port;
+	struct zilog_channel *channel = ZILOG_CHANNEL_FROM_PORT(port);
+	unsigned char set_bits, clear_bits, new_reg;
+	unsigned long flags;
+
+	set_bits = clear_bits = 0;
+
+	if (break_state)
+		set_bits |= SND_BRK;
+	else
+		clear_bits |= SND_BRK;
+
+	spin_lock_irqsave(&port->lock, flags);
+
+	new_reg = (up->curregs[R5] | set_bits) & ~clear_bits;
+	if (new_reg != up->curregs[R5]) {
+		up->curregs[R5] = new_reg;
+
+		/* NOTE: Not subject to 'transmitter active' rule.  */
+		write_zsreg(channel, R5, up->curregs[R5]);
+	}
+
+	spin_unlock_irqrestore(&port->lock, flags);
+}
+
+static void __ip22zilog_reset(struct uart_ip22zilog_port *up)
+{
+	struct zilog_channel *channel;
+	int i;
+
+	if (up->flags & IP22ZILOG_FLAG_RESET_DONE)
+		return;
+
+	/* Let pending transmits finish.  */
+	channel = ZILOG_CHANNEL_FROM_PORT(&up->port);
+	for (i = 0; i < 1000; i++) {
+		unsigned char stat = read_zsreg(channel, R1);
+		if (stat & ALL_SNT)
+			break;
+		udelay(100);
+	}
+
+	if (!ZS_IS_CHANNEL_A(up)) {
+		up++;
+		channel = ZILOG_CHANNEL_FROM_PORT(&up->port);
+	}
+	write_zsreg(channel, R9, FHWRES);
+	ZSDELAY_LONG();
+	(void) read_zsreg(channel, R0);
+
+	up->flags |= IP22ZILOG_FLAG_RESET_DONE;
+	up->next->flags |= IP22ZILOG_FLAG_RESET_DONE;
+}
+
+static void __ip22zilog_startup(struct uart_ip22zilog_port *up)
+{
+	struct zilog_channel *channel;
+
+	channel = ZILOG_CHANNEL_FROM_PORT(&up->port);
+
+	__ip22zilog_reset(up);
+
+	__load_zsregs(channel, up->curregs);
+	/* set master interrupt enable */
+	write_zsreg(channel, R9, up->curregs[R9]);
+	up->prev_status = readb(&channel->control);
+
+	/* Enable receiver and transmitter.  */
+	up->curregs[R3] |= RxENAB;
+	up->curregs[R5] |= TxENAB;
+
+	up->curregs[R1] |= EXT_INT_ENAB | INT_ALL_Rx | TxINT_ENAB;
+	ip22zilog_maybe_update_regs(up, channel);
+}
+
+static int ip22zilog_startup(struct uart_port *port)
+{
+	struct uart_ip22zilog_port *up = UART_ZILOG(port);
+	unsigned long flags;
+
+	if (ZS_IS_CONS(up))
+		return 0;
+
+	spin_lock_irqsave(&port->lock, flags);
+	__ip22zilog_startup(up);
+	spin_unlock_irqrestore(&port->lock, flags);
+	return 0;
+}
+
+/*
+ * The test for ZS_IS_CONS is explained by the following e-mail:
+ *****
+ * From: Russell King <rmk@arm.linux.org.uk>
+ * Date: Sun, 8 Dec 2002 10:18:38 +0000
+ *
+ * On Sun, Dec 08, 2002 at 02:43:36AM -0500, Pete Zaitcev wrote:
+ * > I boot my 2.5 boxes using "console=ttyS0,9600" argument,
+ * > and I noticed that something is not right with reference
+ * > counting in this case. It seems that when the console
+ * > is open by kernel initially, this is not accounted
+ * > as an open, and uart_startup is not called.
+ *
+ * That is correct.  We are unable to call uart_startup when the serial
+ * console is initialised because it may need to allocate memory (as
+ * request_irq does) and the memory allocators may not have been
+ * initialised.
+ *
+ * 1. initialise the port into a state where it can send characters in the
+ *    console write method.
+ *
+ * 2. don't do the actual hardware shutdown in your shutdown() method (but
+ *    do the normal software shutdown - ie, free irqs etc)
+ *****
+ */
+static void ip22zilog_shutdown(struct uart_port *port)
+{
+	struct uart_ip22zilog_port *up = UART_ZILOG(port);
+	struct zilog_channel *channel;
+	unsigned long flags;
+
+	if (ZS_IS_CONS(up))
+		return;
+
+	spin_lock_irqsave(&port->lock, flags);
+
+	channel = ZILOG_CHANNEL_FROM_PORT(port);
+
+	/* Disable receiver and transmitter.  */
+	up->curregs[R3] &= ~RxENAB;
+	up->curregs[R5] &= ~TxENAB;
+
+	/* Disable all interrupts and BRK assertion.  */
+	up->curregs[R1] &= ~(EXT_INT_ENAB | TxINT_ENAB | RxINT_MASK);
+	up->curregs[R5] &= ~SND_BRK;
+	ip22zilog_maybe_update_regs(up, channel);
+
+	spin_unlock_irqrestore(&port->lock, flags);
+}
+
+/* Shared by TTY driver and serial console setup.  The port lock is held
+ * and local interrupts are disabled.
+ */
+static void
+ip22zilog_convert_to_zs(struct uart_ip22zilog_port *up, unsigned int cflag,
+		       unsigned int iflag, int brg)
+{
+
+	up->curregs[R10] = NRZ;
+	up->curregs[R11] = TCBR | RCBR;
+
+	/* Program BAUD and clock source. */
+	up->curregs[R4] &= ~XCLK_MASK;
+	up->curregs[R4] |= X16CLK;
+	up->curregs[R12] = brg & 0xff;
+	up->curregs[R13] = (brg >> 8) & 0xff;
+	up->curregs[R14] = BRENAB;
+
+	/* Character size, stop bits, and parity. */
+	up->curregs[3] &= ~RxN_MASK;
+	up->curregs[5] &= ~TxN_MASK;
+	switch (cflag & CSIZE) {
+	case CS5:
+		up->curregs[3] |= Rx5;
+		up->curregs[5] |= Tx5;
+		up->parity_mask = 0x1f;
+		break;
+	case CS6:
+		up->curregs[3] |= Rx6;
+		up->curregs[5] |= Tx6;
+		up->parity_mask = 0x3f;
+		break;
+	case CS7:
+		up->curregs[3] |= Rx7;
+		up->curregs[5] |= Tx7;
+		up->parity_mask = 0x7f;
+		break;
+	case CS8:
+	default:
+		up->curregs[3] |= Rx8;
+		up->curregs[5] |= Tx8;
+		up->parity_mask = 0xff;
+		break;
+	};
+	up->curregs[4] &= ~0x0c;
+	if (cflag & CSTOPB)
+		up->curregs[4] |= SB2;
+	else
+		up->curregs[4] |= SB1;
+	if (cflag & PARENB)
+		up->curregs[4] |= PAR_ENAB;
+	else
+		up->curregs[4] &= ~PAR_ENAB;
+	if (!(cflag & PARODD))
+		up->curregs[4] |= PAR_EVEN;
+	else
+		up->curregs[4] &= ~PAR_EVEN;
+
+	up->port.read_status_mask = Rx_OVR;
+	if (iflag & INPCK)
+		up->port.read_status_mask |= CRC_ERR | PAR_ERR;
+	if (iflag & (BRKINT | PARMRK))
+		up->port.read_status_mask |= BRK_ABRT;
+
+	up->port.ignore_status_mask = 0;
+	if (iflag & IGNPAR)
+		up->port.ignore_status_mask |= CRC_ERR | PAR_ERR;
+	if (iflag & IGNBRK) {
+		up->port.ignore_status_mask |= BRK_ABRT;
+		if (iflag & IGNPAR)
+			up->port.ignore_status_mask |= Rx_OVR;
+	}
+
+	if ((cflag & CREAD) == 0)
+		up->port.ignore_status_mask = 0xff;
+}
+
+/* The port lock is not held.  */
+static void
+ip22zilog_set_termios(struct uart_port *port, struct ktermios *termios,
+		      struct ktermios *old)
+{
+	struct uart_ip22zilog_port *up = (struct uart_ip22zilog_port *) port;
+	unsigned long flags;
+	int baud, brg;
+
+	baud = uart_get_baud_rate(port, termios, old, 1200, 76800);
+
+	spin_lock_irqsave(&up->port.lock, flags);
+
+	brg = BPS_TO_BRG(baud, ZS_CLOCK / ZS_CLOCK_DIVISOR);
+
+	ip22zilog_convert_to_zs(up, termios->c_cflag, termios->c_iflag, brg);
+
+	if (UART_ENABLE_MS(&up->port, termios->c_cflag))
+		up->flags |= IP22ZILOG_FLAG_MODEM_STATUS;
+	else
+		up->flags &= ~IP22ZILOG_FLAG_MODEM_STATUS;
+
+	ip22zilog_maybe_update_regs(up, ZILOG_CHANNEL_FROM_PORT(port));
+	uart_update_timeout(port, termios->c_cflag, baud);
+
+	spin_unlock_irqrestore(&up->port.lock, flags);
+}
+
+static const char *ip22zilog_type(struct uart_port *port)
+{
+	return "IP22-Zilog";
+}
+
+/* We do not request/release mappings of the registers here, this
+ * happens at early serial probe time.
+ */
+static void ip22zilog_release_port(struct uart_port *port)
+{
+}
+
+static int ip22zilog_request_port(struct uart_port *port)
+{
+	return 0;
+}
+
+/* These do not need to do anything interesting either.  */
+static void ip22zilog_config_port(struct uart_port *port, int flags)
+{
+}
+
+/* We do not support letting the user mess with the divisor, IRQ, etc. */
+static int ip22zilog_verify_port(struct uart_port *port, struct serial_struct *ser)
+{
+	return -EINVAL;
+}
+
+static struct uart_ops ip22zilog_pops = {
+	.tx_empty	=	ip22zilog_tx_empty,
+	.set_mctrl	=	ip22zilog_set_mctrl,
+	.get_mctrl	=	ip22zilog_get_mctrl,
+	.stop_tx	=	ip22zilog_stop_tx,
+	.start_tx	=	ip22zilog_start_tx,
+	.stop_rx	=	ip22zilog_stop_rx,
+	.enable_ms	=	ip22zilog_enable_ms,
+	.break_ctl	=	ip22zilog_break_ctl,
+	.startup	=	ip22zilog_startup,
+	.shutdown	=	ip22zilog_shutdown,
+	.set_termios	=	ip22zilog_set_termios,
+	.type		=	ip22zilog_type,
+	.release_port	=	ip22zilog_release_port,
+	.request_port	=	ip22zilog_request_port,
+	.config_port	=	ip22zilog_config_port,
+	.verify_port	=	ip22zilog_verify_port,
+};
+
+static struct uart_ip22zilog_port *ip22zilog_port_table;
+static struct zilog_layout **ip22zilog_chip_regs;
+
+static struct uart_ip22zilog_port *ip22zilog_irq_chain;
+static int zilog_irq = -1;
+
+static void * __init alloc_one_table(unsigned long size)
+{
+	return kzalloc(size, GFP_KERNEL);
+}
+
+static void __init ip22zilog_alloc_tables(void)
+{
+	ip22zilog_port_table = (struct uart_ip22zilog_port *)
+		alloc_one_table(NUM_CHANNELS * sizeof(struct uart_ip22zilog_port));
+	ip22zilog_chip_regs = (struct zilog_layout **)
+		alloc_one_table(NUM_IP22ZILOG * sizeof(struct zilog_layout *));
+
+	if (ip22zilog_port_table == NULL || ip22zilog_chip_regs == NULL) {
+		panic("IP22-Zilog: Cannot allocate IP22-Zilog tables.");
+	}
+}
+
+/* Get the address of the registers for IP22-Zilog instance CHIP.  */
+static struct zilog_layout * __init get_zs(int chip)
+{
+	unsigned long base;
+
+	if (chip < 0 || chip >= NUM_IP22ZILOG) {
+		panic("IP22-Zilog: Illegal chip number %d in get_zs.", chip);
+	}
+
+	/* Not probe-able, hard code it. */
+	base = (unsigned long) &sgioc->uart;
+
+	zilog_irq = SGI_SERIAL_IRQ;
+	request_mem_region(base, 8, "IP22-Zilog");
+
+	return (struct zilog_layout *) base;
+}
+
+#define ZS_PUT_CHAR_MAX_DELAY	2000	/* 10 ms */
+
+#ifdef CONFIG_SERIAL_IP22_ZILOG_CONSOLE
+static void ip22zilog_put_char(struct uart_port *port, int ch)
+{
+	struct zilog_channel *channel = ZILOG_CHANNEL_FROM_PORT(port);
+	int loops = ZS_PUT_CHAR_MAX_DELAY;
+
+	/* This is a timed polling loop so do not switch the explicit
+	 * udelay with ZSDELAY as that is a NOP on some platforms.  -DaveM
+	 */
+	do {
+		unsigned char val = readb(&channel->control);
+		if (val & Tx_BUF_EMP) {
+			ZSDELAY();
+			break;
+		}
+		udelay(5);
+	} while (--loops);
+
+	writeb(ch, &channel->data);
+	ZSDELAY();
+	ZS_WSYNC(channel);
+}
+
+static void
+ip22zilog_console_write(struct console *con, const char *s, unsigned int count)
+{
+	struct uart_ip22zilog_port *up = &ip22zilog_port_table[con->index];
+	unsigned long flags;
+
+	spin_lock_irqsave(&up->port.lock, flags);
+	uart_console_write(&up->port, s, count, ip22zilog_put_char);
+	udelay(2);
+	spin_unlock_irqrestore(&up->port.lock, flags);
+}
+
+static int __init ip22zilog_console_setup(struct console *con, char *options)
+{
+	struct uart_ip22zilog_port *up = &ip22zilog_port_table[con->index];
+	unsigned long flags;
+	int baud = 9600, bits = 8;
+	int parity = 'n';
+	int flow = 'n';
+
+	up->flags |= IP22ZILOG_FLAG_IS_CONS;
+
+	printk(KERN_INFO "Console: ttyS%d (IP22-Zilog)\n", con->index);
+
+	spin_lock_irqsave(&up->port.lock, flags);
+
+	up->curregs[R15] |= BRKIE;
+
+	__ip22zilog_startup(up);
+
+	spin_unlock_irqrestore(&up->port.lock, flags);
+
+	if (options)
+		uart_parse_options(options, &baud, &parity, &bits, &flow);
+	return uart_set_options(&up->port, con, baud, parity, bits, flow);
+}
+
+static struct uart_driver ip22zilog_reg;
+
+static struct console ip22zilog_console = {
+	.name	=	"ttyS",
+	.write	=	ip22zilog_console_write,
+	.device	=	uart_console_device,
+	.setup	=	ip22zilog_console_setup,
+	.flags	=	CON_PRINTBUFFER,
+	.index	=	-1,
+	.data	=	&ip22zilog_reg,
+};
+#endif /* CONFIG_SERIAL_IP22_ZILOG_CONSOLE */
+
+static struct uart_driver ip22zilog_reg = {
+	.owner		= THIS_MODULE,
+	.driver_name	= "serial",
+	.dev_name	= "ttyS",
+	.major		= TTY_MAJOR,
+	.minor		= 64,
+	.nr		= NUM_CHANNELS,
+#ifdef CONFIG_SERIAL_IP22_ZILOG_CONSOLE
+	.cons		= &ip22zilog_console,
+#endif
+};
+
+static void __init ip22zilog_prepare(void)
+{
+	struct uart_ip22zilog_port *up;
+	struct zilog_layout *rp;
+	int channel, chip;
+
+	/*
+	 * Temporary fix.
+	 */
+	for (channel = 0; channel < NUM_CHANNELS; channel++)
+		spin_lock_init(&ip22zilog_port_table[channel].port.lock);
+
+	ip22zilog_irq_chain = &ip22zilog_port_table[NUM_CHANNELS - 1];
+        up = &ip22zilog_port_table[0];
+	for (channel = NUM_CHANNELS - 1 ; channel > 0; channel--)
+		up[channel].next = &up[channel - 1];
+	up[channel].next = NULL;
+
+	for (chip = 0; chip < NUM_IP22ZILOG; chip++) {
+		if (!ip22zilog_chip_regs[chip]) {
+			ip22zilog_chip_regs[chip] = rp = get_zs(chip);
+
+			up[(chip * 2) + 0].port.membase = (char *) &rp->channelB;
+			up[(chip * 2) + 1].port.membase = (char *) &rp->channelA;
+
+			/* In theory mapbase is the physical address ...  */
+			up[(chip * 2) + 0].port.mapbase =
+				(unsigned long) ioremap((unsigned long) &rp->channelB, 8);
+			up[(chip * 2) + 1].port.mapbase =
+				(unsigned long) ioremap((unsigned long) &rp->channelA, 8);
+		}
+
+		/* Channel A */
+		up[(chip * 2) + 0].port.iotype = UPIO_MEM;
+		up[(chip * 2) + 0].port.irq = zilog_irq;
+		up[(chip * 2) + 0].port.uartclk = ZS_CLOCK;
+		up[(chip * 2) + 0].port.fifosize = 1;
+		up[(chip * 2) + 0].port.ops = &ip22zilog_pops;
+		up[(chip * 2) + 0].port.type = PORT_IP22ZILOG;
+		up[(chip * 2) + 0].port.flags = 0;
+		up[(chip * 2) + 0].port.line = (chip * 2) + 0;
+		up[(chip * 2) + 0].flags = 0;
+
+		/* Channel B */
+		up[(chip * 2) + 1].port.iotype = UPIO_MEM;
+		up[(chip * 2) + 1].port.irq = zilog_irq;
+		up[(chip * 2) + 1].port.uartclk = ZS_CLOCK;
+		up[(chip * 2) + 1].port.fifosize = 1;
+		up[(chip * 2) + 1].port.ops = &ip22zilog_pops;
+		up[(chip * 2) + 1].port.type = PORT_IP22ZILOG;
+		up[(chip * 2) + 1].port.line = (chip * 2) + 1;
+		up[(chip * 2) + 1].flags |= IP22ZILOG_FLAG_IS_CHANNEL_A;
+	}
+
+	for (channel = 0; channel < NUM_CHANNELS; channel++) {
+		struct uart_ip22zilog_port *up = &ip22zilog_port_table[channel];
+		int brg;
+
+		/* Normal serial TTY. */
+		up->parity_mask = 0xff;
+		up->curregs[R1] = EXT_INT_ENAB | INT_ALL_Rx | TxINT_ENAB;
+		up->curregs[R4] = PAR_EVEN | X16CLK | SB1;
+		up->curregs[R3] = RxENAB | Rx8;
+		up->curregs[R5] = TxENAB | Tx8;
+		up->curregs[R9] = NV | MIE;
+		up->curregs[R10] = NRZ;
+		up->curregs[R11] = TCBR | RCBR;
+		brg = BPS_TO_BRG(9600, ZS_CLOCK / ZS_CLOCK_DIVISOR);
+		up->curregs[R12] = (brg & 0xff);
+		up->curregs[R13] = (brg >> 8) & 0xff;
+		up->curregs[R14] = BRENAB;
+	}
+}
+
+static int __init ip22zilog_ports_init(void)
+{
+	int ret;
+
+	printk(KERN_INFO "Serial: IP22 Zilog driver (%d chips).\n", NUM_IP22ZILOG);
+
+	ip22zilog_prepare();
+
+	if (request_irq(zilog_irq, ip22zilog_interrupt, 0,
+			"IP22-Zilog", ip22zilog_irq_chain)) {
+		panic("IP22-Zilog: Unable to register zs interrupt handler.\n");
+	}
+
+	ret = uart_register_driver(&ip22zilog_reg);
+	if (ret == 0) {
+		int i;
+
+		for (i = 0; i < NUM_CHANNELS; i++) {
+			struct uart_ip22zilog_port *up = &ip22zilog_port_table[i];
+
+			uart_add_one_port(&ip22zilog_reg, &up->port);
+		}
+	}
+
+	return ret;
+}
+
+static int __init ip22zilog_init(void)
+{
+	/* IP22 Zilog setup is hard coded, no probing to do.  */
+	ip22zilog_alloc_tables();
+	ip22zilog_ports_init();
+
+	return 0;
+}
+
+static void __exit ip22zilog_exit(void)
+{
+	int i;
+	struct uart_ip22zilog_port *up;
+
+	for (i = 0; i < NUM_CHANNELS; i++) {
+		up = &ip22zilog_port_table[i];
+
+		uart_remove_one_port(&ip22zilog_reg, &up->port);
+	}
+
+	/* Free IO mem */
+	up = &ip22zilog_port_table[0];
+	for (i = 0; i < NUM_IP22ZILOG; i++) {
+		if (up[(i * 2) + 0].port.mapbase) {
+		   iounmap((void*)up[(i * 2) + 0].port.mapbase);
+		   up[(i * 2) + 0].port.mapbase = 0;
+		}
+		if (up[(i * 2) + 1].port.mapbase) {
+			iounmap((void*)up[(i * 2) + 1].port.mapbase);
+			up[(i * 2) + 1].port.mapbase = 0;
+		}
+	}
+
+	uart_unregister_driver(&ip22zilog_reg);
+}
+
+module_init(ip22zilog_init);
+module_exit(ip22zilog_exit);
+
+/* David wrote it but I'm to blame for the bugs ...  */
+MODULE_AUTHOR("Ralf Baechle <ralf@linux-mips.org>");
+MODULE_DESCRIPTION("SGI Zilog serial port driver");
+MODULE_LICENSE("GPL");
diff --git a/ap/os/linux/linux-3.4.x/drivers/tty/serial/ip22zilog.h b/ap/os/linux/linux-3.4.x/drivers/tty/serial/ip22zilog.h
new file mode 100644
index 0000000..a59a9a8
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/tty/serial/ip22zilog.h
@@ -0,0 +1,281 @@
+#ifndef _IP22_ZILOG_H
+#define _IP22_ZILOG_H
+
+#include <asm/byteorder.h>
+
+struct zilog_channel {
+#ifdef __BIG_ENDIAN
+	volatile unsigned char unused0[3];
+	volatile unsigned char control;
+	volatile unsigned char unused1[3];
+	volatile unsigned char data;
+#else /* __LITTLE_ENDIAN */
+	volatile unsigned char control;
+	volatile unsigned char unused0[3];
+	volatile unsigned char data;
+	volatile unsigned char unused1[3];
+#endif
+};
+
+struct zilog_layout {
+	struct zilog_channel channelB;
+	struct zilog_channel channelA;
+};
+
+#define NUM_ZSREGS    16
+
+/* Conversion routines to/from brg time constants from/to bits
+ * per second.
+ */
+#define BRG_TO_BPS(brg, freq) ((freq) / 2 / ((brg) + 2))
+#define BPS_TO_BRG(bps, freq) ((((freq) + (bps)) / (2 * (bps))) - 2)
+
+/* The Zilog register set */
+
+#define	FLAG	0x7e
+
+/* Write Register 0 */
+#define	R0	0		/* Register selects */
+#define	R1	1
+#define	R2	2
+#define	R3	3
+#define	R4	4
+#define	R5	5
+#define	R6	6
+#define	R7	7
+#define	R8	8
+#define	R9	9
+#define	R10	10
+#define	R11	11
+#define	R12	12
+#define	R13	13
+#define	R14	14
+#define	R15	15
+
+#define	NULLCODE	0	/* Null Code */
+#define	POINT_HIGH	0x8	/* Select upper half of registers */
+#define	RES_EXT_INT	0x10	/* Reset Ext. Status Interrupts */
+#define	SEND_ABORT	0x18	/* HDLC Abort */
+#define	RES_RxINT_FC	0x20	/* Reset RxINT on First Character */
+#define	RES_Tx_P	0x28	/* Reset TxINT Pending */
+#define	ERR_RES		0x30	/* Error Reset */
+#define	RES_H_IUS	0x38	/* Reset highest IUS */
+
+#define	RES_Rx_CRC	0x40	/* Reset Rx CRC Checker */
+#define	RES_Tx_CRC	0x80	/* Reset Tx CRC Checker */
+#define	RES_EOM_L	0xC0	/* Reset EOM latch */
+
+/* Write Register 1 */
+
+#define	EXT_INT_ENAB	0x1	/* Ext Int Enable */
+#define	TxINT_ENAB	0x2	/* Tx Int Enable */
+#define	PAR_SPEC	0x4	/* Parity is special condition */
+
+#define	RxINT_DISAB	0	/* Rx Int Disable */
+#define	RxINT_FCERR	0x8	/* Rx Int on First Character Only or Error */
+#define	INT_ALL_Rx	0x10	/* Int on all Rx Characters or error */
+#define	INT_ERR_Rx	0x18	/* Int on error only */
+#define RxINT_MASK	0x18
+
+#define	WT_RDY_RT	0x20	/* Wait/Ready on R/T */
+#define	WT_FN_RDYFN	0x40	/* Wait/FN/Ready FN */
+#define	WT_RDY_ENAB	0x80	/* Wait/Ready Enable */
+
+/* Write Register #2 (Interrupt Vector) */
+
+/* Write Register 3 */
+
+#define	RxENAB  	0x1	/* Rx Enable */
+#define	SYNC_L_INH	0x2	/* Sync Character Load Inhibit */
+#define	ADD_SM		0x4	/* Address Search Mode (SDLC) */
+#define	RxCRC_ENAB	0x8	/* Rx CRC Enable */
+#define	ENT_HM		0x10	/* Enter Hunt Mode */
+#define	AUTO_ENAB	0x20	/* Auto Enables */
+#define	Rx5		0x0	/* Rx 5 Bits/Character */
+#define	Rx7		0x40	/* Rx 7 Bits/Character */
+#define	Rx6		0x80	/* Rx 6 Bits/Character */
+#define	Rx8		0xc0	/* Rx 8 Bits/Character */
+#define RxN_MASK	0xc0
+
+/* Write Register 4 */
+
+#define	PAR_ENAB	0x1	/* Parity Enable */
+#define	PAR_EVEN	0x2	/* Parity Even/Odd* */
+
+#define	SYNC_ENAB	0	/* Sync Modes Enable */
+#define	SB1		0x4	/* 1 stop bit/char */
+#define	SB15		0x8	/* 1.5 stop bits/char */
+#define	SB2		0xc	/* 2 stop bits/char */
+
+#define	MONSYNC		0	/* 8 Bit Sync character */
+#define	BISYNC		0x10	/* 16 bit sync character */
+#define	SDLC		0x20	/* SDLC Mode (01111110 Sync Flag) */
+#define	EXTSYNC		0x30	/* External Sync Mode */
+
+#define	X1CLK		0x0	/* x1 clock mode */
+#define	X16CLK		0x40	/* x16 clock mode */
+#define	X32CLK		0x80	/* x32 clock mode */
+#define	X64CLK		0xC0	/* x64 clock mode */
+#define XCLK_MASK	0xC0
+
+/* Write Register 5 */
+
+#define	TxCRC_ENAB	0x1	/* Tx CRC Enable */
+#define	RTS		0x2	/* RTS */
+#define	SDLC_CRC	0x4	/* SDLC/CRC-16 */
+#define	TxENAB		0x8	/* Tx Enable */
+#define	SND_BRK		0x10	/* Send Break */
+#define	Tx5		0x0	/* Tx 5 bits (or less)/character */
+#define	Tx7		0x20	/* Tx 7 bits/character */
+#define	Tx6		0x40	/* Tx 6 bits/character */
+#define	Tx8		0x60	/* Tx 8 bits/character */
+#define TxN_MASK	0x60
+#define	DTR		0x80	/* DTR */
+
+/* Write Register 6 (Sync bits 0-7/SDLC Address Field) */
+
+/* Write Register 7 (Sync bits 8-15/SDLC 01111110) */
+
+/* Write Register 8 (transmit buffer) */
+
+/* Write Register 9 (Master interrupt control) */
+#define	VIS	1	/* Vector Includes Status */
+#define	NV	2	/* No Vector */
+#define	DLC	4	/* Disable Lower Chain */
+#define	MIE	8	/* Master Interrupt Enable */
+#define	STATHI	0x10	/* Status high */
+#define	NORESET	0	/* No reset on write to R9 */
+#define	CHRB	0x40	/* Reset channel B */
+#define	CHRA	0x80	/* Reset channel A */
+#define	FHWRES	0xc0	/* Force hardware reset */
+
+/* Write Register 10 (misc control bits) */
+#define	BIT6	1	/* 6 bit/8bit sync */
+#define	LOOPMODE 2	/* SDLC Loop mode */
+#define	ABUNDER	4	/* Abort/flag on SDLC xmit underrun */
+#define	MARKIDLE 8	/* Mark/flag on idle */
+#define	GAOP	0x10	/* Go active on poll */
+#define	NRZ	0	/* NRZ mode */
+#define	NRZI	0x20	/* NRZI mode */
+#define	FM1	0x40	/* FM1 (transition = 1) */
+#define	FM0	0x60	/* FM0 (transition = 0) */
+#define	CRCPS	0x80	/* CRC Preset I/O */
+
+/* Write Register 11 (Clock Mode control) */
+#define	TRxCXT	0	/* TRxC = Xtal output */
+#define	TRxCTC	1	/* TRxC = Transmit clock */
+#define	TRxCBR	2	/* TRxC = BR Generator Output */
+#define	TRxCDP	3	/* TRxC = DPLL output */
+#define	TRxCOI	4	/* TRxC O/I */
+#define	TCRTxCP	0	/* Transmit clock = RTxC pin */
+#define	TCTRxCP	8	/* Transmit clock = TRxC pin */
+#define	TCBR	0x10	/* Transmit clock = BR Generator output */
+#define	TCDPLL	0x18	/* Transmit clock = DPLL output */
+#define	RCRTxCP	0	/* Receive clock = RTxC pin */
+#define	RCTRxCP	0x20	/* Receive clock = TRxC pin */
+#define	RCBR	0x40	/* Receive clock = BR Generator output */
+#define	RCDPLL	0x60	/* Receive clock = DPLL output */
+#define	RTxCX	0x80	/* RTxC Xtal/No Xtal */
+
+/* Write Register 12 (lower byte of baud rate generator time constant) */
+
+/* Write Register 13 (upper byte of baud rate generator time constant) */
+
+/* Write Register 14 (Misc control bits) */
+#define	BRENAB 	1	/* Baud rate generator enable */
+#define	BRSRC	2	/* Baud rate generator source */
+#define	DTRREQ	4	/* DTR/Request function */
+#define	AUTOECHO 8	/* Auto Echo */
+#define	LOOPBAK	0x10	/* Local loopback */
+#define	SEARCH	0x20	/* Enter search mode */
+#define	RMC	0x40	/* Reset missing clock */
+#define	DISDPLL	0x60	/* Disable DPLL */
+#define	SSBR	0x80	/* Set DPLL source = BR generator */
+#define	SSRTxC	0xa0	/* Set DPLL source = RTxC */
+#define	SFMM	0xc0	/* Set FM mode */
+#define	SNRZI	0xe0	/* Set NRZI mode */
+
+/* Write Register 15 (external/status interrupt control) */
+#define	ZCIE	2	/* Zero count IE */
+#define	DCDIE	8	/* DCD IE */
+#define	SYNCIE	0x10	/* Sync/hunt IE */
+#define	CTSIE	0x20	/* CTS IE */
+#define	TxUIE	0x40	/* Tx Underrun/EOM IE */
+#define	BRKIE	0x80	/* Break/Abort IE */
+
+
+/* Read Register 0 */
+#define	Rx_CH_AV	0x1	/* Rx Character Available */
+#define	ZCOUNT		0x2	/* Zero count */
+#define	Tx_BUF_EMP	0x4	/* Tx Buffer empty */
+#define	DCD		0x8	/* DCD */
+#define	SYNC		0x10	/* Sync/hunt */
+#define	CTS		0x20	/* CTS */
+#define	TxEOM		0x40	/* Tx underrun */
+#define	BRK_ABRT	0x80	/* Break/Abort */
+
+/* Read Register 1 */
+#define	ALL_SNT		0x1	/* All sent */
+/* Residue Data for 8 Rx bits/char programmed */
+#define	RES3		0x8	/* 0/3 */
+#define	RES4		0x4	/* 0/4 */
+#define	RES5		0xc	/* 0/5 */
+#define	RES6		0x2	/* 0/6 */
+#define	RES7		0xa	/* 0/7 */
+#define	RES8		0x6	/* 0/8 */
+#define	RES18		0xe	/* 1/8 */
+#define	RES28		0x0	/* 2/8 */
+/* Special Rx Condition Interrupts */
+#define	PAR_ERR		0x10	/* Parity error */
+#define	Rx_OVR		0x20	/* Rx Overrun Error */
+#define	CRC_ERR		0x40	/* CRC/Framing Error */
+#define	END_FR		0x80	/* End of Frame (SDLC) */
+
+/* Read Register 2 (channel b only) - Interrupt vector */
+#define CHB_Tx_EMPTY	0x00
+#define CHB_EXT_STAT	0x02
+#define CHB_Rx_AVAIL	0x04
+#define CHB_SPECIAL	0x06
+#define CHA_Tx_EMPTY	0x08
+#define CHA_EXT_STAT	0x0a
+#define CHA_Rx_AVAIL	0x0c
+#define CHA_SPECIAL	0x0e
+#define STATUS_MASK	0x0e
+
+/* Read Register 3 (interrupt pending register) ch a only */
+#define	CHBEXT	0x1		/* Channel B Ext/Stat IP */
+#define	CHBTxIP	0x2		/* Channel B Tx IP */
+#define	CHBRxIP	0x4		/* Channel B Rx IP */
+#define	CHAEXT	0x8		/* Channel A Ext/Stat IP */
+#define	CHATxIP	0x10		/* Channel A Tx IP */
+#define	CHARxIP	0x20		/* Channel A Rx IP */
+
+/* Read Register 8 (receive data register) */
+
+/* Read Register 10  (misc status bits) */
+#define	ONLOOP	2		/* On loop */
+#define	LOOPSEND 0x10		/* Loop sending */
+#define	CLK2MIS	0x40		/* Two clocks missing */
+#define	CLK1MIS	0x80		/* One clock missing */
+
+/* Read Register 12 (lower byte of baud rate generator constant) */
+
+/* Read Register 13 (upper byte of baud rate generator constant) */
+
+/* Read Register 15 (value of WR 15) */
+
+/* Misc macros */
+#define ZS_CLEARERR(channel)    do { writeb(ERR_RES, &channel->control); \
+				     udelay(5); } while(0)
+
+#define ZS_CLEARSTAT(channel)   do { writeb(RES_EXT_INT, &channel->control); \
+				     udelay(5); } while(0)
+
+#define ZS_CLEARFIFO(channel)   do { readb(&channel->data); \
+				     udelay(2); \
+				     readb(&channel->data); \
+				     udelay(2); \
+				     readb(&channel->data); \
+				     udelay(2); } while(0)
+
+#endif /* _IP22_ZILOG_H */
diff --git a/ap/os/linux/linux-3.4.x/drivers/tty/serial/jsm/Makefile b/ap/os/linux/linux-3.4.x/drivers/tty/serial/jsm/Makefile
new file mode 100644
index 0000000..e46b6e0
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/tty/serial/jsm/Makefile
@@ -0,0 +1,8 @@
+#
+# Makefile for Jasmine adapter
+#
+
+obj-$(CONFIG_SERIAL_JSM) += jsm.o
+
+jsm-objs :=    jsm_driver.o jsm_neo.o jsm_tty.o
+
diff --git a/ap/os/linux/linux-3.4.x/drivers/tty/serial/jsm/jsm.h b/ap/os/linux/linux-3.4.x/drivers/tty/serial/jsm/jsm.h
new file mode 100644
index 0000000..529bec6
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/tty/serial/jsm/jsm.h
@@ -0,0 +1,378 @@
+/************************************************************************
+ * Copyright 2003 Digi International (www.digi.com)
+ *
+ * Copyright (C) 2004 IBM Corporation. All rights reserved.
+ *
+ * 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, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY, EXPRESS OR IMPLIED; 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, write to the Free Software
+ * Foundation, Inc., 59 * Temple Place - Suite 330, Boston,
+ * MA  02111-1307, USA.
+ *
+ * Contact Information:
+ * Scott H Kilau <Scott_Kilau@digi.com>
+ * Wendy Xiong   <wendyx@us.ibm.com>
+ *
+ ***********************************************************************/
+
+#ifndef __JSM_DRIVER_H
+#define __JSM_DRIVER_H
+
+#include <linux/kernel.h>
+#include <linux/types.h>	/* To pick up the varions Linux types */
+#include <linux/tty.h>
+#include <linux/serial_core.h>
+#include <linux/device.h>
+
+/*
+ * Debugging levels can be set using debug insmod variable
+ * They can also be compiled out completely.
+ */
+enum {
+	DBG_INIT	= 0x01,
+	DBG_BASIC	= 0x02,
+	DBG_CORE	= 0x04,
+	DBG_OPEN	= 0x08,
+	DBG_CLOSE	= 0x10,
+	DBG_READ	= 0x20,
+	DBG_WRITE	= 0x40,
+	DBG_IOCTL	= 0x80,
+	DBG_PROC	= 0x100,
+	DBG_PARAM	= 0x200,
+	DBG_PSCAN	= 0x400,
+	DBG_EVENT	= 0x800,
+	DBG_DRAIN	= 0x1000,
+	DBG_MSIGS	= 0x2000,
+	DBG_MGMT	= 0x4000,
+	DBG_INTR	= 0x8000,
+	DBG_CARR	= 0x10000,
+};
+
+#define jsm_printk(nlevel, klevel, pdev, fmt, args...)	\
+	if ((DBG_##nlevel & jsm_debug))			\
+	dev_printk(KERN_##klevel, pdev->dev, fmt, ## args)
+
+#define	MAXLINES	256
+#define MAXPORTS	8
+#define MAX_STOPS_SENT	5
+
+/* Board type definitions */
+
+#define T_NEO		0000
+#define T_CLASSIC	0001
+#define T_PCIBUS	0400
+
+/* Board State Definitions */
+
+#define BD_RUNNING	0x0
+#define BD_REASON	0x7f
+#define BD_NOTFOUND	0x1
+#define BD_NOIOPORT	0x2
+#define BD_NOMEM	0x3
+#define BD_NOBIOS	0x4
+#define BD_NOFEP	0x5
+#define BD_FAILED	0x6
+#define BD_ALLOCATED	0x7
+#define BD_TRIBOOT	0x8
+#define BD_BADKME	0x80
+
+
+/* 4 extra for alignment play space */
+#define WRITEBUFLEN	((4096) + 4)
+
+#define JSM_VERSION	"jsm: 1.2-1-INKERNEL"
+#define JSM_PARTNUM	"40002438_A-INKERNEL"
+
+struct jsm_board;
+struct jsm_channel;
+
+/************************************************************************
+ * Per board operations structure					*
+ ************************************************************************/
+struct board_ops {
+	irq_handler_t intr;
+	void (*uart_init) (struct jsm_channel *ch);
+	void (*uart_off) (struct jsm_channel *ch);
+	void (*param) (struct jsm_channel *ch);
+	void (*assert_modem_signals) (struct jsm_channel *ch);
+	void (*flush_uart_write) (struct jsm_channel *ch);
+	void (*flush_uart_read) (struct jsm_channel *ch);
+	void (*disable_receiver) (struct jsm_channel *ch);
+	void (*enable_receiver) (struct jsm_channel *ch);
+	void (*send_break) (struct jsm_channel *ch);
+	void (*clear_break) (struct jsm_channel *ch, int);
+	void (*send_start_character) (struct jsm_channel *ch);
+	void (*send_stop_character) (struct jsm_channel *ch);
+	void (*copy_data_from_queue_to_uart) (struct jsm_channel *ch);
+	u32 (*get_uart_bytes_left) (struct jsm_channel *ch);
+	void (*send_immediate_char) (struct jsm_channel *ch, unsigned char);
+};
+
+
+/*
+ *	Per-board information
+ */
+struct jsm_board
+{
+	int		boardnum;	/* Board number: 0-32 */
+
+	int		type;		/* Type of board */
+	u8		rev;		/* PCI revision ID */
+	struct pci_dev	*pci_dev;
+	u32		maxports;	/* MAX ports this board can handle */
+
+	spinlock_t	bd_intr_lock;	/* Used to protect the poller tasklet and
+					 * the interrupt routine from each other.
+					 */
+
+	u32		nasync;		/* Number of ports on card */
+
+	u32		irq;		/* Interrupt request number */
+
+	u64		membase;	/* Start of base memory of the card */
+	u64		membase_end;	/* End of base memory of the card */
+
+	u8	__iomem *re_map_membase;/* Remapped memory of the card */
+
+	u64		iobase;		/* Start of io base of the card */
+	u64		iobase_end;	/* End of io base of the card */
+
+	u32		bd_uart_offset;	/* Space between each UART */
+
+	struct jsm_channel *channels[MAXPORTS]; /* array of pointers to our channels. */
+
+	u32		bd_dividend;	/* Board/UARTs specific dividend */
+
+	struct board_ops *bd_ops;
+
+	struct list_head jsm_board_entry;
+};
+
+/************************************************************************
+ * Device flag definitions for ch_flags.
+ ************************************************************************/
+#define CH_PRON		0x0001		/* Printer on string		*/
+#define CH_STOP		0x0002		/* Output is stopped		*/
+#define CH_STOPI	0x0004		/* Input is stopped		*/
+#define CH_CD		0x0008		/* Carrier is present		*/
+#define CH_FCAR		0x0010		/* Carrier forced on		*/
+#define CH_HANGUP	0x0020		/* Hangup received		*/
+
+#define CH_RECEIVER_OFF	0x0040		/* Receiver is off		*/
+#define CH_OPENING	0x0080		/* Port in fragile open state	*/
+#define CH_CLOSING	0x0100		/* Port in fragile close state	*/
+#define CH_FIFO_ENABLED 0x0200		/* Port has FIFOs enabled	*/
+#define CH_TX_FIFO_EMPTY 0x0400		/* TX Fifo is completely empty	*/
+#define CH_TX_FIFO_LWM	0x0800		/* TX Fifo is below Low Water	*/
+#define CH_BREAK_SENDING 0x1000		/* Break is being sent		*/
+#define CH_LOOPBACK 0x2000		/* Channel is in lookback mode	*/
+#define CH_BAUD0	0x08000		/* Used for checking B0 transitions */
+
+/* Our Read/Error/Write queue sizes */
+#define RQUEUEMASK	0x1FFF		/* 8 K - 1 */
+#define EQUEUEMASK	0x1FFF		/* 8 K - 1 */
+#define RQUEUESIZE	(RQUEUEMASK + 1)
+#define EQUEUESIZE	RQUEUESIZE
+
+
+/************************************************************************
+ * Channel information structure.
+ ************************************************************************/
+struct jsm_channel {
+	struct uart_port uart_port;
+	struct jsm_board	*ch_bd;		/* Board structure pointer	*/
+
+	spinlock_t	ch_lock;	/* provide for serialization */
+	wait_queue_head_t ch_flags_wait;
+
+	u32		ch_portnum;	/* Port number, 0 offset.	*/
+	u32		ch_open_count;	/* open count			*/
+	u32		ch_flags;	/* Channel flags		*/
+
+	u64		ch_close_delay;	/* How long we should drop RTS/DTR for */
+
+	tcflag_t	ch_c_iflag;	/* channel iflags		*/
+	tcflag_t	ch_c_cflag;	/* channel cflags		*/
+	tcflag_t	ch_c_oflag;	/* channel oflags		*/
+	tcflag_t	ch_c_lflag;	/* channel lflags		*/
+	u8		ch_stopc;	/* Stop character		*/
+	u8		ch_startc;	/* Start character		*/
+
+	u8		ch_mostat;	/* FEP output modem status	*/
+	u8		ch_mistat;	/* FEP input modem status	*/
+
+	struct neo_uart_struct __iomem *ch_neo_uart;	/* Pointer to the "mapped" UART struct */
+	u8		ch_cached_lsr;	/* Cached value of the LSR register */
+
+	u8		*ch_rqueue;	/* Our read queue buffer - malloc'ed */
+	u16		ch_r_head;	/* Head location of the read queue */
+	u16		ch_r_tail;	/* Tail location of the read queue */
+
+	u8		*ch_equeue;	/* Our error queue buffer - malloc'ed */
+	u16		ch_e_head;	/* Head location of the error queue */
+	u16		ch_e_tail;	/* Tail location of the error queue */
+
+	u64		ch_rxcount;	/* total of data received so far */
+	u64		ch_txcount;	/* total of data transmitted so far */
+
+	u8		ch_r_tlevel;	/* Receive Trigger level */
+	u8		ch_t_tlevel;	/* Transmit Trigger level */
+
+	u8		ch_r_watermark;	/* Receive Watermark */
+
+
+	u32		ch_stops_sent;	/* How many times I have sent a stop character
+					 * to try to stop the other guy sending.
+					 */
+	u64		ch_err_parity;	/* Count of parity errors on channel */
+	u64		ch_err_frame;	/* Count of framing errors on channel */
+	u64		ch_err_break;	/* Count of breaks on channel */
+	u64		ch_err_overrun; /* Count of overruns on channel */
+
+	u64		ch_xon_sends;	/* Count of xons transmitted */
+	u64		ch_xoff_sends;	/* Count of xoffs transmitted */
+};
+
+
+/************************************************************************
+ * Per channel/port NEO UART structure					*
+ ************************************************************************
+ *		Base Structure Entries Usage Meanings to Host		*
+ *									*
+ *	W = read write		R = read only				*
+ *			U = Unused.					*
+ ************************************************************************/
+
+struct neo_uart_struct {
+	 u8 txrx;		/* WR	RHR/THR - Holding Reg */
+	 u8 ier;		/* WR	IER - Interrupt Enable Reg */
+	 u8 isr_fcr;		/* WR	ISR/FCR - Interrupt Status Reg/Fifo Control Reg */
+	 u8 lcr;		/* WR	LCR - Line Control Reg */
+	 u8 mcr;		/* WR	MCR - Modem Control Reg */
+	 u8 lsr;		/* WR	LSR - Line Status Reg */
+	 u8 msr;		/* WR	MSR - Modem Status Reg */
+	 u8 spr;		/* WR	SPR - Scratch Pad Reg */
+	 u8 fctr;		/* WR	FCTR - Feature Control Reg */
+	 u8 efr;		/* WR	EFR - Enhanced Function Reg */
+	 u8 tfifo;		/* WR	TXCNT/TXTRG - Transmit FIFO Reg */
+	 u8 rfifo;		/* WR	RXCNT/RXTRG - Receive FIFO Reg */
+	 u8 xoffchar1;	/* WR	XOFF 1 - XOff Character 1 Reg */
+	 u8 xoffchar2;	/* WR	XOFF 2 - XOff Character 2 Reg */
+	 u8 xonchar1;	/* WR	XON 1 - Xon Character 1 Reg */
+	 u8 xonchar2;	/* WR	XON 2 - XOn Character 2 Reg */
+
+	 u8 reserved1[0x2ff - 0x200]; /* U	Reserved by Exar */
+	 u8 txrxburst[64];	/* RW	64 bytes of RX/TX FIFO Data */
+	 u8 reserved2[0x37f - 0x340]; /* U	Reserved by Exar */
+	 u8 rxburst_with_errors[64];	/* R	64 bytes of RX FIFO Data + LSR */
+};
+
+/* Where to read the extended interrupt register (32bits instead of 8bits) */
+#define	UART_17158_POLL_ADDR_OFFSET	0x80
+
+/*
+ * These are the redefinitions for the FCTR on the XR17C158, since
+ * Exar made them different than their earlier design. (XR16C854)
+ */
+
+/* These are only applicable when table D is selected */
+#define UART_17158_FCTR_RTS_NODELAY	0x00
+#define UART_17158_FCTR_RTS_4DELAY	0x01
+#define UART_17158_FCTR_RTS_6DELAY	0x02
+#define UART_17158_FCTR_RTS_8DELAY	0x03
+#define UART_17158_FCTR_RTS_12DELAY	0x12
+#define UART_17158_FCTR_RTS_16DELAY	0x05
+#define UART_17158_FCTR_RTS_20DELAY	0x13
+#define UART_17158_FCTR_RTS_24DELAY	0x06
+#define UART_17158_FCTR_RTS_28DELAY	0x14
+#define UART_17158_FCTR_RTS_32DELAY	0x07
+#define UART_17158_FCTR_RTS_36DELAY	0x16
+#define UART_17158_FCTR_RTS_40DELAY	0x08
+#define UART_17158_FCTR_RTS_44DELAY	0x09
+#define UART_17158_FCTR_RTS_48DELAY	0x10
+#define UART_17158_FCTR_RTS_52DELAY	0x11
+
+#define UART_17158_FCTR_RTS_IRDA	0x10
+#define UART_17158_FCTR_RS485		0x20
+#define UART_17158_FCTR_TRGA		0x00
+#define UART_17158_FCTR_TRGB		0x40
+#define UART_17158_FCTR_TRGC		0x80
+#define UART_17158_FCTR_TRGD		0xC0
+
+/* 17158 trigger table selects.. */
+#define UART_17158_FCTR_BIT6		0x40
+#define UART_17158_FCTR_BIT7		0x80
+
+/* 17158 TX/RX memmapped buffer offsets */
+#define UART_17158_RX_FIFOSIZE		64
+#define UART_17158_TX_FIFOSIZE		64
+
+/* 17158 Extended IIR's */
+#define UART_17158_IIR_RDI_TIMEOUT	0x0C	/* Receiver data TIMEOUT */
+#define UART_17158_IIR_XONXOFF		0x10	/* Received an XON/XOFF char */
+#define UART_17158_IIR_HWFLOW_STATE_CHANGE 0x20	/* CTS/DSR or RTS/DTR state change */
+#define UART_17158_IIR_FIFO_ENABLED	0xC0	/* 16550 FIFOs are Enabled */
+
+/*
+ * These are the extended interrupts that get sent
+ * back to us from the UART's 32bit interrupt register
+ */
+#define UART_17158_RX_LINE_STATUS	0x1	/* RX Ready */
+#define UART_17158_RXRDY_TIMEOUT	0x2	/* RX Ready Timeout */
+#define UART_17158_TXRDY		0x3	/* TX Ready */
+#define UART_17158_MSR			0x4	/* Modem State Change */
+#define UART_17158_TX_AND_FIFO_CLR	0x40	/* Transmitter Holding Reg Empty */
+#define UART_17158_RX_FIFO_DATA_ERROR	0x80	/* UART detected an RX FIFO Data error */
+
+/*
+ * These are the EXTENDED definitions for the 17C158's Interrupt
+ * Enable Register.
+ */
+#define UART_17158_EFR_ECB	0x10	/* Enhanced control bit */
+#define UART_17158_EFR_IXON	0x2	/* Receiver compares Xon1/Xoff1 */
+#define UART_17158_EFR_IXOFF	0x8	/* Transmit Xon1/Xoff1 */
+#define UART_17158_EFR_RTSDTR	0x40	/* Auto RTS/DTR Flow Control Enable */
+#define UART_17158_EFR_CTSDSR	0x80	/* Auto CTS/DSR Flow COntrol Enable */
+
+#define UART_17158_XOFF_DETECT	0x1	/* Indicates whether chip saw an incoming XOFF char */
+#define UART_17158_XON_DETECT	0x2	/* Indicates whether chip saw an incoming XON char */
+
+#define UART_17158_IER_RSVD1	0x10	/* Reserved by Exar */
+#define UART_17158_IER_XOFF	0x20	/* Xoff Interrupt Enable */
+#define UART_17158_IER_RTSDTR	0x40	/* Output Interrupt Enable */
+#define UART_17158_IER_CTSDSR	0x80	/* Input Interrupt Enable */
+
+#define PCI_DEVICE_NEO_2DB9_PCI_NAME		"Neo 2 - DB9 Universal PCI"
+#define PCI_DEVICE_NEO_2DB9PRI_PCI_NAME		"Neo 2 - DB9 Universal PCI - Powered Ring Indicator"
+#define PCI_DEVICE_NEO_2RJ45_PCI_NAME		"Neo 2 - RJ45 Universal PCI"
+#define PCI_DEVICE_NEO_2RJ45PRI_PCI_NAME	"Neo 2 - RJ45 Universal PCI - Powered Ring Indicator"
+#define PCIE_DEVICE_NEO_IBM_PCI_NAME		"Neo 4 - PCI Express - IBM"
+
+/*
+ * Our Global Variables.
+ */
+extern struct	uart_driver jsm_uart_driver;
+extern struct	board_ops jsm_neo_ops;
+extern int	jsm_debug;
+
+/*************************************************************************
+ *
+ * Prototypes for non-static functions used in more than one module
+ *
+ *************************************************************************/
+int jsm_tty_init(struct jsm_board *);
+int jsm_uart_port_init(struct jsm_board *);
+int jsm_remove_uart_port(struct jsm_board *);
+void jsm_input(struct jsm_channel *ch);
+void jsm_check_queue_flow_control(struct jsm_channel *ch);
+
+#endif
diff --git a/ap/os/linux/linux-3.4.x/drivers/tty/serial/jsm/jsm_driver.c b/ap/os/linux/linux-3.4.x/drivers/tty/serial/jsm/jsm_driver.c
new file mode 100644
index 0000000..7545fe1
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/tty/serial/jsm/jsm_driver.c
@@ -0,0 +1,279 @@
+/************************************************************************
+ * Copyright 2003 Digi International (www.digi.com)
+ *
+ * Copyright (C) 2004 IBM Corporation. All rights reserved.
+ *
+ * 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, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY, EXPRESS OR IMPLIED; 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, write to the Free Software
+ * Foundation, Inc., 59 * Temple Place - Suite 330, Boston,
+ * MA  02111-1307, USA.
+ *
+ * Contact Information:
+ * Scott H Kilau <Scott_Kilau@digi.com>
+ * Wendy Xiong   <wendyx@us.ibm.com>
+ *
+ *
+ ***********************************************************************/
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/slab.h>
+
+#include "jsm.h"
+
+MODULE_AUTHOR("Digi International, http://www.digi.com");
+MODULE_DESCRIPTION("Driver for the Digi International "
+		   "Neo PCI based product line");
+MODULE_LICENSE("GPL");
+MODULE_SUPPORTED_DEVICE("jsm");
+
+#define JSM_DRIVER_NAME "jsm"
+#define NR_PORTS	32
+#define JSM_MINOR_START	0
+
+struct uart_driver jsm_uart_driver = {
+	.owner		= THIS_MODULE,
+	.driver_name	= JSM_DRIVER_NAME,
+	.dev_name	= "ttyn",
+	.major		= 0,
+	.minor		= JSM_MINOR_START,
+	.nr		= NR_PORTS,
+};
+
+static pci_ers_result_t jsm_io_error_detected(struct pci_dev *pdev,
+                                       pci_channel_state_t state);
+static pci_ers_result_t jsm_io_slot_reset(struct pci_dev *pdev);
+static void jsm_io_resume(struct pci_dev *pdev);
+
+static struct pci_error_handlers jsm_err_handler = {
+	.error_detected = jsm_io_error_detected,
+	.slot_reset = jsm_io_slot_reset,
+	.resume = jsm_io_resume,
+};
+
+int jsm_debug;
+module_param(jsm_debug, int, 0);
+MODULE_PARM_DESC(jsm_debug, "Driver debugging level");
+
+static int __devinit jsm_probe_one(struct pci_dev *pdev, const struct pci_device_id *ent)
+{
+	int rc = 0;
+	struct jsm_board *brd;
+	static int adapter_count = 0;
+
+	rc = pci_enable_device(pdev);
+	if (rc) {
+		dev_err(&pdev->dev, "Device enable FAILED\n");
+		goto out;
+	}
+
+	rc = pci_request_regions(pdev, "jsm");
+	if (rc) {
+		dev_err(&pdev->dev, "pci_request_region FAILED\n");
+		goto out_disable_device;
+	}
+
+	brd = kzalloc(sizeof(struct jsm_board), GFP_KERNEL);
+	if (!brd) {
+		dev_err(&pdev->dev,
+			"memory allocation for board structure failed\n");
+		rc = -ENOMEM;
+		goto out_release_regions;
+	}
+
+	/* store the info for the board we've found */
+	brd->boardnum = adapter_count++;
+	brd->pci_dev = pdev;
+	if (pdev->device == PCIE_DEVICE_ID_NEO_4_IBM)
+		brd->maxports = 4;
+	else if (pdev->device == PCI_DEVICE_ID_DIGI_NEO_8)
+		brd->maxports = 8;
+	else
+		brd->maxports = 2;
+
+	spin_lock_init(&brd->bd_intr_lock);
+
+	/* store which revision we have */
+	brd->rev = pdev->revision;
+
+	brd->irq = pdev->irq;
+
+	jsm_printk(INIT, INFO, &brd->pci_dev,
+		"jsm_found_board - NEO adapter\n");
+
+	/* get the PCI Base Address Registers */
+	brd->membase	= pci_resource_start(pdev, 0);
+	brd->membase_end = pci_resource_end(pdev, 0);
+
+	if (brd->membase & 1)
+		brd->membase &= ~3;
+	else
+		brd->membase &= ~15;
+
+	/* Assign the board_ops struct */
+	brd->bd_ops = &jsm_neo_ops;
+
+	brd->bd_uart_offset = 0x200;
+	brd->bd_dividend = 921600;
+
+	brd->re_map_membase = ioremap(brd->membase, pci_resource_len(pdev, 0));
+	if (!brd->re_map_membase) {
+		dev_err(&pdev->dev,
+			"card has no PCI Memory resources, "
+			"failing board.\n");
+		rc = -ENOMEM;
+		goto out_kfree_brd;
+	}
+
+	rc = request_irq(brd->irq, brd->bd_ops->intr,
+			IRQF_SHARED, "JSM", brd);
+	if (rc) {
+		printk(KERN_WARNING "Failed to hook IRQ %d\n",brd->irq);
+		goto out_iounmap;
+	}
+
+	rc = jsm_tty_init(brd);
+	if (rc < 0) {
+		dev_err(&pdev->dev, "Can't init tty devices (%d)\n", rc);
+		rc = -ENXIO;
+		goto out_free_irq;
+	}
+
+	rc = jsm_uart_port_init(brd);
+	if (rc < 0) {
+		/* XXX: leaking all resources from jsm_tty_init here! */
+		dev_err(&pdev->dev, "Can't init uart port (%d)\n", rc);
+		rc = -ENXIO;
+		goto out_free_irq;
+	}
+
+	/* Log the information about the board */
+	dev_info(&pdev->dev, "board %d: Digi Neo (rev %d), irq %d\n",
+			adapter_count, brd->rev, brd->irq);
+
+	pci_set_drvdata(pdev, brd);
+	pci_save_state(pdev);
+
+	return 0;
+ out_free_irq:
+	jsm_remove_uart_port(brd);
+	free_irq(brd->irq, brd);
+ out_iounmap:
+	iounmap(brd->re_map_membase);
+ out_kfree_brd:
+	kfree(brd);
+ out_release_regions:
+	pci_release_regions(pdev);
+ out_disable_device:
+	pci_disable_device(pdev);
+ out:
+	return rc;
+}
+
+static void __devexit jsm_remove_one(struct pci_dev *pdev)
+{
+	struct jsm_board *brd = pci_get_drvdata(pdev);
+	int i = 0;
+
+	jsm_remove_uart_port(brd);
+
+	free_irq(brd->irq, brd);
+	iounmap(brd->re_map_membase);
+
+	/* Free all allocated channels structs */
+	for (i = 0; i < brd->maxports; i++) {
+		if (brd->channels[i]) {
+			kfree(brd->channels[i]->ch_rqueue);
+			kfree(brd->channels[i]->ch_equeue);
+			kfree(brd->channels[i]);
+		}
+	}
+
+	pci_release_regions(pdev);
+	pci_disable_device(pdev);
+	kfree(brd);
+}
+
+static struct pci_device_id jsm_pci_tbl[] = {
+	{ PCI_DEVICE(PCI_VENDOR_ID_DIGI, PCI_DEVICE_ID_NEO_2DB9), 0, 0, 0 },
+	{ PCI_DEVICE(PCI_VENDOR_ID_DIGI, PCI_DEVICE_ID_NEO_2DB9PRI), 0, 0, 1 },
+	{ PCI_DEVICE(PCI_VENDOR_ID_DIGI, PCI_DEVICE_ID_NEO_2RJ45), 0, 0, 2 },
+	{ PCI_DEVICE(PCI_VENDOR_ID_DIGI, PCI_DEVICE_ID_NEO_2RJ45PRI), 0, 0, 3 },
+	{ PCI_DEVICE(PCI_VENDOR_ID_DIGI, PCIE_DEVICE_ID_NEO_4_IBM), 0, 0, 4 },
+	{ PCI_DEVICE(PCI_VENDOR_ID_DIGI, PCI_DEVICE_ID_DIGI_NEO_8), 0, 0, 5 },
+	{ 0, }
+};
+MODULE_DEVICE_TABLE(pci, jsm_pci_tbl);
+
+static struct pci_driver jsm_driver = {
+	.name		= "jsm",
+	.id_table	= jsm_pci_tbl,
+	.probe		= jsm_probe_one,
+	.remove		= __devexit_p(jsm_remove_one),
+	.err_handler    = &jsm_err_handler,
+};
+
+static pci_ers_result_t jsm_io_error_detected(struct pci_dev *pdev,
+					pci_channel_state_t state)
+{
+	struct jsm_board *brd = pci_get_drvdata(pdev);
+
+	jsm_remove_uart_port(brd);
+
+	return PCI_ERS_RESULT_NEED_RESET;
+}
+
+static pci_ers_result_t jsm_io_slot_reset(struct pci_dev *pdev)
+{
+	int rc;
+
+	rc = pci_enable_device(pdev);
+
+	if (rc)
+		return PCI_ERS_RESULT_DISCONNECT;
+
+	pci_set_master(pdev);
+
+	return PCI_ERS_RESULT_RECOVERED;
+}
+
+static void jsm_io_resume(struct pci_dev *pdev)
+{
+	struct jsm_board *brd = pci_get_drvdata(pdev);
+
+	pci_restore_state(pdev);
+	pci_save_state(pdev);
+
+	jsm_uart_port_init(brd);
+}
+
+static int __init jsm_init_module(void)
+{
+	int rc;
+
+	rc = uart_register_driver(&jsm_uart_driver);
+	if (!rc) {
+		rc = pci_register_driver(&jsm_driver);
+		if (rc)
+			uart_unregister_driver(&jsm_uart_driver);
+	}
+	return rc;
+}
+
+static void __exit jsm_exit_module(void)
+{
+	pci_unregister_driver(&jsm_driver);
+	uart_unregister_driver(&jsm_uart_driver);
+}
+
+module_init(jsm_init_module);
+module_exit(jsm_exit_module);
diff --git a/ap/os/linux/linux-3.4.x/drivers/tty/serial/jsm/jsm_neo.c b/ap/os/linux/linux-3.4.x/drivers/tty/serial/jsm/jsm_neo.c
new file mode 100644
index 0000000..81dfafa
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/tty/serial/jsm/jsm_neo.c
@@ -0,0 +1,1413 @@
+/************************************************************************
+ * Copyright 2003 Digi International (www.digi.com)
+ *
+ * Copyright (C) 2004 IBM Corporation. All rights reserved.
+ *
+ * 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, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY, EXPRESS OR IMPLIED; 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, write to the Free Software
+ * Foundation, Inc., 59 * Temple Place - Suite 330, Boston,
+ * MA  02111-1307, USA.
+ *
+ * Contact Information:
+ * Scott H Kilau <Scott_Kilau@digi.com>
+ * Wendy Xiong   <wendyx@us.ibm.com>
+ *
+ ***********************************************************************/
+#include <linux/delay.h>	/* For udelay */
+#include <linux/serial_reg.h>	/* For the various UART offsets */
+#include <linux/tty.h>
+#include <linux/pci.h>
+#include <asm/io.h>
+
+#include "jsm.h"		/* Driver main header file */
+
+static u32 jsm_offset_table[8] = { 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80 };
+
+/*
+ * This function allows calls to ensure that all outstanding
+ * PCI writes have been completed, by doing a PCI read against
+ * a non-destructive, read-only location on the Neo card.
+ *
+ * In this case, we are reading the DVID (Read-only Device Identification)
+ * value of the Neo card.
+ */
+static inline void neo_pci_posting_flush(struct jsm_board *bd)
+{
+      readb(bd->re_map_membase + 0x8D);
+}
+
+static void neo_set_cts_flow_control(struct jsm_channel *ch)
+{
+	u8 ier, efr;
+	ier = readb(&ch->ch_neo_uart->ier);
+	efr = readb(&ch->ch_neo_uart->efr);
+
+	jsm_printk(PARAM, INFO, &ch->ch_bd->pci_dev, "Setting CTSFLOW\n");
+
+	/* Turn on auto CTS flow control */
+	ier |= (UART_17158_IER_CTSDSR);
+	efr |= (UART_17158_EFR_ECB | UART_17158_EFR_CTSDSR);
+
+	/* Turn off auto Xon flow control */
+	efr &= ~(UART_17158_EFR_IXON);
+
+	/* Why? Becuz Exar's spec says we have to zero it out before setting it */
+	writeb(0, &ch->ch_neo_uart->efr);
+
+	/* Turn on UART enhanced bits */
+	writeb(efr, &ch->ch_neo_uart->efr);
+
+	/* Turn on table D, with 8 char hi/low watermarks */
+	writeb((UART_17158_FCTR_TRGD | UART_17158_FCTR_RTS_4DELAY), &ch->ch_neo_uart->fctr);
+
+	/* Feed the UART our trigger levels */
+	writeb(8, &ch->ch_neo_uart->tfifo);
+	ch->ch_t_tlevel = 8;
+
+	writeb(ier, &ch->ch_neo_uart->ier);
+}
+
+static void neo_set_rts_flow_control(struct jsm_channel *ch)
+{
+	u8 ier, efr;
+	ier = readb(&ch->ch_neo_uart->ier);
+	efr = readb(&ch->ch_neo_uart->efr);
+
+	jsm_printk(PARAM, INFO, &ch->ch_bd->pci_dev, "Setting RTSFLOW\n");
+
+	/* Turn on auto RTS flow control */
+	ier |= (UART_17158_IER_RTSDTR);
+	efr |= (UART_17158_EFR_ECB | UART_17158_EFR_RTSDTR);
+
+	/* Turn off auto Xoff flow control */
+	ier &= ~(UART_17158_IER_XOFF);
+	efr &= ~(UART_17158_EFR_IXOFF);
+
+	/* Why? Becuz Exar's spec says we have to zero it out before setting it */
+	writeb(0, &ch->ch_neo_uart->efr);
+
+	/* Turn on UART enhanced bits */
+	writeb(efr, &ch->ch_neo_uart->efr);
+
+	writeb((UART_17158_FCTR_TRGD | UART_17158_FCTR_RTS_4DELAY), &ch->ch_neo_uart->fctr);
+	ch->ch_r_watermark = 4;
+
+	writeb(56, &ch->ch_neo_uart->rfifo);
+	ch->ch_r_tlevel = 56;
+
+	writeb(ier, &ch->ch_neo_uart->ier);
+
+	/*
+	 * From the Neo UART spec sheet:
+	 * The auto RTS/DTR function must be started by asserting
+	 * RTS/DTR# output pin (MCR bit-0 or 1 to logic 1 after
+	 * it is enabled.
+	 */
+	ch->ch_mostat |= (UART_MCR_RTS);
+}
+
+
+static void neo_set_ixon_flow_control(struct jsm_channel *ch)
+{
+	u8 ier, efr;
+	ier = readb(&ch->ch_neo_uart->ier);
+	efr = readb(&ch->ch_neo_uart->efr);
+
+	jsm_printk(PARAM, INFO, &ch->ch_bd->pci_dev, "Setting IXON FLOW\n");
+
+	/* Turn off auto CTS flow control */
+	ier &= ~(UART_17158_IER_CTSDSR);
+	efr &= ~(UART_17158_EFR_CTSDSR);
+
+	/* Turn on auto Xon flow control */
+	efr |= (UART_17158_EFR_ECB | UART_17158_EFR_IXON);
+
+	/* Why? Becuz Exar's spec says we have to zero it out before setting it */
+	writeb(0, &ch->ch_neo_uart->efr);
+
+	/* Turn on UART enhanced bits */
+	writeb(efr, &ch->ch_neo_uart->efr);
+
+	writeb((UART_17158_FCTR_TRGD | UART_17158_FCTR_RTS_8DELAY), &ch->ch_neo_uart->fctr);
+	ch->ch_r_watermark = 4;
+
+	writeb(32, &ch->ch_neo_uart->rfifo);
+	ch->ch_r_tlevel = 32;
+
+	/* Tell UART what start/stop chars it should be looking for */
+	writeb(ch->ch_startc, &ch->ch_neo_uart->xonchar1);
+	writeb(0, &ch->ch_neo_uart->xonchar2);
+
+	writeb(ch->ch_stopc, &ch->ch_neo_uart->xoffchar1);
+	writeb(0, &ch->ch_neo_uart->xoffchar2);
+
+	writeb(ier, &ch->ch_neo_uart->ier);
+}
+
+static void neo_set_ixoff_flow_control(struct jsm_channel *ch)
+{
+	u8 ier, efr;
+	ier = readb(&ch->ch_neo_uart->ier);
+	efr = readb(&ch->ch_neo_uart->efr);
+
+	jsm_printk(PARAM, INFO, &ch->ch_bd->pci_dev, "Setting IXOFF FLOW\n");
+
+	/* Turn off auto RTS flow control */
+	ier &= ~(UART_17158_IER_RTSDTR);
+	efr &= ~(UART_17158_EFR_RTSDTR);
+
+	/* Turn on auto Xoff flow control */
+	ier |= (UART_17158_IER_XOFF);
+	efr |= (UART_17158_EFR_ECB | UART_17158_EFR_IXOFF);
+
+	/* Why? Becuz Exar's spec says we have to zero it out before setting it */
+	writeb(0, &ch->ch_neo_uart->efr);
+
+	/* Turn on UART enhanced bits */
+	writeb(efr, &ch->ch_neo_uart->efr);
+
+	/* Turn on table D, with 8 char hi/low watermarks */
+	writeb((UART_17158_FCTR_TRGD | UART_17158_FCTR_RTS_8DELAY), &ch->ch_neo_uart->fctr);
+
+	writeb(8, &ch->ch_neo_uart->tfifo);
+	ch->ch_t_tlevel = 8;
+
+	/* Tell UART what start/stop chars it should be looking for */
+	writeb(ch->ch_startc, &ch->ch_neo_uart->xonchar1);
+	writeb(0, &ch->ch_neo_uart->xonchar2);
+
+	writeb(ch->ch_stopc, &ch->ch_neo_uart->xoffchar1);
+	writeb(0, &ch->ch_neo_uart->xoffchar2);
+
+	writeb(ier, &ch->ch_neo_uart->ier);
+}
+
+static void neo_set_no_input_flow_control(struct jsm_channel *ch)
+{
+	u8 ier, efr;
+	ier = readb(&ch->ch_neo_uart->ier);
+	efr = readb(&ch->ch_neo_uart->efr);
+
+	jsm_printk(PARAM, INFO, &ch->ch_bd->pci_dev, "Unsetting Input FLOW\n");
+
+	/* Turn off auto RTS flow control */
+	ier &= ~(UART_17158_IER_RTSDTR);
+	efr &= ~(UART_17158_EFR_RTSDTR);
+
+	/* Turn off auto Xoff flow control */
+	ier &= ~(UART_17158_IER_XOFF);
+	if (ch->ch_c_iflag & IXON)
+		efr &= ~(UART_17158_EFR_IXOFF);
+	else
+		efr &= ~(UART_17158_EFR_ECB | UART_17158_EFR_IXOFF);
+
+	/* Why? Becuz Exar's spec says we have to zero it out before setting it */
+	writeb(0, &ch->ch_neo_uart->efr);
+
+	/* Turn on UART enhanced bits */
+	writeb(efr, &ch->ch_neo_uart->efr);
+
+	/* Turn on table D, with 8 char hi/low watermarks */
+	writeb((UART_17158_FCTR_TRGD | UART_17158_FCTR_RTS_8DELAY), &ch->ch_neo_uart->fctr);
+
+	ch->ch_r_watermark = 0;
+
+	writeb(16, &ch->ch_neo_uart->tfifo);
+	ch->ch_t_tlevel = 16;
+
+	writeb(16, &ch->ch_neo_uart->rfifo);
+	ch->ch_r_tlevel = 16;
+
+	writeb(ier, &ch->ch_neo_uart->ier);
+}
+
+static void neo_set_no_output_flow_control(struct jsm_channel *ch)
+{
+	u8 ier, efr;
+	ier = readb(&ch->ch_neo_uart->ier);
+	efr = readb(&ch->ch_neo_uart->efr);
+
+	jsm_printk(PARAM, INFO, &ch->ch_bd->pci_dev, "Unsetting Output FLOW\n");
+
+	/* Turn off auto CTS flow control */
+	ier &= ~(UART_17158_IER_CTSDSR);
+	efr &= ~(UART_17158_EFR_CTSDSR);
+
+	/* Turn off auto Xon flow control */
+	if (ch->ch_c_iflag & IXOFF)
+		efr &= ~(UART_17158_EFR_IXON);
+	else
+		efr &= ~(UART_17158_EFR_ECB | UART_17158_EFR_IXON);
+
+	/* Why? Becuz Exar's spec says we have to zero it out before setting it */
+	writeb(0, &ch->ch_neo_uart->efr);
+
+	/* Turn on UART enhanced bits */
+	writeb(efr, &ch->ch_neo_uart->efr);
+
+	/* Turn on table D, with 8 char hi/low watermarks */
+	writeb((UART_17158_FCTR_TRGD | UART_17158_FCTR_RTS_8DELAY), &ch->ch_neo_uart->fctr);
+
+	ch->ch_r_watermark = 0;
+
+	writeb(16, &ch->ch_neo_uart->tfifo);
+	ch->ch_t_tlevel = 16;
+
+	writeb(16, &ch->ch_neo_uart->rfifo);
+	ch->ch_r_tlevel = 16;
+
+	writeb(ier, &ch->ch_neo_uart->ier);
+}
+
+static inline void neo_set_new_start_stop_chars(struct jsm_channel *ch)
+{
+
+	/* if hardware flow control is set, then skip this whole thing */
+	if (ch->ch_c_cflag & CRTSCTS)
+		return;
+
+	jsm_printk(PARAM, INFO, &ch->ch_bd->pci_dev, "start\n");
+
+	/* Tell UART what start/stop chars it should be looking for */
+	writeb(ch->ch_startc, &ch->ch_neo_uart->xonchar1);
+	writeb(0, &ch->ch_neo_uart->xonchar2);
+
+	writeb(ch->ch_stopc, &ch->ch_neo_uart->xoffchar1);
+	writeb(0, &ch->ch_neo_uart->xoffchar2);
+}
+
+static void neo_copy_data_from_uart_to_queue(struct jsm_channel *ch)
+{
+	int qleft = 0;
+	u8 linestatus = 0;
+	u8 error_mask = 0;
+	int n = 0;
+	int total = 0;
+	u16 head;
+	u16 tail;
+
+	if (!ch)
+		return;
+
+	/* cache head and tail of queue */
+	head = ch->ch_r_head & RQUEUEMASK;
+	tail = ch->ch_r_tail & RQUEUEMASK;
+
+	/* Get our cached LSR */
+	linestatus = ch->ch_cached_lsr;
+	ch->ch_cached_lsr = 0;
+
+	/* Store how much space we have left in the queue */
+	if ((qleft = tail - head - 1) < 0)
+		qleft += RQUEUEMASK + 1;
+
+	/*
+	 * If the UART is not in FIFO mode, force the FIFO copy to
+	 * NOT be run, by setting total to 0.
+	 *
+	 * On the other hand, if the UART IS in FIFO mode, then ask
+	 * the UART to give us an approximation of data it has RX'ed.
+	 */
+	if (!(ch->ch_flags & CH_FIFO_ENABLED))
+		total = 0;
+	else {
+		total = readb(&ch->ch_neo_uart->rfifo);
+
+		/*
+		 * EXAR chip bug - RX FIFO COUNT - Fudge factor.
+		 *
+		 * This resolves a problem/bug with the Exar chip that sometimes
+		 * returns a bogus value in the rfifo register.
+		 * The count can be any where from 0-3 bytes "off".
+		 * Bizarre, but true.
+		 */
+		total -= 3;
+	}
+
+	/*
+	 * Finally, bound the copy to make sure we don't overflow
+	 * our own queue...
+	 * The byte by byte copy loop below this loop this will
+	 * deal with the queue overflow possibility.
+	 */
+	total = min(total, qleft);
+
+	while (total > 0) {
+		/*
+		 * Grab the linestatus register, we need to check
+		 * to see if there are any errors in the FIFO.
+		 */
+		linestatus = readb(&ch->ch_neo_uart->lsr);
+
+		/*
+		 * Break out if there is a FIFO error somewhere.
+		 * This will allow us to go byte by byte down below,
+		 * finding the exact location of the error.
+		 */
+		if (linestatus & UART_17158_RX_FIFO_DATA_ERROR)
+			break;
+
+		/* Make sure we don't go over the end of our queue */
+		n = min(((u32) total), (RQUEUESIZE - (u32) head));
+
+		/*
+		 * Cut down n even further if needed, this is to fix
+		 * a problem with memcpy_fromio() with the Neo on the
+		 * IBM pSeries platform.
+		 * 15 bytes max appears to be the magic number.
+		 */
+		n = min((u32) n, (u32) 12);
+
+		/*
+		 * Since we are grabbing the linestatus register, which
+		 * will reset some bits after our read, we need to ensure
+		 * we don't miss our TX FIFO emptys.
+		 */
+		if (linestatus & (UART_LSR_THRE | UART_17158_TX_AND_FIFO_CLR))
+			ch->ch_flags |= (CH_TX_FIFO_EMPTY | CH_TX_FIFO_LWM);
+
+		linestatus = 0;
+
+		/* Copy data from uart to the queue */
+		memcpy_fromio(ch->ch_rqueue + head, &ch->ch_neo_uart->txrxburst, n);
+		/*
+		 * Since RX_FIFO_DATA_ERROR was 0, we are guaranteed
+		 * that all the data currently in the FIFO is free of
+		 * breaks and parity/frame/orun errors.
+		 */
+		memset(ch->ch_equeue + head, 0, n);
+
+		/* Add to and flip head if needed */
+		head = (head + n) & RQUEUEMASK;
+		total -= n;
+		qleft -= n;
+		ch->ch_rxcount += n;
+	}
+
+	/*
+	 * Create a mask to determine whether we should
+	 * insert the character (if any) into our queue.
+	 */
+	if (ch->ch_c_iflag & IGNBRK)
+		error_mask |= UART_LSR_BI;
+
+	/*
+	 * Now cleanup any leftover bytes still in the UART.
+	 * Also deal with any possible queue overflow here as well.
+	 */
+	while (1) {
+
+		/*
+		 * Its possible we have a linestatus from the loop above
+		 * this, so we "OR" on any extra bits.
+		 */
+		linestatus |= readb(&ch->ch_neo_uart->lsr);
+
+		/*
+		 * If the chip tells us there is no more data pending to
+		 * be read, we can then leave.
+		 * But before we do, cache the linestatus, just in case.
+		 */
+		if (!(linestatus & UART_LSR_DR)) {
+			ch->ch_cached_lsr = linestatus;
+			break;
+		}
+
+		/* No need to store this bit */
+		linestatus &= ~UART_LSR_DR;
+
+		/*
+		 * Since we are grabbing the linestatus register, which
+		 * will reset some bits after our read, we need to ensure
+		 * we don't miss our TX FIFO emptys.
+		 */
+		if (linestatus & (UART_LSR_THRE | UART_17158_TX_AND_FIFO_CLR)) {
+			linestatus &= ~(UART_LSR_THRE | UART_17158_TX_AND_FIFO_CLR);
+			ch->ch_flags |= (CH_TX_FIFO_EMPTY | CH_TX_FIFO_LWM);
+		}
+
+		/*
+		 * Discard character if we are ignoring the error mask.
+		 */
+		if (linestatus & error_mask) {
+			u8 discard;
+			linestatus = 0;
+			memcpy_fromio(&discard, &ch->ch_neo_uart->txrxburst, 1);
+			continue;
+		}
+
+		/*
+		 * If our queue is full, we have no choice but to drop some data.
+		 * The assumption is that HWFLOW or SWFLOW should have stopped
+		 * things way way before we got to this point.
+		 *
+		 * I decided that I wanted to ditch the oldest data first,
+		 * I hope thats okay with everyone? Yes? Good.
+		 */
+		while (qleft < 1) {
+			jsm_printk(READ, INFO, &ch->ch_bd->pci_dev,
+				"Queue full, dropping DATA:%x LSR:%x\n",
+				ch->ch_rqueue[tail], ch->ch_equeue[tail]);
+
+			ch->ch_r_tail = tail = (tail + 1) & RQUEUEMASK;
+			ch->ch_err_overrun++;
+			qleft++;
+		}
+
+		memcpy_fromio(ch->ch_rqueue + head, &ch->ch_neo_uart->txrxburst, 1);
+		ch->ch_equeue[head] = (u8) linestatus;
+
+		jsm_printk(READ, INFO, &ch->ch_bd->pci_dev,
+				"DATA/LSR pair: %x %x\n", ch->ch_rqueue[head], ch->ch_equeue[head]);
+
+		/* Ditch any remaining linestatus value. */
+		linestatus = 0;
+
+		/* Add to and flip head if needed */
+		head = (head + 1) & RQUEUEMASK;
+
+		qleft--;
+		ch->ch_rxcount++;
+	}
+
+	/*
+	 * Write new final heads to channel structure.
+	 */
+	ch->ch_r_head = head & RQUEUEMASK;
+	ch->ch_e_head = head & EQUEUEMASK;
+	jsm_input(ch);
+}
+
+static void neo_copy_data_from_queue_to_uart(struct jsm_channel *ch)
+{
+	u16 head;
+	u16 tail;
+	int n;
+	int s;
+	int qlen;
+	u32 len_written = 0;
+	struct circ_buf *circ;
+
+	if (!ch)
+		return;
+
+	circ = &ch->uart_port.state->xmit;
+
+	/* No data to write to the UART */
+	if (uart_circ_empty(circ))
+		return;
+
+	/* If port is "stopped", don't send any data to the UART */
+	if ((ch->ch_flags & CH_STOP) || (ch->ch_flags & CH_BREAK_SENDING))
+		return;
+	/*
+	 * If FIFOs are disabled. Send data directly to txrx register
+	 */
+	if (!(ch->ch_flags & CH_FIFO_ENABLED)) {
+		u8 lsrbits = readb(&ch->ch_neo_uart->lsr);
+
+		ch->ch_cached_lsr |= lsrbits;
+		if (ch->ch_cached_lsr & UART_LSR_THRE) {
+			ch->ch_cached_lsr &= ~(UART_LSR_THRE);
+
+			writeb(circ->buf[circ->tail], &ch->ch_neo_uart->txrx);
+			jsm_printk(WRITE, INFO, &ch->ch_bd->pci_dev,
+					"Tx data: %x\n", circ->buf[circ->tail]);
+			circ->tail = (circ->tail + 1) & (UART_XMIT_SIZE - 1);
+			ch->ch_txcount++;
+		}
+		return;
+	}
+
+	/*
+	 * We have to do it this way, because of the EXAR TXFIFO count bug.
+	 */
+	if (!(ch->ch_flags & (CH_TX_FIFO_EMPTY | CH_TX_FIFO_LWM)))
+		return;
+
+	n = UART_17158_TX_FIFOSIZE - ch->ch_t_tlevel;
+
+	/* cache head and tail of queue */
+	head = circ->head & (UART_XMIT_SIZE - 1);
+	tail = circ->tail & (UART_XMIT_SIZE - 1);
+	qlen = uart_circ_chars_pending(circ);
+
+	/* Find minimum of the FIFO space, versus queue length */
+	n = min(n, qlen);
+
+	while (n > 0) {
+
+		s = ((head >= tail) ? head : UART_XMIT_SIZE) - tail;
+		s = min(s, n);
+
+		if (s <= 0)
+			break;
+
+		memcpy_toio(&ch->ch_neo_uart->txrxburst, circ->buf + tail, s);
+		/* Add and flip queue if needed */
+		tail = (tail + s) & (UART_XMIT_SIZE - 1);
+		n -= s;
+		ch->ch_txcount += s;
+		len_written += s;
+	}
+
+	/* Update the final tail */
+	circ->tail = tail & (UART_XMIT_SIZE - 1);
+
+	if (len_written >= ch->ch_t_tlevel)
+		ch->ch_flags &= ~(CH_TX_FIFO_EMPTY | CH_TX_FIFO_LWM);
+
+	if (uart_circ_empty(circ))
+		uart_write_wakeup(&ch->uart_port);
+}
+
+static void neo_parse_modem(struct jsm_channel *ch, u8 signals)
+{
+	u8 msignals = signals;
+
+	jsm_printk(MSIGS, INFO, &ch->ch_bd->pci_dev,
+			"neo_parse_modem: port: %d msignals: %x\n", ch->ch_portnum, msignals);
+
+	/* Scrub off lower bits. They signify delta's, which I don't care about */
+	/* Keep DDCD and DDSR though */
+	msignals &= 0xf8;
+
+	if (msignals & UART_MSR_DDCD)
+		uart_handle_dcd_change(&ch->uart_port, msignals & UART_MSR_DCD);
+	if (msignals & UART_MSR_DDSR)
+		uart_handle_cts_change(&ch->uart_port, msignals & UART_MSR_CTS);
+	if (msignals & UART_MSR_DCD)
+		ch->ch_mistat |= UART_MSR_DCD;
+	else
+		ch->ch_mistat &= ~UART_MSR_DCD;
+
+	if (msignals & UART_MSR_DSR)
+		ch->ch_mistat |= UART_MSR_DSR;
+	else
+		ch->ch_mistat &= ~UART_MSR_DSR;
+
+	if (msignals & UART_MSR_RI)
+		ch->ch_mistat |= UART_MSR_RI;
+	else
+		ch->ch_mistat &= ~UART_MSR_RI;
+
+	if (msignals & UART_MSR_CTS)
+		ch->ch_mistat |= UART_MSR_CTS;
+	else
+		ch->ch_mistat &= ~UART_MSR_CTS;
+
+	jsm_printk(MSIGS, INFO, &ch->ch_bd->pci_dev,
+			"Port: %d DTR: %d RTS: %d CTS: %d DSR: %d " "RI: %d CD: %d\n",
+		ch->ch_portnum,
+		!!((ch->ch_mistat | ch->ch_mostat) & UART_MCR_DTR),
+		!!((ch->ch_mistat | ch->ch_mostat) & UART_MCR_RTS),
+		!!((ch->ch_mistat | ch->ch_mostat) & UART_MSR_CTS),
+		!!((ch->ch_mistat | ch->ch_mostat) & UART_MSR_DSR),
+		!!((ch->ch_mistat | ch->ch_mostat) & UART_MSR_RI),
+		!!((ch->ch_mistat | ch->ch_mostat) & UART_MSR_DCD));
+}
+
+/* Make the UART raise any of the output signals we want up */
+static void neo_assert_modem_signals(struct jsm_channel *ch)
+{
+	if (!ch)
+		return;
+
+	writeb(ch->ch_mostat, &ch->ch_neo_uart->mcr);
+
+	/* flush write operation */
+	neo_pci_posting_flush(ch->ch_bd);
+}
+
+/*
+ * Flush the WRITE FIFO on the Neo.
+ *
+ * NOTE: Channel lock MUST be held before calling this function!
+ */
+static void neo_flush_uart_write(struct jsm_channel *ch)
+{
+	u8 tmp = 0;
+	int i = 0;
+
+	if (!ch)
+		return;
+
+	writeb((UART_FCR_ENABLE_FIFO | UART_FCR_CLEAR_XMIT), &ch->ch_neo_uart->isr_fcr);
+
+	for (i = 0; i < 10; i++) {
+
+		/* Check to see if the UART feels it completely flushed the FIFO. */
+		tmp = readb(&ch->ch_neo_uart->isr_fcr);
+		if (tmp & 4) {
+			jsm_printk(IOCTL, INFO, &ch->ch_bd->pci_dev,
+					"Still flushing TX UART... i: %d\n", i);
+			udelay(10);
+		}
+		else
+			break;
+	}
+
+	ch->ch_flags |= (CH_TX_FIFO_EMPTY | CH_TX_FIFO_LWM);
+}
+
+
+/*
+ * Flush the READ FIFO on the Neo.
+ *
+ * NOTE: Channel lock MUST be held before calling this function!
+ */
+static void neo_flush_uart_read(struct jsm_channel *ch)
+{
+	u8 tmp = 0;
+	int i = 0;
+
+	if (!ch)
+		return;
+
+	writeb((UART_FCR_ENABLE_FIFO | UART_FCR_CLEAR_RCVR), &ch->ch_neo_uart->isr_fcr);
+
+	for (i = 0; i < 10; i++) {
+
+		/* Check to see if the UART feels it completely flushed the FIFO. */
+		tmp = readb(&ch->ch_neo_uart->isr_fcr);
+		if (tmp & 2) {
+			jsm_printk(IOCTL, INFO, &ch->ch_bd->pci_dev,
+					"Still flushing RX UART... i: %d\n", i);
+			udelay(10);
+		}
+		else
+			break;
+	}
+}
+
+/*
+ * No locks are assumed to be held when calling this function.
+ */
+static void neo_clear_break(struct jsm_channel *ch, int force)
+{
+	unsigned long lock_flags;
+
+	spin_lock_irqsave(&ch->ch_lock, lock_flags);
+
+	/* Turn break off, and unset some variables */
+	if (ch->ch_flags & CH_BREAK_SENDING) {
+		u8 temp = readb(&ch->ch_neo_uart->lcr);
+		writeb((temp & ~UART_LCR_SBC), &ch->ch_neo_uart->lcr);
+
+		ch->ch_flags &= ~(CH_BREAK_SENDING);
+		jsm_printk(IOCTL, INFO, &ch->ch_bd->pci_dev,
+				"clear break Finishing UART_LCR_SBC! finished: %lx\n", jiffies);
+
+		/* flush write operation */
+		neo_pci_posting_flush(ch->ch_bd);
+	}
+	spin_unlock_irqrestore(&ch->ch_lock, lock_flags);
+}
+
+/*
+ * Parse the ISR register.
+ */
+static inline void neo_parse_isr(struct jsm_board *brd, u32 port)
+{
+	struct jsm_channel *ch;
+	u8 isr;
+	u8 cause;
+	unsigned long lock_flags;
+
+	if (!brd)
+		return;
+
+	if (port > brd->maxports)
+		return;
+
+	ch = brd->channels[port];
+	if (!ch)
+		return;
+
+	/* Here we try to figure out what caused the interrupt to happen */
+	while (1) {
+
+		isr = readb(&ch->ch_neo_uart->isr_fcr);
+
+		/* Bail if no pending interrupt */
+		if (isr & UART_IIR_NO_INT)
+			break;
+
+		/*
+		 * Yank off the upper 2 bits, which just show that the FIFO's are enabled.
+		 */
+		isr &= ~(UART_17158_IIR_FIFO_ENABLED);
+
+		jsm_printk(INTR, INFO, &ch->ch_bd->pci_dev,
+				"%s:%d isr: %x\n", __FILE__, __LINE__, isr);
+
+		if (isr & (UART_17158_IIR_RDI_TIMEOUT | UART_IIR_RDI)) {
+			/* Read data from uart -> queue */
+			neo_copy_data_from_uart_to_queue(ch);
+
+			/* Call our tty layer to enforce queue flow control if needed. */
+			spin_lock_irqsave(&ch->ch_lock, lock_flags);
+			jsm_check_queue_flow_control(ch);
+			spin_unlock_irqrestore(&ch->ch_lock, lock_flags);
+		}
+
+		if (isr & UART_IIR_THRI) {
+			/* Transfer data (if any) from Write Queue -> UART. */
+			spin_lock_irqsave(&ch->ch_lock, lock_flags);
+			ch->ch_flags |= (CH_TX_FIFO_EMPTY | CH_TX_FIFO_LWM);
+			spin_unlock_irqrestore(&ch->ch_lock, lock_flags);
+			neo_copy_data_from_queue_to_uart(ch);
+		}
+
+		if (isr & UART_17158_IIR_XONXOFF) {
+			cause = readb(&ch->ch_neo_uart->xoffchar1);
+
+			jsm_printk(INTR, INFO, &ch->ch_bd->pci_dev,
+					"Port %d. Got ISR_XONXOFF: cause:%x\n", port, cause);
+
+			/*
+			 * Since the UART detected either an XON or
+			 * XOFF match, we need to figure out which
+			 * one it was, so we can suspend or resume data flow.
+			 */
+			spin_lock_irqsave(&ch->ch_lock, lock_flags);
+			if (cause == UART_17158_XON_DETECT) {
+				/* Is output stopped right now, if so, resume it */
+				if (brd->channels[port]->ch_flags & CH_STOP) {
+					ch->ch_flags &= ~(CH_STOP);
+				}
+				jsm_printk(INTR, INFO, &ch->ch_bd->pci_dev,
+						"Port %d. XON detected in incoming data\n", port);
+			}
+			else if (cause == UART_17158_XOFF_DETECT) {
+				if (!(brd->channels[port]->ch_flags & CH_STOP)) {
+					ch->ch_flags |= CH_STOP;
+					jsm_printk(INTR, INFO, &ch->ch_bd->pci_dev,
+							"Setting CH_STOP\n");
+				}
+				jsm_printk(INTR, INFO, &ch->ch_bd->pci_dev,
+						"Port: %d. XOFF detected in incoming data\n", port);
+			}
+			spin_unlock_irqrestore(&ch->ch_lock, lock_flags);
+		}
+
+		if (isr & UART_17158_IIR_HWFLOW_STATE_CHANGE) {
+			/*
+			 * If we get here, this means the hardware is doing auto flow control.
+			 * Check to see whether RTS/DTR or CTS/DSR caused this interrupt.
+			 */
+			cause = readb(&ch->ch_neo_uart->mcr);
+
+			/* Which pin is doing auto flow? RTS or DTR? */
+			spin_lock_irqsave(&ch->ch_lock, lock_flags);
+			if ((cause & 0x4) == 0) {
+				if (cause & UART_MCR_RTS)
+					ch->ch_mostat |= UART_MCR_RTS;
+				else
+					ch->ch_mostat &= ~(UART_MCR_RTS);
+			} else {
+				if (cause & UART_MCR_DTR)
+					ch->ch_mostat |= UART_MCR_DTR;
+				else
+					ch->ch_mostat &= ~(UART_MCR_DTR);
+			}
+			spin_unlock_irqrestore(&ch->ch_lock, lock_flags);
+		}
+
+		/* Parse any modem signal changes */
+		jsm_printk(INTR, INFO, &ch->ch_bd->pci_dev,
+				"MOD_STAT: sending to parse_modem_sigs\n");
+		neo_parse_modem(ch, readb(&ch->ch_neo_uart->msr));
+	}
+}
+
+static inline void neo_parse_lsr(struct jsm_board *brd, u32 port)
+{
+	struct jsm_channel *ch;
+	int linestatus;
+	unsigned long lock_flags;
+
+	if (!brd)
+		return;
+
+	if (port > brd->maxports)
+		return;
+
+	ch = brd->channels[port];
+	if (!ch)
+		return;
+
+	linestatus = readb(&ch->ch_neo_uart->lsr);
+
+	jsm_printk(INTR, INFO, &ch->ch_bd->pci_dev,
+			"%s:%d port: %d linestatus: %x\n", __FILE__, __LINE__, port, linestatus);
+
+	ch->ch_cached_lsr |= linestatus;
+
+	if (ch->ch_cached_lsr & UART_LSR_DR) {
+		/* Read data from uart -> queue */
+		neo_copy_data_from_uart_to_queue(ch);
+		spin_lock_irqsave(&ch->ch_lock, lock_flags);
+		jsm_check_queue_flow_control(ch);
+		spin_unlock_irqrestore(&ch->ch_lock, lock_flags);
+	}
+
+	/*
+	 * This is a special flag. It indicates that at least 1
+	 * RX error (parity, framing, or break) has happened.
+	 * Mark this in our struct, which will tell me that I have
+	 *to do the special RX+LSR read for this FIFO load.
+	 */
+	if (linestatus & UART_17158_RX_FIFO_DATA_ERROR)
+		jsm_printk(INTR, DEBUG, &ch->ch_bd->pci_dev,
+			"%s:%d Port: %d Got an RX error, need to parse LSR\n",
+			__FILE__, __LINE__, port);
+
+	/*
+	 * The next 3 tests should *NOT* happen, as the above test
+	 * should encapsulate all 3... At least, thats what Exar says.
+	 */
+
+	if (linestatus & UART_LSR_PE) {
+		ch->ch_err_parity++;
+		jsm_printk(INTR, DEBUG, &ch->ch_bd->pci_dev,
+			"%s:%d Port: %d. PAR ERR!\n", __FILE__, __LINE__, port);
+	}
+
+	if (linestatus & UART_LSR_FE) {
+		ch->ch_err_frame++;
+		jsm_printk(INTR, DEBUG, &ch->ch_bd->pci_dev,
+			"%s:%d Port: %d. FRM ERR!\n", __FILE__, __LINE__, port);
+	}
+
+	if (linestatus & UART_LSR_BI) {
+		ch->ch_err_break++;
+		jsm_printk(INTR, DEBUG, &ch->ch_bd->pci_dev,
+			"%s:%d Port: %d. BRK INTR!\n", __FILE__, __LINE__, port);
+	}
+
+	if (linestatus & UART_LSR_OE) {
+		/*
+		 * Rx Oruns. Exar says that an orun will NOT corrupt
+		 * the FIFO. It will just replace the holding register
+		 * with this new data byte. So basically just ignore this.
+		 * Probably we should eventually have an orun stat in our driver...
+		 */
+		ch->ch_err_overrun++;
+		jsm_printk(INTR, DEBUG, &ch->ch_bd->pci_dev,
+			"%s:%d Port: %d. Rx Overrun!\n", __FILE__, __LINE__, port);
+	}
+
+	if (linestatus & UART_LSR_THRE) {
+		spin_lock_irqsave(&ch->ch_lock, lock_flags);
+		ch->ch_flags |= (CH_TX_FIFO_EMPTY | CH_TX_FIFO_LWM);
+		spin_unlock_irqrestore(&ch->ch_lock, lock_flags);
+
+		/* Transfer data (if any) from Write Queue -> UART. */
+		neo_copy_data_from_queue_to_uart(ch);
+	}
+	else if (linestatus & UART_17158_TX_AND_FIFO_CLR) {
+		spin_lock_irqsave(&ch->ch_lock, lock_flags);
+		ch->ch_flags |= (CH_TX_FIFO_EMPTY | CH_TX_FIFO_LWM);
+		spin_unlock_irqrestore(&ch->ch_lock, lock_flags);
+
+		/* Transfer data (if any) from Write Queue -> UART. */
+		neo_copy_data_from_queue_to_uart(ch);
+	}
+}
+
+/*
+ * neo_param()
+ * Send any/all changes to the line to the UART.
+ */
+static void neo_param(struct jsm_channel *ch)
+{
+	u8 lcr = 0;
+	u8 uart_lcr, ier;
+	u32 baud;
+	int quot;
+	struct jsm_board *bd;
+
+	bd = ch->ch_bd;
+	if (!bd)
+		return;
+
+	/*
+	 * If baud rate is zero, flush queues, and set mval to drop DTR.
+	 */
+	if ((ch->ch_c_cflag & (CBAUD)) == 0) {
+		ch->ch_r_head = ch->ch_r_tail = 0;
+		ch->ch_e_head = ch->ch_e_tail = 0;
+
+		neo_flush_uart_write(ch);
+		neo_flush_uart_read(ch);
+
+		ch->ch_flags |= (CH_BAUD0);
+		ch->ch_mostat &= ~(UART_MCR_RTS | UART_MCR_DTR);
+		neo_assert_modem_signals(ch);
+		return;
+
+	} else {
+		int i;
+		unsigned int cflag;
+		static struct {
+			unsigned int rate;
+			unsigned int cflag;
+		} baud_rates[] = {
+			{ 921600, B921600 },
+			{ 460800, B460800 },
+			{ 230400, B230400 },
+			{ 115200, B115200 },
+			{  57600, B57600  },
+			{  38400, B38400  },
+			{  19200, B19200  },
+			{   9600, B9600   },
+			{   4800, B4800   },
+			{   2400, B2400   },
+			{   1200, B1200   },
+			{    600, B600    },
+			{    300, B300    },
+			{    200, B200    },
+			{    150, B150    },
+			{    134, B134    },
+			{    110, B110    },
+			{     75, B75     },
+			{     50, B50     },
+		};
+
+		cflag = C_BAUD(ch->uart_port.state->port.tty);
+		baud = 9600;
+		for (i = 0; i < ARRAY_SIZE(baud_rates); i++) {
+			if (baud_rates[i].cflag == cflag) {
+				baud = baud_rates[i].rate;
+				break;
+			}
+		}
+
+		if (ch->ch_flags & CH_BAUD0)
+			ch->ch_flags &= ~(CH_BAUD0);
+	}
+
+	if (ch->ch_c_cflag & PARENB)
+		lcr |= UART_LCR_PARITY;
+
+	if (!(ch->ch_c_cflag & PARODD))
+		lcr |= UART_LCR_EPAR;
+
+	/*
+	 * Not all platforms support mark/space parity,
+	 * so this will hide behind an ifdef.
+	 */
+#ifdef CMSPAR
+	if (ch->ch_c_cflag & CMSPAR)
+		lcr |= UART_LCR_SPAR;
+#endif
+
+	if (ch->ch_c_cflag & CSTOPB)
+		lcr |= UART_LCR_STOP;
+
+	switch (ch->ch_c_cflag & CSIZE) {
+		case CS5:
+			lcr |= UART_LCR_WLEN5;
+			break;
+		case CS6:
+			lcr |= UART_LCR_WLEN6;
+			break;
+		case CS7:
+			lcr |= UART_LCR_WLEN7;
+			break;
+		case CS8:
+		default:
+			lcr |= UART_LCR_WLEN8;
+		break;
+	}
+
+	ier = readb(&ch->ch_neo_uart->ier);
+	uart_lcr = readb(&ch->ch_neo_uart->lcr);
+
+	if (baud == 0)
+		baud = 9600;
+
+	quot = ch->ch_bd->bd_dividend / baud;
+
+	if (quot != 0) {
+		writeb(UART_LCR_DLAB, &ch->ch_neo_uart->lcr);
+		writeb((quot & 0xff), &ch->ch_neo_uart->txrx);
+		writeb((quot >> 8), &ch->ch_neo_uart->ier);
+		writeb(lcr, &ch->ch_neo_uart->lcr);
+	}
+
+	if (uart_lcr != lcr)
+		writeb(lcr, &ch->ch_neo_uart->lcr);
+
+	if (ch->ch_c_cflag & CREAD)
+		ier |= (UART_IER_RDI | UART_IER_RLSI);
+
+	ier |= (UART_IER_THRI | UART_IER_MSI);
+
+	writeb(ier, &ch->ch_neo_uart->ier);
+
+	/* Set new start/stop chars */
+	neo_set_new_start_stop_chars(ch);
+
+	if (ch->ch_c_cflag & CRTSCTS)
+		neo_set_cts_flow_control(ch);
+	else if (ch->ch_c_iflag & IXON) {
+		/* If start/stop is set to disable, then we should disable flow control */
+		if ((ch->ch_startc == __DISABLED_CHAR) || (ch->ch_stopc == __DISABLED_CHAR))
+			neo_set_no_output_flow_control(ch);
+		else
+			neo_set_ixon_flow_control(ch);
+	}
+	else
+		neo_set_no_output_flow_control(ch);
+
+	if (ch->ch_c_cflag & CRTSCTS)
+		neo_set_rts_flow_control(ch);
+	else if (ch->ch_c_iflag & IXOFF) {
+		/* If start/stop is set to disable, then we should disable flow control */
+		if ((ch->ch_startc == __DISABLED_CHAR) || (ch->ch_stopc == __DISABLED_CHAR))
+			neo_set_no_input_flow_control(ch);
+		else
+			neo_set_ixoff_flow_control(ch);
+	}
+	else
+		neo_set_no_input_flow_control(ch);
+	/*
+	 * Adjust the RX FIFO Trigger level if baud is less than 9600.
+	 * Not exactly elegant, but this is needed because of the Exar chip's
+	 * delay on firing off the RX FIFO interrupt on slower baud rates.
+	 */
+	if (baud < 9600) {
+		writeb(1, &ch->ch_neo_uart->rfifo);
+		ch->ch_r_tlevel = 1;
+	}
+
+	neo_assert_modem_signals(ch);
+
+	/* Get current status of the modem signals now */
+	neo_parse_modem(ch, readb(&ch->ch_neo_uart->msr));
+	return;
+}
+
+/*
+ * jsm_neo_intr()
+ *
+ * Neo specific interrupt handler.
+ */
+static irqreturn_t neo_intr(int irq, void *voidbrd)
+{
+	struct jsm_board *brd = voidbrd;
+	struct jsm_channel *ch;
+	int port = 0;
+	int type = 0;
+	int current_port;
+	u32 tmp;
+	u32 uart_poll;
+	unsigned long lock_flags;
+	unsigned long lock_flags2;
+	int outofloop_count = 0;
+
+	/* Lock out the slow poller from running on this board. */
+	spin_lock_irqsave(&brd->bd_intr_lock, lock_flags);
+
+	/*
+	 * Read in "extended" IRQ information from the 32bit Neo register.
+	 * Bits 0-7: What port triggered the interrupt.
+	 * Bits 8-31: Each 3bits indicate what type of interrupt occurred.
+	 */
+	uart_poll = readl(brd->re_map_membase + UART_17158_POLL_ADDR_OFFSET);
+
+	jsm_printk(INTR, INFO, &brd->pci_dev,
+		"%s:%d uart_poll: %x\n", __FILE__, __LINE__, uart_poll);
+
+	if (!uart_poll) {
+		jsm_printk(INTR, INFO, &brd->pci_dev,
+			"Kernel interrupted to me, but no pending interrupts...\n");
+		spin_unlock_irqrestore(&brd->bd_intr_lock, lock_flags);
+		return IRQ_NONE;
+	}
+
+	/* At this point, we have at least SOMETHING to service, dig further... */
+
+	current_port = 0;
+
+	/* Loop on each port */
+	while (((uart_poll & 0xff) != 0) && (outofloop_count < 0xff)){
+
+		tmp = uart_poll;
+		outofloop_count++;
+
+		/* Check current port to see if it has interrupt pending */
+		if ((tmp & jsm_offset_table[current_port]) != 0) {
+			port = current_port;
+			type = tmp >> (8 + (port * 3));
+			type &= 0x7;
+		} else {
+			current_port++;
+			continue;
+		}
+
+		jsm_printk(INTR, INFO, &brd->pci_dev,
+		"%s:%d port: %x type: %x\n", __FILE__, __LINE__, port, type);
+
+		/* Remove this port + type from uart_poll */
+		uart_poll &= ~(jsm_offset_table[port]);
+
+		if (!type) {
+			/* If no type, just ignore it, and move onto next port */
+			jsm_printk(INTR, ERR, &brd->pci_dev,
+				"Interrupt with no type! port: %d\n", port);
+			continue;
+		}
+
+		/* Switch on type of interrupt we have */
+		switch (type) {
+
+		case UART_17158_RXRDY_TIMEOUT:
+			/*
+			 * RXRDY Time-out is cleared by reading data in the
+			* RX FIFO until it falls below the trigger level.
+			 */
+
+			/* Verify the port is in range. */
+			if (port > brd->nasync)
+				continue;
+
+			ch = brd->channels[port];
+			neo_copy_data_from_uart_to_queue(ch);
+
+			/* Call our tty layer to enforce queue flow control if needed. */
+			spin_lock_irqsave(&ch->ch_lock, lock_flags2);
+			jsm_check_queue_flow_control(ch);
+			spin_unlock_irqrestore(&ch->ch_lock, lock_flags2);
+
+			continue;
+
+		case UART_17158_RX_LINE_STATUS:
+			/*
+			 * RXRDY and RX LINE Status (logic OR of LSR[4:1])
+			 */
+			neo_parse_lsr(brd, port);
+			continue;
+
+		case UART_17158_TXRDY:
+			/*
+			 * TXRDY interrupt clears after reading ISR register for the UART channel.
+			 */
+
+			/*
+			 * Yes, this is odd...
+			 * Why would I check EVERY possibility of type of
+			 * interrupt, when we know its TXRDY???
+			 * Becuz for some reason, even tho we got triggered for TXRDY,
+			 * it seems to be occasionally wrong. Instead of TX, which
+			 * it should be, I was getting things like RXDY too. Weird.
+			 */
+			neo_parse_isr(brd, port);
+			continue;
+
+		case UART_17158_MSR:
+			/*
+			 * MSR or flow control was seen.
+			 */
+			neo_parse_isr(brd, port);
+			continue;
+
+		default:
+			/*
+			 * The UART triggered us with a bogus interrupt type.
+			 * It appears the Exar chip, when REALLY bogged down, will throw
+			 * these once and awhile.
+			 * Its harmless, just ignore it and move on.
+			 */
+			jsm_printk(INTR, ERR, &brd->pci_dev,
+				"%s:%d Unknown Interrupt type: %x\n", __FILE__, __LINE__, type);
+			continue;
+		}
+	}
+
+	spin_unlock_irqrestore(&brd->bd_intr_lock, lock_flags);
+
+	jsm_printk(INTR, INFO, &brd->pci_dev, "finish.\n");
+	return IRQ_HANDLED;
+}
+
+/*
+ * Neo specific way of turning off the receiver.
+ * Used as a way to enforce queue flow control when in
+ * hardware flow control mode.
+ */
+static void neo_disable_receiver(struct jsm_channel *ch)
+{
+	u8 tmp = readb(&ch->ch_neo_uart->ier);
+	tmp &= ~(UART_IER_RDI);
+	writeb(tmp, &ch->ch_neo_uart->ier);
+
+	/* flush write operation */
+	neo_pci_posting_flush(ch->ch_bd);
+}
+
+
+/*
+ * Neo specific way of turning on the receiver.
+ * Used as a way to un-enforce queue flow control when in
+ * hardware flow control mode.
+ */
+static void neo_enable_receiver(struct jsm_channel *ch)
+{
+	u8 tmp = readb(&ch->ch_neo_uart->ier);
+	tmp |= (UART_IER_RDI);
+	writeb(tmp, &ch->ch_neo_uart->ier);
+
+	/* flush write operation */
+	neo_pci_posting_flush(ch->ch_bd);
+}
+
+static void neo_send_start_character(struct jsm_channel *ch)
+{
+	if (!ch)
+		return;
+
+	if (ch->ch_startc != __DISABLED_CHAR) {
+		ch->ch_xon_sends++;
+		writeb(ch->ch_startc, &ch->ch_neo_uart->txrx);
+
+		/* flush write operation */
+		neo_pci_posting_flush(ch->ch_bd);
+	}
+}
+
+static void neo_send_stop_character(struct jsm_channel *ch)
+{
+	if (!ch)
+		return;
+
+	if (ch->ch_stopc != __DISABLED_CHAR) {
+		ch->ch_xoff_sends++;
+		writeb(ch->ch_stopc, &ch->ch_neo_uart->txrx);
+
+		/* flush write operation */
+		neo_pci_posting_flush(ch->ch_bd);
+	}
+}
+
+/*
+ * neo_uart_init
+ */
+static void neo_uart_init(struct jsm_channel *ch)
+{
+	writeb(0, &ch->ch_neo_uart->ier);
+	writeb(0, &ch->ch_neo_uart->efr);
+	writeb(UART_EFR_ECB, &ch->ch_neo_uart->efr);
+
+	/* Clear out UART and FIFO */
+	readb(&ch->ch_neo_uart->txrx);
+	writeb((UART_FCR_ENABLE_FIFO|UART_FCR_CLEAR_RCVR|UART_FCR_CLEAR_XMIT), &ch->ch_neo_uart->isr_fcr);
+	readb(&ch->ch_neo_uart->lsr);
+	readb(&ch->ch_neo_uart->msr);
+
+	ch->ch_flags |= CH_FIFO_ENABLED;
+
+	/* Assert any signals we want up */
+	writeb(ch->ch_mostat, &ch->ch_neo_uart->mcr);
+}
+
+/*
+ * Make the UART completely turn off.
+ */
+static void neo_uart_off(struct jsm_channel *ch)
+{
+	/* Turn off UART enhanced bits */
+	writeb(0, &ch->ch_neo_uart->efr);
+
+	/* Stop all interrupts from occurring. */
+	writeb(0, &ch->ch_neo_uart->ier);
+}
+
+static u32 neo_get_uart_bytes_left(struct jsm_channel *ch)
+{
+	u8 left = 0;
+	u8 lsr = readb(&ch->ch_neo_uart->lsr);
+
+	/* We must cache the LSR as some of the bits get reset once read... */
+	ch->ch_cached_lsr |= lsr;
+
+	/* Determine whether the Transmitter is empty or not */
+	if (!(lsr & UART_LSR_TEMT))
+		left = 1;
+	else {
+		ch->ch_flags |= (CH_TX_FIFO_EMPTY | CH_TX_FIFO_LWM);
+		left = 0;
+	}
+
+	return left;
+}
+
+/* Channel lock MUST be held by the calling function! */
+static void neo_send_break(struct jsm_channel *ch)
+{
+	/*
+	 * Set the time we should stop sending the break.
+	 * If we are already sending a break, toss away the existing
+	 * time to stop, and use this new value instead.
+	 */
+
+	/* Tell the UART to start sending the break */
+	if (!(ch->ch_flags & CH_BREAK_SENDING)) {
+		u8 temp = readb(&ch->ch_neo_uart->lcr);
+		writeb((temp | UART_LCR_SBC), &ch->ch_neo_uart->lcr);
+		ch->ch_flags |= (CH_BREAK_SENDING);
+
+		/* flush write operation */
+		neo_pci_posting_flush(ch->ch_bd);
+	}
+}
+
+/*
+ * neo_send_immediate_char.
+ *
+ * Sends a specific character as soon as possible to the UART,
+ * jumping over any bytes that might be in the write queue.
+ *
+ * The channel lock MUST be held by the calling function.
+ */
+static void neo_send_immediate_char(struct jsm_channel *ch, unsigned char c)
+{
+	if (!ch)
+		return;
+
+	writeb(c, &ch->ch_neo_uart->txrx);
+
+	/* flush write operation */
+	neo_pci_posting_flush(ch->ch_bd);
+}
+
+struct board_ops jsm_neo_ops = {
+	.intr				= neo_intr,
+	.uart_init			= neo_uart_init,
+	.uart_off			= neo_uart_off,
+	.param				= neo_param,
+	.assert_modem_signals		= neo_assert_modem_signals,
+	.flush_uart_write		= neo_flush_uart_write,
+	.flush_uart_read		= neo_flush_uart_read,
+	.disable_receiver		= neo_disable_receiver,
+	.enable_receiver		= neo_enable_receiver,
+	.send_break			= neo_send_break,
+	.clear_break			= neo_clear_break,
+	.send_start_character		= neo_send_start_character,
+	.send_stop_character		= neo_send_stop_character,
+	.copy_data_from_queue_to_uart	= neo_copy_data_from_queue_to_uart,
+	.get_uart_bytes_left		= neo_get_uart_bytes_left,
+	.send_immediate_char		= neo_send_immediate_char
+};
diff --git a/ap/os/linux/linux-3.4.x/drivers/tty/serial/jsm/jsm_tty.c b/ap/os/linux/linux-3.4.x/drivers/tty/serial/jsm/jsm_tty.c
new file mode 100644
index 0000000..434bd88
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/tty/serial/jsm/jsm_tty.c
@@ -0,0 +1,842 @@
+/************************************************************************
+ * Copyright 2003 Digi International (www.digi.com)
+ *
+ * Copyright (C) 2004 IBM Corporation. All rights reserved.
+ *
+ * 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, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY, EXPRESS OR IMPLIED; 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, write to the Free Software
+ * Foundation, Inc., 59 * Temple Place - Suite 330, Boston,
+ * MA  02111-1307, USA.
+ *
+ * Contact Information:
+ * Scott H Kilau <Scott_Kilau@digi.com>
+ * Ananda Venkatarman <mansarov@us.ibm.com>
+ * Modifications:
+ * 01/19/06:	changed jsm_input routine to use the dynamically allocated
+ *		tty_buffer changes. Contributors: Scott Kilau and Ananda V.
+ ***********************************************************************/
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
+#include <linux/serial_reg.h>
+#include <linux/delay.h>	/* For udelay */
+#include <linux/pci.h>
+#include <linux/slab.h>
+
+#include "jsm.h"
+
+static DECLARE_BITMAP(linemap, MAXLINES);
+
+static void jsm_carrier(struct jsm_channel *ch);
+
+static inline int jsm_get_mstat(struct jsm_channel *ch)
+{
+	unsigned char mstat;
+	unsigned result;
+
+	jsm_printk(IOCTL, INFO, &ch->ch_bd->pci_dev, "start\n");
+
+	mstat = (ch->ch_mostat | ch->ch_mistat);
+
+	result = 0;
+
+	if (mstat & UART_MCR_DTR)
+		result |= TIOCM_DTR;
+	if (mstat & UART_MCR_RTS)
+		result |= TIOCM_RTS;
+	if (mstat & UART_MSR_CTS)
+		result |= TIOCM_CTS;
+	if (mstat & UART_MSR_DSR)
+		result |= TIOCM_DSR;
+	if (mstat & UART_MSR_RI)
+		result |= TIOCM_RI;
+	if (mstat & UART_MSR_DCD)
+		result |= TIOCM_CD;
+
+	jsm_printk(IOCTL, INFO, &ch->ch_bd->pci_dev, "finish\n");
+	return result;
+}
+
+static unsigned int jsm_tty_tx_empty(struct uart_port *port)
+{
+	return TIOCSER_TEMT;
+}
+
+/*
+ * Return modem signals to ld.
+ */
+static unsigned int jsm_tty_get_mctrl(struct uart_port *port)
+{
+	int result;
+	struct jsm_channel *channel = (struct jsm_channel *)port;
+
+	jsm_printk(IOCTL, INFO, &channel->ch_bd->pci_dev, "start\n");
+
+	result = jsm_get_mstat(channel);
+
+	if (result < 0)
+		return -ENXIO;
+
+	jsm_printk(IOCTL, INFO, &channel->ch_bd->pci_dev, "finish\n");
+
+	return result;
+}
+
+/*
+ * jsm_set_modem_info()
+ *
+ * Set modem signals, called by ld.
+ */
+static void jsm_tty_set_mctrl(struct uart_port *port, unsigned int mctrl)
+{
+	struct jsm_channel *channel = (struct jsm_channel *)port;
+
+	jsm_printk(IOCTL, INFO, &channel->ch_bd->pci_dev, "start\n");
+
+	if (mctrl & TIOCM_RTS)
+		channel->ch_mostat |= UART_MCR_RTS;
+	else
+		channel->ch_mostat &= ~UART_MCR_RTS;
+
+	if (mctrl & TIOCM_DTR)
+		channel->ch_mostat |= UART_MCR_DTR;
+	else
+		channel->ch_mostat &= ~UART_MCR_DTR;
+
+	channel->ch_bd->bd_ops->assert_modem_signals(channel);
+
+	jsm_printk(IOCTL, INFO, &channel->ch_bd->pci_dev, "finish\n");
+	udelay(10);
+}
+
+/*
+ * jsm_tty_write()
+ *
+ * Take data from the user or kernel and send it out to the FEP.
+ * In here exists all the Transparent Print magic as well.
+ */
+static void jsm_tty_write(struct uart_port *port)
+{
+	struct jsm_channel *channel;
+	channel = container_of(port, struct jsm_channel, uart_port);
+	channel->ch_bd->bd_ops->copy_data_from_queue_to_uart(channel);
+}
+
+static void jsm_tty_start_tx(struct uart_port *port)
+{
+	struct jsm_channel *channel = (struct jsm_channel *)port;
+
+	jsm_printk(IOCTL, INFO, &channel->ch_bd->pci_dev, "start\n");
+
+	channel->ch_flags &= ~(CH_STOP);
+	jsm_tty_write(port);
+
+	jsm_printk(IOCTL, INFO, &channel->ch_bd->pci_dev, "finish\n");
+}
+
+static void jsm_tty_stop_tx(struct uart_port *port)
+{
+	struct jsm_channel *channel = (struct jsm_channel *)port;
+
+	jsm_printk(IOCTL, INFO, &channel->ch_bd->pci_dev, "start\n");
+
+	channel->ch_flags |= (CH_STOP);
+
+	jsm_printk(IOCTL, INFO, &channel->ch_bd->pci_dev, "finish\n");
+}
+
+static void jsm_tty_send_xchar(struct uart_port *port, char ch)
+{
+	unsigned long lock_flags;
+	struct jsm_channel *channel = (struct jsm_channel *)port;
+	struct ktermios *termios;
+
+	spin_lock_irqsave(&port->lock, lock_flags);
+	termios = port->state->port.tty->termios;
+	if (ch == termios->c_cc[VSTART])
+		channel->ch_bd->bd_ops->send_start_character(channel);
+
+	if (ch == termios->c_cc[VSTOP])
+		channel->ch_bd->bd_ops->send_stop_character(channel);
+	spin_unlock_irqrestore(&port->lock, lock_flags);
+}
+
+static void jsm_tty_stop_rx(struct uart_port *port)
+{
+	struct jsm_channel *channel = (struct jsm_channel *)port;
+
+	channel->ch_bd->bd_ops->disable_receiver(channel);
+}
+
+static void jsm_tty_enable_ms(struct uart_port *port)
+{
+	/* Nothing needed */
+}
+
+static void jsm_tty_break(struct uart_port *port, int break_state)
+{
+	unsigned long lock_flags;
+	struct jsm_channel *channel = (struct jsm_channel *)port;
+
+	spin_lock_irqsave(&port->lock, lock_flags);
+	if (break_state == -1)
+		channel->ch_bd->bd_ops->send_break(channel);
+	else
+		channel->ch_bd->bd_ops->clear_break(channel, 0);
+
+	spin_unlock_irqrestore(&port->lock, lock_flags);
+}
+
+static int jsm_tty_open(struct uart_port *port)
+{
+	struct jsm_board *brd;
+	struct jsm_channel *channel = (struct jsm_channel *)port;
+	struct ktermios *termios;
+
+	/* Get board pointer from our array of majors we have allocated */
+	brd = channel->ch_bd;
+
+	/*
+	 * Allocate channel buffers for read/write/error.
+	 * Set flag, so we don't get trounced on.
+	 */
+	channel->ch_flags |= (CH_OPENING);
+
+	/* Drop locks, as malloc with GFP_KERNEL can sleep */
+
+	if (!channel->ch_rqueue) {
+		channel->ch_rqueue = kzalloc(RQUEUESIZE, GFP_KERNEL);
+		if (!channel->ch_rqueue) {
+			jsm_printk(INIT, ERR, &channel->ch_bd->pci_dev,
+				"unable to allocate read queue buf");
+			return -ENOMEM;
+		}
+	}
+	if (!channel->ch_equeue) {
+		channel->ch_equeue = kzalloc(EQUEUESIZE, GFP_KERNEL);
+		if (!channel->ch_equeue) {
+			jsm_printk(INIT, ERR, &channel->ch_bd->pci_dev,
+				"unable to allocate error queue buf");
+			return -ENOMEM;
+		}
+	}
+
+	channel->ch_flags &= ~(CH_OPENING);
+	/*
+	 * Initialize if neither terminal is open.
+	 */
+	jsm_printk(OPEN, INFO, &channel->ch_bd->pci_dev,
+		"jsm_open: initializing channel in open...\n");
+
+	/*
+	 * Flush input queues.
+	 */
+	channel->ch_r_head = channel->ch_r_tail = 0;
+	channel->ch_e_head = channel->ch_e_tail = 0;
+
+	brd->bd_ops->flush_uart_write(channel);
+	brd->bd_ops->flush_uart_read(channel);
+
+	channel->ch_flags = 0;
+	channel->ch_cached_lsr = 0;
+	channel->ch_stops_sent = 0;
+
+	termios = port->state->port.tty->termios;
+	channel->ch_c_cflag	= termios->c_cflag;
+	channel->ch_c_iflag	= termios->c_iflag;
+	channel->ch_c_oflag	= termios->c_oflag;
+	channel->ch_c_lflag	= termios->c_lflag;
+	channel->ch_startc	= termios->c_cc[VSTART];
+	channel->ch_stopc	= termios->c_cc[VSTOP];
+
+	/* Tell UART to init itself */
+	brd->bd_ops->uart_init(channel);
+
+	/*
+	 * Run param in case we changed anything
+	 */
+	brd->bd_ops->param(channel);
+
+	jsm_carrier(channel);
+
+	channel->ch_open_count++;
+
+	jsm_printk(OPEN, INFO, &channel->ch_bd->pci_dev, "finish\n");
+	return 0;
+}
+
+static void jsm_tty_close(struct uart_port *port)
+{
+	struct jsm_board *bd;
+	struct ktermios *ts;
+	struct jsm_channel *channel = (struct jsm_channel *)port;
+
+	jsm_printk(CLOSE, INFO, &channel->ch_bd->pci_dev, "start\n");
+
+	bd = channel->ch_bd;
+	ts = port->state->port.tty->termios;
+
+	channel->ch_flags &= ~(CH_STOPI);
+
+	channel->ch_open_count--;
+
+	/*
+	 * If we have HUPCL set, lower DTR and RTS
+	 */
+	if (channel->ch_c_cflag & HUPCL) {
+		jsm_printk(CLOSE, INFO, &channel->ch_bd->pci_dev,
+			"Close. HUPCL set, dropping DTR/RTS\n");
+
+		/* Drop RTS/DTR */
+		channel->ch_mostat &= ~(UART_MCR_DTR | UART_MCR_RTS);
+		bd->bd_ops->assert_modem_signals(channel);
+	}
+
+	/* Turn off UART interrupts for this port */
+	channel->ch_bd->bd_ops->uart_off(channel);
+
+	jsm_printk(CLOSE, INFO, &channel->ch_bd->pci_dev, "finish\n");
+}
+
+static void jsm_tty_set_termios(struct uart_port *port,
+				 struct ktermios *termios,
+				 struct ktermios *old_termios)
+{
+	unsigned long lock_flags;
+	struct jsm_channel *channel = (struct jsm_channel *)port;
+
+	spin_lock_irqsave(&port->lock, lock_flags);
+	channel->ch_c_cflag	= termios->c_cflag;
+	channel->ch_c_iflag	= termios->c_iflag;
+	channel->ch_c_oflag	= termios->c_oflag;
+	channel->ch_c_lflag	= termios->c_lflag;
+	channel->ch_startc	= termios->c_cc[VSTART];
+	channel->ch_stopc	= termios->c_cc[VSTOP];
+
+	channel->ch_bd->bd_ops->param(channel);
+	jsm_carrier(channel);
+	spin_unlock_irqrestore(&port->lock, lock_flags);
+}
+
+static const char *jsm_tty_type(struct uart_port *port)
+{
+	return "jsm";
+}
+
+static void jsm_tty_release_port(struct uart_port *port)
+{
+}
+
+static int jsm_tty_request_port(struct uart_port *port)
+{
+	return 0;
+}
+
+static void jsm_config_port(struct uart_port *port, int flags)
+{
+	port->type = PORT_JSM;
+}
+
+static struct uart_ops jsm_ops = {
+	.tx_empty	= jsm_tty_tx_empty,
+	.set_mctrl	= jsm_tty_set_mctrl,
+	.get_mctrl	= jsm_tty_get_mctrl,
+	.stop_tx	= jsm_tty_stop_tx,
+	.start_tx	= jsm_tty_start_tx,
+	.send_xchar	= jsm_tty_send_xchar,
+	.stop_rx	= jsm_tty_stop_rx,
+	.enable_ms	= jsm_tty_enable_ms,
+	.break_ctl	= jsm_tty_break,
+	.startup	= jsm_tty_open,
+	.shutdown	= jsm_tty_close,
+	.set_termios	= jsm_tty_set_termios,
+	.type		= jsm_tty_type,
+	.release_port	= jsm_tty_release_port,
+	.request_port	= jsm_tty_request_port,
+	.config_port	= jsm_config_port,
+};
+
+/*
+ * jsm_tty_init()
+ *
+ * Init the tty subsystem.  Called once per board after board has been
+ * downloaded and init'ed.
+ */
+int __devinit jsm_tty_init(struct jsm_board *brd)
+{
+	int i;
+	void __iomem *vaddr;
+	struct jsm_channel *ch;
+
+	if (!brd)
+		return -ENXIO;
+
+	jsm_printk(INIT, INFO, &brd->pci_dev, "start\n");
+
+	/*
+	 * Initialize board structure elements.
+	 */
+
+	brd->nasync = brd->maxports;
+
+	/*
+	 * Allocate channel memory that might not have been allocated
+	 * when the driver was first loaded.
+	 */
+	for (i = 0; i < brd->nasync; i++) {
+		if (!brd->channels[i]) {
+
+			/*
+			 * Okay to malloc with GFP_KERNEL, we are not at
+			 * interrupt context, and there are no locks held.
+			 */
+			brd->channels[i] = kzalloc(sizeof(struct jsm_channel), GFP_KERNEL);
+			if (!brd->channels[i]) {
+				jsm_printk(CORE, ERR, &brd->pci_dev,
+					"%s:%d Unable to allocate memory for channel struct\n",
+							 __FILE__, __LINE__);
+			}
+		}
+	}
+
+	ch = brd->channels[0];
+	vaddr = brd->re_map_membase;
+
+	/* Set up channel variables */
+	for (i = 0; i < brd->nasync; i++, ch = brd->channels[i]) {
+
+		if (!brd->channels[i])
+			continue;
+
+		spin_lock_init(&ch->ch_lock);
+
+		if (brd->bd_uart_offset == 0x200)
+			ch->ch_neo_uart =  vaddr + (brd->bd_uart_offset * i);
+
+		ch->ch_bd = brd;
+		ch->ch_portnum = i;
+
+		/* .25 second delay */
+		ch->ch_close_delay = 250;
+
+		init_waitqueue_head(&ch->ch_flags_wait);
+	}
+
+	jsm_printk(INIT, INFO, &brd->pci_dev, "finish\n");
+	return 0;
+}
+
+int jsm_uart_port_init(struct jsm_board *brd)
+{
+	int i, rc;
+	unsigned int line;
+	struct jsm_channel *ch;
+
+	if (!brd)
+		return -ENXIO;
+
+	jsm_printk(INIT, INFO, &brd->pci_dev, "start\n");
+
+	/*
+	 * Initialize board structure elements.
+	 */
+
+	brd->nasync = brd->maxports;
+
+	/* Set up channel variables */
+	for (i = 0; i < brd->nasync; i++, ch = brd->channels[i]) {
+
+		if (!brd->channels[i])
+			continue;
+
+		brd->channels[i]->uart_port.irq = brd->irq;
+		brd->channels[i]->uart_port.uartclk = 14745600;
+		brd->channels[i]->uart_port.type = PORT_JSM;
+		brd->channels[i]->uart_port.iotype = UPIO_MEM;
+		brd->channels[i]->uart_port.membase = brd->re_map_membase;
+		brd->channels[i]->uart_port.fifosize = 16;
+		brd->channels[i]->uart_port.ops = &jsm_ops;
+		line = find_first_zero_bit(linemap, MAXLINES);
+		if (line >= MAXLINES) {
+			printk(KERN_INFO "jsm: linemap is full, added device failed\n");
+			continue;
+		} else
+			set_bit(line, linemap);
+		brd->channels[i]->uart_port.line = line;
+		rc = uart_add_one_port (&jsm_uart_driver, &brd->channels[i]->uart_port);
+		if (rc){
+			printk(KERN_INFO "jsm: Port %d failed. Aborting...\n", i);
+			return rc;
+		}
+		else
+			printk(KERN_INFO "jsm: Port %d added\n", i);
+	}
+
+	jsm_printk(INIT, INFO, &brd->pci_dev, "finish\n");
+	return 0;
+}
+
+int jsm_remove_uart_port(struct jsm_board *brd)
+{
+	int i;
+	struct jsm_channel *ch;
+
+	if (!brd)
+		return -ENXIO;
+
+	jsm_printk(INIT, INFO, &brd->pci_dev, "start\n");
+
+	/*
+	 * Initialize board structure elements.
+	 */
+
+	brd->nasync = brd->maxports;
+
+	/* Set up channel variables */
+	for (i = 0; i < brd->nasync; i++) {
+
+		if (!brd->channels[i])
+			continue;
+
+		ch = brd->channels[i];
+
+		clear_bit(ch->uart_port.line, linemap);
+		uart_remove_one_port(&jsm_uart_driver, &brd->channels[i]->uart_port);
+	}
+
+	jsm_printk(INIT, INFO, &brd->pci_dev, "finish\n");
+	return 0;
+}
+
+void jsm_input(struct jsm_channel *ch)
+{
+	struct jsm_board *bd;
+	struct tty_struct *tp;
+	u32 rmask;
+	u16 head;
+	u16 tail;
+	int data_len;
+	unsigned long lock_flags;
+	int len = 0;
+	int n = 0;
+	int s = 0;
+	int i = 0;
+
+	jsm_printk(READ, INFO, &ch->ch_bd->pci_dev, "start\n");
+
+	if (!ch)
+		return;
+
+	tp = ch->uart_port.state->port.tty;
+
+	bd = ch->ch_bd;
+	if(!bd)
+		return;
+
+	spin_lock_irqsave(&ch->ch_lock, lock_flags);
+
+	/*
+	 *Figure the number of characters in the buffer.
+	 *Exit immediately if none.
+	 */
+
+	rmask = RQUEUEMASK;
+
+	head = ch->ch_r_head & rmask;
+	tail = ch->ch_r_tail & rmask;
+
+	data_len = (head - tail) & rmask;
+	if (data_len == 0) {
+		spin_unlock_irqrestore(&ch->ch_lock, lock_flags);
+		return;
+	}
+
+	jsm_printk(READ, INFO, &ch->ch_bd->pci_dev, "start\n");
+
+	/*
+	 *If the device is not open, or CREAD is off, flush
+	 *input data and return immediately.
+	 */
+	if (!tp ||
+		!(tp->termios->c_cflag & CREAD) ) {
+
+		jsm_printk(READ, INFO, &ch->ch_bd->pci_dev,
+			"input. dropping %d bytes on port %d...\n", data_len, ch->ch_portnum);
+		ch->ch_r_head = tail;
+
+		/* Force queue flow control to be released, if needed */
+		jsm_check_queue_flow_control(ch);
+
+		spin_unlock_irqrestore(&ch->ch_lock, lock_flags);
+		return;
+	}
+
+	/*
+	 * If we are throttled, simply don't read any data.
+	 */
+	if (ch->ch_flags & CH_STOPI) {
+		spin_unlock_irqrestore(&ch->ch_lock, lock_flags);
+		jsm_printk(READ, INFO, &ch->ch_bd->pci_dev,
+			"Port %d throttled, not reading any data. head: %x tail: %x\n",
+			ch->ch_portnum, head, tail);
+		return;
+	}
+
+	jsm_printk(READ, INFO, &ch->ch_bd->pci_dev, "start 2\n");
+
+	if (data_len <= 0) {
+		spin_unlock_irqrestore(&ch->ch_lock, lock_flags);
+		jsm_printk(READ, INFO, &ch->ch_bd->pci_dev, "jsm_input 1\n");
+		return;
+	}
+
+	len = tty_buffer_request_room(tp, data_len);
+	n = len;
+
+	/*
+	 * n now contains the most amount of data we can copy,
+	 * bounded either by the flip buffer size or the amount
+	 * of data the card actually has pending...
+	 */
+	while (n) {
+		s = ((head >= tail) ? head : RQUEUESIZE) - tail;
+		s = min(s, n);
+
+		if (s <= 0)
+			break;
+
+			/*
+			 * If conditions are such that ld needs to see all
+			 * UART errors, we will have to walk each character
+			 * and error byte and send them to the buffer one at
+			 * a time.
+			 */
+
+		if (I_PARMRK(tp) || I_BRKINT(tp) || I_INPCK(tp)) {
+			for (i = 0; i < s; i++) {
+				/*
+				 * Give the Linux ld the flags in the
+				 * format it likes.
+				 */
+				if (*(ch->ch_equeue +tail +i) & UART_LSR_BI)
+					tty_insert_flip_char(tp, *(ch->ch_rqueue +tail +i),  TTY_BREAK);
+				else if (*(ch->ch_equeue +tail +i) & UART_LSR_PE)
+					tty_insert_flip_char(tp, *(ch->ch_rqueue +tail +i), TTY_PARITY);
+				else if (*(ch->ch_equeue +tail +i) & UART_LSR_FE)
+					tty_insert_flip_char(tp, *(ch->ch_rqueue +tail +i), TTY_FRAME);
+				else
+					tty_insert_flip_char(tp, *(ch->ch_rqueue +tail +i), TTY_NORMAL);
+			}
+		} else {
+			tty_insert_flip_string(tp, ch->ch_rqueue + tail, s) ;
+		}
+		tail += s;
+		n -= s;
+		/* Flip queue if needed */
+		tail &= rmask;
+	}
+
+	ch->ch_r_tail = tail & rmask;
+	ch->ch_e_tail = tail & rmask;
+	jsm_check_queue_flow_control(ch);
+	spin_unlock_irqrestore(&ch->ch_lock, lock_flags);
+
+	/* Tell the tty layer its okay to "eat" the data now */
+	tty_flip_buffer_push(tp);
+
+	jsm_printk(IOCTL, INFO, &ch->ch_bd->pci_dev, "finish\n");
+}
+
+static void jsm_carrier(struct jsm_channel *ch)
+{
+	struct jsm_board *bd;
+
+	int virt_carrier = 0;
+	int phys_carrier = 0;
+
+	jsm_printk(CARR, INFO, &ch->ch_bd->pci_dev, "start\n");
+	if (!ch)
+		return;
+
+	bd = ch->ch_bd;
+
+	if (!bd)
+		return;
+
+	if (ch->ch_mistat & UART_MSR_DCD) {
+		jsm_printk(CARR, INFO, &ch->ch_bd->pci_dev,
+			"mistat: %x D_CD: %x\n", ch->ch_mistat, ch->ch_mistat & UART_MSR_DCD);
+		phys_carrier = 1;
+	}
+
+	if (ch->ch_c_cflag & CLOCAL)
+		virt_carrier = 1;
+
+	jsm_printk(CARR, INFO, &ch->ch_bd->pci_dev,
+		"DCD: physical: %d virt: %d\n", phys_carrier, virt_carrier);
+
+	/*
+	 * Test for a VIRTUAL carrier transition to HIGH.
+	 */
+	if (((ch->ch_flags & CH_FCAR) == 0) && (virt_carrier == 1)) {
+
+		/*
+		 * When carrier rises, wake any threads waiting
+		 * for carrier in the open routine.
+		 */
+
+		jsm_printk(CARR, INFO, &ch->ch_bd->pci_dev,
+			"carrier: virt DCD rose\n");
+
+		if (waitqueue_active(&(ch->ch_flags_wait)))
+			wake_up_interruptible(&ch->ch_flags_wait);
+	}
+
+	/*
+	 * Test for a PHYSICAL carrier transition to HIGH.
+	 */
+	if (((ch->ch_flags & CH_CD) == 0) && (phys_carrier == 1)) {
+
+		/*
+		 * When carrier rises, wake any threads waiting
+		 * for carrier in the open routine.
+		 */
+
+		jsm_printk(CARR, INFO, &ch->ch_bd->pci_dev,
+			"carrier: physical DCD rose\n");
+
+		if (waitqueue_active(&(ch->ch_flags_wait)))
+			wake_up_interruptible(&ch->ch_flags_wait);
+	}
+
+	/*
+	 *  Test for a PHYSICAL transition to low, so long as we aren't
+	 *  currently ignoring physical transitions (which is what "virtual
+	 *  carrier" indicates).
+	 *
+	 *  The transition of the virtual carrier to low really doesn't
+	 *  matter... it really only means "ignore carrier state", not
+	 *  "make pretend that carrier is there".
+	 */
+	if ((virt_carrier == 0) && ((ch->ch_flags & CH_CD) != 0)
+			&& (phys_carrier == 0)) {
+		/*
+		 *	When carrier drops:
+		 *
+		 *	Drop carrier on all open units.
+		 *
+		 *	Flush queues, waking up any task waiting in the
+		 *	line discipline.
+		 *
+		 *	Send a hangup to the control terminal.
+		 *
+		 *	Enable all select calls.
+		 */
+		if (waitqueue_active(&(ch->ch_flags_wait)))
+			wake_up_interruptible(&ch->ch_flags_wait);
+	}
+
+	/*
+	 *  Make sure that our cached values reflect the current reality.
+	 */
+	if (virt_carrier == 1)
+		ch->ch_flags |= CH_FCAR;
+	else
+		ch->ch_flags &= ~CH_FCAR;
+
+	if (phys_carrier == 1)
+		ch->ch_flags |= CH_CD;
+	else
+		ch->ch_flags &= ~CH_CD;
+}
+
+
+void jsm_check_queue_flow_control(struct jsm_channel *ch)
+{
+	struct board_ops *bd_ops = ch->ch_bd->bd_ops;
+	int qleft;
+
+	/* Store how much space we have left in the queue */
+	if ((qleft = ch->ch_r_tail - ch->ch_r_head - 1) < 0)
+		qleft += RQUEUEMASK + 1;
+
+	/*
+	 * Check to see if we should enforce flow control on our queue because
+	 * the ld (or user) isn't reading data out of our queue fast enuf.
+	 *
+	 * NOTE: This is done based on what the current flow control of the
+	 * port is set for.
+	 *
+	 * 1) HWFLOW (RTS) - Turn off the UART's Receive interrupt.
+	 *	This will cause the UART's FIFO to back up, and force
+	 *	the RTS signal to be dropped.
+	 * 2) SWFLOW (IXOFF) - Keep trying to send a stop character to
+	 *	the other side, in hopes it will stop sending data to us.
+	 * 3) NONE - Nothing we can do.  We will simply drop any extra data
+	 *	that gets sent into us when the queue fills up.
+	 */
+	if (qleft < 256) {
+		/* HWFLOW */
+		if (ch->ch_c_cflag & CRTSCTS) {
+			if(!(ch->ch_flags & CH_RECEIVER_OFF)) {
+				bd_ops->disable_receiver(ch);
+				ch->ch_flags |= (CH_RECEIVER_OFF);
+				jsm_printk(READ, INFO, &ch->ch_bd->pci_dev,
+					"Internal queue hit hilevel mark (%d)! Turning off interrupts.\n",
+					qleft);
+			}
+		}
+		/* SWFLOW */
+		else if (ch->ch_c_iflag & IXOFF) {
+			if (ch->ch_stops_sent <= MAX_STOPS_SENT) {
+				bd_ops->send_stop_character(ch);
+				ch->ch_stops_sent++;
+				jsm_printk(READ, INFO, &ch->ch_bd->pci_dev,
+					"Sending stop char! Times sent: %x\n", ch->ch_stops_sent);
+			}
+		}
+	}
+
+	/*
+	 * Check to see if we should unenforce flow control because
+	 * ld (or user) finally read enuf data out of our queue.
+	 *
+	 * NOTE: This is done based on what the current flow control of the
+	 * port is set for.
+	 *
+	 * 1) HWFLOW (RTS) - Turn back on the UART's Receive interrupt.
+	 *	This will cause the UART's FIFO to raise RTS back up,
+	 *	which will allow the other side to start sending data again.
+	 * 2) SWFLOW (IXOFF) - Send a start character to
+	 *	the other side, so it will start sending data to us again.
+	 * 3) NONE - Do nothing. Since we didn't do anything to turn off the
+	 *	other side, we don't need to do anything now.
+	 */
+	if (qleft > (RQUEUESIZE / 2)) {
+		/* HWFLOW */
+		if (ch->ch_c_cflag & CRTSCTS) {
+			if (ch->ch_flags & CH_RECEIVER_OFF) {
+				bd_ops->enable_receiver(ch);
+				ch->ch_flags &= ~(CH_RECEIVER_OFF);
+				jsm_printk(READ, INFO, &ch->ch_bd->pci_dev,
+					"Internal queue hit lowlevel mark (%d)! Turning on interrupts.\n",
+					qleft);
+			}
+		}
+		/* SWFLOW */
+		else if (ch->ch_c_iflag & IXOFF && ch->ch_stops_sent) {
+			ch->ch_stops_sent = 0;
+			bd_ops->send_start_character(ch);
+			jsm_printk(READ, INFO, &ch->ch_bd->pci_dev, "Sending start char!\n");
+		}
+	}
+}
diff --git a/ap/os/linux/linux-3.4.x/drivers/tty/serial/kgdboc.c b/ap/os/linux/linux-3.4.x/drivers/tty/serial/kgdboc.c
new file mode 100644
index 0000000..2b42a01
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/tty/serial/kgdboc.c
@@ -0,0 +1,329 @@
+/*
+ * Based on the same principle as kgdboe using the NETPOLL api, this
+ * driver uses a console polling api to implement a gdb serial inteface
+ * which is multiplexed on a console port.
+ *
+ * Maintainer: Jason Wessel <jason.wessel@windriver.com>
+ *
+ * 2007-2008 (c) Jason Wessel - Wind River Systems, Inc.
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+#include <linux/kernel.h>
+#include <linux/ctype.h>
+#include <linux/kgdb.h>
+#include <linux/kdb.h>
+#include <linux/tty.h>
+#include <linux/console.h>
+#include <linux/vt_kern.h>
+#include <linux/input.h>
+#include <linux/module.h>
+
+#define MAX_CONFIG_LEN		40
+
+static struct kgdb_io		kgdboc_io_ops;
+
+/* -1 = init not run yet, 0 = unconfigured, 1 = configured. */
+static int configured		= -1;
+
+static char config[MAX_CONFIG_LEN];
+static struct kparam_string kps = {
+	.string			= config,
+	.maxlen			= MAX_CONFIG_LEN,
+};
+
+static int kgdboc_use_kms;  /* 1 if we use kernel mode switching */
+static struct tty_driver	*kgdb_tty_driver;
+static int			kgdb_tty_line;
+
+#ifdef CONFIG_KDB_KEYBOARD
+static int kgdboc_reset_connect(struct input_handler *handler,
+				struct input_dev *dev,
+				const struct input_device_id *id)
+{
+	input_reset_device(dev);
+
+	/* Retrun an error - we do not want to bind, just to reset */
+	return -ENODEV;
+}
+
+static void kgdboc_reset_disconnect(struct input_handle *handle)
+{
+	/* We do not expect anyone to actually bind to us */
+	BUG();
+}
+
+static const struct input_device_id kgdboc_reset_ids[] = {
+	{
+		.flags = INPUT_DEVICE_ID_MATCH_EVBIT,
+		.evbit = { BIT_MASK(EV_KEY) },
+	},
+	{ }
+};
+
+static struct input_handler kgdboc_reset_handler = {
+	.connect	= kgdboc_reset_connect,
+	.disconnect	= kgdboc_reset_disconnect,
+	.name		= "kgdboc_reset",
+	.id_table	= kgdboc_reset_ids,
+};
+
+static DEFINE_MUTEX(kgdboc_reset_mutex);
+
+static void kgdboc_restore_input_helper(struct work_struct *dummy)
+{
+	/*
+	 * We need to take a mutex to prevent several instances of
+	 * this work running on different CPUs so they don't try
+	 * to register again already registered handler.
+	 */
+	mutex_lock(&kgdboc_reset_mutex);
+
+	if (input_register_handler(&kgdboc_reset_handler) == 0)
+		input_unregister_handler(&kgdboc_reset_handler);
+
+	mutex_unlock(&kgdboc_reset_mutex);
+}
+
+static DECLARE_WORK(kgdboc_restore_input_work, kgdboc_restore_input_helper);
+
+static void kgdboc_restore_input(void)
+{
+	if (likely(system_state == SYSTEM_RUNNING))
+		schedule_work(&kgdboc_restore_input_work);
+}
+
+static int kgdboc_register_kbd(char **cptr)
+{
+	if (strncmp(*cptr, "kbd", 3) == 0) {
+		if (kdb_poll_idx < KDB_POLL_FUNC_MAX) {
+			kdb_poll_funcs[kdb_poll_idx] = kdb_get_kbd_char;
+			kdb_poll_idx++;
+			if (cptr[0][3] == ',')
+				*cptr += 4;
+			else
+				return 1;
+		}
+	}
+	return 0;
+}
+
+static void kgdboc_unregister_kbd(void)
+{
+	int i;
+
+	for (i = 0; i < kdb_poll_idx; i++) {
+		if (kdb_poll_funcs[i] == kdb_get_kbd_char) {
+			kdb_poll_idx--;
+			kdb_poll_funcs[i] = kdb_poll_funcs[kdb_poll_idx];
+			kdb_poll_funcs[kdb_poll_idx] = NULL;
+			i--;
+		}
+	}
+	flush_work_sync(&kgdboc_restore_input_work);
+}
+#else /* ! CONFIG_KDB_KEYBOARD */
+#define kgdboc_register_kbd(x) 0
+#define kgdboc_unregister_kbd()
+#define kgdboc_restore_input()
+#endif /* ! CONFIG_KDB_KEYBOARD */
+
+static int kgdboc_option_setup(char *opt)
+{
+	if (strlen(opt) >= MAX_CONFIG_LEN) {
+		printk(KERN_ERR "kgdboc: config string too long\n");
+		return -ENOSPC;
+	}
+	strcpy(config, opt);
+
+	return 0;
+}
+
+__setup("kgdboc=", kgdboc_option_setup);
+
+static void cleanup_kgdboc(void)
+{
+	kgdboc_unregister_kbd();
+	if (configured == 1)
+		kgdb_unregister_io_module(&kgdboc_io_ops);
+}
+
+static int configure_kgdboc(void)
+{
+	struct tty_driver *p;
+	int tty_line = 0;
+	int err;
+	char *cptr = config;
+	struct console *cons;
+
+	err = kgdboc_option_setup(config);
+	if (err || !strlen(config) || isspace(config[0]))
+		goto noconfig;
+
+	err = -ENODEV;
+	kgdboc_io_ops.is_console = 0;
+	kgdb_tty_driver = NULL;
+
+	kgdboc_use_kms = 0;
+	if (strncmp(cptr, "kms,", 4) == 0) {
+		cptr += 4;
+		kgdboc_use_kms = 1;
+	}
+
+	if (kgdboc_register_kbd(&cptr))
+		goto do_register;
+
+	p = tty_find_polling_driver(cptr, &tty_line);
+	if (!p)
+		goto noconfig;
+
+	cons = console_drivers;
+	while (cons) {
+		int idx;
+		if (cons->device && cons->device(cons, &idx) == p &&
+		    idx == tty_line) {
+			kgdboc_io_ops.is_console = 1;
+			break;
+		}
+		cons = cons->next;
+	}
+
+	kgdb_tty_driver = p;
+	kgdb_tty_line = tty_line;
+
+do_register:
+	err = kgdb_register_io_module(&kgdboc_io_ops);
+	if (err)
+		goto noconfig;
+
+	configured = 1;
+
+	return 0;
+
+noconfig:
+	config[0] = 0;
+	configured = 0;
+	cleanup_kgdboc();
+
+	return err;
+}
+
+static int __init init_kgdboc(void)
+{
+	/* Already configured? */
+	if (configured == 1)
+		return 0;
+
+	return configure_kgdboc();
+}
+
+static int kgdboc_get_char(void)
+{
+	if (!kgdb_tty_driver)
+		return -1;
+	return kgdb_tty_driver->ops->poll_get_char(kgdb_tty_driver,
+						kgdb_tty_line);
+}
+
+static void kgdboc_put_char(u8 chr)
+{
+	if (!kgdb_tty_driver)
+		return;
+	kgdb_tty_driver->ops->poll_put_char(kgdb_tty_driver,
+					kgdb_tty_line, chr);
+}
+
+static int param_set_kgdboc_var(const char *kmessage, struct kernel_param *kp)
+{
+	int len = strlen(kmessage);
+
+	if (len >= MAX_CONFIG_LEN) {
+		printk(KERN_ERR "kgdboc: config string too long\n");
+		return -ENOSPC;
+	}
+
+	/* Only copy in the string if the init function has not run yet */
+	if (configured < 0) {
+		strcpy(config, kmessage);
+		return 0;
+	}
+
+	if (kgdb_connected) {
+		printk(KERN_ERR
+		       "kgdboc: Cannot reconfigure while KGDB is connected.\n");
+
+		return -EBUSY;
+	}
+
+	strcpy(config, kmessage);
+	/* Chop out \n char as a result of echo */
+	if (config[len - 1] == '\n')
+		config[len - 1] = '\0';
+
+	if (configured == 1)
+		cleanup_kgdboc();
+
+	/* Go and configure with the new params. */
+	return configure_kgdboc();
+}
+
+static int dbg_restore_graphics;
+
+static void kgdboc_pre_exp_handler(void)
+{
+	if (!dbg_restore_graphics && kgdboc_use_kms) {
+		dbg_restore_graphics = 1;
+		con_debug_enter(vc_cons[fg_console].d);
+	}
+	/* Increment the module count when the debugger is active */
+	if (!kgdb_connected)
+		try_module_get(THIS_MODULE);
+}
+
+static void kgdboc_post_exp_handler(void)
+{
+	/* decrement the module count when the debugger detaches */
+	if (!kgdb_connected)
+		module_put(THIS_MODULE);
+	if (kgdboc_use_kms && dbg_restore_graphics) {
+		dbg_restore_graphics = 0;
+		con_debug_leave();
+	}
+	kgdboc_restore_input();
+}
+
+static struct kgdb_io kgdboc_io_ops = {
+	.name			= "kgdboc",
+	.read_char		= kgdboc_get_char,
+	.write_char		= kgdboc_put_char,
+	.pre_exception		= kgdboc_pre_exp_handler,
+	.post_exception		= kgdboc_post_exp_handler,
+};
+
+#ifdef CONFIG_KGDB_SERIAL_CONSOLE
+/* This is only available if kgdboc is a built in for early debugging */
+static int __init kgdboc_early_init(char *opt)
+{
+	/* save the first character of the config string because the
+	 * init routine can destroy it.
+	 */
+	char save_ch;
+
+	kgdboc_option_setup(opt);
+	save_ch = config[0];
+	init_kgdboc();
+	config[0] = save_ch;
+	return 0;
+}
+
+early_param("ekgdboc", kgdboc_early_init);
+#endif /* CONFIG_KGDB_SERIAL_CONSOLE */
+
+module_init(init_kgdboc);
+module_exit(cleanup_kgdboc);
+module_param_call(kgdboc, param_set_kgdboc_var, param_get_string, &kps, 0644);
+MODULE_PARM_DESC(kgdboc, "<serial_device>[,baud]");
+MODULE_DESCRIPTION("KGDB Console TTY Driver");
+MODULE_LICENSE("GPL");
diff --git a/ap/os/linux/linux-3.4.x/drivers/tty/serial/lantiq.c b/ap/os/linux/linux-3.4.x/drivers/tty/serial/lantiq.c
new file mode 100644
index 0000000..96c1cac
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/tty/serial/lantiq.c
@@ -0,0 +1,758 @@
+/*
+ *  Based on drivers/char/serial.c, by Linus Torvalds, Theodore Ts'o.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published
+ * by the Free Software Foundation.
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ * Copyright (C) 2004 Infineon IFAP DC COM CPE
+ * Copyright (C) 2007 Felix Fietkau <nbd@openwrt.org>
+ * Copyright (C) 2007 John Crispin <blogic@openwrt.org>
+ * Copyright (C) 2010 Thomas Langer, <thomas.langer@lantiq.com>
+ */
+
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/ioport.h>
+#include <linux/init.h>
+#include <linux/console.h>
+#include <linux/sysrq.h>
+#include <linux/device.h>
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
+#include <linux/serial_core.h>
+#include <linux/serial.h>
+#include <linux/platform_device.h>
+#include <linux/io.h>
+#include <linux/clk.h>
+
+#include <lantiq_soc.h>
+
+#define PORT_LTQ_ASC		111
+#define MAXPORTS		2
+#define UART_DUMMY_UER_RX	1
+#define DRVNAME			"ltq_asc"
+#ifdef __BIG_ENDIAN
+#define LTQ_ASC_TBUF		(0x0020 + 3)
+#define LTQ_ASC_RBUF		(0x0024 + 3)
+#else
+#define LTQ_ASC_TBUF		0x0020
+#define LTQ_ASC_RBUF		0x0024
+#endif
+#define LTQ_ASC_FSTAT		0x0048
+#define LTQ_ASC_WHBSTATE	0x0018
+#define LTQ_ASC_STATE		0x0014
+#define LTQ_ASC_IRNCR		0x00F8
+#define LTQ_ASC_CLC		0x0000
+#define LTQ_ASC_ID		0x0008
+#define LTQ_ASC_PISEL		0x0004
+#define LTQ_ASC_TXFCON		0x0044
+#define LTQ_ASC_RXFCON		0x0040
+#define LTQ_ASC_CON		0x0010
+#define LTQ_ASC_BG		0x0050
+#define LTQ_ASC_IRNREN		0x00F4
+
+#define ASC_IRNREN_TX		0x1
+#define ASC_IRNREN_RX		0x2
+#define ASC_IRNREN_ERR		0x4
+#define ASC_IRNREN_TX_BUF	0x8
+#define ASC_IRNCR_TIR		0x1
+#define ASC_IRNCR_RIR		0x2
+#define ASC_IRNCR_EIR		0x4
+
+#define ASCOPT_CSIZE		0x3
+#define TXFIFO_FL		1
+#define RXFIFO_FL		1
+#define ASCCLC_DISS		0x2
+#define ASCCLC_RMCMASK		0x0000FF00
+#define ASCCLC_RMCOFFSET	8
+#define ASCCON_M_8ASYNC		0x0
+#define ASCCON_M_7ASYNC		0x2
+#define ASCCON_ODD		0x00000020
+#define ASCCON_STP		0x00000080
+#define ASCCON_BRS		0x00000100
+#define ASCCON_FDE		0x00000200
+#define ASCCON_R		0x00008000
+#define ASCCON_FEN		0x00020000
+#define ASCCON_ROEN		0x00080000
+#define ASCCON_TOEN		0x00100000
+#define ASCSTATE_PE		0x00010000
+#define ASCSTATE_FE		0x00020000
+#define ASCSTATE_ROE		0x00080000
+#define ASCSTATE_ANY		(ASCSTATE_ROE|ASCSTATE_PE|ASCSTATE_FE)
+#define ASCWHBSTATE_CLRREN	0x00000001
+#define ASCWHBSTATE_SETREN	0x00000002
+#define ASCWHBSTATE_CLRPE	0x00000004
+#define ASCWHBSTATE_CLRFE	0x00000008
+#define ASCWHBSTATE_CLRROE	0x00000020
+#define ASCTXFCON_TXFEN		0x0001
+#define ASCTXFCON_TXFFLU	0x0002
+#define ASCTXFCON_TXFITLMASK	0x3F00
+#define ASCTXFCON_TXFITLOFF	8
+#define ASCRXFCON_RXFEN		0x0001
+#define ASCRXFCON_RXFFLU	0x0002
+#define ASCRXFCON_RXFITLMASK	0x3F00
+#define ASCRXFCON_RXFITLOFF	8
+#define ASCFSTAT_RXFFLMASK	0x003F
+#define ASCFSTAT_TXFFLMASK	0x3F00
+#define ASCFSTAT_TXFREEMASK	0x3F000000
+#define ASCFSTAT_TXFREEOFF	24
+
+static void lqasc_tx_chars(struct uart_port *port);
+static struct ltq_uart_port *lqasc_port[MAXPORTS];
+static struct uart_driver lqasc_reg;
+static DEFINE_SPINLOCK(ltq_asc_lock);
+
+struct ltq_uart_port {
+	struct uart_port	port;
+	struct clk		*clk;
+	unsigned int		tx_irq;
+	unsigned int		rx_irq;
+	unsigned int		err_irq;
+};
+
+static inline struct
+ltq_uart_port *to_ltq_uart_port(struct uart_port *port)
+{
+	return container_of(port, struct ltq_uart_port, port);
+}
+
+static void
+lqasc_stop_tx(struct uart_port *port)
+{
+	return;
+}
+
+static void
+lqasc_start_tx(struct uart_port *port)
+{
+	unsigned long flags;
+	spin_lock_irqsave(&ltq_asc_lock, flags);
+	lqasc_tx_chars(port);
+	spin_unlock_irqrestore(&ltq_asc_lock, flags);
+	return;
+}
+
+static void
+lqasc_stop_rx(struct uart_port *port)
+{
+	ltq_w32(ASCWHBSTATE_CLRREN, port->membase + LTQ_ASC_WHBSTATE);
+}
+
+static void
+lqasc_enable_ms(struct uart_port *port)
+{
+}
+
+static int
+lqasc_rx_chars(struct uart_port *port)
+{
+	struct tty_struct *tty = tty_port_tty_get(&port->state->port);
+	unsigned int ch = 0, rsr = 0, fifocnt;
+
+	if (!tty) {
+		dev_dbg(port->dev, "%s:tty is busy now", __func__);
+		return -EBUSY;
+	}
+	fifocnt =
+		ltq_r32(port->membase + LTQ_ASC_FSTAT) & ASCFSTAT_RXFFLMASK;
+	while (fifocnt--) {
+		u8 flag = TTY_NORMAL;
+		ch = ltq_r8(port->membase + LTQ_ASC_RBUF);
+		rsr = (ltq_r32(port->membase + LTQ_ASC_STATE)
+			& ASCSTATE_ANY) | UART_DUMMY_UER_RX;
+		tty_flip_buffer_push(tty);
+		port->icount.rx++;
+
+		/*
+		 * Note that the error handling code is
+		 * out of the main execution path
+		 */
+		if (rsr & ASCSTATE_ANY) {
+			if (rsr & ASCSTATE_PE) {
+				port->icount.parity++;
+				ltq_w32_mask(0, ASCWHBSTATE_CLRPE,
+					port->membase + LTQ_ASC_WHBSTATE);
+			} else if (rsr & ASCSTATE_FE) {
+				port->icount.frame++;
+				ltq_w32_mask(0, ASCWHBSTATE_CLRFE,
+					port->membase + LTQ_ASC_WHBSTATE);
+			}
+			if (rsr & ASCSTATE_ROE) {
+				port->icount.overrun++;
+				ltq_w32_mask(0, ASCWHBSTATE_CLRROE,
+					port->membase + LTQ_ASC_WHBSTATE);
+			}
+
+			rsr &= port->read_status_mask;
+
+			if (rsr & ASCSTATE_PE)
+				flag = TTY_PARITY;
+			else if (rsr & ASCSTATE_FE)
+				flag = TTY_FRAME;
+		}
+
+		if ((rsr & port->ignore_status_mask) == 0)
+			tty_insert_flip_char(tty, ch, flag);
+
+		if (rsr & ASCSTATE_ROE)
+			/*
+			 * Overrun is special, since it's reported
+			 * immediately, and doesn't affect the current
+			 * character
+			 */
+			tty_insert_flip_char(tty, 0, TTY_OVERRUN);
+	}
+	if (ch != 0)
+		tty_flip_buffer_push(tty);
+	tty_kref_put(tty);
+	return 0;
+}
+
+static void
+lqasc_tx_chars(struct uart_port *port)
+{
+	struct circ_buf *xmit = &port->state->xmit;
+	if (uart_tx_stopped(port)) {
+		lqasc_stop_tx(port);
+		return;
+	}
+
+	while (((ltq_r32(port->membase + LTQ_ASC_FSTAT) &
+		ASCFSTAT_TXFREEMASK) >> ASCFSTAT_TXFREEOFF) != 0) {
+		if (port->x_char) {
+			ltq_w8(port->x_char, port->membase + LTQ_ASC_TBUF);
+			port->icount.tx++;
+			port->x_char = 0;
+			continue;
+		}
+
+		if (uart_circ_empty(xmit))
+			break;
+
+		ltq_w8(port->state->xmit.buf[port->state->xmit.tail],
+			port->membase + LTQ_ASC_TBUF);
+		xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
+		port->icount.tx++;
+	}
+
+	if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
+		uart_write_wakeup(port);
+}
+
+static irqreturn_t
+lqasc_tx_int(int irq, void *_port)
+{
+	unsigned long flags;
+	struct uart_port *port = (struct uart_port *)_port;
+	spin_lock_irqsave(&ltq_asc_lock, flags);
+	ltq_w32(ASC_IRNCR_TIR, port->membase + LTQ_ASC_IRNCR);
+	spin_unlock_irqrestore(&ltq_asc_lock, flags);
+	lqasc_start_tx(port);
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t
+lqasc_err_int(int irq, void *_port)
+{
+	unsigned long flags;
+	struct uart_port *port = (struct uart_port *)_port;
+	spin_lock_irqsave(&ltq_asc_lock, flags);
+	/* clear any pending interrupts */
+	ltq_w32_mask(0, ASCWHBSTATE_CLRPE | ASCWHBSTATE_CLRFE |
+		ASCWHBSTATE_CLRROE, port->membase + LTQ_ASC_WHBSTATE);
+	spin_unlock_irqrestore(&ltq_asc_lock, flags);
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t
+lqasc_rx_int(int irq, void *_port)
+{
+	unsigned long flags;
+	struct uart_port *port = (struct uart_port *)_port;
+	spin_lock_irqsave(&ltq_asc_lock, flags);
+	ltq_w32(ASC_IRNCR_RIR, port->membase + LTQ_ASC_IRNCR);
+	lqasc_rx_chars(port);
+	spin_unlock_irqrestore(&ltq_asc_lock, flags);
+	return IRQ_HANDLED;
+}
+
+static unsigned int
+lqasc_tx_empty(struct uart_port *port)
+{
+	int status;
+	status = ltq_r32(port->membase + LTQ_ASC_FSTAT) & ASCFSTAT_TXFFLMASK;
+	return status ? 0 : TIOCSER_TEMT;
+}
+
+static unsigned int
+lqasc_get_mctrl(struct uart_port *port)
+{
+	return TIOCM_CTS | TIOCM_CAR | TIOCM_DSR;
+}
+
+static void
+lqasc_set_mctrl(struct uart_port *port, u_int mctrl)
+{
+}
+
+static void
+lqasc_break_ctl(struct uart_port *port, int break_state)
+{
+}
+
+static int
+lqasc_startup(struct uart_port *port)
+{
+	struct ltq_uart_port *ltq_port = to_ltq_uart_port(port);
+	int retval;
+
+	port->uartclk = clk_get_rate(ltq_port->clk);
+
+	ltq_w32_mask(ASCCLC_DISS | ASCCLC_RMCMASK, (1 << ASCCLC_RMCOFFSET),
+		port->membase + LTQ_ASC_CLC);
+
+	ltq_w32(0, port->membase + LTQ_ASC_PISEL);
+	ltq_w32(
+		((TXFIFO_FL << ASCTXFCON_TXFITLOFF) & ASCTXFCON_TXFITLMASK) |
+		ASCTXFCON_TXFEN | ASCTXFCON_TXFFLU,
+		port->membase + LTQ_ASC_TXFCON);
+	ltq_w32(
+		((RXFIFO_FL << ASCRXFCON_RXFITLOFF) & ASCRXFCON_RXFITLMASK)
+		| ASCRXFCON_RXFEN | ASCRXFCON_RXFFLU,
+		port->membase + LTQ_ASC_RXFCON);
+	/* make sure other settings are written to hardware before
+	 * setting enable bits
+	 */
+	wmb();
+	ltq_w32_mask(0, ASCCON_M_8ASYNC | ASCCON_FEN | ASCCON_TOEN |
+		ASCCON_ROEN, port->membase + LTQ_ASC_CON);
+
+	retval = request_irq(ltq_port->tx_irq, lqasc_tx_int,
+		0, "asc_tx", port);
+	if (retval) {
+		pr_err("failed to request lqasc_tx_int\n");
+		return retval;
+	}
+
+	retval = request_irq(ltq_port->rx_irq, lqasc_rx_int,
+		0, "asc_rx", port);
+	if (retval) {
+		pr_err("failed to request lqasc_rx_int\n");
+		goto err1;
+	}
+
+	retval = request_irq(ltq_port->err_irq, lqasc_err_int,
+		0, "asc_err", port);
+	if (retval) {
+		pr_err("failed to request lqasc_err_int\n");
+		goto err2;
+	}
+
+	ltq_w32(ASC_IRNREN_RX | ASC_IRNREN_ERR | ASC_IRNREN_TX,
+		port->membase + LTQ_ASC_IRNREN);
+	return 0;
+
+err2:
+	free_irq(ltq_port->rx_irq, port);
+err1:
+	free_irq(ltq_port->tx_irq, port);
+	return retval;
+}
+
+static void
+lqasc_shutdown(struct uart_port *port)
+{
+	struct ltq_uart_port *ltq_port = to_ltq_uart_port(port);
+	free_irq(ltq_port->tx_irq, port);
+	free_irq(ltq_port->rx_irq, port);
+	free_irq(ltq_port->err_irq, port);
+
+	ltq_w32(0, port->membase + LTQ_ASC_CON);
+	ltq_w32_mask(ASCRXFCON_RXFEN, ASCRXFCON_RXFFLU,
+		port->membase + LTQ_ASC_RXFCON);
+	ltq_w32_mask(ASCTXFCON_TXFEN, ASCTXFCON_TXFFLU,
+		port->membase + LTQ_ASC_TXFCON);
+}
+
+static void
+lqasc_set_termios(struct uart_port *port,
+	struct ktermios *new, struct ktermios *old)
+{
+	unsigned int cflag;
+	unsigned int iflag;
+	unsigned int divisor;
+	unsigned int baud;
+	unsigned int con = 0;
+	unsigned long flags;
+
+	cflag = new->c_cflag;
+	iflag = new->c_iflag;
+
+	switch (cflag & CSIZE) {
+	case CS7:
+		con = ASCCON_M_7ASYNC;
+		break;
+
+	case CS5:
+	case CS6:
+	default:
+		new->c_cflag &= ~ CSIZE;
+		new->c_cflag |= CS8;
+		con = ASCCON_M_8ASYNC;
+		break;
+	}
+
+	cflag &= ~CMSPAR; /* Mark/Space parity is not supported */
+
+	if (cflag & CSTOPB)
+		con |= ASCCON_STP;
+
+	if (cflag & PARENB) {
+		if (!(cflag & PARODD))
+			con &= ~ASCCON_ODD;
+		else
+			con |= ASCCON_ODD;
+	}
+
+	port->read_status_mask = ASCSTATE_ROE;
+	if (iflag & INPCK)
+		port->read_status_mask |= ASCSTATE_FE | ASCSTATE_PE;
+
+	port->ignore_status_mask = 0;
+	if (iflag & IGNPAR)
+		port->ignore_status_mask |= ASCSTATE_FE | ASCSTATE_PE;
+
+	if (iflag & IGNBRK) {
+		/*
+		 * If we're ignoring parity and break indicators,
+		 * ignore overruns too (for real raw support).
+		 */
+		if (iflag & IGNPAR)
+			port->ignore_status_mask |= ASCSTATE_ROE;
+	}
+
+	if ((cflag & CREAD) == 0)
+		port->ignore_status_mask |= UART_DUMMY_UER_RX;
+
+	/* set error signals  - framing, parity  and overrun, enable receiver */
+	con |= ASCCON_FEN | ASCCON_TOEN | ASCCON_ROEN;
+
+	spin_lock_irqsave(&ltq_asc_lock, flags);
+
+	/* set up CON */
+	ltq_w32_mask(0, con, port->membase + LTQ_ASC_CON);
+
+	/* Set baud rate - take a divider of 2 into account */
+	baud = uart_get_baud_rate(port, new, old, 0, port->uartclk / 16);
+	divisor = uart_get_divisor(port, baud);
+	divisor = divisor / 2 - 1;
+
+	/* disable the baudrate generator */
+	ltq_w32_mask(ASCCON_R, 0, port->membase + LTQ_ASC_CON);
+
+	/* make sure the fractional divider is off */
+	ltq_w32_mask(ASCCON_FDE, 0, port->membase + LTQ_ASC_CON);
+
+	/* set up to use divisor of 2 */
+	ltq_w32_mask(ASCCON_BRS, 0, port->membase + LTQ_ASC_CON);
+
+	/* now we can write the new baudrate into the register */
+	ltq_w32(divisor, port->membase + LTQ_ASC_BG);
+
+	/* turn the baudrate generator back on */
+	ltq_w32_mask(0, ASCCON_R, port->membase + LTQ_ASC_CON);
+
+	/* enable rx */
+	ltq_w32(ASCWHBSTATE_SETREN, port->membase + LTQ_ASC_WHBSTATE);
+
+	spin_unlock_irqrestore(&ltq_asc_lock, flags);
+
+	/* Don't rewrite B0 */
+	if (tty_termios_baud_rate(new))
+		tty_termios_encode_baud_rate(new, baud, baud);
+
+	uart_update_timeout(port, cflag, baud);
+}
+
+static const char*
+lqasc_type(struct uart_port *port)
+{
+	if (port->type == PORT_LTQ_ASC)
+		return DRVNAME;
+	else
+		return NULL;
+}
+
+static void
+lqasc_release_port(struct uart_port *port)
+{
+	if (port->flags & UPF_IOREMAP) {
+		iounmap(port->membase);
+		port->membase = NULL;
+	}
+}
+
+static int
+lqasc_request_port(struct uart_port *port)
+{
+	struct platform_device *pdev = to_platform_device(port->dev);
+	struct resource *res;
+	int size;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!res) {
+		dev_err(&pdev->dev, "cannot obtain I/O memory region");
+		return -ENODEV;
+	}
+	size = resource_size(res);
+
+	res = devm_request_mem_region(&pdev->dev, res->start,
+		size, dev_name(&pdev->dev));
+	if (!res) {
+		dev_err(&pdev->dev, "cannot request I/O memory region");
+		return -EBUSY;
+	}
+
+	if (port->flags & UPF_IOREMAP) {
+		port->membase = devm_ioremap_nocache(&pdev->dev,
+			port->mapbase, size);
+		if (port->membase == NULL)
+			return -ENOMEM;
+	}
+	return 0;
+}
+
+static void
+lqasc_config_port(struct uart_port *port, int flags)
+{
+	if (flags & UART_CONFIG_TYPE) {
+		port->type = PORT_LTQ_ASC;
+		lqasc_request_port(port);
+	}
+}
+
+static int
+lqasc_verify_port(struct uart_port *port,
+	struct serial_struct *ser)
+{
+	int ret = 0;
+	if (ser->type != PORT_UNKNOWN && ser->type != PORT_LTQ_ASC)
+		ret = -EINVAL;
+	if (ser->irq < 0 || ser->irq >= NR_IRQS)
+		ret = -EINVAL;
+	if (ser->baud_base < 9600)
+		ret = -EINVAL;
+	return ret;
+}
+
+static struct uart_ops lqasc_pops = {
+	.tx_empty =	lqasc_tx_empty,
+	.set_mctrl =	lqasc_set_mctrl,
+	.get_mctrl =	lqasc_get_mctrl,
+	.stop_tx =	lqasc_stop_tx,
+	.start_tx =	lqasc_start_tx,
+	.stop_rx =	lqasc_stop_rx,
+	.enable_ms =	lqasc_enable_ms,
+	.break_ctl =	lqasc_break_ctl,
+	.startup =	lqasc_startup,
+	.shutdown =	lqasc_shutdown,
+	.set_termios =	lqasc_set_termios,
+	.type =		lqasc_type,
+	.release_port =	lqasc_release_port,
+	.request_port =	lqasc_request_port,
+	.config_port =	lqasc_config_port,
+	.verify_port =	lqasc_verify_port,
+};
+
+static void
+lqasc_console_putchar(struct uart_port *port, int ch)
+{
+	int fifofree;
+
+	if (!port->membase)
+		return;
+
+	do {
+		fifofree = (ltq_r32(port->membase + LTQ_ASC_FSTAT)
+			& ASCFSTAT_TXFREEMASK) >> ASCFSTAT_TXFREEOFF;
+	} while (fifofree == 0);
+	ltq_w8(ch, port->membase + LTQ_ASC_TBUF);
+}
+
+
+static void
+lqasc_console_write(struct console *co, const char *s, u_int count)
+{
+	struct ltq_uart_port *ltq_port;
+	struct uart_port *port;
+	unsigned long flags;
+
+	if (co->index >= MAXPORTS)
+		return;
+
+	ltq_port = lqasc_port[co->index];
+	if (!ltq_port)
+		return;
+
+	port = &ltq_port->port;
+
+	spin_lock_irqsave(&ltq_asc_lock, flags);
+	uart_console_write(port, s, count, lqasc_console_putchar);
+	spin_unlock_irqrestore(&ltq_asc_lock, flags);
+}
+
+static int __init
+lqasc_console_setup(struct console *co, char *options)
+{
+	struct ltq_uart_port *ltq_port;
+	struct uart_port *port;
+	int baud = 115200;
+	int bits = 8;
+	int parity = 'n';
+	int flow = 'n';
+
+	if (co->index >= MAXPORTS)
+		return -ENODEV;
+
+	ltq_port = lqasc_port[co->index];
+	if (!ltq_port)
+		return -ENODEV;
+
+	port = &ltq_port->port;
+
+	port->uartclk = clk_get_rate(ltq_port->clk);
+
+	if (options)
+		uart_parse_options(options, &baud, &parity, &bits, &flow);
+	return uart_set_options(port, co, baud, parity, bits, flow);
+}
+
+static struct console lqasc_console = {
+	.name =		"ttyLTQ",
+	.write =	lqasc_console_write,
+	.device =	uart_console_device,
+	.setup =	lqasc_console_setup,
+	.flags =	CON_PRINTBUFFER,
+	.index =	-1,
+	.data =		&lqasc_reg,
+};
+
+static int __init
+lqasc_console_init(void)
+{
+	register_console(&lqasc_console);
+	return 0;
+}
+console_initcall(lqasc_console_init);
+
+static struct uart_driver lqasc_reg = {
+	.owner =	THIS_MODULE,
+	.driver_name =	DRVNAME,
+	.dev_name =	"ttyLTQ",
+	.major =	0,
+	.minor =	0,
+	.nr =		MAXPORTS,
+	.cons =		&lqasc_console,
+};
+
+static int __init
+lqasc_probe(struct platform_device *pdev)
+{
+	struct ltq_uart_port *ltq_port;
+	struct uart_port *port;
+	struct resource *mmres, *irqres;
+	int tx_irq, rx_irq, err_irq;
+	struct clk *clk;
+	int ret;
+
+	mmres = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	irqres = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+	if (!mmres || !irqres)
+		return -ENODEV;
+
+	if (pdev->id >= MAXPORTS)
+		return -EBUSY;
+
+	if (lqasc_port[pdev->id] != NULL)
+		return -EBUSY;
+
+	clk = clk_get(&pdev->dev, "fpi");
+	if (IS_ERR(clk)) {
+		pr_err("failed to get fpi clk\n");
+		return -ENOENT;
+	}
+
+	tx_irq = platform_get_irq_byname(pdev, "tx");
+	rx_irq = platform_get_irq_byname(pdev, "rx");
+	err_irq = platform_get_irq_byname(pdev, "err");
+	if ((tx_irq < 0) | (rx_irq < 0) | (err_irq < 0))
+		return -ENODEV;
+
+	ltq_port = kzalloc(sizeof(struct ltq_uart_port), GFP_KERNEL);
+	if (!ltq_port)
+		return -ENOMEM;
+
+	port = &ltq_port->port;
+
+	port->iotype	= SERIAL_IO_MEM;
+	port->flags	= ASYNC_BOOT_AUTOCONF | UPF_IOREMAP;
+	port->ops	= &lqasc_pops;
+	port->fifosize	= 16;
+	port->type	= PORT_LTQ_ASC,
+	port->line	= pdev->id;
+	port->dev	= &pdev->dev;
+
+	port->irq	= tx_irq; /* unused, just to be backward-compatibe */
+	port->mapbase	= mmres->start;
+
+	ltq_port->clk	= clk;
+
+	ltq_port->tx_irq = tx_irq;
+	ltq_port->rx_irq = rx_irq;
+	ltq_port->err_irq = err_irq;
+
+	lqasc_port[pdev->id] = ltq_port;
+	platform_set_drvdata(pdev, ltq_port);
+
+	ret = uart_add_one_port(&lqasc_reg, port);
+
+	return ret;
+}
+
+static struct platform_driver lqasc_driver = {
+	.driver		= {
+		.name	= DRVNAME,
+		.owner	= THIS_MODULE,
+	},
+};
+
+int __init
+init_lqasc(void)
+{
+	int ret;
+
+	ret = uart_register_driver(&lqasc_reg);
+	if (ret != 0)
+		return ret;
+
+	ret = platform_driver_probe(&lqasc_driver, lqasc_probe);
+	if (ret != 0)
+		uart_unregister_driver(&lqasc_reg);
+
+	return ret;
+}
+
+module_init(init_lqasc);
+
+MODULE_DESCRIPTION("Lantiq serial port driver");
+MODULE_LICENSE("GPL");
diff --git a/ap/os/linux/linux-3.4.x/drivers/tty/serial/m32r_sio.c b/ap/os/linux/linux-3.4.x/drivers/tty/serial/m32r_sio.c
new file mode 100644
index 0000000..a070362
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/tty/serial/m32r_sio.c
@@ -0,0 +1,1183 @@
+/*
+ *  m32r_sio.c
+ *
+ *  Driver for M32R serial ports
+ *
+ *  Based on drivers/char/serial.c, by Linus Torvalds, Theodore Ts'o.
+ *  Based on drivers/serial/8250.c.
+ *
+ *  Copyright (C) 2001  Russell King.
+ *  Copyright (C) 2004  Hirokazu Takata <takata at linux-m32r.org>
+ *
+ * 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.
+ */
+
+/*
+ * A note about mapbase / membase
+ *
+ *  mapbase is the physical address of the IO port.  Currently, we don't
+ *  support this very well, and it may well be dropped from this driver
+ *  in future.  As such, mapbase should be NULL.
+ *
+ *  membase is an 'ioremapped' cookie.  This is compatible with the old
+ *  serial.c driver, and is currently the preferred form.
+ */
+
+#if defined(CONFIG_SERIAL_M32R_SIO_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ)
+#define SUPPORT_SYSRQ
+#endif
+
+#include <linux/module.h>
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
+#include <linux/ioport.h>
+#include <linux/init.h>
+#include <linux/console.h>
+#include <linux/sysrq.h>
+#include <linux/serial.h>
+#include <linux/delay.h>
+
+#include <asm/m32r.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+
+#define PORT_M32R_BASE	PORT_M32R_SIO
+#define PORT_INDEX(x)	(x - PORT_M32R_BASE + 1)
+#define BAUD_RATE	115200
+
+#include <linux/serial_core.h>
+#include "m32r_sio.h"
+#include "m32r_sio_reg.h"
+
+/*
+ * Debugging.
+ */
+#if 0
+#define DEBUG_AUTOCONF(fmt...)	printk(fmt)
+#else
+#define DEBUG_AUTOCONF(fmt...)	do { } while (0)
+#endif
+
+#if 0
+#define DEBUG_INTR(fmt...)	printk(fmt)
+#else
+#define DEBUG_INTR(fmt...)	do { } while (0)
+#endif
+
+#define PASS_LIMIT	256
+
+#define BASE_BAUD	115200
+
+/* Standard COM flags */
+#define STD_COM_FLAGS (UPF_BOOT_AUTOCONF | UPF_SKIP_TEST)
+
+/*
+ * SERIAL_PORT_DFNS tells us about built-in ports that have no
+ * standard enumeration mechanism.   Platforms that can find all
+ * serial ports via mechanisms like ACPI or PCI need not supply it.
+ */
+#if defined(CONFIG_PLAT_USRV)
+
+#define SERIAL_PORT_DFNS						\
+       /* UART  CLK     PORT   IRQ            FLAGS */			\
+	{ 0, BASE_BAUD, 0x3F8, PLD_IRQ_UART0, STD_COM_FLAGS }, /* ttyS0 */ \
+	{ 0, BASE_BAUD, 0x2F8, PLD_IRQ_UART1, STD_COM_FLAGS }, /* ttyS1 */
+
+#else /* !CONFIG_PLAT_USRV */
+
+#if defined(CONFIG_SERIAL_M32R_PLDSIO)
+#define SERIAL_PORT_DFNS						\
+	{ 0, BASE_BAUD, ((unsigned long)PLD_ESIO0CR), PLD_IRQ_SIO0_RCV,	\
+	  STD_COM_FLAGS }, /* ttyS0 */
+#else
+#define SERIAL_PORT_DFNS						\
+	{ 0, BASE_BAUD, M32R_SIO_OFFSET, M32R_IRQ_SIO0_R,		\
+	  STD_COM_FLAGS }, /* ttyS0 */
+#endif
+
+#endif /* !CONFIG_PLAT_USRV */
+
+static struct old_serial_port old_serial_port[] = {
+	SERIAL_PORT_DFNS
+};
+
+#define UART_NR	ARRAY_SIZE(old_serial_port)
+
+struct uart_sio_port {
+	struct uart_port	port;
+	struct timer_list	timer;		/* "no irq" timer */
+	struct list_head	list;		/* ports on this IRQ */
+	unsigned short		rev;
+	unsigned char		acr;
+	unsigned char		ier;
+	unsigned char		lcr;
+	unsigned char		mcr_mask;	/* mask of user bits */
+	unsigned char		mcr_force;	/* mask of forced bits */
+	unsigned char		lsr_break_flag;
+
+	/*
+	 * We provide a per-port pm hook.
+	 */
+	void			(*pm)(struct uart_port *port,
+				      unsigned int state, unsigned int old);
+};
+
+struct irq_info {
+	spinlock_t		lock;
+	struct list_head	*head;
+};
+
+static struct irq_info irq_lists[NR_IRQS];
+
+/*
+ * Here we define the default xmit fifo size used for each type of UART.
+ */
+static const struct serial_uart_config uart_config[] = {
+	[PORT_UNKNOWN] = {
+		.name			= "unknown",
+		.dfl_xmit_fifo_size	= 1,
+		.flags			= 0,
+	},
+	[PORT_INDEX(PORT_M32R_SIO)] = {
+		.name			= "M32RSIO",
+		.dfl_xmit_fifo_size	= 1,
+		.flags			= 0,
+	},
+};
+
+#ifdef CONFIG_SERIAL_M32R_PLDSIO
+
+#define __sio_in(x) inw((unsigned long)(x))
+#define __sio_out(v,x) outw((v),(unsigned long)(x))
+
+static inline void sio_set_baud_rate(unsigned long baud)
+{
+	unsigned short sbaud;
+	sbaud = (boot_cpu_data.bus_clock / (baud * 4))-1;
+	__sio_out(sbaud, PLD_ESIO0BAUR);
+}
+
+static void sio_reset(void)
+{
+	unsigned short tmp;
+
+	tmp = __sio_in(PLD_ESIO0RXB);
+	tmp = __sio_in(PLD_ESIO0RXB);
+	tmp = __sio_in(PLD_ESIO0CR);
+	sio_set_baud_rate(BAUD_RATE);
+	__sio_out(0x0300, PLD_ESIO0CR);
+	__sio_out(0x0003, PLD_ESIO0CR);
+}
+
+static void sio_init(void)
+{
+	unsigned short tmp;
+
+	tmp = __sio_in(PLD_ESIO0RXB);
+	tmp = __sio_in(PLD_ESIO0RXB);
+	tmp = __sio_in(PLD_ESIO0CR);
+	__sio_out(0x0300, PLD_ESIO0CR);
+	__sio_out(0x0003, PLD_ESIO0CR);
+}
+
+static void sio_error(int *status)
+{
+	printk("SIO0 error[%04x]\n", *status);
+	do {
+		sio_init();
+	} while ((*status = __sio_in(PLD_ESIO0CR)) != 3);
+}
+
+#else /* not CONFIG_SERIAL_M32R_PLDSIO */
+
+#define __sio_in(x) inl(x)
+#define __sio_out(v,x) outl((v),(x))
+
+static inline void sio_set_baud_rate(unsigned long baud)
+{
+	unsigned long i, j;
+
+	i = boot_cpu_data.bus_clock / (baud * 16);
+	j = (boot_cpu_data.bus_clock - (i * baud * 16)) / baud;
+	i -= 1;
+	j = (j + 1) >> 1;
+
+	__sio_out(i, M32R_SIO0_BAUR_PORTL);
+	__sio_out(j, M32R_SIO0_RBAUR_PORTL);
+}
+
+static void sio_reset(void)
+{
+	__sio_out(0x00000300, M32R_SIO0_CR_PORTL);	/* init status */
+	__sio_out(0x00000800, M32R_SIO0_MOD1_PORTL);	/* 8bit        */
+	__sio_out(0x00000080, M32R_SIO0_MOD0_PORTL);	/* 1stop non   */
+	sio_set_baud_rate(BAUD_RATE);
+	__sio_out(0x00000000, M32R_SIO0_TRCR_PORTL);
+	__sio_out(0x00000003, M32R_SIO0_CR_PORTL);	/* RXCEN */
+}
+
+static void sio_init(void)
+{
+	unsigned int tmp;
+
+	tmp = __sio_in(M32R_SIO0_RXB_PORTL);
+	tmp = __sio_in(M32R_SIO0_RXB_PORTL);
+	tmp = __sio_in(M32R_SIO0_STS_PORTL);
+	__sio_out(0x00000003, M32R_SIO0_CR_PORTL);
+}
+
+static void sio_error(int *status)
+{
+	printk("SIO0 error[%04x]\n", *status);
+	do {
+		sio_init();
+	} while ((*status = __sio_in(M32R_SIO0_CR_PORTL)) != 3);
+}
+
+#endif /* CONFIG_SERIAL_M32R_PLDSIO */
+
+static unsigned int sio_in(struct uart_sio_port *up, int offset)
+{
+	return __sio_in(up->port.iobase + offset);
+}
+
+static void sio_out(struct uart_sio_port *up, int offset, int value)
+{
+	__sio_out(value, up->port.iobase + offset);
+}
+
+static unsigned int serial_in(struct uart_sio_port *up, int offset)
+{
+	if (!offset)
+		return 0;
+
+	return __sio_in(offset);
+}
+
+static void serial_out(struct uart_sio_port *up, int offset, int value)
+{
+	if (!offset)
+		return;
+
+	__sio_out(value, offset);
+}
+
+static void m32r_sio_stop_tx(struct uart_port *port)
+{
+	struct uart_sio_port *up = (struct uart_sio_port *)port;
+
+	if (up->ier & UART_IER_THRI) {
+		up->ier &= ~UART_IER_THRI;
+		serial_out(up, UART_IER, up->ier);
+	}
+}
+
+static void m32r_sio_start_tx(struct uart_port *port)
+{
+#ifdef CONFIG_SERIAL_M32R_PLDSIO
+	struct uart_sio_port *up = (struct uart_sio_port *)port;
+	struct circ_buf *xmit = &up->port.state->xmit;
+
+	if (!(up->ier & UART_IER_THRI)) {
+		up->ier |= UART_IER_THRI;
+		serial_out(up, UART_IER, up->ier);
+		serial_out(up, UART_TX, xmit->buf[xmit->tail]);
+		xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
+		up->port.icount.tx++;
+	}
+	while((serial_in(up, UART_LSR) & UART_EMPTY) != UART_EMPTY);
+#else
+	struct uart_sio_port *up = (struct uart_sio_port *)port;
+
+	if (!(up->ier & UART_IER_THRI)) {
+		up->ier |= UART_IER_THRI;
+		serial_out(up, UART_IER, up->ier);
+	}
+#endif
+}
+
+static void m32r_sio_stop_rx(struct uart_port *port)
+{
+	struct uart_sio_port *up = (struct uart_sio_port *)port;
+
+	up->ier &= ~UART_IER_RLSI;
+	up->port.read_status_mask &= ~UART_LSR_DR;
+	serial_out(up, UART_IER, up->ier);
+}
+
+static void m32r_sio_enable_ms(struct uart_port *port)
+{
+	struct uart_sio_port *up = (struct uart_sio_port *)port;
+
+	up->ier |= UART_IER_MSI;
+	serial_out(up, UART_IER, up->ier);
+}
+
+static void receive_chars(struct uart_sio_port *up, int *status)
+{
+	struct tty_struct *tty = up->port.state->port.tty;
+	unsigned char ch;
+	unsigned char flag;
+	int max_count = 256;
+
+	do {
+		ch = sio_in(up, SIORXB);
+		flag = TTY_NORMAL;
+		up->port.icount.rx++;
+
+		if (unlikely(*status & (UART_LSR_BI | UART_LSR_PE |
+				       UART_LSR_FE | UART_LSR_OE))) {
+			/*
+			 * For statistics only
+			 */
+			if (*status & UART_LSR_BI) {
+				*status &= ~(UART_LSR_FE | UART_LSR_PE);
+				up->port.icount.brk++;
+				/*
+				 * We do the SysRQ and SAK checking
+				 * here because otherwise the break
+				 * may get masked by ignore_status_mask
+				 * or read_status_mask.
+				 */
+				if (uart_handle_break(&up->port))
+					goto ignore_char;
+			} else if (*status & UART_LSR_PE)
+				up->port.icount.parity++;
+			else if (*status & UART_LSR_FE)
+				up->port.icount.frame++;
+			if (*status & UART_LSR_OE)
+				up->port.icount.overrun++;
+
+			/*
+			 * Mask off conditions which should be ingored.
+			 */
+			*status &= up->port.read_status_mask;
+
+			if (up->port.line == up->port.cons->index) {
+				/* Recover the break flag from console xmit */
+				*status |= up->lsr_break_flag;
+				up->lsr_break_flag = 0;
+			}
+
+			if (*status & UART_LSR_BI) {
+				DEBUG_INTR("handling break....");
+				flag = TTY_BREAK;
+			} else if (*status & UART_LSR_PE)
+				flag = TTY_PARITY;
+			else if (*status & UART_LSR_FE)
+				flag = TTY_FRAME;
+		}
+		if (uart_handle_sysrq_char(&up->port, ch))
+			goto ignore_char;
+		if ((*status & up->port.ignore_status_mask) == 0)
+			tty_insert_flip_char(tty, ch, flag);
+
+		if (*status & UART_LSR_OE) {
+			/*
+			 * Overrun is special, since it's reported
+			 * immediately, and doesn't affect the current
+			 * character.
+			 */
+			tty_insert_flip_char(tty, 0, TTY_OVERRUN);
+		}
+	ignore_char:
+		*status = serial_in(up, UART_LSR);
+	} while ((*status & UART_LSR_DR) && (max_count-- > 0));
+	tty_flip_buffer_push(tty);
+}
+
+static void transmit_chars(struct uart_sio_port *up)
+{
+	struct circ_buf *xmit = &up->port.state->xmit;
+	int count;
+
+	if (up->port.x_char) {
+#ifndef CONFIG_SERIAL_M32R_PLDSIO	/* XXX */
+		serial_out(up, UART_TX, up->port.x_char);
+#endif
+		up->port.icount.tx++;
+		up->port.x_char = 0;
+		return;
+	}
+	if (uart_circ_empty(xmit) || uart_tx_stopped(&up->port)) {
+		m32r_sio_stop_tx(&up->port);
+		return;
+	}
+
+	count = up->port.fifosize;
+	do {
+		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 (!(serial_in(up, UART_LSR) & UART_LSR_THRE));
+
+	} while (--count > 0);
+
+	if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
+		uart_write_wakeup(&up->port);
+
+	DEBUG_INTR("THRE...");
+
+	if (uart_circ_empty(xmit))
+		m32r_sio_stop_tx(&up->port);
+}
+
+/*
+ * This handles the interrupt from one port.
+ */
+static inline void m32r_sio_handle_port(struct uart_sio_port *up,
+	unsigned int status)
+{
+	DEBUG_INTR("status = %x...", status);
+
+	if (status & 0x04)
+		receive_chars(up, &status);
+	if (status & 0x01)
+		transmit_chars(up);
+}
+
+/*
+ * This is the serial driver's interrupt routine.
+ *
+ * Arjan thinks the old way was overly complex, so it got simplified.
+ * Alan disagrees, saying that need the complexity to handle the weird
+ * nature of ISA shared interrupts.  (This is a special exception.)
+ *
+ * In order to handle ISA shared interrupts properly, we need to check
+ * that all ports have been serviced, and therefore the ISA interrupt
+ * line has been de-asserted.
+ *
+ * This means we need to loop through all ports. checking that they
+ * don't have an interrupt pending.
+ */
+static irqreturn_t m32r_sio_interrupt(int irq, void *dev_id)
+{
+	struct irq_info *i = dev_id;
+	struct list_head *l, *end = NULL;
+	int pass_counter = 0;
+
+	DEBUG_INTR("m32r_sio_interrupt(%d)...", irq);
+
+#ifdef CONFIG_SERIAL_M32R_PLDSIO
+//	if (irq == PLD_IRQ_SIO0_SND)
+//		irq = PLD_IRQ_SIO0_RCV;
+#else
+	if (irq == M32R_IRQ_SIO0_S)
+		irq = M32R_IRQ_SIO0_R;
+#endif
+
+	spin_lock(&i->lock);
+
+	l = i->head;
+	do {
+		struct uart_sio_port *up;
+		unsigned int sts;
+
+		up = list_entry(l, struct uart_sio_port, list);
+
+		sts = sio_in(up, SIOSTS);
+		if (sts & 0x5) {
+			spin_lock(&up->port.lock);
+			m32r_sio_handle_port(up, sts);
+			spin_unlock(&up->port.lock);
+
+			end = NULL;
+		} else if (end == NULL)
+			end = l;
+
+		l = l->next;
+
+		if (l == i->head && pass_counter++ > PASS_LIMIT) {
+			if (sts & 0xe0)
+				sio_error(&sts);
+			break;
+		}
+	} while (l != end);
+
+	spin_unlock(&i->lock);
+
+	DEBUG_INTR("end.\n");
+
+	return IRQ_HANDLED;
+}
+
+/*
+ * To support ISA shared interrupts, we need to have one interrupt
+ * handler that ensures that the IRQ line has been deasserted
+ * before returning.  Failing to do this will result in the IRQ
+ * line being stuck active, and, since ISA irqs are edge triggered,
+ * no more IRQs will be seen.
+ */
+static void serial_do_unlink(struct irq_info *i, struct uart_sio_port *up)
+{
+	spin_lock_irq(&i->lock);
+
+	if (!list_empty(i->head)) {
+		if (i->head == &up->list)
+			i->head = i->head->next;
+		list_del(&up->list);
+	} else {
+		BUG_ON(i->head != &up->list);
+		i->head = NULL;
+	}
+
+	spin_unlock_irq(&i->lock);
+}
+
+static int serial_link_irq_chain(struct uart_sio_port *up)
+{
+	struct irq_info *i = irq_lists + up->port.irq;
+	int ret, irq_flags = 0;
+
+	spin_lock_irq(&i->lock);
+
+	if (i->head) {
+		list_add(&up->list, i->head);
+		spin_unlock_irq(&i->lock);
+
+		ret = 0;
+	} else {
+		INIT_LIST_HEAD(&up->list);
+		i->head = &up->list;
+		spin_unlock_irq(&i->lock);
+
+		ret = request_irq(up->port.irq, m32r_sio_interrupt,
+				  irq_flags, "SIO0-RX", i);
+		ret |= request_irq(up->port.irq + 1, m32r_sio_interrupt,
+				  irq_flags, "SIO0-TX", i);
+		if (ret < 0)
+			serial_do_unlink(i, up);
+	}
+
+	return ret;
+}
+
+static void serial_unlink_irq_chain(struct uart_sio_port *up)
+{
+	struct irq_info *i = irq_lists + up->port.irq;
+
+	BUG_ON(i->head == NULL);
+
+	if (list_empty(i->head)) {
+		free_irq(up->port.irq, i);
+		free_irq(up->port.irq + 1, i);
+	}
+
+	serial_do_unlink(i, up);
+}
+
+/*
+ * This function is used to handle ports that do not have an interrupt.
+ */
+static void m32r_sio_timeout(unsigned long data)
+{
+	struct uart_sio_port *up = (struct uart_sio_port *)data;
+	unsigned int timeout;
+	unsigned int sts;
+
+	sts = sio_in(up, SIOSTS);
+	if (sts & 0x5) {
+		spin_lock(&up->port.lock);
+		m32r_sio_handle_port(up, sts);
+		spin_unlock(&up->port.lock);
+	}
+
+	timeout = up->port.timeout;
+	timeout = timeout > 6 ? (timeout / 2 - 2) : 1;
+	mod_timer(&up->timer, jiffies + timeout);
+}
+
+static unsigned int m32r_sio_tx_empty(struct uart_port *port)
+{
+	struct uart_sio_port *up = (struct uart_sio_port *)port;
+	unsigned long flags;
+	unsigned int ret;
+
+	spin_lock_irqsave(&up->port.lock, flags);
+	ret = serial_in(up, UART_LSR) & UART_LSR_TEMT ? TIOCSER_TEMT : 0;
+	spin_unlock_irqrestore(&up->port.lock, flags);
+
+	return ret;
+}
+
+static unsigned int m32r_sio_get_mctrl(struct uart_port *port)
+{
+	return 0;
+}
+
+static void m32r_sio_set_mctrl(struct uart_port *port, unsigned int mctrl)
+{
+
+}
+
+static void m32r_sio_break_ctl(struct uart_port *port, int break_state)
+{
+
+}
+
+static int m32r_sio_startup(struct uart_port *port)
+{
+	struct uart_sio_port *up = (struct uart_sio_port *)port;
+	int retval;
+
+	sio_init();
+
+	/*
+	 * If the "interrupt" for this port doesn't correspond with any
+	 * hardware interrupt, we use a timer-based system.  The original
+	 * driver used to do this with IRQ0.
+	 */
+	if (!up->port.irq) {
+		unsigned int timeout = up->port.timeout;
+
+		timeout = timeout > 6 ? (timeout / 2 - 2) : 1;
+
+		up->timer.data = (unsigned long)up;
+		mod_timer(&up->timer, jiffies + timeout);
+	} else {
+		retval = serial_link_irq_chain(up);
+		if (retval)
+			return retval;
+	}
+
+	/*
+	 * Finally, enable interrupts.  Note: Modem status interrupts
+	 * are set via set_termios(), which will be occurring imminently
+	 * anyway, so we don't enable them here.
+	 * - M32R_SIO: 0x0c
+	 * - M32R_PLDSIO: 0x04
+	 */
+	up->ier = UART_IER_MSI | UART_IER_RLSI | UART_IER_RDI;
+	sio_out(up, SIOTRCR, up->ier);
+
+	/*
+	 * And clear the interrupt registers again for luck.
+	 */
+	sio_reset();
+
+	return 0;
+}
+
+static void m32r_sio_shutdown(struct uart_port *port)
+{
+	struct uart_sio_port *up = (struct uart_sio_port *)port;
+
+	/*
+	 * Disable interrupts from this port
+	 */
+	up->ier = 0;
+	sio_out(up, SIOTRCR, 0);
+
+	/*
+	 * Disable break condition and FIFOs
+	 */
+
+	sio_init();
+
+	if (!up->port.irq)
+		del_timer_sync(&up->timer);
+	else
+		serial_unlink_irq_chain(up);
+}
+
+static unsigned int m32r_sio_get_divisor(struct uart_port *port,
+	unsigned int baud)
+{
+	return uart_get_divisor(port, baud);
+}
+
+static void m32r_sio_set_termios(struct uart_port *port,
+	struct ktermios *termios, struct ktermios *old)
+{
+	struct uart_sio_port *up = (struct uart_sio_port *)port;
+	unsigned char cval = 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.
+	 */
+#ifdef CONFIG_SERIAL_M32R_PLDSIO
+	baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk/4);
+#else
+	baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk/16);
+#endif
+	quot = m32r_sio_get_divisor(port, baud);
+
+	/*
+	 * Ok, we're now changing the port state.  Do it with
+	 * interrupts disabled.
+	 */
+	spin_lock_irqsave(&up->port.lock, flags);
+
+	sio_set_baud_rate(baud);
+
+	/*
+	 * 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;
+
+	serial_out(up, UART_IER, up->ier);
+
+	up->lcr = cval;					/* Save LCR */
+	spin_unlock_irqrestore(&up->port.lock, flags);
+}
+
+static void m32r_sio_pm(struct uart_port *port, unsigned int state,
+	unsigned int oldstate)
+{
+	struct uart_sio_port *up = (struct uart_sio_port *)port;
+
+	if (up->pm)
+		up->pm(port, state, oldstate);
+}
+
+/*
+ * Resource handling.  This is complicated by the fact that resources
+ * depend on the port type.  Maybe we should be claiming the standard
+ * 8250 ports, and then trying to get other resources as necessary?
+ */
+static int
+m32r_sio_request_std_resource(struct uart_sio_port *up, struct resource **res)
+{
+	unsigned int size = 8 << up->port.regshift;
+#ifndef CONFIG_SERIAL_M32R_PLDSIO
+	unsigned long start;
+#endif
+	int ret = 0;
+
+	switch (up->port.iotype) {
+	case UPIO_MEM:
+		if (up->port.mapbase) {
+#ifdef CONFIG_SERIAL_M32R_PLDSIO
+			*res = request_mem_region(up->port.mapbase, size, "serial");
+#else
+			start = up->port.mapbase;
+			*res = request_mem_region(start, size, "serial");
+#endif
+			if (!*res)
+				ret = -EBUSY;
+		}
+		break;
+
+	case UPIO_PORT:
+		*res = request_region(up->port.iobase, size, "serial");
+		if (!*res)
+			ret = -EBUSY;
+		break;
+	}
+	return ret;
+}
+
+static void m32r_sio_release_port(struct uart_port *port)
+{
+	struct uart_sio_port *up = (struct uart_sio_port *)port;
+	unsigned long start, offset = 0, size = 0;
+
+	size <<= up->port.regshift;
+
+	switch (up->port.iotype) {
+	case UPIO_MEM:
+		if (up->port.mapbase) {
+			/*
+			 * Unmap the area.
+			 */
+			iounmap(up->port.membase);
+			up->port.membase = NULL;
+
+			start = up->port.mapbase;
+
+			if (size)
+				release_mem_region(start + offset, size);
+			release_mem_region(start, 8 << up->port.regshift);
+		}
+		break;
+
+	case UPIO_PORT:
+		start = up->port.iobase;
+
+		if (size)
+			release_region(start + offset, size);
+		release_region(start + offset, 8 << up->port.regshift);
+		break;
+
+	default:
+		break;
+	}
+}
+
+static int m32r_sio_request_port(struct uart_port *port)
+{
+	struct uart_sio_port *up = (struct uart_sio_port *)port;
+	struct resource *res = NULL;
+	int ret = 0;
+
+	ret = m32r_sio_request_std_resource(up, &res);
+
+	/*
+	 * If we have a mapbase, then request that as well.
+	 */
+	if (ret == 0 && up->port.flags & UPF_IOREMAP) {
+		int size = resource_size(res);
+
+		up->port.membase = ioremap(up->port.mapbase, size);
+		if (!up->port.membase)
+			ret = -ENOMEM;
+	}
+
+	if (ret < 0) {
+		if (res)
+			release_resource(res);
+	}
+
+	return ret;
+}
+
+static void m32r_sio_config_port(struct uart_port *port, int unused)
+{
+	struct uart_sio_port *up = (struct uart_sio_port *)port;
+	unsigned long flags;
+
+	spin_lock_irqsave(&up->port.lock, flags);
+
+	up->port.type = (PORT_M32R_SIO - PORT_M32R_BASE + 1);
+	up->port.fifosize = uart_config[up->port.type].dfl_xmit_fifo_size;
+
+	spin_unlock_irqrestore(&up->port.lock, flags);
+}
+
+static int
+m32r_sio_verify_port(struct uart_port *port, struct serial_struct *ser)
+{
+	if (ser->irq >= nr_irqs || ser->irq < 0 ||
+	    ser->baud_base < 9600 || ser->type < PORT_UNKNOWN ||
+	    ser->type >= ARRAY_SIZE(uart_config))
+		return -EINVAL;
+	return 0;
+}
+
+static const char *
+m32r_sio_type(struct uart_port *port)
+{
+	int type = port->type;
+
+	if (type >= ARRAY_SIZE(uart_config))
+		type = 0;
+	return uart_config[type].name;
+}
+
+static struct uart_ops m32r_sio_pops = {
+	.tx_empty	= m32r_sio_tx_empty,
+	.set_mctrl	= m32r_sio_set_mctrl,
+	.get_mctrl	= m32r_sio_get_mctrl,
+	.stop_tx	= m32r_sio_stop_tx,
+	.start_tx	= m32r_sio_start_tx,
+	.stop_rx	= m32r_sio_stop_rx,
+	.enable_ms	= m32r_sio_enable_ms,
+	.break_ctl	= m32r_sio_break_ctl,
+	.startup	= m32r_sio_startup,
+	.shutdown	= m32r_sio_shutdown,
+	.set_termios	= m32r_sio_set_termios,
+	.pm		= m32r_sio_pm,
+	.type		= m32r_sio_type,
+	.release_port	= m32r_sio_release_port,
+	.request_port	= m32r_sio_request_port,
+	.config_port	= m32r_sio_config_port,
+	.verify_port	= m32r_sio_verify_port,
+};
+
+static struct uart_sio_port m32r_sio_ports[UART_NR];
+
+static void __init m32r_sio_init_ports(void)
+{
+	struct uart_sio_port *up;
+	static int first = 1;
+	int i;
+
+	if (!first)
+		return;
+	first = 0;
+
+	for (i = 0, up = m32r_sio_ports; i < ARRAY_SIZE(old_serial_port);
+	     i++, up++) {
+		up->port.iobase   = old_serial_port[i].port;
+		up->port.irq      = irq_canonicalize(old_serial_port[i].irq);
+		up->port.uartclk  = old_serial_port[i].baud_base * 16;
+		up->port.flags    = old_serial_port[i].flags;
+		up->port.membase  = old_serial_port[i].iomem_base;
+		up->port.iotype   = old_serial_port[i].io_type;
+		up->port.regshift = old_serial_port[i].iomem_reg_shift;
+		up->port.ops      = &m32r_sio_pops;
+	}
+}
+
+static void __init m32r_sio_register_ports(struct uart_driver *drv)
+{
+	int i;
+
+	m32r_sio_init_ports();
+
+	for (i = 0; i < UART_NR; i++) {
+		struct uart_sio_port *up = &m32r_sio_ports[i];
+
+		up->port.line = i;
+		up->port.ops = &m32r_sio_pops;
+		init_timer(&up->timer);
+		up->timer.function = m32r_sio_timeout;
+
+		up->mcr_mask = ~0;
+		up->mcr_force = 0;
+
+		uart_add_one_port(drv, &up->port);
+	}
+}
+
+#ifdef CONFIG_SERIAL_M32R_SIO_CONSOLE
+
+/*
+ *	Wait for transmitter & holding register to empty
+ */
+static inline void wait_for_xmitr(struct uart_sio_port *up)
+{
+	unsigned int status, tmout = 10000;
+
+	/* Wait up to 10ms for the character(s) to be sent. */
+	do {
+		status = sio_in(up, SIOSTS);
+
+		if (--tmout == 0)
+			break;
+		udelay(1);
+	} while ((status & UART_EMPTY) != UART_EMPTY);
+
+	/* Wait up to 1s for flow control if necessary */
+	if (up->port.flags & UPF_CONS_FLOW) {
+		tmout = 1000000;
+		while (--tmout)
+			udelay(1);
+	}
+}
+
+static void m32r_sio_console_putchar(struct uart_port *port, int ch)
+{
+	struct uart_sio_port *up = (struct uart_sio_port *)port;
+
+	wait_for_xmitr(up);
+	sio_out(up, SIOTXB, ch);
+}
+
+/*
+ *	Print a string to the serial port trying not to disturb
+ *	any possible real use of the port...
+ *
+ *	The console_lock must be held when we get here.
+ */
+static void m32r_sio_console_write(struct console *co, const char *s,
+	unsigned int count)
+{
+	struct uart_sio_port *up = &m32r_sio_ports[co->index];
+	unsigned int ier;
+
+	/*
+	 *	First save the UER then disable the interrupts
+	 */
+	ier = sio_in(up, SIOTRCR);
+	sio_out(up, SIOTRCR, 0);
+
+	uart_console_write(&up->port, s, count, m32r_sio_console_putchar);
+
+	/*
+	 *	Finally, wait for transmitter to become empty
+	 *	and restore the IER
+	 */
+	wait_for_xmitr(up);
+	sio_out(up, SIOTRCR, ier);
+}
+
+static int __init m32r_sio_console_setup(struct console *co, char *options)
+{
+	struct uart_port *port;
+	int baud = 9600;
+	int bits = 8;
+	int parity = 'n';
+	int flow = 'n';
+
+	/*
+	 * 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 >= UART_NR)
+		co->index = 0;
+	port = &m32r_sio_ports[co->index].port;
+
+	/*
+	 * Temporary fix.
+	 */
+	spin_lock_init(&port->lock);
+
+	if (options)
+		uart_parse_options(options, &baud, &parity, &bits, &flow);
+
+	return uart_set_options(port, co, baud, parity, bits, flow);
+}
+
+static struct uart_driver m32r_sio_reg;
+static struct console m32r_sio_console = {
+	.name		= "ttyS",
+	.write		= m32r_sio_console_write,
+	.device		= uart_console_device,
+	.setup		= m32r_sio_console_setup,
+	.flags		= CON_PRINTBUFFER,
+	.index		= -1,
+	.data		= &m32r_sio_reg,
+};
+
+static int __init m32r_sio_console_init(void)
+{
+	sio_reset();
+	sio_init();
+	m32r_sio_init_ports();
+	register_console(&m32r_sio_console);
+	return 0;
+}
+console_initcall(m32r_sio_console_init);
+
+#define M32R_SIO_CONSOLE	&m32r_sio_console
+#else
+#define M32R_SIO_CONSOLE	NULL
+#endif
+
+static struct uart_driver m32r_sio_reg = {
+	.owner			= THIS_MODULE,
+	.driver_name		= "sio",
+	.dev_name		= "ttyS",
+	.major			= TTY_MAJOR,
+	.minor			= 64,
+	.nr			= UART_NR,
+	.cons			= M32R_SIO_CONSOLE,
+};
+
+/**
+ *	m32r_sio_suspend_port - suspend one serial port
+ *	@line: serial line number
+ *
+ *	Suspend one serial port.
+ */
+void m32r_sio_suspend_port(int line)
+{
+	uart_suspend_port(&m32r_sio_reg, &m32r_sio_ports[line].port);
+}
+
+/**
+ *	m32r_sio_resume_port - resume one serial port
+ *	@line: serial line number
+ *
+ *	Resume one serial port.
+ */
+void m32r_sio_resume_port(int line)
+{
+	uart_resume_port(&m32r_sio_reg, &m32r_sio_ports[line].port);
+}
+
+static int __init m32r_sio_init(void)
+{
+	int ret, i;
+
+	printk(KERN_INFO "Serial: M32R SIO driver\n");
+
+	for (i = 0; i < nr_irqs; i++)
+		spin_lock_init(&irq_lists[i].lock);
+
+	ret = uart_register_driver(&m32r_sio_reg);
+	if (ret >= 0)
+		m32r_sio_register_ports(&m32r_sio_reg);
+
+	return ret;
+}
+
+static void __exit m32r_sio_exit(void)
+{
+	int i;
+
+	for (i = 0; i < UART_NR; i++)
+		uart_remove_one_port(&m32r_sio_reg, &m32r_sio_ports[i].port);
+
+	uart_unregister_driver(&m32r_sio_reg);
+}
+
+module_init(m32r_sio_init);
+module_exit(m32r_sio_exit);
+
+EXPORT_SYMBOL(m32r_sio_suspend_port);
+EXPORT_SYMBOL(m32r_sio_resume_port);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Generic M32R SIO serial driver");
diff --git a/ap/os/linux/linux-3.4.x/drivers/tty/serial/m32r_sio.h b/ap/os/linux/linux-3.4.x/drivers/tty/serial/m32r_sio.h
new file mode 100644
index 0000000..8129824
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/tty/serial/m32r_sio.h
@@ -0,0 +1,49 @@
+/*
+ *  m32r_sio.h
+ *
+ *  Driver for M32R serial ports
+ *
+ *  Based on drivers/char/serial.c, by Linus Torvalds, Theodore Ts'o.
+ *  Based on drivers/serial/8250.h.
+ *
+ *  Copyright (C) 2001  Russell King.
+ *  Copyright (C) 2004  Hirokazu Takata <takata at linux-m32r.org>
+ *
+ * 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.
+ */
+
+#include <linux/pci.h>
+
+struct m32r_sio_probe {
+	struct module	*owner;
+	int		(*pci_init_one)(struct pci_dev *dev);
+	void		(*pci_remove_one)(struct pci_dev *dev);
+	void		(*pnp_init)(void);
+};
+
+int m32r_sio_register_probe(struct m32r_sio_probe *probe);
+void m32r_sio_unregister_probe(struct m32r_sio_probe *probe);
+void m32r_sio_get_irq_map(unsigned int *map);
+void m32r_sio_suspend_port(int line);
+void m32r_sio_resume_port(int line);
+
+struct old_serial_port {
+	unsigned int uart;
+	unsigned int baud_base;
+	unsigned int port;
+	unsigned int irq;
+	unsigned int flags;
+	unsigned char io_type;
+	unsigned char __iomem *iomem_base;
+	unsigned short iomem_reg_shift;
+};
+
+#define _INLINE_ inline
+
+#define PROBE_RSA	(1 << 0)
+#define PROBE_ANY	(~0)
+
+#define HIGH_BITS_OFFSET ((sizeof(long)-sizeof(int))*8)
diff --git a/ap/os/linux/linux-3.4.x/drivers/tty/serial/m32r_sio_reg.h b/ap/os/linux/linux-3.4.x/drivers/tty/serial/m32r_sio_reg.h
new file mode 100644
index 0000000..4671473
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/tty/serial/m32r_sio_reg.h
@@ -0,0 +1,152 @@
+/*
+ * m32r_sio_reg.h
+ *
+ * Copyright (C) 1992, 1994 by Theodore Ts'o.
+ * Copyright (C) 2004  Hirokazu Takata <takata at linux-m32r.org>
+ *
+ * Redistribution of this file is permitted under the terms of the GNU
+ * Public License (GPL)
+ *
+ * These are the UART port assignments, expressed as offsets from the base
+ * register.  These assignments should hold for any serial port based on
+ * a 8250, 16450, or 16550(A).
+ */
+
+#ifndef _M32R_SIO_REG_H
+#define _M32R_SIO_REG_H
+
+
+#ifdef CONFIG_SERIAL_M32R_PLDSIO
+
+#define SIOCR		0x000
+#define SIOMOD0		0x002
+#define SIOMOD1		0x004
+#define SIOSTS		0x006
+#define SIOTRCR		0x008
+#define SIOBAUR		0x00a
+// #define SIORBAUR	0x018
+#define SIOTXB		0x00c
+#define SIORXB		0x00e
+
+#define UART_RX		((unsigned long) PLD_ESIO0RXB)
+				/* In:  Receive buffer (DLAB=0) */
+#define UART_TX		((unsigned long) PLD_ESIO0TXB)
+				/* Out: Transmit buffer (DLAB=0) */
+#define UART_DLL	0	/* Out: Divisor Latch Low (DLAB=1) */
+#define UART_TRG	0	/* (LCR=BF) FCTR bit 7 selects Rx or Tx
+				 * In: Fifo count
+				 * Out: Fifo custom trigger levels
+				 * XR16C85x only */
+
+#define UART_DLM	0	/* Out: Divisor Latch High (DLAB=1) */
+#define UART_IER	((unsigned long) PLD_ESIO0INTCR)
+				/* Out: Interrupt Enable Register */
+#define UART_FCTR	0	/* (LCR=BF) Feature Control Register
+				 * XR16C85x only */
+
+#define UART_IIR	0	/* In:  Interrupt ID Register */
+#define UART_FCR	0	/* Out: FIFO Control Register */
+#define UART_EFR	0	/* I/O: Extended Features Register */
+				/* (DLAB=1, 16C660 only) */
+
+#define UART_LCR	0	/* Out: Line Control Register */
+#define UART_MCR	0	/* Out: Modem Control Register */
+#define UART_LSR	((unsigned long) PLD_ESIO0STS)
+				/* In:  Line Status Register */
+#define UART_MSR	0	/* In:  Modem Status Register */
+#define UART_SCR	0	/* I/O: Scratch Register */
+#define UART_EMSR	0	/* (LCR=BF) Extended Mode Select Register
+				 * FCTR bit 6 selects SCR or EMSR
+				 * XR16c85x only */
+
+#else /* not CONFIG_SERIAL_M32R_PLDSIO */
+
+#define SIOCR		0x000
+#define SIOMOD0		0x004
+#define SIOMOD1		0x008
+#define SIOSTS		0x00c
+#define SIOTRCR		0x010
+#define SIOBAUR		0x014
+#define SIORBAUR	0x018
+#define SIOTXB		0x01c
+#define SIORXB		0x020
+
+#define UART_RX		M32R_SIO0_RXB_PORTL	/* In:  Receive buffer (DLAB=0) */
+#define UART_TX		M32R_SIO0_TXB_PORTL	/* Out: Transmit buffer (DLAB=0) */
+#define UART_DLL	0	/* Out: Divisor Latch Low (DLAB=1) */
+#define UART_TRG	0	/* (LCR=BF) FCTR bit 7 selects Rx or Tx
+				 * In: Fifo count
+				 * Out: Fifo custom trigger levels
+				 * XR16C85x only */
+
+#define UART_DLM	0	/* Out: Divisor Latch High (DLAB=1) */
+#define UART_IER	M32R_SIO0_TRCR_PORTL	/* Out: Interrupt Enable Register */
+#define UART_FCTR	0	/* (LCR=BF) Feature Control Register
+				 * XR16C85x only */
+
+#define UART_IIR	0	/* In:  Interrupt ID Register */
+#define UART_FCR	0	/* Out: FIFO Control Register */
+#define UART_EFR	0	/* I/O: Extended Features Register */
+				/* (DLAB=1, 16C660 only) */
+
+#define UART_LCR	0	/* Out: Line Control Register */
+#define UART_MCR	0	/* Out: Modem Control Register */
+#define UART_LSR	M32R_SIO0_STS_PORTL	/* In:  Line Status Register */
+#define UART_MSR	0	/* In:  Modem Status Register */
+#define UART_SCR	0	/* I/O: Scratch Register */
+#define UART_EMSR	0	/* (LCR=BF) Extended Mode Select Register
+				 * FCTR bit 6 selects SCR or EMSR
+				 * XR16c85x only */
+
+#endif /* CONFIG_SERIAL_M32R_PLDSIO */
+
+#define UART_EMPTY	(UART_LSR_TEMT | UART_LSR_THRE)
+
+/*
+ * These are the definitions for the Line Control Register
+ *
+ * Note: if the word length is 5 bits (UART_LCR_WLEN5), then setting
+ * UART_LCR_STOP will select 1.5 stop bits, not 2 stop bits.
+ */
+#define UART_LCR_DLAB	0x80	/* Divisor latch access bit */
+#define UART_LCR_SBC	0x40	/* Set break control */
+#define UART_LCR_SPAR	0x20	/* Stick parity (?) */
+#define UART_LCR_EPAR	0x10	/* Even parity select */
+#define UART_LCR_PARITY	0x08	/* Parity Enable */
+#define UART_LCR_STOP	0x04	/* Stop bits: 0=1 stop bit, 1= 2 stop bits */
+#define UART_LCR_WLEN5  0x00	/* Wordlength: 5 bits */
+#define UART_LCR_WLEN6  0x01	/* Wordlength: 6 bits */
+#define UART_LCR_WLEN7  0x02	/* Wordlength: 7 bits */
+#define UART_LCR_WLEN8  0x03	/* Wordlength: 8 bits */
+
+/*
+ * These are the definitions for the Line Status Register
+ */
+#define UART_LSR_TEMT	0x02	/* Transmitter empty */
+#define UART_LSR_THRE	0x01	/* Transmit-hold-register empty */
+#define UART_LSR_BI	0x00	/* Break interrupt indicator */
+#define UART_LSR_FE	0x80	/* Frame error indicator */
+#define UART_LSR_PE	0x40	/* Parity error indicator */
+#define UART_LSR_OE	0x20	/* Overrun error indicator */
+#define UART_LSR_DR	0x04	/* Receiver data ready */
+
+/*
+ * These are the definitions for the Interrupt Identification Register
+ */
+#define UART_IIR_NO_INT	0x01	/* No interrupts pending */
+#define UART_IIR_ID	0x06	/* Mask for the interrupt ID */
+
+#define UART_IIR_MSI	0x00	/* Modem status interrupt */
+#define UART_IIR_THRI	0x02	/* Transmitter holding register empty */
+#define UART_IIR_RDI	0x04	/* Receiver data interrupt */
+#define UART_IIR_RLSI	0x06	/* Receiver line status interrupt */
+
+/*
+ * These are the definitions for the Interrupt Enable Register
+ */
+#define UART_IER_MSI	0x00	/* Enable Modem status interrupt */
+#define UART_IER_RLSI	0x08	/* Enable receiver line status interrupt */
+#define UART_IER_THRI	0x03	/* Enable Transmitter holding register int. */
+#define UART_IER_RDI	0x04	/* Enable receiver data interrupt */
+
+#endif /* _M32R_SIO_REG_H */
diff --git a/ap/os/linux/linux-3.4.x/drivers/tty/serial/max3100.c b/ap/os/linux/linux-3.4.x/drivers/tty/serial/max3100.c
new file mode 100644
index 0000000..b4902b9
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/tty/serial/max3100.c
@@ -0,0 +1,928 @@
+/*
+ *
+ *  Copyright (C) 2008 Christian Pellegrin <chripell@evolware.org>
+ *
+ * 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.
+ *
+ *
+ * Notes: the MAX3100 doesn't provide an interrupt on CTS so we have
+ * to use polling for flow control. TX empty IRQ is unusable, since
+ * writing conf clears FIFO buffer and we cannot have this interrupt
+ * always asking us for attention.
+ *
+ * Example platform data:
+
+ static struct plat_max3100 max3100_plat_data = {
+ .loopback = 0,
+ .crystal = 0,
+ .poll_time = 100,
+ };
+
+ static struct spi_board_info spi_board_info[] = {
+ {
+ .modalias	= "max3100",
+ .platform_data	= &max3100_plat_data,
+ .irq		= IRQ_EINT12,
+ .max_speed_hz	= 5*1000*1000,
+ .chip_select	= 0,
+ },
+ };
+
+ * The initial minor number is 209 in the low-density serial port:
+ * mknod /dev/ttyMAX0 c 204 209
+ */
+
+#define MAX3100_MAJOR 204
+#define MAX3100_MINOR 209
+/* 4 MAX3100s should be enough for everyone */
+#define MAX_MAX3100 4
+
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/serial_core.h>
+#include <linux/serial.h>
+#include <linux/spi/spi.h>
+#include <linux/freezer.h>
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
+
+#include <linux/serial_max3100.h>
+
+#define MAX3100_C    (1<<14)
+#define MAX3100_D    (0<<14)
+#define MAX3100_W    (1<<15)
+#define MAX3100_RX   (0<<15)
+
+#define MAX3100_WC   (MAX3100_W  | MAX3100_C)
+#define MAX3100_RC   (MAX3100_RX | MAX3100_C)
+#define MAX3100_WD   (MAX3100_W  | MAX3100_D)
+#define MAX3100_RD   (MAX3100_RX | MAX3100_D)
+#define MAX3100_CMD  (3 << 14)
+
+#define MAX3100_T    (1<<14)
+#define MAX3100_R    (1<<15)
+
+#define MAX3100_FEN  (1<<13)
+#define MAX3100_SHDN (1<<12)
+#define MAX3100_TM   (1<<11)
+#define MAX3100_RM   (1<<10)
+#define MAX3100_PM   (1<<9)
+#define MAX3100_RAM  (1<<8)
+#define MAX3100_IR   (1<<7)
+#define MAX3100_ST   (1<<6)
+#define MAX3100_PE   (1<<5)
+#define MAX3100_L    (1<<4)
+#define MAX3100_BAUD (0xf)
+
+#define MAX3100_TE   (1<<10)
+#define MAX3100_RAFE (1<<10)
+#define MAX3100_RTS  (1<<9)
+#define MAX3100_CTS  (1<<9)
+#define MAX3100_PT   (1<<8)
+#define MAX3100_DATA (0xff)
+
+#define MAX3100_RT   (MAX3100_R | MAX3100_T)
+#define MAX3100_RTC  (MAX3100_RT | MAX3100_CTS | MAX3100_RAFE)
+
+/* the following simulate a status reg for ignore_status_mask */
+#define MAX3100_STATUS_PE 1
+#define MAX3100_STATUS_FE 2
+#define MAX3100_STATUS_OE 4
+
+struct max3100_port {
+	struct uart_port port;
+	struct spi_device *spi;
+
+	int cts;	        /* last CTS received for flow ctrl */
+	int tx_empty;		/* last TX empty bit */
+
+	spinlock_t conf_lock;	/* shared data */
+	int conf_commit;	/* need to make changes */
+	int conf;		/* configuration for the MAX31000
+				 * (bits 0-7, bits 8-11 are irqs) */
+	int rts_commit;	        /* need to change rts */
+	int rts;		/* rts status */
+	int baud;		/* current baud rate */
+
+	int parity;		/* keeps track if we should send parity */
+#define MAX3100_PARITY_ON 1
+#define MAX3100_PARITY_ODD 2
+#define MAX3100_7BIT 4
+	int rx_enabled;	        /* if we should rx chars */
+
+	int irq;		/* irq assigned to the max3100 */
+
+	int minor;		/* minor number */
+	int crystal;		/* 1 if 3.6864Mhz crystal 0 for 1.8432 */
+	int loopback;		/* 1 if we are in loopback mode */
+
+	/* for handling irqs: need workqueue since we do spi_sync */
+	struct workqueue_struct *workqueue;
+	struct work_struct work;
+	/* set to 1 to make the workhandler exit as soon as possible */
+	int  force_end_work;
+	/* need to know we are suspending to avoid deadlock on workqueue */
+	int suspending;
+
+	/* hook for suspending MAX3100 via dedicated pin */
+	void (*max3100_hw_suspend) (int suspend);
+
+	/* poll time (in ms) for ctrl lines */
+	int poll_time;
+	/* and its timer */
+	struct timer_list	timer;
+};
+
+static struct max3100_port *max3100s[MAX_MAX3100]; /* the chips */
+static DEFINE_MUTEX(max3100s_lock);		   /* race on probe */
+
+static int max3100_do_parity(struct max3100_port *s, u16 c)
+{
+	int parity;
+
+	if (s->parity & MAX3100_PARITY_ODD)
+		parity = 1;
+	else
+		parity = 0;
+
+	if (s->parity & MAX3100_7BIT)
+		c &= 0x7f;
+	else
+		c &= 0xff;
+
+	parity = parity ^ (hweight8(c) & 1);
+	return parity;
+}
+
+static int max3100_check_parity(struct max3100_port *s, u16 c)
+{
+	return max3100_do_parity(s, c) == ((c >> 8) & 1);
+}
+
+static void max3100_calc_parity(struct max3100_port *s, u16 *c)
+{
+	if (s->parity & MAX3100_7BIT)
+		*c &= 0x7f;
+	else
+		*c &= 0xff;
+
+	if (s->parity & MAX3100_PARITY_ON)
+		*c |= max3100_do_parity(s, *c) << 8;
+}
+
+static void max3100_work(struct work_struct *w);
+
+static void max3100_dowork(struct max3100_port *s)
+{
+	if (!s->force_end_work && !work_pending(&s->work) &&
+	    !freezing(current) && !s->suspending)
+		queue_work(s->workqueue, &s->work);
+}
+
+static void max3100_timeout(unsigned long data)
+{
+	struct max3100_port *s = (struct max3100_port *)data;
+
+	if (s->port.state) {
+		max3100_dowork(s);
+		mod_timer(&s->timer, jiffies + s->poll_time);
+	}
+}
+
+static int max3100_sr(struct max3100_port *s, u16 tx, u16 *rx)
+{
+	struct spi_message message;
+	u16 etx, erx;
+	int status;
+	struct spi_transfer tran = {
+		.tx_buf = &etx,
+		.rx_buf = &erx,
+		.len = 2,
+	};
+
+	etx = cpu_to_be16(tx);
+	spi_message_init(&message);
+	spi_message_add_tail(&tran, &message);
+	status = spi_sync(s->spi, &message);
+	if (status) {
+		dev_warn(&s->spi->dev, "error while calling spi_sync\n");
+		return -EIO;
+	}
+	*rx = be16_to_cpu(erx);
+	s->tx_empty = (*rx & MAX3100_T) > 0;
+	dev_dbg(&s->spi->dev, "%04x - %04x\n", tx, *rx);
+	return 0;
+}
+
+static int max3100_handlerx(struct max3100_port *s, u16 rx)
+{
+	unsigned int ch, flg, status = 0;
+	int ret = 0, cts;
+
+	if (rx & MAX3100_R && s->rx_enabled) {
+		dev_dbg(&s->spi->dev, "%s\n", __func__);
+		ch = rx & (s->parity & MAX3100_7BIT ? 0x7f : 0xff);
+		if (rx & MAX3100_RAFE) {
+			s->port.icount.frame++;
+			flg = TTY_FRAME;
+			status |= MAX3100_STATUS_FE;
+		} else {
+			if (s->parity & MAX3100_PARITY_ON) {
+				if (max3100_check_parity(s, rx)) {
+					s->port.icount.rx++;
+					flg = TTY_NORMAL;
+				} else {
+					s->port.icount.parity++;
+					flg = TTY_PARITY;
+					status |= MAX3100_STATUS_PE;
+				}
+			} else {
+				s->port.icount.rx++;
+				flg = TTY_NORMAL;
+			}
+		}
+		uart_insert_char(&s->port, status, MAX3100_STATUS_OE, ch, flg);
+		ret = 1;
+	}
+
+	cts = (rx & MAX3100_CTS) > 0;
+	if (s->cts != cts) {
+		s->cts = cts;
+		uart_handle_cts_change(&s->port, cts ? TIOCM_CTS : 0);
+	}
+
+	return ret;
+}
+
+static void max3100_work(struct work_struct *w)
+{
+	struct max3100_port *s = container_of(w, struct max3100_port, work);
+	int rxchars;
+	u16 tx, rx;
+	int conf, cconf, rts, crts;
+	struct circ_buf *xmit = &s->port.state->xmit;
+
+	dev_dbg(&s->spi->dev, "%s\n", __func__);
+
+	rxchars = 0;
+	do {
+		spin_lock(&s->conf_lock);
+		conf = s->conf;
+		cconf = s->conf_commit;
+		s->conf_commit = 0;
+		rts = s->rts;
+		crts = s->rts_commit;
+		s->rts_commit = 0;
+		spin_unlock(&s->conf_lock);
+		if (cconf)
+			max3100_sr(s, MAX3100_WC | conf, &rx);
+		if (crts) {
+			max3100_sr(s, MAX3100_WD | MAX3100_TE |
+				   (s->rts ? MAX3100_RTS : 0), &rx);
+			rxchars += max3100_handlerx(s, rx);
+		}
+
+		max3100_sr(s, MAX3100_RD, &rx);
+		rxchars += max3100_handlerx(s, rx);
+
+		if (rx & MAX3100_T) {
+			tx = 0xffff;
+			if (s->port.x_char) {
+				tx = s->port.x_char;
+				s->port.icount.tx++;
+				s->port.x_char = 0;
+			} else if (!uart_circ_empty(xmit) &&
+				   !uart_tx_stopped(&s->port)) {
+				tx = xmit->buf[xmit->tail];
+				xmit->tail = (xmit->tail + 1) &
+					(UART_XMIT_SIZE - 1);
+				s->port.icount.tx++;
+			}
+			if (tx != 0xffff) {
+				max3100_calc_parity(s, &tx);
+				tx |= MAX3100_WD | (s->rts ? MAX3100_RTS : 0);
+				max3100_sr(s, tx, &rx);
+				rxchars += max3100_handlerx(s, rx);
+			}
+		}
+
+		if (rxchars > 16 && s->port.state->port.tty != NULL) {
+			tty_flip_buffer_push(s->port.state->port.tty);
+			rxchars = 0;
+		}
+		if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
+			uart_write_wakeup(&s->port);
+
+	} while (!s->force_end_work &&
+		 !freezing(current) &&
+		 ((rx & MAX3100_R) ||
+		  (!uart_circ_empty(xmit) &&
+		   !uart_tx_stopped(&s->port))));
+
+	if (rxchars > 0 && s->port.state->port.tty != NULL)
+		tty_flip_buffer_push(s->port.state->port.tty);
+}
+
+static irqreturn_t max3100_irq(int irqno, void *dev_id)
+{
+	struct max3100_port *s = dev_id;
+
+	dev_dbg(&s->spi->dev, "%s\n", __func__);
+
+	max3100_dowork(s);
+	return IRQ_HANDLED;
+}
+
+static void max3100_enable_ms(struct uart_port *port)
+{
+	struct max3100_port *s = container_of(port,
+					      struct max3100_port,
+					      port);
+
+	if (s->poll_time > 0)
+		mod_timer(&s->timer, jiffies);
+	dev_dbg(&s->spi->dev, "%s\n", __func__);
+}
+
+static void max3100_start_tx(struct uart_port *port)
+{
+	struct max3100_port *s = container_of(port,
+					      struct max3100_port,
+					      port);
+
+	dev_dbg(&s->spi->dev, "%s\n", __func__);
+
+	max3100_dowork(s);
+}
+
+static void max3100_stop_rx(struct uart_port *port)
+{
+	struct max3100_port *s = container_of(port,
+					      struct max3100_port,
+					      port);
+
+	dev_dbg(&s->spi->dev, "%s\n", __func__);
+
+	s->rx_enabled = 0;
+	spin_lock(&s->conf_lock);
+	s->conf &= ~MAX3100_RM;
+	s->conf_commit = 1;
+	spin_unlock(&s->conf_lock);
+	max3100_dowork(s);
+}
+
+static unsigned int max3100_tx_empty(struct uart_port *port)
+{
+	struct max3100_port *s = container_of(port,
+					      struct max3100_port,
+					      port);
+
+	dev_dbg(&s->spi->dev, "%s\n", __func__);
+
+	/* may not be truly up-to-date */
+	max3100_dowork(s);
+	return s->tx_empty;
+}
+
+static unsigned int max3100_get_mctrl(struct uart_port *port)
+{
+	struct max3100_port *s = container_of(port,
+					      struct max3100_port,
+					      port);
+
+	dev_dbg(&s->spi->dev, "%s\n", __func__);
+
+	/* may not be truly up-to-date */
+	max3100_dowork(s);
+	/* always assert DCD and DSR since these lines are not wired */
+	return (s->cts ? TIOCM_CTS : 0) | TIOCM_DSR | TIOCM_CAR;
+}
+
+static void max3100_set_mctrl(struct uart_port *port, unsigned int mctrl)
+{
+	struct max3100_port *s = container_of(port,
+					      struct max3100_port,
+					      port);
+	int rts;
+
+	dev_dbg(&s->spi->dev, "%s\n", __func__);
+
+	rts = (mctrl & TIOCM_RTS) > 0;
+
+	spin_lock(&s->conf_lock);
+	if (s->rts != rts) {
+		s->rts = rts;
+		s->rts_commit = 1;
+		max3100_dowork(s);
+	}
+	spin_unlock(&s->conf_lock);
+}
+
+static void
+max3100_set_termios(struct uart_port *port, struct ktermios *termios,
+		    struct ktermios *old)
+{
+	struct max3100_port *s = container_of(port,
+					      struct max3100_port,
+					      port);
+	int baud = 0;
+	unsigned cflag;
+	u32 param_new, param_mask, parity = 0;
+
+	dev_dbg(&s->spi->dev, "%s\n", __func__);
+
+	cflag = termios->c_cflag;
+	param_new = 0;
+	param_mask = 0;
+
+	baud = tty_termios_baud_rate(termios);
+	param_new = s->conf & MAX3100_BAUD;
+	switch (baud) {
+	case 300:
+		if (s->crystal)
+			baud = s->baud;
+		else
+			param_new = 15;
+		break;
+	case 600:
+		param_new = 14 + s->crystal;
+		break;
+	case 1200:
+		param_new = 13 + s->crystal;
+		break;
+	case 2400:
+		param_new = 12 + s->crystal;
+		break;
+	case 4800:
+		param_new = 11 + s->crystal;
+		break;
+	case 9600:
+		param_new = 10 + s->crystal;
+		break;
+	case 19200:
+		param_new = 9 + s->crystal;
+		break;
+	case 38400:
+		param_new = 8 + s->crystal;
+		break;
+	case 57600:
+		param_new = 1 + s->crystal;
+		break;
+	case 115200:
+		param_new = 0 + s->crystal;
+		break;
+	case 230400:
+		if (s->crystal)
+			param_new = 0;
+		else
+			baud = s->baud;
+		break;
+	default:
+		baud = s->baud;
+	}
+	tty_termios_encode_baud_rate(termios, baud, baud);
+	s->baud = baud;
+	param_mask |= MAX3100_BAUD;
+
+	if ((cflag & CSIZE) == CS8) {
+		param_new &= ~MAX3100_L;
+		parity &= ~MAX3100_7BIT;
+	} else {
+		param_new |= MAX3100_L;
+		parity |= MAX3100_7BIT;
+		cflag = (cflag & ~CSIZE) | CS7;
+	}
+	param_mask |= MAX3100_L;
+
+	if (cflag & CSTOPB)
+		param_new |= MAX3100_ST;
+	else
+		param_new &= ~MAX3100_ST;
+	param_mask |= MAX3100_ST;
+
+	if (cflag & PARENB) {
+		param_new |= MAX3100_PE;
+		parity |= MAX3100_PARITY_ON;
+	} else {
+		param_new &= ~MAX3100_PE;
+		parity &= ~MAX3100_PARITY_ON;
+	}
+	param_mask |= MAX3100_PE;
+
+	if (cflag & PARODD)
+		parity |= MAX3100_PARITY_ODD;
+	else
+		parity &= ~MAX3100_PARITY_ODD;
+
+	/* mask termios capabilities we don't support */
+	cflag &= ~CMSPAR;
+	termios->c_cflag = cflag;
+
+	s->port.ignore_status_mask = 0;
+	if (termios->c_iflag & IGNPAR)
+		s->port.ignore_status_mask |=
+			MAX3100_STATUS_PE | MAX3100_STATUS_FE |
+			MAX3100_STATUS_OE;
+
+	/* we are sending char from a workqueue so enable */
+	s->port.state->port.tty->low_latency = 1;
+
+	if (s->poll_time > 0)
+		del_timer_sync(&s->timer);
+
+	uart_update_timeout(port, termios->c_cflag, baud);
+
+	spin_lock(&s->conf_lock);
+	s->conf = (s->conf & ~param_mask) | (param_new & param_mask);
+	s->conf_commit = 1;
+	s->parity = parity;
+	spin_unlock(&s->conf_lock);
+	max3100_dowork(s);
+
+	if (UART_ENABLE_MS(&s->port, termios->c_cflag))
+		max3100_enable_ms(&s->port);
+}
+
+static void max3100_shutdown(struct uart_port *port)
+{
+	struct max3100_port *s = container_of(port,
+					      struct max3100_port,
+					      port);
+
+	dev_dbg(&s->spi->dev, "%s\n", __func__);
+
+	if (s->suspending)
+		return;
+
+	s->force_end_work = 1;
+
+	if (s->poll_time > 0)
+		del_timer_sync(&s->timer);
+
+	if (s->workqueue) {
+		flush_workqueue(s->workqueue);
+		destroy_workqueue(s->workqueue);
+		s->workqueue = NULL;
+	}
+	if (s->irq)
+		free_irq(s->irq, s);
+
+	/* set shutdown mode to save power */
+	if (s->max3100_hw_suspend)
+		s->max3100_hw_suspend(1);
+	else  {
+		u16 tx, rx;
+
+		tx = MAX3100_WC | MAX3100_SHDN;
+		max3100_sr(s, tx, &rx);
+	}
+}
+
+static int max3100_startup(struct uart_port *port)
+{
+	struct max3100_port *s = container_of(port,
+					      struct max3100_port,
+					      port);
+	char b[12];
+
+	dev_dbg(&s->spi->dev, "%s\n", __func__);
+
+	s->conf = MAX3100_RM;
+	s->baud = s->crystal ? 230400 : 115200;
+	s->rx_enabled = 1;
+
+	if (s->suspending)
+		return 0;
+
+	s->force_end_work = 0;
+	s->parity = 0;
+	s->rts = 0;
+
+	sprintf(b, "max3100-%d", s->minor);
+	s->workqueue = create_freezable_workqueue(b);
+	if (!s->workqueue) {
+		dev_warn(&s->spi->dev, "cannot create workqueue\n");
+		return -EBUSY;
+	}
+	INIT_WORK(&s->work, max3100_work);
+
+	if (request_irq(s->irq, max3100_irq,
+			IRQF_TRIGGER_FALLING, "max3100", s) < 0) {
+		dev_warn(&s->spi->dev, "cannot allocate irq %d\n", s->irq);
+		s->irq = 0;
+		destroy_workqueue(s->workqueue);
+		s->workqueue = NULL;
+		return -EBUSY;
+	}
+
+	if (s->loopback) {
+		u16 tx, rx;
+		tx = 0x4001;
+		max3100_sr(s, tx, &rx);
+	}
+
+	if (s->max3100_hw_suspend)
+		s->max3100_hw_suspend(0);
+	s->conf_commit = 1;
+	max3100_dowork(s);
+	/* wait for clock to settle */
+	msleep(50);
+
+	max3100_enable_ms(&s->port);
+
+	return 0;
+}
+
+static const char *max3100_type(struct uart_port *port)
+{
+	struct max3100_port *s = container_of(port,
+					      struct max3100_port,
+					      port);
+
+	dev_dbg(&s->spi->dev, "%s\n", __func__);
+
+	return s->port.type == PORT_MAX3100 ? "MAX3100" : NULL;
+}
+
+static void max3100_release_port(struct uart_port *port)
+{
+	struct max3100_port *s = container_of(port,
+					      struct max3100_port,
+					      port);
+
+	dev_dbg(&s->spi->dev, "%s\n", __func__);
+}
+
+static void max3100_config_port(struct uart_port *port, int flags)
+{
+	struct max3100_port *s = container_of(port,
+					      struct max3100_port,
+					      port);
+
+	dev_dbg(&s->spi->dev, "%s\n", __func__);
+
+	if (flags & UART_CONFIG_TYPE)
+		s->port.type = PORT_MAX3100;
+}
+
+static int max3100_verify_port(struct uart_port *port,
+			       struct serial_struct *ser)
+{
+	struct max3100_port *s = container_of(port,
+					      struct max3100_port,
+					      port);
+	int ret = -EINVAL;
+
+	dev_dbg(&s->spi->dev, "%s\n", __func__);
+
+	if (ser->type == PORT_UNKNOWN || ser->type == PORT_MAX3100)
+		ret = 0;
+	return ret;
+}
+
+static void max3100_stop_tx(struct uart_port *port)
+{
+	struct max3100_port *s = container_of(port,
+					      struct max3100_port,
+					      port);
+
+	dev_dbg(&s->spi->dev, "%s\n", __func__);
+}
+
+static int max3100_request_port(struct uart_port *port)
+{
+	struct max3100_port *s = container_of(port,
+					      struct max3100_port,
+					      port);
+
+	dev_dbg(&s->spi->dev, "%s\n", __func__);
+	return 0;
+}
+
+static void max3100_break_ctl(struct uart_port *port, int break_state)
+{
+	struct max3100_port *s = container_of(port,
+					      struct max3100_port,
+					      port);
+
+	dev_dbg(&s->spi->dev, "%s\n", __func__);
+}
+
+static struct uart_ops max3100_ops = {
+	.tx_empty	= max3100_tx_empty,
+	.set_mctrl	= max3100_set_mctrl,
+	.get_mctrl	= max3100_get_mctrl,
+	.stop_tx        = max3100_stop_tx,
+	.start_tx	= max3100_start_tx,
+	.stop_rx	= max3100_stop_rx,
+	.enable_ms      = max3100_enable_ms,
+	.break_ctl      = max3100_break_ctl,
+	.startup	= max3100_startup,
+	.shutdown	= max3100_shutdown,
+	.set_termios	= max3100_set_termios,
+	.type		= max3100_type,
+	.release_port   = max3100_release_port,
+	.request_port   = max3100_request_port,
+	.config_port	= max3100_config_port,
+	.verify_port	= max3100_verify_port,
+};
+
+static struct uart_driver max3100_uart_driver = {
+	.owner          = THIS_MODULE,
+	.driver_name    = "ttyMAX",
+	.dev_name       = "ttyMAX",
+	.major          = MAX3100_MAJOR,
+	.minor          = MAX3100_MINOR,
+	.nr             = MAX_MAX3100,
+};
+static int uart_driver_registered;
+
+static int __devinit max3100_probe(struct spi_device *spi)
+{
+	int i, retval;
+	struct plat_max3100 *pdata;
+	u16 tx, rx;
+
+	mutex_lock(&max3100s_lock);
+
+	if (!uart_driver_registered) {
+		uart_driver_registered = 1;
+		retval = uart_register_driver(&max3100_uart_driver);
+		if (retval) {
+			printk(KERN_ERR "Couldn't register max3100 uart driver\n");
+			mutex_unlock(&max3100s_lock);
+			return retval;
+		}
+	}
+
+	for (i = 0; i < MAX_MAX3100; i++)
+		if (!max3100s[i])
+			break;
+	if (i == MAX_MAX3100) {
+		dev_warn(&spi->dev, "too many MAX3100 chips\n");
+		mutex_unlock(&max3100s_lock);
+		return -ENOMEM;
+	}
+
+	max3100s[i] = kzalloc(sizeof(struct max3100_port), GFP_KERNEL);
+	if (!max3100s[i]) {
+		dev_warn(&spi->dev,
+			 "kmalloc for max3100 structure %d failed!\n", i);
+		mutex_unlock(&max3100s_lock);
+		return -ENOMEM;
+	}
+	max3100s[i]->spi = spi;
+	max3100s[i]->irq = spi->irq;
+	spin_lock_init(&max3100s[i]->conf_lock);
+	dev_set_drvdata(&spi->dev, max3100s[i]);
+	pdata = spi->dev.platform_data;
+	max3100s[i]->crystal = pdata->crystal;
+	max3100s[i]->loopback = pdata->loopback;
+	max3100s[i]->poll_time = pdata->poll_time * HZ / 1000;
+	if (pdata->poll_time > 0 && max3100s[i]->poll_time == 0)
+		max3100s[i]->poll_time = 1;
+	max3100s[i]->max3100_hw_suspend = pdata->max3100_hw_suspend;
+	max3100s[i]->minor = i;
+	init_timer(&max3100s[i]->timer);
+	max3100s[i]->timer.function = max3100_timeout;
+	max3100s[i]->timer.data = (unsigned long) max3100s[i];
+
+	dev_dbg(&spi->dev, "%s: adding port %d\n", __func__, i);
+	max3100s[i]->port.irq = max3100s[i]->irq;
+	max3100s[i]->port.uartclk = max3100s[i]->crystal ? 3686400 : 1843200;
+	max3100s[i]->port.fifosize = 16;
+	max3100s[i]->port.ops = &max3100_ops;
+	max3100s[i]->port.flags = UPF_SKIP_TEST | UPF_BOOT_AUTOCONF;
+	max3100s[i]->port.line = i;
+	max3100s[i]->port.type = PORT_MAX3100;
+	max3100s[i]->port.dev = &spi->dev;
+	retval = uart_add_one_port(&max3100_uart_driver, &max3100s[i]->port);
+	if (retval < 0)
+		dev_warn(&spi->dev,
+			 "uart_add_one_port failed for line %d with error %d\n",
+			 i, retval);
+
+	/* set shutdown mode to save power. Will be woken-up on open */
+	if (max3100s[i]->max3100_hw_suspend)
+		max3100s[i]->max3100_hw_suspend(1);
+	else {
+		tx = MAX3100_WC | MAX3100_SHDN;
+		max3100_sr(max3100s[i], tx, &rx);
+	}
+	mutex_unlock(&max3100s_lock);
+	return 0;
+}
+
+static int __devexit max3100_remove(struct spi_device *spi)
+{
+	struct max3100_port *s = dev_get_drvdata(&spi->dev);
+	int i;
+
+	mutex_lock(&max3100s_lock);
+
+	/* find out the index for the chip we are removing */
+	for (i = 0; i < MAX_MAX3100; i++)
+		if (max3100s[i] == s)
+			break;
+
+	dev_dbg(&spi->dev, "%s: removing port %d\n", __func__, i);
+	uart_remove_one_port(&max3100_uart_driver, &max3100s[i]->port);
+	kfree(max3100s[i]);
+	max3100s[i] = NULL;
+
+	/* check if this is the last chip we have */
+	for (i = 0; i < MAX_MAX3100; i++)
+		if (max3100s[i]) {
+			mutex_unlock(&max3100s_lock);
+			return 0;
+		}
+	pr_debug("removing max3100 driver\n");
+	uart_unregister_driver(&max3100_uart_driver);
+
+	mutex_unlock(&max3100s_lock);
+	return 0;
+}
+
+#ifdef CONFIG_PM
+
+static int max3100_suspend(struct spi_device *spi, pm_message_t state)
+{
+	struct max3100_port *s = dev_get_drvdata(&spi->dev);
+
+	dev_dbg(&s->spi->dev, "%s\n", __func__);
+
+	disable_irq(s->irq);
+
+	s->suspending = 1;
+	uart_suspend_port(&max3100_uart_driver, &s->port);
+
+	if (s->max3100_hw_suspend)
+		s->max3100_hw_suspend(1);
+	else {
+		/* no HW suspend, so do SW one */
+		u16 tx, rx;
+
+		tx = MAX3100_WC | MAX3100_SHDN;
+		max3100_sr(s, tx, &rx);
+	}
+	return 0;
+}
+
+static int max3100_resume(struct spi_device *spi)
+{
+	struct max3100_port *s = dev_get_drvdata(&spi->dev);
+
+	dev_dbg(&s->spi->dev, "%s\n", __func__);
+
+	if (s->max3100_hw_suspend)
+		s->max3100_hw_suspend(0);
+	uart_resume_port(&max3100_uart_driver, &s->port);
+	s->suspending = 0;
+
+	enable_irq(s->irq);
+
+	s->conf_commit = 1;
+	if (s->workqueue)
+		max3100_dowork(s);
+
+	return 0;
+}
+
+#else
+#define max3100_suspend NULL
+#define max3100_resume  NULL
+#endif
+
+static struct spi_driver max3100_driver = {
+	.driver = {
+		.name		= "max3100",
+		.owner		= THIS_MODULE,
+	},
+
+	.probe		= max3100_probe,
+	.remove		= __devexit_p(max3100_remove),
+	.suspend	= max3100_suspend,
+	.resume		= max3100_resume,
+};
+
+static int __init max3100_init(void)
+{
+	return spi_register_driver(&max3100_driver);
+}
+module_init(max3100_init);
+
+static void __exit max3100_exit(void)
+{
+	spi_unregister_driver(&max3100_driver);
+}
+module_exit(max3100_exit);
+
+MODULE_DESCRIPTION("MAX3100 driver");
+MODULE_AUTHOR("Christian Pellegrin <chripell@evolware.org>");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("spi:max3100");
diff --git a/ap/os/linux/linux-3.4.x/drivers/tty/serial/max3107.c b/ap/os/linux/linux-3.4.x/drivers/tty/serial/max3107.c
new file mode 100644
index 0000000..17c7ba8
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/tty/serial/max3107.c
@@ -0,0 +1,1215 @@
+/*
+ *  max3107.c - spi uart protocol driver for Maxim 3107
+ *  Based on max3100.c
+ *	by Christian Pellegrin <chripell@evolware.org>
+ *  and	max3110.c
+ *	by Feng Tang <feng.tang@intel.com>
+ *
+ *  Copyright (C) Aavamobile 2009
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ *  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, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ */
+
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/serial_core.h>
+#include <linux/serial.h>
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
+#include <linux/gpio.h>
+#include <linux/spi/spi.h>
+#include <linux/freezer.h>
+#include <linux/module.h>
+#include "max3107.h"
+
+static const struct baud_table brg26_ext[] = {
+	{ 300,    MAX3107_BRG26_B300 },
+	{ 600,    MAX3107_BRG26_B600 },
+	{ 1200,   MAX3107_BRG26_B1200 },
+	{ 2400,   MAX3107_BRG26_B2400 },
+	{ 4800,   MAX3107_BRG26_B4800 },
+	{ 9600,   MAX3107_BRG26_B9600 },
+	{ 19200,  MAX3107_BRG26_B19200 },
+	{ 57600,  MAX3107_BRG26_B57600 },
+	{ 115200, MAX3107_BRG26_B115200 },
+	{ 230400, MAX3107_BRG26_B230400 },
+	{ 460800, MAX3107_BRG26_B460800 },
+	{ 921600, MAX3107_BRG26_B921600 },
+	{ 0, 0 }
+};
+
+static const struct baud_table brg13_int[] = {
+	{ 300,    MAX3107_BRG13_IB300 },
+	{ 600,    MAX3107_BRG13_IB600 },
+	{ 1200,   MAX3107_BRG13_IB1200 },
+	{ 2400,   MAX3107_BRG13_IB2400 },
+	{ 4800,   MAX3107_BRG13_IB4800 },
+	{ 9600,   MAX3107_BRG13_IB9600 },
+	{ 19200,  MAX3107_BRG13_IB19200 },
+	{ 57600,  MAX3107_BRG13_IB57600 },
+	{ 115200, MAX3107_BRG13_IB115200 },
+	{ 230400, MAX3107_BRG13_IB230400 },
+	{ 460800, MAX3107_BRG13_IB460800 },
+	{ 921600, MAX3107_BRG13_IB921600 },
+	{ 0, 0 }
+};
+
+static u32 get_new_brg(int baud, struct max3107_port *s)
+{
+	int i;
+	const struct baud_table *baud_tbl = s->baud_tbl;
+
+	for (i = 0; i < 13; i++) {
+		if (baud == baud_tbl[i].baud)
+			return baud_tbl[i].new_brg;
+	}
+
+	return 0;
+}
+
+/* Perform SPI transfer for write/read of device register(s) */
+int max3107_rw(struct max3107_port *s, u8 *tx, u8 *rx, int len)
+{
+	struct spi_message spi_msg;
+	struct spi_transfer spi_xfer;
+
+	/* Initialize SPI ,message */
+	spi_message_init(&spi_msg);
+
+	/* Initialize SPI transfer */
+	memset(&spi_xfer, 0, sizeof spi_xfer);
+	spi_xfer.len = len;
+	spi_xfer.tx_buf = tx;
+	spi_xfer.rx_buf = rx;
+	spi_xfer.speed_hz = MAX3107_SPI_SPEED;
+
+	/* Add SPI transfer to SPI message */
+	spi_message_add_tail(&spi_xfer, &spi_msg);
+
+#ifdef DBG_TRACE_SPI_DATA
+	{
+		int i;
+		pr_info("tx len %d:\n", spi_xfer.len);
+		for (i = 0 ; i < spi_xfer.len && i < 32 ; i++)
+			pr_info(" %x", ((u8 *)spi_xfer.tx_buf)[i]);
+		pr_info("\n");
+	}
+#endif
+
+	/* Perform synchronous SPI transfer */
+	if (spi_sync(s->spi, &spi_msg)) {
+		dev_err(&s->spi->dev, "spi_sync failure\n");
+		return -EIO;
+	}
+
+#ifdef DBG_TRACE_SPI_DATA
+	if (spi_xfer.rx_buf) {
+		int i;
+		pr_info("rx len %d:\n", spi_xfer.len);
+		for (i = 0 ; i < spi_xfer.len && i < 32 ; i++)
+			pr_info(" %x", ((u8 *)spi_xfer.rx_buf)[i]);
+		pr_info("\n");
+	}
+#endif
+	return 0;
+}
+EXPORT_SYMBOL_GPL(max3107_rw);
+
+/* Puts received data to circular buffer */
+static void put_data_to_circ_buf(struct max3107_port *s, unsigned char *data,
+					int len)
+{
+	struct uart_port *port = &s->port;
+	struct tty_struct *tty;
+
+	if (!port->state)
+		return;
+
+	tty = port->state->port.tty;
+	if (!tty)
+		return;
+
+	/* Insert received data */
+	tty_insert_flip_string(tty, data, len);
+	/* Update RX counter */
+	port->icount.rx += len;
+}
+
+/* Handle data receiving */
+static void max3107_handlerx(struct max3107_port *s, u16 rxlvl)
+{
+	int i;
+	int j;
+	int len;				/* SPI transfer buffer length */
+	u16 *buf;
+	u8 *valid_str;
+
+	if (!s->rx_enabled)
+		/* RX is disabled */
+		return;
+
+	if (rxlvl == 0) {
+		/* RX fifo is empty */
+		return;
+	} else if (rxlvl >= MAX3107_RX_FIFO_SIZE) {
+		dev_warn(&s->spi->dev, "Possible RX FIFO overrun %d\n", rxlvl);
+		/* Ensure sanity of RX level */
+		rxlvl = MAX3107_RX_FIFO_SIZE;
+	}
+	if ((s->rxbuf == 0) || (s->rxstr == 0)) {
+		dev_warn(&s->spi->dev, "Rx buffer/str isn't ready\n");
+		return;
+	}
+	buf = s->rxbuf;
+	valid_str = s->rxstr;
+	while (rxlvl) {
+		pr_debug("rxlvl %d\n", rxlvl);
+		/* Clear buffer */
+		memset(buf, 0, sizeof(u16) * (MAX3107_RX_FIFO_SIZE + 2));
+		len = 0;
+		if (s->irqen_reg & MAX3107_IRQ_RXFIFO_BIT) {
+			/* First disable RX FIFO interrupt */
+			pr_debug("Disabling RX INT\n");
+			buf[0] = (MAX3107_WRITE_BIT | MAX3107_IRQEN_REG);
+			s->irqen_reg &= ~MAX3107_IRQ_RXFIFO_BIT;
+			buf[0] |= s->irqen_reg;
+			len++;
+		}
+		/* Just increase the length by amount of words in FIFO since
+		 * buffer was zeroed and SPI transfer of 0x0000 means reading
+		 * from RX FIFO
+		 */
+		len += rxlvl;
+		/* Append RX level query */
+		buf[len] = MAX3107_RXFIFOLVL_REG;
+		len++;
+
+		/* Perform the SPI transfer */
+		if (max3107_rw(s, (u8 *)buf, (u8 *)buf, len * 2)) {
+			dev_err(&s->spi->dev, "SPI transfer for RX h failed\n");
+			return;
+		}
+
+		/* Skip RX FIFO interrupt disabling word if it was added */
+		j = ((len - 1) - rxlvl);
+		/* Read received words */
+		for (i = 0; i < rxlvl; i++, j++)
+			valid_str[i] = (u8)buf[j];
+		put_data_to_circ_buf(s, valid_str, rxlvl);
+		/* Get new RX level */
+		rxlvl = (buf[len - 1] & MAX3107_SPI_RX_DATA_MASK);
+	}
+
+	if (s->rx_enabled) {
+		/* RX still enabled, re-enable RX FIFO interrupt */
+		pr_debug("Enabling RX INT\n");
+		buf[0] = (MAX3107_WRITE_BIT | MAX3107_IRQEN_REG);
+		s->irqen_reg |= MAX3107_IRQ_RXFIFO_BIT;
+		buf[0] |= s->irqen_reg;
+		if (max3107_rw(s, (u8 *)buf, NULL, 2))
+			dev_err(&s->spi->dev, "RX FIFO INT enabling failed\n");
+	}
+
+	/* Push the received data to receivers */
+	if (s->port.state->port.tty)
+		tty_flip_buffer_push(s->port.state->port.tty);
+}
+
+
+/* Handle data sending */
+static void max3107_handletx(struct max3107_port *s)
+{
+	struct circ_buf *xmit = &s->port.state->xmit;
+	int i;
+	unsigned long flags;
+	int len;				/* SPI transfer buffer length */
+	u16 *buf;
+
+	if (!s->tx_fifo_empty)
+		/* Don't send more data before previous data is sent */
+		return;
+
+	if (uart_circ_empty(xmit) || uart_tx_stopped(&s->port))
+		/* No data to send or TX is stopped */
+		return;
+
+	if (!s->txbuf) {
+		dev_warn(&s->spi->dev, "Txbuf isn't ready\n");
+		return;
+	}
+	buf = s->txbuf;
+	/* Get length of data pending in circular buffer */
+	len = uart_circ_chars_pending(xmit);
+	if (len) {
+		/* Limit to size of TX FIFO */
+		if (len > MAX3107_TX_FIFO_SIZE)
+			len = MAX3107_TX_FIFO_SIZE;
+
+		pr_debug("txlen %d\n", len);
+
+		/* Update TX counter */
+		s->port.icount.tx += len;
+
+		/* TX FIFO will no longer be empty */
+		s->tx_fifo_empty = 0;
+
+		i = 0;
+		if (s->irqen_reg & MAX3107_IRQ_TXEMPTY_BIT) {
+			/* First disable TX empty interrupt */
+			pr_debug("Disabling TE INT\n");
+			buf[i] = (MAX3107_WRITE_BIT | MAX3107_IRQEN_REG);
+			s->irqen_reg &= ~MAX3107_IRQ_TXEMPTY_BIT;
+			buf[i] |= s->irqen_reg;
+			i++;
+			len++;
+		}
+		/* Add data to send */
+		spin_lock_irqsave(&s->port.lock, flags);
+		for ( ; i < len ; i++) {
+			buf[i] = (MAX3107_WRITE_BIT | MAX3107_THR_REG);
+			buf[i] |= ((u16)xmit->buf[xmit->tail] &
+						MAX3107_SPI_TX_DATA_MASK);
+			xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
+		}
+		spin_unlock_irqrestore(&s->port.lock, flags);
+		if (!(s->irqen_reg & MAX3107_IRQ_TXEMPTY_BIT)) {
+			/* Enable TX empty interrupt */
+			pr_debug("Enabling TE INT\n");
+			buf[i] = (MAX3107_WRITE_BIT | MAX3107_IRQEN_REG);
+			s->irqen_reg |= MAX3107_IRQ_TXEMPTY_BIT;
+			buf[i] |= s->irqen_reg;
+			i++;
+			len++;
+		}
+		if (!s->tx_enabled) {
+			/* Enable TX */
+			pr_debug("Enable TX\n");
+			buf[i] = (MAX3107_WRITE_BIT | MAX3107_MODE1_REG);
+			spin_lock_irqsave(&s->data_lock, flags);
+			s->mode1_reg &= ~MAX3107_MODE1_TXDIS_BIT;
+			buf[i] |= s->mode1_reg;
+			spin_unlock_irqrestore(&s->data_lock, flags);
+			s->tx_enabled = 1;
+			i++;
+			len++;
+		}
+
+		/* Perform the SPI transfer */
+		if (max3107_rw(s, (u8 *)buf, NULL, len*2)) {
+			dev_err(&s->spi->dev,
+				"SPI transfer TX handling failed\n");
+			return;
+		}
+	}
+
+	/* Indicate wake up if circular buffer is getting low on data */
+	if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
+		uart_write_wakeup(&s->port);
+
+}
+
+/* Handle interrupts
+ * Also reads and returns current RX FIFO level
+ */
+static u16 handle_interrupt(struct max3107_port *s)
+{
+	u16 buf[4];	/* Buffer for SPI transfers */
+	u8 irq_status;
+	u16 rx_level;
+	unsigned long flags;
+
+	/* Read IRQ status register */
+	buf[0] = MAX3107_IRQSTS_REG;
+	/* Read status IRQ status register */
+	buf[1] = MAX3107_STS_IRQSTS_REG;
+	/* Read LSR IRQ status register */
+	buf[2] = MAX3107_LSR_IRQSTS_REG;
+	/* Query RX level */
+	buf[3] = MAX3107_RXFIFOLVL_REG;
+
+	if (max3107_rw(s, (u8 *)buf, (u8 *)buf, 8)) {
+		dev_err(&s->spi->dev,
+			"SPI transfer for INTR handling failed\n");
+		return 0;
+	}
+
+	irq_status = (u8)buf[0];
+	pr_debug("IRQSTS %x\n", irq_status);
+	rx_level = (buf[3] & MAX3107_SPI_RX_DATA_MASK);
+
+	if (irq_status & MAX3107_IRQ_LSR_BIT) {
+		/* LSR interrupt */
+		if (buf[2] & MAX3107_LSR_RXTO_BIT)
+			/* RX timeout interrupt,
+			 * handled by normal RX handling
+			 */
+			pr_debug("RX TO INT\n");
+	}
+
+	if (irq_status & MAX3107_IRQ_TXEMPTY_BIT) {
+		/* Tx empty interrupt,
+		 * disable TX and set tx_fifo_empty flag
+		 */
+		pr_debug("TE INT, disabling TX\n");
+		buf[0] = (MAX3107_WRITE_BIT | MAX3107_MODE1_REG);
+		spin_lock_irqsave(&s->data_lock, flags);
+		s->mode1_reg |= MAX3107_MODE1_TXDIS_BIT;
+		buf[0] |= s->mode1_reg;
+		spin_unlock_irqrestore(&s->data_lock, flags);
+		if (max3107_rw(s, (u8 *)buf, NULL, 2))
+			dev_err(&s->spi->dev, "SPI transfer TX dis failed\n");
+		s->tx_enabled = 0;
+		s->tx_fifo_empty = 1;
+	}
+
+	if (irq_status & MAX3107_IRQ_RXFIFO_BIT)
+		/* RX FIFO interrupt,
+		 * handled by normal RX handling
+		 */
+		pr_debug("RFIFO INT\n");
+
+	/* Return RX level */
+	return rx_level;
+}
+
+/* Trigger work thread*/
+static void max3107_dowork(struct max3107_port *s)
+{
+	if (!work_pending(&s->work) && !freezing(current) && !s->suspended)
+		queue_work(s->workqueue, &s->work);
+	else
+		dev_warn(&s->spi->dev, "interrup isn't serviced normally!\n");
+}
+
+/* Work thread */
+static void max3107_work(struct work_struct *w)
+{
+	struct max3107_port *s = container_of(w, struct max3107_port, work);
+	u16 rxlvl = 0;
+	int len;	/* SPI transfer buffer length */
+	u16 buf[5];	/* Buffer for SPI transfers */
+	unsigned long flags;
+
+	/* Start by reading current RX FIFO level */
+	buf[0] = MAX3107_RXFIFOLVL_REG;
+	if (max3107_rw(s, (u8 *)buf, (u8 *)buf, 2)) {
+		dev_err(&s->spi->dev, "SPI transfer RX lev failed\n");
+		rxlvl = 0;
+	} else {
+		rxlvl = (buf[0] & MAX3107_SPI_RX_DATA_MASK);
+	}
+
+	do {
+		pr_debug("rxlvl %d\n", rxlvl);
+
+		/* Handle RX */
+		max3107_handlerx(s, rxlvl);
+		rxlvl = 0;
+
+		if (s->handle_irq) {
+			/* Handle pending interrupts
+			 * We also get new RX FIFO level since new data may
+			 * have been received while pushing received data to
+			 * receivers
+			 */
+			s->handle_irq = 0;
+			rxlvl = handle_interrupt(s);
+		}
+
+		/* Handle TX */
+		max3107_handletx(s);
+
+		/* Handle configuration changes */
+		len = 0;
+		spin_lock_irqsave(&s->data_lock, flags);
+		if (s->mode1_commit) {
+			pr_debug("mode1_commit\n");
+			buf[len] = (MAX3107_WRITE_BIT | MAX3107_MODE1_REG);
+			buf[len++] |= s->mode1_reg;
+			s->mode1_commit = 0;
+		}
+		if (s->lcr_commit) {
+			pr_debug("lcr_commit\n");
+			buf[len] = (MAX3107_WRITE_BIT | MAX3107_LCR_REG);
+			buf[len++] |= s->lcr_reg;
+			s->lcr_commit = 0;
+		}
+		if (s->brg_commit) {
+			pr_debug("brg_commit\n");
+			buf[len] = (MAX3107_WRITE_BIT | MAX3107_BRGDIVMSB_REG);
+			buf[len++] |= ((s->brg_cfg >> 16) &
+						MAX3107_SPI_TX_DATA_MASK);
+			buf[len] = (MAX3107_WRITE_BIT | MAX3107_BRGDIVLSB_REG);
+			buf[len++] |= ((s->brg_cfg >> 8) &
+						MAX3107_SPI_TX_DATA_MASK);
+			buf[len] = (MAX3107_WRITE_BIT | MAX3107_BRGCFG_REG);
+			buf[len++] |= ((s->brg_cfg) & 0xff);
+			s->brg_commit = 0;
+		}
+		spin_unlock_irqrestore(&s->data_lock, flags);
+
+		if (len > 0) {
+			if (max3107_rw(s, (u8 *)buf, NULL, len * 2))
+				dev_err(&s->spi->dev,
+					"SPI transfer config failed\n");
+		}
+
+		/* Reloop if interrupt handling indicated data in RX FIFO */
+	} while (rxlvl);
+
+}
+
+/* Set sleep mode */
+static void max3107_set_sleep(struct max3107_port *s, int mode)
+{
+	u16 buf[1];	/* Buffer for SPI transfer */
+	unsigned long flags;
+	pr_debug("enter, mode %d\n", mode);
+
+	buf[0] = (MAX3107_WRITE_BIT | MAX3107_MODE1_REG);
+	spin_lock_irqsave(&s->data_lock, flags);
+	switch (mode) {
+	case MAX3107_DISABLE_FORCED_SLEEP:
+			s->mode1_reg &= ~MAX3107_MODE1_FORCESLEEP_BIT;
+			break;
+	case MAX3107_ENABLE_FORCED_SLEEP:
+			s->mode1_reg |= MAX3107_MODE1_FORCESLEEP_BIT;
+			break;
+	case MAX3107_DISABLE_AUTOSLEEP:
+			s->mode1_reg &= ~MAX3107_MODE1_AUTOSLEEP_BIT;
+			break;
+	case MAX3107_ENABLE_AUTOSLEEP:
+			s->mode1_reg |= MAX3107_MODE1_AUTOSLEEP_BIT;
+			break;
+	default:
+		spin_unlock_irqrestore(&s->data_lock, flags);
+		dev_warn(&s->spi->dev, "invalid sleep mode\n");
+		return;
+	}
+	buf[0] |= s->mode1_reg;
+	spin_unlock_irqrestore(&s->data_lock, flags);
+
+	if (max3107_rw(s, (u8 *)buf, NULL, 2))
+		dev_err(&s->spi->dev, "SPI transfer sleep mode failed\n");
+
+	if (mode == MAX3107_DISABLE_AUTOSLEEP ||
+			mode == MAX3107_DISABLE_FORCED_SLEEP)
+		msleep(MAX3107_WAKEUP_DELAY);
+}
+
+/* Perform full register initialization */
+static void max3107_register_init(struct max3107_port *s)
+{
+	u16 buf[11];	/* Buffer for SPI transfers */
+
+	/* 1. Configure baud rate, 9600 as default */
+	s->baud = 9600;
+	/* the below is default*/
+	if (s->ext_clk) {
+		s->brg_cfg = MAX3107_BRG26_B9600;
+		s->baud_tbl = (struct baud_table *)brg26_ext;
+	} else {
+		s->brg_cfg = MAX3107_BRG13_IB9600;
+		s->baud_tbl = (struct baud_table *)brg13_int;
+	}
+
+	if (s->pdata->init)
+		s->pdata->init(s);
+
+	buf[0] = (MAX3107_WRITE_BIT | MAX3107_BRGDIVMSB_REG)
+		| ((s->brg_cfg >> 16) & MAX3107_SPI_TX_DATA_MASK);
+	buf[1] = (MAX3107_WRITE_BIT | MAX3107_BRGDIVLSB_REG)
+		| ((s->brg_cfg >> 8) & MAX3107_SPI_TX_DATA_MASK);
+	buf[2] = (MAX3107_WRITE_BIT | MAX3107_BRGCFG_REG)
+		| ((s->brg_cfg) & 0xff);
+
+	/* 2. Configure LCR register, 8N1 mode by default */
+	s->lcr_reg = MAX3107_LCR_WORD_LEN_8;
+	buf[3] = (MAX3107_WRITE_BIT | MAX3107_LCR_REG)
+		| s->lcr_reg;
+
+	/* 3. Configure MODE 1 register */
+	s->mode1_reg = 0;
+	/* Enable IRQ pin */
+	s->mode1_reg |= MAX3107_MODE1_IRQSEL_BIT;
+	/* Disable TX */
+	s->mode1_reg |= MAX3107_MODE1_TXDIS_BIT;
+	s->tx_enabled = 0;
+	/* RX is enabled */
+	s->rx_enabled = 1;
+	buf[4] = (MAX3107_WRITE_BIT | MAX3107_MODE1_REG)
+		| s->mode1_reg;
+
+	/* 4. Configure MODE 2 register */
+	buf[5] = (MAX3107_WRITE_BIT | MAX3107_MODE2_REG);
+	if (s->loopback) {
+		/* Enable loopback */
+		buf[5] |= MAX3107_MODE2_LOOPBACK_BIT;
+	}
+	/* Reset FIFOs */
+	buf[5] |= MAX3107_MODE2_FIFORST_BIT;
+	s->tx_fifo_empty = 1;
+
+	/* 5. Configure FIFO trigger level register */
+	buf[6] = (MAX3107_WRITE_BIT | MAX3107_FIFOTRIGLVL_REG);
+	/* RX FIFO trigger for 16 words, TX FIFO trigger not used */
+	buf[6] |= (MAX3107_FIFOTRIGLVL_RX(16) | MAX3107_FIFOTRIGLVL_TX(0));
+
+	/* 6. Configure flow control levels */
+	buf[7] = (MAX3107_WRITE_BIT | MAX3107_FLOWLVL_REG);
+	/* Flow control halt level 96, resume level 48 */
+	buf[7] |= (MAX3107_FLOWLVL_RES(48) | MAX3107_FLOWLVL_HALT(96));
+
+	/* 7. Configure flow control */
+	buf[8] = (MAX3107_WRITE_BIT | MAX3107_FLOWCTRL_REG);
+	/* Enable auto CTS and auto RTS flow control */
+	buf[8] |= (MAX3107_FLOWCTRL_AUTOCTS_BIT | MAX3107_FLOWCTRL_AUTORTS_BIT);
+
+	/* 8. Configure RX timeout register */
+	buf[9] = (MAX3107_WRITE_BIT | MAX3107_RXTO_REG);
+	/* Timeout after 48 character intervals */
+	buf[9] |= 0x0030;
+
+	/* 9. Configure LSR interrupt enable register */
+	buf[10] = (MAX3107_WRITE_BIT | MAX3107_LSR_IRQEN_REG);
+	/* Enable RX timeout interrupt */
+	buf[10] |= MAX3107_LSR_RXTO_BIT;
+
+	/* Perform SPI transfer */
+	if (max3107_rw(s, (u8 *)buf, NULL, 22))
+		dev_err(&s->spi->dev, "SPI transfer for init failed\n");
+
+	/* 10. Clear IRQ status register by reading it */
+	buf[0] = MAX3107_IRQSTS_REG;
+
+	/* 11. Configure interrupt enable register */
+	/* Enable LSR interrupt */
+	s->irqen_reg = MAX3107_IRQ_LSR_BIT;
+	/* Enable RX FIFO interrupt */
+	s->irqen_reg |= MAX3107_IRQ_RXFIFO_BIT;
+	buf[1] = (MAX3107_WRITE_BIT | MAX3107_IRQEN_REG)
+		| s->irqen_reg;
+
+	/* 12. Clear FIFO reset that was set in step 6 */
+	buf[2] = (MAX3107_WRITE_BIT | MAX3107_MODE2_REG);
+	if (s->loopback) {
+		/* Keep loopback enabled */
+		buf[2] |= MAX3107_MODE2_LOOPBACK_BIT;
+	}
+
+	/* Perform SPI transfer */
+	if (max3107_rw(s, (u8 *)buf, (u8 *)buf, 6))
+		dev_err(&s->spi->dev, "SPI transfer for init failed\n");
+
+}
+
+/* IRQ handler */
+static irqreturn_t max3107_irq(int irqno, void *dev_id)
+{
+	struct max3107_port *s = dev_id;
+
+	if (irqno != s->spi->irq) {
+		/* Unexpected IRQ */
+		return IRQ_NONE;
+	}
+
+	/* Indicate irq */
+	s->handle_irq = 1;
+
+	/* Trigger work thread */
+	max3107_dowork(s);
+
+	return IRQ_HANDLED;
+}
+
+/* HW suspension function
+ *
+ * Currently autosleep is used to decrease current consumption, alternative
+ * approach would be to set the chip to reset mode if UART is not being
+ * used but that would mess the GPIOs
+ *
+ */
+void max3107_hw_susp(struct max3107_port *s, int suspend)
+{
+	pr_debug("enter, suspend %d\n", suspend);
+
+	if (suspend) {
+		/* Suspend requested,
+		 * enable autosleep to decrease current consumption
+		 */
+		s->suspended = 1;
+		max3107_set_sleep(s, MAX3107_ENABLE_AUTOSLEEP);
+	} else {
+		/* Resume requested,
+		 * disable autosleep
+		 */
+		s->suspended = 0;
+		max3107_set_sleep(s, MAX3107_DISABLE_AUTOSLEEP);
+	}
+}
+EXPORT_SYMBOL_GPL(max3107_hw_susp);
+
+/* Modem status IRQ enabling */
+static void max3107_enable_ms(struct uart_port *port)
+{
+	/* Modem status not supported */
+}
+
+/* Data send function */
+static void max3107_start_tx(struct uart_port *port)
+{
+	struct max3107_port *s = container_of(port, struct max3107_port, port);
+
+	/* Trigger work thread for sending data */
+	max3107_dowork(s);
+}
+
+/* Function for checking that there is no pending transfers */
+static unsigned int max3107_tx_empty(struct uart_port *port)
+{
+	struct max3107_port *s = container_of(port, struct max3107_port, port);
+
+	pr_debug("returning %d\n",
+		  (s->tx_fifo_empty && uart_circ_empty(&s->port.state->xmit)));
+	return s->tx_fifo_empty && uart_circ_empty(&s->port.state->xmit);
+}
+
+/* Function for stopping RX */
+static void max3107_stop_rx(struct uart_port *port)
+{
+	struct max3107_port *s = container_of(port, struct max3107_port, port);
+	unsigned long flags;
+
+	/* Set RX disabled in MODE 1 register */
+	spin_lock_irqsave(&s->data_lock, flags);
+	s->mode1_reg |= MAX3107_MODE1_RXDIS_BIT;
+	s->mode1_commit = 1;
+	spin_unlock_irqrestore(&s->data_lock, flags);
+	/* Set RX disabled */
+	s->rx_enabled = 0;
+	/* Trigger work thread for doing the actual configuration change */
+	max3107_dowork(s);
+}
+
+/* Function for returning control pin states */
+static unsigned int max3107_get_mctrl(struct uart_port *port)
+{
+	/* DCD and DSR are not wired and CTS/RTS is handled automatically
+	 * so just indicate DSR and CAR asserted
+	 */
+	return TIOCM_DSR | TIOCM_CAR;
+}
+
+/* Function for setting control pin states */
+static void max3107_set_mctrl(struct uart_port *port, unsigned int mctrl)
+{
+	/* DCD and DSR are not wired and CTS/RTS is hadnled automatically
+	 * so do nothing
+	 */
+}
+
+/* Function for configuring UART parameters */
+static void max3107_set_termios(struct uart_port *port,
+				struct ktermios *termios,
+				struct ktermios *old)
+{
+	struct max3107_port *s = container_of(port, struct max3107_port, port);
+	struct tty_struct *tty;
+	int baud;
+	u16 new_lcr = 0;
+	u32 new_brg = 0;
+	unsigned long flags;
+
+	if (!port->state)
+		return;
+
+	tty = port->state->port.tty;
+	if (!tty)
+		return;
+
+	/* Get new LCR register values */
+	/* Word size */
+	if ((termios->c_cflag & CSIZE) == CS7)
+		new_lcr |= MAX3107_LCR_WORD_LEN_7;
+	else
+		new_lcr |= MAX3107_LCR_WORD_LEN_8;
+
+	/* Parity */
+	if (termios->c_cflag & PARENB) {
+		new_lcr |= MAX3107_LCR_PARITY_BIT;
+		if (!(termios->c_cflag & PARODD))
+			new_lcr |= MAX3107_LCR_EVENPARITY_BIT;
+	}
+
+	/* Stop bits */
+	if (termios->c_cflag & CSTOPB) {
+		/* 2 stop bits */
+		new_lcr |= MAX3107_LCR_STOPLEN_BIT;
+	}
+
+	/* Mask termios capabilities we don't support */
+	termios->c_cflag &= ~CMSPAR;
+
+	/* Set status ignore mask */
+	s->port.ignore_status_mask = 0;
+	if (termios->c_iflag & IGNPAR)
+		s->port.ignore_status_mask |= MAX3107_ALL_ERRORS;
+
+	/* Set low latency to immediately handle pushed data */
+	s->port.state->port.tty->low_latency = 1;
+
+	/* Get new baud rate generator configuration */
+	baud = tty_get_baud_rate(tty);
+
+	spin_lock_irqsave(&s->data_lock, flags);
+	new_brg = get_new_brg(baud, s);
+	/* if can't find the corrent config, use previous */
+	if (!new_brg) {
+		baud = s->baud;
+		new_brg = s->brg_cfg;
+	}
+	spin_unlock_irqrestore(&s->data_lock, flags);
+	tty_termios_encode_baud_rate(termios, baud, baud);
+	s->baud = baud;
+
+	/* Update timeout according to new baud rate */
+	uart_update_timeout(port, termios->c_cflag, baud);
+
+	spin_lock_irqsave(&s->data_lock, flags);
+	if (s->lcr_reg != new_lcr) {
+		s->lcr_reg = new_lcr;
+		s->lcr_commit = 1;
+	}
+	if (s->brg_cfg != new_brg) {
+		s->brg_cfg = new_brg;
+		s->brg_commit = 1;
+	}
+	spin_unlock_irqrestore(&s->data_lock, flags);
+
+	/* Trigger work thread for doing the actual configuration change */
+	max3107_dowork(s);
+}
+
+/* Port shutdown function */
+static void max3107_shutdown(struct uart_port *port)
+{
+	struct max3107_port *s = container_of(port, struct max3107_port, port);
+
+	if (s->suspended && s->pdata->hw_suspend)
+		s->pdata->hw_suspend(s, 0);
+
+	/* Free the interrupt */
+	free_irq(s->spi->irq, s);
+
+	if (s->workqueue) {
+		/* Flush and destroy work queue */
+		flush_workqueue(s->workqueue);
+		destroy_workqueue(s->workqueue);
+		s->workqueue = NULL;
+	}
+
+	/* Suspend HW */
+	if (s->pdata->hw_suspend)
+		s->pdata->hw_suspend(s, 1);
+}
+
+/* Port startup function */
+static int max3107_startup(struct uart_port *port)
+{
+	struct max3107_port *s = container_of(port, struct max3107_port, port);
+
+	/* Initialize work queue */
+	s->workqueue = create_freezable_workqueue("max3107");
+	if (!s->workqueue) {
+		dev_err(&s->spi->dev, "Workqueue creation failed\n");
+		return -EBUSY;
+	}
+	INIT_WORK(&s->work, max3107_work);
+
+	/* Setup IRQ */
+	if (request_irq(s->spi->irq, max3107_irq, IRQF_TRIGGER_FALLING,
+			"max3107", s)) {
+		dev_err(&s->spi->dev, "IRQ reguest failed\n");
+		destroy_workqueue(s->workqueue);
+		s->workqueue = NULL;
+		return -EBUSY;
+	}
+
+	/* Resume HW */
+	if (s->pdata->hw_suspend)
+		s->pdata->hw_suspend(s, 0);
+
+	/* Init registers */
+	max3107_register_init(s);
+
+	return 0;
+}
+
+/* Port type function */
+static const char *max3107_type(struct uart_port *port)
+{
+	struct max3107_port *s = container_of(port, struct max3107_port, port);
+	return s->spi->modalias;
+}
+
+/* Port release function */
+static void max3107_release_port(struct uart_port *port)
+{
+	/* Do nothing */
+}
+
+/* Port request function */
+static int max3107_request_port(struct uart_port *port)
+{
+	/* Do nothing */
+	return 0;
+}
+
+/* Port config function */
+static void max3107_config_port(struct uart_port *port, int flags)
+{
+	struct max3107_port *s = container_of(port, struct max3107_port, port);
+	s->port.type = PORT_MAX3107;
+}
+
+/* Port verify function */
+static int max3107_verify_port(struct uart_port *port,
+				struct serial_struct *ser)
+{
+	if (ser->type == PORT_UNKNOWN || ser->type == PORT_MAX3107)
+		return 0;
+
+	return -EINVAL;
+}
+
+/* Port stop TX function */
+static void max3107_stop_tx(struct uart_port *port)
+{
+	/* Do nothing */
+}
+
+/* Port break control function */
+static void max3107_break_ctl(struct uart_port *port, int break_state)
+{
+	/* We don't support break control, do nothing */
+}
+
+
+/* Port functions */
+static struct uart_ops max3107_ops = {
+	.tx_empty       = max3107_tx_empty,
+	.set_mctrl      = max3107_set_mctrl,
+	.get_mctrl      = max3107_get_mctrl,
+	.stop_tx        = max3107_stop_tx,
+	.start_tx       = max3107_start_tx,
+	.stop_rx        = max3107_stop_rx,
+	.enable_ms      = max3107_enable_ms,
+	.break_ctl      = max3107_break_ctl,
+	.startup        = max3107_startup,
+	.shutdown       = max3107_shutdown,
+	.set_termios    = max3107_set_termios,
+	.type           = max3107_type,
+	.release_port   = max3107_release_port,
+	.request_port   = max3107_request_port,
+	.config_port    = max3107_config_port,
+	.verify_port    = max3107_verify_port,
+};
+
+/* UART driver data */
+static struct uart_driver max3107_uart_driver = {
+	.owner          = THIS_MODULE,
+	.driver_name    = "ttyMAX",
+	.dev_name       = "ttyMAX",
+	.nr             = 1,
+};
+
+static int driver_registered = 0;
+
+
+
+/* 'Generic' platform data */
+static struct max3107_plat generic_plat_data = {
+	.loopback               = 0,
+	.ext_clk                = 1,
+	.hw_suspend		= max3107_hw_susp,
+	.polled_mode            = 0,
+	.poll_time              = 0,
+};
+
+
+/*******************************************************************/
+
+/**
+ *	max3107_probe		-	SPI bus probe entry point
+ *	@spi: the spi device
+ *
+ *	SPI wants us to probe this device and if appropriate claim it.
+ *	Perform any platform specific requirements and then initialise
+ *	the device.
+ */
+
+int max3107_probe(struct spi_device *spi, struct max3107_plat *pdata)
+{
+	struct max3107_port *s;
+	u16 buf[2];	/* Buffer for SPI transfers */
+	int retval;
+
+	pr_info("enter max3107 probe\n");
+
+	/* Allocate port structure */
+	s = kzalloc(sizeof(*s), GFP_KERNEL);
+	if (!s) {
+		pr_err("Allocating port structure failed\n");
+		return -ENOMEM;
+	}
+
+	s->pdata = pdata;
+
+	/* SPI Rx buffer
+	 * +2 for RX FIFO interrupt
+	 * disabling and RX level query
+	 */
+	s->rxbuf = kzalloc(sizeof(u16) * (MAX3107_RX_FIFO_SIZE+2), GFP_KERNEL);
+	if (!s->rxbuf) {
+		pr_err("Allocating RX buffer failed\n");
+		retval = -ENOMEM;
+		goto err_free4;
+	}
+	s->rxstr = kzalloc(sizeof(u8) * MAX3107_RX_FIFO_SIZE, GFP_KERNEL);
+	if (!s->rxstr) {
+		pr_err("Allocating RX buffer failed\n");
+		retval = -ENOMEM;
+		goto err_free3;
+	}
+	/* SPI Tx buffer
+	 * SPI transfer buffer
+	 * +3 for TX FIFO empty
+	 * interrupt disabling and
+	 * enabling and TX enabling
+	 */
+	s->txbuf = kzalloc(sizeof(u16) * MAX3107_TX_FIFO_SIZE + 3, GFP_KERNEL);
+	if (!s->txbuf) {
+		pr_err("Allocating TX buffer failed\n");
+		retval = -ENOMEM;
+		goto err_free2;
+	}
+	/* Initialize shared data lock */
+	spin_lock_init(&s->data_lock);
+
+	/* SPI intializations */
+	dev_set_drvdata(&spi->dev, s);
+	spi->mode = SPI_MODE_0;
+	spi->dev.platform_data = pdata;
+	spi->bits_per_word = 16;
+	s->ext_clk = pdata->ext_clk;
+	s->loopback = pdata->loopback;
+	spi_setup(spi);
+	s->spi = spi;
+
+	/* Check REV ID to ensure we are talking to what we expect */
+	buf[0] = MAX3107_REVID_REG;
+	if (max3107_rw(s, (u8 *)buf, (u8 *)buf, 2)) {
+		dev_err(&s->spi->dev, "SPI transfer for REVID read failed\n");
+		retval = -EIO;
+		goto err_free1;
+	}
+	if ((buf[0] & MAX3107_SPI_RX_DATA_MASK) != MAX3107_REVID1 &&
+		(buf[0] & MAX3107_SPI_RX_DATA_MASK) != MAX3107_REVID2) {
+		dev_err(&s->spi->dev, "REVID %x does not match\n",
+				(buf[0] & MAX3107_SPI_RX_DATA_MASK));
+		retval = -ENODEV;
+		goto err_free1;
+	}
+
+	/* Disable all interrupts */
+	buf[0] = (MAX3107_WRITE_BIT | MAX3107_IRQEN_REG | 0x0000);
+	buf[0] |= 0x0000;
+
+	/* Configure clock source */
+	buf[1] = (MAX3107_WRITE_BIT | MAX3107_CLKSRC_REG);
+	if (s->ext_clk) {
+		/* External clock */
+		buf[1] |= MAX3107_CLKSRC_EXTCLK_BIT;
+	}
+
+	/* PLL bypass ON */
+	buf[1] |= MAX3107_CLKSRC_PLLBYP_BIT;
+
+	/* Perform SPI transfer */
+	if (max3107_rw(s, (u8 *)buf, NULL, 4)) {
+		dev_err(&s->spi->dev, "SPI transfer for init failed\n");
+		retval = -EIO;
+		goto err_free1;
+	}
+
+	/* Register UART driver */
+	if (!driver_registered) {
+		retval = uart_register_driver(&max3107_uart_driver);
+		if (retval) {
+			dev_err(&s->spi->dev, "Registering UART driver failed\n");
+			goto err_free1;
+		}
+		driver_registered = 1;
+	}
+
+	/* Initialize UART port data */
+	s->port.fifosize = 128;
+	s->port.ops = &max3107_ops;
+	s->port.line = 0;
+	s->port.dev = &spi->dev;
+	s->port.uartclk = 9600;
+	s->port.flags = UPF_SKIP_TEST | UPF_BOOT_AUTOCONF;
+	s->port.irq = s->spi->irq;
+	s->port.type = PORT_MAX3107;
+
+	/* Add UART port */
+	retval = uart_add_one_port(&max3107_uart_driver, &s->port);
+	if (retval < 0) {
+		dev_err(&s->spi->dev, "Adding UART port failed\n");
+		goto err_free1;
+	}
+
+	if (pdata->configure) {
+		retval = pdata->configure(s);
+		if (retval < 0)
+			goto err_free1;
+	}
+
+	/* Go to suspend mode */
+	if (pdata->hw_suspend)
+		pdata->hw_suspend(s, 1);
+
+	return 0;
+
+err_free1:
+	kfree(s->txbuf);
+err_free2:
+	kfree(s->rxstr);
+err_free3:
+	kfree(s->rxbuf);
+err_free4:
+	kfree(s);
+	return retval;
+}
+EXPORT_SYMBOL_GPL(max3107_probe);
+
+/* Driver remove function */
+int max3107_remove(struct spi_device *spi)
+{
+	struct max3107_port *s = dev_get_drvdata(&spi->dev);
+
+	pr_info("enter max3107 remove\n");
+
+	/* Remove port */
+	if (uart_remove_one_port(&max3107_uart_driver, &s->port))
+		dev_warn(&s->spi->dev, "Removing UART port failed\n");
+
+
+	/* Free TxRx buffer */
+	kfree(s->rxbuf);
+	kfree(s->rxstr);
+	kfree(s->txbuf);
+
+	/* Free port structure */
+	kfree(s);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(max3107_remove);
+
+/* Driver suspend function */
+int max3107_suspend(struct spi_device *spi, pm_message_t state)
+{
+#ifdef CONFIG_PM
+	struct max3107_port *s = dev_get_drvdata(&spi->dev);
+
+	pr_debug("enter suspend\n");
+
+	/* Suspend UART port */
+	uart_suspend_port(&max3107_uart_driver, &s->port);
+
+	/* Go to suspend mode */
+	if (s->pdata->hw_suspend)
+		s->pdata->hw_suspend(s, 1);
+#endif	/* CONFIG_PM */
+	return 0;
+}
+EXPORT_SYMBOL_GPL(max3107_suspend);
+
+/* Driver resume function */
+int max3107_resume(struct spi_device *spi)
+{
+#ifdef CONFIG_PM
+	struct max3107_port *s = dev_get_drvdata(&spi->dev);
+
+	pr_debug("enter resume\n");
+
+	/* Resume from suspend */
+	if (s->pdata->hw_suspend)
+		s->pdata->hw_suspend(s, 0);
+
+	/* Resume UART port */
+	uart_resume_port(&max3107_uart_driver, &s->port);
+#endif	/* CONFIG_PM */
+	return 0;
+}
+EXPORT_SYMBOL_GPL(max3107_resume);
+
+static int max3107_probe_generic(struct spi_device *spi)
+{
+	return max3107_probe(spi, &generic_plat_data);
+}
+
+/* Spi driver data */
+static struct spi_driver max3107_driver = {
+	.driver = {
+		.name		= "max3107",
+		.owner		= THIS_MODULE,
+	},
+	.probe		= max3107_probe_generic,
+	.remove		= __devexit_p(max3107_remove),
+	.suspend	= max3107_suspend,
+	.resume		= max3107_resume,
+};
+
+/* Driver init function */
+static int __init max3107_init(void)
+{
+	pr_info("enter max3107 init\n");
+	return spi_register_driver(&max3107_driver);
+}
+
+/* Driver exit function */
+static void __exit max3107_exit(void)
+{
+	pr_info("enter max3107 exit\n");
+	/* Unregister UART driver */
+	if (driver_registered)
+		uart_unregister_driver(&max3107_uart_driver);
+	spi_unregister_driver(&max3107_driver);
+}
+
+module_init(max3107_init);
+module_exit(max3107_exit);
+
+MODULE_DESCRIPTION("MAX3107 driver");
+MODULE_AUTHOR("Aavamobile");
+MODULE_ALIAS("spi:max3107");
+MODULE_LICENSE("GPL v2");
diff --git a/ap/os/linux/linux-3.4.x/drivers/tty/serial/max3107.h b/ap/os/linux/linux-3.4.x/drivers/tty/serial/max3107.h
new file mode 100644
index 0000000..8415fc7
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/tty/serial/max3107.h
@@ -0,0 +1,441 @@
+/*
+ * max3107.h - spi uart protocol driver header for Maxim 3107
+ *
+ * Copyright (C) Aavamobile 2009
+ * Based on serial_max3100.h by Christian Pellegrin
+ *
+ * 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.
+ */
+
+#ifndef _MAX3107_H
+#define _MAX3107_H
+
+/* Serial error status definitions */
+#define MAX3107_PARITY_ERROR	1
+#define MAX3107_FRAME_ERROR	2
+#define MAX3107_OVERRUN_ERROR	4
+#define MAX3107_ALL_ERRORS	(MAX3107_PARITY_ERROR | \
+				 MAX3107_FRAME_ERROR | \
+				 MAX3107_OVERRUN_ERROR)
+
+/* GPIO definitions */
+#define MAX3107_GPIO_BASE	88
+#define MAX3107_GPIO_COUNT	4
+
+
+/* GPIO connected to chip's reset pin */
+#define MAX3107_RESET_GPIO	87
+
+
+/* Chip reset delay */
+#define MAX3107_RESET_DELAY	10
+
+/* Chip wakeup delay */
+#define MAX3107_WAKEUP_DELAY	50
+
+
+/* Sleep mode definitions */
+#define MAX3107_DISABLE_FORCED_SLEEP	0
+#define MAX3107_ENABLE_FORCED_SLEEP	1
+#define MAX3107_DISABLE_AUTOSLEEP	2
+#define MAX3107_ENABLE_AUTOSLEEP	3
+
+
+/* Definitions for register access with SPI transfers
+ *
+ * SPI transfer format:
+ *
+ * Master to slave bits xzzzzzzzyyyyyyyy
+ * Slave to master bits aaaaaaaabbbbbbbb
+ *
+ * where:
+ * x = 0 for reads, 1 for writes
+ * z = register address
+ * y = new register value if write, 0 if read
+ * a = unspecified
+ * b = register value if read, unspecified if write
+ */
+
+/* SPI speed */
+#define MAX3107_SPI_SPEED	(3125000 * 2)
+
+/* Write bit */
+#define MAX3107_WRITE_BIT	(1 << 15)
+
+/* SPI TX data mask */
+#define MAX3107_SPI_RX_DATA_MASK	(0x00ff)
+
+/* SPI RX data mask */
+#define MAX3107_SPI_TX_DATA_MASK	(0x00ff)
+
+/* Register access masks */
+#define MAX3107_RHR_REG			(0x0000) /* RX FIFO */
+#define MAX3107_THR_REG			(0x0000) /* TX FIFO */
+#define MAX3107_IRQEN_REG		(0x0100) /* IRQ enable */
+#define MAX3107_IRQSTS_REG		(0x0200) /* IRQ status */
+#define MAX3107_LSR_IRQEN_REG		(0x0300) /* LSR IRQ enable */
+#define MAX3107_LSR_IRQSTS_REG		(0x0400) /* LSR IRQ status */
+#define MAX3107_SPCHR_IRQEN_REG		(0x0500) /* Special char IRQ enable */
+#define MAX3107_SPCHR_IRQSTS_REG	(0x0600) /* Special char IRQ status */
+#define MAX3107_STS_IRQEN_REG		(0x0700) /* Status IRQ enable */
+#define MAX3107_STS_IRQSTS_REG		(0x0800) /* Status IRQ status */
+#define MAX3107_MODE1_REG		(0x0900) /* MODE1 */
+#define MAX3107_MODE2_REG		(0x0a00) /* MODE2 */
+#define MAX3107_LCR_REG			(0x0b00) /* LCR */
+#define MAX3107_RXTO_REG		(0x0c00) /* RX timeout */
+#define MAX3107_HDPIXDELAY_REG		(0x0d00) /* Auto transceiver delays */
+#define MAX3107_IRDA_REG		(0x0e00) /* IRDA settings */
+#define MAX3107_FLOWLVL_REG		(0x0f00) /* Flow control levels */
+#define MAX3107_FIFOTRIGLVL_REG		(0x1000) /* FIFO IRQ trigger levels */
+#define MAX3107_TXFIFOLVL_REG		(0x1100) /* TX FIFO level */
+#define MAX3107_RXFIFOLVL_REG		(0x1200) /* RX FIFO level */
+#define MAX3107_FLOWCTRL_REG		(0x1300) /* Flow control */
+#define MAX3107_XON1_REG		(0x1400) /* XON1 character */
+#define MAX3107_XON2_REG		(0x1500) /* XON2 character */
+#define MAX3107_XOFF1_REG		(0x1600) /* XOFF1 character */
+#define MAX3107_XOFF2_REG		(0x1700) /* XOFF2 character */
+#define MAX3107_GPIOCFG_REG		(0x1800) /* GPIO config */
+#define MAX3107_GPIODATA_REG		(0x1900) /* GPIO data */
+#define MAX3107_PLLCFG_REG		(0x1a00) /* PLL config */
+#define MAX3107_BRGCFG_REG		(0x1b00) /* Baud rate generator conf */
+#define MAX3107_BRGDIVLSB_REG		(0x1c00) /* Baud rate divisor LSB */
+#define MAX3107_BRGDIVMSB_REG		(0x1d00) /* Baud rate divisor MSB */
+#define MAX3107_CLKSRC_REG		(0x1e00) /* Clock source */
+#define MAX3107_REVID_REG		(0x1f00) /* Revision identification */
+
+/* IRQ register bits */
+#define MAX3107_IRQ_LSR_BIT	(1 << 0) /* LSR interrupt */
+#define MAX3107_IRQ_SPCHR_BIT	(1 << 1) /* Special char interrupt */
+#define MAX3107_IRQ_STS_BIT	(1 << 2) /* Status interrupt */
+#define MAX3107_IRQ_RXFIFO_BIT	(1 << 3) /* RX FIFO interrupt */
+#define MAX3107_IRQ_TXFIFO_BIT	(1 << 4) /* TX FIFO interrupt */
+#define MAX3107_IRQ_TXEMPTY_BIT	(1 << 5) /* TX FIFO empty interrupt */
+#define MAX3107_IRQ_RXEMPTY_BIT	(1 << 6) /* RX FIFO empty interrupt */
+#define MAX3107_IRQ_CTS_BIT	(1 << 7) /* CTS interrupt */
+
+/* LSR register bits */
+#define MAX3107_LSR_RXTO_BIT	(1 << 0) /* RX timeout */
+#define MAX3107_LSR_RXOVR_BIT	(1 << 1) /* RX overrun */
+#define MAX3107_LSR_RXPAR_BIT	(1 << 2) /* RX parity error */
+#define MAX3107_LSR_FRERR_BIT	(1 << 3) /* Frame error */
+#define MAX3107_LSR_RXBRK_BIT	(1 << 4) /* RX break */
+#define MAX3107_LSR_RXNOISE_BIT	(1 << 5) /* RX noise */
+#define MAX3107_LSR_UNDEF6_BIT	(1 << 6) /* Undefined/not used */
+#define MAX3107_LSR_CTS_BIT	(1 << 7) /* CTS pin state */
+
+/* Special character register bits */
+#define MAX3107_SPCHR_XON1_BIT		(1 << 0) /* XON1 character */
+#define MAX3107_SPCHR_XON2_BIT		(1 << 1) /* XON2 character */
+#define MAX3107_SPCHR_XOFF1_BIT		(1 << 2) /* XOFF1 character */
+#define MAX3107_SPCHR_XOFF2_BIT		(1 << 3) /* XOFF2 character */
+#define MAX3107_SPCHR_BREAK_BIT		(1 << 4) /* RX break */
+#define MAX3107_SPCHR_MULTIDROP_BIT	(1 << 5) /* 9-bit multidrop addr char */
+#define MAX3107_SPCHR_UNDEF6_BIT	(1 << 6) /* Undefined/not used */
+#define MAX3107_SPCHR_UNDEF7_BIT	(1 << 7) /* Undefined/not used */
+
+/* Status register bits */
+#define MAX3107_STS_GPIO0_BIT		(1 << 0) /* GPIO 0 interrupt */
+#define MAX3107_STS_GPIO1_BIT		(1 << 1) /* GPIO 1 interrupt */
+#define MAX3107_STS_GPIO2_BIT		(1 << 2) /* GPIO 2 interrupt */
+#define MAX3107_STS_GPIO3_BIT		(1 << 3) /* GPIO 3 interrupt */
+#define MAX3107_STS_UNDEF4_BIT		(1 << 4) /* Undefined/not used */
+#define MAX3107_STS_CLKREADY_BIT	(1 << 5) /* Clock ready */
+#define MAX3107_STS_SLEEP_BIT		(1 << 6) /* Sleep interrupt */
+#define MAX3107_STS_UNDEF7_BIT		(1 << 7) /* Undefined/not used */
+
+/* MODE1 register bits */
+#define MAX3107_MODE1_RXDIS_BIT		(1 << 0) /* RX disable */
+#define MAX3107_MODE1_TXDIS_BIT		(1 << 1) /* TX disable */
+#define MAX3107_MODE1_TXHIZ_BIT		(1 << 2) /* TX pin three-state */
+#define MAX3107_MODE1_RTSHIZ_BIT	(1 << 3) /* RTS pin three-state */
+#define MAX3107_MODE1_TRNSCVCTRL_BIT	(1 << 4) /* Transceiver ctrl enable */
+#define MAX3107_MODE1_FORCESLEEP_BIT	(1 << 5) /* Force sleep mode */
+#define MAX3107_MODE1_AUTOSLEEP_BIT	(1 << 6) /* Auto sleep enable */
+#define MAX3107_MODE1_IRQSEL_BIT	(1 << 7) /* IRQ pin enable */
+
+/* MODE2 register bits */
+#define MAX3107_MODE2_RST_BIT		(1 << 0) /* Chip reset */
+#define MAX3107_MODE2_FIFORST_BIT	(1 << 1) /* FIFO reset */
+#define MAX3107_MODE2_RXTRIGINV_BIT	(1 << 2) /* RX FIFO INT invert */
+#define MAX3107_MODE2_RXEMPTINV_BIT	(1 << 3) /* RX FIFO empty INT invert */
+#define MAX3107_MODE2_SPCHR_BIT		(1 << 4) /* Special chr detect enable */
+#define MAX3107_MODE2_LOOPBACK_BIT	(1 << 5) /* Internal loopback enable */
+#define MAX3107_MODE2_MULTIDROP_BIT	(1 << 6) /* 9-bit multidrop enable */
+#define MAX3107_MODE2_ECHOSUPR_BIT	(1 << 7) /* ECHO suppression enable */
+
+/* LCR register bits */
+#define MAX3107_LCR_LENGTH0_BIT		(1 << 0) /* Word length bit 0 */
+#define MAX3107_LCR_LENGTH1_BIT		(1 << 1) /* Word length bit 1
+						  *
+						  * Word length bits table:
+						  * 00 -> 5 bit words
+						  * 01 -> 6 bit words
+						  * 10 -> 7 bit words
+						  * 11 -> 8 bit words
+						  */
+#define MAX3107_LCR_STOPLEN_BIT		(1 << 2) /* STOP length bit
+						  *
+						  * STOP length bit table:
+						  * 0 -> 1 stop bit
+						  * 1 -> 1-1.5 stop bits if
+						  *      word length is 5,
+						  *      2 stop bits otherwise
+						  */
+#define MAX3107_LCR_PARITY_BIT		(1 << 3) /* Parity bit enable */
+#define MAX3107_LCR_EVENPARITY_BIT	(1 << 4) /* Even parity bit enable */
+#define MAX3107_LCR_FORCEPARITY_BIT	(1 << 5) /* 9-bit multidrop parity */
+#define MAX3107_LCR_TXBREAK_BIT		(1 << 6) /* TX break enable */
+#define MAX3107_LCR_RTS_BIT		(1 << 7) /* RTS pin control */
+#define MAX3107_LCR_WORD_LEN_5		(0x0000)
+#define MAX3107_LCR_WORD_LEN_6		(0x0001)
+#define MAX3107_LCR_WORD_LEN_7		(0x0002)
+#define MAX3107_LCR_WORD_LEN_8		(0x0003)
+
+
+/* IRDA register bits */
+#define MAX3107_IRDA_IRDAEN_BIT		(1 << 0) /* IRDA mode enable */
+#define MAX3107_IRDA_SIR_BIT		(1 << 1) /* SIR mode enable */
+#define MAX3107_IRDA_SHORTIR_BIT	(1 << 2) /* Short SIR mode enable */
+#define MAX3107_IRDA_MIR_BIT		(1 << 3) /* MIR mode enable */
+#define MAX3107_IRDA_RXINV_BIT		(1 << 4) /* RX logic inversion enable */
+#define MAX3107_IRDA_TXINV_BIT		(1 << 5) /* TX logic inversion enable */
+#define MAX3107_IRDA_UNDEF6_BIT		(1 << 6) /* Undefined/not used */
+#define MAX3107_IRDA_UNDEF7_BIT		(1 << 7) /* Undefined/not used */
+
+/* Flow control trigger level register masks */
+#define MAX3107_FLOWLVL_HALT_MASK	(0x000f) /* Flow control halt level */
+#define MAX3107_FLOWLVL_RES_MASK	(0x00f0) /* Flow control resume level */
+#define MAX3107_FLOWLVL_HALT(words)	((words/8) & 0x000f)
+#define MAX3107_FLOWLVL_RES(words)	(((words/8) & 0x000f) << 4)
+
+/* FIFO interrupt trigger level register masks */
+#define MAX3107_FIFOTRIGLVL_TX_MASK	(0x000f) /* TX FIFO trigger level */
+#define MAX3107_FIFOTRIGLVL_RX_MASK	(0x00f0) /* RX FIFO trigger level */
+#define MAX3107_FIFOTRIGLVL_TX(words)	((words/8) & 0x000f)
+#define MAX3107_FIFOTRIGLVL_RX(words)	(((words/8) & 0x000f) << 4)
+
+/* Flow control register bits */
+#define MAX3107_FLOWCTRL_AUTORTS_BIT	(1 << 0) /* Auto RTS flow ctrl enable */
+#define MAX3107_FLOWCTRL_AUTOCTS_BIT	(1 << 1) /* Auto CTS flow ctrl enable */
+#define MAX3107_FLOWCTRL_GPIADDR_BIT	(1 << 2) /* Enables that GPIO inputs
+						  * are used in conjunction with
+						  * XOFF2 for definition of
+						  * special character */
+#define MAX3107_FLOWCTRL_SWFLOWEN_BIT	(1 << 3) /* Auto SW flow ctrl enable */
+#define MAX3107_FLOWCTRL_SWFLOW0_BIT	(1 << 4) /* SWFLOW bit 0 */
+#define MAX3107_FLOWCTRL_SWFLOW1_BIT	(1 << 5) /* SWFLOW bit 1
+						  *
+						  * SWFLOW bits 1 & 0 table:
+						  * 00 -> no transmitter flow
+						  *       control
+						  * 01 -> receiver compares
+						  *       XON2 and XOFF2
+						  *       and controls
+						  *       transmitter
+						  * 10 -> receiver compares
+						  *       XON1 and XOFF1
+						  *       and controls
+						  *       transmitter
+						  * 11 -> receiver compares
+						  *       XON1, XON2, XOFF1 and
+						  *       XOFF2 and controls
+						  *       transmitter
+						  */
+#define MAX3107_FLOWCTRL_SWFLOW2_BIT	(1 << 6) /* SWFLOW bit 2 */
+#define MAX3107_FLOWCTRL_SWFLOW3_BIT	(1 << 7) /* SWFLOW bit 3
+						  *
+						  * SWFLOW bits 3 & 2 table:
+						  * 00 -> no received flow
+						  *       control
+						  * 01 -> transmitter generates
+						  *       XON2 and XOFF2
+						  * 10 -> transmitter generates
+						  *       XON1 and XOFF1
+						  * 11 -> transmitter generates
+						  *       XON1, XON2, XOFF1 and
+						  *       XOFF2
+						  */
+
+/* GPIO configuration register bits */
+#define MAX3107_GPIOCFG_GP0OUT_BIT	(1 << 0) /* GPIO 0 output enable */
+#define MAX3107_GPIOCFG_GP1OUT_BIT	(1 << 1) /* GPIO 1 output enable */
+#define MAX3107_GPIOCFG_GP2OUT_BIT	(1 << 2) /* GPIO 2 output enable */
+#define MAX3107_GPIOCFG_GP3OUT_BIT	(1 << 3) /* GPIO 3 output enable */
+#define MAX3107_GPIOCFG_GP0OD_BIT	(1 << 4) /* GPIO 0 open-drain enable */
+#define MAX3107_GPIOCFG_GP1OD_BIT	(1 << 5) /* GPIO 1 open-drain enable */
+#define MAX3107_GPIOCFG_GP2OD_BIT	(1 << 6) /* GPIO 2 open-drain enable */
+#define MAX3107_GPIOCFG_GP3OD_BIT	(1 << 7) /* GPIO 3 open-drain enable */
+
+/* GPIO DATA register bits */
+#define MAX3107_GPIODATA_GP0OUT_BIT	(1 << 0) /* GPIO 0 output value */
+#define MAX3107_GPIODATA_GP1OUT_BIT	(1 << 1) /* GPIO 1 output value */
+#define MAX3107_GPIODATA_GP2OUT_BIT	(1 << 2) /* GPIO 2 output value */
+#define MAX3107_GPIODATA_GP3OUT_BIT	(1 << 3) /* GPIO 3 output value */
+#define MAX3107_GPIODATA_GP0IN_BIT	(1 << 4) /* GPIO 0 input value */
+#define MAX3107_GPIODATA_GP1IN_BIT	(1 << 5) /* GPIO 1 input value */
+#define MAX3107_GPIODATA_GP2IN_BIT	(1 << 6) /* GPIO 2 input value */
+#define MAX3107_GPIODATA_GP3IN_BIT	(1 << 7) /* GPIO 3 input value */
+
+/* PLL configuration register masks */
+#define MAX3107_PLLCFG_PREDIV_MASK	(0x003f) /* PLL predivision value */
+#define MAX3107_PLLCFG_PLLFACTOR_MASK	(0x00c0) /* PLL multiplication factor */
+
+/* Baud rate generator configuration register masks and bits */
+#define MAX3107_BRGCFG_FRACT_MASK	(0x000f) /* Fractional portion of
+						  * Baud rate generator divisor
+						  */
+#define MAX3107_BRGCFG_2XMODE_BIT	(1 << 4) /* Double baud rate */
+#define MAX3107_BRGCFG_4XMODE_BIT	(1 << 5) /* Quadruple baud rate */
+#define MAX3107_BRGCFG_UNDEF6_BIT	(1 << 6) /* Undefined/not used */
+#define MAX3107_BRGCFG_UNDEF7_BIT	(1 << 7) /* Undefined/not used */
+
+/* Clock source register bits */
+#define MAX3107_CLKSRC_INTOSC_BIT	(1 << 0) /* Internal osc enable */
+#define MAX3107_CLKSRC_CRYST_BIT	(1 << 1) /* Crystal osc enable */
+#define MAX3107_CLKSRC_PLL_BIT		(1 << 2) /* PLL enable */
+#define MAX3107_CLKSRC_PLLBYP_BIT	(1 << 3) /* PLL bypass */
+#define MAX3107_CLKSRC_EXTCLK_BIT	(1 << 4) /* External clock enable */
+#define MAX3107_CLKSRC_UNDEF5_BIT	(1 << 5) /* Undefined/not used */
+#define MAX3107_CLKSRC_UNDEF6_BIT	(1 << 6) /* Undefined/not used */
+#define MAX3107_CLKSRC_CLK2RTS_BIT	(1 << 7) /* Baud clk to RTS pin */
+
+
+/* HW definitions */
+#define MAX3107_RX_FIFO_SIZE	128
+#define MAX3107_TX_FIFO_SIZE	128
+#define MAX3107_REVID1		0x00a0
+#define MAX3107_REVID2		0x00a1
+
+
+/* Baud rate generator configuration values for external clock 13MHz */
+#define MAX3107_BRG13_B300	(0x0A9400 | 0x05)
+#define MAX3107_BRG13_B600	(0x054A00 | 0x03)
+#define MAX3107_BRG13_B1200	(0x02A500 | 0x01)
+#define MAX3107_BRG13_B2400	(0x015200 | 0x09)
+#define MAX3107_BRG13_B4800	(0x00A900 | 0x04)
+#define MAX3107_BRG13_B9600	(0x005400 | 0x0A)
+#define MAX3107_BRG13_B19200	(0x002A00 | 0x05)
+#define MAX3107_BRG13_B38400	(0x001500 | 0x03)
+#define MAX3107_BRG13_B57600	(0x000E00 | 0x02)
+#define MAX3107_BRG13_B115200	(0x000700 | 0x01)
+#define MAX3107_BRG13_B230400	(0x000300 | 0x08)
+#define MAX3107_BRG13_B460800	(0x000100 | 0x0c)
+#define MAX3107_BRG13_B921600	(0x000100 | 0x1c)
+
+/* Baud rate generator configuration values for external clock 26MHz */
+#define MAX3107_BRG26_B300	(0x152800 | 0x0A)
+#define MAX3107_BRG26_B600	(0x0A9400 | 0x05)
+#define MAX3107_BRG26_B1200	(0x054A00 | 0x03)
+#define MAX3107_BRG26_B2400	(0x02A500 | 0x01)
+#define MAX3107_BRG26_B4800	(0x015200 | 0x09)
+#define MAX3107_BRG26_B9600	(0x00A900 | 0x04)
+#define MAX3107_BRG26_B19200	(0x005400 | 0x0A)
+#define MAX3107_BRG26_B38400	(0x002A00 | 0x05)
+#define MAX3107_BRG26_B57600	(0x001C00 | 0x03)
+#define MAX3107_BRG26_B115200	(0x000E00 | 0x02)
+#define MAX3107_BRG26_B230400	(0x000700 | 0x01)
+#define MAX3107_BRG26_B460800	(0x000300 | 0x08)
+#define MAX3107_BRG26_B921600	(0x000100 | 0x0C)
+
+/* Baud rate generator configuration values for internal clock */
+#define MAX3107_BRG13_IB300	(0x008000 | 0x00)
+#define MAX3107_BRG13_IB600	(0x004000 | 0x00)
+#define MAX3107_BRG13_IB1200	(0x002000 | 0x00)
+#define MAX3107_BRG13_IB2400	(0x001000 | 0x00)
+#define MAX3107_BRG13_IB4800	(0x000800 | 0x00)
+#define MAX3107_BRG13_IB9600	(0x000400 | 0x00)
+#define MAX3107_BRG13_IB19200	(0x000200 | 0x00)
+#define MAX3107_BRG13_IB38400	(0x000100 | 0x00)
+#define MAX3107_BRG13_IB57600	(0x000000 | 0x0B)
+#define MAX3107_BRG13_IB115200	(0x000000 | 0x05)
+#define MAX3107_BRG13_IB230400	(0x000000 | 0x03)
+#define MAX3107_BRG13_IB460800	(0x000000 | 0x00)
+#define MAX3107_BRG13_IB921600	(0x000000 | 0x00)
+
+
+struct baud_table {
+	int baud;
+	u32 new_brg;
+};
+
+struct max3107_port {
+	/* UART port structure */
+	struct uart_port port;
+
+	/* SPI device structure */
+	struct spi_device *spi;
+
+#if defined(CONFIG_GPIOLIB)
+	/* GPIO chip structure */
+	struct gpio_chip chip;
+#endif
+
+	/* Workqueue that does all the magic */
+	struct workqueue_struct *workqueue;
+	struct work_struct work;
+
+	/* Lock for shared data */
+	spinlock_t data_lock;
+
+	/* Device configuration */
+	int ext_clk;		/* 1 if external clock used */
+	int loopback;		/* Current loopback mode state */
+	int baud;			/* Current baud rate */
+
+	/* State flags */
+	int suspended;		/* Indicates suspend mode */
+	int tx_fifo_empty;	/* Flag for TX FIFO state */
+	int rx_enabled;		/* Flag for receiver state */
+	int tx_enabled;		/* Flag for transmitter state */
+
+	u16 irqen_reg;		/* Current IRQ enable register value */
+	/* Shared data */
+	u16 mode1_reg;		/* Current mode1 register value*/
+	int mode1_commit;	/* Flag for setting new mode1 register value */
+	u16 lcr_reg;		/* Current LCR register value */
+	int lcr_commit;		/* Flag for setting new LCR register value */
+	u32 brg_cfg;		/* Current Baud rate generator config  */
+	int brg_commit;		/* Flag for setting new baud rate generator
+				 * config
+				 */
+	struct baud_table *baud_tbl;
+	int handle_irq;		/* Indicates that IRQ should be handled */
+
+	/* Rx buffer and str*/
+	u16 *rxbuf;
+	u8  *rxstr;
+	/* Tx buffer*/
+	u16 *txbuf;
+
+	struct max3107_plat *pdata;	/* Platform data */
+};
+
+/* Platform data structure */
+struct max3107_plat {
+	/* Loopback mode enable */
+	int loopback;
+	/* External clock enable */
+	int ext_clk;
+	/* Called during the register initialisation */
+	void (*init)(struct max3107_port *s);
+	/* Called when the port is found and configured */
+	int (*configure)(struct max3107_port *s);
+	/* HW suspend function */
+	void (*hw_suspend) (struct max3107_port *s, int suspend);
+	/* Polling mode enable */
+	int polled_mode;
+	/* Polling period if polling mode enabled */
+	int poll_time;
+};
+
+extern int max3107_rw(struct max3107_port *s, u8 *tx, u8 *rx, int len);
+extern void max3107_hw_susp(struct max3107_port *s, int suspend);
+extern int max3107_probe(struct spi_device *spi, struct max3107_plat *pdata);
+extern int max3107_remove(struct spi_device *spi);
+extern int max3107_suspend(struct spi_device *spi, pm_message_t state);
+extern int max3107_resume(struct spi_device *spi);
+
+#endif /* _LINUX_SERIAL_MAX3107_H */
diff --git a/ap/os/linux/linux-3.4.x/drivers/tty/serial/mcf.c b/ap/os/linux/linux-3.4.x/drivers/tty/serial/mcf.c
new file mode 100644
index 0000000..9afca09
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/tty/serial/mcf.c
@@ -0,0 +1,662 @@
+/****************************************************************************/
+
+/*
+ *	mcf.c -- Freescale ColdFire UART driver
+ *
+ *	(C) Copyright 2003-2007, Greg Ungerer <gerg@snapgear.com>
+ *
+ * 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.
+ */
+
+/****************************************************************************/
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/console.h>
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
+#include <linux/serial.h>
+#include <linux/serial_core.h>
+#include <linux/io.h>
+#include <asm/coldfire.h>
+#include <asm/mcfsim.h>
+#include <asm/mcfuart.h>
+#include <asm/nettel.h>
+
+/****************************************************************************/
+
+/*
+ *	Some boards implement the DTR/DCD lines using GPIO lines, most
+ *	don't. Dummy out the access macros for those that don't. Those
+ *	that do should define these macros somewhere in there board
+ *	specific inlude files.
+ */
+#if !defined(mcf_getppdcd)
+#define	mcf_getppdcd(p)		(1)
+#endif
+#if !defined(mcf_getppdtr)
+#define	mcf_getppdtr(p)		(1)
+#endif
+#if !defined(mcf_setppdtr)
+#define	mcf_setppdtr(p, v)	do { } while (0)
+#endif
+
+/****************************************************************************/
+
+/*
+ *	Local per-uart structure.
+ */
+struct mcf_uart {
+	struct uart_port	port;
+	unsigned int		sigs;		/* Local copy of line sigs */
+	unsigned char		imr;		/* Local IMR mirror */
+};
+
+/****************************************************************************/
+
+static unsigned int mcf_tx_empty(struct uart_port *port)
+{
+	return (readb(port->membase + MCFUART_USR) & MCFUART_USR_TXEMPTY) ?
+		TIOCSER_TEMT : 0;
+}
+
+/****************************************************************************/
+
+static unsigned int mcf_get_mctrl(struct uart_port *port)
+{
+	struct mcf_uart *pp = container_of(port, struct mcf_uart, port);
+	unsigned int sigs;
+
+	sigs = (readb(port->membase + MCFUART_UIPR) & MCFUART_UIPR_CTS) ?
+		0 : TIOCM_CTS;
+	sigs |= (pp->sigs & TIOCM_RTS);
+	sigs |= (mcf_getppdcd(port->line) ? TIOCM_CD : 0);
+	sigs |= (mcf_getppdtr(port->line) ? TIOCM_DTR : 0);
+
+	return sigs;
+}
+
+/****************************************************************************/
+
+static void mcf_set_mctrl(struct uart_port *port, unsigned int sigs)
+{
+	struct mcf_uart *pp = container_of(port, struct mcf_uart, port);
+
+	pp->sigs = sigs;
+	mcf_setppdtr(port->line, (sigs & TIOCM_DTR));
+	if (sigs & TIOCM_RTS)
+		writeb(MCFUART_UOP_RTS, port->membase + MCFUART_UOP1);
+	else
+		writeb(MCFUART_UOP_RTS, port->membase + MCFUART_UOP0);
+}
+
+/****************************************************************************/
+
+static void mcf_start_tx(struct uart_port *port)
+{
+	struct mcf_uart *pp = container_of(port, struct mcf_uart, port);
+
+	pp->imr |= MCFUART_UIR_TXREADY;
+	writeb(pp->imr, port->membase + MCFUART_UIMR);
+}
+
+/****************************************************************************/
+
+static void mcf_stop_tx(struct uart_port *port)
+{
+	struct mcf_uart *pp = container_of(port, struct mcf_uart, port);
+
+	pp->imr &= ~MCFUART_UIR_TXREADY;
+	writeb(pp->imr, port->membase + MCFUART_UIMR);
+}
+
+/****************************************************************************/
+
+static void mcf_stop_rx(struct uart_port *port)
+{
+	struct mcf_uart *pp = container_of(port, struct mcf_uart, port);
+
+	pp->imr &= ~MCFUART_UIR_RXREADY;
+	writeb(pp->imr, port->membase + MCFUART_UIMR);
+}
+
+/****************************************************************************/
+
+static void mcf_break_ctl(struct uart_port *port, int break_state)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&port->lock, flags);
+	if (break_state == -1)
+		writeb(MCFUART_UCR_CMDBREAKSTART, port->membase + MCFUART_UCR);
+	else
+		writeb(MCFUART_UCR_CMDBREAKSTOP, port->membase + MCFUART_UCR);
+	spin_unlock_irqrestore(&port->lock, flags);
+}
+
+/****************************************************************************/
+
+static void mcf_enable_ms(struct uart_port *port)
+{
+}
+
+/****************************************************************************/
+
+static int mcf_startup(struct uart_port *port)
+{
+	struct mcf_uart *pp = container_of(port, struct mcf_uart, port);
+	unsigned long flags;
+
+	spin_lock_irqsave(&port->lock, flags);
+
+	/* Reset UART, get it into known state... */
+	writeb(MCFUART_UCR_CMDRESETRX, port->membase + MCFUART_UCR);
+	writeb(MCFUART_UCR_CMDRESETTX, port->membase + MCFUART_UCR);
+
+	/* Enable the UART transmitter and receiver */
+	writeb(MCFUART_UCR_RXENABLE | MCFUART_UCR_TXENABLE,
+		port->membase + MCFUART_UCR);
+
+	/* Enable RX interrupts now */
+	pp->imr = MCFUART_UIR_RXREADY;
+	writeb(pp->imr, port->membase + MCFUART_UIMR);
+
+	spin_unlock_irqrestore(&port->lock, flags);
+
+	return 0;
+}
+
+/****************************************************************************/
+
+static void mcf_shutdown(struct uart_port *port)
+{
+	struct mcf_uart *pp = container_of(port, struct mcf_uart, port);
+	unsigned long flags;
+
+	spin_lock_irqsave(&port->lock, flags);
+
+	/* Disable all interrupts now */
+	pp->imr = 0;
+	writeb(pp->imr, port->membase + MCFUART_UIMR);
+
+	/* Disable UART transmitter and receiver */
+	writeb(MCFUART_UCR_CMDRESETRX, port->membase + MCFUART_UCR);
+	writeb(MCFUART_UCR_CMDRESETTX, port->membase + MCFUART_UCR);
+
+	spin_unlock_irqrestore(&port->lock, flags);
+}
+
+/****************************************************************************/
+
+static void mcf_set_termios(struct uart_port *port, struct ktermios *termios,
+	struct ktermios *old)
+{
+	unsigned long flags;
+	unsigned int baud, baudclk;
+#if defined(CONFIG_M5272)
+	unsigned int baudfr;
+#endif
+	unsigned char mr1, mr2;
+
+	baud = uart_get_baud_rate(port, termios, old, 0, 230400);
+#if defined(CONFIG_M5272)
+	baudclk = (MCF_BUSCLK / baud) / 32;
+	baudfr = (((MCF_BUSCLK / baud) + 1) / 2) % 16;
+#else
+	baudclk = ((MCF_BUSCLK / baud) + 16) / 32;
+#endif
+
+	mr1 = MCFUART_MR1_RXIRQRDY | MCFUART_MR1_RXERRCHAR;
+	mr2 = 0;
+
+	switch (termios->c_cflag & CSIZE) {
+	case CS5: mr1 |= MCFUART_MR1_CS5; break;
+	case CS6: mr1 |= MCFUART_MR1_CS6; break;
+	case CS7: mr1 |= MCFUART_MR1_CS7; break;
+	case CS8:
+	default:  mr1 |= MCFUART_MR1_CS8; break;
+	}
+
+	if (termios->c_cflag & PARENB) {
+		if (termios->c_cflag & CMSPAR) {
+			if (termios->c_cflag & PARODD)
+				mr1 |= MCFUART_MR1_PARITYMARK;
+			else
+				mr1 |= MCFUART_MR1_PARITYSPACE;
+		} else {
+			if (termios->c_cflag & PARODD)
+				mr1 |= MCFUART_MR1_PARITYODD;
+			else
+				mr1 |= MCFUART_MR1_PARITYEVEN;
+		}
+	} else {
+		mr1 |= MCFUART_MR1_PARITYNONE;
+	}
+
+	if (termios->c_cflag & CSTOPB)
+		mr2 |= MCFUART_MR2_STOP2;
+	else
+		mr2 |= MCFUART_MR2_STOP1;
+
+	if (termios->c_cflag & CRTSCTS) {
+		mr1 |= MCFUART_MR1_RXRTS;
+		mr2 |= MCFUART_MR2_TXCTS;
+	}
+
+	spin_lock_irqsave(&port->lock, flags);
+	uart_update_timeout(port, termios->c_cflag, baud);
+	writeb(MCFUART_UCR_CMDRESETRX, port->membase + MCFUART_UCR);
+	writeb(MCFUART_UCR_CMDRESETTX, port->membase + MCFUART_UCR);
+	writeb(MCFUART_UCR_CMDRESETMRPTR, port->membase + MCFUART_UCR);
+	writeb(mr1, port->membase + MCFUART_UMR);
+	writeb(mr2, port->membase + MCFUART_UMR);
+	writeb((baudclk & 0xff00) >> 8, port->membase + MCFUART_UBG1);
+	writeb((baudclk & 0xff), port->membase + MCFUART_UBG2);
+#if defined(CONFIG_M5272)
+	writeb((baudfr & 0x0f), port->membase + MCFUART_UFPD);
+#endif
+	writeb(MCFUART_UCSR_RXCLKTIMER | MCFUART_UCSR_TXCLKTIMER,
+		port->membase + MCFUART_UCSR);
+	writeb(MCFUART_UCR_RXENABLE | MCFUART_UCR_TXENABLE,
+		port->membase + MCFUART_UCR);
+	spin_unlock_irqrestore(&port->lock, flags);
+}
+
+/****************************************************************************/
+
+static void mcf_rx_chars(struct mcf_uart *pp)
+{
+	struct uart_port *port = &pp->port;
+	unsigned char status, ch, flag;
+
+	while ((status = readb(port->membase + MCFUART_USR)) & MCFUART_USR_RXREADY) {
+		ch = readb(port->membase + MCFUART_URB);
+		flag = TTY_NORMAL;
+		port->icount.rx++;
+
+		if (status & MCFUART_USR_RXERR) {
+			writeb(MCFUART_UCR_CMDRESETERR,
+				port->membase + MCFUART_UCR);
+
+			if (status & MCFUART_USR_RXBREAK) {
+				port->icount.brk++;
+				if (uart_handle_break(port))
+					continue;
+			} else if (status & MCFUART_USR_RXPARITY) {
+				port->icount.parity++;
+			} else if (status & MCFUART_USR_RXOVERRUN) {
+				port->icount.overrun++;
+			} else if (status & MCFUART_USR_RXFRAMING) {
+				port->icount.frame++;
+			}
+
+			status &= port->read_status_mask;
+
+			if (status & MCFUART_USR_RXBREAK)
+				flag = TTY_BREAK;
+			else if (status & MCFUART_USR_RXPARITY)
+				flag = TTY_PARITY;
+			else if (status & MCFUART_USR_RXFRAMING)
+				flag = TTY_FRAME;
+		}
+
+		if (uart_handle_sysrq_char(port, ch))
+			continue;
+		uart_insert_char(port, status, MCFUART_USR_RXOVERRUN, ch, flag);
+	}
+
+	tty_flip_buffer_push(port->state->port.tty);
+}
+
+/****************************************************************************/
+
+static void mcf_tx_chars(struct mcf_uart *pp)
+{
+	struct uart_port *port = &pp->port;
+	struct circ_buf *xmit = &port->state->xmit;
+
+	if (port->x_char) {
+		/* Send special char - probably flow control */
+		writeb(port->x_char, port->membase + MCFUART_UTB);
+		port->x_char = 0;
+		port->icount.tx++;
+		return;
+	}
+
+	while (readb(port->membase + MCFUART_USR) & MCFUART_USR_TXREADY) {
+		if (xmit->head == xmit->tail)
+			break;
+		writeb(xmit->buf[xmit->tail], port->membase + MCFUART_UTB);
+		xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE -1);
+		port->icount.tx++;
+	}
+
+	if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
+		uart_write_wakeup(port);
+
+	if (xmit->head == xmit->tail) {
+		pp->imr &= ~MCFUART_UIR_TXREADY;
+		writeb(pp->imr, port->membase + MCFUART_UIMR);
+	}
+}
+
+/****************************************************************************/
+
+static irqreturn_t mcf_interrupt(int irq, void *data)
+{
+	struct uart_port *port = data;
+	struct mcf_uart *pp = container_of(port, struct mcf_uart, port);
+	unsigned int isr;
+	irqreturn_t ret = IRQ_NONE;
+
+	isr = readb(port->membase + MCFUART_UISR) & pp->imr;
+
+	spin_lock(&port->lock);
+	if (isr & MCFUART_UIR_RXREADY) {
+		mcf_rx_chars(pp);
+		ret = IRQ_HANDLED;
+	}
+	if (isr & MCFUART_UIR_TXREADY) {
+		mcf_tx_chars(pp);
+		ret = IRQ_HANDLED;
+	}
+	spin_unlock(&port->lock);
+
+	return ret;
+}
+
+/****************************************************************************/
+
+static void mcf_config_port(struct uart_port *port, int flags)
+{
+	port->type = PORT_MCF;
+	port->fifosize = MCFUART_TXFIFOSIZE;
+
+	/* Clear mask, so no surprise interrupts. */
+	writeb(0, port->membase + MCFUART_UIMR);
+
+	if (request_irq(port->irq, mcf_interrupt, 0, "UART", port))
+		printk(KERN_ERR "MCF: unable to attach ColdFire UART %d "
+			"interrupt vector=%d\n", port->line, port->irq);
+}
+
+/****************************************************************************/
+
+static const char *mcf_type(struct uart_port *port)
+{
+	return (port->type == PORT_MCF) ? "ColdFire UART" : NULL;
+}
+
+/****************************************************************************/
+
+static int mcf_request_port(struct uart_port *port)
+{
+	/* UARTs always present */
+	return 0;
+}
+
+/****************************************************************************/
+
+static void mcf_release_port(struct uart_port *port)
+{
+	/* Nothing to release... */
+}
+
+/****************************************************************************/
+
+static int mcf_verify_port(struct uart_port *port, struct serial_struct *ser)
+{
+	if ((ser->type != PORT_UNKNOWN) && (ser->type != PORT_MCF))
+		return -EINVAL;
+	return 0;
+}
+
+/****************************************************************************/
+
+/*
+ *	Define the basic serial functions we support.
+ */
+static const struct uart_ops mcf_uart_ops = {
+	.tx_empty	= mcf_tx_empty,
+	.get_mctrl	= mcf_get_mctrl,
+	.set_mctrl	= mcf_set_mctrl,
+	.start_tx	= mcf_start_tx,
+	.stop_tx	= mcf_stop_tx,
+	.stop_rx	= mcf_stop_rx,
+	.enable_ms	= mcf_enable_ms,
+	.break_ctl	= mcf_break_ctl,
+	.startup	= mcf_startup,
+	.shutdown	= mcf_shutdown,
+	.set_termios	= mcf_set_termios,
+	.type		= mcf_type,
+	.request_port	= mcf_request_port,
+	.release_port	= mcf_release_port,
+	.config_port	= mcf_config_port,
+	.verify_port	= mcf_verify_port,
+};
+
+static struct mcf_uart mcf_ports[4];
+
+#define	MCF_MAXPORTS	ARRAY_SIZE(mcf_ports)
+
+/****************************************************************************/
+#if defined(CONFIG_SERIAL_MCF_CONSOLE)
+/****************************************************************************/
+
+int __init early_mcf_setup(struct mcf_platform_uart *platp)
+{
+	struct uart_port *port;
+	int i;
+
+	for (i = 0; ((i < MCF_MAXPORTS) && (platp[i].mapbase)); i++) {
+		port = &mcf_ports[i].port;
+
+		port->line = i;
+		port->type = PORT_MCF;
+		port->mapbase = platp[i].mapbase;
+		port->membase = (platp[i].membase) ? platp[i].membase :
+			(unsigned char __iomem *) port->mapbase;
+		port->iotype = SERIAL_IO_MEM;
+		port->irq = platp[i].irq;
+		port->uartclk = MCF_BUSCLK;
+		port->flags = ASYNC_BOOT_AUTOCONF;
+		port->ops = &mcf_uart_ops;
+	}
+
+	return 0;
+}
+
+/****************************************************************************/
+
+static void mcf_console_putc(struct console *co, const char c)
+{
+	struct uart_port *port = &(mcf_ports + co->index)->port;
+	int i;
+
+	for (i = 0; (i < 0x10000); i++) {
+		if (readb(port->membase + MCFUART_USR) & MCFUART_USR_TXREADY)
+			break;
+	}
+	writeb(c, port->membase + MCFUART_UTB);
+	for (i = 0; (i < 0x10000); i++) {
+		if (readb(port->membase + MCFUART_USR) & MCFUART_USR_TXREADY)
+			break;
+	}
+}
+
+/****************************************************************************/
+
+static void mcf_console_write(struct console *co, const char *s, unsigned int count)
+{
+	for (; (count); count--, s++) {
+		mcf_console_putc(co, *s);
+		if (*s == '\n')
+			mcf_console_putc(co, '\r');
+	}
+}
+
+/****************************************************************************/
+
+static int __init mcf_console_setup(struct console *co, char *options)
+{
+	struct uart_port *port;
+	int baud = CONFIG_SERIAL_MCF_BAUDRATE;
+	int bits = 8;
+	int parity = 'n';
+	int flow = 'n';
+
+	if ((co->index < 0) || (co->index >= MCF_MAXPORTS))
+		co->index = 0;
+	port = &mcf_ports[co->index].port;
+	if (port->membase == 0)
+		return -ENODEV;
+
+	if (options)
+		uart_parse_options(options, &baud, &parity, &bits, &flow);
+
+	return uart_set_options(port, co, baud, parity, bits, flow);
+}
+
+/****************************************************************************/
+
+static struct uart_driver mcf_driver;
+
+static struct console mcf_console = {
+	.name		= "ttyS",
+	.write		= mcf_console_write,
+	.device		= uart_console_device,
+	.setup		= mcf_console_setup,
+	.flags		= CON_PRINTBUFFER,
+	.index		= -1,
+	.data		= &mcf_driver,
+};
+
+static int __init mcf_console_init(void)
+{
+	register_console(&mcf_console);
+	return 0;
+}
+
+console_initcall(mcf_console_init);
+
+#define	MCF_CONSOLE	&mcf_console
+
+/****************************************************************************/
+#else
+/****************************************************************************/
+
+#define	MCF_CONSOLE	NULL
+
+/****************************************************************************/
+#endif /* CONFIG_MCF_CONSOLE */
+/****************************************************************************/
+
+/*
+ *	Define the mcf UART driver structure.
+ */
+static struct uart_driver mcf_driver = {
+	.owner		= THIS_MODULE,
+	.driver_name	= "mcf",
+	.dev_name	= "ttyS",
+	.major		= TTY_MAJOR,
+	.minor		= 64,
+	.nr		= MCF_MAXPORTS,
+	.cons		= MCF_CONSOLE,
+};
+
+/****************************************************************************/
+
+static int __devinit mcf_probe(struct platform_device *pdev)
+{
+	struct mcf_platform_uart *platp = pdev->dev.platform_data;
+	struct uart_port *port;
+	int i;
+
+	for (i = 0; ((i < MCF_MAXPORTS) && (platp[i].mapbase)); i++) {
+		port = &mcf_ports[i].port;
+
+		port->line = i;
+		port->type = PORT_MCF;
+		port->mapbase = platp[i].mapbase;
+		port->membase = (platp[i].membase) ? platp[i].membase :
+			(unsigned char __iomem *) platp[i].mapbase;
+		port->iotype = SERIAL_IO_MEM;
+		port->irq = platp[i].irq;
+		port->uartclk = MCF_BUSCLK;
+		port->ops = &mcf_uart_ops;
+		port->flags = ASYNC_BOOT_AUTOCONF;
+
+		uart_add_one_port(&mcf_driver, port);
+	}
+
+	return 0;
+}
+
+/****************************************************************************/
+
+static int __devexit mcf_remove(struct platform_device *pdev)
+{
+	struct uart_port *port;
+	int i;
+
+	for (i = 0; (i < MCF_MAXPORTS); i++) {
+		port = &mcf_ports[i].port;
+		if (port)
+			uart_remove_one_port(&mcf_driver, port);
+	}
+
+	return 0;
+}
+
+/****************************************************************************/
+
+static struct platform_driver mcf_platform_driver = {
+	.probe		= mcf_probe,
+	.remove		= __devexit_p(mcf_remove),
+	.driver		= {
+		.name	= "mcfuart",
+		.owner	= THIS_MODULE,
+	},
+};
+
+/****************************************************************************/
+
+static int __init mcf_init(void)
+{
+	int rc;
+
+	printk("ColdFire internal UART serial driver\n");
+
+	rc = uart_register_driver(&mcf_driver);
+	if (rc)
+		return rc;
+	rc = platform_driver_register(&mcf_platform_driver);
+	if (rc)
+		return rc;
+	return 0;
+}
+
+/****************************************************************************/
+
+static void __exit mcf_exit(void)
+{
+	platform_driver_unregister(&mcf_platform_driver);
+	uart_unregister_driver(&mcf_driver);
+}
+
+/****************************************************************************/
+
+module_init(mcf_init);
+module_exit(mcf_exit);
+
+MODULE_AUTHOR("Greg Ungerer <gerg@snapgear.com>");
+MODULE_DESCRIPTION("Freescale ColdFire UART driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:mcfuart");
+
+/****************************************************************************/
diff --git a/ap/os/linux/linux-3.4.x/drivers/tty/serial/mfd.c b/ap/os/linux/linux-3.4.x/drivers/tty/serial/mfd.c
new file mode 100644
index 0000000..c4b50af
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/tty/serial/mfd.c
@@ -0,0 +1,1506 @@
+/*
+ * mfd.c: driver for High Speed UART device of Intel Medfield platform
+ *
+ * Refer pxa.c, 8250.c and some other drivers in drivers/serial/
+ *
+ * (C) Copyright 2010 Intel Corporation
+ *
+ * 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; version 2
+ * of the License.
+ */
+
+/* Notes:
+ * 1. DMA channel allocation: 0/1 channel are assigned to port 0,
+ *    2/3 chan to port 1, 4/5 chan to port 3. Even number chans
+ *    are used for RX, odd chans for TX
+ *
+ * 2. The RI/DSR/DCD/DTR are not pinned out, DCD & DSR are always
+ *    asserted, only when the HW is reset the DDCD and DDSR will
+ *    be triggered
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/console.h>
+#include <linux/sysrq.h>
+#include <linux/slab.h>
+#include <linux/serial_reg.h>
+#include <linux/circ_buf.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
+#include <linux/serial_core.h>
+#include <linux/serial_mfd.h>
+#include <linux/dma-mapping.h>
+#include <linux/pci.h>
+#include <linux/io.h>
+#include <linux/debugfs.h>
+#include <linux/pm_runtime.h>
+
+#define HSU_DMA_BUF_SIZE	2048
+
+#define chan_readl(chan, offset)	readl(chan->reg + offset)
+#define chan_writel(chan, offset, val)	writel(val, chan->reg + offset)
+
+#define mfd_readl(obj, offset)		readl(obj->reg + offset)
+#define mfd_writel(obj, offset, val)	writel(val, obj->reg + offset)
+
+static int hsu_dma_enable;
+module_param(hsu_dma_enable, int, 0);
+MODULE_PARM_DESC(hsu_dma_enable,
+		 "It is a bitmap to set working mode, if bit[x] is 1, then port[x] will work in DMA mode, otherwise in PIO mode.");
+
+struct hsu_dma_buffer {
+	u8		*buf;
+	dma_addr_t	dma_addr;
+	u32		dma_size;
+	u32		ofs;
+};
+
+struct hsu_dma_chan {
+	u32	id;
+	enum dma_data_direction	dirt;
+	struct uart_hsu_port	*uport;
+	void __iomem		*reg;
+};
+
+struct uart_hsu_port {
+	struct uart_port        port;
+	unsigned char           ier;
+	unsigned char           lcr;
+	unsigned char           mcr;
+	unsigned int            lsr_break_flag;
+	char			name[12];
+	int			index;
+	struct device		*dev;
+
+	struct hsu_dma_chan	*txc;
+	struct hsu_dma_chan	*rxc;
+	struct hsu_dma_buffer	txbuf;
+	struct hsu_dma_buffer	rxbuf;
+	int			use_dma;	/* flag for DMA/PIO */
+	int			running;
+	int			dma_tx_on;
+};
+
+/* Top level data structure of HSU */
+struct hsu_port {
+	void __iomem	*reg;
+	unsigned long	paddr;
+	unsigned long	iolen;
+	u32		irq;
+
+	struct uart_hsu_port	port[3];
+	struct hsu_dma_chan	chans[10];
+
+	struct dentry *debugfs;
+};
+
+static inline unsigned int serial_in(struct uart_hsu_port *up, int offset)
+{
+	unsigned int val;
+
+	if (offset > UART_MSR) {
+		offset <<= 2;
+		val = readl(up->port.membase + offset);
+	} else
+		val = (unsigned int)readb(up->port.membase + offset);
+
+	return val;
+}
+
+static inline void serial_out(struct uart_hsu_port *up, int offset, int value)
+{
+	if (offset > UART_MSR) {
+		offset <<= 2;
+		writel(value, up->port.membase + offset);
+	} else {
+		unsigned char val = value & 0xff;
+		writeb(val, up->port.membase + offset);
+	}
+}
+
+#ifdef CONFIG_DEBUG_FS
+
+#define HSU_REGS_BUFSIZE	1024
+
+
+static ssize_t port_show_regs(struct file *file, char __user *user_buf,
+				size_t count, loff_t *ppos)
+{
+	struct uart_hsu_port *up = file->private_data;
+	char *buf;
+	u32 len = 0;
+	ssize_t ret;
+
+	buf = kzalloc(HSU_REGS_BUFSIZE, GFP_KERNEL);
+	if (!buf)
+		return 0;
+
+	len += snprintf(buf + len, HSU_REGS_BUFSIZE - len,
+			"MFD HSU port[%d] regs:\n", up->index);
+
+	len += snprintf(buf + len, HSU_REGS_BUFSIZE - len,
+			"=================================\n");
+	len += snprintf(buf + len, HSU_REGS_BUFSIZE - len,
+			"IER: \t\t0x%08x\n", serial_in(up, UART_IER));
+	len += snprintf(buf + len, HSU_REGS_BUFSIZE - len,
+			"IIR: \t\t0x%08x\n", serial_in(up, UART_IIR));
+	len += snprintf(buf + len, HSU_REGS_BUFSIZE - len,
+			"LCR: \t\t0x%08x\n", serial_in(up, UART_LCR));
+	len += snprintf(buf + len, HSU_REGS_BUFSIZE - len,
+			"MCR: \t\t0x%08x\n", serial_in(up, UART_MCR));
+	len += snprintf(buf + len, HSU_REGS_BUFSIZE - len,
+			"LSR: \t\t0x%08x\n", serial_in(up, UART_LSR));
+	len += snprintf(buf + len, HSU_REGS_BUFSIZE - len,
+			"MSR: \t\t0x%08x\n", serial_in(up, UART_MSR));
+	len += snprintf(buf + len, HSU_REGS_BUFSIZE - len,
+			"FOR: \t\t0x%08x\n", serial_in(up, UART_FOR));
+	len += snprintf(buf + len, HSU_REGS_BUFSIZE - len,
+			"PS: \t\t0x%08x\n", serial_in(up, UART_PS));
+	len += snprintf(buf + len, HSU_REGS_BUFSIZE - len,
+			"MUL: \t\t0x%08x\n", serial_in(up, UART_MUL));
+	len += snprintf(buf + len, HSU_REGS_BUFSIZE - len,
+			"DIV: \t\t0x%08x\n", serial_in(up, UART_DIV));
+
+	if (len > HSU_REGS_BUFSIZE)
+		len = HSU_REGS_BUFSIZE;
+
+	ret =  simple_read_from_buffer(user_buf, count, ppos, buf, len);
+	kfree(buf);
+	return ret;
+}
+
+static ssize_t dma_show_regs(struct file *file, char __user *user_buf,
+				size_t count, loff_t *ppos)
+{
+	struct hsu_dma_chan *chan = file->private_data;
+	char *buf;
+	u32 len = 0;
+	ssize_t ret;
+
+	buf = kzalloc(HSU_REGS_BUFSIZE, GFP_KERNEL);
+	if (!buf)
+		return 0;
+
+	len += snprintf(buf + len, HSU_REGS_BUFSIZE - len,
+			"MFD HSU DMA channel [%d] regs:\n", chan->id);
+
+	len += snprintf(buf + len, HSU_REGS_BUFSIZE - len,
+			"=================================\n");
+	len += snprintf(buf + len, HSU_REGS_BUFSIZE - len,
+			"CR: \t\t0x%08x\n", chan_readl(chan, HSU_CH_CR));
+	len += snprintf(buf + len, HSU_REGS_BUFSIZE - len,
+			"DCR: \t\t0x%08x\n", chan_readl(chan, HSU_CH_DCR));
+	len += snprintf(buf + len, HSU_REGS_BUFSIZE - len,
+			"BSR: \t\t0x%08x\n", chan_readl(chan, HSU_CH_BSR));
+	len += snprintf(buf + len, HSU_REGS_BUFSIZE - len,
+			"MOTSR: \t\t0x%08x\n", chan_readl(chan, HSU_CH_MOTSR));
+	len += snprintf(buf + len, HSU_REGS_BUFSIZE - len,
+			"D0SAR: \t\t0x%08x\n", chan_readl(chan, HSU_CH_D0SAR));
+	len += snprintf(buf + len, HSU_REGS_BUFSIZE - len,
+			"D0TSR: \t\t0x%08x\n", chan_readl(chan, HSU_CH_D0TSR));
+	len += snprintf(buf + len, HSU_REGS_BUFSIZE - len,
+			"D0SAR: \t\t0x%08x\n", chan_readl(chan, HSU_CH_D1SAR));
+	len += snprintf(buf + len, HSU_REGS_BUFSIZE - len,
+			"D0TSR: \t\t0x%08x\n", chan_readl(chan, HSU_CH_D1TSR));
+	len += snprintf(buf + len, HSU_REGS_BUFSIZE - len,
+			"D0SAR: \t\t0x%08x\n", chan_readl(chan, HSU_CH_D2SAR));
+	len += snprintf(buf + len, HSU_REGS_BUFSIZE - len,
+			"D0TSR: \t\t0x%08x\n", chan_readl(chan, HSU_CH_D2TSR));
+	len += snprintf(buf + len, HSU_REGS_BUFSIZE - len,
+			"D0SAR: \t\t0x%08x\n", chan_readl(chan, HSU_CH_D3SAR));
+	len += snprintf(buf + len, HSU_REGS_BUFSIZE - len,
+			"D0TSR: \t\t0x%08x\n", chan_readl(chan, HSU_CH_D3TSR));
+
+	if (len > HSU_REGS_BUFSIZE)
+		len = HSU_REGS_BUFSIZE;
+
+	ret =  simple_read_from_buffer(user_buf, count, ppos, buf, len);
+	kfree(buf);
+	return ret;
+}
+
+static const struct file_operations port_regs_ops = {
+	.owner		= THIS_MODULE,
+	.open		= simple_open,
+	.read		= port_show_regs,
+	.llseek		= default_llseek,
+};
+
+static const struct file_operations dma_regs_ops = {
+	.owner		= THIS_MODULE,
+	.open		= simple_open,
+	.read		= dma_show_regs,
+	.llseek		= default_llseek,
+};
+
+static int hsu_debugfs_init(struct hsu_port *hsu)
+{
+	int i;
+	char name[32];
+
+	hsu->debugfs = debugfs_create_dir("hsu", NULL);
+	if (!hsu->debugfs)
+		return -ENOMEM;
+
+	for (i = 0; i < 3; i++) {
+		snprintf(name, sizeof(name), "port_%d_regs", i);
+		debugfs_create_file(name, S_IFREG | S_IRUGO,
+			hsu->debugfs, (void *)(&hsu->port[i]), &port_regs_ops);
+	}
+
+	for (i = 0; i < 6; i++) {
+		snprintf(name, sizeof(name), "dma_chan_%d_regs", i);
+		debugfs_create_file(name, S_IFREG | S_IRUGO,
+			hsu->debugfs, (void *)&hsu->chans[i], &dma_regs_ops);
+	}
+
+	return 0;
+}
+
+static void hsu_debugfs_remove(struct hsu_port *hsu)
+{
+	if (hsu->debugfs)
+		debugfs_remove_recursive(hsu->debugfs);
+}
+
+#else
+static inline int hsu_debugfs_init(struct hsu_port *hsu)
+{
+	return 0;
+}
+
+static inline void hsu_debugfs_remove(struct hsu_port *hsu)
+{
+}
+#endif /* CONFIG_DEBUG_FS */
+
+static void serial_hsu_enable_ms(struct uart_port *port)
+{
+	struct uart_hsu_port *up =
+		container_of(port, struct uart_hsu_port, port);
+
+	up->ier |= UART_IER_MSI;
+	serial_out(up, UART_IER, up->ier);
+}
+
+void hsu_dma_tx(struct uart_hsu_port *up)
+{
+	struct circ_buf *xmit = &up->port.state->xmit;
+	struct hsu_dma_buffer *dbuf = &up->txbuf;
+	int count;
+
+	/* test_and_set_bit may be better, but anyway it's in lock protected mode */
+	if (up->dma_tx_on)
+		return;
+
+	/* Update the circ buf info */
+	xmit->tail += dbuf->ofs;
+	xmit->tail &= UART_XMIT_SIZE - 1;
+
+	up->port.icount.tx += dbuf->ofs;
+	dbuf->ofs = 0;
+
+	/* Disable the channel */
+	chan_writel(up->txc, HSU_CH_CR, 0x0);
+
+	if (!uart_circ_empty(xmit) && !uart_tx_stopped(&up->port)) {
+		dma_sync_single_for_device(up->port.dev,
+					   dbuf->dma_addr,
+					   dbuf->dma_size,
+					   DMA_TO_DEVICE);
+
+		count = CIRC_CNT_TO_END(xmit->head, xmit->tail, UART_XMIT_SIZE);
+		dbuf->ofs = count;
+
+		/* Reprogram the channel */
+		chan_writel(up->txc, HSU_CH_D0SAR, dbuf->dma_addr + xmit->tail);
+		chan_writel(up->txc, HSU_CH_D0TSR, count);
+
+		/* Reenable the channel */
+		chan_writel(up->txc, HSU_CH_DCR, 0x1
+						 | (0x1 << 8)
+						 | (0x1 << 16)
+						 | (0x1 << 24));
+		up->dma_tx_on = 1;
+		chan_writel(up->txc, HSU_CH_CR, 0x1);
+	}
+
+	if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
+		uart_write_wakeup(&up->port);
+}
+
+/* The buffer is already cache coherent */
+void hsu_dma_start_rx_chan(struct hsu_dma_chan *rxc, struct hsu_dma_buffer *dbuf)
+{
+	dbuf->ofs = 0;
+
+	chan_writel(rxc, HSU_CH_BSR, 32);
+	chan_writel(rxc, HSU_CH_MOTSR, 4);
+
+	chan_writel(rxc, HSU_CH_D0SAR, dbuf->dma_addr);
+	chan_writel(rxc, HSU_CH_D0TSR, dbuf->dma_size);
+	chan_writel(rxc, HSU_CH_DCR, 0x1 | (0x1 << 8)
+					 | (0x1 << 16)
+					 | (0x1 << 24)	/* timeout bit, see HSU Errata 1 */
+					 );
+	chan_writel(rxc, HSU_CH_CR, 0x3);
+}
+
+/* Protected by spin_lock_irqsave(port->lock) */
+static void serial_hsu_start_tx(struct uart_port *port)
+{
+	struct uart_hsu_port *up =
+		container_of(port, struct uart_hsu_port, port);
+
+	if (up->use_dma) {
+		hsu_dma_tx(up);
+	} else if (!(up->ier & UART_IER_THRI)) {
+		up->ier |= UART_IER_THRI;
+		serial_out(up, UART_IER, up->ier);
+	}
+}
+
+static void serial_hsu_stop_tx(struct uart_port *port)
+{
+	struct uart_hsu_port *up =
+		container_of(port, struct uart_hsu_port, port);
+	struct hsu_dma_chan *txc = up->txc;
+
+	if (up->use_dma)
+		chan_writel(txc, HSU_CH_CR, 0x0);
+	else if (up->ier & UART_IER_THRI) {
+		up->ier &= ~UART_IER_THRI;
+		serial_out(up, UART_IER, up->ier);
+	}
+}
+
+/* This is always called in spinlock protected mode, so
+ * modify timeout timer is safe here */
+void hsu_dma_rx(struct uart_hsu_port *up, u32 int_sts)
+{
+	struct hsu_dma_buffer *dbuf = &up->rxbuf;
+	struct hsu_dma_chan *chan = up->rxc;
+	struct uart_port *port = &up->port;
+	struct tty_struct *tty = port->state->port.tty;
+	int count;
+
+	if (!tty)
+		return;
+
+	/*
+	 * First need to know how many is already transferred,
+	 * then check if its a timeout DMA irq, and return
+	 * the trail bytes out, push them up and reenable the
+	 * channel
+	 */
+
+	/* Timeout IRQ, need wait some time, see Errata 2 */
+	if (int_sts & 0xf00)
+		udelay(2);
+
+	/* Stop the channel */
+	chan_writel(chan, HSU_CH_CR, 0x0);
+
+	count = chan_readl(chan, HSU_CH_D0SAR) - dbuf->dma_addr;
+	if (!count) {
+		/* Restart the channel before we leave */
+		chan_writel(chan, HSU_CH_CR, 0x3);
+		return;
+	}
+
+	dma_sync_single_for_cpu(port->dev, dbuf->dma_addr,
+			dbuf->dma_size, DMA_FROM_DEVICE);
+
+	/*
+	 * Head will only wrap around when we recycle
+	 * the DMA buffer, and when that happens, we
+	 * explicitly set tail to 0. So head will
+	 * always be greater than tail.
+	 */
+	tty_insert_flip_string(tty, dbuf->buf, count);
+	port->icount.rx += count;
+
+	dma_sync_single_for_device(up->port.dev, dbuf->dma_addr,
+			dbuf->dma_size, DMA_FROM_DEVICE);
+
+	/* Reprogram the channel */
+	chan_writel(chan, HSU_CH_D0SAR, dbuf->dma_addr);
+	chan_writel(chan, HSU_CH_D0TSR, dbuf->dma_size);
+	chan_writel(chan, HSU_CH_DCR, 0x1
+					 | (0x1 << 8)
+					 | (0x1 << 16)
+					 | (0x1 << 24)	/* timeout bit, see HSU Errata 1 */
+					 );
+	tty_flip_buffer_push(tty);
+
+	chan_writel(chan, HSU_CH_CR, 0x3);
+
+}
+
+static void serial_hsu_stop_rx(struct uart_port *port)
+{
+	struct uart_hsu_port *up =
+		container_of(port, struct uart_hsu_port, port);
+	struct hsu_dma_chan *chan = up->rxc;
+
+	if (up->use_dma)
+		chan_writel(chan, HSU_CH_CR, 0x2);
+	else {
+		up->ier &= ~UART_IER_RLSI;
+		up->port.read_status_mask &= ~UART_LSR_DR;
+		serial_out(up, UART_IER, up->ier);
+	}
+}
+
+static inline void receive_chars(struct uart_hsu_port *up, int *status)
+{
+	struct tty_struct *tty = up->port.state->port.tty;
+	unsigned int ch, flag;
+	unsigned int max_count = 256;
+
+	if (!tty)
+		return;
+
+	do {
+		ch = serial_in(up, UART_RX);
+		flag = TTY_NORMAL;
+		up->port.icount.rx++;
+
+		if (unlikely(*status & (UART_LSR_BI | UART_LSR_PE |
+				       UART_LSR_FE | UART_LSR_OE))) {
+
+			dev_warn(up->dev, "We really rush into ERR/BI case"
+				"status = 0x%02x", *status);
+			/* For statistics only */
+			if (*status & UART_LSR_BI) {
+				*status &= ~(UART_LSR_FE | UART_LSR_PE);
+				up->port.icount.brk++;
+				/*
+				 * We do the SysRQ and SAK checking
+				 * here because otherwise the break
+				 * may get masked by ignore_status_mask
+				 * or read_status_mask.
+				 */
+				if (uart_handle_break(&up->port))
+					goto ignore_char;
+			} else if (*status & UART_LSR_PE)
+				up->port.icount.parity++;
+			else if (*status & UART_LSR_FE)
+				up->port.icount.frame++;
+			if (*status & UART_LSR_OE)
+				up->port.icount.overrun++;
+
+			/* Mask off conditions which should be ignored. */
+			*status &= up->port.read_status_mask;
+
+#ifdef CONFIG_SERIAL_MFD_HSU_CONSOLE
+			if (up->port.cons &&
+				up->port.cons->index == up->port.line) {
+				/* Recover the break flag from console xmit */
+				*status |= up->lsr_break_flag;
+				up->lsr_break_flag = 0;
+			}
+#endif
+			if (*status & UART_LSR_BI) {
+				flag = TTY_BREAK;
+			} else if (*status & UART_LSR_PE)
+				flag = TTY_PARITY;
+			else if (*status & UART_LSR_FE)
+				flag = TTY_FRAME;
+		}
+
+		if (uart_handle_sysrq_char(&up->port, ch))
+			goto ignore_char;
+
+		uart_insert_char(&up->port, *status, UART_LSR_OE, ch, flag);
+	ignore_char:
+		*status = serial_in(up, UART_LSR);
+	} while ((*status & UART_LSR_DR) && max_count--);
+	tty_flip_buffer_push(tty);
+}
+
+static void transmit_chars(struct uart_hsu_port *up)
+{
+	struct circ_buf *xmit = &up->port.state->xmit;
+	int count;
+
+	if (up->port.x_char) {
+		serial_out(up, UART_TX, up->port.x_char);
+		up->port.icount.tx++;
+		up->port.x_char = 0;
+		return;
+	}
+	if (uart_circ_empty(xmit) || uart_tx_stopped(&up->port)) {
+		serial_hsu_stop_tx(&up->port);
+		return;
+	}
+
+	/* The IRQ is for TX FIFO half-empty */
+	count = up->port.fifosize / 2;
+
+	do {
+		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))
+		serial_hsu_stop_tx(&up->port);
+}
+
+static inline void check_modem_status(struct uart_hsu_port *up)
+{
+	int status;
+
+	status = serial_in(up, UART_MSR);
+
+	if ((status & UART_MSR_ANY_DELTA) == 0)
+		return;
+
+	if (status & UART_MSR_TERI)
+		up->port.icount.rng++;
+	if (status & UART_MSR_DDSR)
+		up->port.icount.dsr++;
+	/* We may only get DDCD when HW init and reset */
+	if (status & UART_MSR_DDCD)
+		uart_handle_dcd_change(&up->port, status & UART_MSR_DCD);
+	/* Will start/stop_tx accordingly */
+	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);
+}
+
+/*
+ * This handles the interrupt from one port.
+ */
+static irqreturn_t port_irq(int irq, void *dev_id)
+{
+	struct uart_hsu_port *up = dev_id;
+	unsigned int iir, lsr;
+	unsigned long flags;
+
+	if (unlikely(!up->running))
+		return IRQ_NONE;
+
+	spin_lock_irqsave(&up->port.lock, flags);
+	if (up->use_dma) {
+		lsr = serial_in(up, UART_LSR);
+		if (unlikely(lsr & (UART_LSR_BI | UART_LSR_PE |
+				       UART_LSR_FE | UART_LSR_OE)))
+			dev_warn(up->dev,
+				"Got lsr irq while using DMA, lsr = 0x%2x\n",
+				lsr);
+		check_modem_status(up);
+		spin_unlock_irqrestore(&up->port.lock, flags);
+		return IRQ_HANDLED;
+	}
+
+	iir = serial_in(up, UART_IIR);
+	if (iir & UART_IIR_NO_INT) {
+		spin_unlock_irqrestore(&up->port.lock, flags);
+		return IRQ_NONE;
+	}
+
+	lsr = serial_in(up, UART_LSR);
+	if (lsr & UART_LSR_DR)
+		receive_chars(up, &lsr);
+	check_modem_status(up);
+
+	/* lsr will be renewed during the receive_chars */
+	if (lsr & UART_LSR_THRE)
+		transmit_chars(up);
+
+	spin_unlock_irqrestore(&up->port.lock, flags);
+	return IRQ_HANDLED;
+}
+
+static inline void dma_chan_irq(struct hsu_dma_chan *chan)
+{
+	struct uart_hsu_port *up = chan->uport;
+	unsigned long flags;
+	u32 int_sts;
+
+	spin_lock_irqsave(&up->port.lock, flags);
+
+	if (!up->use_dma || !up->running)
+		goto exit;
+
+	/*
+	 * No matter what situation, need read clear the IRQ status
+	 * There is a bug, see Errata 5, HSD 2900918
+	 */
+	int_sts = chan_readl(chan, HSU_CH_SR);
+
+	/* Rx channel */
+	if (chan->dirt == DMA_FROM_DEVICE)
+		hsu_dma_rx(up, int_sts);
+
+	/* Tx channel */
+	if (chan->dirt == DMA_TO_DEVICE) {
+		chan_writel(chan, HSU_CH_CR, 0x0);
+		up->dma_tx_on = 0;
+		hsu_dma_tx(up);
+	}
+
+exit:
+	spin_unlock_irqrestore(&up->port.lock, flags);
+	return;
+}
+
+static irqreturn_t dma_irq(int irq, void *dev_id)
+{
+	struct hsu_port *hsu = dev_id;
+	u32 int_sts, i;
+
+	int_sts = mfd_readl(hsu, HSU_GBL_DMAISR);
+
+	/* Currently we only have 6 channels may be used */
+	for (i = 0; i < 6; i++) {
+		if (int_sts & 0x1)
+			dma_chan_irq(&hsu->chans[i]);
+		int_sts >>= 1;
+	}
+
+	return IRQ_HANDLED;
+}
+
+static unsigned int serial_hsu_tx_empty(struct uart_port *port)
+{
+	struct uart_hsu_port *up =
+		container_of(port, struct uart_hsu_port, port);
+	unsigned long flags;
+	unsigned int ret;
+
+	spin_lock_irqsave(&up->port.lock, flags);
+	ret = serial_in(up, UART_LSR) & UART_LSR_TEMT ? TIOCSER_TEMT : 0;
+	spin_unlock_irqrestore(&up->port.lock, flags);
+
+	return ret;
+}
+
+static unsigned int serial_hsu_get_mctrl(struct uart_port *port)
+{
+	struct uart_hsu_port *up =
+		container_of(port, struct uart_hsu_port, port);
+	unsigned char status;
+	unsigned int ret;
+
+	status = serial_in(up, UART_MSR);
+
+	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 serial_hsu_set_mctrl(struct uart_port *port, unsigned int mctrl)
+{
+	struct uart_hsu_port *up =
+		container_of(port, struct uart_hsu_port, port);
+	unsigned char mcr = 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 |= up->mcr;
+
+	serial_out(up, UART_MCR, mcr);
+}
+
+static void serial_hsu_break_ctl(struct uart_port *port, int break_state)
+{
+	struct uart_hsu_port *up =
+		container_of(port, struct uart_hsu_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;
+	serial_out(up, UART_LCR, up->lcr);
+	spin_unlock_irqrestore(&up->port.lock, flags);
+}
+
+/*
+ * What special to do:
+ * 1. chose the 64B fifo mode
+ * 2. start dma or pio depends on configuration
+ * 3. we only allocate dma memory when needed
+ */
+static int serial_hsu_startup(struct uart_port *port)
+{
+	struct uart_hsu_port *up =
+		container_of(port, struct uart_hsu_port, port);
+	unsigned long flags;
+
+	pm_runtime_get_sync(up->dev);
+
+	/*
+	 * Clear the FIFO buffers and disable them.
+	 * (they will be reenabled in set_termios())
+	 */
+	serial_out(up, UART_FCR, UART_FCR_ENABLE_FIFO);
+	serial_out(up, UART_FCR, UART_FCR_ENABLE_FIFO |
+			UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT);
+	serial_out(up, UART_FCR, 0);
+
+	/* Clear the interrupt registers. */
+	(void) serial_in(up, UART_LSR);
+	(void) serial_in(up, UART_RX);
+	(void) serial_in(up, UART_IIR);
+	(void) serial_in(up, UART_MSR);
+
+	/* Now, initialize the UART, default is 8n1 */
+	serial_out(up, UART_LCR, UART_LCR_WLEN8);
+
+	spin_lock_irqsave(&up->port.lock, flags);
+
+	up->port.mctrl |= TIOCM_OUT2;
+	serial_hsu_set_mctrl(&up->port, up->port.mctrl);
+
+	/*
+	 * Finally, enable interrupts.  Note: Modem status interrupts
+	 * are set via set_termios(), which will be occurring imminently
+	 * anyway, so we don't enable them here.
+	 */
+	if (!up->use_dma)
+		up->ier = UART_IER_RLSI | UART_IER_RDI | UART_IER_RTOIE;
+	else
+		up->ier = 0;
+	serial_out(up, UART_IER, up->ier);
+
+	spin_unlock_irqrestore(&up->port.lock, flags);
+
+	/* DMA init */
+	if (up->use_dma) {
+		struct hsu_dma_buffer *dbuf;
+		struct circ_buf *xmit = &port->state->xmit;
+
+		up->dma_tx_on = 0;
+
+		/* First allocate the RX buffer */
+		dbuf = &up->rxbuf;
+		dbuf->buf = kzalloc(HSU_DMA_BUF_SIZE, GFP_KERNEL);
+		if (!dbuf->buf) {
+			up->use_dma = 0;
+			goto exit;
+		}
+		dbuf->dma_addr = dma_map_single(port->dev,
+						dbuf->buf,
+						HSU_DMA_BUF_SIZE,
+						DMA_FROM_DEVICE);
+		dbuf->dma_size = HSU_DMA_BUF_SIZE;
+
+		/* Start the RX channel right now */
+		hsu_dma_start_rx_chan(up->rxc, dbuf);
+
+		/* Next init the TX DMA */
+		dbuf = &up->txbuf;
+		dbuf->buf = xmit->buf;
+		dbuf->dma_addr = dma_map_single(port->dev,
+					       dbuf->buf,
+					       UART_XMIT_SIZE,
+					       DMA_TO_DEVICE);
+		dbuf->dma_size = UART_XMIT_SIZE;
+
+		/* This should not be changed all around */
+		chan_writel(up->txc, HSU_CH_BSR, 32);
+		chan_writel(up->txc, HSU_CH_MOTSR, 4);
+		dbuf->ofs = 0;
+	}
+
+exit:
+	 /* And clear the interrupt registers again for luck. */
+	(void) serial_in(up, UART_LSR);
+	(void) serial_in(up, UART_RX);
+	(void) serial_in(up, UART_IIR);
+	(void) serial_in(up, UART_MSR);
+
+	up->running = 1;
+	return 0;
+}
+
+static void serial_hsu_shutdown(struct uart_port *port)
+{
+	struct uart_hsu_port *up =
+		container_of(port, struct uart_hsu_port, port);
+	unsigned long flags;
+
+	/* Disable interrupts from this port */
+	up->ier = 0;
+	serial_out(up, UART_IER, 0);
+	up->running = 0;
+
+	spin_lock_irqsave(&up->port.lock, flags);
+	up->port.mctrl &= ~TIOCM_OUT2;
+	serial_hsu_set_mctrl(&up->port, up->port.mctrl);
+	spin_unlock_irqrestore(&up->port.lock, flags);
+
+	/* Disable break condition and FIFOs */
+	serial_out(up, UART_LCR, serial_in(up, UART_LCR) & ~UART_LCR_SBC);
+	serial_out(up, UART_FCR, UART_FCR_ENABLE_FIFO |
+				  UART_FCR_CLEAR_RCVR |
+				  UART_FCR_CLEAR_XMIT);
+	serial_out(up, UART_FCR, 0);
+
+	pm_runtime_put(up->dev);
+}
+
+static void
+serial_hsu_set_termios(struct uart_port *port, struct ktermios *termios,
+		       struct ktermios *old)
+{
+	struct uart_hsu_port *up =
+			container_of(port, struct uart_hsu_port, port);
+	unsigned char cval, fcr = 0;
+	unsigned long flags;
+	unsigned int baud, quot;
+	u32 ps, mul;
+
+	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;
+	}
+
+	/* CMSPAR isn't supported by this driver */
+	termios->c_cflag &= ~CMSPAR;
+
+	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;
+
+	/*
+	 * The base clk is 50Mhz, and the baud rate come from:
+	 *	baud = 50M * MUL / (DIV * PS * DLAB)
+	 *
+	 * For those basic low baud rate we can get the direct
+	 * scalar from 2746800, like 115200 = 2746800/24. For those
+	 * higher baud rate, we handle them case by case, mainly by
+	 * adjusting the MUL/PS registers, and DIV register is kept
+	 * as default value 0x3d09 to make things simple
+	 */
+	baud = uart_get_baud_rate(port, termios, old, 0, 4000000);
+
+	quot = 1;
+	ps = 0x10;
+	mul = 0x3600;
+	switch (baud) {
+	case 3500000:
+		mul = 0x3345;
+		ps = 0xC;
+		break;
+	case 1843200:
+		mul = 0x2400;
+		break;
+	case 3000000:
+	case 2500000:
+	case 2000000:
+	case 1500000:
+	case 1000000:
+	case 500000:
+		/* mul/ps/quot = 0x9C4/0x10/0x1 will make a 500000 bps */
+		mul = baud / 500000 * 0x9C4;
+		break;
+	default:
+		/* Use uart_get_divisor to get quot for other baud rates */
+		quot = 0;
+	}
+
+	if (!quot)
+		quot = uart_get_divisor(port, baud);
+
+	if ((up->port.uartclk / quot) < (2400 * 16))
+		fcr = UART_FCR_ENABLE_FIFO | UART_FCR_HSU_64_1B;
+	else if ((up->port.uartclk / quot) < (230400 * 16))
+		fcr = UART_FCR_ENABLE_FIFO | UART_FCR_HSU_64_16B;
+	else
+		fcr = UART_FCR_ENABLE_FIFO | UART_FCR_HSU_64_32B;
+
+	fcr |= UART_FCR_HSU_64B_FIFO;
+
+	/*
+	 * 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;
+
+	/* Characters 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, disable
+	 * MSI by default
+	 */
+	up->ier &= ~UART_IER_MSI;
+	if (UART_ENABLE_MS(&up->port, termios->c_cflag))
+		up->ier |= UART_IER_MSI;
+
+	serial_out(up, UART_IER, up->ier);
+
+	if (termios->c_cflag & CRTSCTS)
+		up->mcr |= UART_MCR_AFE | UART_MCR_RTS;
+	else
+		up->mcr &= ~UART_MCR_AFE;
+
+	serial_out(up, UART_LCR, cval | UART_LCR_DLAB);	/* set DLAB */
+	serial_out(up, UART_DLL, quot & 0xff);		/* LS of divisor */
+	serial_out(up, UART_DLM, quot >> 8);		/* MS of divisor */
+	serial_out(up, UART_LCR, cval);			/* reset DLAB */
+	serial_out(up, UART_MUL, mul);			/* set MUL */
+	serial_out(up, UART_PS, ps);			/* set PS */
+	up->lcr = cval;					/* Save LCR */
+	serial_hsu_set_mctrl(&up->port, up->port.mctrl);
+	serial_out(up, UART_FCR, fcr);
+	spin_unlock_irqrestore(&up->port.lock, flags);
+}
+
+static void
+serial_hsu_pm(struct uart_port *port, unsigned int state,
+	      unsigned int oldstate)
+{
+}
+
+static void serial_hsu_release_port(struct uart_port *port)
+{
+}
+
+static int serial_hsu_request_port(struct uart_port *port)
+{
+	return 0;
+}
+
+static void serial_hsu_config_port(struct uart_port *port, int flags)
+{
+	struct uart_hsu_port *up =
+		container_of(port, struct uart_hsu_port, port);
+	up->port.type = PORT_MFD;
+}
+
+static int
+serial_hsu_verify_port(struct uart_port *port, struct serial_struct *ser)
+{
+	/* We don't want the core code to modify any port params */
+	return -EINVAL;
+}
+
+static const char *
+serial_hsu_type(struct uart_port *port)
+{
+	struct uart_hsu_port *up =
+		container_of(port, struct uart_hsu_port, port);
+	return up->name;
+}
+
+/* Mainly for uart console use */
+static struct uart_hsu_port *serial_hsu_ports[3];
+static struct uart_driver serial_hsu_reg;
+
+#ifdef CONFIG_SERIAL_MFD_HSU_CONSOLE
+
+#define BOTH_EMPTY (UART_LSR_TEMT | UART_LSR_THRE)
+
+/* Wait for transmitter & holding register to empty */
+static inline void wait_for_xmitr(struct uart_hsu_port *up)
+{
+	unsigned int status, tmout = 1000;
+
+	/* Wait up to 1ms for the character to be sent. */
+	do {
+		status = serial_in(up, UART_LSR);
+
+		if (status & UART_LSR_BI)
+			up->lsr_break_flag = UART_LSR_BI;
+
+		if (--tmout == 0)
+			break;
+		udelay(1);
+	} while (!(status & BOTH_EMPTY));
+
+	/* Wait up to 1s for flow control if necessary */
+	if (up->port.flags & UPF_CONS_FLOW) {
+		tmout = 1000000;
+		while (--tmout &&
+		       ((serial_in(up, UART_MSR) & UART_MSR_CTS) == 0))
+			udelay(1);
+	}
+}
+
+static void serial_hsu_console_putchar(struct uart_port *port, int ch)
+{
+	struct uart_hsu_port *up =
+		container_of(port, struct uart_hsu_port, port);
+
+	wait_for_xmitr(up);
+	serial_out(up, UART_TX, ch);
+}
+
+/*
+ * Print a string to the serial port trying not to disturb
+ * any possible real use of the port...
+ *
+ *	The console_lock must be held when we get here.
+ */
+static void
+serial_hsu_console_write(struct console *co, const char *s, unsigned int count)
+{
+	struct uart_hsu_port *up = serial_hsu_ports[co->index];
+	unsigned long flags;
+	unsigned int ier;
+	int locked = 1;
+
+	local_irq_save(flags);
+	if (up->port.sysrq)
+		locked = 0;
+	else if (oops_in_progress) {
+		locked = spin_trylock(&up->port.lock);
+	} else
+		spin_lock(&up->port.lock);
+
+	/* First save the IER then disable the interrupts */
+	ier = serial_in(up, UART_IER);
+	serial_out(up, UART_IER, 0);
+
+	uart_console_write(&up->port, s, count, serial_hsu_console_putchar);
+
+	/*
+	 * Finally, wait for transmitter to become empty
+	 * and restore the IER
+	 */
+	wait_for_xmitr(up);
+	serial_out(up, UART_IER, ier);
+
+	if (locked)
+		spin_unlock(&up->port.lock);
+	local_irq_restore(flags);
+}
+
+static struct console serial_hsu_console;
+
+static int __init
+serial_hsu_console_setup(struct console *co, char *options)
+{
+	struct uart_hsu_port *up;
+	int baud = 115200;
+	int bits = 8;
+	int parity = 'n';
+	int flow = 'n';
+
+	if (co->index == -1 || co->index >= serial_hsu_reg.nr)
+		co->index = 0;
+	up = serial_hsu_ports[co->index];
+	if (!up)
+		return -ENODEV;
+
+	if (options)
+		uart_parse_options(options, &baud, &parity, &bits, &flow);
+
+	return uart_set_options(&up->port, co, baud, parity, bits, flow);
+}
+
+static struct console serial_hsu_console = {
+	.name		= "ttyMFD",
+	.write		= serial_hsu_console_write,
+	.device		= uart_console_device,
+	.setup		= serial_hsu_console_setup,
+	.flags		= CON_PRINTBUFFER,
+	.index		= -1,
+	.data		= &serial_hsu_reg,
+};
+
+#define SERIAL_HSU_CONSOLE	(&serial_hsu_console)
+#else
+#define SERIAL_HSU_CONSOLE	NULL
+#endif
+
+struct uart_ops serial_hsu_pops = {
+	.tx_empty	= serial_hsu_tx_empty,
+	.set_mctrl	= serial_hsu_set_mctrl,
+	.get_mctrl	= serial_hsu_get_mctrl,
+	.stop_tx	= serial_hsu_stop_tx,
+	.start_tx	= serial_hsu_start_tx,
+	.stop_rx	= serial_hsu_stop_rx,
+	.enable_ms	= serial_hsu_enable_ms,
+	.break_ctl	= serial_hsu_break_ctl,
+	.startup	= serial_hsu_startup,
+	.shutdown	= serial_hsu_shutdown,
+	.set_termios	= serial_hsu_set_termios,
+	.pm		= serial_hsu_pm,
+	.type		= serial_hsu_type,
+	.release_port	= serial_hsu_release_port,
+	.request_port	= serial_hsu_request_port,
+	.config_port	= serial_hsu_config_port,
+	.verify_port	= serial_hsu_verify_port,
+};
+
+static struct uart_driver serial_hsu_reg = {
+	.owner		= THIS_MODULE,
+	.driver_name	= "MFD serial",
+	.dev_name	= "ttyMFD",
+	.major		= TTY_MAJOR,
+	.minor		= 128,
+	.nr		= 3,
+	.cons		= SERIAL_HSU_CONSOLE,
+};
+
+#ifdef CONFIG_PM
+static int serial_hsu_suspend(struct pci_dev *pdev, pm_message_t state)
+{
+	void *priv = pci_get_drvdata(pdev);
+	struct uart_hsu_port *up;
+
+	/* Make sure this is not the internal dma controller */
+	if (priv && (pdev->device != 0x081E)) {
+		up = priv;
+		uart_suspend_port(&serial_hsu_reg, &up->port);
+	}
+
+	pci_save_state(pdev);
+	pci_set_power_state(pdev, pci_choose_state(pdev, state));
+        return 0;
+}
+
+static int serial_hsu_resume(struct pci_dev *pdev)
+{
+	void *priv = pci_get_drvdata(pdev);
+	struct uart_hsu_port *up;
+	int ret;
+
+	pci_set_power_state(pdev, PCI_D0);
+	pci_restore_state(pdev);
+
+	ret = pci_enable_device(pdev);
+	if (ret)
+		dev_warn(&pdev->dev,
+			"HSU: can't re-enable device, try to continue\n");
+
+	if (priv && (pdev->device != 0x081E)) {
+		up = priv;
+		uart_resume_port(&serial_hsu_reg, &up->port);
+	}
+	return 0;
+}
+#else
+#define serial_hsu_suspend	NULL
+#define serial_hsu_resume	NULL
+#endif
+
+#ifdef CONFIG_PM_RUNTIME
+static int serial_hsu_runtime_idle(struct device *dev)
+{
+	int err;
+
+	err = pm_schedule_suspend(dev, 500);
+	if (err)
+		return -EBUSY;
+
+	return 0;
+}
+
+static int serial_hsu_runtime_suspend(struct device *dev)
+{
+	return 0;
+}
+
+static int serial_hsu_runtime_resume(struct device *dev)
+{
+	return 0;
+}
+#else
+#define serial_hsu_runtime_idle		NULL
+#define serial_hsu_runtime_suspend	NULL
+#define serial_hsu_runtime_resume	NULL
+#endif
+
+static const struct dev_pm_ops serial_hsu_pm_ops = {
+	.runtime_suspend = serial_hsu_runtime_suspend,
+	.runtime_resume = serial_hsu_runtime_resume,
+	.runtime_idle = serial_hsu_runtime_idle,
+};
+
+/* temp global pointer before we settle down on using one or four PCI dev */
+static struct hsu_port *phsu;
+
+static int serial_hsu_probe(struct pci_dev *pdev,
+				const struct pci_device_id *ent)
+{
+	struct uart_hsu_port *uport;
+	int index, ret;
+
+	printk(KERN_INFO "HSU: found PCI Serial controller(ID: %04x:%04x)\n",
+		pdev->vendor, pdev->device);
+
+	switch (pdev->device) {
+	case 0x081B:
+		index = 0;
+		break;
+	case 0x081C:
+		index = 1;
+		break;
+	case 0x081D:
+		index = 2;
+		break;
+	case 0x081E:
+		/* internal DMA controller */
+		index = 3;
+		break;
+	default:
+		dev_err(&pdev->dev, "HSU: out of index!");
+		return -ENODEV;
+	}
+
+	ret = pci_enable_device(pdev);
+	if (ret)
+		return ret;
+
+	if (index == 3) {
+		/* DMA controller */
+		ret = request_irq(pdev->irq, dma_irq, 0, "hsu_dma", phsu);
+		if (ret) {
+			dev_err(&pdev->dev, "can not get IRQ\n");
+			goto err_disable;
+		}
+		pci_set_drvdata(pdev, phsu);
+	} else {
+		/* UART port 0~2 */
+		uport = &phsu->port[index];
+		uport->port.irq = pdev->irq;
+		uport->port.dev = &pdev->dev;
+		uport->dev = &pdev->dev;
+
+		ret = request_irq(pdev->irq, port_irq, 0, uport->name, uport);
+		if (ret) {
+			dev_err(&pdev->dev, "can not get IRQ\n");
+			goto err_disable;
+		}
+		uart_add_one_port(&serial_hsu_reg, &uport->port);
+
+		pci_set_drvdata(pdev, uport);
+	}
+
+	pm_runtime_put_noidle(&pdev->dev);
+	pm_runtime_allow(&pdev->dev);
+
+	return 0;
+
+err_disable:
+	pci_disable_device(pdev);
+	return ret;
+}
+
+static void hsu_global_init(void)
+{
+	struct hsu_port *hsu;
+	struct uart_hsu_port *uport;
+	struct hsu_dma_chan *dchan;
+	int i, ret;
+
+	hsu = kzalloc(sizeof(struct hsu_port), GFP_KERNEL);
+	if (!hsu)
+		return;
+
+	/* Get basic io resource and map it */
+	hsu->paddr = 0xffa28000;
+	hsu->iolen = 0x1000;
+
+	if (!(request_mem_region(hsu->paddr, hsu->iolen, "HSU global")))
+		pr_warning("HSU: error in request mem region\n");
+
+	hsu->reg = ioremap_nocache((unsigned long)hsu->paddr, hsu->iolen);
+	if (!hsu->reg) {
+		pr_err("HSU: error in ioremap\n");
+		ret = -ENOMEM;
+		goto err_free_region;
+	}
+
+	/* Initialise the 3 UART ports */
+	uport = hsu->port;
+	for (i = 0; i < 3; i++) {
+		uport->port.type = PORT_MFD;
+		uport->port.iotype = UPIO_MEM;
+		uport->port.mapbase = (resource_size_t)hsu->paddr
+					+ HSU_PORT_REG_OFFSET
+					+ i * HSU_PORT_REG_LENGTH;
+		uport->port.membase = hsu->reg + HSU_PORT_REG_OFFSET
+					+ i * HSU_PORT_REG_LENGTH;
+
+		sprintf(uport->name, "hsu_port%d", i);
+		uport->port.fifosize = 64;
+		uport->port.ops = &serial_hsu_pops;
+		uport->port.line = i;
+		uport->port.flags = UPF_IOREMAP;
+		/* set the scalable maxim support rate to 2746800 bps */
+		uport->port.uartclk = 115200 * 24 * 16;
+
+		uport->running = 0;
+		uport->txc = &hsu->chans[i * 2];
+		uport->rxc = &hsu->chans[i * 2 + 1];
+
+		serial_hsu_ports[i] = uport;
+		uport->index = i;
+
+		if (hsu_dma_enable & (1<<i))
+			uport->use_dma = 1;
+		else
+			uport->use_dma = 0;
+
+		uport++;
+	}
+
+	/* Initialise 6 dma channels */
+	dchan = hsu->chans;
+	for (i = 0; i < 6; i++) {
+		dchan->id = i;
+		dchan->dirt = (i & 0x1) ? DMA_FROM_DEVICE : DMA_TO_DEVICE;
+		dchan->uport = &hsu->port[i/2];
+		dchan->reg = hsu->reg + HSU_DMA_CHANS_REG_OFFSET +
+				i * HSU_DMA_CHANS_REG_LENGTH;
+
+		dchan++;
+	}
+
+	phsu = hsu;
+	hsu_debugfs_init(hsu);
+	return;
+
+err_free_region:
+	release_mem_region(hsu->paddr, hsu->iolen);
+	kfree(hsu);
+	return;
+}
+
+static void serial_hsu_remove(struct pci_dev *pdev)
+{
+	void *priv = pci_get_drvdata(pdev);
+	struct uart_hsu_port *up;
+
+	if (!priv)
+		return;
+
+	pm_runtime_forbid(&pdev->dev);
+	pm_runtime_get_noresume(&pdev->dev);
+
+	/* For port 0/1/2, priv is the address of uart_hsu_port */
+	if (pdev->device != 0x081E) {
+		up = priv;
+		uart_remove_one_port(&serial_hsu_reg, &up->port);
+	}
+
+	pci_set_drvdata(pdev, NULL);
+	free_irq(pdev->irq, priv);
+	pci_disable_device(pdev);
+}
+
+/* First 3 are UART ports, and the 4th is the DMA */
+static const struct pci_device_id pci_ids[] __devinitconst = {
+	{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x081B) },
+	{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x081C) },
+	{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x081D) },
+	{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x081E) },
+	{},
+};
+
+static struct pci_driver hsu_pci_driver = {
+	.name =		"HSU serial",
+	.id_table =	pci_ids,
+	.probe =	serial_hsu_probe,
+	.remove =	__devexit_p(serial_hsu_remove),
+	.suspend =	serial_hsu_suspend,
+	.resume	=	serial_hsu_resume,
+	.driver = {
+		.pm = &serial_hsu_pm_ops,
+	},
+};
+
+static int __init hsu_pci_init(void)
+{
+	int ret;
+
+	hsu_global_init();
+
+	ret = uart_register_driver(&serial_hsu_reg);
+	if (ret)
+		return ret;
+
+	return pci_register_driver(&hsu_pci_driver);
+}
+
+static void __exit hsu_pci_exit(void)
+{
+	pci_unregister_driver(&hsu_pci_driver);
+	uart_unregister_driver(&serial_hsu_reg);
+
+	hsu_debugfs_remove(phsu);
+
+	kfree(phsu);
+}
+
+module_init(hsu_pci_init);
+module_exit(hsu_pci_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:medfield-hsu");
diff --git a/ap/os/linux/linux-3.4.x/drivers/tty/serial/mpc52xx_uart.c b/ap/os/linux/linux-3.4.x/drivers/tty/serial/mpc52xx_uart.c
new file mode 100644
index 0000000..bedac0d
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/tty/serial/mpc52xx_uart.c
@@ -0,0 +1,1526 @@
+/*
+ * Driver for the PSC of the Freescale MPC52xx PSCs configured as UARTs.
+ *
+ * FIXME According to the usermanual the status bits in the status register
+ * are only updated when the peripherals access the FIFO and not when the
+ * CPU access them. So since we use this bits to know when we stop writing
+ * and reading, they may not be updated in-time and a race condition may
+ * exists. But I haven't be able to prove this and I don't care. But if
+ * any problem arises, it might worth checking. The TX/RX FIFO Stats
+ * registers should be used in addition.
+ * Update: Actually, they seem updated ... At least the bits we use.
+ *
+ *
+ * Maintainer : Sylvain Munaut <tnt@246tNt.com>
+ *
+ * Some of the code has been inspired/copied from the 2.4 code written
+ * by Dale Farnsworth <dfarnsworth@mvista.com>.
+ *
+ * Copyright (C) 2008 Freescale Semiconductor Inc.
+ *                    John Rigby <jrigby@gmail.com>
+ * Added support for MPC5121
+ * Copyright (C) 2006 Secret Lab Technologies Ltd.
+ *                    Grant Likely <grant.likely@secretlab.ca>
+ * Copyright (C) 2004-2006 Sylvain Munaut <tnt@246tNt.com>
+ * Copyright (C) 2003 MontaVista, Software, Inc.
+ *
+ * This file is licensed under the terms of the GNU General Public License
+ * version 2. This program is licensed "as is" without any warranty of any
+ * kind, whether express or implied.
+ */
+
+#undef DEBUG
+
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
+#include <linux/serial.h>
+#include <linux/sysrq.h>
+#include <linux/console.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <linux/clk.h>
+
+#include <asm/mpc52xx.h>
+#include <asm/mpc52xx_psc.h>
+
+#if defined(CONFIG_SERIAL_MPC52xx_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ)
+#define SUPPORT_SYSRQ
+#endif
+
+#include <linux/serial_core.h>
+
+
+/* We've been assigned a range on the "Low-density serial ports" major */
+#define SERIAL_PSC_MAJOR	204
+#define SERIAL_PSC_MINOR	148
+
+
+#define ISR_PASS_LIMIT 256	/* Max number of iteration in the interrupt */
+
+
+static struct uart_port mpc52xx_uart_ports[MPC52xx_PSC_MAXNUM];
+	/* Rem: - We use the read_status_mask as a shadow of
+	 *        psc->mpc52xx_psc_imr
+	 *      - It's important that is array is all zero on start as we
+	 *        use it to know if it's initialized or not ! If it's not sure
+	 *        it's cleared, then a memset(...,0,...) should be added to
+	 *        the console_init
+	 */
+
+/* lookup table for matching device nodes to index numbers */
+static struct device_node *mpc52xx_uart_nodes[MPC52xx_PSC_MAXNUM];
+
+static void mpc52xx_uart_of_enumerate(void);
+
+
+#define PSC(port) ((struct mpc52xx_psc __iomem *)((port)->membase))
+
+
+/* Forward declaration of the interruption handling routine */
+static irqreturn_t mpc52xx_uart_int(int irq, void *dev_id);
+static irqreturn_t mpc5xxx_uart_process_int(struct uart_port *port);
+
+
+/* Simple macro to test if a port is console or not. This one is taken
+ * for serial_core.c and maybe should be moved to serial_core.h ? */
+#ifdef CONFIG_SERIAL_CORE_CONSOLE
+#define uart_console(port) \
+	((port)->cons && (port)->cons->index == (port)->line)
+#else
+#define uart_console(port)	(0)
+#endif
+
+/* ======================================================================== */
+/* PSC fifo operations for isolating differences between 52xx and 512x      */
+/* ======================================================================== */
+
+struct psc_ops {
+	void		(*fifo_init)(struct uart_port *port);
+	int		(*raw_rx_rdy)(struct uart_port *port);
+	int		(*raw_tx_rdy)(struct uart_port *port);
+	int		(*rx_rdy)(struct uart_port *port);
+	int		(*tx_rdy)(struct uart_port *port);
+	int		(*tx_empty)(struct uart_port *port);
+	void		(*stop_rx)(struct uart_port *port);
+	void		(*start_tx)(struct uart_port *port);
+	void		(*stop_tx)(struct uart_port *port);
+	void		(*rx_clr_irq)(struct uart_port *port);
+	void		(*tx_clr_irq)(struct uart_port *port);
+	void		(*write_char)(struct uart_port *port, unsigned char c);
+	unsigned char	(*read_char)(struct uart_port *port);
+	void		(*cw_disable_ints)(struct uart_port *port);
+	void		(*cw_restore_ints)(struct uart_port *port);
+	unsigned int	(*set_baudrate)(struct uart_port *port,
+					struct ktermios *new,
+					struct ktermios *old);
+	int		(*clock)(struct uart_port *port, int enable);
+	int		(*fifoc_init)(void);
+	void		(*fifoc_uninit)(void);
+	void		(*get_irq)(struct uart_port *, struct device_node *);
+	irqreturn_t	(*handle_irq)(struct uart_port *port);
+};
+
+/* setting the prescaler and divisor reg is common for all chips */
+static inline void mpc52xx_set_divisor(struct mpc52xx_psc __iomem *psc,
+				       u16 prescaler, unsigned int divisor)
+{
+	/* select prescaler */
+	out_be16(&psc->mpc52xx_psc_clock_select, prescaler);
+	out_8(&psc->ctur, divisor >> 8);
+	out_8(&psc->ctlr, divisor & 0xff);
+}
+
+#ifdef CONFIG_PPC_MPC52xx
+#define FIFO_52xx(port) ((struct mpc52xx_psc_fifo __iomem *)(PSC(port)+1))
+static void mpc52xx_psc_fifo_init(struct uart_port *port)
+{
+	struct mpc52xx_psc __iomem *psc = PSC(port);
+	struct mpc52xx_psc_fifo __iomem *fifo = FIFO_52xx(port);
+
+	out_8(&fifo->rfcntl, 0x00);
+	out_be16(&fifo->rfalarm, 0x1ff);
+	out_8(&fifo->tfcntl, 0x07);
+	out_be16(&fifo->tfalarm, 0x80);
+
+	port->read_status_mask |= MPC52xx_PSC_IMR_RXRDY | MPC52xx_PSC_IMR_TXRDY;
+	out_be16(&psc->mpc52xx_psc_imr, port->read_status_mask);
+}
+
+static int mpc52xx_psc_raw_rx_rdy(struct uart_port *port)
+{
+	return in_be16(&PSC(port)->mpc52xx_psc_status)
+	    & MPC52xx_PSC_SR_RXRDY;
+}
+
+static int mpc52xx_psc_raw_tx_rdy(struct uart_port *port)
+{
+	return in_be16(&PSC(port)->mpc52xx_psc_status)
+	    & MPC52xx_PSC_SR_TXRDY;
+}
+
+
+static int mpc52xx_psc_rx_rdy(struct uart_port *port)
+{
+	return in_be16(&PSC(port)->mpc52xx_psc_isr)
+	    & port->read_status_mask
+	    & MPC52xx_PSC_IMR_RXRDY;
+}
+
+static int mpc52xx_psc_tx_rdy(struct uart_port *port)
+{
+	return in_be16(&PSC(port)->mpc52xx_psc_isr)
+	    & port->read_status_mask
+	    & MPC52xx_PSC_IMR_TXRDY;
+}
+
+static int mpc52xx_psc_tx_empty(struct uart_port *port)
+{
+	return in_be16(&PSC(port)->mpc52xx_psc_status)
+	    & MPC52xx_PSC_SR_TXEMP;
+}
+
+static void mpc52xx_psc_start_tx(struct uart_port *port)
+{
+	port->read_status_mask |= MPC52xx_PSC_IMR_TXRDY;
+	out_be16(&PSC(port)->mpc52xx_psc_imr, port->read_status_mask);
+}
+
+static void mpc52xx_psc_stop_tx(struct uart_port *port)
+{
+	port->read_status_mask &= ~MPC52xx_PSC_IMR_TXRDY;
+	out_be16(&PSC(port)->mpc52xx_psc_imr, port->read_status_mask);
+}
+
+static void mpc52xx_psc_stop_rx(struct uart_port *port)
+{
+	port->read_status_mask &= ~MPC52xx_PSC_IMR_RXRDY;
+	out_be16(&PSC(port)->mpc52xx_psc_imr, port->read_status_mask);
+}
+
+static void mpc52xx_psc_rx_clr_irq(struct uart_port *port)
+{
+}
+
+static void mpc52xx_psc_tx_clr_irq(struct uart_port *port)
+{
+}
+
+static void mpc52xx_psc_write_char(struct uart_port *port, unsigned char c)
+{
+	out_8(&PSC(port)->mpc52xx_psc_buffer_8, c);
+}
+
+static unsigned char mpc52xx_psc_read_char(struct uart_port *port)
+{
+	return in_8(&PSC(port)->mpc52xx_psc_buffer_8);
+}
+
+static void mpc52xx_psc_cw_disable_ints(struct uart_port *port)
+{
+	out_be16(&PSC(port)->mpc52xx_psc_imr, 0);
+}
+
+static void mpc52xx_psc_cw_restore_ints(struct uart_port *port)
+{
+	out_be16(&PSC(port)->mpc52xx_psc_imr, port->read_status_mask);
+}
+
+static unsigned int mpc5200_psc_set_baudrate(struct uart_port *port,
+					     struct ktermios *new,
+					     struct ktermios *old)
+{
+	unsigned int baud;
+	unsigned int divisor;
+
+	/* The 5200 has a fixed /32 prescaler, uartclk contains the ipb freq */
+	baud = uart_get_baud_rate(port, new, old,
+				  port->uartclk / (32 * 0xffff) + 1,
+				  port->uartclk / 32);
+	divisor = (port->uartclk + 16 * baud) / (32 * baud);
+
+	/* enable the /32 prescaler and set the divisor */
+	mpc52xx_set_divisor(PSC(port), 0xdd00, divisor);
+	return baud;
+}
+
+static unsigned int mpc5200b_psc_set_baudrate(struct uart_port *port,
+					      struct ktermios *new,
+					      struct ktermios *old)
+{
+	unsigned int baud;
+	unsigned int divisor;
+	u16 prescaler;
+
+	/* The 5200B has a selectable /4 or /32 prescaler, uartclk contains the
+	 * ipb freq */
+	baud = uart_get_baud_rate(port, new, old,
+				  port->uartclk / (32 * 0xffff) + 1,
+				  port->uartclk / 4);
+	divisor = (port->uartclk + 2 * baud) / (4 * baud);
+
+	/* select the proper prescaler and set the divisor
+	 * prefer high prescaler for more tolerance on low baudrates */
+	if (divisor > 0xffff || baud <= 115200) {
+		divisor = (divisor + 4) / 8;
+		prescaler = 0xdd00; /* /32 */
+	} else
+		prescaler = 0xff00; /* /4 */
+	mpc52xx_set_divisor(PSC(port), prescaler, divisor);
+	return baud;
+}
+
+static void mpc52xx_psc_get_irq(struct uart_port *port, struct device_node *np)
+{
+	port->irqflags = 0;
+	port->irq = irq_of_parse_and_map(np, 0);
+}
+
+/* 52xx specific interrupt handler. The caller holds the port lock */
+static irqreturn_t mpc52xx_psc_handle_irq(struct uart_port *port)
+{
+	return mpc5xxx_uart_process_int(port);
+}
+
+static struct psc_ops mpc52xx_psc_ops = {
+	.fifo_init = mpc52xx_psc_fifo_init,
+	.raw_rx_rdy = mpc52xx_psc_raw_rx_rdy,
+	.raw_tx_rdy = mpc52xx_psc_raw_tx_rdy,
+	.rx_rdy = mpc52xx_psc_rx_rdy,
+	.tx_rdy = mpc52xx_psc_tx_rdy,
+	.tx_empty = mpc52xx_psc_tx_empty,
+	.stop_rx = mpc52xx_psc_stop_rx,
+	.start_tx = mpc52xx_psc_start_tx,
+	.stop_tx = mpc52xx_psc_stop_tx,
+	.rx_clr_irq = mpc52xx_psc_rx_clr_irq,
+	.tx_clr_irq = mpc52xx_psc_tx_clr_irq,
+	.write_char = mpc52xx_psc_write_char,
+	.read_char = mpc52xx_psc_read_char,
+	.cw_disable_ints = mpc52xx_psc_cw_disable_ints,
+	.cw_restore_ints = mpc52xx_psc_cw_restore_ints,
+	.set_baudrate = mpc5200_psc_set_baudrate,
+	.get_irq = mpc52xx_psc_get_irq,
+	.handle_irq = mpc52xx_psc_handle_irq,
+};
+
+static struct psc_ops mpc5200b_psc_ops = {
+	.fifo_init = mpc52xx_psc_fifo_init,
+	.raw_rx_rdy = mpc52xx_psc_raw_rx_rdy,
+	.raw_tx_rdy = mpc52xx_psc_raw_tx_rdy,
+	.rx_rdy = mpc52xx_psc_rx_rdy,
+	.tx_rdy = mpc52xx_psc_tx_rdy,
+	.tx_empty = mpc52xx_psc_tx_empty,
+	.stop_rx = mpc52xx_psc_stop_rx,
+	.start_tx = mpc52xx_psc_start_tx,
+	.stop_tx = mpc52xx_psc_stop_tx,
+	.rx_clr_irq = mpc52xx_psc_rx_clr_irq,
+	.tx_clr_irq = mpc52xx_psc_tx_clr_irq,
+	.write_char = mpc52xx_psc_write_char,
+	.read_char = mpc52xx_psc_read_char,
+	.cw_disable_ints = mpc52xx_psc_cw_disable_ints,
+	.cw_restore_ints = mpc52xx_psc_cw_restore_ints,
+	.set_baudrate = mpc5200b_psc_set_baudrate,
+	.get_irq = mpc52xx_psc_get_irq,
+	.handle_irq = mpc52xx_psc_handle_irq,
+};
+
+#endif /* CONFIG_MPC52xx */
+
+#ifdef CONFIG_PPC_MPC512x
+#define FIFO_512x(port) ((struct mpc512x_psc_fifo __iomem *)(PSC(port)+1))
+
+/* PSC FIFO Controller for mpc512x */
+struct psc_fifoc {
+	u32 fifoc_cmd;
+	u32 fifoc_int;
+	u32 fifoc_dma;
+	u32 fifoc_axe;
+	u32 fifoc_debug;
+};
+
+static struct psc_fifoc __iomem *psc_fifoc;
+static unsigned int psc_fifoc_irq;
+
+static void mpc512x_psc_fifo_init(struct uart_port *port)
+{
+	/* /32 prescaler */
+	out_be16(&PSC(port)->mpc52xx_psc_clock_select, 0xdd00);
+
+	out_be32(&FIFO_512x(port)->txcmd, MPC512x_PSC_FIFO_RESET_SLICE);
+	out_be32(&FIFO_512x(port)->txcmd, MPC512x_PSC_FIFO_ENABLE_SLICE);
+	out_be32(&FIFO_512x(port)->txalarm, 1);
+	out_be32(&FIFO_512x(port)->tximr, 0);
+
+	out_be32(&FIFO_512x(port)->rxcmd, MPC512x_PSC_FIFO_RESET_SLICE);
+	out_be32(&FIFO_512x(port)->rxcmd, MPC512x_PSC_FIFO_ENABLE_SLICE);
+	out_be32(&FIFO_512x(port)->rxalarm, 1);
+	out_be32(&FIFO_512x(port)->rximr, 0);
+
+	out_be32(&FIFO_512x(port)->tximr, MPC512x_PSC_FIFO_ALARM);
+	out_be32(&FIFO_512x(port)->rximr, MPC512x_PSC_FIFO_ALARM);
+}
+
+static int mpc512x_psc_raw_rx_rdy(struct uart_port *port)
+{
+	return !(in_be32(&FIFO_512x(port)->rxsr) & MPC512x_PSC_FIFO_EMPTY);
+}
+
+static int mpc512x_psc_raw_tx_rdy(struct uart_port *port)
+{
+	return !(in_be32(&FIFO_512x(port)->txsr) & MPC512x_PSC_FIFO_FULL);
+}
+
+static int mpc512x_psc_rx_rdy(struct uart_port *port)
+{
+	return in_be32(&FIFO_512x(port)->rxsr)
+	    & in_be32(&FIFO_512x(port)->rximr)
+	    & MPC512x_PSC_FIFO_ALARM;
+}
+
+static int mpc512x_psc_tx_rdy(struct uart_port *port)
+{
+	return in_be32(&FIFO_512x(port)->txsr)
+	    & in_be32(&FIFO_512x(port)->tximr)
+	    & MPC512x_PSC_FIFO_ALARM;
+}
+
+static int mpc512x_psc_tx_empty(struct uart_port *port)
+{
+	return in_be32(&FIFO_512x(port)->txsr)
+	    & MPC512x_PSC_FIFO_EMPTY;
+}
+
+static void mpc512x_psc_stop_rx(struct uart_port *port)
+{
+	unsigned long rx_fifo_imr;
+
+	rx_fifo_imr = in_be32(&FIFO_512x(port)->rximr);
+	rx_fifo_imr &= ~MPC512x_PSC_FIFO_ALARM;
+	out_be32(&FIFO_512x(port)->rximr, rx_fifo_imr);
+}
+
+static void mpc512x_psc_start_tx(struct uart_port *port)
+{
+	unsigned long tx_fifo_imr;
+
+	tx_fifo_imr = in_be32(&FIFO_512x(port)->tximr);
+	tx_fifo_imr |= MPC512x_PSC_FIFO_ALARM;
+	out_be32(&FIFO_512x(port)->tximr, tx_fifo_imr);
+}
+
+static void mpc512x_psc_stop_tx(struct uart_port *port)
+{
+	unsigned long tx_fifo_imr;
+
+	tx_fifo_imr = in_be32(&FIFO_512x(port)->tximr);
+	tx_fifo_imr &= ~MPC512x_PSC_FIFO_ALARM;
+	out_be32(&FIFO_512x(port)->tximr, tx_fifo_imr);
+}
+
+static void mpc512x_psc_rx_clr_irq(struct uart_port *port)
+{
+	out_be32(&FIFO_512x(port)->rxisr, in_be32(&FIFO_512x(port)->rxisr));
+}
+
+static void mpc512x_psc_tx_clr_irq(struct uart_port *port)
+{
+	out_be32(&FIFO_512x(port)->txisr, in_be32(&FIFO_512x(port)->txisr));
+}
+
+static void mpc512x_psc_write_char(struct uart_port *port, unsigned char c)
+{
+	out_8(&FIFO_512x(port)->txdata_8, c);
+}
+
+static unsigned char mpc512x_psc_read_char(struct uart_port *port)
+{
+	return in_8(&FIFO_512x(port)->rxdata_8);
+}
+
+static void mpc512x_psc_cw_disable_ints(struct uart_port *port)
+{
+	port->read_status_mask =
+		in_be32(&FIFO_512x(port)->tximr) << 16 |
+		in_be32(&FIFO_512x(port)->rximr);
+	out_be32(&FIFO_512x(port)->tximr, 0);
+	out_be32(&FIFO_512x(port)->rximr, 0);
+}
+
+static void mpc512x_psc_cw_restore_ints(struct uart_port *port)
+{
+	out_be32(&FIFO_512x(port)->tximr,
+		(port->read_status_mask >> 16) & 0x7f);
+	out_be32(&FIFO_512x(port)->rximr, port->read_status_mask & 0x7f);
+}
+
+static unsigned int mpc512x_psc_set_baudrate(struct uart_port *port,
+					     struct ktermios *new,
+					     struct ktermios *old)
+{
+	unsigned int baud;
+	unsigned int divisor;
+
+	/*
+	 * The "MPC5121e Microcontroller Reference Manual, Rev. 3" says on
+	 * pg. 30-10 that the chip supports a /32 and a /10 prescaler.
+	 * Furthermore, it states that "After reset, the prescaler by 10
+	 * for the UART mode is selected", but the reset register value is
+	 * 0x0000 which means a /32 prescaler. This is wrong.
+	 *
+	 * In reality using /32 prescaler doesn't work, as it is not supported!
+	 * Use /16 or /10 prescaler, see "MPC5121e Hardware Design Guide",
+	 * Chapter 4.1 PSC in UART Mode.
+	 * Calculate with a /16 prescaler here.
+	 */
+
+	/* uartclk contains the ips freq */
+	baud = uart_get_baud_rate(port, new, old,
+				  port->uartclk / (16 * 0xffff) + 1,
+				  port->uartclk / 16);
+	divisor = (port->uartclk + 8 * baud) / (16 * baud);
+
+	/* enable the /16 prescaler and set the divisor */
+	mpc52xx_set_divisor(PSC(port), 0xdd00, divisor);
+	return baud;
+}
+
+/* Init PSC FIFO Controller */
+static int __init mpc512x_psc_fifoc_init(void)
+{
+	struct device_node *np;
+
+	np = of_find_compatible_node(NULL, NULL,
+				     "fsl,mpc5121-psc-fifo");
+	if (!np) {
+		pr_err("%s: Can't find FIFOC node\n", __func__);
+		return -ENODEV;
+	}
+
+	psc_fifoc = of_iomap(np, 0);
+	if (!psc_fifoc) {
+		pr_err("%s: Can't map FIFOC\n", __func__);
+		of_node_put(np);
+		return -ENODEV;
+	}
+
+	psc_fifoc_irq = irq_of_parse_and_map(np, 0);
+	of_node_put(np);
+	if (psc_fifoc_irq == 0) {
+		pr_err("%s: Can't get FIFOC irq\n", __func__);
+		iounmap(psc_fifoc);
+		return -ENODEV;
+	}
+
+	return 0;
+}
+
+static void __exit mpc512x_psc_fifoc_uninit(void)
+{
+	iounmap(psc_fifoc);
+}
+
+/* 512x specific interrupt handler. The caller holds the port lock */
+static irqreturn_t mpc512x_psc_handle_irq(struct uart_port *port)
+{
+	unsigned long fifoc_int;
+	int psc_num;
+
+	/* Read pending PSC FIFOC interrupts */
+	fifoc_int = in_be32(&psc_fifoc->fifoc_int);
+
+	/* Check if it is an interrupt for this port */
+	psc_num = (port->mapbase & 0xf00) >> 8;
+	if (test_bit(psc_num, &fifoc_int) ||
+	    test_bit(psc_num + 16, &fifoc_int))
+		return mpc5xxx_uart_process_int(port);
+
+	return IRQ_NONE;
+}
+
+static int mpc512x_psc_clock(struct uart_port *port, int enable)
+{
+	struct clk *psc_clk;
+	int psc_num;
+	char clk_name[10];
+
+	if (uart_console(port))
+		return 0;
+
+	psc_num = (port->mapbase & 0xf00) >> 8;
+	snprintf(clk_name, sizeof(clk_name), "psc%d_clk", psc_num);
+	psc_clk = clk_get(port->dev, clk_name);
+	if (IS_ERR(psc_clk)) {
+		dev_err(port->dev, "Failed to get PSC clock entry!\n");
+		return -ENODEV;
+	}
+
+	dev_dbg(port->dev, "%s %sable\n", clk_name, enable ? "en" : "dis");
+
+	if (enable)
+		clk_enable(psc_clk);
+	else
+		clk_disable(psc_clk);
+
+	return 0;
+}
+
+static void mpc512x_psc_get_irq(struct uart_port *port, struct device_node *np)
+{
+	port->irqflags = IRQF_SHARED;
+	port->irq = psc_fifoc_irq;
+}
+
+static struct psc_ops mpc512x_psc_ops = {
+	.fifo_init = mpc512x_psc_fifo_init,
+	.raw_rx_rdy = mpc512x_psc_raw_rx_rdy,
+	.raw_tx_rdy = mpc512x_psc_raw_tx_rdy,
+	.rx_rdy = mpc512x_psc_rx_rdy,
+	.tx_rdy = mpc512x_psc_tx_rdy,
+	.tx_empty = mpc512x_psc_tx_empty,
+	.stop_rx = mpc512x_psc_stop_rx,
+	.start_tx = mpc512x_psc_start_tx,
+	.stop_tx = mpc512x_psc_stop_tx,
+	.rx_clr_irq = mpc512x_psc_rx_clr_irq,
+	.tx_clr_irq = mpc512x_psc_tx_clr_irq,
+	.write_char = mpc512x_psc_write_char,
+	.read_char = mpc512x_psc_read_char,
+	.cw_disable_ints = mpc512x_psc_cw_disable_ints,
+	.cw_restore_ints = mpc512x_psc_cw_restore_ints,
+	.set_baudrate = mpc512x_psc_set_baudrate,
+	.clock = mpc512x_psc_clock,
+	.fifoc_init = mpc512x_psc_fifoc_init,
+	.fifoc_uninit = mpc512x_psc_fifoc_uninit,
+	.get_irq = mpc512x_psc_get_irq,
+	.handle_irq = mpc512x_psc_handle_irq,
+};
+#endif
+
+static struct psc_ops *psc_ops;
+
+/* ======================================================================== */
+/* UART operations                                                          */
+/* ======================================================================== */
+
+static unsigned int
+mpc52xx_uart_tx_empty(struct uart_port *port)
+{
+	return psc_ops->tx_empty(port) ? TIOCSER_TEMT : 0;
+}
+
+static void
+mpc52xx_uart_set_mctrl(struct uart_port *port, unsigned int mctrl)
+{
+	if (mctrl & TIOCM_RTS)
+		out_8(&PSC(port)->op1, MPC52xx_PSC_OP_RTS);
+	else
+		out_8(&PSC(port)->op0, MPC52xx_PSC_OP_RTS);
+}
+
+static unsigned int
+mpc52xx_uart_get_mctrl(struct uart_port *port)
+{
+	unsigned int ret = TIOCM_DSR;
+	u8 status = in_8(&PSC(port)->mpc52xx_psc_ipcr);
+
+	if (!(status & MPC52xx_PSC_CTS))
+		ret |= TIOCM_CTS;
+	if (!(status & MPC52xx_PSC_DCD))
+		ret |= TIOCM_CAR;
+
+	return ret;
+}
+
+static void
+mpc52xx_uart_stop_tx(struct uart_port *port)
+{
+	/* port->lock taken by caller */
+	psc_ops->stop_tx(port);
+}
+
+static void
+mpc52xx_uart_start_tx(struct uart_port *port)
+{
+	/* port->lock taken by caller */
+	psc_ops->start_tx(port);
+}
+
+static void
+mpc52xx_uart_send_xchar(struct uart_port *port, char ch)
+{
+	unsigned long flags;
+	spin_lock_irqsave(&port->lock, flags);
+
+	port->x_char = ch;
+	if (ch) {
+		/* Make sure tx interrupts are on */
+		/* Truly necessary ??? They should be anyway */
+		psc_ops->start_tx(port);
+	}
+
+	spin_unlock_irqrestore(&port->lock, flags);
+}
+
+static void
+mpc52xx_uart_stop_rx(struct uart_port *port)
+{
+	/* port->lock taken by caller */
+	psc_ops->stop_rx(port);
+}
+
+static void
+mpc52xx_uart_enable_ms(struct uart_port *port)
+{
+	struct mpc52xx_psc __iomem *psc = PSC(port);
+
+	/* clear D_*-bits by reading them */
+	in_8(&psc->mpc52xx_psc_ipcr);
+	/* enable CTS and DCD as IPC interrupts */
+	out_8(&psc->mpc52xx_psc_acr, MPC52xx_PSC_IEC_CTS | MPC52xx_PSC_IEC_DCD);
+
+	port->read_status_mask |= MPC52xx_PSC_IMR_IPC;
+	out_be16(&psc->mpc52xx_psc_imr, port->read_status_mask);
+}
+
+static void
+mpc52xx_uart_break_ctl(struct uart_port *port, int ctl)
+{
+	unsigned long flags;
+	spin_lock_irqsave(&port->lock, flags);
+
+	if (ctl == -1)
+		out_8(&PSC(port)->command, MPC52xx_PSC_START_BRK);
+	else
+		out_8(&PSC(port)->command, MPC52xx_PSC_STOP_BRK);
+
+	spin_unlock_irqrestore(&port->lock, flags);
+}
+
+static int
+mpc52xx_uart_startup(struct uart_port *port)
+{
+	struct mpc52xx_psc __iomem *psc = PSC(port);
+	int ret;
+
+	if (psc_ops->clock) {
+		ret = psc_ops->clock(port, 1);
+		if (ret)
+			return ret;
+	}
+
+	/* Request IRQ */
+	ret = request_irq(port->irq, mpc52xx_uart_int,
+			  port->irqflags, "mpc52xx_psc_uart", port);
+	if (ret)
+		return ret;
+
+	/* Reset/activate the port, clear and enable interrupts */
+	out_8(&psc->command, MPC52xx_PSC_RST_RX);
+	out_8(&psc->command, MPC52xx_PSC_RST_TX);
+
+	out_be32(&psc->sicr, 0);	/* UART mode DCD ignored */
+
+	psc_ops->fifo_init(port);
+
+	out_8(&psc->command, MPC52xx_PSC_TX_ENABLE);
+	out_8(&psc->command, MPC52xx_PSC_RX_ENABLE);
+
+	return 0;
+}
+
+static void
+mpc52xx_uart_shutdown(struct uart_port *port)
+{
+	struct mpc52xx_psc __iomem *psc = PSC(port);
+
+	/* Shut down the port.  Leave TX active if on a console port */
+	out_8(&psc->command, MPC52xx_PSC_RST_RX);
+	if (!uart_console(port))
+		out_8(&psc->command, MPC52xx_PSC_RST_TX);
+
+	port->read_status_mask = 0;
+	out_be16(&psc->mpc52xx_psc_imr, port->read_status_mask);
+
+	if (psc_ops->clock)
+		psc_ops->clock(port, 0);
+
+	/* Release interrupt */
+	free_irq(port->irq, port);
+}
+
+static void
+mpc52xx_uart_set_termios(struct uart_port *port, struct ktermios *new,
+			 struct ktermios *old)
+{
+	struct mpc52xx_psc __iomem *psc = PSC(port);
+	unsigned long flags;
+	unsigned char mr1, mr2;
+	unsigned int j;
+	unsigned int baud;
+
+	/* Prepare what we're gonna write */
+	mr1 = 0;
+
+	switch (new->c_cflag & CSIZE) {
+	case CS5:	mr1 |= MPC52xx_PSC_MODE_5_BITS;
+		break;
+	case CS6:	mr1 |= MPC52xx_PSC_MODE_6_BITS;
+		break;
+	case CS7:	mr1 |= MPC52xx_PSC_MODE_7_BITS;
+		break;
+	case CS8:
+	default:	mr1 |= MPC52xx_PSC_MODE_8_BITS;
+	}
+
+	if (new->c_cflag & PARENB) {
+		mr1 |= (new->c_cflag & PARODD) ?
+			MPC52xx_PSC_MODE_PARODD : MPC52xx_PSC_MODE_PAREVEN;
+	} else
+		mr1 |= MPC52xx_PSC_MODE_PARNONE;
+
+
+	mr2 = 0;
+
+	if (new->c_cflag & CSTOPB)
+		mr2 |= MPC52xx_PSC_MODE_TWO_STOP;
+	else
+		mr2 |= ((new->c_cflag & CSIZE) == CS5) ?
+			MPC52xx_PSC_MODE_ONE_STOP_5_BITS :
+			MPC52xx_PSC_MODE_ONE_STOP;
+
+	if (new->c_cflag & CRTSCTS) {
+		mr1 |= MPC52xx_PSC_MODE_RXRTS;
+		mr2 |= MPC52xx_PSC_MODE_TXCTS;
+	}
+
+	/* Get the lock */
+	spin_lock_irqsave(&port->lock, flags);
+
+	/* Do our best to flush TX & RX, so we don't lose anything */
+	/* But we don't wait indefinitely ! */
+	j = 5000000;	/* Maximum wait */
+	/* FIXME Can't receive chars since set_termios might be called at early
+	 * boot for the console, all stuff is not yet ready to receive at that
+	 * time and that just makes the kernel oops */
+	/* while (j-- && mpc52xx_uart_int_rx_chars(port)); */
+	while (!mpc52xx_uart_tx_empty(port) && --j)
+		udelay(1);
+
+	if (!j)
+		printk(KERN_ERR "mpc52xx_uart.c: "
+			"Unable to flush RX & TX fifos in-time in set_termios."
+			"Some chars may have been lost.\n");
+
+	/* Reset the TX & RX */
+	out_8(&psc->command, MPC52xx_PSC_RST_RX);
+	out_8(&psc->command, MPC52xx_PSC_RST_TX);
+
+	/* Send new mode settings */
+	out_8(&psc->command, MPC52xx_PSC_SEL_MODE_REG_1);
+	out_8(&psc->mode, mr1);
+	out_8(&psc->mode, mr2);
+	baud = psc_ops->set_baudrate(port, new, old);
+
+	/* Update the per-port timeout */
+	uart_update_timeout(port, new->c_cflag, baud);
+
+	if (UART_ENABLE_MS(port, new->c_cflag))
+		mpc52xx_uart_enable_ms(port);
+
+	/* Reenable TX & RX */
+	out_8(&psc->command, MPC52xx_PSC_TX_ENABLE);
+	out_8(&psc->command, MPC52xx_PSC_RX_ENABLE);
+
+	/* We're all set, release the lock */
+	spin_unlock_irqrestore(&port->lock, flags);
+}
+
+static const char *
+mpc52xx_uart_type(struct uart_port *port)
+{
+	/*
+	 * We keep using PORT_MPC52xx for historic reasons although it applies
+	 * for MPC512x, too, but print "MPC5xxx" to not irritate users
+	 */
+	return port->type == PORT_MPC52xx ? "MPC5xxx PSC" : NULL;
+}
+
+static void
+mpc52xx_uart_release_port(struct uart_port *port)
+{
+	/* remapped by us ? */
+	if (port->flags & UPF_IOREMAP) {
+		iounmap(port->membase);
+		port->membase = NULL;
+	}
+
+	release_mem_region(port->mapbase, sizeof(struct mpc52xx_psc));
+}
+
+static int
+mpc52xx_uart_request_port(struct uart_port *port)
+{
+	int err;
+
+	if (port->flags & UPF_IOREMAP) /* Need to remap ? */
+		port->membase = ioremap(port->mapbase,
+					sizeof(struct mpc52xx_psc));
+
+	if (!port->membase)
+		return -EINVAL;
+
+	err = request_mem_region(port->mapbase, sizeof(struct mpc52xx_psc),
+			"mpc52xx_psc_uart") != NULL ? 0 : -EBUSY;
+
+	if (err && (port->flags & UPF_IOREMAP)) {
+		iounmap(port->membase);
+		port->membase = NULL;
+	}
+
+	return err;
+}
+
+static void
+mpc52xx_uart_config_port(struct uart_port *port, int flags)
+{
+	if ((flags & UART_CONFIG_TYPE)
+		&& (mpc52xx_uart_request_port(port) == 0))
+		port->type = PORT_MPC52xx;
+}
+
+static int
+mpc52xx_uart_verify_port(struct uart_port *port, struct serial_struct *ser)
+{
+	if (ser->type != PORT_UNKNOWN && ser->type != PORT_MPC52xx)
+		return -EINVAL;
+
+	if ((ser->irq != port->irq) ||
+	    (ser->io_type != UPIO_MEM) ||
+	    (ser->baud_base != port->uartclk)  ||
+	    (ser->iomem_base != (void *)port->mapbase) ||
+	    (ser->hub6 != 0))
+		return -EINVAL;
+
+	return 0;
+}
+
+
+static struct uart_ops mpc52xx_uart_ops = {
+	.tx_empty	= mpc52xx_uart_tx_empty,
+	.set_mctrl	= mpc52xx_uart_set_mctrl,
+	.get_mctrl	= mpc52xx_uart_get_mctrl,
+	.stop_tx	= mpc52xx_uart_stop_tx,
+	.start_tx	= mpc52xx_uart_start_tx,
+	.send_xchar	= mpc52xx_uart_send_xchar,
+	.stop_rx	= mpc52xx_uart_stop_rx,
+	.enable_ms	= mpc52xx_uart_enable_ms,
+	.break_ctl	= mpc52xx_uart_break_ctl,
+	.startup	= mpc52xx_uart_startup,
+	.shutdown	= mpc52xx_uart_shutdown,
+	.set_termios	= mpc52xx_uart_set_termios,
+/*	.pm		= mpc52xx_uart_pm,		Not supported yet */
+/*	.set_wake	= mpc52xx_uart_set_wake,	Not supported yet */
+	.type		= mpc52xx_uart_type,
+	.release_port	= mpc52xx_uart_release_port,
+	.request_port	= mpc52xx_uart_request_port,
+	.config_port	= mpc52xx_uart_config_port,
+	.verify_port	= mpc52xx_uart_verify_port
+};
+
+
+/* ======================================================================== */
+/* Interrupt handling                                                       */
+/* ======================================================================== */
+
+static inline int
+mpc52xx_uart_int_rx_chars(struct uart_port *port)
+{
+	struct tty_struct *tty = port->state->port.tty;
+	unsigned char ch, flag;
+	unsigned short status;
+
+	/* While we can read, do so ! */
+	while (psc_ops->raw_rx_rdy(port)) {
+		/* Get the char */
+		ch = psc_ops->read_char(port);
+
+		/* Handle sysreq char */
+#ifdef SUPPORT_SYSRQ
+		if (uart_handle_sysrq_char(port, ch)) {
+			port->sysrq = 0;
+			continue;
+		}
+#endif
+
+		/* Store it */
+
+		flag = TTY_NORMAL;
+		port->icount.rx++;
+
+		status = in_be16(&PSC(port)->mpc52xx_psc_status);
+
+		if (status & (MPC52xx_PSC_SR_PE |
+			      MPC52xx_PSC_SR_FE |
+			      MPC52xx_PSC_SR_RB)) {
+
+			if (status & MPC52xx_PSC_SR_RB) {
+				flag = TTY_BREAK;
+				uart_handle_break(port);
+				port->icount.brk++;
+			} else if (status & MPC52xx_PSC_SR_PE) {
+				flag = TTY_PARITY;
+				port->icount.parity++;
+			}
+			else if (status & MPC52xx_PSC_SR_FE) {
+				flag = TTY_FRAME;
+				port->icount.frame++;
+			}
+
+			/* Clear error condition */
+			out_8(&PSC(port)->command, MPC52xx_PSC_RST_ERR_STAT);
+
+		}
+		tty_insert_flip_char(tty, ch, flag);
+		if (status & MPC52xx_PSC_SR_OE) {
+			/*
+			 * Overrun is special, since it's
+			 * reported immediately, and doesn't
+			 * affect the current character
+			 */
+			tty_insert_flip_char(tty, 0, TTY_OVERRUN);
+			port->icount.overrun++;
+		}
+	}
+
+	spin_unlock(&port->lock);
+	tty_flip_buffer_push(tty);
+	spin_lock(&port->lock);
+
+	return psc_ops->raw_rx_rdy(port);
+}
+
+static inline int
+mpc52xx_uart_int_tx_chars(struct uart_port *port)
+{
+	struct circ_buf *xmit = &port->state->xmit;
+
+	/* Process out of band chars */
+	if (port->x_char) {
+		psc_ops->write_char(port, port->x_char);
+		port->icount.tx++;
+		port->x_char = 0;
+		return 1;
+	}
+
+	/* Nothing to do ? */
+	if (uart_circ_empty(xmit) || uart_tx_stopped(port)) {
+		mpc52xx_uart_stop_tx(port);
+		return 0;
+	}
+
+	/* Send chars */
+	while (psc_ops->raw_tx_rdy(port)) {
+		psc_ops->write_char(port, xmit->buf[xmit->tail]);
+		xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
+		port->icount.tx++;
+		if (uart_circ_empty(xmit))
+			break;
+	}
+
+	/* Wake up */
+	if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
+		uart_write_wakeup(port);
+
+	/* Maybe we're done after all */
+	if (uart_circ_empty(xmit)) {
+		mpc52xx_uart_stop_tx(port);
+		return 0;
+	}
+
+	return 1;
+}
+
+static irqreturn_t
+mpc5xxx_uart_process_int(struct uart_port *port)
+{
+	unsigned long pass = ISR_PASS_LIMIT;
+	unsigned int keepgoing;
+	u8 status;
+
+	/* While we have stuff to do, we continue */
+	do {
+		/* If we don't find anything to do, we stop */
+		keepgoing = 0;
+
+		psc_ops->rx_clr_irq(port);
+		if (psc_ops->rx_rdy(port))
+			keepgoing |= mpc52xx_uart_int_rx_chars(port);
+
+		psc_ops->tx_clr_irq(port);
+		if (psc_ops->tx_rdy(port))
+			keepgoing |= mpc52xx_uart_int_tx_chars(port);
+
+		status = in_8(&PSC(port)->mpc52xx_psc_ipcr);
+		if (status & MPC52xx_PSC_D_DCD)
+			uart_handle_dcd_change(port, !(status & MPC52xx_PSC_DCD));
+
+		if (status & MPC52xx_PSC_D_CTS)
+			uart_handle_cts_change(port, !(status & MPC52xx_PSC_CTS));
+
+		/* Limit number of iteration */
+		if (!(--pass))
+			keepgoing = 0;
+
+	} while (keepgoing);
+
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t
+mpc52xx_uart_int(int irq, void *dev_id)
+{
+	struct uart_port *port = dev_id;
+	irqreturn_t ret;
+
+	spin_lock(&port->lock);
+
+	ret = psc_ops->handle_irq(port);
+
+	spin_unlock(&port->lock);
+
+	return ret;
+}
+
+/* ======================================================================== */
+/* Console ( if applicable )                                                */
+/* ======================================================================== */
+
+#ifdef CONFIG_SERIAL_MPC52xx_CONSOLE
+
+static void __init
+mpc52xx_console_get_options(struct uart_port *port,
+			    int *baud, int *parity, int *bits, int *flow)
+{
+	struct mpc52xx_psc __iomem *psc = PSC(port);
+	unsigned char mr1;
+
+	pr_debug("mpc52xx_console_get_options(port=%p)\n", port);
+
+	/* Read the mode registers */
+	out_8(&psc->command, MPC52xx_PSC_SEL_MODE_REG_1);
+	mr1 = in_8(&psc->mode);
+
+	/* CT{U,L}R are write-only ! */
+	*baud = CONFIG_SERIAL_MPC52xx_CONSOLE_BAUD;
+
+	/* Parse them */
+	switch (mr1 & MPC52xx_PSC_MODE_BITS_MASK) {
+	case MPC52xx_PSC_MODE_5_BITS:
+		*bits = 5;
+		break;
+	case MPC52xx_PSC_MODE_6_BITS:
+		*bits = 6;
+		break;
+	case MPC52xx_PSC_MODE_7_BITS:
+		*bits = 7;
+		break;
+	case MPC52xx_PSC_MODE_8_BITS:
+	default:
+		*bits = 8;
+	}
+
+	if (mr1 & MPC52xx_PSC_MODE_PARNONE)
+		*parity = 'n';
+	else
+		*parity = mr1 & MPC52xx_PSC_MODE_PARODD ? 'o' : 'e';
+}
+
+static void
+mpc52xx_console_write(struct console *co, const char *s, unsigned int count)
+{
+	struct uart_port *port = &mpc52xx_uart_ports[co->index];
+	unsigned int i, j;
+
+	/* Disable interrupts */
+	psc_ops->cw_disable_ints(port);
+
+	/* Wait the TX buffer to be empty */
+	j = 5000000;	/* Maximum wait */
+	while (!mpc52xx_uart_tx_empty(port) && --j)
+		udelay(1);
+
+	/* Write all the chars */
+	for (i = 0; i < count; i++, s++) {
+		/* Line return handling */
+		if (*s == '\n')
+			psc_ops->write_char(port, '\r');
+
+		/* Send the char */
+		psc_ops->write_char(port, *s);
+
+		/* Wait the TX buffer to be empty */
+		j = 20000;	/* Maximum wait */
+		while (!mpc52xx_uart_tx_empty(port) && --j)
+			udelay(1);
+	}
+
+	/* Restore interrupt state */
+	psc_ops->cw_restore_ints(port);
+}
+
+
+static int __init
+mpc52xx_console_setup(struct console *co, char *options)
+{
+	struct uart_port *port = &mpc52xx_uart_ports[co->index];
+	struct device_node *np = mpc52xx_uart_nodes[co->index];
+	unsigned int uartclk;
+	struct resource res;
+	int ret;
+
+	int baud = CONFIG_SERIAL_MPC52xx_CONSOLE_BAUD;
+	int bits = 8;
+	int parity = 'n';
+	int flow = 'n';
+
+	pr_debug("mpc52xx_console_setup co=%p, co->index=%i, options=%s\n",
+		 co, co->index, options);
+
+	if ((co->index < 0) || (co->index >= MPC52xx_PSC_MAXNUM)) {
+		pr_debug("PSC%x out of range\n", co->index);
+		return -EINVAL;
+	}
+
+	if (!np) {
+		pr_debug("PSC%x not found in device tree\n", co->index);
+		return -EINVAL;
+	}
+
+	pr_debug("Console on ttyPSC%x is %s\n",
+		 co->index, mpc52xx_uart_nodes[co->index]->full_name);
+
+	/* Fetch register locations */
+	ret = of_address_to_resource(np, 0, &res);
+	if (ret) {
+		pr_debug("Could not get resources for PSC%x\n", co->index);
+		return ret;
+	}
+
+	uartclk = mpc5xxx_get_bus_frequency(np);
+	if (uartclk == 0) {
+		pr_debug("Could not find uart clock frequency!\n");
+		return -EINVAL;
+	}
+
+	/* Basic port init. Needed since we use some uart_??? func before
+	 * real init for early access */
+	spin_lock_init(&port->lock);
+	port->uartclk = uartclk;
+	port->ops	= &mpc52xx_uart_ops;
+	port->mapbase = res.start;
+	port->membase = ioremap(res.start, sizeof(struct mpc52xx_psc));
+	port->irq = irq_of_parse_and_map(np, 0);
+
+	if (port->membase == NULL)
+		return -EINVAL;
+
+	pr_debug("mpc52xx-psc uart at %p, mapped to %p, irq=%x, freq=%i\n",
+		 (void *)port->mapbase, port->membase,
+		 port->irq, port->uartclk);
+
+	/* Setup the port parameters accoding to options */
+	if (options)
+		uart_parse_options(options, &baud, &parity, &bits, &flow);
+	else
+		mpc52xx_console_get_options(port, &baud, &parity, &bits, &flow);
+
+	pr_debug("Setting console parameters: %i %i%c1 flow=%c\n",
+		 baud, bits, parity, flow);
+
+	return uart_set_options(port, co, baud, parity, bits, flow);
+}
+
+
+static struct uart_driver mpc52xx_uart_driver;
+
+static struct console mpc52xx_console = {
+	.name	= "ttyPSC",
+	.write	= mpc52xx_console_write,
+	.device	= uart_console_device,
+	.setup	= mpc52xx_console_setup,
+	.flags	= CON_PRINTBUFFER,
+	.index	= -1,	/* Specified on the cmdline (e.g. console=ttyPSC0) */
+	.data	= &mpc52xx_uart_driver,
+};
+
+
+static int __init
+mpc52xx_console_init(void)
+{
+	mpc52xx_uart_of_enumerate();
+	register_console(&mpc52xx_console);
+	return 0;
+}
+
+console_initcall(mpc52xx_console_init);
+
+#define MPC52xx_PSC_CONSOLE &mpc52xx_console
+#else
+#define MPC52xx_PSC_CONSOLE NULL
+#endif
+
+
+/* ======================================================================== */
+/* UART Driver                                                              */
+/* ======================================================================== */
+
+static struct uart_driver mpc52xx_uart_driver = {
+	.driver_name	= "mpc52xx_psc_uart",
+	.dev_name	= "ttyPSC",
+	.major		= SERIAL_PSC_MAJOR,
+	.minor		= SERIAL_PSC_MINOR,
+	.nr		= MPC52xx_PSC_MAXNUM,
+	.cons		= MPC52xx_PSC_CONSOLE,
+};
+
+/* ======================================================================== */
+/* OF Platform Driver                                                       */
+/* ======================================================================== */
+
+static struct of_device_id mpc52xx_uart_of_match[] = {
+#ifdef CONFIG_PPC_MPC52xx
+	{ .compatible = "fsl,mpc5200b-psc-uart", .data = &mpc5200b_psc_ops, },
+	{ .compatible = "fsl,mpc5200-psc-uart", .data = &mpc52xx_psc_ops, },
+	/* binding used by old lite5200 device trees: */
+	{ .compatible = "mpc5200-psc-uart", .data = &mpc52xx_psc_ops, },
+	/* binding used by efika: */
+	{ .compatible = "mpc5200-serial", .data = &mpc52xx_psc_ops, },
+#endif
+#ifdef CONFIG_PPC_MPC512x
+	{ .compatible = "fsl,mpc5121-psc-uart", .data = &mpc512x_psc_ops, },
+#endif
+	{},
+};
+
+static int __devinit mpc52xx_uart_of_probe(struct platform_device *op)
+{
+	int idx = -1;
+	unsigned int uartclk;
+	struct uart_port *port = NULL;
+	struct resource res;
+	int ret;
+
+	/* Check validity & presence */
+	for (idx = 0; idx < MPC52xx_PSC_MAXNUM; idx++)
+		if (mpc52xx_uart_nodes[idx] == op->dev.of_node)
+			break;
+	if (idx >= MPC52xx_PSC_MAXNUM)
+		return -EINVAL;
+	pr_debug("Found %s assigned to ttyPSC%x\n",
+		 mpc52xx_uart_nodes[idx]->full_name, idx);
+
+	/* set the uart clock to the input clock of the psc, the different
+	 * prescalers are taken into account in the set_baudrate() methods
+	 * of the respective chip */
+	uartclk = mpc5xxx_get_bus_frequency(op->dev.of_node);
+	if (uartclk == 0) {
+		dev_dbg(&op->dev, "Could not find uart clock frequency!\n");
+		return -EINVAL;
+	}
+
+	/* Init the port structure */
+	port = &mpc52xx_uart_ports[idx];
+
+	spin_lock_init(&port->lock);
+	port->uartclk = uartclk;
+	port->fifosize	= 512;
+	port->iotype	= UPIO_MEM;
+	port->flags	= UPF_BOOT_AUTOCONF |
+			  (uart_console(port) ? 0 : UPF_IOREMAP);
+	port->line	= idx;
+	port->ops	= &mpc52xx_uart_ops;
+	port->dev	= &op->dev;
+
+	/* Search for IRQ and mapbase */
+	ret = of_address_to_resource(op->dev.of_node, 0, &res);
+	if (ret)
+		return ret;
+
+	port->mapbase = res.start;
+	if (!port->mapbase) {
+		dev_dbg(&op->dev, "Could not allocate resources for PSC\n");
+		return -EINVAL;
+	}
+
+	psc_ops->get_irq(port, op->dev.of_node);
+	if (port->irq == 0) {
+		dev_dbg(&op->dev, "Could not get irq\n");
+		return -EINVAL;
+	}
+
+	dev_dbg(&op->dev, "mpc52xx-psc uart at %p, irq=%x, freq=%i\n",
+		(void *)port->mapbase, port->irq, port->uartclk);
+
+	/* Add the port to the uart sub-system */
+	ret = uart_add_one_port(&mpc52xx_uart_driver, port);
+	if (ret)
+		return ret;
+
+	dev_set_drvdata(&op->dev, (void *)port);
+	return 0;
+}
+
+static int
+mpc52xx_uart_of_remove(struct platform_device *op)
+{
+	struct uart_port *port = dev_get_drvdata(&op->dev);
+	dev_set_drvdata(&op->dev, NULL);
+
+	if (port)
+		uart_remove_one_port(&mpc52xx_uart_driver, port);
+
+	return 0;
+}
+
+#ifdef CONFIG_PM
+static int
+mpc52xx_uart_of_suspend(struct platform_device *op, pm_message_t state)
+{
+	struct uart_port *port = (struct uart_port *) dev_get_drvdata(&op->dev);
+
+	if (port)
+		uart_suspend_port(&mpc52xx_uart_driver, port);
+
+	return 0;
+}
+
+static int
+mpc52xx_uart_of_resume(struct platform_device *op)
+{
+	struct uart_port *port = (struct uart_port *) dev_get_drvdata(&op->dev);
+
+	if (port)
+		uart_resume_port(&mpc52xx_uart_driver, port);
+
+	return 0;
+}
+#endif
+
+static void
+mpc52xx_uart_of_assign(struct device_node *np)
+{
+	int i;
+
+	/* Find the first free PSC number */
+	for (i = 0; i < MPC52xx_PSC_MAXNUM; i++) {
+		if (mpc52xx_uart_nodes[i] == NULL) {
+			of_node_get(np);
+			mpc52xx_uart_nodes[i] = np;
+			return;
+		}
+	}
+}
+
+static void
+mpc52xx_uart_of_enumerate(void)
+{
+	static int enum_done;
+	struct device_node *np;
+	const struct  of_device_id *match;
+	int i;
+
+	if (enum_done)
+		return;
+
+	/* Assign index to each PSC in device tree */
+	for_each_matching_node(np, mpc52xx_uart_of_match) {
+		match = of_match_node(mpc52xx_uart_of_match, np);
+		psc_ops = match->data;
+		mpc52xx_uart_of_assign(np);
+	}
+
+	enum_done = 1;
+
+	for (i = 0; i < MPC52xx_PSC_MAXNUM; i++) {
+		if (mpc52xx_uart_nodes[i])
+			pr_debug("%s assigned to ttyPSC%x\n",
+				 mpc52xx_uart_nodes[i]->full_name, i);
+	}
+}
+
+MODULE_DEVICE_TABLE(of, mpc52xx_uart_of_match);
+
+static struct platform_driver mpc52xx_uart_of_driver = {
+	.probe		= mpc52xx_uart_of_probe,
+	.remove		= mpc52xx_uart_of_remove,
+#ifdef CONFIG_PM
+	.suspend	= mpc52xx_uart_of_suspend,
+	.resume		= mpc52xx_uart_of_resume,
+#endif
+	.driver = {
+		.name = "mpc52xx-psc-uart",
+		.owner = THIS_MODULE,
+		.of_match_table = mpc52xx_uart_of_match,
+	},
+};
+
+
+/* ======================================================================== */
+/* Module                                                                   */
+/* ======================================================================== */
+
+static int __init
+mpc52xx_uart_init(void)
+{
+	int ret;
+
+	printk(KERN_INFO "Serial: MPC52xx PSC UART driver\n");
+
+	ret = uart_register_driver(&mpc52xx_uart_driver);
+	if (ret) {
+		printk(KERN_ERR "%s: uart_register_driver failed (%i)\n",
+		       __FILE__, ret);
+		return ret;
+	}
+
+	mpc52xx_uart_of_enumerate();
+
+	/*
+	 * Map the PSC FIFO Controller and init if on MPC512x.
+	 */
+	if (psc_ops && psc_ops->fifoc_init) {
+		ret = psc_ops->fifoc_init();
+		if (ret)
+			return ret;
+	}
+
+	ret = platform_driver_register(&mpc52xx_uart_of_driver);
+	if (ret) {
+		printk(KERN_ERR "%s: platform_driver_register failed (%i)\n",
+		       __FILE__, ret);
+		uart_unregister_driver(&mpc52xx_uart_driver);
+		return ret;
+	}
+
+	return 0;
+}
+
+static void __exit
+mpc52xx_uart_exit(void)
+{
+	if (psc_ops->fifoc_uninit)
+		psc_ops->fifoc_uninit();
+
+	platform_driver_unregister(&mpc52xx_uart_of_driver);
+	uart_unregister_driver(&mpc52xx_uart_driver);
+}
+
+
+module_init(mpc52xx_uart_init);
+module_exit(mpc52xx_uart_exit);
+
+MODULE_AUTHOR("Sylvain Munaut <tnt@246tNt.com>");
+MODULE_DESCRIPTION("Freescale MPC52xx PSC UART");
+MODULE_LICENSE("GPL");
diff --git a/ap/os/linux/linux-3.4.x/drivers/tty/serial/mpsc.c b/ap/os/linux/linux-3.4.x/drivers/tty/serial/mpsc.c
new file mode 100644
index 0000000..6a9c660
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/tty/serial/mpsc.c
@@ -0,0 +1,2159 @@
+/*
+ * Generic driver for the MPSC (UART mode) on Marvell parts (e.g., GT64240,
+ * GT64260, MV64340, MV64360, GT96100, ... ).
+ *
+ * Author: Mark A. Greer <mgreer@mvista.com>
+ *
+ * Based on an old MPSC driver that was in the linuxppc tree.  It appears to
+ * have been created by Chris Zankel (formerly of MontaVista) but there
+ * is no proper Copyright so I'm not sure.  Apparently, parts were also
+ * taken from PPCBoot (now U-Boot).  Also based on drivers/serial/8250.c
+ * by Russell King.
+ *
+ * 2004 (c) MontaVista, Software, Inc.  This file is licensed under
+ * the terms of the GNU General Public License version 2.  This program
+ * is licensed "as is" without any warranty of any kind, whether express
+ * or implied.
+ */
+/*
+ * The MPSC interface is much like a typical network controller's interface.
+ * That is, you set up separate rings of descriptors for transmitting and
+ * receiving data.  There is also a pool of buffers with (one buffer per
+ * descriptor) that incoming data are dma'd into or outgoing data are dma'd
+ * out of.
+ *
+ * The MPSC requires two other controllers to be able to work.  The Baud Rate
+ * Generator (BRG) provides a clock at programmable frequencies which determines
+ * the baud rate.  The Serial DMA Controller (SDMA) takes incoming data from the
+ * MPSC and DMA's it into memory or DMA's outgoing data and passes it to the
+ * MPSC.  It is actually the SDMA interrupt that the driver uses to keep the
+ * transmit and receive "engines" going (i.e., indicate data has been
+ * transmitted or received).
+ *
+ * NOTES:
+ *
+ * 1) Some chips have an erratum where several regs cannot be
+ * read.  To work around that, we keep a local copy of those regs in
+ * 'mpsc_port_info'.
+ *
+ * 2) Some chips have an erratum where the ctlr will hang when the SDMA ctlr
+ * accesses system mem with coherency enabled.  For that reason, the driver
+ * assumes that coherency for that ctlr has been disabled.  This means
+ * that when in a cache coherent system, the driver has to manually manage
+ * the data cache on the areas that it touches because the dma_* macro are
+ * basically no-ops.
+ *
+ * 3) There is an erratum (on PPC) where you can't use the instruction to do
+ * a DMA_TO_DEVICE/cache clean so DMA_BIDIRECTIONAL/flushes are used in places
+ * where a DMA_TO_DEVICE/clean would have [otherwise] sufficed.
+ *
+ * 4) AFAICT, hardware flow control isn't supported by the controller --MAG.
+ */
+
+
+#if defined(CONFIG_SERIAL_MPSC_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ)
+#define SUPPORT_SYSRQ
+#endif
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
+#include <linux/ioport.h>
+#include <linux/init.h>
+#include <linux/console.h>
+#include <linux/sysrq.h>
+#include <linux/serial.h>
+#include <linux/serial_core.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/dma-mapping.h>
+#include <linux/mv643xx.h>
+#include <linux/platform_device.h>
+#include <linux/gfp.h>
+
+#include <asm/io.h>
+#include <asm/irq.h>
+
+#define	MPSC_NUM_CTLRS		2
+
+/*
+ * Descriptors and buffers must be cache line aligned.
+ * Buffers lengths must be multiple of cache line size.
+ * Number of Tx & Rx descriptors must be powers of 2.
+ */
+#define	MPSC_RXR_ENTRIES	32
+#define	MPSC_RXRE_SIZE		dma_get_cache_alignment()
+#define	MPSC_RXR_SIZE		(MPSC_RXR_ENTRIES * MPSC_RXRE_SIZE)
+#define	MPSC_RXBE_SIZE		dma_get_cache_alignment()
+#define	MPSC_RXB_SIZE		(MPSC_RXR_ENTRIES * MPSC_RXBE_SIZE)
+
+#define	MPSC_TXR_ENTRIES	32
+#define	MPSC_TXRE_SIZE		dma_get_cache_alignment()
+#define	MPSC_TXR_SIZE		(MPSC_TXR_ENTRIES * MPSC_TXRE_SIZE)
+#define	MPSC_TXBE_SIZE		dma_get_cache_alignment()
+#define	MPSC_TXB_SIZE		(MPSC_TXR_ENTRIES * MPSC_TXBE_SIZE)
+
+#define	MPSC_DMA_ALLOC_SIZE	(MPSC_RXR_SIZE + MPSC_RXB_SIZE + MPSC_TXR_SIZE \
+		+ MPSC_TXB_SIZE + dma_get_cache_alignment() /* for alignment */)
+
+/* Rx and Tx Ring entry descriptors -- assume entry size is <= cacheline size */
+struct mpsc_rx_desc {
+	u16 bufsize;
+	u16 bytecnt;
+	u32 cmdstat;
+	u32 link;
+	u32 buf_ptr;
+} __attribute((packed));
+
+struct mpsc_tx_desc {
+	u16 bytecnt;
+	u16 shadow;
+	u32 cmdstat;
+	u32 link;
+	u32 buf_ptr;
+} __attribute((packed));
+
+/*
+ * Some regs that have the erratum that you can't read them are are shared
+ * between the two MPSC controllers.  This struct contains those shared regs.
+ */
+struct mpsc_shared_regs {
+	phys_addr_t mpsc_routing_base_p;
+	phys_addr_t sdma_intr_base_p;
+
+	void __iomem *mpsc_routing_base;
+	void __iomem *sdma_intr_base;
+
+	u32 MPSC_MRR_m;
+	u32 MPSC_RCRR_m;
+	u32 MPSC_TCRR_m;
+	u32 SDMA_INTR_CAUSE_m;
+	u32 SDMA_INTR_MASK_m;
+};
+
+/* The main driver data structure */
+struct mpsc_port_info {
+	struct uart_port port;	/* Overlay uart_port structure */
+
+	/* Internal driver state for this ctlr */
+	u8 ready;
+	u8 rcv_data;
+	tcflag_t c_iflag;	/* save termios->c_iflag */
+	tcflag_t c_cflag;	/* save termios->c_cflag */
+
+	/* Info passed in from platform */
+	u8 mirror_regs;		/* Need to mirror regs? */
+	u8 cache_mgmt;		/* Need manual cache mgmt? */
+	u8 brg_can_tune;	/* BRG has baud tuning? */
+	u32 brg_clk_src;
+	u16 mpsc_max_idle;
+	int default_baud;
+	int default_bits;
+	int default_parity;
+	int default_flow;
+
+	/* Physical addresses of various blocks of registers (from platform) */
+	phys_addr_t mpsc_base_p;
+	phys_addr_t sdma_base_p;
+	phys_addr_t brg_base_p;
+
+	/* Virtual addresses of various blocks of registers (from platform) */
+	void __iomem *mpsc_base;
+	void __iomem *sdma_base;
+	void __iomem *brg_base;
+
+	/* Descriptor ring and buffer allocations */
+	void *dma_region;
+	dma_addr_t dma_region_p;
+
+	dma_addr_t rxr;		/* Rx descriptor ring */
+	dma_addr_t rxr_p;	/* Phys addr of rxr */
+	u8 *rxb;		/* Rx Ring I/O buf */
+	u8 *rxb_p;		/* Phys addr of rxb */
+	u32 rxr_posn;		/* First desc w/ Rx data */
+
+	dma_addr_t txr;		/* Tx descriptor ring */
+	dma_addr_t txr_p;	/* Phys addr of txr */
+	u8 *txb;		/* Tx Ring I/O buf */
+	u8 *txb_p;		/* Phys addr of txb */
+	int txr_head;		/* Where new data goes */
+	int txr_tail;		/* Where sent data comes off */
+	spinlock_t tx_lock;	/* transmit lock */
+
+	/* Mirrored values of regs we can't read (if 'mirror_regs' set) */
+	u32 MPSC_MPCR_m;
+	u32 MPSC_CHR_1_m;
+	u32 MPSC_CHR_2_m;
+	u32 MPSC_CHR_10_m;
+	u32 BRG_BCR_m;
+	struct mpsc_shared_regs *shared_regs;
+};
+
+/* Hooks to platform-specific code */
+int mpsc_platform_register_driver(void);
+void mpsc_platform_unregister_driver(void);
+
+/* Hooks back in to mpsc common to be called by platform-specific code */
+struct mpsc_port_info *mpsc_device_probe(int index);
+struct mpsc_port_info *mpsc_device_remove(int index);
+
+/* Main MPSC Configuration Register Offsets */
+#define	MPSC_MMCRL			0x0000
+#define	MPSC_MMCRH			0x0004
+#define	MPSC_MPCR			0x0008
+#define	MPSC_CHR_1			0x000c
+#define	MPSC_CHR_2			0x0010
+#define	MPSC_CHR_3			0x0014
+#define	MPSC_CHR_4			0x0018
+#define	MPSC_CHR_5			0x001c
+#define	MPSC_CHR_6			0x0020
+#define	MPSC_CHR_7			0x0024
+#define	MPSC_CHR_8			0x0028
+#define	MPSC_CHR_9			0x002c
+#define	MPSC_CHR_10			0x0030
+#define	MPSC_CHR_11			0x0034
+
+#define	MPSC_MPCR_FRZ			(1 << 9)
+#define	MPSC_MPCR_CL_5			0
+#define	MPSC_MPCR_CL_6			1
+#define	MPSC_MPCR_CL_7			2
+#define	MPSC_MPCR_CL_8			3
+#define	MPSC_MPCR_SBL_1			0
+#define	MPSC_MPCR_SBL_2			1
+
+#define	MPSC_CHR_2_TEV			(1<<1)
+#define	MPSC_CHR_2_TA			(1<<7)
+#define	MPSC_CHR_2_TTCS			(1<<9)
+#define	MPSC_CHR_2_REV			(1<<17)
+#define	MPSC_CHR_2_RA			(1<<23)
+#define	MPSC_CHR_2_CRD			(1<<25)
+#define	MPSC_CHR_2_EH			(1<<31)
+#define	MPSC_CHR_2_PAR_ODD		0
+#define	MPSC_CHR_2_PAR_SPACE		1
+#define	MPSC_CHR_2_PAR_EVEN		2
+#define	MPSC_CHR_2_PAR_MARK		3
+
+/* MPSC Signal Routing */
+#define	MPSC_MRR			0x0000
+#define	MPSC_RCRR			0x0004
+#define	MPSC_TCRR			0x0008
+
+/* Serial DMA Controller Interface Registers */
+#define	SDMA_SDC			0x0000
+#define	SDMA_SDCM			0x0008
+#define	SDMA_RX_DESC			0x0800
+#define	SDMA_RX_BUF_PTR			0x0808
+#define	SDMA_SCRDP			0x0810
+#define	SDMA_TX_DESC			0x0c00
+#define	SDMA_SCTDP			0x0c10
+#define	SDMA_SFTDP			0x0c14
+
+#define	SDMA_DESC_CMDSTAT_PE		(1<<0)
+#define	SDMA_DESC_CMDSTAT_CDL		(1<<1)
+#define	SDMA_DESC_CMDSTAT_FR		(1<<3)
+#define	SDMA_DESC_CMDSTAT_OR		(1<<6)
+#define	SDMA_DESC_CMDSTAT_BR		(1<<9)
+#define	SDMA_DESC_CMDSTAT_MI		(1<<10)
+#define	SDMA_DESC_CMDSTAT_A		(1<<11)
+#define	SDMA_DESC_CMDSTAT_AM		(1<<12)
+#define	SDMA_DESC_CMDSTAT_CT		(1<<13)
+#define	SDMA_DESC_CMDSTAT_C		(1<<14)
+#define	SDMA_DESC_CMDSTAT_ES		(1<<15)
+#define	SDMA_DESC_CMDSTAT_L		(1<<16)
+#define	SDMA_DESC_CMDSTAT_F		(1<<17)
+#define	SDMA_DESC_CMDSTAT_P		(1<<18)
+#define	SDMA_DESC_CMDSTAT_EI		(1<<23)
+#define	SDMA_DESC_CMDSTAT_O		(1<<31)
+
+#define SDMA_DESC_DFLT			(SDMA_DESC_CMDSTAT_O \
+		| SDMA_DESC_CMDSTAT_EI)
+
+#define	SDMA_SDC_RFT			(1<<0)
+#define	SDMA_SDC_SFM			(1<<1)
+#define	SDMA_SDC_BLMR			(1<<6)
+#define	SDMA_SDC_BLMT			(1<<7)
+#define	SDMA_SDC_POVR			(1<<8)
+#define	SDMA_SDC_RIFB			(1<<9)
+
+#define	SDMA_SDCM_ERD			(1<<7)
+#define	SDMA_SDCM_AR			(1<<15)
+#define	SDMA_SDCM_STD			(1<<16)
+#define	SDMA_SDCM_TXD			(1<<23)
+#define	SDMA_SDCM_AT			(1<<31)
+
+#define	SDMA_0_CAUSE_RXBUF		(1<<0)
+#define	SDMA_0_CAUSE_RXERR		(1<<1)
+#define	SDMA_0_CAUSE_TXBUF		(1<<2)
+#define	SDMA_0_CAUSE_TXEND		(1<<3)
+#define	SDMA_1_CAUSE_RXBUF		(1<<8)
+#define	SDMA_1_CAUSE_RXERR		(1<<9)
+#define	SDMA_1_CAUSE_TXBUF		(1<<10)
+#define	SDMA_1_CAUSE_TXEND		(1<<11)
+
+#define	SDMA_CAUSE_RX_MASK	(SDMA_0_CAUSE_RXBUF | SDMA_0_CAUSE_RXERR \
+		| SDMA_1_CAUSE_RXBUF | SDMA_1_CAUSE_RXERR)
+#define	SDMA_CAUSE_TX_MASK	(SDMA_0_CAUSE_TXBUF | SDMA_0_CAUSE_TXEND \
+		| SDMA_1_CAUSE_TXBUF | SDMA_1_CAUSE_TXEND)
+
+/* SDMA Interrupt registers */
+#define	SDMA_INTR_CAUSE			0x0000
+#define	SDMA_INTR_MASK			0x0080
+
+/* Baud Rate Generator Interface Registers */
+#define	BRG_BCR				0x0000
+#define	BRG_BTR				0x0004
+
+/*
+ * Define how this driver is known to the outside (we've been assigned a
+ * range on the "Low-density serial ports" major).
+ */
+#define MPSC_MAJOR			204
+#define MPSC_MINOR_START		44
+#define	MPSC_DRIVER_NAME		"MPSC"
+#define	MPSC_DEV_NAME			"ttyMM"
+#define	MPSC_VERSION			"1.00"
+
+static struct mpsc_port_info mpsc_ports[MPSC_NUM_CTLRS];
+static struct mpsc_shared_regs mpsc_shared_regs;
+static struct uart_driver mpsc_reg;
+
+static void mpsc_start_rx(struct mpsc_port_info *pi);
+static void mpsc_free_ring_mem(struct mpsc_port_info *pi);
+static void mpsc_release_port(struct uart_port *port);
+/*
+ ******************************************************************************
+ *
+ * Baud Rate Generator Routines (BRG)
+ *
+ ******************************************************************************
+ */
+static void mpsc_brg_init(struct mpsc_port_info *pi, u32 clk_src)
+{
+	u32	v;
+
+	v = (pi->mirror_regs) ? pi->BRG_BCR_m : readl(pi->brg_base + BRG_BCR);
+	v = (v & ~(0xf << 18)) | ((clk_src & 0xf) << 18);
+
+	if (pi->brg_can_tune)
+		v &= ~(1 << 25);
+
+	if (pi->mirror_regs)
+		pi->BRG_BCR_m = v;
+	writel(v, pi->brg_base + BRG_BCR);
+
+	writel(readl(pi->brg_base + BRG_BTR) & 0xffff0000,
+		pi->brg_base + BRG_BTR);
+}
+
+static void mpsc_brg_enable(struct mpsc_port_info *pi)
+{
+	u32	v;
+
+	v = (pi->mirror_regs) ? pi->BRG_BCR_m : readl(pi->brg_base + BRG_BCR);
+	v |= (1 << 16);
+
+	if (pi->mirror_regs)
+		pi->BRG_BCR_m = v;
+	writel(v, pi->brg_base + BRG_BCR);
+}
+
+static void mpsc_brg_disable(struct mpsc_port_info *pi)
+{
+	u32	v;
+
+	v = (pi->mirror_regs) ? pi->BRG_BCR_m : readl(pi->brg_base + BRG_BCR);
+	v &= ~(1 << 16);
+
+	if (pi->mirror_regs)
+		pi->BRG_BCR_m = v;
+	writel(v, pi->brg_base + BRG_BCR);
+}
+
+/*
+ * To set the baud, we adjust the CDV field in the BRG_BCR reg.
+ * From manual: Baud = clk / ((CDV+1)*2) ==> CDV = (clk / (baud*2)) - 1.
+ * However, the input clock is divided by 16 in the MPSC b/c of how
+ * 'MPSC_MMCRH' was set up so we have to divide the 'clk' used in our
+ * calculation by 16 to account for that.  So the real calculation
+ * that accounts for the way the mpsc is set up is:
+ * CDV = (clk / (baud*2*16)) - 1 ==> CDV = (clk / (baud << 5)) - 1.
+ */
+static void mpsc_set_baudrate(struct mpsc_port_info *pi, u32 baud)
+{
+	u32	cdv = (pi->port.uartclk / (baud << 5)) - 1;
+	u32	v;
+
+	mpsc_brg_disable(pi);
+	v = (pi->mirror_regs) ? pi->BRG_BCR_m : readl(pi->brg_base + BRG_BCR);
+	v = (v & 0xffff0000) | (cdv & 0xffff);
+
+	if (pi->mirror_regs)
+		pi->BRG_BCR_m = v;
+	writel(v, pi->brg_base + BRG_BCR);
+	mpsc_brg_enable(pi);
+}
+
+/*
+ ******************************************************************************
+ *
+ * Serial DMA Routines (SDMA)
+ *
+ ******************************************************************************
+ */
+
+static void mpsc_sdma_burstsize(struct mpsc_port_info *pi, u32 burst_size)
+{
+	u32	v;
+
+	pr_debug("mpsc_sdma_burstsize[%d]: burst_size: %d\n",
+			pi->port.line, burst_size);
+
+	burst_size >>= 3; /* Divide by 8 b/c reg values are 8-byte chunks */
+
+	if (burst_size < 2)
+		v = 0x0;	/* 1 64-bit word */
+	else if (burst_size < 4)
+		v = 0x1;	/* 2 64-bit words */
+	else if (burst_size < 8)
+		v = 0x2;	/* 4 64-bit words */
+	else
+		v = 0x3;	/* 8 64-bit words */
+
+	writel((readl(pi->sdma_base + SDMA_SDC) & (0x3 << 12)) | (v << 12),
+		pi->sdma_base + SDMA_SDC);
+}
+
+static void mpsc_sdma_init(struct mpsc_port_info *pi, u32 burst_size)
+{
+	pr_debug("mpsc_sdma_init[%d]: burst_size: %d\n", pi->port.line,
+		burst_size);
+
+	writel((readl(pi->sdma_base + SDMA_SDC) & 0x3ff) | 0x03f,
+		pi->sdma_base + SDMA_SDC);
+	mpsc_sdma_burstsize(pi, burst_size);
+}
+
+static u32 mpsc_sdma_intr_mask(struct mpsc_port_info *pi, u32 mask)
+{
+	u32	old, v;
+
+	pr_debug("mpsc_sdma_intr_mask[%d]: mask: 0x%x\n", pi->port.line, mask);
+
+	old = v = (pi->mirror_regs) ? pi->shared_regs->SDMA_INTR_MASK_m :
+		readl(pi->shared_regs->sdma_intr_base + SDMA_INTR_MASK);
+
+	mask &= 0xf;
+	if (pi->port.line)
+		mask <<= 8;
+	v &= ~mask;
+
+	if (pi->mirror_regs)
+		pi->shared_regs->SDMA_INTR_MASK_m = v;
+	writel(v, pi->shared_regs->sdma_intr_base + SDMA_INTR_MASK);
+
+	if (pi->port.line)
+		old >>= 8;
+	return old & 0xf;
+}
+
+static void mpsc_sdma_intr_unmask(struct mpsc_port_info *pi, u32 mask)
+{
+	u32	v;
+
+	pr_debug("mpsc_sdma_intr_unmask[%d]: mask: 0x%x\n", pi->port.line,mask);
+
+	v = (pi->mirror_regs) ? pi->shared_regs->SDMA_INTR_MASK_m
+		: readl(pi->shared_regs->sdma_intr_base + SDMA_INTR_MASK);
+
+	mask &= 0xf;
+	if (pi->port.line)
+		mask <<= 8;
+	v |= mask;
+
+	if (pi->mirror_regs)
+		pi->shared_regs->SDMA_INTR_MASK_m = v;
+	writel(v, pi->shared_regs->sdma_intr_base + SDMA_INTR_MASK);
+}
+
+static void mpsc_sdma_intr_ack(struct mpsc_port_info *pi)
+{
+	pr_debug("mpsc_sdma_intr_ack[%d]: Acknowledging IRQ\n", pi->port.line);
+
+	if (pi->mirror_regs)
+		pi->shared_regs->SDMA_INTR_CAUSE_m = 0;
+	writeb(0x00, pi->shared_regs->sdma_intr_base + SDMA_INTR_CAUSE
+			+ pi->port.line);
+}
+
+static void mpsc_sdma_set_rx_ring(struct mpsc_port_info *pi,
+		struct mpsc_rx_desc *rxre_p)
+{
+	pr_debug("mpsc_sdma_set_rx_ring[%d]: rxre_p: 0x%x\n",
+		pi->port.line, (u32)rxre_p);
+
+	writel((u32)rxre_p, pi->sdma_base + SDMA_SCRDP);
+}
+
+static void mpsc_sdma_set_tx_ring(struct mpsc_port_info *pi,
+		struct mpsc_tx_desc *txre_p)
+{
+	writel((u32)txre_p, pi->sdma_base + SDMA_SFTDP);
+	writel((u32)txre_p, pi->sdma_base + SDMA_SCTDP);
+}
+
+static void mpsc_sdma_cmd(struct mpsc_port_info *pi, u32 val)
+{
+	u32	v;
+
+	v = readl(pi->sdma_base + SDMA_SDCM);
+	if (val)
+		v |= val;
+	else
+		v = 0;
+	wmb();
+	writel(v, pi->sdma_base + SDMA_SDCM);
+	wmb();
+}
+
+static uint mpsc_sdma_tx_active(struct mpsc_port_info *pi)
+{
+	return readl(pi->sdma_base + SDMA_SDCM) & SDMA_SDCM_TXD;
+}
+
+static void mpsc_sdma_start_tx(struct mpsc_port_info *pi)
+{
+	struct mpsc_tx_desc *txre, *txre_p;
+
+	/* If tx isn't running & there's a desc ready to go, start it */
+	if (!mpsc_sdma_tx_active(pi)) {
+		txre = (struct mpsc_tx_desc *)(pi->txr
+				+ (pi->txr_tail * MPSC_TXRE_SIZE));
+		dma_cache_sync(pi->port.dev, (void *)txre, MPSC_TXRE_SIZE,
+				DMA_FROM_DEVICE);
+#if defined(CONFIG_PPC32) && !defined(CONFIG_NOT_COHERENT_CACHE)
+		if (pi->cache_mgmt) /* GT642[46]0 Res #COMM-2 */
+			invalidate_dcache_range((ulong)txre,
+					(ulong)txre + MPSC_TXRE_SIZE);
+#endif
+
+		if (be32_to_cpu(txre->cmdstat) & SDMA_DESC_CMDSTAT_O) {
+			txre_p = (struct mpsc_tx_desc *)
+				(pi->txr_p + (pi->txr_tail * MPSC_TXRE_SIZE));
+
+			mpsc_sdma_set_tx_ring(pi, txre_p);
+			mpsc_sdma_cmd(pi, SDMA_SDCM_STD | SDMA_SDCM_TXD);
+		}
+	}
+}
+
+static void mpsc_sdma_stop(struct mpsc_port_info *pi)
+{
+	pr_debug("mpsc_sdma_stop[%d]: Stopping SDMA\n", pi->port.line);
+
+	/* Abort any SDMA transfers */
+	mpsc_sdma_cmd(pi, 0);
+	mpsc_sdma_cmd(pi, SDMA_SDCM_AR | SDMA_SDCM_AT);
+
+	/* Clear the SDMA current and first TX and RX pointers */
+	mpsc_sdma_set_tx_ring(pi, NULL);
+	mpsc_sdma_set_rx_ring(pi, NULL);
+
+	/* Disable interrupts */
+	mpsc_sdma_intr_mask(pi, 0xf);
+	mpsc_sdma_intr_ack(pi);
+}
+
+/*
+ ******************************************************************************
+ *
+ * Multi-Protocol Serial Controller Routines (MPSC)
+ *
+ ******************************************************************************
+ */
+
+static void mpsc_hw_init(struct mpsc_port_info *pi)
+{
+	u32	v;
+
+	pr_debug("mpsc_hw_init[%d]: Initializing hardware\n", pi->port.line);
+
+	/* Set up clock routing */
+	if (pi->mirror_regs) {
+		v = pi->shared_regs->MPSC_MRR_m;
+		v &= ~0x1c7;
+		pi->shared_regs->MPSC_MRR_m = v;
+		writel(v, pi->shared_regs->mpsc_routing_base + MPSC_MRR);
+
+		v = pi->shared_regs->MPSC_RCRR_m;
+		v = (v & ~0xf0f) | 0x100;
+		pi->shared_regs->MPSC_RCRR_m = v;
+		writel(v, pi->shared_regs->mpsc_routing_base + MPSC_RCRR);
+
+		v = pi->shared_regs->MPSC_TCRR_m;
+		v = (v & ~0xf0f) | 0x100;
+		pi->shared_regs->MPSC_TCRR_m = v;
+		writel(v, pi->shared_regs->mpsc_routing_base + MPSC_TCRR);
+	} else {
+		v = readl(pi->shared_regs->mpsc_routing_base + MPSC_MRR);
+		v &= ~0x1c7;
+		writel(v, pi->shared_regs->mpsc_routing_base + MPSC_MRR);
+
+		v = readl(pi->shared_regs->mpsc_routing_base + MPSC_RCRR);
+		v = (v & ~0xf0f) | 0x100;
+		writel(v, pi->shared_regs->mpsc_routing_base + MPSC_RCRR);
+
+		v = readl(pi->shared_regs->mpsc_routing_base + MPSC_TCRR);
+		v = (v & ~0xf0f) | 0x100;
+		writel(v, pi->shared_regs->mpsc_routing_base + MPSC_TCRR);
+	}
+
+	/* Put MPSC in UART mode & enabel Tx/Rx egines */
+	writel(0x000004c4, pi->mpsc_base + MPSC_MMCRL);
+
+	/* No preamble, 16x divider, low-latency, */
+	writel(0x04400400, pi->mpsc_base + MPSC_MMCRH);
+	mpsc_set_baudrate(pi, pi->default_baud);
+
+	if (pi->mirror_regs) {
+		pi->MPSC_CHR_1_m = 0;
+		pi->MPSC_CHR_2_m = 0;
+	}
+	writel(0, pi->mpsc_base + MPSC_CHR_1);
+	writel(0, pi->mpsc_base + MPSC_CHR_2);
+	writel(pi->mpsc_max_idle, pi->mpsc_base + MPSC_CHR_3);
+	writel(0, pi->mpsc_base + MPSC_CHR_4);
+	writel(0, pi->mpsc_base + MPSC_CHR_5);
+	writel(0, pi->mpsc_base + MPSC_CHR_6);
+	writel(0, pi->mpsc_base + MPSC_CHR_7);
+	writel(0, pi->mpsc_base + MPSC_CHR_8);
+	writel(0, pi->mpsc_base + MPSC_CHR_9);
+	writel(0, pi->mpsc_base + MPSC_CHR_10);
+}
+
+static void mpsc_enter_hunt(struct mpsc_port_info *pi)
+{
+	pr_debug("mpsc_enter_hunt[%d]: Hunting...\n", pi->port.line);
+
+	if (pi->mirror_regs) {
+		writel(pi->MPSC_CHR_2_m | MPSC_CHR_2_EH,
+			pi->mpsc_base + MPSC_CHR_2);
+		/* Erratum prevents reading CHR_2 so just delay for a while */
+		udelay(100);
+	} else {
+		writel(readl(pi->mpsc_base + MPSC_CHR_2) | MPSC_CHR_2_EH,
+				pi->mpsc_base + MPSC_CHR_2);
+
+		while (readl(pi->mpsc_base + MPSC_CHR_2) & MPSC_CHR_2_EH)
+			udelay(10);
+	}
+}
+
+static void mpsc_freeze(struct mpsc_port_info *pi)
+{
+	u32	v;
+
+	pr_debug("mpsc_freeze[%d]: Freezing\n", pi->port.line);
+
+	v = (pi->mirror_regs) ? pi->MPSC_MPCR_m :
+		readl(pi->mpsc_base + MPSC_MPCR);
+	v |= MPSC_MPCR_FRZ;
+
+	if (pi->mirror_regs)
+		pi->MPSC_MPCR_m = v;
+	writel(v, pi->mpsc_base + MPSC_MPCR);
+}
+
+static void mpsc_unfreeze(struct mpsc_port_info *pi)
+{
+	u32	v;
+
+	v = (pi->mirror_regs) ? pi->MPSC_MPCR_m :
+		readl(pi->mpsc_base + MPSC_MPCR);
+	v &= ~MPSC_MPCR_FRZ;
+
+	if (pi->mirror_regs)
+		pi->MPSC_MPCR_m = v;
+	writel(v, pi->mpsc_base + MPSC_MPCR);
+
+	pr_debug("mpsc_unfreeze[%d]: Unfrozen\n", pi->port.line);
+}
+
+static void mpsc_set_char_length(struct mpsc_port_info *pi, u32 len)
+{
+	u32	v;
+
+	pr_debug("mpsc_set_char_length[%d]: char len: %d\n", pi->port.line,len);
+
+	v = (pi->mirror_regs) ? pi->MPSC_MPCR_m :
+		readl(pi->mpsc_base + MPSC_MPCR);
+	v = (v & ~(0x3 << 12)) | ((len & 0x3) << 12);
+
+	if (pi->mirror_regs)
+		pi->MPSC_MPCR_m = v;
+	writel(v, pi->mpsc_base + MPSC_MPCR);
+}
+
+static void mpsc_set_stop_bit_length(struct mpsc_port_info *pi, u32 len)
+{
+	u32	v;
+
+	pr_debug("mpsc_set_stop_bit_length[%d]: stop bits: %d\n",
+		pi->port.line, len);
+
+	v = (pi->mirror_regs) ? pi->MPSC_MPCR_m :
+		readl(pi->mpsc_base + MPSC_MPCR);
+
+	v = (v & ~(1 << 14)) | ((len & 0x1) << 14);
+
+	if (pi->mirror_regs)
+		pi->MPSC_MPCR_m = v;
+	writel(v, pi->mpsc_base + MPSC_MPCR);
+}
+
+static void mpsc_set_parity(struct mpsc_port_info *pi, u32 p)
+{
+	u32	v;
+
+	pr_debug("mpsc_set_parity[%d]: parity bits: 0x%x\n", pi->port.line, p);
+
+	v = (pi->mirror_regs) ? pi->MPSC_CHR_2_m :
+		readl(pi->mpsc_base + MPSC_CHR_2);
+
+	p &= 0x3;
+	v = (v & ~0xc000c) | (p << 18) | (p << 2);
+
+	if (pi->mirror_regs)
+		pi->MPSC_CHR_2_m = v;
+	writel(v, pi->mpsc_base + MPSC_CHR_2);
+}
+
+/*
+ ******************************************************************************
+ *
+ * Driver Init Routines
+ *
+ ******************************************************************************
+ */
+
+static void mpsc_init_hw(struct mpsc_port_info *pi)
+{
+	pr_debug("mpsc_init_hw[%d]: Initializing\n", pi->port.line);
+
+	mpsc_brg_init(pi, pi->brg_clk_src);
+	mpsc_brg_enable(pi);
+	mpsc_sdma_init(pi, dma_get_cache_alignment());	/* burst a cacheline */
+	mpsc_sdma_stop(pi);
+	mpsc_hw_init(pi);
+}
+
+static int mpsc_alloc_ring_mem(struct mpsc_port_info *pi)
+{
+	int rc = 0;
+
+	pr_debug("mpsc_alloc_ring_mem[%d]: Allocating ring mem\n",
+		pi->port.line);
+
+	if (!pi->dma_region) {
+		if (!dma_supported(pi->port.dev, 0xffffffff)) {
+			printk(KERN_ERR "MPSC: Inadequate DMA support\n");
+			rc = -ENXIO;
+		} else if ((pi->dma_region = dma_alloc_noncoherent(pi->port.dev,
+						MPSC_DMA_ALLOC_SIZE,
+						&pi->dma_region_p, GFP_KERNEL))
+				== NULL) {
+			printk(KERN_ERR "MPSC: Can't alloc Desc region\n");
+			rc = -ENOMEM;
+		}
+	}
+
+	return rc;
+}
+
+static void mpsc_free_ring_mem(struct mpsc_port_info *pi)
+{
+	pr_debug("mpsc_free_ring_mem[%d]: Freeing ring mem\n", pi->port.line);
+
+	if (pi->dma_region) {
+		dma_free_noncoherent(pi->port.dev, MPSC_DMA_ALLOC_SIZE,
+				pi->dma_region, pi->dma_region_p);
+		pi->dma_region = NULL;
+		pi->dma_region_p = (dma_addr_t)NULL;
+	}
+}
+
+static void mpsc_init_rings(struct mpsc_port_info *pi)
+{
+	struct mpsc_rx_desc *rxre;
+	struct mpsc_tx_desc *txre;
+	dma_addr_t dp, dp_p;
+	u8 *bp, *bp_p;
+	int i;
+
+	pr_debug("mpsc_init_rings[%d]: Initializing rings\n", pi->port.line);
+
+	BUG_ON(pi->dma_region == NULL);
+
+	memset(pi->dma_region, 0, MPSC_DMA_ALLOC_SIZE);
+
+	/*
+	 * Descriptors & buffers are multiples of cacheline size and must be
+	 * cacheline aligned.
+	 */
+	dp = ALIGN((u32)pi->dma_region, dma_get_cache_alignment());
+	dp_p = ALIGN((u32)pi->dma_region_p, dma_get_cache_alignment());
+
+	/*
+	 * Partition dma region into rx ring descriptor, rx buffers,
+	 * tx ring descriptors, and tx buffers.
+	 */
+	pi->rxr = dp;
+	pi->rxr_p = dp_p;
+	dp += MPSC_RXR_SIZE;
+	dp_p += MPSC_RXR_SIZE;
+
+	pi->rxb = (u8 *)dp;
+	pi->rxb_p = (u8 *)dp_p;
+	dp += MPSC_RXB_SIZE;
+	dp_p += MPSC_RXB_SIZE;
+
+	pi->rxr_posn = 0;
+
+	pi->txr = dp;
+	pi->txr_p = dp_p;
+	dp += MPSC_TXR_SIZE;
+	dp_p += MPSC_TXR_SIZE;
+
+	pi->txb = (u8 *)dp;
+	pi->txb_p = (u8 *)dp_p;
+
+	pi->txr_head = 0;
+	pi->txr_tail = 0;
+
+	/* Init rx ring descriptors */
+	dp = pi->rxr;
+	dp_p = pi->rxr_p;
+	bp = pi->rxb;
+	bp_p = pi->rxb_p;
+
+	for (i = 0; i < MPSC_RXR_ENTRIES; i++) {
+		rxre = (struct mpsc_rx_desc *)dp;
+
+		rxre->bufsize = cpu_to_be16(MPSC_RXBE_SIZE);
+		rxre->bytecnt = cpu_to_be16(0);
+		rxre->cmdstat = cpu_to_be32(SDMA_DESC_CMDSTAT_O
+				| SDMA_DESC_CMDSTAT_EI | SDMA_DESC_CMDSTAT_F
+				| SDMA_DESC_CMDSTAT_L);
+		rxre->link = cpu_to_be32(dp_p + MPSC_RXRE_SIZE);
+		rxre->buf_ptr = cpu_to_be32(bp_p);
+
+		dp += MPSC_RXRE_SIZE;
+		dp_p += MPSC_RXRE_SIZE;
+		bp += MPSC_RXBE_SIZE;
+		bp_p += MPSC_RXBE_SIZE;
+	}
+	rxre->link = cpu_to_be32(pi->rxr_p);	/* Wrap last back to first */
+
+	/* Init tx ring descriptors */
+	dp = pi->txr;
+	dp_p = pi->txr_p;
+	bp = pi->txb;
+	bp_p = pi->txb_p;
+
+	for (i = 0; i < MPSC_TXR_ENTRIES; i++) {
+		txre = (struct mpsc_tx_desc *)dp;
+
+		txre->link = cpu_to_be32(dp_p + MPSC_TXRE_SIZE);
+		txre->buf_ptr = cpu_to_be32(bp_p);
+
+		dp += MPSC_TXRE_SIZE;
+		dp_p += MPSC_TXRE_SIZE;
+		bp += MPSC_TXBE_SIZE;
+		bp_p += MPSC_TXBE_SIZE;
+	}
+	txre->link = cpu_to_be32(pi->txr_p);	/* Wrap last back to first */
+
+	dma_cache_sync(pi->port.dev, (void *)pi->dma_region,
+			MPSC_DMA_ALLOC_SIZE, DMA_BIDIRECTIONAL);
+#if defined(CONFIG_PPC32) && !defined(CONFIG_NOT_COHERENT_CACHE)
+		if (pi->cache_mgmt) /* GT642[46]0 Res #COMM-2 */
+			flush_dcache_range((ulong)pi->dma_region,
+					(ulong)pi->dma_region
+					+ MPSC_DMA_ALLOC_SIZE);
+#endif
+
+	return;
+}
+
+static void mpsc_uninit_rings(struct mpsc_port_info *pi)
+{
+	pr_debug("mpsc_uninit_rings[%d]: Uninitializing rings\n",pi->port.line);
+
+	BUG_ON(pi->dma_region == NULL);
+
+	pi->rxr = 0;
+	pi->rxr_p = 0;
+	pi->rxb = NULL;
+	pi->rxb_p = NULL;
+	pi->rxr_posn = 0;
+
+	pi->txr = 0;
+	pi->txr_p = 0;
+	pi->txb = NULL;
+	pi->txb_p = NULL;
+	pi->txr_head = 0;
+	pi->txr_tail = 0;
+}
+
+static int mpsc_make_ready(struct mpsc_port_info *pi)
+{
+	int rc;
+
+	pr_debug("mpsc_make_ready[%d]: Making cltr ready\n", pi->port.line);
+
+	if (!pi->ready) {
+		mpsc_init_hw(pi);
+		if ((rc = mpsc_alloc_ring_mem(pi)))
+			return rc;
+		mpsc_init_rings(pi);
+		pi->ready = 1;
+	}
+
+	return 0;
+}
+
+#ifdef CONFIG_CONSOLE_POLL
+static int serial_polled;
+#endif
+
+/*
+ ******************************************************************************
+ *
+ * Interrupt Handling Routines
+ *
+ ******************************************************************************
+ */
+
+static int mpsc_rx_intr(struct mpsc_port_info *pi)
+{
+	struct mpsc_rx_desc *rxre;
+	struct tty_struct *tty = pi->port.state->port.tty;
+	u32	cmdstat, bytes_in, i;
+	int	rc = 0;
+	u8	*bp;
+	char	flag = TTY_NORMAL;
+
+	pr_debug("mpsc_rx_intr[%d]: Handling Rx intr\n", pi->port.line);
+
+	rxre = (struct mpsc_rx_desc *)(pi->rxr + (pi->rxr_posn*MPSC_RXRE_SIZE));
+
+	dma_cache_sync(pi->port.dev, (void *)rxre, MPSC_RXRE_SIZE,
+			DMA_FROM_DEVICE);
+#if defined(CONFIG_PPC32) && !defined(CONFIG_NOT_COHERENT_CACHE)
+	if (pi->cache_mgmt) /* GT642[46]0 Res #COMM-2 */
+		invalidate_dcache_range((ulong)rxre,
+				(ulong)rxre + MPSC_RXRE_SIZE);
+#endif
+
+	/*
+	 * Loop through Rx descriptors handling ones that have been completed.
+	 */
+	while (!((cmdstat = be32_to_cpu(rxre->cmdstat))
+				& SDMA_DESC_CMDSTAT_O)) {
+		bytes_in = be16_to_cpu(rxre->bytecnt);
+#ifdef CONFIG_CONSOLE_POLL
+		if (unlikely(serial_polled)) {
+			serial_polled = 0;
+			return 0;
+		}
+#endif
+		/* Following use of tty struct directly is deprecated */
+		if (unlikely(tty_buffer_request_room(tty, bytes_in)
+					< bytes_in)) {
+			if (tty->low_latency)
+				tty_flip_buffer_push(tty);
+			/*
+			 * If this failed then we will throw away the bytes
+			 * but must do so to clear interrupts.
+			 */
+		}
+
+		bp = pi->rxb + (pi->rxr_posn * MPSC_RXBE_SIZE);
+		dma_cache_sync(pi->port.dev, (void *)bp, MPSC_RXBE_SIZE,
+				DMA_FROM_DEVICE);
+#if defined(CONFIG_PPC32) && !defined(CONFIG_NOT_COHERENT_CACHE)
+		if (pi->cache_mgmt) /* GT642[46]0 Res #COMM-2 */
+			invalidate_dcache_range((ulong)bp,
+					(ulong)bp + MPSC_RXBE_SIZE);
+#endif
+
+		/*
+		 * Other than for parity error, the manual provides little
+		 * info on what data will be in a frame flagged by any of
+		 * these errors.  For parity error, it is the last byte in
+		 * the buffer that had the error.  As for the rest, I guess
+		 * we'll assume there is no data in the buffer.
+		 * If there is...it gets lost.
+		 */
+		if (unlikely(cmdstat & (SDMA_DESC_CMDSTAT_BR
+						| SDMA_DESC_CMDSTAT_FR
+						| SDMA_DESC_CMDSTAT_OR))) {
+
+			pi->port.icount.rx++;
+
+			if (cmdstat & SDMA_DESC_CMDSTAT_BR) {	/* Break */
+				pi->port.icount.brk++;
+
+				if (uart_handle_break(&pi->port))
+					goto next_frame;
+			} else if (cmdstat & SDMA_DESC_CMDSTAT_FR) {
+				pi->port.icount.frame++;
+			} else if (cmdstat & SDMA_DESC_CMDSTAT_OR) {
+				pi->port.icount.overrun++;
+			}
+
+			cmdstat &= pi->port.read_status_mask;
+
+			if (cmdstat & SDMA_DESC_CMDSTAT_BR)
+				flag = TTY_BREAK;
+			else if (cmdstat & SDMA_DESC_CMDSTAT_FR)
+				flag = TTY_FRAME;
+			else if (cmdstat & SDMA_DESC_CMDSTAT_OR)
+				flag = TTY_OVERRUN;
+			else if (cmdstat & SDMA_DESC_CMDSTAT_PE)
+				flag = TTY_PARITY;
+		}
+
+		if (uart_handle_sysrq_char(&pi->port, *bp)) {
+			bp++;
+			bytes_in--;
+#ifdef CONFIG_CONSOLE_POLL
+			if (unlikely(serial_polled)) {
+				serial_polled = 0;
+				return 0;
+			}
+#endif
+			goto next_frame;
+		}
+
+		if ((unlikely(cmdstat & (SDMA_DESC_CMDSTAT_BR
+						| SDMA_DESC_CMDSTAT_FR
+						| SDMA_DESC_CMDSTAT_OR)))
+				&& !(cmdstat & pi->port.ignore_status_mask)) {
+			tty_insert_flip_char(tty, *bp, flag);
+		} else {
+			for (i=0; i<bytes_in; i++)
+				tty_insert_flip_char(tty, *bp++, TTY_NORMAL);
+
+			pi->port.icount.rx += bytes_in;
+		}
+
+next_frame:
+		rxre->bytecnt = cpu_to_be16(0);
+		wmb();
+		rxre->cmdstat = cpu_to_be32(SDMA_DESC_CMDSTAT_O
+				| SDMA_DESC_CMDSTAT_EI | SDMA_DESC_CMDSTAT_F
+				| SDMA_DESC_CMDSTAT_L);
+		wmb();
+		dma_cache_sync(pi->port.dev, (void *)rxre, MPSC_RXRE_SIZE,
+				DMA_BIDIRECTIONAL);
+#if defined(CONFIG_PPC32) && !defined(CONFIG_NOT_COHERENT_CACHE)
+		if (pi->cache_mgmt) /* GT642[46]0 Res #COMM-2 */
+			flush_dcache_range((ulong)rxre,
+					(ulong)rxre + MPSC_RXRE_SIZE);
+#endif
+
+		/* Advance to next descriptor */
+		pi->rxr_posn = (pi->rxr_posn + 1) & (MPSC_RXR_ENTRIES - 1);
+		rxre = (struct mpsc_rx_desc *)
+			(pi->rxr + (pi->rxr_posn * MPSC_RXRE_SIZE));
+		dma_cache_sync(pi->port.dev, (void *)rxre, MPSC_RXRE_SIZE,
+				DMA_FROM_DEVICE);
+#if defined(CONFIG_PPC32) && !defined(CONFIG_NOT_COHERENT_CACHE)
+		if (pi->cache_mgmt) /* GT642[46]0 Res #COMM-2 */
+			invalidate_dcache_range((ulong)rxre,
+					(ulong)rxre + MPSC_RXRE_SIZE);
+#endif
+		rc = 1;
+	}
+
+	/* Restart rx engine, if its stopped */
+	if ((readl(pi->sdma_base + SDMA_SDCM) & SDMA_SDCM_ERD) == 0)
+		mpsc_start_rx(pi);
+
+	tty_flip_buffer_push(tty);
+	return rc;
+}
+
+static void mpsc_setup_tx_desc(struct mpsc_port_info *pi, u32 count, u32 intr)
+{
+	struct mpsc_tx_desc *txre;
+
+	txre = (struct mpsc_tx_desc *)(pi->txr
+			+ (pi->txr_head * MPSC_TXRE_SIZE));
+
+	txre->bytecnt = cpu_to_be16(count);
+	txre->shadow = txre->bytecnt;
+	wmb();			/* ensure cmdstat is last field updated */
+	txre->cmdstat = cpu_to_be32(SDMA_DESC_CMDSTAT_O | SDMA_DESC_CMDSTAT_F
+			| SDMA_DESC_CMDSTAT_L
+			| ((intr) ? SDMA_DESC_CMDSTAT_EI : 0));
+	wmb();
+	dma_cache_sync(pi->port.dev, (void *)txre, MPSC_TXRE_SIZE,
+			DMA_BIDIRECTIONAL);
+#if defined(CONFIG_PPC32) && !defined(CONFIG_NOT_COHERENT_CACHE)
+	if (pi->cache_mgmt) /* GT642[46]0 Res #COMM-2 */
+		flush_dcache_range((ulong)txre,
+				(ulong)txre + MPSC_TXRE_SIZE);
+#endif
+}
+
+static void mpsc_copy_tx_data(struct mpsc_port_info *pi)
+{
+	struct circ_buf *xmit = &pi->port.state->xmit;
+	u8 *bp;
+	u32 i;
+
+	/* Make sure the desc ring isn't full */
+	while (CIRC_CNT(pi->txr_head, pi->txr_tail, MPSC_TXR_ENTRIES)
+			< (MPSC_TXR_ENTRIES - 1)) {
+		if (pi->port.x_char) {
+			/*
+			 * Ideally, we should use the TCS field in
+			 * CHR_1 to put the x_char out immediately but
+			 * errata prevents us from being able to read
+			 * CHR_2 to know that its safe to write to
+			 * CHR_1.  Instead, just put it in-band with
+			 * all the other Tx data.
+			 */
+			bp = pi->txb + (pi->txr_head * MPSC_TXBE_SIZE);
+			*bp = pi->port.x_char;
+			pi->port.x_char = 0;
+			i = 1;
+		} else if (!uart_circ_empty(xmit)
+				&& !uart_tx_stopped(&pi->port)) {
+			i = min((u32)MPSC_TXBE_SIZE,
+				(u32)uart_circ_chars_pending(xmit));
+			i = min(i, (u32)CIRC_CNT_TO_END(xmit->head, xmit->tail,
+				UART_XMIT_SIZE));
+			bp = pi->txb + (pi->txr_head * MPSC_TXBE_SIZE);
+			memcpy(bp, &xmit->buf[xmit->tail], i);
+			xmit->tail = (xmit->tail + i) & (UART_XMIT_SIZE - 1);
+
+			if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
+				uart_write_wakeup(&pi->port);
+		} else { /* All tx data copied into ring bufs */
+			return;
+		}
+
+		dma_cache_sync(pi->port.dev, (void *)bp, MPSC_TXBE_SIZE,
+				DMA_BIDIRECTIONAL);
+#if defined(CONFIG_PPC32) && !defined(CONFIG_NOT_COHERENT_CACHE)
+		if (pi->cache_mgmt) /* GT642[46]0 Res #COMM-2 */
+			flush_dcache_range((ulong)bp,
+					(ulong)bp + MPSC_TXBE_SIZE);
+#endif
+		mpsc_setup_tx_desc(pi, i, 1);
+
+		/* Advance to next descriptor */
+		pi->txr_head = (pi->txr_head + 1) & (MPSC_TXR_ENTRIES - 1);
+	}
+}
+
+static int mpsc_tx_intr(struct mpsc_port_info *pi)
+{
+	struct mpsc_tx_desc *txre;
+	int rc = 0;
+	unsigned long iflags;
+
+	spin_lock_irqsave(&pi->tx_lock, iflags);
+
+	if (!mpsc_sdma_tx_active(pi)) {
+		txre = (struct mpsc_tx_desc *)(pi->txr
+				+ (pi->txr_tail * MPSC_TXRE_SIZE));
+
+		dma_cache_sync(pi->port.dev, (void *)txre, MPSC_TXRE_SIZE,
+				DMA_FROM_DEVICE);
+#if defined(CONFIG_PPC32) && !defined(CONFIG_NOT_COHERENT_CACHE)
+		if (pi->cache_mgmt) /* GT642[46]0 Res #COMM-2 */
+			invalidate_dcache_range((ulong)txre,
+					(ulong)txre + MPSC_TXRE_SIZE);
+#endif
+
+		while (!(be32_to_cpu(txre->cmdstat) & SDMA_DESC_CMDSTAT_O)) {
+			rc = 1;
+			pi->port.icount.tx += be16_to_cpu(txre->bytecnt);
+			pi->txr_tail = (pi->txr_tail+1) & (MPSC_TXR_ENTRIES-1);
+
+			/* If no more data to tx, fall out of loop */
+			if (pi->txr_head == pi->txr_tail)
+				break;
+
+			txre = (struct mpsc_tx_desc *)(pi->txr
+					+ (pi->txr_tail * MPSC_TXRE_SIZE));
+			dma_cache_sync(pi->port.dev, (void *)txre,
+					MPSC_TXRE_SIZE, DMA_FROM_DEVICE);
+#if defined(CONFIG_PPC32) && !defined(CONFIG_NOT_COHERENT_CACHE)
+			if (pi->cache_mgmt) /* GT642[46]0 Res #COMM-2 */
+				invalidate_dcache_range((ulong)txre,
+						(ulong)txre + MPSC_TXRE_SIZE);
+#endif
+		}
+
+		mpsc_copy_tx_data(pi);
+		mpsc_sdma_start_tx(pi);	/* start next desc if ready */
+	}
+
+	spin_unlock_irqrestore(&pi->tx_lock, iflags);
+	return rc;
+}
+
+/*
+ * This is the driver's interrupt handler.  To avoid a race, we first clear
+ * the interrupt, then handle any completed Rx/Tx descriptors.  When done
+ * handling those descriptors, we restart the Rx/Tx engines if they're stopped.
+ */
+static irqreturn_t mpsc_sdma_intr(int irq, void *dev_id)
+{
+	struct mpsc_port_info *pi = dev_id;
+	ulong iflags;
+	int rc = IRQ_NONE;
+
+	pr_debug("mpsc_sdma_intr[%d]: SDMA Interrupt Received\n",pi->port.line);
+
+	spin_lock_irqsave(&pi->port.lock, iflags);
+	mpsc_sdma_intr_ack(pi);
+	if (mpsc_rx_intr(pi))
+		rc = IRQ_HANDLED;
+	if (mpsc_tx_intr(pi))
+		rc = IRQ_HANDLED;
+	spin_unlock_irqrestore(&pi->port.lock, iflags);
+
+	pr_debug("mpsc_sdma_intr[%d]: SDMA Interrupt Handled\n", pi->port.line);
+	return rc;
+}
+
+/*
+ ******************************************************************************
+ *
+ * serial_core.c Interface routines
+ *
+ ******************************************************************************
+ */
+static uint mpsc_tx_empty(struct uart_port *port)
+{
+	struct mpsc_port_info *pi = (struct mpsc_port_info *)port;
+	ulong iflags;
+	uint rc;
+
+	spin_lock_irqsave(&pi->port.lock, iflags);
+	rc = mpsc_sdma_tx_active(pi) ? 0 : TIOCSER_TEMT;
+	spin_unlock_irqrestore(&pi->port.lock, iflags);
+
+	return rc;
+}
+
+static void mpsc_set_mctrl(struct uart_port *port, uint mctrl)
+{
+	/* Have no way to set modem control lines AFAICT */
+}
+
+static uint mpsc_get_mctrl(struct uart_port *port)
+{
+	struct mpsc_port_info *pi = (struct mpsc_port_info *)port;
+	u32 mflags, status;
+
+	status = (pi->mirror_regs) ? pi->MPSC_CHR_10_m
+		: readl(pi->mpsc_base + MPSC_CHR_10);
+
+	mflags = 0;
+	if (status & 0x1)
+		mflags |= TIOCM_CTS;
+	if (status & 0x2)
+		mflags |= TIOCM_CAR;
+
+	return mflags | TIOCM_DSR;	/* No way to tell if DSR asserted */
+}
+
+static void mpsc_stop_tx(struct uart_port *port)
+{
+	struct mpsc_port_info *pi = (struct mpsc_port_info *)port;
+
+	pr_debug("mpsc_stop_tx[%d]\n", port->line);
+
+	mpsc_freeze(pi);
+}
+
+static void mpsc_start_tx(struct uart_port *port)
+{
+	struct mpsc_port_info *pi = (struct mpsc_port_info *)port;
+	unsigned long iflags;
+
+	spin_lock_irqsave(&pi->tx_lock, iflags);
+
+	mpsc_unfreeze(pi);
+	mpsc_copy_tx_data(pi);
+	mpsc_sdma_start_tx(pi);
+
+	spin_unlock_irqrestore(&pi->tx_lock, iflags);
+
+	pr_debug("mpsc_start_tx[%d]\n", port->line);
+}
+
+static void mpsc_start_rx(struct mpsc_port_info *pi)
+{
+	pr_debug("mpsc_start_rx[%d]: Starting...\n", pi->port.line);
+
+	if (pi->rcv_data) {
+		mpsc_enter_hunt(pi);
+		mpsc_sdma_cmd(pi, SDMA_SDCM_ERD);
+	}
+}
+
+static void mpsc_stop_rx(struct uart_port *port)
+{
+	struct mpsc_port_info *pi = (struct mpsc_port_info *)port;
+
+	pr_debug("mpsc_stop_rx[%d]: Stopping...\n", port->line);
+
+	if (pi->mirror_regs) {
+		writel(pi->MPSC_CHR_2_m | MPSC_CHR_2_RA,
+				pi->mpsc_base + MPSC_CHR_2);
+		/* Erratum prevents reading CHR_2 so just delay for a while */
+		udelay(100);
+	} else {
+		writel(readl(pi->mpsc_base + MPSC_CHR_2) | MPSC_CHR_2_RA,
+				pi->mpsc_base + MPSC_CHR_2);
+
+		while (readl(pi->mpsc_base + MPSC_CHR_2) & MPSC_CHR_2_RA)
+			udelay(10);
+	}
+
+	mpsc_sdma_cmd(pi, SDMA_SDCM_AR);
+}
+
+static void mpsc_enable_ms(struct uart_port *port)
+{
+}
+
+static void mpsc_break_ctl(struct uart_port *port, int ctl)
+{
+	struct mpsc_port_info *pi = (struct mpsc_port_info *)port;
+	ulong	flags;
+	u32	v;
+
+	v = ctl ? 0x00ff0000 : 0;
+
+	spin_lock_irqsave(&pi->port.lock, flags);
+	if (pi->mirror_regs)
+		pi->MPSC_CHR_1_m = v;
+	writel(v, pi->mpsc_base + MPSC_CHR_1);
+	spin_unlock_irqrestore(&pi->port.lock, flags);
+}
+
+static int mpsc_startup(struct uart_port *port)
+{
+	struct mpsc_port_info *pi = (struct mpsc_port_info *)port;
+	u32 flag = 0;
+	int rc;
+
+	pr_debug("mpsc_startup[%d]: Starting up MPSC, irq: %d\n",
+		port->line, pi->port.irq);
+
+	if ((rc = mpsc_make_ready(pi)) == 0) {
+		/* Setup IRQ handler */
+		mpsc_sdma_intr_ack(pi);
+
+		/* If irq's are shared, need to set flag */
+		if (mpsc_ports[0].port.irq == mpsc_ports[1].port.irq)
+			flag = IRQF_SHARED;
+
+		if (request_irq(pi->port.irq, mpsc_sdma_intr, flag,
+					"mpsc-sdma", pi))
+			printk(KERN_ERR "MPSC: Can't get SDMA IRQ %d\n",
+					pi->port.irq);
+
+		mpsc_sdma_intr_unmask(pi, 0xf);
+		mpsc_sdma_set_rx_ring(pi, (struct mpsc_rx_desc *)(pi->rxr_p
+					+ (pi->rxr_posn * MPSC_RXRE_SIZE)));
+	}
+
+	return rc;
+}
+
+static void mpsc_shutdown(struct uart_port *port)
+{
+	struct mpsc_port_info *pi = (struct mpsc_port_info *)port;
+
+	pr_debug("mpsc_shutdown[%d]: Shutting down MPSC\n", port->line);
+
+	mpsc_sdma_stop(pi);
+	free_irq(pi->port.irq, pi);
+}
+
+static void mpsc_set_termios(struct uart_port *port, struct ktermios *termios,
+		 struct ktermios *old)
+{
+	struct mpsc_port_info *pi = (struct mpsc_port_info *)port;
+	u32 baud;
+	ulong flags;
+	u32 chr_bits, stop_bits, par;
+
+	pi->c_iflag = termios->c_iflag;
+	pi->c_cflag = termios->c_cflag;
+
+	switch (termios->c_cflag & CSIZE) {
+	case CS5:
+		chr_bits = MPSC_MPCR_CL_5;
+		break;
+	case CS6:
+		chr_bits = MPSC_MPCR_CL_6;
+		break;
+	case CS7:
+		chr_bits = MPSC_MPCR_CL_7;
+		break;
+	case CS8:
+	default:
+		chr_bits = MPSC_MPCR_CL_8;
+		break;
+	}
+
+	if (termios->c_cflag & CSTOPB)
+		stop_bits = MPSC_MPCR_SBL_2;
+	else
+		stop_bits = MPSC_MPCR_SBL_1;
+
+	par = MPSC_CHR_2_PAR_EVEN;
+	if (termios->c_cflag & PARENB)
+		if (termios->c_cflag & PARODD)
+			par = MPSC_CHR_2_PAR_ODD;
+#ifdef	CMSPAR
+		if (termios->c_cflag & CMSPAR) {
+			if (termios->c_cflag & PARODD)
+				par = MPSC_CHR_2_PAR_MARK;
+			else
+				par = MPSC_CHR_2_PAR_SPACE;
+		}
+#endif
+
+	baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk);
+
+	spin_lock_irqsave(&pi->port.lock, flags);
+
+	uart_update_timeout(port, termios->c_cflag, baud);
+
+	mpsc_set_char_length(pi, chr_bits);
+	mpsc_set_stop_bit_length(pi, stop_bits);
+	mpsc_set_parity(pi, par);
+	mpsc_set_baudrate(pi, baud);
+
+	/* Characters/events to read */
+	pi->port.read_status_mask = SDMA_DESC_CMDSTAT_OR;
+
+	if (termios->c_iflag & INPCK)
+		pi->port.read_status_mask |= SDMA_DESC_CMDSTAT_PE
+			| SDMA_DESC_CMDSTAT_FR;
+
+	if (termios->c_iflag & (BRKINT | PARMRK))
+		pi->port.read_status_mask |= SDMA_DESC_CMDSTAT_BR;
+
+	/* Characters/events to ignore */
+	pi->port.ignore_status_mask = 0;
+
+	if (termios->c_iflag & IGNPAR)
+		pi->port.ignore_status_mask |= SDMA_DESC_CMDSTAT_PE
+			| SDMA_DESC_CMDSTAT_FR;
+
+	if (termios->c_iflag & IGNBRK) {
+		pi->port.ignore_status_mask |= SDMA_DESC_CMDSTAT_BR;
+
+		if (termios->c_iflag & IGNPAR)
+			pi->port.ignore_status_mask |= SDMA_DESC_CMDSTAT_OR;
+	}
+
+	if ((termios->c_cflag & CREAD)) {
+		if (!pi->rcv_data) {
+			pi->rcv_data = 1;
+			mpsc_start_rx(pi);
+		}
+	} else if (pi->rcv_data) {
+		mpsc_stop_rx(port);
+		pi->rcv_data = 0;
+	}
+
+	spin_unlock_irqrestore(&pi->port.lock, flags);
+}
+
+static const char *mpsc_type(struct uart_port *port)
+{
+	pr_debug("mpsc_type[%d]: port type: %s\n", port->line,MPSC_DRIVER_NAME);
+	return MPSC_DRIVER_NAME;
+}
+
+static int mpsc_request_port(struct uart_port *port)
+{
+	/* Should make chip/platform specific call */
+	return 0;
+}
+
+static void mpsc_release_port(struct uart_port *port)
+{
+	struct mpsc_port_info *pi = (struct mpsc_port_info *)port;
+
+	if (pi->ready) {
+		mpsc_uninit_rings(pi);
+		mpsc_free_ring_mem(pi);
+		pi->ready = 0;
+	}
+}
+
+static void mpsc_config_port(struct uart_port *port, int flags)
+{
+}
+
+static int mpsc_verify_port(struct uart_port *port, struct serial_struct *ser)
+{
+	struct mpsc_port_info *pi = (struct mpsc_port_info *)port;
+	int rc = 0;
+
+	pr_debug("mpsc_verify_port[%d]: Verifying port data\n", pi->port.line);
+
+	if (ser->type != PORT_UNKNOWN && ser->type != PORT_MPSC)
+		rc = -EINVAL;
+	else if (pi->port.irq != ser->irq)
+		rc = -EINVAL;
+	else if (ser->io_type != SERIAL_IO_MEM)
+		rc = -EINVAL;
+	else if (pi->port.uartclk / 16 != ser->baud_base) /* Not sure */
+		rc = -EINVAL;
+	else if ((void *)pi->port.mapbase != ser->iomem_base)
+		rc = -EINVAL;
+	else if (pi->port.iobase != ser->port)
+		rc = -EINVAL;
+	else if (ser->hub6 != 0)
+		rc = -EINVAL;
+
+	return rc;
+}
+#ifdef CONFIG_CONSOLE_POLL
+/* Serial polling routines for writing and reading from the uart while
+ * in an interrupt or debug context.
+ */
+
+static char poll_buf[2048];
+static int poll_ptr;
+static int poll_cnt;
+static void mpsc_put_poll_char(struct uart_port *port,
+							   unsigned char c);
+
+static int mpsc_get_poll_char(struct uart_port *port)
+{
+	struct mpsc_port_info *pi = (struct mpsc_port_info *)port;
+	struct mpsc_rx_desc *rxre;
+	u32	cmdstat, bytes_in, i;
+	u8	*bp;
+
+	if (!serial_polled)
+		serial_polled = 1;
+
+	pr_debug("mpsc_rx_intr[%d]: Handling Rx intr\n", pi->port.line);
+
+	if (poll_cnt) {
+		poll_cnt--;
+		return poll_buf[poll_ptr++];
+	}
+	poll_ptr = 0;
+	poll_cnt = 0;
+
+	while (poll_cnt == 0) {
+		rxre = (struct mpsc_rx_desc *)(pi->rxr +
+		       (pi->rxr_posn*MPSC_RXRE_SIZE));
+		dma_cache_sync(pi->port.dev, (void *)rxre,
+			       MPSC_RXRE_SIZE, DMA_FROM_DEVICE);
+#if defined(CONFIG_PPC32) && !defined(CONFIG_NOT_COHERENT_CACHE)
+		if (pi->cache_mgmt) /* GT642[46]0 Res #COMM-2 */
+			invalidate_dcache_range((ulong)rxre,
+			(ulong)rxre + MPSC_RXRE_SIZE);
+#endif
+		/*
+		 * Loop through Rx descriptors handling ones that have
+		 * been completed.
+		 */
+		while (poll_cnt == 0 &&
+		       !((cmdstat = be32_to_cpu(rxre->cmdstat)) &
+			 SDMA_DESC_CMDSTAT_O)){
+			bytes_in = be16_to_cpu(rxre->bytecnt);
+			bp = pi->rxb + (pi->rxr_posn * MPSC_RXBE_SIZE);
+			dma_cache_sync(pi->port.dev, (void *) bp,
+				       MPSC_RXBE_SIZE, DMA_FROM_DEVICE);
+#if defined(CONFIG_PPC32) && !defined(CONFIG_NOT_COHERENT_CACHE)
+			if (pi->cache_mgmt) /* GT642[46]0 Res #COMM-2 */
+				invalidate_dcache_range((ulong)bp,
+					(ulong)bp + MPSC_RXBE_SIZE);
+#endif
+			if ((unlikely(cmdstat & (SDMA_DESC_CMDSTAT_BR |
+			 SDMA_DESC_CMDSTAT_FR | SDMA_DESC_CMDSTAT_OR))) &&
+				!(cmdstat & pi->port.ignore_status_mask)) {
+				poll_buf[poll_cnt] = *bp;
+				poll_cnt++;
+			} else {
+				for (i = 0; i < bytes_in; i++) {
+					poll_buf[poll_cnt] = *bp++;
+					poll_cnt++;
+				}
+				pi->port.icount.rx += bytes_in;
+			}
+			rxre->bytecnt = cpu_to_be16(0);
+			wmb();
+			rxre->cmdstat = cpu_to_be32(SDMA_DESC_CMDSTAT_O |
+						    SDMA_DESC_CMDSTAT_EI |
+						    SDMA_DESC_CMDSTAT_F |
+						    SDMA_DESC_CMDSTAT_L);
+			wmb();
+			dma_cache_sync(pi->port.dev, (void *)rxre,
+				       MPSC_RXRE_SIZE, DMA_BIDIRECTIONAL);
+#if defined(CONFIG_PPC32) && !defined(CONFIG_NOT_COHERENT_CACHE)
+			if (pi->cache_mgmt) /* GT642[46]0 Res #COMM-2 */
+				flush_dcache_range((ulong)rxre,
+					   (ulong)rxre + MPSC_RXRE_SIZE);
+#endif
+
+			/* Advance to next descriptor */
+			pi->rxr_posn = (pi->rxr_posn + 1) &
+				(MPSC_RXR_ENTRIES - 1);
+			rxre = (struct mpsc_rx_desc *)(pi->rxr +
+				       (pi->rxr_posn * MPSC_RXRE_SIZE));
+			dma_cache_sync(pi->port.dev, (void *)rxre,
+				       MPSC_RXRE_SIZE, DMA_FROM_DEVICE);
+#if defined(CONFIG_PPC32) && !defined(CONFIG_NOT_COHERENT_CACHE)
+			if (pi->cache_mgmt) /* GT642[46]0 Res #COMM-2 */
+				invalidate_dcache_range((ulong)rxre,
+						(ulong)rxre + MPSC_RXRE_SIZE);
+#endif
+		}
+
+		/* Restart rx engine, if its stopped */
+		if ((readl(pi->sdma_base + SDMA_SDCM) & SDMA_SDCM_ERD) == 0)
+			mpsc_start_rx(pi);
+	}
+	if (poll_cnt) {
+		poll_cnt--;
+		return poll_buf[poll_ptr++];
+	}
+
+	return 0;
+}
+
+
+static void mpsc_put_poll_char(struct uart_port *port,
+			 unsigned char c)
+{
+	struct mpsc_port_info *pi = (struct mpsc_port_info *)port;
+	u32 data;
+
+	data = readl(pi->mpsc_base + MPSC_MPCR);
+	writeb(c, pi->mpsc_base + MPSC_CHR_1);
+	mb();
+	data = readl(pi->mpsc_base + MPSC_CHR_2);
+	data |= MPSC_CHR_2_TTCS;
+	writel(data, pi->mpsc_base + MPSC_CHR_2);
+	mb();
+
+	while (readl(pi->mpsc_base + MPSC_CHR_2) & MPSC_CHR_2_TTCS);
+}
+#endif
+
+static struct uart_ops mpsc_pops = {
+	.tx_empty	= mpsc_tx_empty,
+	.set_mctrl	= mpsc_set_mctrl,
+	.get_mctrl	= mpsc_get_mctrl,
+	.stop_tx	= mpsc_stop_tx,
+	.start_tx	= mpsc_start_tx,
+	.stop_rx	= mpsc_stop_rx,
+	.enable_ms	= mpsc_enable_ms,
+	.break_ctl	= mpsc_break_ctl,
+	.startup	= mpsc_startup,
+	.shutdown	= mpsc_shutdown,
+	.set_termios	= mpsc_set_termios,
+	.type		= mpsc_type,
+	.release_port	= mpsc_release_port,
+	.request_port	= mpsc_request_port,
+	.config_port	= mpsc_config_port,
+	.verify_port	= mpsc_verify_port,
+#ifdef CONFIG_CONSOLE_POLL
+	.poll_get_char = mpsc_get_poll_char,
+	.poll_put_char = mpsc_put_poll_char,
+#endif
+};
+
+/*
+ ******************************************************************************
+ *
+ * Console Interface Routines
+ *
+ ******************************************************************************
+ */
+
+#ifdef CONFIG_SERIAL_MPSC_CONSOLE
+static void mpsc_console_write(struct console *co, const char *s, uint count)
+{
+	struct mpsc_port_info *pi = &mpsc_ports[co->index];
+	u8 *bp, *dp, add_cr = 0;
+	int i;
+	unsigned long iflags;
+
+	spin_lock_irqsave(&pi->tx_lock, iflags);
+
+	while (pi->txr_head != pi->txr_tail) {
+		while (mpsc_sdma_tx_active(pi))
+			udelay(100);
+		mpsc_sdma_intr_ack(pi);
+		mpsc_tx_intr(pi);
+	}
+
+	while (mpsc_sdma_tx_active(pi))
+		udelay(100);
+
+	while (count > 0) {
+		bp = dp = pi->txb + (pi->txr_head * MPSC_TXBE_SIZE);
+
+		for (i = 0; i < MPSC_TXBE_SIZE; i++) {
+			if (count == 0)
+				break;
+
+			if (add_cr) {
+				*(dp++) = '\r';
+				add_cr = 0;
+			} else {
+				*(dp++) = *s;
+
+				if (*(s++) == '\n') { /* add '\r' after '\n' */
+					add_cr = 1;
+					count++;
+				}
+			}
+
+			count--;
+		}
+
+		dma_cache_sync(pi->port.dev, (void *)bp, MPSC_TXBE_SIZE,
+				DMA_BIDIRECTIONAL);
+#if defined(CONFIG_PPC32) && !defined(CONFIG_NOT_COHERENT_CACHE)
+		if (pi->cache_mgmt) /* GT642[46]0 Res #COMM-2 */
+			flush_dcache_range((ulong)bp,
+					(ulong)bp + MPSC_TXBE_SIZE);
+#endif
+		mpsc_setup_tx_desc(pi, i, 0);
+		pi->txr_head = (pi->txr_head + 1) & (MPSC_TXR_ENTRIES - 1);
+		mpsc_sdma_start_tx(pi);
+
+		while (mpsc_sdma_tx_active(pi))
+			udelay(100);
+
+		pi->txr_tail = (pi->txr_tail + 1) & (MPSC_TXR_ENTRIES - 1);
+	}
+
+	spin_unlock_irqrestore(&pi->tx_lock, iflags);
+}
+
+static int __init mpsc_console_setup(struct console *co, char *options)
+{
+	struct mpsc_port_info *pi;
+	int baud, bits, parity, flow;
+
+	pr_debug("mpsc_console_setup[%d]: options: %s\n", co->index, options);
+
+	if (co->index >= MPSC_NUM_CTLRS)
+		co->index = 0;
+
+	pi = &mpsc_ports[co->index];
+
+	baud = pi->default_baud;
+	bits = pi->default_bits;
+	parity = pi->default_parity;
+	flow = pi->default_flow;
+
+	if (!pi->port.ops)
+		return -ENODEV;
+
+	spin_lock_init(&pi->port.lock);	/* Temporary fix--copied from 8250.c */
+
+	if (options)
+		uart_parse_options(options, &baud, &parity, &bits, &flow);
+
+	return uart_set_options(&pi->port, co, baud, parity, bits, flow);
+}
+
+static struct console mpsc_console = {
+	.name	= MPSC_DEV_NAME,
+	.write	= mpsc_console_write,
+	.device	= uart_console_device,
+	.setup	= mpsc_console_setup,
+	.flags	= CON_PRINTBUFFER,
+	.index	= -1,
+	.data	= &mpsc_reg,
+};
+
+static int __init mpsc_late_console_init(void)
+{
+	pr_debug("mpsc_late_console_init: Enter\n");
+
+	if (!(mpsc_console.flags & CON_ENABLED))
+		register_console(&mpsc_console);
+	return 0;
+}
+
+late_initcall(mpsc_late_console_init);
+
+#define MPSC_CONSOLE	&mpsc_console
+#else
+#define MPSC_CONSOLE	NULL
+#endif
+/*
+ ******************************************************************************
+ *
+ * Dummy Platform Driver to extract & map shared register regions
+ *
+ ******************************************************************************
+ */
+static void mpsc_resource_err(char *s)
+{
+	printk(KERN_WARNING "MPSC: Platform device resource error in %s\n", s);
+}
+
+static int mpsc_shared_map_regs(struct platform_device *pd)
+{
+	struct resource	*r;
+
+	if ((r = platform_get_resource(pd, IORESOURCE_MEM,
+					MPSC_ROUTING_BASE_ORDER))
+			&& request_mem_region(r->start,
+				MPSC_ROUTING_REG_BLOCK_SIZE,
+				"mpsc_routing_regs")) {
+		mpsc_shared_regs.mpsc_routing_base = ioremap(r->start,
+				MPSC_ROUTING_REG_BLOCK_SIZE);
+		mpsc_shared_regs.mpsc_routing_base_p = r->start;
+	} else {
+		mpsc_resource_err("MPSC routing base");
+		return -ENOMEM;
+	}
+
+	if ((r = platform_get_resource(pd, IORESOURCE_MEM,
+					MPSC_SDMA_INTR_BASE_ORDER))
+			&& request_mem_region(r->start,
+				MPSC_SDMA_INTR_REG_BLOCK_SIZE,
+				"sdma_intr_regs")) {
+		mpsc_shared_regs.sdma_intr_base = ioremap(r->start,
+			MPSC_SDMA_INTR_REG_BLOCK_SIZE);
+		mpsc_shared_regs.sdma_intr_base_p = r->start;
+	} else {
+		iounmap(mpsc_shared_regs.mpsc_routing_base);
+		release_mem_region(mpsc_shared_regs.mpsc_routing_base_p,
+				MPSC_ROUTING_REG_BLOCK_SIZE);
+		mpsc_resource_err("SDMA intr base");
+		return -ENOMEM;
+	}
+
+	return 0;
+}
+
+static void mpsc_shared_unmap_regs(void)
+{
+	if (!mpsc_shared_regs.mpsc_routing_base) {
+		iounmap(mpsc_shared_regs.mpsc_routing_base);
+		release_mem_region(mpsc_shared_regs.mpsc_routing_base_p,
+				MPSC_ROUTING_REG_BLOCK_SIZE);
+	}
+	if (!mpsc_shared_regs.sdma_intr_base) {
+		iounmap(mpsc_shared_regs.sdma_intr_base);
+		release_mem_region(mpsc_shared_regs.sdma_intr_base_p,
+				MPSC_SDMA_INTR_REG_BLOCK_SIZE);
+	}
+
+	mpsc_shared_regs.mpsc_routing_base = NULL;
+	mpsc_shared_regs.sdma_intr_base = NULL;
+
+	mpsc_shared_regs.mpsc_routing_base_p = 0;
+	mpsc_shared_regs.sdma_intr_base_p = 0;
+}
+
+static int mpsc_shared_drv_probe(struct platform_device *dev)
+{
+	struct mpsc_shared_pdata	*pdata;
+	int				 rc = -ENODEV;
+
+	if (dev->id == 0) {
+		if (!(rc = mpsc_shared_map_regs(dev))) {
+			pdata = (struct mpsc_shared_pdata *)
+				dev->dev.platform_data;
+
+			mpsc_shared_regs.MPSC_MRR_m = pdata->mrr_val;
+			mpsc_shared_regs.MPSC_RCRR_m= pdata->rcrr_val;
+			mpsc_shared_regs.MPSC_TCRR_m= pdata->tcrr_val;
+			mpsc_shared_regs.SDMA_INTR_CAUSE_m =
+				pdata->intr_cause_val;
+			mpsc_shared_regs.SDMA_INTR_MASK_m =
+				pdata->intr_mask_val;
+
+			rc = 0;
+		}
+	}
+
+	return rc;
+}
+
+static int mpsc_shared_drv_remove(struct platform_device *dev)
+{
+	int	rc = -ENODEV;
+
+	if (dev->id == 0) {
+		mpsc_shared_unmap_regs();
+		mpsc_shared_regs.MPSC_MRR_m = 0;
+		mpsc_shared_regs.MPSC_RCRR_m = 0;
+		mpsc_shared_regs.MPSC_TCRR_m = 0;
+		mpsc_shared_regs.SDMA_INTR_CAUSE_m = 0;
+		mpsc_shared_regs.SDMA_INTR_MASK_m = 0;
+		rc = 0;
+	}
+
+	return rc;
+}
+
+static struct platform_driver mpsc_shared_driver = {
+	.probe	= mpsc_shared_drv_probe,
+	.remove	= mpsc_shared_drv_remove,
+	.driver	= {
+		.name	= MPSC_SHARED_NAME,
+	},
+};
+
+/*
+ ******************************************************************************
+ *
+ * Driver Interface Routines
+ *
+ ******************************************************************************
+ */
+static struct uart_driver mpsc_reg = {
+	.owner		= THIS_MODULE,
+	.driver_name	= MPSC_DRIVER_NAME,
+	.dev_name	= MPSC_DEV_NAME,
+	.major		= MPSC_MAJOR,
+	.minor		= MPSC_MINOR_START,
+	.nr		= MPSC_NUM_CTLRS,
+	.cons		= MPSC_CONSOLE,
+};
+
+static int mpsc_drv_map_regs(struct mpsc_port_info *pi,
+		struct platform_device *pd)
+{
+	struct resource	*r;
+
+	if ((r = platform_get_resource(pd, IORESOURCE_MEM, MPSC_BASE_ORDER))
+			&& request_mem_region(r->start, MPSC_REG_BLOCK_SIZE,
+			"mpsc_regs")) {
+		pi->mpsc_base = ioremap(r->start, MPSC_REG_BLOCK_SIZE);
+		pi->mpsc_base_p = r->start;
+	} else {
+		mpsc_resource_err("MPSC base");
+		goto err;
+	}
+
+	if ((r = platform_get_resource(pd, IORESOURCE_MEM,
+					MPSC_SDMA_BASE_ORDER))
+			&& request_mem_region(r->start,
+				MPSC_SDMA_REG_BLOCK_SIZE, "sdma_regs")) {
+		pi->sdma_base = ioremap(r->start,MPSC_SDMA_REG_BLOCK_SIZE);
+		pi->sdma_base_p = r->start;
+	} else {
+		mpsc_resource_err("SDMA base");
+		if (pi->mpsc_base) {
+			iounmap(pi->mpsc_base);
+			pi->mpsc_base = NULL;
+		}
+		goto err;
+	}
+
+	if ((r = platform_get_resource(pd,IORESOURCE_MEM,MPSC_BRG_BASE_ORDER))
+			&& request_mem_region(r->start,
+				MPSC_BRG_REG_BLOCK_SIZE, "brg_regs")) {
+		pi->brg_base = ioremap(r->start, MPSC_BRG_REG_BLOCK_SIZE);
+		pi->brg_base_p = r->start;
+	} else {
+		mpsc_resource_err("BRG base");
+		if (pi->mpsc_base) {
+			iounmap(pi->mpsc_base);
+			pi->mpsc_base = NULL;
+		}
+		if (pi->sdma_base) {
+			iounmap(pi->sdma_base);
+			pi->sdma_base = NULL;
+		}
+		goto err;
+	}
+	return 0;
+
+err:
+	return -ENOMEM;
+}
+
+static void mpsc_drv_unmap_regs(struct mpsc_port_info *pi)
+{
+	if (!pi->mpsc_base) {
+		iounmap(pi->mpsc_base);
+		release_mem_region(pi->mpsc_base_p, MPSC_REG_BLOCK_SIZE);
+	}
+	if (!pi->sdma_base) {
+		iounmap(pi->sdma_base);
+		release_mem_region(pi->sdma_base_p, MPSC_SDMA_REG_BLOCK_SIZE);
+	}
+	if (!pi->brg_base) {
+		iounmap(pi->brg_base);
+		release_mem_region(pi->brg_base_p, MPSC_BRG_REG_BLOCK_SIZE);
+	}
+
+	pi->mpsc_base = NULL;
+	pi->sdma_base = NULL;
+	pi->brg_base = NULL;
+
+	pi->mpsc_base_p = 0;
+	pi->sdma_base_p = 0;
+	pi->brg_base_p = 0;
+}
+
+static void mpsc_drv_get_platform_data(struct mpsc_port_info *pi,
+		struct platform_device *pd, int num)
+{
+	struct mpsc_pdata	*pdata;
+
+	pdata = (struct mpsc_pdata *)pd->dev.platform_data;
+
+	pi->port.uartclk = pdata->brg_clk_freq;
+	pi->port.iotype = UPIO_MEM;
+	pi->port.line = num;
+	pi->port.type = PORT_MPSC;
+	pi->port.fifosize = MPSC_TXBE_SIZE;
+	pi->port.membase = pi->mpsc_base;
+	pi->port.mapbase = (ulong)pi->mpsc_base;
+	pi->port.ops = &mpsc_pops;
+
+	pi->mirror_regs = pdata->mirror_regs;
+	pi->cache_mgmt = pdata->cache_mgmt;
+	pi->brg_can_tune = pdata->brg_can_tune;
+	pi->brg_clk_src = pdata->brg_clk_src;
+	pi->mpsc_max_idle = pdata->max_idle;
+	pi->default_baud = pdata->default_baud;
+	pi->default_bits = pdata->default_bits;
+	pi->default_parity = pdata->default_parity;
+	pi->default_flow = pdata->default_flow;
+
+	/* Initial values of mirrored regs */
+	pi->MPSC_CHR_1_m = pdata->chr_1_val;
+	pi->MPSC_CHR_2_m = pdata->chr_2_val;
+	pi->MPSC_CHR_10_m = pdata->chr_10_val;
+	pi->MPSC_MPCR_m = pdata->mpcr_val;
+	pi->BRG_BCR_m = pdata->bcr_val;
+
+	pi->shared_regs = &mpsc_shared_regs;
+
+	pi->port.irq = platform_get_irq(pd, 0);
+}
+
+static int mpsc_drv_probe(struct platform_device *dev)
+{
+	struct mpsc_port_info	*pi;
+	int			rc = -ENODEV;
+
+	pr_debug("mpsc_drv_probe: Adding MPSC %d\n", dev->id);
+
+	if (dev->id < MPSC_NUM_CTLRS) {
+		pi = &mpsc_ports[dev->id];
+
+		if (!(rc = mpsc_drv_map_regs(pi, dev))) {
+			mpsc_drv_get_platform_data(pi, dev, dev->id);
+			pi->port.dev = &dev->dev;
+
+			if (!(rc = mpsc_make_ready(pi))) {
+				spin_lock_init(&pi->tx_lock);
+				if (!(rc = uart_add_one_port(&mpsc_reg,
+								&pi->port))) {
+					rc = 0;
+				} else {
+					mpsc_release_port((struct uart_port *)
+							pi);
+					mpsc_drv_unmap_regs(pi);
+				}
+			} else {
+				mpsc_drv_unmap_regs(pi);
+			}
+		}
+	}
+
+	return rc;
+}
+
+static int mpsc_drv_remove(struct platform_device *dev)
+{
+	pr_debug("mpsc_drv_exit: Removing MPSC %d\n", dev->id);
+
+	if (dev->id < MPSC_NUM_CTLRS) {
+		uart_remove_one_port(&mpsc_reg, &mpsc_ports[dev->id].port);
+		mpsc_release_port((struct uart_port *)
+				&mpsc_ports[dev->id].port);
+		mpsc_drv_unmap_regs(&mpsc_ports[dev->id]);
+		return 0;
+	} else {
+		return -ENODEV;
+	}
+}
+
+static struct platform_driver mpsc_driver = {
+	.probe	= mpsc_drv_probe,
+	.remove	= mpsc_drv_remove,
+	.driver	= {
+		.name	= MPSC_CTLR_NAME,
+		.owner	= THIS_MODULE,
+	},
+};
+
+static int __init mpsc_drv_init(void)
+{
+	int	rc;
+
+	printk(KERN_INFO "Serial: MPSC driver\n");
+
+	memset(mpsc_ports, 0, sizeof(mpsc_ports));
+	memset(&mpsc_shared_regs, 0, sizeof(mpsc_shared_regs));
+
+	if (!(rc = uart_register_driver(&mpsc_reg))) {
+		if (!(rc = platform_driver_register(&mpsc_shared_driver))) {
+			if ((rc = platform_driver_register(&mpsc_driver))) {
+				platform_driver_unregister(&mpsc_shared_driver);
+				uart_unregister_driver(&mpsc_reg);
+			}
+		} else {
+			uart_unregister_driver(&mpsc_reg);
+		}
+	}
+
+	return rc;
+}
+
+static void __exit mpsc_drv_exit(void)
+{
+	platform_driver_unregister(&mpsc_driver);
+	platform_driver_unregister(&mpsc_shared_driver);
+	uart_unregister_driver(&mpsc_reg);
+	memset(mpsc_ports, 0, sizeof(mpsc_ports));
+	memset(&mpsc_shared_regs, 0, sizeof(mpsc_shared_regs));
+}
+
+module_init(mpsc_drv_init);
+module_exit(mpsc_drv_exit);
+
+MODULE_AUTHOR("Mark A. Greer <mgreer@mvista.com>");
+MODULE_DESCRIPTION("Generic Marvell MPSC serial/UART driver");
+MODULE_VERSION(MPSC_VERSION);
+MODULE_LICENSE("GPL");
+MODULE_ALIAS_CHARDEV_MAJOR(MPSC_MAJOR);
+MODULE_ALIAS("platform:" MPSC_CTLR_NAME);
diff --git a/ap/os/linux/linux-3.4.x/drivers/tty/serial/mrst_max3110.c b/ap/os/linux/linux-3.4.x/drivers/tty/serial/mrst_max3110.c
new file mode 100644
index 0000000..df2a224
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/tty/serial/mrst_max3110.c
@@ -0,0 +1,912 @@
+/*
+ *  mrst_max3110.c - spi uart protocol driver for Maxim 3110
+ *
+ * Copyright (c) 2008-2010, Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+/*
+ * Note:
+ * 1. From Max3110 spec, the Rx FIFO has 8 words, while the Tx FIFO only has
+ *    1 word. If SPI master controller doesn't support sclk frequency change,
+ *    then the char need be sent out one by one with some delay
+ *
+ * 2. Currently only RX available interrupt is used, no need for waiting TXE
+ *    interrupt for a low speed UART device
+ */
+
+#ifdef CONFIG_MAGIC_SYSRQ
+#define SUPPORT_SYSRQ
+#endif
+
+#include <linux/module.h>
+#include <linux/ioport.h>
+#include <linux/irq.h>
+#include <linux/init.h>
+#include <linux/console.h>
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
+#include <linux/serial_core.h>
+#include <linux/serial_reg.h>
+
+#include <linux/kthread.h>
+#include <linux/spi/spi.h>
+
+#include "mrst_max3110.h"
+
+#define PR_FMT	"mrst_max3110: "
+
+#define UART_TX_NEEDED 1
+#define CON_TX_NEEDED  2
+#define BIT_IRQ_PENDING    3
+
+struct uart_max3110 {
+	struct uart_port port;
+	struct spi_device *spi;
+	char name[SPI_NAME_SIZE];
+
+	wait_queue_head_t wq;
+	struct task_struct *main_thread;
+	struct task_struct *read_thread;
+	struct mutex thread_mutex;
+
+	u32 baud;
+	u16 cur_conf;
+	u8 clock;
+	u8 parity, word_7bits;
+	u16 irq;
+
+	unsigned long uart_flags;
+
+	/* console related */
+	struct circ_buf con_xmit;
+};
+
+/* global data structure, may need be removed */
+static struct uart_max3110 *pmax;
+
+static int receive_chars(struct uart_max3110 *max,
+				unsigned short *str, int len);
+static int max3110_read_multi(struct uart_max3110 *max);
+static void max3110_con_receive(struct uart_max3110 *max);
+
+static int max3110_write_then_read(struct uart_max3110 *max,
+		const void *txbuf, void *rxbuf, unsigned len, int always_fast)
+{
+	struct spi_device *spi = max->spi;
+	struct spi_message	message;
+	struct spi_transfer	x;
+	int ret;
+
+	spi_message_init(&message);
+	memset(&x, 0, sizeof x);
+	x.len = len;
+	x.tx_buf = txbuf;
+	x.rx_buf = rxbuf;
+	spi_message_add_tail(&x, &message);
+
+	if (always_fast)
+		x.speed_hz = spi->max_speed_hz;
+	else if (max->baud)
+		x.speed_hz = max->baud;
+
+	/* Do the i/o */
+	ret = spi_sync(spi, &message);
+	return ret;
+}
+
+/* Write a 16b word to the device */
+static int max3110_out(struct uart_max3110 *max, const u16 out)
+{
+	void *buf;
+	u16 *obuf, *ibuf;
+	int ret;
+
+	buf = kzalloc(8, GFP_KERNEL | GFP_DMA);
+	if (!buf)
+		return -ENOMEM;
+
+	obuf = buf;
+	ibuf = buf + 4;
+	*obuf = out;
+	ret = max3110_write_then_read(max, obuf, ibuf, 2, 1);
+	if (ret) {
+		pr_warning(PR_FMT "%s(): get err msg %d when sending 0x%x\n",
+				__func__, ret, out);
+		goto exit;
+	}
+
+	receive_chars(max, ibuf, 1);
+
+exit:
+	kfree(buf);
+	return ret;
+}
+
+/*
+ * This is usually used to read data from SPIC RX FIFO, which doesn't
+ * need any delay like flushing character out.
+ *
+ * Return how many valide bytes are read back
+ */
+static int max3110_read_multi(struct uart_max3110 *max)
+{
+	void *buf;
+	u16 *obuf, *ibuf;
+	int ret, blen;
+
+	blen = M3110_RX_FIFO_DEPTH * sizeof(u16);
+	buf = kzalloc(blen * 2, GFP_KERNEL | GFP_DMA);
+	if (!buf) {
+		pr_warning(PR_FMT "%s(): fail to alloc dma buffer\n", __func__);
+		return 0;
+	}
+
+	/* tx/rx always have the same length */
+	obuf = buf;
+	ibuf = buf + blen;
+
+	if (max3110_write_then_read(max, obuf, ibuf, blen, 1)) {
+		kfree(buf);
+		return 0;
+	}
+
+	ret = receive_chars(max, ibuf, M3110_RX_FIFO_DEPTH);
+
+	kfree(buf);
+	return ret;
+}
+
+static void serial_m3110_con_putchar(struct uart_port *port, int ch)
+{
+	struct uart_max3110 *max =
+		container_of(port, struct uart_max3110, port);
+	struct circ_buf *xmit = &max->con_xmit;
+
+	if (uart_circ_chars_free(xmit)) {
+		xmit->buf[xmit->head] = (char)ch;
+		xmit->head = (xmit->head + 1) & (PAGE_SIZE - 1);
+	}
+}
+
+/*
+ * Print a string to the serial port trying not to disturb
+ * any possible real use of the port...
+ *
+ *	The console_lock must be held when we get here.
+ */
+static void serial_m3110_con_write(struct console *co,
+				const char *s, unsigned int count)
+{
+	if (!pmax)
+		return;
+
+	uart_console_write(&pmax->port, s, count, serial_m3110_con_putchar);
+
+	if (!test_and_set_bit(CON_TX_NEEDED, &pmax->uart_flags))
+		wake_up(&pmax->wq);
+}
+
+static int __init
+serial_m3110_con_setup(struct console *co, char *options)
+{
+	struct uart_max3110 *max = pmax;
+	int baud = 115200;
+	int bits = 8;
+	int parity = 'n';
+	int flow = 'n';
+
+	pr_info(PR_FMT "setting up console\n");
+
+	if (co->index == -1)
+		co->index = 0;
+
+	if (!max) {
+		pr_err(PR_FMT "pmax is NULL, return");
+		return -ENODEV;
+	}
+
+	if (options)
+		uart_parse_options(options, &baud, &parity, &bits, &flow);
+
+	return uart_set_options(&max->port, co, baud, parity, bits, flow);
+}
+
+static struct tty_driver *serial_m3110_con_device(struct console *co,
+							int *index)
+{
+	struct uart_driver *p = co->data;
+	*index = co->index;
+	return p->tty_driver;
+}
+
+static struct uart_driver serial_m3110_reg;
+static struct console serial_m3110_console = {
+	.name		= "ttyS",
+	.write		= serial_m3110_con_write,
+	.device		= serial_m3110_con_device,
+	.setup		= serial_m3110_con_setup,
+	.flags		= CON_PRINTBUFFER,
+	.index		= -1,
+	.data		= &serial_m3110_reg,
+};
+
+static unsigned int serial_m3110_tx_empty(struct uart_port *port)
+{
+	return 1;
+}
+
+static void serial_m3110_stop_tx(struct uart_port *port)
+{
+	return;
+}
+
+/* stop_rx will be called in spin_lock env */
+static void serial_m3110_stop_rx(struct uart_port *port)
+{
+	return;
+}
+
+#define WORDS_PER_XFER	128
+static void send_circ_buf(struct uart_max3110 *max,
+				struct circ_buf *xmit)
+{
+	void *buf;
+	u16 *obuf, *ibuf;
+	int i, len, blen, dma_size, left, ret = 0;
+
+
+	dma_size = WORDS_PER_XFER * sizeof(u16) * 2;
+	buf = kzalloc(dma_size, GFP_KERNEL | GFP_DMA);
+	if (!buf)
+		return;
+	obuf = buf;
+	ibuf = buf + dma_size/2;
+
+	while (!uart_circ_empty(xmit)) {
+		left = uart_circ_chars_pending(xmit);
+		while (left) {
+			len = min(left, WORDS_PER_XFER);
+			blen = len * sizeof(u16);
+			memset(ibuf, 0, blen);
+
+			for (i = 0; i < len; i++) {
+				obuf[i] = (u8)xmit->buf[xmit->tail] | WD_TAG;
+				xmit->tail = (xmit->tail + 1) &
+						(UART_XMIT_SIZE - 1);
+			}
+
+			/* Fail to send msg to console is not very critical */
+
+			ret = max3110_write_then_read(max, obuf, ibuf, blen, 0);
+			if (ret)
+				pr_warning(PR_FMT "%s(): get err msg %d\n",
+						__func__, ret);
+
+			receive_chars(max, ibuf, len);
+
+			max->port.icount.tx += len;
+			left -= len;
+		}
+	}
+
+	kfree(buf);
+}
+
+static void transmit_char(struct uart_max3110 *max)
+{
+	struct uart_port *port = &max->port;
+	struct circ_buf *xmit = &port->state->xmit;
+
+	if (uart_circ_empty(xmit) || uart_tx_stopped(port))
+		return;
+
+	send_circ_buf(max, xmit);
+
+	if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
+		uart_write_wakeup(port);
+
+	if (uart_circ_empty(xmit))
+		serial_m3110_stop_tx(port);
+}
+
+/*
+ * This will be called by uart_write() and tty_write, can't
+ * go to sleep
+ */
+static void serial_m3110_start_tx(struct uart_port *port)
+{
+	struct uart_max3110 *max =
+		container_of(port, struct uart_max3110, port);
+
+	if (!test_and_set_bit(UART_TX_NEEDED, &max->uart_flags))
+		wake_up(&max->wq);
+}
+
+static int
+receive_chars(struct uart_max3110 *max, unsigned short *str, int len)
+{
+	struct uart_port *port = &max->port;
+	struct tty_struct *tty;
+	char buf[M3110_RX_FIFO_DEPTH];
+	int r, w, usable;
+
+	/* If uart is not opened, just return */
+	if (!port->state)
+		return 0;
+
+	tty = tty_port_tty_get(&port->state->port);
+	if (!tty)
+		return 0;
+
+	for (r = 0, w = 0; r < len; r++) {
+		if (str[r] & MAX3110_BREAK &&
+		    uart_handle_break(port))
+			continue;
+
+		if (str[r] & MAX3110_READ_DATA_AVAILABLE) {
+			if (uart_handle_sysrq_char(port, str[r] & 0xff))
+				continue;
+
+			buf[w++] = str[r] & 0xff;
+		}
+	}
+
+	if (!w) {
+		tty_kref_put(tty);
+		return 0;
+	}
+
+	for (r = 0; w; r += usable, w -= usable) {
+		usable = tty_buffer_request_room(tty, w);
+		if (usable) {
+			tty_insert_flip_string(tty, buf + r, usable);
+			port->icount.rx += usable;
+		}
+	}
+	tty_flip_buffer_push(tty);
+	tty_kref_put(tty);
+
+	return r;
+}
+
+/*
+ * This routine will be used in read_thread or RX IRQ handling,
+ * it will first do one round buffer read(8 words), if there is some
+ * valid RX data, will try to read 5 more rounds till all data
+ * is read out.
+ *
+ * Use stack space as data buffer to save some system load, and chose
+ * 504 Btyes as a threadhold to do a bulk push to upper tty layer when
+ * receiving bulk data, a much bigger buffer may cause stack overflow
+ */
+static void max3110_con_receive(struct uart_max3110 *max)
+{
+	int loop = 1, num;
+
+	do {
+		num = max3110_read_multi(max);
+
+		if (num) {
+			loop = 5;
+		}
+	} while (--loop);
+}
+
+static int max3110_main_thread(void *_max)
+{
+	struct uart_max3110 *max = _max;
+	wait_queue_head_t *wq = &max->wq;
+	int ret = 0;
+	struct circ_buf *xmit = &max->con_xmit;
+
+	pr_info(PR_FMT "start main thread\n");
+
+	do {
+		wait_event_interruptible(*wq,
+				max->uart_flags || kthread_should_stop());
+
+		mutex_lock(&max->thread_mutex);
+
+		if (test_and_clear_bit(BIT_IRQ_PENDING, &max->uart_flags))
+			max3110_con_receive(max);
+
+		/* first handle console output */
+		if (test_and_clear_bit(CON_TX_NEEDED, &max->uart_flags))
+			send_circ_buf(max, xmit);
+
+		/* handle uart output */
+		if (test_and_clear_bit(UART_TX_NEEDED, &max->uart_flags))
+			transmit_char(max);
+
+		mutex_unlock(&max->thread_mutex);
+
+	} while (!kthread_should_stop());
+
+	return ret;
+}
+
+static irqreturn_t serial_m3110_irq(int irq, void *dev_id)
+{
+	struct uart_max3110 *max = dev_id;
+
+	/* max3110's irq is a falling edge, not level triggered,
+	 * so no need to disable the irq */
+
+	if (!test_and_set_bit(BIT_IRQ_PENDING, &max->uart_flags))
+		wake_up(&max->wq);
+
+	return IRQ_HANDLED;
+}
+
+/* if don't use RX IRQ, then need a thread to polling read */
+static int max3110_read_thread(void *_max)
+{
+	struct uart_max3110 *max = _max;
+
+	pr_info(PR_FMT "start read thread\n");
+	do {
+		/*
+		 * If can't acquire the mutex, it means the main thread
+		 * is running which will also perform the rx job
+		 */
+		if (mutex_trylock(&max->thread_mutex)) {
+			max3110_con_receive(max);
+			mutex_unlock(&max->thread_mutex);
+		}
+
+		set_current_state(TASK_INTERRUPTIBLE);
+		schedule_timeout(HZ / 20);
+	} while (!kthread_should_stop());
+
+	return 0;
+}
+
+static int serial_m3110_startup(struct uart_port *port)
+{
+	struct uart_max3110 *max =
+		container_of(port, struct uart_max3110, port);
+	u16 config = 0;
+	int ret = 0;
+
+	if (port->line != 0) {
+		pr_err(PR_FMT "uart port startup failed\n");
+		return -1;
+	}
+
+	/* Disable all IRQ and config it to 115200, 8n1 */
+	config = WC_TAG | WC_FIFO_ENABLE
+			| WC_1_STOPBITS
+			| WC_8BIT_WORD
+			| WC_BAUD_DR2;
+
+	/* as we use thread to handle tx/rx, need set low latency */
+	port->state->port.tty->low_latency = 1;
+
+	if (max->irq) {
+		max->read_thread = NULL;
+		ret = request_irq(max->irq, serial_m3110_irq,
+				IRQ_TYPE_EDGE_FALLING, "max3110", max);
+		if (ret) {
+			max->irq = 0;
+			pr_err(PR_FMT "unable to allocate IRQ, polling\n");
+		}  else {
+			/* Enable RX IRQ only */
+			config |= WC_RXA_IRQ_ENABLE;
+		}
+	}
+
+	if (max->irq == 0) {
+		/* If IRQ is disabled, start a read thread for input data */
+		max->read_thread =
+			kthread_run(max3110_read_thread, max, "max3110_read");
+		if (IS_ERR(max->read_thread)) {
+			ret = PTR_ERR(max->read_thread);
+			max->read_thread = NULL;
+			pr_err(PR_FMT "Can't create read thread!\n");
+			return ret;
+		}
+	}
+
+	ret = max3110_out(max, config);
+	if (ret) {
+		if (max->irq)
+			free_irq(max->irq, max);
+		if (max->read_thread)
+			kthread_stop(max->read_thread);
+		max->read_thread = NULL;
+		return ret;
+	}
+
+	max->cur_conf = config;
+	return 0;
+}
+
+static void serial_m3110_shutdown(struct uart_port *port)
+{
+	struct uart_max3110 *max =
+		container_of(port, struct uart_max3110, port);
+	u16 config;
+
+	if (max->read_thread) {
+		kthread_stop(max->read_thread);
+		max->read_thread = NULL;
+	}
+
+	if (max->irq)
+		free_irq(max->irq, max);
+
+	/* Disable interrupts from this port */
+	config = WC_TAG | WC_SW_SHDI;
+	max3110_out(max, config);
+}
+
+static void serial_m3110_release_port(struct uart_port *port)
+{
+}
+
+static int serial_m3110_request_port(struct uart_port *port)
+{
+	return 0;
+}
+
+static void serial_m3110_config_port(struct uart_port *port, int flags)
+{
+	port->type = PORT_MAX3100;
+}
+
+static int
+serial_m3110_verify_port(struct uart_port *port, struct serial_struct *ser)
+{
+	/* we don't want the core code to modify any port params */
+	return -EINVAL;
+}
+
+
+static const char *serial_m3110_type(struct uart_port *port)
+{
+	struct uart_max3110 *max =
+		container_of(port, struct uart_max3110, port);
+	return max->name;
+}
+
+static void
+serial_m3110_set_termios(struct uart_port *port, struct ktermios *termios,
+		       struct ktermios *old)
+{
+	struct uart_max3110 *max =
+		container_of(port, struct uart_max3110, port);
+	unsigned char cval;
+	unsigned int baud, parity = 0;
+	int clk_div = -1;
+	u16 new_conf = max->cur_conf;
+
+	switch (termios->c_cflag & CSIZE) {
+	case CS7:
+		cval = UART_LCR_WLEN7;
+		new_conf |= WC_7BIT_WORD;
+		break;
+	default:
+		/* We only support CS7 & CS8 */
+		termios->c_cflag &= ~CSIZE;
+		termios->c_cflag |= CS8;
+	case CS8:
+		cval = UART_LCR_WLEN8;
+		new_conf |= WC_8BIT_WORD;
+		break;
+	}
+
+	baud = uart_get_baud_rate(port, termios, old, 0, 230400);
+
+	/* First calc the div for 1.8MHZ clock case */
+	switch (baud) {
+	case 300:
+		clk_div = WC_BAUD_DR384;
+		break;
+	case 600:
+		clk_div = WC_BAUD_DR192;
+		break;
+	case 1200:
+		clk_div = WC_BAUD_DR96;
+		break;
+	case 2400:
+		clk_div = WC_BAUD_DR48;
+		break;
+	case 4800:
+		clk_div = WC_BAUD_DR24;
+		break;
+	case 9600:
+		clk_div = WC_BAUD_DR12;
+		break;
+	case 19200:
+		clk_div = WC_BAUD_DR6;
+		break;
+	case 38400:
+		clk_div = WC_BAUD_DR3;
+		break;
+	case 57600:
+		clk_div = WC_BAUD_DR2;
+		break;
+	case 115200:
+		clk_div = WC_BAUD_DR1;
+		break;
+	case 230400:
+		if (max->clock & MAX3110_HIGH_CLK)
+			break;
+	default:
+		/* Pick the previous baud rate */
+		baud = max->baud;
+		clk_div = max->cur_conf & WC_BAUD_DIV_MASK;
+		tty_termios_encode_baud_rate(termios, baud, baud);
+	}
+
+	if (max->clock & MAX3110_HIGH_CLK) {
+		clk_div += 1;
+		/* High clk version max3110 doesn't support B300 */
+		if (baud == 300) {
+			baud = 600;
+			clk_div = WC_BAUD_DR384;
+		}
+		if (baud == 230400)
+			clk_div = WC_BAUD_DR1;
+		tty_termios_encode_baud_rate(termios, baud, baud);
+	}
+
+	new_conf = (new_conf & ~WC_BAUD_DIV_MASK) | clk_div;
+
+	if (unlikely(termios->c_cflag & CMSPAR))
+		termios->c_cflag &= ~CMSPAR;
+
+	if (termios->c_cflag & CSTOPB)
+		new_conf |= WC_2_STOPBITS;
+	else
+		new_conf &= ~WC_2_STOPBITS;
+
+	if (termios->c_cflag & PARENB) {
+		new_conf |= WC_PARITY_ENABLE;
+		parity |= UART_LCR_PARITY;
+	} else
+		new_conf &= ~WC_PARITY_ENABLE;
+
+	if (!(termios->c_cflag & PARODD))
+		parity |= UART_LCR_EPAR;
+	max->parity = parity;
+
+	uart_update_timeout(port, termios->c_cflag, baud);
+
+	new_conf |= WC_TAG;
+	if (new_conf != max->cur_conf) {
+		if (!max3110_out(max, new_conf)) {
+			max->cur_conf = new_conf;
+			max->baud = baud;
+		}
+	}
+}
+
+/* Don't handle hw handshaking */
+static unsigned int serial_m3110_get_mctrl(struct uart_port *port)
+{
+	return TIOCM_DSR | TIOCM_CAR | TIOCM_DSR;
+}
+
+static void serial_m3110_set_mctrl(struct uart_port *port, unsigned int mctrl)
+{
+}
+
+static void serial_m3110_break_ctl(struct uart_port *port, int break_state)
+{
+}
+
+static void serial_m3110_pm(struct uart_port *port, unsigned int state,
+			unsigned int oldstate)
+{
+}
+
+static void serial_m3110_enable_ms(struct uart_port *port)
+{
+}
+
+struct uart_ops serial_m3110_ops = {
+	.tx_empty	= serial_m3110_tx_empty,
+	.set_mctrl	= serial_m3110_set_mctrl,
+	.get_mctrl	= serial_m3110_get_mctrl,
+	.stop_tx	= serial_m3110_stop_tx,
+	.start_tx	= serial_m3110_start_tx,
+	.stop_rx	= serial_m3110_stop_rx,
+	.enable_ms	= serial_m3110_enable_ms,
+	.break_ctl	= serial_m3110_break_ctl,
+	.startup	= serial_m3110_startup,
+	.shutdown	= serial_m3110_shutdown,
+	.set_termios	= serial_m3110_set_termios,
+	.pm		= serial_m3110_pm,
+	.type		= serial_m3110_type,
+	.release_port	= serial_m3110_release_port,
+	.request_port	= serial_m3110_request_port,
+	.config_port	= serial_m3110_config_port,
+	.verify_port	= serial_m3110_verify_port,
+};
+
+static struct uart_driver serial_m3110_reg = {
+	.owner		= THIS_MODULE,
+	.driver_name	= "MRST serial",
+	.dev_name	= "ttyS",
+	.major		= TTY_MAJOR,
+	.minor		= 64,
+	.nr		= 1,
+	.cons		= &serial_m3110_console,
+};
+
+#ifdef CONFIG_PM
+static int serial_m3110_suspend(struct spi_device *spi, pm_message_t state)
+{
+	struct uart_max3110 *max = spi_get_drvdata(spi);
+
+	disable_irq(max->irq);
+	uart_suspend_port(&serial_m3110_reg, &max->port);
+	max3110_out(max, max->cur_conf | WC_SW_SHDI);
+	return 0;
+}
+
+static int serial_m3110_resume(struct spi_device *spi)
+{
+	struct uart_max3110 *max = spi_get_drvdata(spi);
+
+	max3110_out(max, max->cur_conf);
+	uart_resume_port(&serial_m3110_reg, &max->port);
+	enable_irq(max->irq);
+	return 0;
+}
+#else
+#define serial_m3110_suspend	NULL
+#define serial_m3110_resume	NULL
+#endif
+
+static int __devinit serial_m3110_probe(struct spi_device *spi)
+{
+	struct uart_max3110 *max;
+	void *buffer;
+	u16 res;
+	int ret = 0;
+
+	max = kzalloc(sizeof(*max), GFP_KERNEL);
+	if (!max)
+		return -ENOMEM;
+
+	/* Set spi info */
+	spi->bits_per_word = 16;
+	max->clock = MAX3110_HIGH_CLK;
+
+	spi_setup(spi);
+
+	max->port.type = PORT_MAX3100;
+	max->port.fifosize = 2;		/* Only have 16b buffer */
+	max->port.ops = &serial_m3110_ops;
+	max->port.line = 0;
+	max->port.dev = &spi->dev;
+	max->port.uartclk = 115200;
+
+	max->spi = spi;
+	strcpy(max->name, spi->modalias);
+	max->irq = (u16)spi->irq;
+
+	mutex_init(&max->thread_mutex);
+
+	max->word_7bits = 0;
+	max->parity = 0;
+	max->baud = 0;
+
+	max->cur_conf = 0;
+	max->uart_flags = 0;
+
+	/* Check if reading configuration register returns something sane */
+
+	res = RC_TAG;
+	ret = max3110_write_then_read(max, (u8 *)&res, (u8 *)&res, 2, 0);
+	if (ret < 0 || res == 0 || res == 0xffff) {
+		dev_dbg(&spi->dev, "MAX3111 deemed not present (conf reg %04x)",
+									res);
+		ret = -ENODEV;
+		goto err_get_page;
+	}
+
+	buffer = (void *)__get_free_page(GFP_KERNEL);
+	if (!buffer) {
+		ret = -ENOMEM;
+		goto err_get_page;
+	}
+	max->con_xmit.buf = buffer;
+	max->con_xmit.head = 0;
+	max->con_xmit.tail = 0;
+
+	init_waitqueue_head(&max->wq);
+
+	max->main_thread = kthread_run(max3110_main_thread,
+					max, "max3110_main");
+	if (IS_ERR(max->main_thread)) {
+		ret = PTR_ERR(max->main_thread);
+		goto err_kthread;
+	}
+
+	spi_set_drvdata(spi, max);
+	pmax = max;
+
+	/* Give membase a psudo value to pass serial_core's check */
+	max->port.membase = (void *)0xff110000;
+	uart_add_one_port(&serial_m3110_reg, &max->port);
+
+	return 0;
+
+err_kthread:
+	free_page((unsigned long)buffer);
+err_get_page:
+	kfree(max);
+	return ret;
+}
+
+static int __devexit serial_m3110_remove(struct spi_device *dev)
+{
+	struct uart_max3110 *max = spi_get_drvdata(dev);
+
+	if (!max)
+		return 0;
+
+	uart_remove_one_port(&serial_m3110_reg, &max->port);
+
+	free_page((unsigned long)max->con_xmit.buf);
+
+	if (max->main_thread)
+		kthread_stop(max->main_thread);
+
+	kfree(max);
+	return 0;
+}
+
+static struct spi_driver uart_max3110_driver = {
+	.driver = {
+			.name	= "spi_max3111",
+			.owner	= THIS_MODULE,
+	},
+	.probe		= serial_m3110_probe,
+	.remove		= __devexit_p(serial_m3110_remove),
+	.suspend	= serial_m3110_suspend,
+	.resume		= serial_m3110_resume,
+};
+
+static int __init serial_m3110_init(void)
+{
+	int ret = 0;
+
+	ret = uart_register_driver(&serial_m3110_reg);
+	if (ret)
+		return ret;
+
+	ret = spi_register_driver(&uart_max3110_driver);
+	if (ret)
+		uart_unregister_driver(&serial_m3110_reg);
+
+	return ret;
+}
+
+static void __exit serial_m3110_exit(void)
+{
+	spi_unregister_driver(&uart_max3110_driver);
+	uart_unregister_driver(&serial_m3110_reg);
+}
+
+module_init(serial_m3110_init);
+module_exit(serial_m3110_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("spi:max3110-uart");
diff --git a/ap/os/linux/linux-3.4.x/drivers/tty/serial/mrst_max3110.h b/ap/os/linux/linux-3.4.x/drivers/tty/serial/mrst_max3110.h
new file mode 100644
index 0000000..35af073
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/tty/serial/mrst_max3110.h
@@ -0,0 +1,61 @@
+#ifndef _MRST_MAX3110_H
+#define _MRST_MAX3110_H
+
+#define MAX3110_HIGH_CLK	0x1	/* 3.6864 MHZ */
+#define MAX3110_LOW_CLK		0x0	/* 1.8432 MHZ */
+
+/* status bits for all 4 MAX3110 operate modes */
+#define MAX3110_READ_DATA_AVAILABLE	(1 << 15)
+#define MAX3110_WRITE_BUF_EMPTY		(1 << 14)
+#define MAX3110_BREAK			(1 << 10)
+
+#define WC_TAG			(3 << 14)
+#define RC_TAG			(1 << 14)
+#define WD_TAG			(2 << 14)
+#define RD_TAG			(0 << 14)
+
+/* bits def for write configuration */
+#define WC_FIFO_ENABLE_MASK	(1 << 13)
+#define WC_FIFO_ENABLE		(0 << 13)
+
+#define WC_SW_SHDI		(1 << 12)
+
+#define WC_IRQ_MASK		(0xF << 8)
+#define WC_TXE_IRQ_ENABLE	(1 << 11)	/* TX empty irq */
+#define WC_RXA_IRQ_ENABLE	(1 << 10)	/* RX available irq */
+#define WC_PAR_HIGH_IRQ_ENABLE	(1 << 9)
+#define WC_REC_ACT_IRQ_ENABLE	(1 << 8)
+
+#define WC_IRDA_ENABLE		(1 << 7)
+
+#define WC_STOPBITS_MASK	(1 << 6)
+#define WC_2_STOPBITS		(1 << 6)
+#define WC_1_STOPBITS		(0 << 6)
+
+#define WC_PARITY_ENABLE_MASK	(1 << 5)
+#define WC_PARITY_ENABLE	(1 << 5)
+
+#define WC_WORDLEN_MASK		(1 << 4)
+#define WC_7BIT_WORD		(1 << 4)
+#define WC_8BIT_WORD		(0 << 4)
+
+#define WC_BAUD_DIV_MASK	(0xF)
+#define WC_BAUD_DR1		(0x0)
+#define WC_BAUD_DR2		(0x1)
+#define WC_BAUD_DR4		(0x2)
+#define WC_BAUD_DR8		(0x3)
+#define WC_BAUD_DR16		(0x4)
+#define WC_BAUD_DR32		(0x5)
+#define WC_BAUD_DR64		(0x6)
+#define WC_BAUD_DR128		(0x7)
+#define WC_BAUD_DR3		(0x8)
+#define WC_BAUD_DR6		(0x9)
+#define WC_BAUD_DR12		(0xA)
+#define WC_BAUD_DR24		(0xB)
+#define WC_BAUD_DR48		(0xC)
+#define WC_BAUD_DR96		(0xD)
+#define WC_BAUD_DR192		(0xE)
+#define WC_BAUD_DR384		(0xF)
+
+#define M3110_RX_FIFO_DEPTH	8
+#endif
diff --git a/ap/os/linux/linux-3.4.x/drivers/tty/serial/msm_serial.c b/ap/os/linux/linux-3.4.x/drivers/tty/serial/msm_serial.c
new file mode 100644
index 0000000..8131e2c
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/tty/serial/msm_serial.c
@@ -0,0 +1,974 @@
+/*
+ * Driver for msm7k serial device and console
+ *
+ * Copyright (C) 2007 Google, Inc.
+ * Author: Robert Love <rlove@google.com>
+ * Copyright (c) 2011, Code Aurora Forum. All rights reserved.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ */
+
+#if defined(CONFIG_SERIAL_MSM_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ)
+# define SUPPORT_SYSRQ
+#endif
+
+#include <linux/atomic.h>
+#include <linux/hrtimer.h>
+#include <linux/module.h>
+#include <linux/io.h>
+#include <linux/ioport.h>
+#include <linux/irq.h>
+#include <linux/init.h>
+#include <linux/console.h>
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
+#include <linux/serial_core.h>
+#include <linux/serial.h>
+#include <linux/clk.h>
+#include <linux/platform_device.h>
+#include <linux/delay.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+
+#include "msm_serial.h"
+
+struct msm_port {
+	struct uart_port	uart;
+	char			name[16];
+	struct clk		*clk;
+	struct clk		*pclk;
+	unsigned int		imr;
+	unsigned int            *gsbi_base;
+	int			is_uartdm;
+	unsigned int		old_snap_state;
+};
+
+static inline void wait_for_xmitr(struct uart_port *port, int bits)
+{
+	if (!(msm_read(port, UART_SR) & UART_SR_TX_EMPTY))
+		while ((msm_read(port, UART_ISR) & bits) != bits)
+			cpu_relax();
+}
+
+static void msm_stop_tx(struct uart_port *port)
+{
+	struct msm_port *msm_port = UART_TO_MSM(port);
+
+	msm_port->imr &= ~UART_IMR_TXLEV;
+	msm_write(port, msm_port->imr, UART_IMR);
+}
+
+static void msm_start_tx(struct uart_port *port)
+{
+	struct msm_port *msm_port = UART_TO_MSM(port);
+
+	msm_port->imr |= UART_IMR_TXLEV;
+	msm_write(port, msm_port->imr, UART_IMR);
+}
+
+static void msm_stop_rx(struct uart_port *port)
+{
+	struct msm_port *msm_port = UART_TO_MSM(port);
+
+	msm_port->imr &= ~(UART_IMR_RXLEV | UART_IMR_RXSTALE);
+	msm_write(port, msm_port->imr, UART_IMR);
+}
+
+static void msm_enable_ms(struct uart_port *port)
+{
+	struct msm_port *msm_port = UART_TO_MSM(port);
+
+	msm_port->imr |= UART_IMR_DELTA_CTS;
+	msm_write(port, msm_port->imr, UART_IMR);
+}
+
+static void handle_rx_dm(struct uart_port *port, unsigned int misr)
+{
+	struct tty_struct *tty = port->state->port.tty;
+	unsigned int sr;
+	int count = 0;
+	struct msm_port *msm_port = UART_TO_MSM(port);
+
+	if ((msm_read(port, UART_SR) & UART_SR_OVERRUN)) {
+		port->icount.overrun++;
+		tty_insert_flip_char(tty, 0, TTY_OVERRUN);
+		msm_write(port, UART_CR_CMD_RESET_ERR, UART_CR);
+	}
+
+	if (misr & UART_IMR_RXSTALE) {
+		count = msm_read(port, UARTDM_RX_TOTAL_SNAP) -
+			msm_port->old_snap_state;
+		msm_port->old_snap_state = 0;
+	} else {
+		count = 4 * (msm_read(port, UART_RFWR));
+		msm_port->old_snap_state += count;
+	}
+
+	/* TODO: Precise error reporting */
+
+	port->icount.rx += count;
+
+	while (count > 0) {
+		unsigned int c;
+
+		sr = msm_read(port, UART_SR);
+		if ((sr & UART_SR_RX_READY) == 0) {
+			msm_port->old_snap_state -= count;
+			break;
+		}
+		c = msm_read(port, UARTDM_RF);
+		if (sr & UART_SR_RX_BREAK) {
+			port->icount.brk++;
+			if (uart_handle_break(port))
+				continue;
+		} else if (sr & UART_SR_PAR_FRAME_ERR)
+			port->icount.frame++;
+
+		/* TODO: handle sysrq */
+		tty_insert_flip_string(tty, (char *) &c,
+				       (count > 4) ? 4 : count);
+		count -= 4;
+	}
+
+	tty_flip_buffer_push(tty);
+	if (misr & (UART_IMR_RXSTALE))
+		msm_write(port, UART_CR_CMD_RESET_STALE_INT, UART_CR);
+	msm_write(port, 0xFFFFFF, UARTDM_DMRX);
+	msm_write(port, UART_CR_CMD_STALE_EVENT_ENABLE, UART_CR);
+}
+
+static void handle_rx(struct uart_port *port)
+{
+	struct tty_struct *tty = port->state->port.tty;
+	unsigned int sr;
+
+	/*
+	 * Handle overrun. My understanding of the hardware is that overrun
+	 * is not tied to the RX buffer, so we handle the case out of band.
+	 */
+	if ((msm_read(port, UART_SR) & UART_SR_OVERRUN)) {
+		port->icount.overrun++;
+		tty_insert_flip_char(tty, 0, TTY_OVERRUN);
+		msm_write(port, UART_CR_CMD_RESET_ERR, UART_CR);
+	}
+
+	/* and now the main RX loop */
+	while ((sr = msm_read(port, UART_SR)) & UART_SR_RX_READY) {
+		unsigned int c;
+		char flag = TTY_NORMAL;
+
+		c = msm_read(port, UART_RF);
+
+		if (sr & UART_SR_RX_BREAK) {
+			port->icount.brk++;
+			if (uart_handle_break(port))
+				continue;
+		} else if (sr & UART_SR_PAR_FRAME_ERR) {
+			port->icount.frame++;
+		} else {
+			port->icount.rx++;
+		}
+
+		/* Mask conditions we're ignorning. */
+		sr &= port->read_status_mask;
+
+		if (sr & UART_SR_RX_BREAK) {
+			flag = TTY_BREAK;
+		} else if (sr & UART_SR_PAR_FRAME_ERR) {
+			flag = TTY_FRAME;
+		}
+
+		if (!uart_handle_sysrq_char(port, c))
+			tty_insert_flip_char(tty, c, flag);
+	}
+
+	tty_flip_buffer_push(tty);
+}
+
+static void reset_dm_count(struct uart_port *port)
+{
+	wait_for_xmitr(port, UART_ISR_TX_READY);
+	msm_write(port, 1, UARTDM_NCF_TX);
+}
+
+static void handle_tx(struct uart_port *port)
+{
+	struct circ_buf *xmit = &port->state->xmit;
+	struct msm_port *msm_port = UART_TO_MSM(port);
+	int sent_tx;
+
+	if (port->x_char) {
+		if (msm_port->is_uartdm)
+			reset_dm_count(port);
+
+		msm_write(port, port->x_char,
+			  msm_port->is_uartdm ? UARTDM_TF : UART_TF);
+		port->icount.tx++;
+		port->x_char = 0;
+	}
+
+	if (msm_port->is_uartdm)
+		reset_dm_count(port);
+
+	while (msm_read(port, UART_SR) & UART_SR_TX_READY) {
+		if (uart_circ_empty(xmit)) {
+			/* disable tx interrupts */
+			msm_port->imr &= ~UART_IMR_TXLEV;
+			msm_write(port, msm_port->imr, UART_IMR);
+			break;
+		}
+		msm_write(port, xmit->buf[xmit->tail],
+			  msm_port->is_uartdm ? UARTDM_TF : UART_TF);
+
+		if (msm_port->is_uartdm)
+			reset_dm_count(port);
+
+		xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
+		port->icount.tx++;
+		sent_tx = 1;
+	}
+
+	if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
+		uart_write_wakeup(port);
+}
+
+static void handle_delta_cts(struct uart_port *port)
+{
+	msm_write(port, UART_CR_CMD_RESET_CTS, UART_CR);
+	port->icount.cts++;
+	wake_up_interruptible(&port->state->port.delta_msr_wait);
+}
+
+static irqreturn_t msm_irq(int irq, void *dev_id)
+{
+	struct uart_port *port = dev_id;
+	struct msm_port *msm_port = UART_TO_MSM(port);
+	unsigned int misr;
+
+	spin_lock(&port->lock);
+	misr = msm_read(port, UART_MISR);
+	msm_write(port, 0, UART_IMR); /* disable interrupt */
+
+	if (misr & (UART_IMR_RXLEV | UART_IMR_RXSTALE)) {
+		if (msm_port->is_uartdm)
+			handle_rx_dm(port, misr);
+		else
+			handle_rx(port);
+	}
+	if (misr & UART_IMR_TXLEV)
+		handle_tx(port);
+	if (misr & UART_IMR_DELTA_CTS)
+		handle_delta_cts(port);
+
+	msm_write(port, msm_port->imr, UART_IMR); /* restore interrupt */
+	spin_unlock(&port->lock);
+
+	return IRQ_HANDLED;
+}
+
+static unsigned int msm_tx_empty(struct uart_port *port)
+{
+	return (msm_read(port, UART_SR) & UART_SR_TX_EMPTY) ? TIOCSER_TEMT : 0;
+}
+
+static unsigned int msm_get_mctrl(struct uart_port *port)
+{
+	return TIOCM_CAR | TIOCM_CTS | TIOCM_DSR | TIOCM_RTS;
+}
+
+
+static void msm_reset(struct uart_port *port)
+{
+	/* reset everything */
+	msm_write(port, UART_CR_CMD_RESET_RX, UART_CR);
+	msm_write(port, UART_CR_CMD_RESET_TX, UART_CR);
+	msm_write(port, UART_CR_CMD_RESET_ERR, UART_CR);
+	msm_write(port, UART_CR_CMD_RESET_BREAK_INT, UART_CR);
+	msm_write(port, UART_CR_CMD_RESET_CTS, UART_CR);
+	msm_write(port, UART_CR_CMD_SET_RFR, UART_CR);
+}
+
+void msm_set_mctrl(struct uart_port *port, unsigned int mctrl)
+{
+	unsigned int mr;
+	mr = msm_read(port, UART_MR1);
+
+	if (!(mctrl & TIOCM_RTS)) {
+		mr &= ~UART_MR1_RX_RDY_CTL;
+		msm_write(port, mr, UART_MR1);
+		msm_write(port, UART_CR_CMD_RESET_RFR, UART_CR);
+	} else {
+		mr |= UART_MR1_RX_RDY_CTL;
+		msm_write(port, mr, UART_MR1);
+	}
+}
+
+static void msm_break_ctl(struct uart_port *port, int break_ctl)
+{
+	if (break_ctl)
+		msm_write(port, UART_CR_CMD_START_BREAK, UART_CR);
+	else
+		msm_write(port, UART_CR_CMD_STOP_BREAK, UART_CR);
+}
+
+static int msm_set_baud_rate(struct uart_port *port, unsigned int baud)
+{
+	unsigned int baud_code, rxstale, watermark;
+	struct msm_port *msm_port = UART_TO_MSM(port);
+
+	switch (baud) {
+	case 300:
+		baud_code = UART_CSR_300;
+		rxstale = 1;
+		break;
+	case 600:
+		baud_code = UART_CSR_600;
+		rxstale = 1;
+		break;
+	case 1200:
+		baud_code = UART_CSR_1200;
+		rxstale = 1;
+		break;
+	case 2400:
+		baud_code = UART_CSR_2400;
+		rxstale = 1;
+		break;
+	case 4800:
+		baud_code = UART_CSR_4800;
+		rxstale = 1;
+		break;
+	case 9600:
+		baud_code = UART_CSR_9600;
+		rxstale = 2;
+		break;
+	case 14400:
+		baud_code = UART_CSR_14400;
+		rxstale = 3;
+		break;
+	case 19200:
+		baud_code = UART_CSR_19200;
+		rxstale = 4;
+		break;
+	case 28800:
+		baud_code = UART_CSR_28800;
+		rxstale = 6;
+		break;
+	case 38400:
+		baud_code = UART_CSR_38400;
+		rxstale = 8;
+		break;
+	case 57600:
+		baud_code = UART_CSR_57600;
+		rxstale = 16;
+		break;
+	case 115200:
+	default:
+		baud_code = UART_CSR_115200;
+		baud = 115200;
+		rxstale = 31;
+		break;
+	}
+
+	if (msm_port->is_uartdm)
+		msm_write(port, UART_CR_CMD_RESET_RX, UART_CR);
+
+	msm_write(port, baud_code, UART_CSR);
+
+	/* RX stale watermark */
+	watermark = UART_IPR_STALE_LSB & rxstale;
+	watermark |= UART_IPR_RXSTALE_LAST;
+	watermark |= UART_IPR_STALE_TIMEOUT_MSB & (rxstale << 2);
+	msm_write(port, watermark, UART_IPR);
+
+	/* set RX watermark */
+	watermark = (port->fifosize * 3) / 4;
+	msm_write(port, watermark, UART_RFWR);
+
+	/* set TX watermark */
+	msm_write(port, 10, UART_TFWR);
+
+	if (msm_port->is_uartdm) {
+		msm_write(port, UART_CR_CMD_RESET_STALE_INT, UART_CR);
+		msm_write(port, 0xFFFFFF, UARTDM_DMRX);
+		msm_write(port, UART_CR_CMD_STALE_EVENT_ENABLE, UART_CR);
+	}
+
+	return baud;
+}
+
+
+static void msm_init_clock(struct uart_port *port)
+{
+	struct msm_port *msm_port = UART_TO_MSM(port);
+
+	clk_enable(msm_port->clk);
+	if (!IS_ERR(msm_port->pclk))
+		clk_enable(msm_port->pclk);
+	msm_serial_set_mnd_regs(port);
+}
+
+static int msm_startup(struct uart_port *port)
+{
+	struct msm_port *msm_port = UART_TO_MSM(port);
+	unsigned int data, rfr_level;
+	int ret;
+
+	snprintf(msm_port->name, sizeof(msm_port->name),
+		 "msm_serial%d", port->line);
+
+	ret = request_irq(port->irq, msm_irq, IRQF_TRIGGER_HIGH,
+			  msm_port->name, port);
+	if (unlikely(ret))
+		return ret;
+
+	msm_init_clock(port);
+
+	if (likely(port->fifosize > 12))
+		rfr_level = port->fifosize - 12;
+	else
+		rfr_level = port->fifosize;
+
+	/* set automatic RFR level */
+	data = msm_read(port, UART_MR1);
+	data &= ~UART_MR1_AUTO_RFR_LEVEL1;
+	data &= ~UART_MR1_AUTO_RFR_LEVEL0;
+	data |= UART_MR1_AUTO_RFR_LEVEL1 & (rfr_level << 2);
+	data |= UART_MR1_AUTO_RFR_LEVEL0 & rfr_level;
+	msm_write(port, data, UART_MR1);
+
+	/* make sure that RXSTALE count is non-zero */
+	data = msm_read(port, UART_IPR);
+	if (unlikely(!data)) {
+		data |= UART_IPR_RXSTALE_LAST;
+		data |= UART_IPR_STALE_LSB;
+		msm_write(port, data, UART_IPR);
+	}
+
+	data = 0;
+	if (!port->cons || (port->cons && !(port->cons->flags & CON_ENABLED))) {
+		msm_write(port, UART_CR_CMD_PROTECTION_EN, UART_CR);
+		msm_reset(port);
+		data = UART_CR_TX_ENABLE;
+	}
+
+	data |= UART_CR_RX_ENABLE;
+	msm_write(port, data, UART_CR);	/* enable TX & RX */
+
+	/* Make sure IPR is not 0 to start with*/
+	if (msm_port->is_uartdm)
+		msm_write(port, UART_IPR_STALE_LSB, UART_IPR);
+
+	/* turn on RX and CTS interrupts */
+	msm_port->imr = UART_IMR_RXLEV | UART_IMR_RXSTALE |
+			UART_IMR_CURRENT_CTS;
+
+	if (msm_port->is_uartdm) {
+		msm_write(port, 0xFFFFFF, UARTDM_DMRX);
+		msm_write(port, UART_CR_CMD_RESET_STALE_INT, UART_CR);
+		msm_write(port, UART_CR_CMD_STALE_EVENT_ENABLE, UART_CR);
+	}
+
+	msm_write(port, msm_port->imr, UART_IMR);
+	return 0;
+}
+
+static void msm_shutdown(struct uart_port *port)
+{
+	struct msm_port *msm_port = UART_TO_MSM(port);
+
+	msm_port->imr = 0;
+	msm_write(port, 0, UART_IMR); /* disable interrupts */
+
+	clk_disable(msm_port->clk);
+
+	free_irq(port->irq, port);
+}
+
+static void msm_set_termios(struct uart_port *port, struct ktermios *termios,
+			    struct ktermios *old)
+{
+	unsigned long flags;
+	unsigned int baud, mr;
+
+	spin_lock_irqsave(&port->lock, flags);
+
+	/* calculate and set baud rate */
+	baud = uart_get_baud_rate(port, termios, old, 300, 115200);
+	baud = msm_set_baud_rate(port, baud);
+	if (tty_termios_baud_rate(termios))
+		tty_termios_encode_baud_rate(termios, baud, baud);
+
+	/* calculate parity */
+	mr = msm_read(port, UART_MR2);
+	mr &= ~UART_MR2_PARITY_MODE;
+	if (termios->c_cflag & PARENB) {
+		if (termios->c_cflag & PARODD)
+			mr |= UART_MR2_PARITY_MODE_ODD;
+		else if (termios->c_cflag & CMSPAR)
+			mr |= UART_MR2_PARITY_MODE_SPACE;
+		else
+			mr |= UART_MR2_PARITY_MODE_EVEN;
+	}
+
+	/* calculate bits per char */
+	mr &= ~UART_MR2_BITS_PER_CHAR;
+	switch (termios->c_cflag & CSIZE) {
+	case CS5:
+		mr |= UART_MR2_BITS_PER_CHAR_5;
+		break;
+	case CS6:
+		mr |= UART_MR2_BITS_PER_CHAR_6;
+		break;
+	case CS7:
+		mr |= UART_MR2_BITS_PER_CHAR_7;
+		break;
+	case CS8:
+	default:
+		mr |= UART_MR2_BITS_PER_CHAR_8;
+		break;
+	}
+
+	/* calculate stop bits */
+	mr &= ~(UART_MR2_STOP_BIT_LEN_ONE | UART_MR2_STOP_BIT_LEN_TWO);
+	if (termios->c_cflag & CSTOPB)
+		mr |= UART_MR2_STOP_BIT_LEN_TWO;
+	else
+		mr |= UART_MR2_STOP_BIT_LEN_ONE;
+
+	/* set parity, bits per char, and stop bit */
+	msm_write(port, mr, UART_MR2);
+
+	/* calculate and set hardware flow control */
+	mr = msm_read(port, UART_MR1);
+	mr &= ~(UART_MR1_CTS_CTL | UART_MR1_RX_RDY_CTL);
+	if (termios->c_cflag & CRTSCTS) {
+		mr |= UART_MR1_CTS_CTL;
+		mr |= UART_MR1_RX_RDY_CTL;
+	}
+	msm_write(port, mr, UART_MR1);
+
+	/* Configure status bits to ignore based on termio flags. */
+	port->read_status_mask = 0;
+	if (termios->c_iflag & INPCK)
+		port->read_status_mask |= UART_SR_PAR_FRAME_ERR;
+	if (termios->c_iflag & (BRKINT | PARMRK))
+		port->read_status_mask |= UART_SR_RX_BREAK;
+
+	uart_update_timeout(port, termios->c_cflag, baud);
+
+	spin_unlock_irqrestore(&port->lock, flags);
+}
+
+static const char *msm_type(struct uart_port *port)
+{
+	return "MSM";
+}
+
+static void msm_release_port(struct uart_port *port)
+{
+	struct platform_device *pdev = to_platform_device(port->dev);
+	struct msm_port *msm_port = UART_TO_MSM(port);
+	struct resource *uart_resource;
+	struct resource *gsbi_resource;
+	resource_size_t size;
+
+	uart_resource = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (unlikely(!uart_resource))
+		return;
+	size = resource_size(uart_resource);
+
+	release_mem_region(port->mapbase, size);
+	iounmap(port->membase);
+	port->membase = NULL;
+
+	if (msm_port->gsbi_base) {
+		iowrite32(GSBI_PROTOCOL_IDLE, msm_port->gsbi_base +
+			  GSBI_CONTROL);
+
+		gsbi_resource = platform_get_resource(pdev,
+							IORESOURCE_MEM, 1);
+
+		if (unlikely(!gsbi_resource))
+			return;
+
+		size = resource_size(gsbi_resource);
+		release_mem_region(gsbi_resource->start, size);
+		iounmap(msm_port->gsbi_base);
+		msm_port->gsbi_base = NULL;
+	}
+}
+
+static int msm_request_port(struct uart_port *port)
+{
+	struct msm_port *msm_port = UART_TO_MSM(port);
+	struct platform_device *pdev = to_platform_device(port->dev);
+	struct resource *uart_resource;
+	struct resource *gsbi_resource;
+	resource_size_t size;
+	int ret;
+
+	uart_resource = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (unlikely(!uart_resource))
+		return -ENXIO;
+
+	size = resource_size(uart_resource);
+
+	if (!request_mem_region(port->mapbase, size, "msm_serial"))
+		return -EBUSY;
+
+	port->membase = ioremap(port->mapbase, size);
+	if (!port->membase) {
+		ret = -EBUSY;
+		goto fail_release_port;
+	}
+
+	gsbi_resource = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+	/* Is this a GSBI-based port? */
+	if (gsbi_resource) {
+		size = resource_size(gsbi_resource);
+
+		if (!request_mem_region(gsbi_resource->start, size,
+						 "msm_serial")) {
+			ret = -EBUSY;
+			goto fail_release_port;
+		}
+
+		msm_port->gsbi_base = ioremap(gsbi_resource->start, size);
+		if (!msm_port->gsbi_base) {
+			ret = -EBUSY;
+			goto fail_release_gsbi;
+		}
+	}
+
+	return 0;
+
+fail_release_gsbi:
+	release_mem_region(gsbi_resource->start, size);
+fail_release_port:
+	release_mem_region(port->mapbase, size);
+	return ret;
+}
+
+static void msm_config_port(struct uart_port *port, int flags)
+{
+	struct msm_port *msm_port = UART_TO_MSM(port);
+	int ret;
+	if (flags & UART_CONFIG_TYPE) {
+		port->type = PORT_MSM;
+		ret = msm_request_port(port);
+		if (ret)
+			return;
+	}
+
+	if (msm_port->is_uartdm)
+		iowrite32(GSBI_PROTOCOL_UART, msm_port->gsbi_base +
+			  GSBI_CONTROL);
+}
+
+static int msm_verify_port(struct uart_port *port, struct serial_struct *ser)
+{
+	if (unlikely(ser->type != PORT_UNKNOWN && ser->type != PORT_MSM))
+		return -EINVAL;
+	if (unlikely(port->irq != ser->irq))
+		return -EINVAL;
+	return 0;
+}
+
+static void msm_power(struct uart_port *port, unsigned int state,
+		      unsigned int oldstate)
+{
+	struct msm_port *msm_port = UART_TO_MSM(port);
+
+	switch (state) {
+	case 0:
+		clk_enable(msm_port->clk);
+		if (!IS_ERR(msm_port->pclk))
+			clk_enable(msm_port->pclk);
+		break;
+	case 3:
+		clk_disable(msm_port->clk);
+		if (!IS_ERR(msm_port->pclk))
+			clk_disable(msm_port->pclk);
+		break;
+	default:
+		printk(KERN_ERR "msm_serial: Unknown PM state %d\n", state);
+	}
+}
+
+static struct uart_ops msm_uart_pops = {
+	.tx_empty = msm_tx_empty,
+	.set_mctrl = msm_set_mctrl,
+	.get_mctrl = msm_get_mctrl,
+	.stop_tx = msm_stop_tx,
+	.start_tx = msm_start_tx,
+	.stop_rx = msm_stop_rx,
+	.enable_ms = msm_enable_ms,
+	.break_ctl = msm_break_ctl,
+	.startup = msm_startup,
+	.shutdown = msm_shutdown,
+	.set_termios = msm_set_termios,
+	.type = msm_type,
+	.release_port = msm_release_port,
+	.request_port = msm_request_port,
+	.config_port = msm_config_port,
+	.verify_port = msm_verify_port,
+	.pm = msm_power,
+};
+
+static struct msm_port msm_uart_ports[] = {
+	{
+		.uart = {
+			.iotype = UPIO_MEM,
+			.ops = &msm_uart_pops,
+			.flags = UPF_BOOT_AUTOCONF,
+			.fifosize = 64,
+			.line = 0,
+		},
+	},
+	{
+		.uart = {
+			.iotype = UPIO_MEM,
+			.ops = &msm_uart_pops,
+			.flags = UPF_BOOT_AUTOCONF,
+			.fifosize = 64,
+			.line = 1,
+		},
+	},
+	{
+		.uart = {
+			.iotype = UPIO_MEM,
+			.ops = &msm_uart_pops,
+			.flags = UPF_BOOT_AUTOCONF,
+			.fifosize = 64,
+			.line = 2,
+		},
+	},
+};
+
+#define UART_NR	ARRAY_SIZE(msm_uart_ports)
+
+static inline struct uart_port *get_port_from_line(unsigned int line)
+{
+	return &msm_uart_ports[line].uart;
+}
+
+#ifdef CONFIG_SERIAL_MSM_CONSOLE
+
+static void msm_console_putchar(struct uart_port *port, int c)
+{
+	struct msm_port *msm_port = UART_TO_MSM(port);
+
+	if (msm_port->is_uartdm)
+		reset_dm_count(port);
+
+	while (!(msm_read(port, UART_SR) & UART_SR_TX_READY))
+		;
+	msm_write(port, c, msm_port->is_uartdm ? UARTDM_TF : UART_TF);
+}
+
+static void msm_console_write(struct console *co, const char *s,
+			      unsigned int count)
+{
+	struct uart_port *port;
+	struct msm_port *msm_port;
+
+	BUG_ON(co->index < 0 || co->index >= UART_NR);
+
+	port = get_port_from_line(co->index);
+	msm_port = UART_TO_MSM(port);
+
+	spin_lock(&port->lock);
+	uart_console_write(port, s, count, msm_console_putchar);
+	spin_unlock(&port->lock);
+}
+
+static int __init msm_console_setup(struct console *co, char *options)
+{
+	struct uart_port *port;
+	struct msm_port *msm_port;
+	int baud, flow, bits, parity;
+
+	if (unlikely(co->index >= UART_NR || co->index < 0))
+		return -ENXIO;
+
+	port = get_port_from_line(co->index);
+	msm_port = UART_TO_MSM(port);
+
+	if (unlikely(!port->membase))
+		return -ENXIO;
+
+	msm_init_clock(port);
+
+	if (options)
+		uart_parse_options(options, &baud, &parity, &bits, &flow);
+
+	bits = 8;
+	parity = 'n';
+	flow = 'n';
+	msm_write(port, UART_MR2_BITS_PER_CHAR_8 | UART_MR2_STOP_BIT_LEN_ONE,
+		  UART_MR2);	/* 8N1 */
+
+	if (baud < 300 || baud > 115200)
+		baud = 115200;
+	msm_set_baud_rate(port, baud);
+
+	msm_reset(port);
+
+	if (msm_port->is_uartdm) {
+		msm_write(port, UART_CR_CMD_PROTECTION_EN, UART_CR);
+		msm_write(port, UART_CR_TX_ENABLE, UART_CR);
+	}
+
+	printk(KERN_INFO "msm_serial: console setup on port #%d\n", port->line);
+
+	return uart_set_options(port, co, baud, parity, bits, flow);
+}
+
+static struct uart_driver msm_uart_driver;
+
+static struct console msm_console = {
+	.name = "ttyMSM",
+	.write = msm_console_write,
+	.device = uart_console_device,
+	.setup = msm_console_setup,
+	.flags = CON_PRINTBUFFER,
+	.index = -1,
+	.data = &msm_uart_driver,
+};
+
+#define MSM_CONSOLE	(&msm_console)
+
+#else
+#define MSM_CONSOLE	NULL
+#endif
+
+static struct uart_driver msm_uart_driver = {
+	.owner = THIS_MODULE,
+	.driver_name = "msm_serial",
+	.dev_name = "ttyMSM",
+	.nr = UART_NR,
+	.cons = MSM_CONSOLE,
+};
+
+static atomic_t msm_uart_next_id = ATOMIC_INIT(0);
+
+static int __init msm_serial_probe(struct platform_device *pdev)
+{
+	struct msm_port *msm_port;
+	struct resource *resource;
+	struct uart_port *port;
+	int irq;
+
+	if (pdev->id == -1)
+		pdev->id = atomic_inc_return(&msm_uart_next_id) - 1;
+
+	if (unlikely(pdev->id < 0 || pdev->id >= UART_NR))
+		return -ENXIO;
+
+	printk(KERN_INFO "msm_serial: detected port #%d\n", pdev->id);
+
+	port = get_port_from_line(pdev->id);
+	port->dev = &pdev->dev;
+	msm_port = UART_TO_MSM(port);
+
+	if (platform_get_resource(pdev, IORESOURCE_MEM, 1))
+		msm_port->is_uartdm = 1;
+	else
+		msm_port->is_uartdm = 0;
+
+	if (msm_port->is_uartdm) {
+		msm_port->clk = clk_get(&pdev->dev, "gsbi_uart_clk");
+		msm_port->pclk = clk_get(&pdev->dev, "gsbi_pclk");
+	} else {
+		msm_port->clk = clk_get(&pdev->dev, "uart_clk");
+		msm_port->pclk = ERR_PTR(-ENOENT);
+	}
+
+	if (unlikely(IS_ERR(msm_port->clk) || (IS_ERR(msm_port->pclk) &&
+					       msm_port->is_uartdm)))
+			return PTR_ERR(msm_port->clk);
+
+	if (msm_port->is_uartdm)
+		clk_set_rate(msm_port->clk, 7372800);
+
+	port->uartclk = clk_get_rate(msm_port->clk);
+	printk(KERN_INFO "uartclk = %d\n", port->uartclk);
+
+
+	resource = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (unlikely(!resource))
+		return -ENXIO;
+	port->mapbase = resource->start;
+
+	irq = platform_get_irq(pdev, 0);
+	if (unlikely(irq < 0))
+		return -ENXIO;
+	port->irq = irq;
+
+	platform_set_drvdata(pdev, port);
+
+	return uart_add_one_port(&msm_uart_driver, port);
+}
+
+static int __devexit msm_serial_remove(struct platform_device *pdev)
+{
+	struct msm_port *msm_port = platform_get_drvdata(pdev);
+
+	clk_put(msm_port->clk);
+
+	return 0;
+}
+
+static struct of_device_id msm_match_table[] = {
+	{ .compatible = "qcom,msm-uart" },
+	{}
+};
+
+static struct platform_driver msm_platform_driver = {
+	.remove = msm_serial_remove,
+	.driver = {
+		.name = "msm_serial",
+		.owner = THIS_MODULE,
+		.of_match_table = msm_match_table,
+	},
+};
+
+static int __init msm_serial_init(void)
+{
+	int ret;
+
+	ret = uart_register_driver(&msm_uart_driver);
+	if (unlikely(ret))
+		return ret;
+
+	ret = platform_driver_probe(&msm_platform_driver, msm_serial_probe);
+	if (unlikely(ret))
+		uart_unregister_driver(&msm_uart_driver);
+
+	printk(KERN_INFO "msm_serial: driver initialized\n");
+
+	return ret;
+}
+
+static void __exit msm_serial_exit(void)
+{
+#ifdef CONFIG_SERIAL_MSM_CONSOLE
+	unregister_console(&msm_console);
+#endif
+	platform_driver_unregister(&msm_platform_driver);
+	uart_unregister_driver(&msm_uart_driver);
+}
+
+module_init(msm_serial_init);
+module_exit(msm_serial_exit);
+
+MODULE_AUTHOR("Robert Love <rlove@google.com>");
+MODULE_DESCRIPTION("Driver for msm7x serial device");
+MODULE_LICENSE("GPL");
diff --git a/ap/os/linux/linux-3.4.x/drivers/tty/serial/msm_serial.h b/ap/os/linux/linux-3.4.x/drivers/tty/serial/msm_serial.h
new file mode 100644
index 0000000..e4acef5
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/tty/serial/msm_serial.h
@@ -0,0 +1,187 @@
+/*
+ * Copyright (C) 2007 Google, Inc.
+ * Author: Robert Love <rlove@google.com>
+ * Copyright (c) 2011, Code Aurora Forum. All rights reserved.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ */
+
+#ifndef __DRIVERS_SERIAL_MSM_SERIAL_H
+#define __DRIVERS_SERIAL_MSM_SERIAL_H
+
+#define UART_MR1			0x0000
+
+#define UART_MR1_AUTO_RFR_LEVEL0	0x3F
+#define UART_MR1_AUTO_RFR_LEVEL1	0x3FF00
+#define UART_MR1_RX_RDY_CTL    		(1 << 7)
+#define UART_MR1_CTS_CTL       		(1 << 6)
+
+#define UART_MR2			0x0004
+#define UART_MR2_ERROR_MODE		(1 << 6)
+#define UART_MR2_BITS_PER_CHAR		0x30
+#define UART_MR2_BITS_PER_CHAR_5	(0x0 << 4)
+#define UART_MR2_BITS_PER_CHAR_6	(0x1 << 4)
+#define UART_MR2_BITS_PER_CHAR_7	(0x2 << 4)
+#define UART_MR2_BITS_PER_CHAR_8	(0x3 << 4)
+#define UART_MR2_STOP_BIT_LEN_ONE	(0x1 << 2)
+#define UART_MR2_STOP_BIT_LEN_TWO	(0x3 << 2)
+#define UART_MR2_PARITY_MODE_NONE	0x0
+#define UART_MR2_PARITY_MODE_ODD	0x1
+#define UART_MR2_PARITY_MODE_EVEN	0x2
+#define UART_MR2_PARITY_MODE_SPACE	0x3
+#define UART_MR2_PARITY_MODE		0x3
+
+#define UART_CSR	0x0008
+#define UART_CSR_115200	0xFF
+#define UART_CSR_57600	0xEE
+#define UART_CSR_38400	0xDD
+#define UART_CSR_28800	0xCC
+#define UART_CSR_19200	0xBB
+#define UART_CSR_14400	0xAA
+#define UART_CSR_9600	0x99
+#define UART_CSR_4800	0x77
+#define UART_CSR_2400	0x55
+#define UART_CSR_1200	0x44
+#define UART_CSR_600	0x33
+#define UART_CSR_300	0x22
+
+#define UART_TF		0x000C
+#define UARTDM_TF	0x0070
+
+#define UART_CR				0x0010
+#define UART_CR_CMD_NULL		(0 << 4)
+#define UART_CR_CMD_RESET_RX		(1 << 4)
+#define UART_CR_CMD_RESET_TX		(2 << 4)
+#define UART_CR_CMD_RESET_ERR		(3 << 4)
+#define UART_CR_CMD_RESET_BREAK_INT	(4 << 4)
+#define UART_CR_CMD_START_BREAK		(5 << 4)
+#define UART_CR_CMD_STOP_BREAK		(6 << 4)
+#define UART_CR_CMD_RESET_CTS		(7 << 4)
+#define UART_CR_CMD_RESET_STALE_INT	(8 << 4)
+#define UART_CR_CMD_PACKET_MODE		(9 << 4)
+#define UART_CR_CMD_MODE_RESET		(12 << 4)
+#define UART_CR_CMD_SET_RFR		(13 << 4)
+#define UART_CR_CMD_RESET_RFR		(14 << 4)
+#define UART_CR_CMD_PROTECTION_EN	(16 << 4)
+#define UART_CR_CMD_STALE_EVENT_ENABLE	(80 << 4)
+#define UART_CR_TX_DISABLE		(1 << 3)
+#define UART_CR_TX_ENABLE		(1 << 2)
+#define UART_CR_RX_DISABLE		(1 << 1)
+#define UART_CR_RX_ENABLE		(1 << 0)
+
+#define UART_IMR		0x0014
+#define UART_IMR_TXLEV		(1 << 0)
+#define UART_IMR_RXSTALE	(1 << 3)
+#define UART_IMR_RXLEV		(1 << 4)
+#define UART_IMR_DELTA_CTS	(1 << 5)
+#define UART_IMR_CURRENT_CTS	(1 << 6)
+
+#define UART_IPR_RXSTALE_LAST		0x20
+#define UART_IPR_STALE_LSB		0x1F
+#define UART_IPR_STALE_TIMEOUT_MSB	0x3FF80
+
+#define UART_IPR	0x0018
+#define UART_TFWR	0x001C
+#define UART_RFWR	0x0020
+#define UART_HCR	0x0024
+
+#define UART_MREG		0x0028
+#define UART_NREG		0x002C
+#define UART_DREG		0x0030
+#define UART_MNDREG		0x0034
+#define UART_IRDA		0x0038
+#define UART_MISR_MODE		0x0040
+#define UART_MISR_RESET		0x0044
+#define UART_MISR_EXPORT	0x0048
+#define UART_MISR_VAL		0x004C
+#define UART_TEST_CTRL		0x0050
+
+#define UART_SR			0x0008
+#define UART_SR_HUNT_CHAR	(1 << 7)
+#define UART_SR_RX_BREAK	(1 << 6)
+#define UART_SR_PAR_FRAME_ERR	(1 << 5)
+#define UART_SR_OVERRUN		(1 << 4)
+#define UART_SR_TX_EMPTY	(1 << 3)
+#define UART_SR_TX_READY	(1 << 2)
+#define UART_SR_RX_FULL		(1 << 1)
+#define UART_SR_RX_READY	(1 << 0)
+
+#define UART_RF			0x000C
+#define UARTDM_RF		0x0070
+#define UART_MISR		0x0010
+#define UART_ISR		0x0014
+#define UART_ISR_TX_READY	(1 << 7)
+
+#define GSBI_CONTROL		0x0
+#define GSBI_PROTOCOL_CODE	0x30
+#define GSBI_PROTOCOL_UART	0x40
+#define GSBI_PROTOCOL_IDLE	0x0
+
+#define UARTDM_DMRX		0x34
+#define UARTDM_NCF_TX		0x40
+#define UARTDM_RX_TOTAL_SNAP	0x38
+
+#define UART_TO_MSM(uart_port)	((struct msm_port *) uart_port)
+
+static inline
+void msm_write(struct uart_port *port, unsigned int val, unsigned int off)
+{
+	__raw_writel(val, port->membase + off);
+}
+
+static inline
+unsigned int msm_read(struct uart_port *port, unsigned int off)
+{
+	return __raw_readl(port->membase + off);
+}
+
+/*
+ * Setup the MND registers to use the TCXO clock.
+ */
+static inline void msm_serial_set_mnd_regs_tcxo(struct uart_port *port)
+{
+	msm_write(port, 0x06, UART_MREG);
+	msm_write(port, 0xF1, UART_NREG);
+	msm_write(port, 0x0F, UART_DREG);
+	msm_write(port, 0x1A, UART_MNDREG);
+}
+
+/*
+ * Setup the MND registers to use the TCXO clock divided by 4.
+ */
+static inline void msm_serial_set_mnd_regs_tcxoby4(struct uart_port *port)
+{
+	msm_write(port, 0x18, UART_MREG);
+	msm_write(port, 0xF6, UART_NREG);
+	msm_write(port, 0x0F, UART_DREG);
+	msm_write(port, 0x0A, UART_MNDREG);
+}
+
+static inline
+void msm_serial_set_mnd_regs_from_uartclk(struct uart_port *port)
+{
+	if (port->uartclk == 19200000)
+		msm_serial_set_mnd_regs_tcxo(port);
+	else
+		msm_serial_set_mnd_regs_tcxoby4(port);
+}
+
+/*
+ * TROUT has a specific defect that makes it report it's uartclk
+ * as 19.2Mhz (TCXO) when it's actually 4.8Mhz (TCXO/4). This special
+ * cases TROUT to use the right clock.
+ */
+#ifdef CONFIG_MACH_TROUT
+#define msm_serial_set_mnd_regs msm_serial_set_mnd_regs_tcxoby4
+#else
+#define msm_serial_set_mnd_regs msm_serial_set_mnd_regs_from_uartclk
+#endif
+
+#endif	/* __DRIVERS_SERIAL_MSM_SERIAL_H */
diff --git a/ap/os/linux/linux-3.4.x/drivers/tty/serial/msm_serial_hs.c b/ap/os/linux/linux-3.4.x/drivers/tty/serial/msm_serial_hs.c
new file mode 100644
index 0000000..fca13dc
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/tty/serial/msm_serial_hs.c
@@ -0,0 +1,1878 @@
+/*
+ * MSM 7k/8k High speed uart driver
+ *
+ * Copyright (c) 2007-2011, Code Aurora Forum. All rights reserved.
+ * Copyright (c) 2008 Google Inc.
+ * Modified: Nick Pelly <npelly@google.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * 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.
+ *
+ * Has optional support for uart power management independent of linux
+ * suspend/resume:
+ *
+ * RX wakeup.
+ * UART wakeup can be triggered by RX activity (using a wakeup GPIO on the
+ * UART RX pin). This should only be used if there is not a wakeup
+ * GPIO on the UART CTS, and the first RX byte is known (for example, with the
+ * Bluetooth Texas Instruments HCILL protocol), since the first RX byte will
+ * always be lost. RTS will be asserted even while the UART is off in this mode
+ * of operation. See msm_serial_hs_platform_data.rx_wakeup_irq.
+ */
+
+#include <linux/module.h>
+
+#include <linux/serial.h>
+#include <linux/serial_core.h>
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/io.h>
+#include <linux/ioport.h>
+#include <linux/kernel.h>
+#include <linux/timer.h>
+#include <linux/clk.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/dma-mapping.h>
+#include <linux/dmapool.h>
+#include <linux/wait.h>
+#include <linux/workqueue.h>
+
+#include <linux/atomic.h>
+#include <asm/irq.h>
+
+#include <mach/hardware.h>
+#include <mach/dma.h>
+#include <linux/platform_data/msm_serial_hs.h>
+
+/* HSUART Registers */
+#define UARTDM_MR1_ADDR 0x0
+#define UARTDM_MR2_ADDR 0x4
+
+/* Data Mover result codes */
+#define RSLT_FIFO_CNTR_BMSK (0xE << 28)
+#define RSLT_VLD            BIT(1)
+
+/* write only register */
+#define UARTDM_CSR_ADDR 0x8
+#define UARTDM_CSR_115200 0xFF
+#define UARTDM_CSR_57600  0xEE
+#define UARTDM_CSR_38400  0xDD
+#define UARTDM_CSR_28800  0xCC
+#define UARTDM_CSR_19200  0xBB
+#define UARTDM_CSR_14400  0xAA
+#define UARTDM_CSR_9600   0x99
+#define UARTDM_CSR_7200   0x88
+#define UARTDM_CSR_4800   0x77
+#define UARTDM_CSR_3600   0x66
+#define UARTDM_CSR_2400   0x55
+#define UARTDM_CSR_1200   0x44
+#define UARTDM_CSR_600    0x33
+#define UARTDM_CSR_300    0x22
+#define UARTDM_CSR_150    0x11
+#define UARTDM_CSR_75     0x00
+
+/* write only register */
+#define UARTDM_TF_ADDR 0x70
+#define UARTDM_TF2_ADDR 0x74
+#define UARTDM_TF3_ADDR 0x78
+#define UARTDM_TF4_ADDR 0x7C
+
+/* write only register */
+#define UARTDM_CR_ADDR 0x10
+#define UARTDM_IMR_ADDR 0x14
+
+#define UARTDM_IPR_ADDR 0x18
+#define UARTDM_TFWR_ADDR 0x1c
+#define UARTDM_RFWR_ADDR 0x20
+#define UARTDM_HCR_ADDR 0x24
+#define UARTDM_DMRX_ADDR 0x34
+#define UARTDM_IRDA_ADDR 0x38
+#define UARTDM_DMEN_ADDR 0x3c
+
+/* UART_DM_NO_CHARS_FOR_TX */
+#define UARTDM_NCF_TX_ADDR 0x40
+
+#define UARTDM_BADR_ADDR 0x44
+
+#define UARTDM_SIM_CFG_ADDR 0x80
+/* Read Only register */
+#define UARTDM_SR_ADDR 0x8
+
+/* Read Only register */
+#define UARTDM_RF_ADDR  0x70
+#define UARTDM_RF2_ADDR 0x74
+#define UARTDM_RF3_ADDR 0x78
+#define UARTDM_RF4_ADDR 0x7C
+
+/* Read Only register */
+#define UARTDM_MISR_ADDR 0x10
+
+/* Read Only register */
+#define UARTDM_ISR_ADDR 0x14
+#define UARTDM_RX_TOTAL_SNAP_ADDR 0x38
+
+#define UARTDM_RXFS_ADDR 0x50
+
+/* Register field Mask Mapping */
+#define UARTDM_SR_PAR_FRAME_BMSK        BIT(5)
+#define UARTDM_SR_OVERRUN_BMSK          BIT(4)
+#define UARTDM_SR_TXEMT_BMSK            BIT(3)
+#define UARTDM_SR_TXRDY_BMSK            BIT(2)
+#define UARTDM_SR_RXRDY_BMSK            BIT(0)
+
+#define UARTDM_CR_TX_DISABLE_BMSK       BIT(3)
+#define UARTDM_CR_RX_DISABLE_BMSK       BIT(1)
+#define UARTDM_CR_TX_EN_BMSK            BIT(2)
+#define UARTDM_CR_RX_EN_BMSK            BIT(0)
+
+/* UARTDM_CR channel_comman bit value (register field is bits 8:4) */
+#define RESET_RX                0x10
+#define RESET_TX                0x20
+#define RESET_ERROR_STATUS      0x30
+#define RESET_BREAK_INT         0x40
+#define START_BREAK             0x50
+#define STOP_BREAK              0x60
+#define RESET_CTS               0x70
+#define RESET_STALE_INT         0x80
+#define RFR_LOW                 0xD0
+#define RFR_HIGH                0xE0
+#define CR_PROTECTION_EN        0x100
+#define STALE_EVENT_ENABLE      0x500
+#define STALE_EVENT_DISABLE     0x600
+#define FORCE_STALE_EVENT       0x400
+#define CLEAR_TX_READY          0x300
+#define RESET_TX_ERROR          0x800
+#define RESET_TX_DONE           0x810
+
+#define UARTDM_MR1_AUTO_RFR_LEVEL1_BMSK 0xffffff00
+#define UARTDM_MR1_AUTO_RFR_LEVEL0_BMSK 0x3f
+#define UARTDM_MR1_CTS_CTL_BMSK 0x40
+#define UARTDM_MR1_RX_RDY_CTL_BMSK 0x80
+
+#define UARTDM_MR2_ERROR_MODE_BMSK 0x40
+#define UARTDM_MR2_BITS_PER_CHAR_BMSK 0x30
+
+/* bits per character configuration */
+#define FIVE_BPC  (0 << 4)
+#define SIX_BPC   (1 << 4)
+#define SEVEN_BPC (2 << 4)
+#define EIGHT_BPC (3 << 4)
+
+#define UARTDM_MR2_STOP_BIT_LEN_BMSK 0xc
+#define STOP_BIT_ONE (1 << 2)
+#define STOP_BIT_TWO (3 << 2)
+
+#define UARTDM_MR2_PARITY_MODE_BMSK 0x3
+
+/* Parity configuration */
+#define NO_PARITY 0x0
+#define EVEN_PARITY 0x1
+#define ODD_PARITY 0x2
+#define SPACE_PARITY 0x3
+
+#define UARTDM_IPR_STALE_TIMEOUT_MSB_BMSK 0xffffff80
+#define UARTDM_IPR_STALE_LSB_BMSK 0x1f
+
+/* These can be used for both ISR and IMR register */
+#define UARTDM_ISR_TX_READY_BMSK        BIT(7)
+#define UARTDM_ISR_CURRENT_CTS_BMSK     BIT(6)
+#define UARTDM_ISR_DELTA_CTS_BMSK       BIT(5)
+#define UARTDM_ISR_RXLEV_BMSK           BIT(4)
+#define UARTDM_ISR_RXSTALE_BMSK         BIT(3)
+#define UARTDM_ISR_RXBREAK_BMSK         BIT(2)
+#define UARTDM_ISR_RXHUNT_BMSK          BIT(1)
+#define UARTDM_ISR_TXLEV_BMSK           BIT(0)
+
+/* Field definitions for UART_DM_DMEN*/
+#define UARTDM_TX_DM_EN_BMSK 0x1
+#define UARTDM_RX_DM_EN_BMSK 0x2
+
+#define UART_FIFOSIZE 64
+#define UARTCLK 7372800
+
+/* Rx DMA request states */
+enum flush_reason {
+	FLUSH_NONE,
+	FLUSH_DATA_READY,
+	FLUSH_DATA_INVALID,  /* values after this indicate invalid data */
+	FLUSH_IGNORE = FLUSH_DATA_INVALID,
+	FLUSH_STOP,
+	FLUSH_SHUTDOWN,
+};
+
+/* UART clock states */
+enum msm_hs_clk_states_e {
+	MSM_HS_CLK_PORT_OFF,     /* port not in use */
+	MSM_HS_CLK_OFF,          /* clock disabled */
+	MSM_HS_CLK_REQUEST_OFF,  /* disable after TX and RX flushed */
+	MSM_HS_CLK_ON,           /* clock enabled */
+};
+
+/* Track the forced RXSTALE flush during clock off sequence.
+ * These states are only valid during MSM_HS_CLK_REQUEST_OFF */
+enum msm_hs_clk_req_off_state_e {
+	CLK_REQ_OFF_START,
+	CLK_REQ_OFF_RXSTALE_ISSUED,
+	CLK_REQ_OFF_FLUSH_ISSUED,
+	CLK_REQ_OFF_RXSTALE_FLUSHED,
+};
+
+/**
+ * struct msm_hs_tx
+ * @tx_ready_int_en: ok to dma more tx?
+ * @dma_in_flight: tx dma in progress
+ * @xfer: top level DMA command pointer structure
+ * @command_ptr: third level command struct pointer
+ * @command_ptr_ptr: second level command list struct pointer
+ * @mapped_cmd_ptr: DMA view of third level command struct
+ * @mapped_cmd_ptr_ptr: DMA view of second level command list struct
+ * @tx_count: number of bytes to transfer in DMA transfer
+ * @dma_base: DMA view of UART xmit buffer
+ *
+ * This structure describes a single Tx DMA transaction. MSM DMA
+ * commands have two levels of indirection. The top level command
+ * ptr points to a list of command ptr which in turn points to a
+ * single DMA 'command'. In our case each Tx transaction consists
+ * of a single second level pointer pointing to a 'box type' command.
+ */
+struct msm_hs_tx {
+	unsigned int tx_ready_int_en;
+	unsigned int dma_in_flight;
+	struct msm_dmov_cmd xfer;
+	dmov_box *command_ptr;
+	u32 *command_ptr_ptr;
+	dma_addr_t mapped_cmd_ptr;
+	dma_addr_t mapped_cmd_ptr_ptr;
+	int tx_count;
+	dma_addr_t dma_base;
+};
+
+/**
+ * struct msm_hs_rx
+ * @flush: Rx DMA request state
+ * @xfer: top level DMA command pointer structure
+ * @cmdptr_dmaaddr: DMA view of second level command structure
+ * @command_ptr: third level DMA command pointer structure
+ * @command_ptr_ptr: second level DMA command list pointer
+ * @mapped_cmd_ptr: DMA view of the third level command structure
+ * @wait: wait for DMA completion before shutdown
+ * @buffer: destination buffer for RX DMA
+ * @rbuffer: DMA view of buffer
+ * @pool: dma pool out of which coherent rx buffer is allocated
+ * @tty_work: private work-queue for tty flip buffer push task
+ *
+ * This structure describes a single Rx DMA transaction. Rx DMA
+ * transactions use box mode DMA commands.
+ */
+struct msm_hs_rx {
+	enum flush_reason flush;
+	struct msm_dmov_cmd xfer;
+	dma_addr_t cmdptr_dmaaddr;
+	dmov_box *command_ptr;
+	u32 *command_ptr_ptr;
+	dma_addr_t mapped_cmd_ptr;
+	wait_queue_head_t wait;
+	dma_addr_t rbuffer;
+	unsigned char *buffer;
+	struct dma_pool *pool;
+	struct work_struct tty_work;
+};
+
+/**
+ * struct msm_hs_rx_wakeup
+ * @irq: IRQ line to be configured as interrupt source on Rx activity
+ * @ignore: boolean value. 1 = ignore the wakeup interrupt
+ * @rx_to_inject: extra character to be inserted to Rx tty on wakeup
+ * @inject_rx: 1 = insert rx_to_inject. 0 = do not insert extra character
+ *
+ * This is an optional structure required for UART Rx GPIO IRQ based
+ * wakeup from low power state. UART wakeup can be triggered by RX activity
+ * (using a wakeup GPIO on the UART RX pin). This should only be used if
+ * there is not a wakeup GPIO on the UART CTS, and the first RX byte is
+ * known (eg., with the Bluetooth Texas Instruments HCILL protocol),
+ * since the first RX byte will always be lost. RTS will be asserted even
+ * while the UART is clocked off in this mode of operation.
+ */
+struct msm_hs_rx_wakeup {
+	int irq;  /* < 0 indicates low power wakeup disabled */
+	unsigned char ignore;
+	unsigned char inject_rx;
+	char rx_to_inject;
+};
+
+/**
+ * struct msm_hs_port
+ * @uport: embedded uart port structure
+ * @imr_reg: shadow value of UARTDM_IMR
+ * @clk: uart input clock handle
+ * @tx: Tx transaction related data structure
+ * @rx: Rx transaction related data structure
+ * @dma_tx_channel: Tx DMA command channel
+ * @dma_rx_channel Rx DMA command channel
+ * @dma_tx_crci: Tx channel rate control interface number
+ * @dma_rx_crci: Rx channel rate control interface number
+ * @clk_off_timer: Timer to poll DMA event completion before clock off
+ * @clk_off_delay: clk_off_timer poll interval
+ * @clk_state: overall clock state
+ * @clk_req_off_state: post flush clock states
+ * @rx_wakeup: optional rx_wakeup feature related data
+ * @exit_lpm_cb: optional callback to exit low power mode
+ *
+ * Low level serial port structure.
+ */
+struct msm_hs_port {
+	struct uart_port uport;
+	unsigned long imr_reg;
+	struct clk *clk;
+	struct msm_hs_tx tx;
+	struct msm_hs_rx rx;
+
+	int dma_tx_channel;
+	int dma_rx_channel;
+	int dma_tx_crci;
+	int dma_rx_crci;
+
+	struct hrtimer clk_off_timer;
+	ktime_t clk_off_delay;
+	enum msm_hs_clk_states_e clk_state;
+	enum msm_hs_clk_req_off_state_e clk_req_off_state;
+
+	struct msm_hs_rx_wakeup rx_wakeup;
+	void (*exit_lpm_cb)(struct uart_port *);
+};
+
+#define MSM_UARTDM_BURST_SIZE 16   /* DM burst size (in bytes) */
+#define UARTDM_TX_BUF_SIZE UART_XMIT_SIZE
+#define UARTDM_RX_BUF_SIZE 512
+
+#define UARTDM_NR 2
+
+static struct msm_hs_port q_uart_port[UARTDM_NR];
+static struct platform_driver msm_serial_hs_platform_driver;
+static struct uart_driver msm_hs_driver;
+static struct uart_ops msm_hs_ops;
+static struct workqueue_struct *msm_hs_workqueue;
+
+#define UARTDM_TO_MSM(uart_port) \
+	container_of((uart_port), struct msm_hs_port, uport)
+
+static unsigned int use_low_power_rx_wakeup(struct msm_hs_port
+						   *msm_uport)
+{
+	return (msm_uport->rx_wakeup.irq >= 0);
+}
+
+static unsigned int msm_hs_read(struct uart_port *uport,
+				       unsigned int offset)
+{
+	return ioread32(uport->membase + offset);
+}
+
+static void msm_hs_write(struct uart_port *uport, unsigned int offset,
+				 unsigned int value)
+{
+	iowrite32(value, uport->membase + offset);
+}
+
+static void msm_hs_release_port(struct uart_port *port)
+{
+	iounmap(port->membase);
+}
+
+static int msm_hs_request_port(struct uart_port *port)
+{
+	port->membase = ioremap(port->mapbase, PAGE_SIZE);
+	if (unlikely(!port->membase))
+		return -ENOMEM;
+
+	/* configure the CR Protection to Enable */
+	msm_hs_write(port, UARTDM_CR_ADDR, CR_PROTECTION_EN);
+	return 0;
+}
+
+static int __devexit msm_hs_remove(struct platform_device *pdev)
+{
+
+	struct msm_hs_port *msm_uport;
+	struct device *dev;
+
+	if (pdev->id < 0 || pdev->id >= UARTDM_NR) {
+		printk(KERN_ERR "Invalid plaform device ID = %d\n", pdev->id);
+		return -EINVAL;
+	}
+
+	msm_uport = &q_uart_port[pdev->id];
+	dev = msm_uport->uport.dev;
+
+	dma_unmap_single(dev, msm_uport->rx.mapped_cmd_ptr, sizeof(dmov_box),
+			 DMA_TO_DEVICE);
+	dma_pool_free(msm_uport->rx.pool, msm_uport->rx.buffer,
+		      msm_uport->rx.rbuffer);
+	dma_pool_destroy(msm_uport->rx.pool);
+
+	dma_unmap_single(dev, msm_uport->rx.cmdptr_dmaaddr, sizeof(u32),
+			 DMA_TO_DEVICE);
+	dma_unmap_single(dev, msm_uport->tx.mapped_cmd_ptr_ptr, sizeof(u32),
+			 DMA_TO_DEVICE);
+	dma_unmap_single(dev, msm_uport->tx.mapped_cmd_ptr, sizeof(dmov_box),
+			 DMA_TO_DEVICE);
+
+	uart_remove_one_port(&msm_hs_driver, &msm_uport->uport);
+	clk_put(msm_uport->clk);
+
+	/* Free the tx resources */
+	kfree(msm_uport->tx.command_ptr);
+	kfree(msm_uport->tx.command_ptr_ptr);
+
+	/* Free the rx resources */
+	kfree(msm_uport->rx.command_ptr);
+	kfree(msm_uport->rx.command_ptr_ptr);
+
+	iounmap(msm_uport->uport.membase);
+
+	return 0;
+}
+
+static int msm_hs_init_clk_locked(struct uart_port *uport)
+{
+	int ret;
+	struct msm_hs_port *msm_uport = UARTDM_TO_MSM(uport);
+
+	ret = clk_enable(msm_uport->clk);
+	if (ret) {
+		printk(KERN_ERR "Error could not turn on UART clk\n");
+		return ret;
+	}
+
+	/* Set up the MREG/NREG/DREG/MNDREG */
+	ret = clk_set_rate(msm_uport->clk, uport->uartclk);
+	if (ret) {
+		printk(KERN_WARNING "Error setting clock rate on UART\n");
+		clk_disable(msm_uport->clk);
+		return ret;
+	}
+
+	msm_uport->clk_state = MSM_HS_CLK_ON;
+	return 0;
+}
+
+/* Enable and Disable clocks  (Used for power management) */
+static void msm_hs_pm(struct uart_port *uport, unsigned int state,
+		      unsigned int oldstate)
+{
+	struct msm_hs_port *msm_uport = UARTDM_TO_MSM(uport);
+
+	if (use_low_power_rx_wakeup(msm_uport) ||
+	    msm_uport->exit_lpm_cb)
+		return;  /* ignore linux PM states,
+			    use msm_hs_request_clock API */
+
+	switch (state) {
+	case 0:
+		clk_enable(msm_uport->clk);
+		break;
+	case 3:
+		clk_disable(msm_uport->clk);
+		break;
+	default:
+		dev_err(uport->dev, "msm_serial: Unknown PM state %d\n",
+			state);
+	}
+}
+
+/*
+ * programs the UARTDM_CSR register with correct bit rates
+ *
+ * Interrupts should be disabled before we are called, as
+ * we modify Set Baud rate
+ * Set receive stale interrupt level, dependent on Bit Rate
+ * Goal is to have around 8 ms before indicate stale.
+ * roundup (((Bit Rate * .008) / 10) + 1
+ */
+static void msm_hs_set_bps_locked(struct uart_port *uport,
+				  unsigned int bps)
+{
+	unsigned long rxstale;
+	unsigned long data;
+	struct msm_hs_port *msm_uport = UARTDM_TO_MSM(uport);
+
+	switch (bps) {
+	case 300:
+		msm_hs_write(uport, UARTDM_CSR_ADDR, UARTDM_CSR_75);
+		rxstale = 1;
+		break;
+	case 600:
+		msm_hs_write(uport, UARTDM_CSR_ADDR, UARTDM_CSR_150);
+		rxstale = 1;
+		break;
+	case 1200:
+		msm_hs_write(uport, UARTDM_CSR_ADDR, UARTDM_CSR_300);
+		rxstale = 1;
+		break;
+	case 2400:
+		msm_hs_write(uport, UARTDM_CSR_ADDR, UARTDM_CSR_600);
+		rxstale = 1;
+		break;
+	case 4800:
+		msm_hs_write(uport, UARTDM_CSR_ADDR, UARTDM_CSR_1200);
+		rxstale = 1;
+		break;
+	case 9600:
+		msm_hs_write(uport, UARTDM_CSR_ADDR, UARTDM_CSR_2400);
+		rxstale = 2;
+		break;
+	case 14400:
+		msm_hs_write(uport, UARTDM_CSR_ADDR, UARTDM_CSR_3600);
+		rxstale = 3;
+		break;
+	case 19200:
+		msm_hs_write(uport, UARTDM_CSR_ADDR, UARTDM_CSR_4800);
+		rxstale = 4;
+		break;
+	case 28800:
+		msm_hs_write(uport, UARTDM_CSR_ADDR, UARTDM_CSR_7200);
+		rxstale = 6;
+		break;
+	case 38400:
+		msm_hs_write(uport, UARTDM_CSR_ADDR, UARTDM_CSR_9600);
+		rxstale = 8;
+		break;
+	case 57600:
+		msm_hs_write(uport, UARTDM_CSR_ADDR, UARTDM_CSR_14400);
+		rxstale = 16;
+		break;
+	case 76800:
+		msm_hs_write(uport, UARTDM_CSR_ADDR, UARTDM_CSR_19200);
+		rxstale = 16;
+		break;
+	case 115200:
+		msm_hs_write(uport, UARTDM_CSR_ADDR, UARTDM_CSR_28800);
+		rxstale = 31;
+		break;
+	case 230400:
+		msm_hs_write(uport, UARTDM_CSR_ADDR, UARTDM_CSR_57600);
+		rxstale = 31;
+		break;
+	case 460800:
+		msm_hs_write(uport, UARTDM_CSR_ADDR, UARTDM_CSR_115200);
+		rxstale = 31;
+		break;
+	case 4000000:
+	case 3686400:
+	case 3200000:
+	case 3500000:
+	case 3000000:
+	case 2500000:
+	case 1500000:
+	case 1152000:
+	case 1000000:
+	case 921600:
+		msm_hs_write(uport, UARTDM_CSR_ADDR, UARTDM_CSR_115200);
+		rxstale = 31;
+		break;
+	default:
+		msm_hs_write(uport, UARTDM_CSR_ADDR, UARTDM_CSR_2400);
+		/* default to 9600 */
+		bps = 9600;
+		rxstale = 2;
+		break;
+	}
+	if (bps > 460800)
+		uport->uartclk = bps * 16;
+	else
+		uport->uartclk = UARTCLK;
+
+	if (clk_set_rate(msm_uport->clk, uport->uartclk)) {
+		printk(KERN_WARNING "Error setting clock rate on UART\n");
+		return;
+	}
+
+	data = rxstale & UARTDM_IPR_STALE_LSB_BMSK;
+	data |= UARTDM_IPR_STALE_TIMEOUT_MSB_BMSK & (rxstale << 2);
+
+	msm_hs_write(uport, UARTDM_IPR_ADDR, data);
+}
+
+/*
+ * termios :  new ktermios
+ * oldtermios:  old ktermios previous setting
+ *
+ * Configure the serial port
+ */
+static void msm_hs_set_termios(struct uart_port *uport,
+			       struct ktermios *termios,
+			       struct ktermios *oldtermios)
+{
+	unsigned int bps;
+	unsigned long data;
+	unsigned long flags;
+	unsigned int c_cflag = termios->c_cflag;
+	struct msm_hs_port *msm_uport = UARTDM_TO_MSM(uport);
+
+	spin_lock_irqsave(&uport->lock, flags);
+	clk_enable(msm_uport->clk);
+
+	/* 300 is the minimum baud support by the driver  */
+	bps = uart_get_baud_rate(uport, termios, oldtermios, 200, 4000000);
+
+	/* Temporary remapping  200 BAUD to 3.2 mbps */
+	if (bps == 200)
+		bps = 3200000;
+
+	msm_hs_set_bps_locked(uport, bps);
+
+	data = msm_hs_read(uport, UARTDM_MR2_ADDR);
+	data &= ~UARTDM_MR2_PARITY_MODE_BMSK;
+	/* set parity */
+	if (PARENB == (c_cflag & PARENB)) {
+		if (PARODD == (c_cflag & PARODD))
+			data |= ODD_PARITY;
+		else if (CMSPAR == (c_cflag & CMSPAR))
+			data |= SPACE_PARITY;
+		else
+			data |= EVEN_PARITY;
+	}
+
+	/* Set bits per char */
+	data &= ~UARTDM_MR2_BITS_PER_CHAR_BMSK;
+
+	switch (c_cflag & CSIZE) {
+	case CS5:
+		data |= FIVE_BPC;
+		break;
+	case CS6:
+		data |= SIX_BPC;
+		break;
+	case CS7:
+		data |= SEVEN_BPC;
+		break;
+	default:
+		data |= EIGHT_BPC;
+		break;
+	}
+	/* stop bits */
+	if (c_cflag & CSTOPB) {
+		data |= STOP_BIT_TWO;
+	} else {
+		/* otherwise 1 stop bit */
+		data |= STOP_BIT_ONE;
+	}
+	data |= UARTDM_MR2_ERROR_MODE_BMSK;
+	/* write parity/bits per char/stop bit configuration */
+	msm_hs_write(uport, UARTDM_MR2_ADDR, data);
+
+	/* Configure HW flow control */
+	data = msm_hs_read(uport, UARTDM_MR1_ADDR);
+
+	data &= ~(UARTDM_MR1_CTS_CTL_BMSK | UARTDM_MR1_RX_RDY_CTL_BMSK);
+
+	if (c_cflag & CRTSCTS) {
+		data |= UARTDM_MR1_CTS_CTL_BMSK;
+		data |= UARTDM_MR1_RX_RDY_CTL_BMSK;
+	}
+
+	msm_hs_write(uport, UARTDM_MR1_ADDR, data);
+
+	uport->ignore_status_mask = termios->c_iflag & INPCK;
+	uport->ignore_status_mask |= termios->c_iflag & IGNPAR;
+	uport->read_status_mask = (termios->c_cflag & CREAD);
+
+	msm_hs_write(uport, UARTDM_IMR_ADDR, 0);
+
+	/* Set Transmit software time out */
+	uart_update_timeout(uport, c_cflag, bps);
+
+	msm_hs_write(uport, UARTDM_CR_ADDR, RESET_RX);
+	msm_hs_write(uport, UARTDM_CR_ADDR, RESET_TX);
+
+	if (msm_uport->rx.flush == FLUSH_NONE) {
+		msm_uport->rx.flush = FLUSH_IGNORE;
+		msm_dmov_stop_cmd(msm_uport->dma_rx_channel, NULL, 1);
+	}
+
+	msm_hs_write(uport, UARTDM_IMR_ADDR, msm_uport->imr_reg);
+
+	clk_disable(msm_uport->clk);
+	spin_unlock_irqrestore(&uport->lock, flags);
+}
+
+/*
+ *  Standard API, Transmitter
+ *  Any character in the transmit shift register is sent
+ */
+static unsigned int msm_hs_tx_empty(struct uart_port *uport)
+{
+	unsigned int data;
+	unsigned int ret = 0;
+	struct msm_hs_port *msm_uport = UARTDM_TO_MSM(uport);
+
+	clk_enable(msm_uport->clk);
+
+	data = msm_hs_read(uport, UARTDM_SR_ADDR);
+	if (data & UARTDM_SR_TXEMT_BMSK)
+		ret = TIOCSER_TEMT;
+
+	clk_disable(msm_uport->clk);
+
+	return ret;
+}
+
+/*
+ *  Standard API, Stop transmitter.
+ *  Any character in the transmit shift register is sent as
+ *  well as the current data mover transfer .
+ */
+static void msm_hs_stop_tx_locked(struct uart_port *uport)
+{
+	struct msm_hs_port *msm_uport = UARTDM_TO_MSM(uport);
+
+	msm_uport->tx.tx_ready_int_en = 0;
+}
+
+/*
+ *  Standard API, Stop receiver as soon as possible.
+ *
+ *  Function immediately terminates the operation of the
+ *  channel receiver and any incoming characters are lost. None
+ *  of the receiver status bits are affected by this command and
+ *  characters that are already in the receive FIFO there.
+ */
+static void msm_hs_stop_rx_locked(struct uart_port *uport)
+{
+	struct msm_hs_port *msm_uport = UARTDM_TO_MSM(uport);
+	unsigned int data;
+
+	clk_enable(msm_uport->clk);
+
+	/* disable dlink */
+	data = msm_hs_read(uport, UARTDM_DMEN_ADDR);
+	data &= ~UARTDM_RX_DM_EN_BMSK;
+	msm_hs_write(uport, UARTDM_DMEN_ADDR, data);
+
+	/* Disable the receiver */
+	if (msm_uport->rx.flush == FLUSH_NONE)
+		msm_dmov_stop_cmd(msm_uport->dma_rx_channel, NULL, 1);
+
+	if (msm_uport->rx.flush != FLUSH_SHUTDOWN)
+		msm_uport->rx.flush = FLUSH_STOP;
+
+	clk_disable(msm_uport->clk);
+}
+
+/*  Transmit the next chunk of data */
+static void msm_hs_submit_tx_locked(struct uart_port *uport)
+{
+	int left;
+	int tx_count;
+	dma_addr_t src_addr;
+	struct msm_hs_port *msm_uport = UARTDM_TO_MSM(uport);
+	struct msm_hs_tx *tx = &msm_uport->tx;
+	struct circ_buf *tx_buf = &msm_uport->uport.state->xmit;
+
+	if (uart_circ_empty(tx_buf) || uport->state->port.tty->stopped) {
+		msm_hs_stop_tx_locked(uport);
+		return;
+	}
+
+	tx->dma_in_flight = 1;
+
+	tx_count = uart_circ_chars_pending(tx_buf);
+
+	if (UARTDM_TX_BUF_SIZE < tx_count)
+		tx_count = UARTDM_TX_BUF_SIZE;
+
+	left = UART_XMIT_SIZE - tx_buf->tail;
+
+	if (tx_count > left)
+		tx_count = left;
+
+	src_addr = tx->dma_base + tx_buf->tail;
+	dma_sync_single_for_device(uport->dev, src_addr, tx_count,
+				   DMA_TO_DEVICE);
+
+	tx->command_ptr->num_rows = (((tx_count + 15) >> 4) << 16) |
+				     ((tx_count + 15) >> 4);
+	tx->command_ptr->src_row_addr = src_addr;
+
+	dma_sync_single_for_device(uport->dev, tx->mapped_cmd_ptr,
+				   sizeof(dmov_box), DMA_TO_DEVICE);
+
+	*tx->command_ptr_ptr = CMD_PTR_LP | DMOV_CMD_ADDR(tx->mapped_cmd_ptr);
+
+	dma_sync_single_for_device(uport->dev, tx->mapped_cmd_ptr_ptr,
+				   sizeof(u32), DMA_TO_DEVICE);
+
+	/* Save tx_count to use in Callback */
+	tx->tx_count = tx_count;
+	msm_hs_write(uport, UARTDM_NCF_TX_ADDR, tx_count);
+
+	/* Disable the tx_ready interrupt */
+	msm_uport->imr_reg &= ~UARTDM_ISR_TX_READY_BMSK;
+	msm_hs_write(uport, UARTDM_IMR_ADDR, msm_uport->imr_reg);
+	msm_dmov_enqueue_cmd(msm_uport->dma_tx_channel, &tx->xfer);
+}
+
+/* Start to receive the next chunk of data */
+static void msm_hs_start_rx_locked(struct uart_port *uport)
+{
+	struct msm_hs_port *msm_uport = UARTDM_TO_MSM(uport);
+
+	msm_hs_write(uport, UARTDM_CR_ADDR, RESET_STALE_INT);
+	msm_hs_write(uport, UARTDM_DMRX_ADDR, UARTDM_RX_BUF_SIZE);
+	msm_hs_write(uport, UARTDM_CR_ADDR, STALE_EVENT_ENABLE);
+	msm_uport->imr_reg |= UARTDM_ISR_RXLEV_BMSK;
+	msm_hs_write(uport, UARTDM_IMR_ADDR, msm_uport->imr_reg);
+
+	msm_uport->rx.flush = FLUSH_NONE;
+	msm_dmov_enqueue_cmd(msm_uport->dma_rx_channel, &msm_uport->rx.xfer);
+
+	/* might have finished RX and be ready to clock off */
+	hrtimer_start(&msm_uport->clk_off_timer, msm_uport->clk_off_delay,
+			HRTIMER_MODE_REL);
+}
+
+/* Enable the transmitter Interrupt */
+static void msm_hs_start_tx_locked(struct uart_port *uport)
+{
+	struct msm_hs_port *msm_uport = UARTDM_TO_MSM(uport);
+
+	clk_enable(msm_uport->clk);
+
+	if (msm_uport->exit_lpm_cb)
+		msm_uport->exit_lpm_cb(uport);
+
+	if (msm_uport->tx.tx_ready_int_en == 0) {
+		msm_uport->tx.tx_ready_int_en = 1;
+		msm_hs_submit_tx_locked(uport);
+	}
+
+	clk_disable(msm_uport->clk);
+}
+
+/*
+ *  This routine is called when we are done with a DMA transfer
+ *
+ *  This routine is registered with Data mover when we set
+ *  up a Data Mover transfer. It is called from Data mover ISR
+ *  when the DMA transfer is done.
+ */
+static void msm_hs_dmov_tx_callback(struct msm_dmov_cmd *cmd_ptr,
+					unsigned int result,
+					struct msm_dmov_errdata *err)
+{
+	unsigned long flags;
+	struct msm_hs_port *msm_uport;
+
+	/* DMA did not finish properly */
+	WARN_ON((((result & RSLT_FIFO_CNTR_BMSK) >> 28) == 1) &&
+		!(result & RSLT_VLD));
+
+	msm_uport = container_of(cmd_ptr, struct msm_hs_port, tx.xfer);
+
+	spin_lock_irqsave(&msm_uport->uport.lock, flags);
+	clk_enable(msm_uport->clk);
+
+	msm_uport->imr_reg |= UARTDM_ISR_TX_READY_BMSK;
+	msm_hs_write(&msm_uport->uport, UARTDM_IMR_ADDR, msm_uport->imr_reg);
+
+	clk_disable(msm_uport->clk);
+	spin_unlock_irqrestore(&msm_uport->uport.lock, flags);
+}
+
+/*
+ * This routine is called when we are done with a DMA transfer or the
+ * a flush has been sent to the data mover driver.
+ *
+ * This routine is registered with Data mover when we set up a Data Mover
+ *  transfer. It is called from Data mover ISR when the DMA transfer is done.
+ */
+static void msm_hs_dmov_rx_callback(struct msm_dmov_cmd *cmd_ptr,
+					unsigned int result,
+					struct msm_dmov_errdata *err)
+{
+	int retval;
+	int rx_count;
+	unsigned long status;
+	unsigned int error_f = 0;
+	unsigned long flags;
+	unsigned int flush;
+	struct tty_struct *tty;
+	struct uart_port *uport;
+	struct msm_hs_port *msm_uport;
+
+	msm_uport = container_of(cmd_ptr, struct msm_hs_port, rx.xfer);
+	uport = &msm_uport->uport;
+
+	spin_lock_irqsave(&uport->lock, flags);
+	clk_enable(msm_uport->clk);
+
+	tty = uport->state->port.tty;
+
+	msm_hs_write(uport, UARTDM_CR_ADDR, STALE_EVENT_DISABLE);
+
+	status = msm_hs_read(uport, UARTDM_SR_ADDR);
+
+	/* overflow is not connect to data in a FIFO */
+	if (unlikely((status & UARTDM_SR_OVERRUN_BMSK) &&
+		     (uport->read_status_mask & CREAD))) {
+		tty_insert_flip_char(tty, 0, TTY_OVERRUN);
+		uport->icount.buf_overrun++;
+		error_f = 1;
+	}
+
+	if (!(uport->ignore_status_mask & INPCK))
+		status = status & ~(UARTDM_SR_PAR_FRAME_BMSK);
+
+	if (unlikely(status & UARTDM_SR_PAR_FRAME_BMSK)) {
+		/* Can not tell difference between parity & frame error */
+		uport->icount.parity++;
+		error_f = 1;
+		if (uport->ignore_status_mask & IGNPAR)
+			tty_insert_flip_char(tty, 0, TTY_PARITY);
+	}
+
+	if (error_f)
+		msm_hs_write(uport, UARTDM_CR_ADDR, RESET_ERROR_STATUS);
+
+	if (msm_uport->clk_req_off_state == CLK_REQ_OFF_FLUSH_ISSUED)
+		msm_uport->clk_req_off_state = CLK_REQ_OFF_RXSTALE_FLUSHED;
+
+	flush = msm_uport->rx.flush;
+	if (flush == FLUSH_IGNORE)
+		msm_hs_start_rx_locked(uport);
+	if (flush == FLUSH_STOP)
+		msm_uport->rx.flush = FLUSH_SHUTDOWN;
+	if (flush >= FLUSH_DATA_INVALID)
+		goto out;
+
+	rx_count = msm_hs_read(uport, UARTDM_RX_TOTAL_SNAP_ADDR);
+
+	if (0 != (uport->read_status_mask & CREAD)) {
+		retval = tty_insert_flip_string(tty, msm_uport->rx.buffer,
+						rx_count);
+		BUG_ON(retval != rx_count);
+	}
+
+	msm_hs_start_rx_locked(uport);
+
+out:
+	clk_disable(msm_uport->clk);
+
+	spin_unlock_irqrestore(&uport->lock, flags);
+
+	if (flush < FLUSH_DATA_INVALID)
+		queue_work(msm_hs_workqueue, &msm_uport->rx.tty_work);
+}
+
+static void msm_hs_tty_flip_buffer_work(struct work_struct *work)
+{
+	struct msm_hs_port *msm_uport =
+			container_of(work, struct msm_hs_port, rx.tty_work);
+	struct tty_struct *tty = msm_uport->uport.state->port.tty;
+
+	tty_flip_buffer_push(tty);
+}
+
+/*
+ *  Standard API, Current states of modem control inputs
+ *
+ * Since CTS can be handled entirely by HARDWARE we always
+ * indicate clear to send and count on the TX FIFO to block when
+ * it fills up.
+ *
+ * - TIOCM_DCD
+ * - TIOCM_CTS
+ * - TIOCM_DSR
+ * - TIOCM_RI
+ *  (Unsupported) DCD and DSR will return them high. RI will return low.
+ */
+static unsigned int msm_hs_get_mctrl_locked(struct uart_port *uport)
+{
+	return TIOCM_DSR | TIOCM_CAR | TIOCM_CTS;
+}
+
+/*
+ * True enables UART auto RFR, which indicates we are ready for data if the RX
+ * buffer is not full. False disables auto RFR, and deasserts RFR to indicate
+ * we are not ready for data. Must be called with UART clock on.
+ */
+static void set_rfr_locked(struct uart_port *uport, int auto_rfr)
+{
+	unsigned int data;
+
+	data = msm_hs_read(uport, UARTDM_MR1_ADDR);
+
+	if (auto_rfr) {
+		/* enable auto ready-for-receiving */
+		data |= UARTDM_MR1_RX_RDY_CTL_BMSK;
+		msm_hs_write(uport, UARTDM_MR1_ADDR, data);
+	} else {
+		/* disable auto ready-for-receiving */
+		data &= ~UARTDM_MR1_RX_RDY_CTL_BMSK;
+		msm_hs_write(uport, UARTDM_MR1_ADDR, data);
+		/* RFR is active low, set high */
+		msm_hs_write(uport, UARTDM_CR_ADDR, RFR_HIGH);
+	}
+}
+
+/*
+ *  Standard API, used to set or clear RFR
+ */
+static void msm_hs_set_mctrl_locked(struct uart_port *uport,
+				    unsigned int mctrl)
+{
+	unsigned int auto_rfr;
+	struct msm_hs_port *msm_uport = UARTDM_TO_MSM(uport);
+
+	clk_enable(msm_uport->clk);
+
+	auto_rfr = TIOCM_RTS & mctrl ? 1 : 0;
+	set_rfr_locked(uport, auto_rfr);
+
+	clk_disable(msm_uport->clk);
+}
+
+/* Standard API, Enable modem status (CTS) interrupt  */
+static void msm_hs_enable_ms_locked(struct uart_port *uport)
+{
+	struct msm_hs_port *msm_uport = UARTDM_TO_MSM(uport);
+
+	clk_enable(msm_uport->clk);
+
+	/* Enable DELTA_CTS Interrupt */
+	msm_uport->imr_reg |= UARTDM_ISR_DELTA_CTS_BMSK;
+	msm_hs_write(uport, UARTDM_IMR_ADDR, msm_uport->imr_reg);
+
+	clk_disable(msm_uport->clk);
+
+}
+
+/*
+ *  Standard API, Break Signal
+ *
+ * Control the transmission of a break signal. ctl eq 0 => break
+ * signal terminate ctl ne 0 => start break signal
+ */
+static void msm_hs_break_ctl(struct uart_port *uport, int ctl)
+{
+	struct msm_hs_port *msm_uport = UARTDM_TO_MSM(uport);
+
+	clk_enable(msm_uport->clk);
+	msm_hs_write(uport, UARTDM_CR_ADDR, ctl ? START_BREAK : STOP_BREAK);
+	clk_disable(msm_uport->clk);
+}
+
+static void msm_hs_config_port(struct uart_port *uport, int cfg_flags)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&uport->lock, flags);
+	if (cfg_flags & UART_CONFIG_TYPE) {
+		uport->type = PORT_MSM;
+		msm_hs_request_port(uport);
+	}
+	spin_unlock_irqrestore(&uport->lock, flags);
+}
+
+/*  Handle CTS changes (Called from interrupt handler) */
+static void msm_hs_handle_delta_cts_locked(struct uart_port *uport)
+{
+	struct msm_hs_port *msm_uport = UARTDM_TO_MSM(uport);
+
+	clk_enable(msm_uport->clk);
+
+	/* clear interrupt */
+	msm_hs_write(uport, UARTDM_CR_ADDR, RESET_CTS);
+	uport->icount.cts++;
+
+	clk_disable(msm_uport->clk);
+
+	/* clear the IOCTL TIOCMIWAIT if called */
+	wake_up_interruptible(&uport->state->port.delta_msr_wait);
+}
+
+/* check if the TX path is flushed, and if so clock off
+ * returns 0 did not clock off, need to retry (still sending final byte)
+ *        -1 did not clock off, do not retry
+ *         1 if we clocked off
+ */
+static int msm_hs_check_clock_off_locked(struct uart_port *uport)
+{
+	unsigned long sr_status;
+	struct msm_hs_port *msm_uport = UARTDM_TO_MSM(uport);
+	struct circ_buf *tx_buf = &uport->state->xmit;
+
+	/* Cancel if tx tty buffer is not empty, dma is in flight,
+	 * or tx fifo is not empty, or rx fifo is not empty */
+	if (msm_uport->clk_state != MSM_HS_CLK_REQUEST_OFF ||
+	    !uart_circ_empty(tx_buf) || msm_uport->tx.dma_in_flight ||
+	    (msm_uport->imr_reg & UARTDM_ISR_TXLEV_BMSK) ||
+	    !(msm_uport->imr_reg & UARTDM_ISR_RXLEV_BMSK))  {
+		return -1;
+	}
+
+	/* Make sure the uart is finished with the last byte */
+	sr_status = msm_hs_read(uport, UARTDM_SR_ADDR);
+	if (!(sr_status & UARTDM_SR_TXEMT_BMSK))
+		return 0;  /* retry */
+
+	/* Make sure forced RXSTALE flush complete */
+	switch (msm_uport->clk_req_off_state) {
+	case CLK_REQ_OFF_START:
+		msm_uport->clk_req_off_state = CLK_REQ_OFF_RXSTALE_ISSUED;
+		msm_hs_write(uport, UARTDM_CR_ADDR, FORCE_STALE_EVENT);
+		return 0;  /* RXSTALE flush not complete - retry */
+	case CLK_REQ_OFF_RXSTALE_ISSUED:
+	case CLK_REQ_OFF_FLUSH_ISSUED:
+		return 0;  /* RXSTALE flush not complete - retry */
+	case CLK_REQ_OFF_RXSTALE_FLUSHED:
+		break;  /* continue */
+	}
+
+	if (msm_uport->rx.flush != FLUSH_SHUTDOWN) {
+		if (msm_uport->rx.flush == FLUSH_NONE)
+			msm_hs_stop_rx_locked(uport);
+		return 0;  /* come back later to really clock off */
+	}
+
+	/* we really want to clock off */
+	clk_disable(msm_uport->clk);
+	msm_uport->clk_state = MSM_HS_CLK_OFF;
+
+	if (use_low_power_rx_wakeup(msm_uport)) {
+		msm_uport->rx_wakeup.ignore = 1;
+		enable_irq(msm_uport->rx_wakeup.irq);
+	}
+	return 1;
+}
+
+static enum hrtimer_restart msm_hs_clk_off_retry(struct hrtimer *timer)
+{
+	unsigned long flags;
+	int ret = HRTIMER_NORESTART;
+	struct msm_hs_port *msm_uport = container_of(timer, struct msm_hs_port,
+						     clk_off_timer);
+	struct uart_port *uport = &msm_uport->uport;
+
+	spin_lock_irqsave(&uport->lock, flags);
+
+	if (!msm_hs_check_clock_off_locked(uport)) {
+		hrtimer_forward_now(timer, msm_uport->clk_off_delay);
+		ret = HRTIMER_RESTART;
+	}
+
+	spin_unlock_irqrestore(&uport->lock, flags);
+
+	return ret;
+}
+
+static irqreturn_t msm_hs_isr(int irq, void *dev)
+{
+	unsigned long flags;
+	unsigned long isr_status;
+	struct msm_hs_port *msm_uport = dev;
+	struct uart_port *uport = &msm_uport->uport;
+	struct circ_buf *tx_buf = &uport->state->xmit;
+	struct msm_hs_tx *tx = &msm_uport->tx;
+	struct msm_hs_rx *rx = &msm_uport->rx;
+
+	spin_lock_irqsave(&uport->lock, flags);
+
+	isr_status = msm_hs_read(uport, UARTDM_MISR_ADDR);
+
+	/* Uart RX starting */
+	if (isr_status & UARTDM_ISR_RXLEV_BMSK) {
+		msm_uport->imr_reg &= ~UARTDM_ISR_RXLEV_BMSK;
+		msm_hs_write(uport, UARTDM_IMR_ADDR, msm_uport->imr_reg);
+	}
+	/* Stale rx interrupt */
+	if (isr_status & UARTDM_ISR_RXSTALE_BMSK) {
+		msm_hs_write(uport, UARTDM_CR_ADDR, STALE_EVENT_DISABLE);
+		msm_hs_write(uport, UARTDM_CR_ADDR, RESET_STALE_INT);
+
+		if (msm_uport->clk_req_off_state == CLK_REQ_OFF_RXSTALE_ISSUED)
+			msm_uport->clk_req_off_state =
+					CLK_REQ_OFF_FLUSH_ISSUED;
+		if (rx->flush == FLUSH_NONE) {
+			rx->flush = FLUSH_DATA_READY;
+			msm_dmov_stop_cmd(msm_uport->dma_rx_channel, NULL, 1);
+		}
+	}
+	/* tx ready interrupt */
+	if (isr_status & UARTDM_ISR_TX_READY_BMSK) {
+		/* Clear  TX Ready */
+		msm_hs_write(uport, UARTDM_CR_ADDR, CLEAR_TX_READY);
+
+		if (msm_uport->clk_state == MSM_HS_CLK_REQUEST_OFF) {
+			msm_uport->imr_reg |= UARTDM_ISR_TXLEV_BMSK;
+			msm_hs_write(uport, UARTDM_IMR_ADDR,
+				     msm_uport->imr_reg);
+		}
+
+		/* Complete DMA TX transactions and submit new transactions */
+		tx_buf->tail = (tx_buf->tail + tx->tx_count) & ~UART_XMIT_SIZE;
+
+		tx->dma_in_flight = 0;
+
+		uport->icount.tx += tx->tx_count;
+		if (tx->tx_ready_int_en)
+			msm_hs_submit_tx_locked(uport);
+
+		if (uart_circ_chars_pending(tx_buf) < WAKEUP_CHARS)
+			uart_write_wakeup(uport);
+	}
+	if (isr_status & UARTDM_ISR_TXLEV_BMSK) {
+		/* TX FIFO is empty */
+		msm_uport->imr_reg &= ~UARTDM_ISR_TXLEV_BMSK;
+		msm_hs_write(uport, UARTDM_IMR_ADDR, msm_uport->imr_reg);
+		if (!msm_hs_check_clock_off_locked(uport))
+			hrtimer_start(&msm_uport->clk_off_timer,
+				      msm_uport->clk_off_delay,
+				      HRTIMER_MODE_REL);
+	}
+
+	/* Change in CTS interrupt */
+	if (isr_status & UARTDM_ISR_DELTA_CTS_BMSK)
+		msm_hs_handle_delta_cts_locked(uport);
+
+	spin_unlock_irqrestore(&uport->lock, flags);
+
+	return IRQ_HANDLED;
+}
+
+void msm_hs_request_clock_off_locked(struct uart_port *uport)
+{
+	struct msm_hs_port *msm_uport = UARTDM_TO_MSM(uport);
+
+	if (msm_uport->clk_state == MSM_HS_CLK_ON) {
+		msm_uport->clk_state = MSM_HS_CLK_REQUEST_OFF;
+		msm_uport->clk_req_off_state = CLK_REQ_OFF_START;
+		if (!use_low_power_rx_wakeup(msm_uport))
+			set_rfr_locked(uport, 0);
+		msm_uport->imr_reg |= UARTDM_ISR_TXLEV_BMSK;
+		msm_hs_write(uport, UARTDM_IMR_ADDR, msm_uport->imr_reg);
+	}
+}
+
+/**
+ * msm_hs_request_clock_off - request to (i.e. asynchronously) turn off uart
+ * clock once pending TX is flushed and Rx DMA command is terminated.
+ * @uport: uart_port structure for the device instance.
+ *
+ * This functions puts the device into a partially active low power mode. It
+ * waits to complete all pending tx transactions, flushes ongoing Rx DMA
+ * command and terminates UART side Rx transaction, puts UART HW in non DMA
+ * mode and then clocks off the device. A client calls this when no UART
+ * data is expected. msm_request_clock_on() must be called before any further
+ * UART can be sent or received.
+ */
+void msm_hs_request_clock_off(struct uart_port *uport)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&uport->lock, flags);
+	msm_hs_request_clock_off_locked(uport);
+	spin_unlock_irqrestore(&uport->lock, flags);
+}
+
+void msm_hs_request_clock_on_locked(struct uart_port *uport)
+{
+	struct msm_hs_port *msm_uport = UARTDM_TO_MSM(uport);
+	unsigned int data;
+
+	switch (msm_uport->clk_state) {
+	case MSM_HS_CLK_OFF:
+		clk_enable(msm_uport->clk);
+		disable_irq_nosync(msm_uport->rx_wakeup.irq);
+		/* fall-through */
+	case MSM_HS_CLK_REQUEST_OFF:
+		if (msm_uport->rx.flush == FLUSH_STOP ||
+		    msm_uport->rx.flush == FLUSH_SHUTDOWN) {
+			msm_hs_write(uport, UARTDM_CR_ADDR, RESET_RX);
+			data = msm_hs_read(uport, UARTDM_DMEN_ADDR);
+			data |= UARTDM_RX_DM_EN_BMSK;
+			msm_hs_write(uport, UARTDM_DMEN_ADDR, data);
+		}
+		hrtimer_try_to_cancel(&msm_uport->clk_off_timer);
+		if (msm_uport->rx.flush == FLUSH_SHUTDOWN)
+			msm_hs_start_rx_locked(uport);
+		if (!use_low_power_rx_wakeup(msm_uport))
+			set_rfr_locked(uport, 1);
+		if (msm_uport->rx.flush == FLUSH_STOP)
+			msm_uport->rx.flush = FLUSH_IGNORE;
+		msm_uport->clk_state = MSM_HS_CLK_ON;
+		break;
+	case MSM_HS_CLK_ON:
+		break;
+	case MSM_HS_CLK_PORT_OFF:
+		break;
+	}
+}
+
+/**
+ * msm_hs_request_clock_on - Switch the device from partially active low
+ * power mode to fully active (i.e. clock on) mode.
+ * @uport: uart_port structure for the device.
+ *
+ * This function switches on the input clock, puts UART HW into DMA mode
+ * and enqueues an Rx DMA command if the device was in partially active
+ * mode. It has no effect if called with the device in inactive state.
+ */
+void msm_hs_request_clock_on(struct uart_port *uport)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&uport->lock, flags);
+	msm_hs_request_clock_on_locked(uport);
+	spin_unlock_irqrestore(&uport->lock, flags);
+}
+
+static irqreturn_t msm_hs_rx_wakeup_isr(int irq, void *dev)
+{
+	unsigned int wakeup = 0;
+	unsigned long flags;
+	struct msm_hs_port *msm_uport = dev;
+	struct uart_port *uport = &msm_uport->uport;
+	struct tty_struct *tty = NULL;
+
+	spin_lock_irqsave(&uport->lock, flags);
+	if (msm_uport->clk_state == MSM_HS_CLK_OFF) {
+		/* ignore the first irq - it is a pending irq that occurred
+		 * before enable_irq() */
+		if (msm_uport->rx_wakeup.ignore)
+			msm_uport->rx_wakeup.ignore = 0;
+		else
+			wakeup = 1;
+	}
+
+	if (wakeup) {
+		/* the uart was clocked off during an rx, wake up and
+		 * optionally inject char into tty rx */
+		msm_hs_request_clock_on_locked(uport);
+		if (msm_uport->rx_wakeup.inject_rx) {
+			tty = uport->state->port.tty;
+			tty_insert_flip_char(tty,
+					     msm_uport->rx_wakeup.rx_to_inject,
+					     TTY_NORMAL);
+			queue_work(msm_hs_workqueue, &msm_uport->rx.tty_work);
+		}
+	}
+
+	spin_unlock_irqrestore(&uport->lock, flags);
+
+	return IRQ_HANDLED;
+}
+
+static const char *msm_hs_type(struct uart_port *port)
+{
+	return (port->type == PORT_MSM) ? "MSM_HS_UART" : NULL;
+}
+
+/* Called when port is opened */
+static int msm_hs_startup(struct uart_port *uport)
+{
+	int ret;
+	int rfr_level;
+	unsigned long flags;
+	unsigned int data;
+	struct msm_hs_port *msm_uport = UARTDM_TO_MSM(uport);
+	struct circ_buf *tx_buf = &uport->state->xmit;
+	struct msm_hs_tx *tx = &msm_uport->tx;
+	struct msm_hs_rx *rx = &msm_uport->rx;
+
+	rfr_level = uport->fifosize;
+	if (rfr_level > 16)
+		rfr_level -= 16;
+
+	tx->dma_base = dma_map_single(uport->dev, tx_buf->buf, UART_XMIT_SIZE,
+				      DMA_TO_DEVICE);
+
+	/* do not let tty layer execute RX in global workqueue, use a
+	 * dedicated workqueue managed by this driver */
+	uport->state->port.tty->low_latency = 1;
+
+	/* turn on uart clk */
+	ret = msm_hs_init_clk_locked(uport);
+	if (unlikely(ret)) {
+		printk(KERN_ERR "Turning uartclk failed!\n");
+		goto err_msm_hs_init_clk;
+	}
+
+	/* Set auto RFR Level */
+	data = msm_hs_read(uport, UARTDM_MR1_ADDR);
+	data &= ~UARTDM_MR1_AUTO_RFR_LEVEL1_BMSK;
+	data &= ~UARTDM_MR1_AUTO_RFR_LEVEL0_BMSK;
+	data |= (UARTDM_MR1_AUTO_RFR_LEVEL1_BMSK & (rfr_level << 2));
+	data |= (UARTDM_MR1_AUTO_RFR_LEVEL0_BMSK & rfr_level);
+	msm_hs_write(uport, UARTDM_MR1_ADDR, data);
+
+	/* Make sure RXSTALE count is non-zero */
+	data = msm_hs_read(uport, UARTDM_IPR_ADDR);
+	if (!data) {
+		data |= 0x1f & UARTDM_IPR_STALE_LSB_BMSK;
+		msm_hs_write(uport, UARTDM_IPR_ADDR, data);
+	}
+
+	/* Enable Data Mover Mode */
+	data = UARTDM_TX_DM_EN_BMSK | UARTDM_RX_DM_EN_BMSK;
+	msm_hs_write(uport, UARTDM_DMEN_ADDR, data);
+
+	/* Reset TX */
+	msm_hs_write(uport, UARTDM_CR_ADDR, RESET_TX);
+	msm_hs_write(uport, UARTDM_CR_ADDR, RESET_RX);
+	msm_hs_write(uport, UARTDM_CR_ADDR, RESET_ERROR_STATUS);
+	msm_hs_write(uport, UARTDM_CR_ADDR, RESET_BREAK_INT);
+	msm_hs_write(uport, UARTDM_CR_ADDR, RESET_STALE_INT);
+	msm_hs_write(uport, UARTDM_CR_ADDR, RESET_CTS);
+	msm_hs_write(uport, UARTDM_CR_ADDR, RFR_LOW);
+	/* Turn on Uart Receiver */
+	msm_hs_write(uport, UARTDM_CR_ADDR, UARTDM_CR_RX_EN_BMSK);
+
+	/* Turn on Uart Transmitter */
+	msm_hs_write(uport, UARTDM_CR_ADDR, UARTDM_CR_TX_EN_BMSK);
+
+	/* Initialize the tx */
+	tx->tx_ready_int_en = 0;
+	tx->dma_in_flight = 0;
+
+	tx->xfer.complete_func = msm_hs_dmov_tx_callback;
+	tx->xfer.execute_func = NULL;
+
+	tx->command_ptr->cmd = CMD_LC |
+	    CMD_DST_CRCI(msm_uport->dma_tx_crci) | CMD_MODE_BOX;
+
+	tx->command_ptr->src_dst_len = (MSM_UARTDM_BURST_SIZE << 16)
+					   | (MSM_UARTDM_BURST_SIZE);
+
+	tx->command_ptr->row_offset = (MSM_UARTDM_BURST_SIZE << 16);
+
+	tx->command_ptr->dst_row_addr =
+	    msm_uport->uport.mapbase + UARTDM_TF_ADDR;
+
+
+	/* Turn on Uart Receive */
+	rx->xfer.complete_func = msm_hs_dmov_rx_callback;
+	rx->xfer.execute_func = NULL;
+
+	rx->command_ptr->cmd = CMD_LC |
+	    CMD_SRC_CRCI(msm_uport->dma_rx_crci) | CMD_MODE_BOX;
+
+	rx->command_ptr->src_dst_len = (MSM_UARTDM_BURST_SIZE << 16)
+					   | (MSM_UARTDM_BURST_SIZE);
+	rx->command_ptr->row_offset =  MSM_UARTDM_BURST_SIZE;
+	rx->command_ptr->src_row_addr = uport->mapbase + UARTDM_RF_ADDR;
+
+
+	msm_uport->imr_reg |= UARTDM_ISR_RXSTALE_BMSK;
+	/* Enable reading the current CTS, no harm even if CTS is ignored */
+	msm_uport->imr_reg |= UARTDM_ISR_CURRENT_CTS_BMSK;
+
+	msm_hs_write(uport, UARTDM_TFWR_ADDR, 0);  /* TXLEV on empty TX fifo */
+
+
+	ret = request_irq(uport->irq, msm_hs_isr, IRQF_TRIGGER_HIGH,
+			  "msm_hs_uart", msm_uport);
+	if (unlikely(ret)) {
+		printk(KERN_ERR "Request msm_hs_uart IRQ failed!\n");
+		goto err_request_irq;
+	}
+	if (use_low_power_rx_wakeup(msm_uport)) {
+		ret = request_irq(msm_uport->rx_wakeup.irq,
+				  msm_hs_rx_wakeup_isr,
+				  IRQF_TRIGGER_FALLING,
+				  "msm_hs_rx_wakeup", msm_uport);
+		if (unlikely(ret)) {
+			printk(KERN_ERR "Request msm_hs_rx_wakeup IRQ failed!\n");
+			free_irq(uport->irq, msm_uport);
+			goto err_request_irq;
+		}
+		disable_irq(msm_uport->rx_wakeup.irq);
+	}
+
+	spin_lock_irqsave(&uport->lock, flags);
+
+	msm_hs_write(uport, UARTDM_RFWR_ADDR, 0);
+	msm_hs_start_rx_locked(uport);
+
+	spin_unlock_irqrestore(&uport->lock, flags);
+	ret = pm_runtime_set_active(uport->dev);
+	if (ret)
+		dev_err(uport->dev, "set active error:%d\n", ret);
+	pm_runtime_enable(uport->dev);
+
+	return 0;
+
+err_request_irq:
+err_msm_hs_init_clk:
+	dma_unmap_single(uport->dev, tx->dma_base,
+				UART_XMIT_SIZE, DMA_TO_DEVICE);
+	return ret;
+}
+
+/* Initialize tx and rx data structures */
+static int __devinit uartdm_init_port(struct uart_port *uport)
+{
+	int ret = 0;
+	struct msm_hs_port *msm_uport = UARTDM_TO_MSM(uport);
+	struct msm_hs_tx *tx = &msm_uport->tx;
+	struct msm_hs_rx *rx = &msm_uport->rx;
+
+	/* Allocate the command pointer. Needs to be 64 bit aligned */
+	tx->command_ptr = kmalloc(sizeof(dmov_box), GFP_KERNEL | __GFP_DMA);
+	if (!tx->command_ptr)
+		return -ENOMEM;
+
+	tx->command_ptr_ptr = kmalloc(sizeof(u32), GFP_KERNEL | __GFP_DMA);
+	if (!tx->command_ptr_ptr) {
+		ret = -ENOMEM;
+		goto err_tx_command_ptr_ptr;
+	}
+
+	tx->mapped_cmd_ptr = dma_map_single(uport->dev, tx->command_ptr,
+					    sizeof(dmov_box), DMA_TO_DEVICE);
+	tx->mapped_cmd_ptr_ptr = dma_map_single(uport->dev,
+						tx->command_ptr_ptr,
+						sizeof(u32), DMA_TO_DEVICE);
+	tx->xfer.cmdptr = DMOV_CMD_ADDR(tx->mapped_cmd_ptr_ptr);
+
+	init_waitqueue_head(&rx->wait);
+
+	rx->pool = dma_pool_create("rx_buffer_pool", uport->dev,
+				   UARTDM_RX_BUF_SIZE, 16, 0);
+	if (!rx->pool) {
+		pr_err("%s(): cannot allocate rx_buffer_pool", __func__);
+		ret = -ENOMEM;
+		goto err_dma_pool_create;
+	}
+
+	rx->buffer = dma_pool_alloc(rx->pool, GFP_KERNEL, &rx->rbuffer);
+	if (!rx->buffer) {
+		pr_err("%s(): cannot allocate rx->buffer", __func__);
+		ret = -ENOMEM;
+		goto err_dma_pool_alloc;
+	}
+
+	/* Allocate the command pointer. Needs to be 64 bit aligned */
+	rx->command_ptr = kmalloc(sizeof(dmov_box), GFP_KERNEL | __GFP_DMA);
+	if (!rx->command_ptr) {
+		pr_err("%s(): cannot allocate rx->command_ptr", __func__);
+		ret = -ENOMEM;
+		goto err_rx_command_ptr;
+	}
+
+	rx->command_ptr_ptr = kmalloc(sizeof(u32), GFP_KERNEL | __GFP_DMA);
+	if (!rx->command_ptr_ptr) {
+		pr_err("%s(): cannot allocate rx->command_ptr_ptr", __func__);
+		ret = -ENOMEM;
+		goto err_rx_command_ptr_ptr;
+	}
+
+	rx->command_ptr->num_rows = ((UARTDM_RX_BUF_SIZE >> 4) << 16) |
+					 (UARTDM_RX_BUF_SIZE >> 4);
+
+	rx->command_ptr->dst_row_addr = rx->rbuffer;
+
+	rx->mapped_cmd_ptr = dma_map_single(uport->dev, rx->command_ptr,
+					    sizeof(dmov_box), DMA_TO_DEVICE);
+
+	*rx->command_ptr_ptr = CMD_PTR_LP | DMOV_CMD_ADDR(rx->mapped_cmd_ptr);
+
+	rx->cmdptr_dmaaddr = dma_map_single(uport->dev, rx->command_ptr_ptr,
+					    sizeof(u32), DMA_TO_DEVICE);
+	rx->xfer.cmdptr = DMOV_CMD_ADDR(rx->cmdptr_dmaaddr);
+
+	INIT_WORK(&rx->tty_work, msm_hs_tty_flip_buffer_work);
+
+	return ret;
+
+err_rx_command_ptr_ptr:
+	kfree(rx->command_ptr);
+err_rx_command_ptr:
+	dma_pool_free(msm_uport->rx.pool, msm_uport->rx.buffer,
+						msm_uport->rx.rbuffer);
+err_dma_pool_alloc:
+	dma_pool_destroy(msm_uport->rx.pool);
+err_dma_pool_create:
+	dma_unmap_single(uport->dev, msm_uport->tx.mapped_cmd_ptr_ptr,
+				sizeof(u32), DMA_TO_DEVICE);
+	dma_unmap_single(uport->dev, msm_uport->tx.mapped_cmd_ptr,
+				sizeof(dmov_box), DMA_TO_DEVICE);
+	kfree(msm_uport->tx.command_ptr_ptr);
+err_tx_command_ptr_ptr:
+	kfree(msm_uport->tx.command_ptr);
+	return ret;
+}
+
+static int __devinit msm_hs_probe(struct platform_device *pdev)
+{
+	int ret;
+	struct uart_port *uport;
+	struct msm_hs_port *msm_uport;
+	struct resource *resource;
+	const struct msm_serial_hs_platform_data *pdata =
+						pdev->dev.platform_data;
+
+	if (pdev->id < 0 || pdev->id >= UARTDM_NR) {
+		printk(KERN_ERR "Invalid plaform device ID = %d\n", pdev->id);
+		return -EINVAL;
+	}
+
+	msm_uport = &q_uart_port[pdev->id];
+	uport = &msm_uport->uport;
+
+	uport->dev = &pdev->dev;
+
+	resource = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (unlikely(!resource))
+		return -ENXIO;
+
+	uport->mapbase = resource->start;
+	uport->irq = platform_get_irq(pdev, 0);
+	if (unlikely(uport->irq < 0))
+		return -ENXIO;
+
+	if (unlikely(irq_set_irq_wake(uport->irq, 1)))
+		return -ENXIO;
+
+	if (pdata == NULL || pdata->rx_wakeup_irq < 0)
+		msm_uport->rx_wakeup.irq = -1;
+	else {
+		msm_uport->rx_wakeup.irq = pdata->rx_wakeup_irq;
+		msm_uport->rx_wakeup.ignore = 1;
+		msm_uport->rx_wakeup.inject_rx = pdata->inject_rx_on_wakeup;
+		msm_uport->rx_wakeup.rx_to_inject = pdata->rx_to_inject;
+
+		if (unlikely(msm_uport->rx_wakeup.irq < 0))
+			return -ENXIO;
+
+		if (unlikely(irq_set_irq_wake(msm_uport->rx_wakeup.irq, 1)))
+			return -ENXIO;
+	}
+
+	if (pdata == NULL)
+		msm_uport->exit_lpm_cb = NULL;
+	else
+		msm_uport->exit_lpm_cb = pdata->exit_lpm_cb;
+
+	resource = platform_get_resource_byname(pdev, IORESOURCE_DMA,
+						"uartdm_channels");
+	if (unlikely(!resource))
+		return -ENXIO;
+
+	msm_uport->dma_tx_channel = resource->start;
+	msm_uport->dma_rx_channel = resource->end;
+
+	resource = platform_get_resource_byname(pdev, IORESOURCE_DMA,
+						"uartdm_crci");
+	if (unlikely(!resource))
+		return -ENXIO;
+
+	msm_uport->dma_tx_crci = resource->start;
+	msm_uport->dma_rx_crci = resource->end;
+
+	uport->iotype = UPIO_MEM;
+	uport->fifosize = UART_FIFOSIZE;
+	uport->ops = &msm_hs_ops;
+	uport->flags = UPF_BOOT_AUTOCONF;
+	uport->uartclk = UARTCLK;
+	msm_uport->imr_reg = 0x0;
+	msm_uport->clk = clk_get(&pdev->dev, "uartdm_clk");
+	if (IS_ERR(msm_uport->clk))
+		return PTR_ERR(msm_uport->clk);
+
+	ret = uartdm_init_port(uport);
+	if (unlikely(ret))
+		return ret;
+
+	msm_uport->clk_state = MSM_HS_CLK_PORT_OFF;
+	hrtimer_init(&msm_uport->clk_off_timer, CLOCK_MONOTONIC,
+		     HRTIMER_MODE_REL);
+	msm_uport->clk_off_timer.function = msm_hs_clk_off_retry;
+	msm_uport->clk_off_delay = ktime_set(0, 1000000);  /* 1ms */
+
+	uport->line = pdev->id;
+	return uart_add_one_port(&msm_hs_driver, uport);
+}
+
+static int __init msm_serial_hs_init(void)
+{
+	int ret, i;
+
+	/* Init all UARTS as non-configured */
+	for (i = 0; i < UARTDM_NR; i++)
+		q_uart_port[i].uport.type = PORT_UNKNOWN;
+
+	msm_hs_workqueue = create_singlethread_workqueue("msm_serial_hs");
+	if (unlikely(!msm_hs_workqueue))
+		return -ENOMEM;
+
+	ret = uart_register_driver(&msm_hs_driver);
+	if (unlikely(ret)) {
+		printk(KERN_ERR "%s failed to load\n", __func__);
+		goto err_uart_register_driver;
+	}
+
+	ret = platform_driver_register(&msm_serial_hs_platform_driver);
+	if (ret) {
+		printk(KERN_ERR "%s failed to load\n", __func__);
+		goto err_platform_driver_register;
+	}
+
+	return ret;
+
+err_platform_driver_register:
+	uart_unregister_driver(&msm_hs_driver);
+err_uart_register_driver:
+	destroy_workqueue(msm_hs_workqueue);
+	return ret;
+}
+module_init(msm_serial_hs_init);
+
+/*
+ *  Called by the upper layer when port is closed.
+ *     - Disables the port
+ *     - Unhook the ISR
+ */
+static void msm_hs_shutdown(struct uart_port *uport)
+{
+	unsigned long flags;
+	struct msm_hs_port *msm_uport = UARTDM_TO_MSM(uport);
+
+	BUG_ON(msm_uport->rx.flush < FLUSH_STOP);
+
+	spin_lock_irqsave(&uport->lock, flags);
+	clk_enable(msm_uport->clk);
+
+	/* Disable the transmitter */
+	msm_hs_write(uport, UARTDM_CR_ADDR, UARTDM_CR_TX_DISABLE_BMSK);
+	/* Disable the receiver */
+	msm_hs_write(uport, UARTDM_CR_ADDR, UARTDM_CR_RX_DISABLE_BMSK);
+
+	pm_runtime_disable(uport->dev);
+	pm_runtime_set_suspended(uport->dev);
+
+	/* Free the interrupt */
+	free_irq(uport->irq, msm_uport);
+	if (use_low_power_rx_wakeup(msm_uport))
+		free_irq(msm_uport->rx_wakeup.irq, msm_uport);
+
+	msm_uport->imr_reg = 0;
+	msm_hs_write(uport, UARTDM_IMR_ADDR, msm_uport->imr_reg);
+
+	wait_event(msm_uport->rx.wait, msm_uport->rx.flush == FLUSH_SHUTDOWN);
+
+	clk_disable(msm_uport->clk);  /* to balance local clk_enable() */
+	if (msm_uport->clk_state != MSM_HS_CLK_OFF)
+		clk_disable(msm_uport->clk);  /* to balance clk_state */
+	msm_uport->clk_state = MSM_HS_CLK_PORT_OFF;
+
+	dma_unmap_single(uport->dev, msm_uport->tx.dma_base,
+			 UART_XMIT_SIZE, DMA_TO_DEVICE);
+
+	spin_unlock_irqrestore(&uport->lock, flags);
+
+	if (cancel_work_sync(&msm_uport->rx.tty_work))
+		msm_hs_tty_flip_buffer_work(&msm_uport->rx.tty_work);
+}
+
+static void __exit msm_serial_hs_exit(void)
+{
+	flush_workqueue(msm_hs_workqueue);
+	destroy_workqueue(msm_hs_workqueue);
+	platform_driver_unregister(&msm_serial_hs_platform_driver);
+	uart_unregister_driver(&msm_hs_driver);
+}
+module_exit(msm_serial_hs_exit);
+
+#ifdef CONFIG_PM_RUNTIME
+static int msm_hs_runtime_idle(struct device *dev)
+{
+	/*
+	 * returning success from idle results in runtime suspend to be
+	 * called
+	 */
+	return 0;
+}
+
+static int msm_hs_runtime_resume(struct device *dev)
+{
+	struct platform_device *pdev = container_of(dev, struct
+						    platform_device, dev);
+	struct msm_hs_port *msm_uport = &q_uart_port[pdev->id];
+
+	msm_hs_request_clock_on(&msm_uport->uport);
+	return 0;
+}
+
+static int msm_hs_runtime_suspend(struct device *dev)
+{
+	struct platform_device *pdev = container_of(dev, struct
+						    platform_device, dev);
+	struct msm_hs_port *msm_uport = &q_uart_port[pdev->id];
+
+	msm_hs_request_clock_off(&msm_uport->uport);
+	return 0;
+}
+#else
+#define msm_hs_runtime_idle NULL
+#define msm_hs_runtime_resume NULL
+#define msm_hs_runtime_suspend NULL
+#endif
+
+static const struct dev_pm_ops msm_hs_dev_pm_ops = {
+	.runtime_suspend = msm_hs_runtime_suspend,
+	.runtime_resume  = msm_hs_runtime_resume,
+	.runtime_idle    = msm_hs_runtime_idle,
+};
+
+static struct platform_driver msm_serial_hs_platform_driver = {
+	.probe = msm_hs_probe,
+	.remove = __devexit_p(msm_hs_remove),
+	.driver = {
+		.name = "msm_serial_hs",
+		.owner = THIS_MODULE,
+		.pm   = &msm_hs_dev_pm_ops,
+	},
+};
+
+static struct uart_driver msm_hs_driver = {
+	.owner = THIS_MODULE,
+	.driver_name = "msm_serial_hs",
+	.dev_name = "ttyHS",
+	.nr = UARTDM_NR,
+	.cons = 0,
+};
+
+static struct uart_ops msm_hs_ops = {
+	.tx_empty = msm_hs_tx_empty,
+	.set_mctrl = msm_hs_set_mctrl_locked,
+	.get_mctrl = msm_hs_get_mctrl_locked,
+	.stop_tx = msm_hs_stop_tx_locked,
+	.start_tx = msm_hs_start_tx_locked,
+	.stop_rx = msm_hs_stop_rx_locked,
+	.enable_ms = msm_hs_enable_ms_locked,
+	.break_ctl = msm_hs_break_ctl,
+	.startup = msm_hs_startup,
+	.shutdown = msm_hs_shutdown,
+	.set_termios = msm_hs_set_termios,
+	.pm = msm_hs_pm,
+	.type = msm_hs_type,
+	.config_port = msm_hs_config_port,
+	.release_port = msm_hs_release_port,
+	.request_port = msm_hs_request_port,
+};
+
+MODULE_DESCRIPTION("High Speed UART Driver for the MSM chipset");
+MODULE_VERSION("1.2");
+MODULE_LICENSE("GPL v2");
diff --git a/ap/os/linux/linux-3.4.x/drivers/tty/serial/msm_smd_tty.c b/ap/os/linux/linux-3.4.x/drivers/tty/serial/msm_smd_tty.c
new file mode 100644
index 0000000..b25e6ee
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/tty/serial/msm_smd_tty.c
@@ -0,0 +1,234 @@
+/*
+ * Copyright (C) 2007 Google, Inc.
+ * Copyright (c) 2011, Code Aurora Forum. All rights reserved.
+ * Author: Brian Swetland <swetland@google.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/fs.h>
+#include <linux/cdev.h>
+#include <linux/device.h>
+#include <linux/wait.h>
+
+#include <linux/tty.h>
+#include <linux/tty_driver.h>
+#include <linux/tty_flip.h>
+
+#include <mach/msm_smd.h>
+
+#define MAX_SMD_TTYS 32
+
+struct smd_tty_info {
+	struct tty_port port;
+	smd_channel_t *ch;
+};
+
+struct smd_tty_channel_desc {
+	int id;
+	const char *name;
+};
+
+static struct smd_tty_info smd_tty[MAX_SMD_TTYS];
+
+static const struct smd_tty_channel_desc smd_default_tty_channels[] = {
+	{ .id = 0, .name = "SMD_DS" },
+	{ .id = 27, .name = "SMD_GPSNMEA" },
+};
+
+static const struct smd_tty_channel_desc *smd_tty_channels =
+		smd_default_tty_channels;
+static int smd_tty_channels_len = ARRAY_SIZE(smd_default_tty_channels);
+
+static void smd_tty_notify(void *priv, unsigned event)
+{
+	unsigned char *ptr;
+	int avail;
+	struct smd_tty_info *info = priv;
+	struct tty_struct *tty;
+
+	if (event != SMD_EVENT_DATA)
+		return;
+
+	tty = tty_port_tty_get(&info->port);
+	if (!tty)
+		return;
+
+	for (;;) {
+		if (test_bit(TTY_THROTTLED, &tty->flags))
+			break;
+		avail = smd_read_avail(info->ch);
+		if (avail == 0)
+			break;
+
+		avail = tty_prepare_flip_string(tty, &ptr, avail);
+
+		if (smd_read(info->ch, ptr, avail) != avail) {
+			/* shouldn't be possible since we're in interrupt
+			** context here and nobody else could 'steal' our
+			** characters.
+			*/
+			pr_err("OOPS - smd_tty_buffer mismatch?!");
+		}
+
+		tty_flip_buffer_push(tty);
+	}
+
+	/* XXX only when writable and necessary */
+	tty_wakeup(tty);
+	tty_kref_put(tty);
+}
+
+static int smd_tty_port_activate(struct tty_port *tport, struct tty_struct *tty)
+{
+	int i, res = 0;
+	int n = tty->index;
+	const char *name = NULL;
+	struct smd_tty_info *info = smd_tty + n;
+
+	for (i = 0; i < smd_tty_channels_len; i++) {
+		if (smd_tty_channels[i].id == n) {
+			name = smd_tty_channels[i].name;
+			break;
+		}
+	}
+	if (!name)
+		return -ENODEV;
+
+	if (info->ch)
+		smd_kick(info->ch);
+	else
+		res = smd_open(name, &info->ch, info, smd_tty_notify);
+
+	if (!res)
+		tty->driver_data = info;
+
+	return res;
+}
+
+static void smd_tty_port_shutdown(struct tty_port *tport)
+{
+	struct smd_tty_info *info;
+	struct tty_struct *tty = tty_port_tty_get(tport);
+
+	info = tty->driver_data;
+	if (info->ch) {
+		smd_close(info->ch);
+		info->ch = 0;
+	}
+
+	tty->driver_data = 0;
+	tty_kref_put(tty);
+}
+
+static int smd_tty_open(struct tty_struct *tty, struct file *f)
+{
+	struct smd_tty_info *info = smd_tty + tty->index;
+
+	return tty_port_open(&info->port, tty, f);
+}
+
+static void smd_tty_close(struct tty_struct *tty, struct file *f)
+{
+	struct smd_tty_info *info = tty->driver_data;
+
+	tty_port_close(&info->port, tty, f);
+}
+
+static int smd_tty_write(struct tty_struct *tty,
+			 const unsigned char *buf, int len)
+{
+	struct smd_tty_info *info = tty->driver_data;
+	int avail;
+
+	/* if we're writing to a packet channel we will
+	** never be able to write more data than there
+	** is currently space for
+	*/
+	avail = smd_write_avail(info->ch);
+	if (len > avail)
+		len = avail;
+
+	return smd_write(info->ch, buf, len);
+}
+
+static int smd_tty_write_room(struct tty_struct *tty)
+{
+	struct smd_tty_info *info = tty->driver_data;
+	return smd_write_avail(info->ch);
+}
+
+static int smd_tty_chars_in_buffer(struct tty_struct *tty)
+{
+	struct smd_tty_info *info = tty->driver_data;
+	return smd_read_avail(info->ch);
+}
+
+static void smd_tty_unthrottle(struct tty_struct *tty)
+{
+	struct smd_tty_info *info = tty->driver_data;
+	smd_kick(info->ch);
+}
+
+static const struct tty_port_operations smd_tty_port_ops = {
+	.shutdown = smd_tty_port_shutdown,
+	.activate = smd_tty_port_activate,
+};
+
+static const struct tty_operations smd_tty_ops = {
+	.open = smd_tty_open,
+	.close = smd_tty_close,
+	.write = smd_tty_write,
+	.write_room = smd_tty_write_room,
+	.chars_in_buffer = smd_tty_chars_in_buffer,
+	.unthrottle = smd_tty_unthrottle,
+};
+
+static struct tty_driver *smd_tty_driver;
+
+static int __init smd_tty_init(void)
+{
+	int ret, i;
+
+	smd_tty_driver = alloc_tty_driver(MAX_SMD_TTYS);
+	if (smd_tty_driver == 0)
+		return -ENOMEM;
+
+	smd_tty_driver->driver_name = "smd_tty_driver";
+	smd_tty_driver->name = "smd";
+	smd_tty_driver->major = 0;
+	smd_tty_driver->minor_start = 0;
+	smd_tty_driver->type = TTY_DRIVER_TYPE_SERIAL;
+	smd_tty_driver->subtype = SERIAL_TYPE_NORMAL;
+	smd_tty_driver->init_termios = tty_std_termios;
+	smd_tty_driver->init_termios.c_iflag = 0;
+	smd_tty_driver->init_termios.c_oflag = 0;
+	smd_tty_driver->init_termios.c_cflag = B38400 | CS8 | CREAD;
+	smd_tty_driver->init_termios.c_lflag = 0;
+	smd_tty_driver->flags = TTY_DRIVER_RESET_TERMIOS |
+		TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV;
+	tty_set_operations(smd_tty_driver, &smd_tty_ops);
+
+	ret = tty_register_driver(smd_tty_driver);
+	if (ret)
+		return ret;
+
+	for (i = 0; i < smd_tty_channels_len; i++) {
+		tty_port_init(&smd_tty[smd_tty_channels[i].id].port);
+		smd_tty[smd_tty_channels[i].id].port.ops = &smd_tty_port_ops;
+		tty_register_device(smd_tty_driver, smd_tty_channels[i].id, 0);
+	}
+
+	return 0;
+}
+
+module_init(smd_tty_init);
diff --git a/ap/os/linux/linux-3.4.x/drivers/tty/serial/mux.c b/ap/os/linux/linux-3.4.x/drivers/tty/serial/mux.c
new file mode 100644
index 0000000..7ea8a26
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/tty/serial/mux.c
@@ -0,0 +1,634 @@
+/*
+** mux.c:
+**	serial driver for the Mux console found in some PA-RISC servers.
+**
+**	(c) Copyright 2002 Ryan Bradetich
+**	(c) Copyright 2002 Hewlett-Packard Company
+**
+** 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 Driver currently only supports the console (port 0) on the MUX.
+** Additional work will be needed on this driver to enable the full
+** functionality of the MUX.
+**
+*/
+
+#include <linux/module.h>
+#include <linux/ioport.h>
+#include <linux/init.h>
+#include <linux/serial.h>
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
+#include <linux/console.h>
+#include <linux/delay.h> /* for udelay */
+#include <linux/device.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/parisc-device.h>
+
+#ifdef CONFIG_MAGIC_SYSRQ
+#include <linux/sysrq.h>
+#define SUPPORT_SYSRQ
+#endif
+
+#include <linux/serial_core.h>
+
+#define MUX_OFFSET 0x800
+#define MUX_LINE_OFFSET 0x80
+
+#define MUX_FIFO_SIZE 255
+#define MUX_POLL_DELAY (30 * HZ / 1000)
+
+#define IO_DATA_REG_OFFSET 0x3c
+#define IO_DCOUNT_REG_OFFSET 0x40
+
+#define MUX_EOFIFO(status) ((status & 0xF000) == 0xF000)
+#define MUX_STATUS(status) ((status & 0xF000) == 0x8000)
+#define MUX_BREAK(status) ((status & 0xF000) == 0x2000)
+
+#define MUX_NR 256
+static unsigned int port_cnt __read_mostly;
+struct mux_port {
+	struct uart_port port;
+	int enabled;
+};
+static struct mux_port mux_ports[MUX_NR];
+
+static struct uart_driver mux_driver = {
+	.owner = THIS_MODULE,
+	.driver_name = "ttyB",
+	.dev_name = "ttyB",
+	.major = MUX_MAJOR,
+	.minor = 0,
+	.nr = MUX_NR,
+};
+
+static struct timer_list mux_timer;
+
+#define UART_PUT_CHAR(p, c) __raw_writel((c), (p)->membase + IO_DATA_REG_OFFSET)
+#define UART_GET_FIFO_CNT(p) __raw_readl((p)->membase + IO_DCOUNT_REG_OFFSET)
+
+/**
+ * get_mux_port_count - Get the number of available ports on the Mux.
+ * @dev: The parisc device.
+ *
+ * This function is used to determine the number of ports the Mux
+ * supports.  The IODC data reports the number of ports the Mux
+ * can support, but there are cases where not all the Mux ports
+ * are connected.  This function can override the IODC and
+ * return the true port count.
+ */
+static int __init get_mux_port_count(struct parisc_device *dev)
+{
+	int status;
+	u8 iodc_data[32];
+	unsigned long bytecnt;
+
+	/* If this is the built-in Mux for the K-Class (Eole CAP/MUX),
+	 * we only need to allocate resources for 1 port since the
+	 * other 7 ports are not connected.
+	 */
+	if(dev->id.hversion == 0x15)
+		return 1;
+
+	status = pdc_iodc_read(&bytecnt, dev->hpa.start, 0, iodc_data, 32);
+	BUG_ON(status != PDC_OK);
+
+	/* Return the number of ports specified in the iodc data. */
+	return ((((iodc_data)[4] & 0xf0) >> 4) * 8) + 8;
+}
+
+/**
+ * mux_tx_empty - Check if the transmitter fifo is empty.
+ * @port: Ptr to the uart_port.
+ *
+ * This function test if the transmitter fifo for the port
+ * described by 'port' is empty.  If it is empty, this function
+ * should return TIOCSER_TEMT, otherwise return 0.
+ */
+static unsigned int mux_tx_empty(struct uart_port *port)
+{
+	return UART_GET_FIFO_CNT(port) ? 0 : TIOCSER_TEMT;
+} 
+
+/**
+ * mux_set_mctrl - Set the current state of the modem control inputs.
+ * @ports: Ptr to the uart_port.
+ * @mctrl: Modem control bits.
+ *
+ * The Serial MUX does not support CTS, DCD or DSR so this function
+ * is ignored.
+ */
+static void mux_set_mctrl(struct uart_port *port, unsigned int mctrl)
+{
+}
+
+/**
+ * mux_get_mctrl - Returns the current state of modem control inputs.
+ * @port: Ptr to the uart_port.
+ *
+ * The Serial MUX does not support CTS, DCD or DSR so these lines are
+ * treated as permanently active.
+ */
+static unsigned int mux_get_mctrl(struct uart_port *port)
+{ 
+	return TIOCM_CAR | TIOCM_DSR | TIOCM_CTS;
+}
+
+/**
+ * mux_stop_tx - Stop transmitting characters.
+ * @port: Ptr to the uart_port.
+ *
+ * The Serial MUX does not support this function.
+ */
+static void mux_stop_tx(struct uart_port *port)
+{
+}
+
+/**
+ * mux_start_tx - Start transmitting characters.
+ * @port: Ptr to the uart_port.
+ *
+ * The Serial Mux does not support this function.
+ */
+static void mux_start_tx(struct uart_port *port)
+{
+}
+
+/**
+ * mux_stop_rx - Stop receiving characters.
+ * @port: Ptr to the uart_port.
+ *
+ * The Serial Mux does not support this function.
+ */
+static void mux_stop_rx(struct uart_port *port)
+{
+}
+
+/**
+ * mux_enable_ms - Enable modum status interrupts.
+ * @port: Ptr to the uart_port.
+ *
+ * The Serial Mux does not support this function.
+ */
+static void mux_enable_ms(struct uart_port *port)
+{
+}
+
+/**
+ * mux_break_ctl - Control the transmitssion of a break signal.
+ * @port: Ptr to the uart_port.
+ * @break_state: Raise/Lower the break signal.
+ *
+ * The Serial Mux does not support this function.
+ */
+static void mux_break_ctl(struct uart_port *port, int break_state)
+{
+}
+
+/**
+ * mux_write - Write chars to the mux fifo.
+ * @port: Ptr to the uart_port.
+ *
+ * This function writes all the data from the uart buffer to
+ * the mux fifo.
+ */
+static void mux_write(struct uart_port *port)
+{
+	int count;
+	struct circ_buf *xmit = &port->state->xmit;
+
+	if(port->x_char) {
+		UART_PUT_CHAR(port, port->x_char);
+		port->icount.tx++;
+		port->x_char = 0;
+		return;
+	}
+
+	if(uart_circ_empty(xmit) || uart_tx_stopped(port)) {
+		mux_stop_tx(port);
+		return;
+	}
+
+	count = (port->fifosize) - UART_GET_FIFO_CNT(port);
+	do {
+		UART_PUT_CHAR(port, xmit->buf[xmit->tail]);
+		xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
+		port->icount.tx++;
+		if(uart_circ_empty(xmit))
+			break;
+
+	} while(--count > 0);
+
+	while(UART_GET_FIFO_CNT(port)) 
+		udelay(1);
+
+	if(uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
+		uart_write_wakeup(port);
+
+	if (uart_circ_empty(xmit))
+		mux_stop_tx(port);
+}
+
+/**
+ * mux_read - Read chars from the mux fifo.
+ * @port: Ptr to the uart_port.
+ *
+ * This reads all available data from the mux's fifo and pushes
+ * the data to the tty layer.
+ */
+static void mux_read(struct uart_port *port)
+{
+	int data;
+	struct tty_struct *tty = port->state->port.tty;
+	__u32 start_count = port->icount.rx;
+
+	while(1) {
+		data = __raw_readl(port->membase + IO_DATA_REG_OFFSET);
+
+		if (MUX_STATUS(data))
+			continue;
+
+		if (MUX_EOFIFO(data))
+			break;
+
+		port->icount.rx++;
+
+		if (MUX_BREAK(data)) {
+			port->icount.brk++;
+			if(uart_handle_break(port))
+				continue;
+		}
+
+		if (uart_handle_sysrq_char(port, data & 0xffu))
+			continue;
+
+		tty_insert_flip_char(tty, data & 0xFF, TTY_NORMAL);
+	}
+	
+	if (start_count != port->icount.rx) {
+		tty_flip_buffer_push(tty);
+	}
+}
+
+/**
+ * mux_startup - Initialize the port.
+ * @port: Ptr to the uart_port.
+ *
+ * Grab any resources needed for this port and start the
+ * mux timer.
+ */
+static int mux_startup(struct uart_port *port)
+{
+	mux_ports[port->line].enabled = 1;
+	return 0;
+}
+
+/**
+ * mux_shutdown - Disable the port.
+ * @port: Ptr to the uart_port.
+ *
+ * Release any resources needed for the port.
+ */
+static void mux_shutdown(struct uart_port *port)
+{
+	mux_ports[port->line].enabled = 0;
+}
+
+/**
+ * mux_set_termios - Chane port parameters.
+ * @port: Ptr to the uart_port.
+ * @termios: new termios settings.
+ * @old: old termios settings.
+ *
+ * The Serial Mux does not support this function.
+ */
+static void
+mux_set_termios(struct uart_port *port, struct ktermios *termios,
+	        struct ktermios *old)
+{
+}
+
+/**
+ * mux_type - Describe the port.
+ * @port: Ptr to the uart_port.
+ *
+ * Return a pointer to a string constant describing the
+ * specified port.
+ */
+static const char *mux_type(struct uart_port *port)
+{
+	return "Mux";
+}
+
+/**
+ * mux_release_port - Release memory and IO regions.
+ * @port: Ptr to the uart_port.
+ * 
+ * Release any memory and IO region resources currently in use by
+ * the port.
+ */
+static void mux_release_port(struct uart_port *port)
+{
+}
+
+/**
+ * mux_request_port - Request memory and IO regions.
+ * @port: Ptr to the uart_port.
+ *
+ * Request any memory and IO region resources required by the port.
+ * If any fail, no resources should be registered when this function
+ * returns, and it should return -EBUSY on failure.
+ */
+static int mux_request_port(struct uart_port *port)
+{
+	return 0;
+}
+
+/**
+ * mux_config_port - Perform port autoconfiguration.
+ * @port: Ptr to the uart_port.
+ * @type: Bitmask of required configurations.
+ *
+ * Perform any autoconfiguration steps for the port.  This function is
+ * called if the UPF_BOOT_AUTOCONF flag is specified for the port.
+ * [Note: This is required for now because of a bug in the Serial core.
+ *  rmk has already submitted a patch to linus, should be available for
+ *  2.5.47.]
+ */
+static void mux_config_port(struct uart_port *port, int type)
+{
+	port->type = PORT_MUX;
+}
+
+/**
+ * mux_verify_port - Verify the port information.
+ * @port: Ptr to the uart_port.
+ * @ser: Ptr to the serial information.
+ *
+ * Verify the new serial port information contained within serinfo is
+ * suitable for this port type.
+ */
+static int mux_verify_port(struct uart_port *port, struct serial_struct *ser)
+{
+	if(port->membase == NULL)
+		return -EINVAL;
+
+	return 0;
+}
+
+/**
+ * mux_drv_poll - Mux poll function.
+ * @unused: Unused variable
+ *
+ * This function periodically polls the Serial MUX to check for new data.
+ */
+static void mux_poll(unsigned long unused)
+{  
+	int i;
+
+	for(i = 0; i < port_cnt; ++i) {
+		if(!mux_ports[i].enabled)
+			continue;
+
+		mux_read(&mux_ports[i].port);
+		mux_write(&mux_ports[i].port);
+	}
+
+	mod_timer(&mux_timer, jiffies + MUX_POLL_DELAY);
+}
+
+
+#ifdef CONFIG_SERIAL_MUX_CONSOLE
+static void mux_console_write(struct console *co, const char *s, unsigned count)
+{
+	/* Wait until the FIFO drains. */
+	while(UART_GET_FIFO_CNT(&mux_ports[0].port))
+		udelay(1);
+
+	while(count--) {
+		if(*s == '\n') {
+			UART_PUT_CHAR(&mux_ports[0].port, '\r');
+		}
+		UART_PUT_CHAR(&mux_ports[0].port, *s++);
+	}
+
+}
+
+static int mux_console_setup(struct console *co, char *options)
+{
+        return 0;
+}
+
+struct tty_driver *mux_console_device(struct console *co, int *index)
+{
+        *index = co->index;
+	return mux_driver.tty_driver;
+}
+
+static struct console mux_console = {
+	.name =		"ttyB",
+	.write =	mux_console_write,
+	.device =	mux_console_device,
+	.setup =	mux_console_setup,
+	.flags =	CON_ENABLED | CON_PRINTBUFFER,
+	.index =	0,
+};
+
+#define MUX_CONSOLE	&mux_console
+#else
+#define MUX_CONSOLE	NULL
+#endif
+
+static struct uart_ops mux_pops = {
+	.tx_empty =		mux_tx_empty,
+	.set_mctrl =		mux_set_mctrl,
+	.get_mctrl =		mux_get_mctrl,
+	.stop_tx =		mux_stop_tx,
+	.start_tx =		mux_start_tx,
+	.stop_rx =		mux_stop_rx,
+	.enable_ms =		mux_enable_ms,
+	.break_ctl =		mux_break_ctl,
+	.startup =		mux_startup,
+	.shutdown =		mux_shutdown,
+	.set_termios =		mux_set_termios,
+	.type =			mux_type,
+	.release_port =		mux_release_port,
+	.request_port =		mux_request_port,
+	.config_port =		mux_config_port,
+	.verify_port =		mux_verify_port,
+};
+
+/**
+ * mux_probe - Determine if the Serial Mux should claim this device.
+ * @dev: The parisc device.
+ *
+ * Deterimine if the Serial Mux should claim this chip (return 0)
+ * or not (return 1).
+ */
+static int __init mux_probe(struct parisc_device *dev)
+{
+	int i, status;
+
+	int port_count = get_mux_port_count(dev);
+	printk(KERN_INFO "Serial mux driver (%d ports) Revision: 0.6\n", port_count);
+
+	dev_set_drvdata(&dev->dev, (void *)(long)port_count);
+	request_mem_region(dev->hpa.start + MUX_OFFSET,
+                           port_count * MUX_LINE_OFFSET, "Mux");
+
+	if(!port_cnt) {
+		mux_driver.cons = MUX_CONSOLE;
+
+		status = uart_register_driver(&mux_driver);
+		if(status) {
+			printk(KERN_ERR "Serial mux: Unable to register driver.\n");
+			return 1;
+		}
+	}
+
+	for(i = 0; i < port_count; ++i, ++port_cnt) {
+		struct uart_port *port = &mux_ports[port_cnt].port;
+		port->iobase	= 0;
+		port->mapbase	= dev->hpa.start + MUX_OFFSET +
+						(i * MUX_LINE_OFFSET);
+		port->membase	= ioremap_nocache(port->mapbase, MUX_LINE_OFFSET);
+		port->iotype	= UPIO_MEM;
+		port->type	= PORT_MUX;
+		port->irq	= 0;
+		port->uartclk	= 0;
+		port->fifosize	= MUX_FIFO_SIZE;
+		port->ops	= &mux_pops;
+		port->flags	= UPF_BOOT_AUTOCONF;
+		port->line	= port_cnt;
+
+		/* The port->timeout needs to match what is present in
+		 * uart_wait_until_sent in serial_core.c.  Otherwise
+		 * the time spent in msleep_interruptable will be very
+		 * long, causing the appearance of a console hang.
+		 */
+		port->timeout   = HZ / 50;
+		spin_lock_init(&port->lock);
+
+		status = uart_add_one_port(&mux_driver, port);
+		BUG_ON(status);
+	}
+
+	return 0;
+}
+
+static int __devexit mux_remove(struct parisc_device *dev)
+{
+	int i, j;
+	int port_count = (long)dev_get_drvdata(&dev->dev);
+
+	/* Find Port 0 for this card in the mux_ports list. */
+	for(i = 0; i < port_cnt; ++i) {
+		if(mux_ports[i].port.mapbase == dev->hpa.start + MUX_OFFSET)
+			break;
+	}
+	BUG_ON(i + port_count > port_cnt);
+
+	/* Release the resources associated with each port on the device. */
+	for(j = 0; j < port_count; ++j, ++i) {
+		struct uart_port *port = &mux_ports[i].port;
+
+		uart_remove_one_port(&mux_driver, port);
+		if(port->membase)
+			iounmap(port->membase);
+	}
+
+	release_mem_region(dev->hpa.start + MUX_OFFSET, port_count * MUX_LINE_OFFSET);
+	return 0;
+}
+
+/* Hack.  This idea was taken from the 8250_gsc.c on how to properly order
+ * the serial port detection in the proper order.   The idea is we always
+ * want the builtin mux to be detected before addin mux cards, so we
+ * specifically probe for the builtin mux cards first.
+ *
+ * This table only contains the parisc_device_id of known builtin mux
+ * devices.  All other mux cards will be detected by the generic mux_tbl.
+ */
+static struct parisc_device_id builtin_mux_tbl[] = {
+	{ HPHW_A_DIRECT, HVERSION_REV_ANY_ID, 0x15, 0x0000D }, /* All K-class */
+	{ HPHW_A_DIRECT, HVERSION_REV_ANY_ID, 0x44, 0x0000D }, /* E35, E45, and E55 */
+	{ 0, }
+};
+
+static struct parisc_device_id mux_tbl[] = {
+	{ HPHW_A_DIRECT, HVERSION_REV_ANY_ID, HVERSION_ANY_ID, 0x0000D },
+	{ 0, }
+};
+
+MODULE_DEVICE_TABLE(parisc, builtin_mux_tbl);
+MODULE_DEVICE_TABLE(parisc, mux_tbl);
+
+static struct parisc_driver builtin_serial_mux_driver = {
+	.name =		"builtin_serial_mux",
+	.id_table =	builtin_mux_tbl,
+	.probe =	mux_probe,
+	.remove =       __devexit_p(mux_remove),
+};
+
+static struct parisc_driver serial_mux_driver = {
+	.name =		"serial_mux",
+	.id_table =	mux_tbl,
+	.probe =	mux_probe,
+	.remove =       __devexit_p(mux_remove),
+};
+
+/**
+ * mux_init - Serial MUX initialization procedure.
+ *
+ * Register the Serial MUX driver.
+ */
+static int __init mux_init(void)
+{
+	register_parisc_driver(&builtin_serial_mux_driver);
+	register_parisc_driver(&serial_mux_driver);
+
+	if(port_cnt > 0) {
+		/* Start the Mux timer */
+		init_timer(&mux_timer);
+		mux_timer.function = mux_poll;
+		mod_timer(&mux_timer, jiffies + MUX_POLL_DELAY);
+
+#ifdef CONFIG_SERIAL_MUX_CONSOLE
+	        register_console(&mux_console);
+#endif
+	}
+
+	return 0;
+}
+
+/**
+ * mux_exit - Serial MUX cleanup procedure.
+ *
+ * Unregister the Serial MUX driver from the tty layer.
+ */
+static void __exit mux_exit(void)
+{
+	/* Delete the Mux timer. */
+	if(port_cnt > 0) {
+		del_timer(&mux_timer);
+#ifdef CONFIG_SERIAL_MUX_CONSOLE
+		unregister_console(&mux_console);
+#endif
+	}
+
+	unregister_parisc_driver(&builtin_serial_mux_driver);
+	unregister_parisc_driver(&serial_mux_driver);
+	uart_unregister_driver(&mux_driver);
+}
+
+module_init(mux_init);
+module_exit(mux_exit);
+
+MODULE_AUTHOR("Ryan Bradetich");
+MODULE_DESCRIPTION("Serial MUX driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS_CHARDEV_MAJOR(MUX_MAJOR);
diff --git a/ap/os/linux/linux-3.4.x/drivers/tty/serial/mxs-auart.c b/ap/os/linux/linux-3.4.x/drivers/tty/serial/mxs-auart.c
new file mode 100644
index 0000000..c75b27b
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/tty/serial/mxs-auart.c
@@ -0,0 +1,807 @@
+/*
+ * Freescale STMP37XX/STMP378X Application UART driver
+ *
+ * Author: dmitry pervushin <dimka@embeddedalley.com>
+ *
+ * Copyright 2008-2010 Freescale Semiconductor, Inc.
+ * Copyright 2008 Embedded Alley Solutions, Inc All Rights Reserved.
+ *
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/console.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/wait.h>
+#include <linux/tty.h>
+#include <linux/tty_driver.h>
+#include <linux/tty_flip.h>
+#include <linux/serial.h>
+#include <linux/serial_core.h>
+#include <linux/platform_device.h>
+#include <linux/device.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+
+#include <asm/cacheflush.h>
+
+#define MXS_AUART_PORTS 5
+
+#define AUART_CTRL0			0x00000000
+#define AUART_CTRL0_SET			0x00000004
+#define AUART_CTRL0_CLR			0x00000008
+#define AUART_CTRL0_TOG			0x0000000c
+#define AUART_CTRL1			0x00000010
+#define AUART_CTRL1_SET			0x00000014
+#define AUART_CTRL1_CLR			0x00000018
+#define AUART_CTRL1_TOG			0x0000001c
+#define AUART_CTRL2			0x00000020
+#define AUART_CTRL2_SET			0x00000024
+#define AUART_CTRL2_CLR			0x00000028
+#define AUART_CTRL2_TOG			0x0000002c
+#define AUART_LINECTRL			0x00000030
+#define AUART_LINECTRL_SET		0x00000034
+#define AUART_LINECTRL_CLR		0x00000038
+#define AUART_LINECTRL_TOG		0x0000003c
+#define AUART_LINECTRL2			0x00000040
+#define AUART_LINECTRL2_SET		0x00000044
+#define AUART_LINECTRL2_CLR		0x00000048
+#define AUART_LINECTRL2_TOG		0x0000004c
+#define AUART_INTR			0x00000050
+#define AUART_INTR_SET			0x00000054
+#define AUART_INTR_CLR			0x00000058
+#define AUART_INTR_TOG			0x0000005c
+#define AUART_DATA			0x00000060
+#define AUART_STAT			0x00000070
+#define AUART_DEBUG			0x00000080
+#define AUART_VERSION			0x00000090
+#define AUART_AUTOBAUD			0x000000a0
+
+#define AUART_CTRL0_SFTRST			(1 << 31)
+#define AUART_CTRL0_CLKGATE			(1 << 30)
+
+#define AUART_CTRL2_CTSEN			(1 << 15)
+#define AUART_CTRL2_RTS				(1 << 11)
+#define AUART_CTRL2_RXE				(1 << 9)
+#define AUART_CTRL2_TXE				(1 << 8)
+#define AUART_CTRL2_UARTEN			(1 << 0)
+
+#define AUART_LINECTRL_BAUD_DIVINT_SHIFT	16
+#define AUART_LINECTRL_BAUD_DIVINT_MASK		0xffff0000
+#define AUART_LINECTRL_BAUD_DIVINT(v)		(((v) & 0xffff) << 16)
+#define AUART_LINECTRL_BAUD_DIVFRAC_SHIFT	8
+#define AUART_LINECTRL_BAUD_DIVFRAC_MASK	0x00003f00
+#define AUART_LINECTRL_BAUD_DIVFRAC(v)		(((v) & 0x3f) << 8)
+#define AUART_LINECTRL_WLEN_MASK		0x00000060
+#define AUART_LINECTRL_WLEN(v)			(((v) & 0x3) << 5)
+#define AUART_LINECTRL_FEN			(1 << 4)
+#define AUART_LINECTRL_STP2			(1 << 3)
+#define AUART_LINECTRL_EPS			(1 << 2)
+#define AUART_LINECTRL_PEN			(1 << 1)
+#define AUART_LINECTRL_BRK			(1 << 0)
+
+#define AUART_INTR_RTIEN			(1 << 22)
+#define AUART_INTR_TXIEN			(1 << 21)
+#define AUART_INTR_RXIEN			(1 << 20)
+#define AUART_INTR_CTSMIEN			(1 << 17)
+#define AUART_INTR_RTIS				(1 << 6)
+#define AUART_INTR_TXIS				(1 << 5)
+#define AUART_INTR_RXIS				(1 << 4)
+#define AUART_INTR_CTSMIS			(1 << 1)
+
+#define AUART_STAT_BUSY				(1 << 29)
+#define AUART_STAT_CTS				(1 << 28)
+#define AUART_STAT_TXFE				(1 << 27)
+#define AUART_STAT_TXFF				(1 << 25)
+#define AUART_STAT_RXFE				(1 << 24)
+#define AUART_STAT_OERR				(1 << 19)
+#define AUART_STAT_BERR				(1 << 18)
+#define AUART_STAT_PERR				(1 << 17)
+#define AUART_STAT_FERR				(1 << 16)
+
+static struct uart_driver auart_driver;
+
+struct mxs_auart_port {
+	struct uart_port port;
+
+	unsigned int flags;
+	unsigned int ctrl;
+
+	unsigned int irq;
+
+	struct clk *clk;
+	struct device *dev;
+};
+
+static void mxs_auart_stop_tx(struct uart_port *u);
+
+#define to_auart_port(u) container_of(u, struct mxs_auart_port, port)
+
+static inline void mxs_auart_tx_chars(struct mxs_auart_port *s)
+{
+	struct circ_buf *xmit = &s->port.state->xmit;
+
+	while (!(readl(s->port.membase + AUART_STAT) &
+		 AUART_STAT_TXFF)) {
+		if (s->port.x_char) {
+			s->port.icount.tx++;
+			writel(s->port.x_char,
+				     s->port.membase + AUART_DATA);
+			s->port.x_char = 0;
+			continue;
+		}
+		if (!uart_circ_empty(xmit) && !uart_tx_stopped(&s->port)) {
+			s->port.icount.tx++;
+			writel(xmit->buf[xmit->tail],
+				     s->port.membase + AUART_DATA);
+			xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
+		} else
+			break;
+	}
+	if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
+		uart_write_wakeup(&s->port);
+
+	if (uart_circ_empty(&(s->port.state->xmit)))
+		writel(AUART_INTR_TXIEN,
+			     s->port.membase + AUART_INTR_CLR);
+	else
+		writel(AUART_INTR_TXIEN,
+			     s->port.membase + AUART_INTR_SET);
+
+	if (uart_tx_stopped(&s->port))
+		mxs_auart_stop_tx(&s->port);
+}
+
+static void mxs_auart_rx_char(struct mxs_auart_port *s)
+{
+	int flag;
+	u32 stat;
+	u8 c;
+
+	c = readl(s->port.membase + AUART_DATA);
+	stat = readl(s->port.membase + AUART_STAT);
+
+	flag = TTY_NORMAL;
+	s->port.icount.rx++;
+
+	if (stat & AUART_STAT_BERR) {
+		s->port.icount.brk++;
+		if (uart_handle_break(&s->port))
+			goto out;
+	} else if (stat & AUART_STAT_PERR) {
+		s->port.icount.parity++;
+	} else if (stat & AUART_STAT_FERR) {
+		s->port.icount.frame++;
+	}
+
+	/*
+	 * Mask off conditions which should be ingored.
+	 */
+	stat &= s->port.read_status_mask;
+
+	if (stat & AUART_STAT_BERR) {
+		flag = TTY_BREAK;
+	} else if (stat & AUART_STAT_PERR)
+		flag = TTY_PARITY;
+	else if (stat & AUART_STAT_FERR)
+		flag = TTY_FRAME;
+
+	if (stat & AUART_STAT_OERR)
+		s->port.icount.overrun++;
+
+	if (uart_handle_sysrq_char(&s->port, c))
+		goto out;
+
+	uart_insert_char(&s->port, stat, AUART_STAT_OERR, c, flag);
+out:
+	writel(stat, s->port.membase + AUART_STAT);
+}
+
+static void mxs_auart_rx_chars(struct mxs_auart_port *s)
+{
+	struct tty_struct *tty = s->port.state->port.tty;
+	u32 stat = 0;
+
+	for (;;) {
+		stat = readl(s->port.membase + AUART_STAT);
+		if (stat & AUART_STAT_RXFE)
+			break;
+		mxs_auart_rx_char(s);
+	}
+
+	writel(stat, s->port.membase + AUART_STAT);
+	tty_flip_buffer_push(tty);
+}
+
+static int mxs_auart_request_port(struct uart_port *u)
+{
+	return 0;
+}
+
+static int mxs_auart_verify_port(struct uart_port *u,
+				    struct serial_struct *ser)
+{
+	if (u->type != PORT_UNKNOWN && u->type != PORT_IMX)
+		return -EINVAL;
+	return 0;
+}
+
+static void mxs_auart_config_port(struct uart_port *u, int flags)
+{
+}
+
+static const char *mxs_auart_type(struct uart_port *u)
+{
+	struct mxs_auart_port *s = to_auart_port(u);
+
+	return dev_name(s->dev);
+}
+
+static void mxs_auart_release_port(struct uart_port *u)
+{
+}
+
+static void mxs_auart_set_mctrl(struct uart_port *u, unsigned mctrl)
+{
+	struct mxs_auart_port *s = to_auart_port(u);
+
+	u32 ctrl = readl(u->membase + AUART_CTRL2);
+
+	ctrl &= ~AUART_CTRL2_RTS;
+	if (mctrl & TIOCM_RTS)
+		ctrl |= AUART_CTRL2_RTS;
+	s->ctrl = mctrl;
+	writel(ctrl, u->membase + AUART_CTRL2);
+}
+
+static u32 mxs_auart_get_mctrl(struct uart_port *u)
+{
+	struct mxs_auart_port *s = to_auart_port(u);
+	u32 stat = readl(u->membase + AUART_STAT);
+	int ctrl2 = readl(u->membase + AUART_CTRL2);
+	u32 mctrl = s->ctrl;
+
+	mctrl &= ~TIOCM_CTS;
+	if (stat & AUART_STAT_CTS)
+		mctrl |= TIOCM_CTS;
+
+	if (ctrl2 & AUART_CTRL2_RTS)
+		mctrl |= TIOCM_RTS;
+
+	return mctrl;
+}
+
+static void mxs_auart_settermios(struct uart_port *u,
+				 struct ktermios *termios,
+				 struct ktermios *old)
+{
+	u32 bm, ctrl, ctrl2, div;
+	unsigned int cflag, baud;
+
+	cflag = termios->c_cflag;
+
+	ctrl = AUART_LINECTRL_FEN;
+	ctrl2 = readl(u->membase + AUART_CTRL2);
+
+	/* byte size */
+	switch (cflag & CSIZE) {
+	case CS5:
+		bm = 0;
+		break;
+	case CS6:
+		bm = 1;
+		break;
+	case CS7:
+		bm = 2;
+		break;
+	case CS8:
+		bm = 3;
+		break;
+	default:
+		return;
+	}
+
+	ctrl |= AUART_LINECTRL_WLEN(bm);
+
+	/* parity */
+	if (cflag & PARENB) {
+		ctrl |= AUART_LINECTRL_PEN;
+		if ((cflag & PARODD) == 0)
+			ctrl |= AUART_LINECTRL_EPS;
+	}
+
+	u->read_status_mask = 0;
+
+	if (termios->c_iflag & INPCK)
+		u->read_status_mask |= AUART_STAT_PERR;
+	if (termios->c_iflag & (BRKINT | PARMRK))
+		u->read_status_mask |= AUART_STAT_BERR;
+
+	/*
+	 * Characters to ignore
+	 */
+	u->ignore_status_mask = 0;
+	if (termios->c_iflag & IGNPAR)
+		u->ignore_status_mask |= AUART_STAT_PERR;
+	if (termios->c_iflag & IGNBRK) {
+		u->ignore_status_mask |= AUART_STAT_BERR;
+		/*
+		 * If we're ignoring parity and break indicators,
+		 * ignore overruns too (for real raw support).
+		 */
+		if (termios->c_iflag & IGNPAR)
+			u->ignore_status_mask |= AUART_STAT_OERR;
+	}
+
+	/*
+	 * ignore all characters if CREAD is not set
+	 */
+	if (cflag & CREAD)
+		ctrl2 |= AUART_CTRL2_RXE;
+	else
+		ctrl2 &= ~AUART_CTRL2_RXE;
+
+	/* figure out the stop bits requested */
+	if (cflag & CSTOPB)
+		ctrl |= AUART_LINECTRL_STP2;
+
+	/* figure out the hardware flow control settings */
+	if (cflag & CRTSCTS)
+		ctrl2 |= AUART_CTRL2_CTSEN;
+	else
+		ctrl2 &= ~AUART_CTRL2_CTSEN;
+
+	/* set baud rate */
+	baud = uart_get_baud_rate(u, termios, old, 0, u->uartclk);
+	div = u->uartclk * 32 / baud;
+	ctrl |= AUART_LINECTRL_BAUD_DIVFRAC(div & 0x3F);
+	ctrl |= AUART_LINECTRL_BAUD_DIVINT(div >> 6);
+
+	writel(ctrl, u->membase + AUART_LINECTRL);
+	writel(ctrl2, u->membase + AUART_CTRL2);
+
+	uart_update_timeout(u, termios->c_cflag, baud);
+}
+
+static irqreturn_t mxs_auart_irq_handle(int irq, void *context)
+{
+	u32 istat;
+	struct mxs_auart_port *s = context;
+	u32 stat = readl(s->port.membase + AUART_STAT);
+
+	istat = readl(s->port.membase + AUART_INTR);
+
+	/* ack irq */
+	writel(istat & (AUART_INTR_RTIS
+		| AUART_INTR_TXIS
+		| AUART_INTR_RXIS
+		| AUART_INTR_CTSMIS),
+			s->port.membase + AUART_INTR_CLR);
+
+	if (istat & AUART_INTR_CTSMIS) {
+		uart_handle_cts_change(&s->port, stat & AUART_STAT_CTS);
+		writel(AUART_INTR_CTSMIS,
+				s->port.membase + AUART_INTR_CLR);
+		istat &= ~AUART_INTR_CTSMIS;
+	}
+
+	if (istat & (AUART_INTR_RTIS | AUART_INTR_RXIS)) {
+		mxs_auart_rx_chars(s);
+		istat &= ~(AUART_INTR_RTIS | AUART_INTR_RXIS);
+	}
+
+	if (istat & AUART_INTR_TXIS) {
+		mxs_auart_tx_chars(s);
+		istat &= ~AUART_INTR_TXIS;
+	}
+
+	return IRQ_HANDLED;
+}
+
+static void mxs_auart_reset(struct uart_port *u)
+{
+	int i;
+	unsigned int reg;
+
+	writel(AUART_CTRL0_SFTRST, u->membase + AUART_CTRL0_CLR);
+
+	for (i = 0; i < 10000; i++) {
+		reg = readl(u->membase + AUART_CTRL0);
+		if (!(reg & AUART_CTRL0_SFTRST))
+			break;
+		udelay(3);
+	}
+	writel(AUART_CTRL0_CLKGATE, u->membase + AUART_CTRL0_CLR);
+}
+
+static int mxs_auart_startup(struct uart_port *u)
+{
+	struct mxs_auart_port *s = to_auart_port(u);
+
+	clk_prepare_enable(s->clk);
+
+	writel(AUART_CTRL0_CLKGATE, u->membase + AUART_CTRL0_CLR);
+
+	writel(AUART_CTRL2_UARTEN, u->membase + AUART_CTRL2_SET);
+
+	writel(AUART_INTR_RXIEN | AUART_INTR_RTIEN | AUART_INTR_CTSMIEN,
+			u->membase + AUART_INTR);
+
+	/*
+	 * Enable fifo so all four bytes of a DMA word are written to
+	 * output (otherwise, only the LSB is written, ie. 1 in 4 bytes)
+	 */
+	writel(AUART_LINECTRL_FEN, u->membase + AUART_LINECTRL_SET);
+
+	return 0;
+}
+
+static void mxs_auart_shutdown(struct uart_port *u)
+{
+	struct mxs_auart_port *s = to_auart_port(u);
+
+	writel(AUART_CTRL2_UARTEN, u->membase + AUART_CTRL2_CLR);
+
+	writel(AUART_CTRL0_CLKGATE, u->membase + AUART_CTRL0_SET);
+
+	writel(AUART_INTR_RXIEN | AUART_INTR_RTIEN | AUART_INTR_CTSMIEN,
+			u->membase + AUART_INTR_CLR);
+
+	clk_disable_unprepare(s->clk);
+}
+
+static unsigned int mxs_auart_tx_empty(struct uart_port *u)
+{
+	if (readl(u->membase + AUART_STAT) & AUART_STAT_TXFE)
+		return TIOCSER_TEMT;
+	else
+		return 0;
+}
+
+static void mxs_auart_start_tx(struct uart_port *u)
+{
+	struct mxs_auart_port *s = to_auart_port(u);
+
+	/* enable transmitter */
+	writel(AUART_CTRL2_TXE, u->membase + AUART_CTRL2_SET);
+
+	mxs_auart_tx_chars(s);
+}
+
+static void mxs_auart_stop_tx(struct uart_port *u)
+{
+	writel(AUART_CTRL2_TXE, u->membase + AUART_CTRL2_CLR);
+}
+
+static void mxs_auart_stop_rx(struct uart_port *u)
+{
+	writel(AUART_CTRL2_RXE, u->membase + AUART_CTRL2_CLR);
+}
+
+static void mxs_auart_break_ctl(struct uart_port *u, int ctl)
+{
+	if (ctl)
+		writel(AUART_LINECTRL_BRK,
+			     u->membase + AUART_LINECTRL_SET);
+	else
+		writel(AUART_LINECTRL_BRK,
+			     u->membase + AUART_LINECTRL_CLR);
+}
+
+static void mxs_auart_enable_ms(struct uart_port *port)
+{
+	/* just empty */
+}
+
+static struct uart_ops mxs_auart_ops = {
+	.tx_empty       = mxs_auart_tx_empty,
+	.start_tx       = mxs_auart_start_tx,
+	.stop_tx	= mxs_auart_stop_tx,
+	.stop_rx	= mxs_auart_stop_rx,
+	.enable_ms      = mxs_auart_enable_ms,
+	.break_ctl      = mxs_auart_break_ctl,
+	.set_mctrl	= mxs_auart_set_mctrl,
+	.get_mctrl      = mxs_auart_get_mctrl,
+	.startup	= mxs_auart_startup,
+	.shutdown       = mxs_auart_shutdown,
+	.set_termios    = mxs_auart_settermios,
+	.type	   	= mxs_auart_type,
+	.release_port   = mxs_auart_release_port,
+	.request_port   = mxs_auart_request_port,
+	.config_port    = mxs_auart_config_port,
+	.verify_port    = mxs_auart_verify_port,
+};
+
+static struct mxs_auart_port *auart_port[MXS_AUART_PORTS];
+
+#ifdef CONFIG_SERIAL_MXS_AUART_CONSOLE
+static void mxs_auart_console_putchar(struct uart_port *port, int ch)
+{
+	unsigned int to = 1000;
+
+	while (readl(port->membase + AUART_STAT) & AUART_STAT_TXFF) {
+		if (!to--)
+			break;
+		udelay(1);
+	}
+
+	writel(ch, port->membase + AUART_DATA);
+}
+
+static void
+auart_console_write(struct console *co, const char *str, unsigned int count)
+{
+	struct mxs_auart_port *s;
+	struct uart_port *port;
+	unsigned int old_ctrl0, old_ctrl2;
+	unsigned int to = 20000;
+
+	if (co->index >	MXS_AUART_PORTS || co->index < 0)
+		return;
+
+	s = auart_port[co->index];
+	port = &s->port;
+
+	clk_enable(s->clk);
+
+	/* First save the CR then disable the interrupts */
+	old_ctrl2 = readl(port->membase + AUART_CTRL2);
+	old_ctrl0 = readl(port->membase + AUART_CTRL0);
+
+	writel(AUART_CTRL0_CLKGATE,
+		     port->membase + AUART_CTRL0_CLR);
+	writel(AUART_CTRL2_UARTEN | AUART_CTRL2_TXE,
+		     port->membase + AUART_CTRL2_SET);
+
+	uart_console_write(port, str, count, mxs_auart_console_putchar);
+
+	/* Finally, wait for transmitter to become empty ... */
+	while (readl(port->membase + AUART_STAT) & AUART_STAT_BUSY) {
+		udelay(1);
+		if (!to--)
+			break;
+	}
+
+	/*
+	 * ... and restore the TCR if we waited long enough for the transmitter
+	 * to be idle. This might keep the transmitter enabled although it is
+	 * unused, but that is better than to disable it while it is still
+	 * transmitting.
+	 */
+	if (!(readl(port->membase + AUART_STAT) & AUART_STAT_BUSY)) {
+		writel(old_ctrl0, port->membase + AUART_CTRL0);
+		writel(old_ctrl2, port->membase + AUART_CTRL2);
+	}
+
+	clk_disable(s->clk);
+}
+
+static void __init
+auart_console_get_options(struct uart_port *port, int *baud,
+			  int *parity, int *bits)
+{
+	unsigned int lcr_h, quot;
+
+	if (!(readl(port->membase + AUART_CTRL2) & AUART_CTRL2_UARTEN))
+		return;
+
+	lcr_h = readl(port->membase + AUART_LINECTRL);
+
+	*parity = 'n';
+	if (lcr_h & AUART_LINECTRL_PEN) {
+		if (lcr_h & AUART_LINECTRL_EPS)
+			*parity = 'e';
+		else
+			*parity = 'o';
+	}
+
+	if ((lcr_h & AUART_LINECTRL_WLEN_MASK) == AUART_LINECTRL_WLEN(2))
+		*bits = 7;
+	else
+		*bits = 8;
+
+	quot = ((readl(port->membase + AUART_LINECTRL)
+			& AUART_LINECTRL_BAUD_DIVINT_MASK))
+			    >> (AUART_LINECTRL_BAUD_DIVINT_SHIFT - 6);
+	quot |= ((readl(port->membase + AUART_LINECTRL)
+			& AUART_LINECTRL_BAUD_DIVFRAC_MASK))
+				>> AUART_LINECTRL_BAUD_DIVFRAC_SHIFT;
+	if (quot == 0)
+		quot = 1;
+
+	*baud = (port->uartclk << 2) / quot;
+}
+
+static int __init
+auart_console_setup(struct console *co, char *options)
+{
+	struct mxs_auart_port *s;
+	int baud = 9600;
+	int bits = 8;
+	int parity = 'n';
+	int flow = 'n';
+	int ret;
+
+	/*
+	 * 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 >= ARRAY_SIZE(auart_port))
+		co->index = 0;
+	s = auart_port[co->index];
+	if (!s)
+		return -ENODEV;
+
+	clk_prepare_enable(s->clk);
+
+	if (options)
+		uart_parse_options(options, &baud, &parity, &bits, &flow);
+	else
+		auart_console_get_options(&s->port, &baud, &parity, &bits);
+
+	ret = uart_set_options(&s->port, co, baud, parity, bits, flow);
+
+	clk_disable_unprepare(s->clk);
+
+	return ret;
+}
+
+static struct console auart_console = {
+	.name		= "ttyAPP",
+	.write		= auart_console_write,
+	.device		= uart_console_device,
+	.setup		= auart_console_setup,
+	.flags		= CON_PRINTBUFFER,
+	.index		= -1,
+	.data		= &auart_driver,
+};
+#endif
+
+static struct uart_driver auart_driver = {
+	.owner		= THIS_MODULE,
+	.driver_name	= "ttyAPP",
+	.dev_name	= "ttyAPP",
+	.major		= 0,
+	.minor		= 0,
+	.nr		= MXS_AUART_PORTS,
+#ifdef CONFIG_SERIAL_MXS_AUART_CONSOLE
+	.cons =		&auart_console,
+#endif
+};
+
+static int __devinit mxs_auart_probe(struct platform_device *pdev)
+{
+	struct mxs_auart_port *s;
+	u32 version;
+	int ret = 0;
+	struct resource *r;
+
+	s = kzalloc(sizeof(struct mxs_auart_port), GFP_KERNEL);
+	if (!s) {
+		ret = -ENOMEM;
+		goto out;
+	}
+
+	s->clk = clk_get(&pdev->dev, NULL);
+	if (IS_ERR(s->clk)) {
+		ret = PTR_ERR(s->clk);
+		goto out_free;
+	}
+
+	r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!r) {
+		ret = -ENXIO;
+		goto out_free_clk;
+	}
+
+	s->port.mapbase = r->start;
+	s->port.membase = ioremap(r->start, resource_size(r));
+	s->port.ops = &mxs_auart_ops;
+	s->port.iotype = UPIO_MEM;
+	s->port.line = pdev->id < 0 ? 0 : pdev->id;
+	s->port.fifosize = 16;
+	s->port.uartclk = clk_get_rate(s->clk);
+	s->port.type = PORT_IMX;
+	s->port.dev = s->dev = get_device(&pdev->dev);
+
+	s->flags = 0;
+	s->ctrl = 0;
+
+	s->irq = platform_get_irq(pdev, 0);
+	s->port.irq = s->irq;
+	ret = request_irq(s->irq, mxs_auart_irq_handle, 0, dev_name(&pdev->dev), s);
+	if (ret)
+		goto out_free_clk;
+
+	platform_set_drvdata(pdev, s);
+
+	auart_port[pdev->id] = s;
+
+	mxs_auart_reset(&s->port);
+
+	ret = uart_add_one_port(&auart_driver, &s->port);
+	if (ret)
+		goto out_free_irq;
+
+	version = readl(s->port.membase + AUART_VERSION);
+	dev_info(&pdev->dev, "Found APPUART %d.%d.%d\n",
+	       (version >> 24) & 0xff,
+	       (version >> 16) & 0xff, version & 0xffff);
+
+	return 0;
+
+out_free_irq:
+	auart_port[pdev->id] = NULL;
+	free_irq(s->irq, s);
+out_free_clk:
+	clk_put(s->clk);
+out_free:
+	kfree(s);
+out:
+	return ret;
+}
+
+static int __devexit mxs_auart_remove(struct platform_device *pdev)
+{
+	struct mxs_auart_port *s = platform_get_drvdata(pdev);
+
+	uart_remove_one_port(&auart_driver, &s->port);
+
+	auart_port[pdev->id] = NULL;
+
+	clk_put(s->clk);
+	free_irq(s->irq, s);
+	kfree(s);
+
+	return 0;
+}
+
+static struct platform_driver mxs_auart_driver = {
+	.probe = mxs_auart_probe,
+	.remove = __devexit_p(mxs_auart_remove),
+	.driver = {
+		.name = "mxs-auart",
+		.owner = THIS_MODULE,
+	},
+};
+
+static int __init mxs_auart_init(void)
+{
+	int r;
+
+	r = uart_register_driver(&auart_driver);
+	if (r)
+		goto out;
+
+	r = platform_driver_register(&mxs_auart_driver);
+	if (r)
+		goto out_err;
+
+	return 0;
+out_err:
+	uart_unregister_driver(&auart_driver);
+out:
+	return r;
+}
+
+static void __exit mxs_auart_exit(void)
+{
+	platform_driver_unregister(&mxs_auart_driver);
+	uart_unregister_driver(&auart_driver);
+}
+
+module_init(mxs_auart_init);
+module_exit(mxs_auart_exit);
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Freescale MXS application uart driver");
diff --git a/ap/os/linux/linux-3.4.x/drivers/tty/serial/netx-serial.c b/ap/os/linux/linux-3.4.x/drivers/tty/serial/netx-serial.c
new file mode 100644
index 0000000..d40da78
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/tty/serial/netx-serial.c
@@ -0,0 +1,748 @@
+/*
+ * Copyright (c) 2005 Sascha Hauer <s.hauer@pengutronix.de>, Pengutronix
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation.
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#if defined(CONFIG_SERIAL_NETX_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ)
+#define SUPPORT_SYSRQ
+#endif
+
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/ioport.h>
+#include <linux/init.h>
+#include <linux/console.h>
+#include <linux/sysrq.h>
+#include <linux/platform_device.h>
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
+#include <linux/serial_core.h>
+#include <linux/serial.h>
+
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <mach/hardware.h>
+#include <mach/netx-regs.h>
+
+/* We've been assigned a range on the "Low-density serial ports" major */
+#define SERIAL_NX_MAJOR	204
+#define MINOR_START	170
+
+enum uart_regs {
+	UART_DR              = 0x00,
+	UART_SR              = 0x04,
+	UART_LINE_CR         = 0x08,
+	UART_BAUDDIV_MSB     = 0x0c,
+	UART_BAUDDIV_LSB     = 0x10,
+	UART_CR              = 0x14,
+	UART_FR              = 0x18,
+	UART_IIR             = 0x1c,
+	UART_ILPR            = 0x20,
+	UART_RTS_CR          = 0x24,
+	UART_RTS_LEAD        = 0x28,
+	UART_RTS_TRAIL       = 0x2c,
+	UART_DRV_ENABLE      = 0x30,
+	UART_BRM_CR          = 0x34,
+	UART_RXFIFO_IRQLEVEL = 0x38,
+	UART_TXFIFO_IRQLEVEL = 0x3c,
+};
+
+#define SR_FE (1<<0)
+#define SR_PE (1<<1)
+#define SR_BE (1<<2)
+#define SR_OE (1<<3)
+
+#define LINE_CR_BRK       (1<<0)
+#define LINE_CR_PEN       (1<<1)
+#define LINE_CR_EPS       (1<<2)
+#define LINE_CR_STP2      (1<<3)
+#define LINE_CR_FEN       (1<<4)
+#define LINE_CR_5BIT      (0<<5)
+#define LINE_CR_6BIT      (1<<5)
+#define LINE_CR_7BIT      (2<<5)
+#define LINE_CR_8BIT      (3<<5)
+#define LINE_CR_BITS_MASK (3<<5)
+
+#define CR_UART_EN (1<<0)
+#define CR_SIREN   (1<<1)
+#define CR_SIRLP   (1<<2)
+#define CR_MSIE    (1<<3)
+#define CR_RIE     (1<<4)
+#define CR_TIE     (1<<5)
+#define CR_RTIE    (1<<6)
+#define CR_LBE     (1<<7)
+
+#define FR_CTS  (1<<0)
+#define FR_DSR  (1<<1)
+#define FR_DCD  (1<<2)
+#define FR_BUSY (1<<3)
+#define FR_RXFE (1<<4)
+#define FR_TXFF (1<<5)
+#define FR_RXFF (1<<6)
+#define FR_TXFE (1<<7)
+
+#define IIR_MIS (1<<0)
+#define IIR_RIS (1<<1)
+#define IIR_TIS (1<<2)
+#define IIR_RTIS (1<<3)
+#define IIR_MASK 0xf
+
+#define RTS_CR_AUTO (1<<0)
+#define RTS_CR_RTS  (1<<1)
+#define RTS_CR_COUNT (1<<2)
+#define RTS_CR_MOD2  (1<<3)
+#define RTS_CR_RTS_POL (1<<4)
+#define RTS_CR_CTS_CTR (1<<5)
+#define RTS_CR_CTS_POL (1<<6)
+#define RTS_CR_STICK   (1<<7)
+
+#define UART_PORT_SIZE 0x40
+#define DRIVER_NAME "netx-uart"
+
+struct netx_port {
+	struct uart_port	port;
+};
+
+static void netx_stop_tx(struct uart_port *port)
+{
+	unsigned int val;
+	val = readl(port->membase + UART_CR);
+	writel(val & ~CR_TIE,  port->membase + UART_CR);
+}
+
+static void netx_stop_rx(struct uart_port *port)
+{
+	unsigned int val;
+	val = readl(port->membase + UART_CR);
+	writel(val & ~CR_RIE,  port->membase + UART_CR);
+}
+
+static void netx_enable_ms(struct uart_port *port)
+{
+	unsigned int val;
+	val = readl(port->membase + UART_CR);
+	writel(val | CR_MSIE, port->membase + UART_CR);
+}
+
+static inline void netx_transmit_buffer(struct uart_port *port)
+{
+	struct circ_buf *xmit = &port->state->xmit;
+
+	if (port->x_char) {
+		writel(port->x_char, port->membase + UART_DR);
+		port->icount.tx++;
+		port->x_char = 0;
+		return;
+	}
+
+	if (uart_tx_stopped(port) || uart_circ_empty(xmit)) {
+		netx_stop_tx(port);
+		return;
+	}
+
+	do {
+		/* send xmit->buf[xmit->tail]
+		 * out the port here */
+		writel(xmit->buf[xmit->tail], port->membase + UART_DR);
+		xmit->tail = (xmit->tail + 1) &
+		         (UART_XMIT_SIZE - 1);
+		port->icount.tx++;
+		if (uart_circ_empty(xmit))
+			break;
+	} while (!(readl(port->membase + UART_FR) & FR_TXFF));
+
+	if (uart_circ_empty(xmit))
+		netx_stop_tx(port);
+}
+
+static void netx_start_tx(struct uart_port *port)
+{
+	writel(
+	    readl(port->membase + UART_CR) | CR_TIE, port->membase + UART_CR);
+
+	if (!(readl(port->membase + UART_FR) & FR_TXFF))
+		netx_transmit_buffer(port);
+}
+
+static unsigned int netx_tx_empty(struct uart_port *port)
+{
+	return readl(port->membase + UART_FR) & FR_BUSY ? 0 : TIOCSER_TEMT;
+}
+
+static void netx_txint(struct uart_port *port)
+{
+	struct circ_buf *xmit = &port->state->xmit;
+
+	if (uart_circ_empty(xmit) || uart_tx_stopped(port)) {
+		netx_stop_tx(port);
+		return;
+	}
+
+	netx_transmit_buffer(port);
+
+	if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
+		uart_write_wakeup(port);
+}
+
+static void netx_rxint(struct uart_port *port)
+{
+	unsigned char rx, flg, status;
+	struct tty_struct *tty = port->state->port.tty;
+
+	while (!(readl(port->membase + UART_FR) & FR_RXFE)) {
+		rx = readl(port->membase + UART_DR);
+		flg = TTY_NORMAL;
+		port->icount.rx++;
+		status = readl(port->membase + UART_SR);
+		if (status & SR_BE) {
+			writel(0, port->membase + UART_SR);
+			if (uart_handle_break(port))
+				continue;
+		}
+
+		if (unlikely(status & (SR_FE | SR_PE | SR_OE))) {
+
+			if (status & SR_PE)
+				port->icount.parity++;
+			else if (status & SR_FE)
+				port->icount.frame++;
+			if (status & SR_OE)
+				port->icount.overrun++;
+
+			status &= port->read_status_mask;
+
+			if (status & SR_BE)
+				flg = TTY_BREAK;
+			else if (status & SR_PE)
+				flg = TTY_PARITY;
+			else if (status & SR_FE)
+				flg = TTY_FRAME;
+		}
+
+		if (uart_handle_sysrq_char(port, rx))
+			continue;
+
+		uart_insert_char(port, status, SR_OE, rx, flg);
+	}
+
+	tty_flip_buffer_push(tty);
+	return;
+}
+
+static irqreturn_t netx_int(int irq, void *dev_id)
+{
+	struct uart_port *port = dev_id;
+	unsigned long flags;
+	unsigned char status;
+
+	spin_lock_irqsave(&port->lock,flags);
+
+	status = readl(port->membase + UART_IIR) & IIR_MASK;
+	while (status) {
+		if (status & IIR_RIS)
+			netx_rxint(port);
+		if (status & IIR_TIS)
+			netx_txint(port);
+		if (status & IIR_MIS) {
+			if (readl(port->membase + UART_FR) & FR_CTS)
+				uart_handle_cts_change(port, 1);
+			else
+				uart_handle_cts_change(port, 0);
+		}
+		writel(0, port->membase + UART_IIR);
+		status = readl(port->membase + UART_IIR) & IIR_MASK;
+	}
+
+	spin_unlock_irqrestore(&port->lock,flags);
+	return IRQ_HANDLED;
+}
+
+static unsigned int netx_get_mctrl(struct uart_port *port)
+{
+	unsigned int ret = TIOCM_DSR | TIOCM_CAR;
+
+	if (readl(port->membase + UART_FR) & FR_CTS)
+		ret |= TIOCM_CTS;
+
+	return ret;
+}
+
+static void netx_set_mctrl(struct uart_port *port, unsigned int mctrl)
+{
+	unsigned int val;
+
+	/* FIXME: Locking needed ? */
+	if (mctrl & TIOCM_RTS) {
+		val = readl(port->membase + UART_RTS_CR);
+		writel(val | RTS_CR_RTS, port->membase + UART_RTS_CR);
+	}
+}
+
+static void netx_break_ctl(struct uart_port *port, int break_state)
+{
+	unsigned int line_cr;
+	spin_lock_irq(&port->lock);
+
+	line_cr = readl(port->membase + UART_LINE_CR);
+	if (break_state != 0)
+		line_cr |= LINE_CR_BRK;
+	else
+		line_cr &= ~LINE_CR_BRK;
+	writel(line_cr, port->membase + UART_LINE_CR);
+
+	spin_unlock_irq(&port->lock);
+}
+
+static int netx_startup(struct uart_port *port)
+{
+	int ret;
+
+	ret = request_irq(port->irq, netx_int, 0,
+			     DRIVER_NAME, port);
+	if (ret) {
+		dev_err(port->dev, "unable to grab irq%d\n",port->irq);
+		goto exit;
+	}
+
+	writel(readl(port->membase + UART_LINE_CR) | LINE_CR_FEN,
+		port->membase + UART_LINE_CR);
+
+	writel(CR_MSIE | CR_RIE | CR_TIE | CR_RTIE | CR_UART_EN,
+		port->membase + UART_CR);
+
+exit:
+	return ret;
+}
+
+static void netx_shutdown(struct uart_port *port)
+{
+	writel(0, port->membase + UART_CR) ;
+
+	free_irq(port->irq, port);
+}
+
+static void
+netx_set_termios(struct uart_port *port, struct ktermios *termios,
+		   struct ktermios *old)
+{
+	unsigned int baud, quot;
+	unsigned char old_cr;
+	unsigned char line_cr = LINE_CR_FEN;
+	unsigned char rts_cr = 0;
+
+	switch (termios->c_cflag & CSIZE) {
+	case CS5:
+		line_cr |= LINE_CR_5BIT;
+		break;
+	case CS6:
+		line_cr |= LINE_CR_6BIT;
+		break;
+	case CS7:
+		line_cr |= LINE_CR_7BIT;
+		break;
+	case CS8:
+		line_cr |= LINE_CR_8BIT;
+		break;
+	}
+
+	if (termios->c_cflag & CSTOPB)
+		line_cr |= LINE_CR_STP2;
+
+	if (termios->c_cflag & PARENB) {
+		line_cr |= LINE_CR_PEN;
+		if (!(termios->c_cflag & PARODD))
+			line_cr |= LINE_CR_EPS;
+	}
+
+	if (termios->c_cflag & CRTSCTS)
+		rts_cr = RTS_CR_AUTO | RTS_CR_CTS_CTR | RTS_CR_RTS_POL;
+
+	baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk/16);
+	quot = baud * 4096;
+	quot /= 1000;
+	quot *= 256;
+	quot /= 100000;
+
+	spin_lock_irq(&port->lock);
+
+	uart_update_timeout(port, termios->c_cflag, baud);
+
+	old_cr = readl(port->membase + UART_CR);
+
+	/* disable interrupts */
+	writel(old_cr & ~(CR_MSIE | CR_RIE | CR_TIE | CR_RTIE),
+		port->membase + UART_CR);
+
+	/* drain transmitter */
+	while (readl(port->membase + UART_FR) & FR_BUSY);
+
+	/* disable UART */
+	writel(old_cr & ~CR_UART_EN, port->membase + UART_CR);
+
+	/* modem status interrupts */
+	old_cr &= ~CR_MSIE;
+	if (UART_ENABLE_MS(port, termios->c_cflag))
+		old_cr |= CR_MSIE;
+
+	writel((quot>>8) & 0xff, port->membase + UART_BAUDDIV_MSB);
+	writel(quot & 0xff, port->membase + UART_BAUDDIV_LSB);
+	writel(line_cr, port->membase + UART_LINE_CR);
+
+	writel(rts_cr, port->membase + UART_RTS_CR);
+
+	/*
+	 * Characters to ignore
+	 */
+	port->ignore_status_mask = 0;
+	if (termios->c_iflag & IGNPAR)
+		port->ignore_status_mask |= SR_PE;
+	if (termios->c_iflag & IGNBRK) {
+		port->ignore_status_mask |= SR_BE;
+		/*
+		 * If we're ignoring parity and break indicators,
+		 * ignore overruns too (for real raw support).
+		 */
+		if (termios->c_iflag & IGNPAR)
+			port->ignore_status_mask |= SR_PE;
+	}
+
+	port->read_status_mask = 0;
+	if (termios->c_iflag & (BRKINT | PARMRK))
+		port->read_status_mask |= SR_BE;
+	if (termios->c_iflag & INPCK)
+		port->read_status_mask |= SR_PE | SR_FE;
+
+	writel(old_cr, port->membase + UART_CR);
+
+	spin_unlock_irq(&port->lock);
+}
+
+static const char *netx_type(struct uart_port *port)
+{
+	return port->type == PORT_NETX ? "NETX" : NULL;
+}
+
+static void netx_release_port(struct uart_port *port)
+{
+	release_mem_region(port->mapbase, UART_PORT_SIZE);
+}
+
+static int netx_request_port(struct uart_port *port)
+{
+	return request_mem_region(port->mapbase, UART_PORT_SIZE,
+			DRIVER_NAME) != NULL ? 0 : -EBUSY;
+}
+
+static void netx_config_port(struct uart_port *port, int flags)
+{
+	if (flags & UART_CONFIG_TYPE && netx_request_port(port) == 0)
+		port->type = PORT_NETX;
+}
+
+static int
+netx_verify_port(struct uart_port *port, struct serial_struct *ser)
+{
+	int ret = 0;
+
+	if (ser->type != PORT_UNKNOWN && ser->type != PORT_NETX)
+		ret = -EINVAL;
+
+	return ret;
+}
+
+static struct uart_ops netx_pops = {
+	.tx_empty	= netx_tx_empty,
+	.set_mctrl	= netx_set_mctrl,
+	.get_mctrl	= netx_get_mctrl,
+	.stop_tx	= netx_stop_tx,
+	.start_tx	= netx_start_tx,
+	.stop_rx	= netx_stop_rx,
+	.enable_ms	= netx_enable_ms,
+	.break_ctl	= netx_break_ctl,
+	.startup	= netx_startup,
+	.shutdown	= netx_shutdown,
+	.set_termios	= netx_set_termios,
+	.type		= netx_type,
+	.release_port	= netx_release_port,
+	.request_port	= netx_request_port,
+	.config_port	= netx_config_port,
+	.verify_port	= netx_verify_port,
+};
+
+static struct netx_port netx_ports[] = {
+	{
+	.port = {
+		.type = PORT_NETX,
+		.iotype = UPIO_MEM,
+		.membase = (char __iomem *)io_p2v(NETX_PA_UART0),
+		.mapbase = NETX_PA_UART0,
+		.irq = NETX_IRQ_UART0,
+		.uartclk = 100000000,
+		.fifosize = 16,
+		.flags = UPF_BOOT_AUTOCONF,
+		.ops = &netx_pops,
+		.line = 0,
+	},
+	}, {
+	.port = {
+		.type = PORT_NETX,
+		.iotype = UPIO_MEM,
+		.membase = (char __iomem *)io_p2v(NETX_PA_UART1),
+		.mapbase = NETX_PA_UART1,
+		.irq = NETX_IRQ_UART1,
+		.uartclk = 100000000,
+		.fifosize = 16,
+		.flags = UPF_BOOT_AUTOCONF,
+		.ops = &netx_pops,
+		.line = 1,
+	},
+	}, {
+	.port = {
+		.type = PORT_NETX,
+		.iotype = UPIO_MEM,
+		.membase = (char __iomem *)io_p2v(NETX_PA_UART2),
+		.mapbase = NETX_PA_UART2,
+		.irq = NETX_IRQ_UART2,
+		.uartclk = 100000000,
+		.fifosize = 16,
+		.flags = UPF_BOOT_AUTOCONF,
+		.ops = &netx_pops,
+		.line = 2,
+	},
+	}
+};
+
+#ifdef CONFIG_SERIAL_NETX_CONSOLE
+
+static void netx_console_putchar(struct uart_port *port, int ch)
+{
+	while (readl(port->membase + UART_FR) & FR_BUSY);
+	writel(ch, port->membase + UART_DR);
+}
+
+static void
+netx_console_write(struct console *co, const char *s, unsigned int count)
+{
+	struct uart_port *port = &netx_ports[co->index].port;
+	unsigned char cr_save;
+
+	cr_save = readl(port->membase + UART_CR);
+	writel(cr_save | CR_UART_EN, port->membase + UART_CR);
+
+	uart_console_write(port, s, count, netx_console_putchar);
+
+	while (readl(port->membase + UART_FR) & FR_BUSY);
+	writel(cr_save, port->membase + UART_CR);
+}
+
+static void __init
+netx_console_get_options(struct uart_port *port, int *baud,
+			int *parity, int *bits, int *flow)
+{
+	unsigned char line_cr;
+
+	*baud = (readl(port->membase + UART_BAUDDIV_MSB) << 8) |
+		readl(port->membase + UART_BAUDDIV_LSB);
+	*baud *= 1000;
+	*baud /= 4096;
+	*baud *= 1000;
+	*baud /= 256;
+	*baud *= 100;
+
+	line_cr = readl(port->membase + UART_LINE_CR);
+	*parity = 'n';
+	if (line_cr & LINE_CR_PEN) {
+		if (line_cr & LINE_CR_EPS)
+			*parity = 'e';
+		else
+			*parity = 'o';
+	}
+
+	switch (line_cr & LINE_CR_BITS_MASK) {
+	case LINE_CR_8BIT:
+		*bits = 8;
+		break;
+	case LINE_CR_7BIT:
+		*bits = 7;
+		break;
+	case LINE_CR_6BIT:
+		*bits = 6;
+		break;
+	case LINE_CR_5BIT:
+		*bits = 5;
+		break;
+	}
+
+	if (readl(port->membase + UART_RTS_CR) & RTS_CR_AUTO)
+		*flow = 'r';
+}
+
+static int __init
+netx_console_setup(struct console *co, char *options)
+{
+	struct netx_port *sport;
+	int baud = 9600;
+	int bits = 8;
+	int parity = 'n';
+	int flow = 'n';
+
+	/*
+	 * 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 >= ARRAY_SIZE(netx_ports))
+		co->index = 0;
+	sport = &netx_ports[co->index];
+
+	if (options) {
+		uart_parse_options(options, &baud, &parity, &bits, &flow);
+	} else {
+		/* if the UART is enabled, assume it has been correctly setup
+		 * by the bootloader and get the options
+		 */
+		if (readl(sport->port.membase + UART_CR) & CR_UART_EN) {
+			netx_console_get_options(&sport->port, &baud,
+			&parity, &bits, &flow);
+		}
+
+	}
+
+	return uart_set_options(&sport->port, co, baud, parity, bits, flow);
+}
+
+static struct uart_driver netx_reg;
+static struct console netx_console = {
+	.name		= "ttyNX",
+	.write		= netx_console_write,
+	.device		= uart_console_device,
+	.setup		= netx_console_setup,
+	.flags		= CON_PRINTBUFFER,
+	.index		= -1,
+	.data		= &netx_reg,
+};
+
+static int __init netx_console_init(void)
+{
+	register_console(&netx_console);
+	return 0;
+}
+console_initcall(netx_console_init);
+
+#define NETX_CONSOLE	&netx_console
+#else
+#define NETX_CONSOLE	NULL
+#endif
+
+static struct uart_driver netx_reg = {
+	.owner          = THIS_MODULE,
+	.driver_name    = DRIVER_NAME,
+	.dev_name       = "ttyNX",
+	.major          = SERIAL_NX_MAJOR,
+	.minor          = MINOR_START,
+	.nr             = ARRAY_SIZE(netx_ports),
+	.cons           = NETX_CONSOLE,
+};
+
+static int serial_netx_suspend(struct platform_device *pdev, pm_message_t state)
+{
+	struct netx_port *sport = platform_get_drvdata(pdev);
+
+	if (sport)
+		uart_suspend_port(&netx_reg, &sport->port);
+
+	return 0;
+}
+
+static int serial_netx_resume(struct platform_device *pdev)
+{
+	struct netx_port *sport = platform_get_drvdata(pdev);
+
+	if (sport)
+		uart_resume_port(&netx_reg, &sport->port);
+
+	return 0;
+}
+
+static int serial_netx_probe(struct platform_device *pdev)
+{
+	struct uart_port *port = &netx_ports[pdev->id].port;
+
+	dev_info(&pdev->dev, "initialising\n");
+
+	port->dev = &pdev->dev;
+
+	writel(1, port->membase + UART_RXFIFO_IRQLEVEL);
+	uart_add_one_port(&netx_reg, &netx_ports[pdev->id].port);
+	platform_set_drvdata(pdev, &netx_ports[pdev->id]);
+
+	return 0;
+}
+
+static int serial_netx_remove(struct platform_device *pdev)
+{
+	struct netx_port *sport = platform_get_drvdata(pdev);
+
+	platform_set_drvdata(pdev, NULL);
+
+	if (sport)
+		uart_remove_one_port(&netx_reg, &sport->port);
+
+	return 0;
+}
+
+static struct platform_driver serial_netx_driver = {
+	.probe          = serial_netx_probe,
+	.remove         = serial_netx_remove,
+
+	.suspend	= serial_netx_suspend,
+	.resume		= serial_netx_resume,
+
+	.driver		= {
+		.name   = DRIVER_NAME,
+		.owner	= THIS_MODULE,
+	},
+};
+
+static int __init netx_serial_init(void)
+{
+	int ret;
+
+	printk(KERN_INFO "Serial: NetX driver\n");
+
+	ret = uart_register_driver(&netx_reg);
+	if (ret)
+		return ret;
+
+	ret = platform_driver_register(&serial_netx_driver);
+	if (ret != 0)
+		uart_unregister_driver(&netx_reg);
+
+	return 0;
+}
+
+static void __exit netx_serial_exit(void)
+{
+	platform_driver_unregister(&serial_netx_driver);
+	uart_unregister_driver(&netx_reg);
+}
+
+module_init(netx_serial_init);
+module_exit(netx_serial_exit);
+
+MODULE_AUTHOR("Sascha Hauer");
+MODULE_DESCRIPTION("NetX serial port driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:" DRIVER_NAME);
diff --git a/ap/os/linux/linux-3.4.x/drivers/tty/serial/nwpserial.c b/ap/os/linux/linux-3.4.x/drivers/tty/serial/nwpserial.c
new file mode 100644
index 0000000..dd4c31d
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/tty/serial/nwpserial.c
@@ -0,0 +1,479 @@
+/*
+ *  Serial Port driver for a NWP uart device
+ *
+ *    Copyright (C) 2008 IBM Corp., Benjamin Krill <ben@codiert.org>
+ *
+ *  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.
+ *
+ */
+#include <linux/init.h>
+#include <linux/export.h>
+#include <linux/console.h>
+#include <linux/serial.h>
+#include <linux/serial_reg.h>
+#include <linux/serial_core.h>
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
+#include <linux/irqreturn.h>
+#include <linux/mutex.h>
+#include <linux/of_platform.h>
+#include <linux/of_device.h>
+#include <linux/nwpserial.h>
+#include <asm/prom.h>
+#include <asm/dcr.h>
+
+#define NWPSERIAL_NR               2
+
+#define NWPSERIAL_STATUS_RXVALID 0x1
+#define NWPSERIAL_STATUS_TXFULL  0x2
+
+struct nwpserial_port {
+	struct uart_port port;
+	dcr_host_t dcr_host;
+	unsigned int ier;
+	unsigned int mcr;
+};
+
+static DEFINE_MUTEX(nwpserial_mutex);
+static struct nwpserial_port nwpserial_ports[NWPSERIAL_NR];
+
+static void wait_for_bits(struct nwpserial_port *up, int bits)
+{
+	unsigned int status, tmout = 10000;
+
+	/* Wait up to 10ms for the character(s) to be sent. */
+	do {
+		status = dcr_read(up->dcr_host, UART_LSR);
+
+		if (--tmout == 0)
+			break;
+		udelay(1);
+	} while ((status & bits) != bits);
+}
+
+#ifdef CONFIG_SERIAL_OF_PLATFORM_NWPSERIAL_CONSOLE
+static void nwpserial_console_putchar(struct uart_port *port, int c)
+{
+	struct nwpserial_port *up;
+	up = container_of(port, struct nwpserial_port, port);
+	/* check if tx buffer is full */
+	wait_for_bits(up, UART_LSR_THRE);
+	dcr_write(up->dcr_host, UART_TX, c);
+	up->port.icount.tx++;
+}
+
+static void
+nwpserial_console_write(struct console *co, const char *s, unsigned int count)
+{
+	struct nwpserial_port *up = &nwpserial_ports[co->index];
+	unsigned long flags;
+	int locked = 1;
+
+	if (oops_in_progress)
+		locked = spin_trylock_irqsave(&up->port.lock, flags);
+	else
+		spin_lock_irqsave(&up->port.lock, flags);
+
+	/* save and disable interrupt */
+	up->ier = dcr_read(up->dcr_host, UART_IER);
+	dcr_write(up->dcr_host, UART_IER, up->ier & ~UART_IER_RDI);
+
+	uart_console_write(&up->port, s, count, nwpserial_console_putchar);
+
+	/* wait for transmitter to become empty */
+	while ((dcr_read(up->dcr_host, UART_LSR) & UART_LSR_THRE) == 0)
+		cpu_relax();
+
+	/* restore interrupt state */
+	dcr_write(up->dcr_host, UART_IER, up->ier);
+
+	if (locked)
+		spin_unlock_irqrestore(&up->port.lock, flags);
+}
+
+static struct uart_driver nwpserial_reg;
+static struct console nwpserial_console = {
+	.name		= "ttySQ",
+	.write		= nwpserial_console_write,
+	.device		= uart_console_device,
+	.flags		= CON_PRINTBUFFER,
+	.index		= -1,
+	.data		= &nwpserial_reg,
+};
+#define NWPSERIAL_CONSOLE	(&nwpserial_console)
+#else
+#define NWPSERIAL_CONSOLE	NULL
+#endif /* CONFIG_SERIAL_OF_PLATFORM_NWPSERIAL_CONSOLE */
+
+/**************************************************************************/
+
+static int nwpserial_request_port(struct uart_port *port)
+{
+	return 0;
+}
+
+static void nwpserial_release_port(struct uart_port *port)
+{
+	/* N/A */
+}
+
+static void nwpserial_config_port(struct uart_port *port, int flags)
+{
+	port->type = PORT_NWPSERIAL;
+}
+
+static irqreturn_t nwpserial_interrupt(int irq, void *dev_id)
+{
+	struct nwpserial_port *up = dev_id;
+	struct tty_struct *tty = up->port.state->port.tty;
+	irqreturn_t ret;
+	unsigned int iir;
+	unsigned char ch;
+
+	spin_lock(&up->port.lock);
+
+	/* check if the uart was the interrupt source. */
+	iir = dcr_read(up->dcr_host, UART_IIR);
+	if (!iir) {
+		ret = IRQ_NONE;
+		goto out;
+	}
+
+	do {
+		up->port.icount.rx++;
+		ch = dcr_read(up->dcr_host, UART_RX);
+		if (up->port.ignore_status_mask != NWPSERIAL_STATUS_RXVALID)
+			tty_insert_flip_char(tty, ch, TTY_NORMAL);
+	} while (dcr_read(up->dcr_host, UART_LSR) & UART_LSR_DR);
+
+	tty_flip_buffer_push(tty);
+	ret = IRQ_HANDLED;
+
+	/* clear interrupt */
+	dcr_write(up->dcr_host, UART_IIR, 1);
+out:
+	spin_unlock(&up->port.lock);
+	return ret;
+}
+
+static int nwpserial_startup(struct uart_port *port)
+{
+	struct nwpserial_port *up;
+	int err;
+
+	up = container_of(port, struct nwpserial_port, port);
+
+	/* disable flow control by default */
+	up->mcr = dcr_read(up->dcr_host, UART_MCR) & ~UART_MCR_AFE;
+	dcr_write(up->dcr_host, UART_MCR, up->mcr);
+
+	/* register interrupt handler */
+	err = request_irq(up->port.irq, nwpserial_interrupt,
+			IRQF_SHARED, "nwpserial", up);
+	if (err)
+		return err;
+
+	/* enable interrupts */
+	up->ier = UART_IER_RDI;
+	dcr_write(up->dcr_host, UART_IER, up->ier);
+
+	/* enable receiving */
+	up->port.ignore_status_mask &= ~NWPSERIAL_STATUS_RXVALID;
+
+	return 0;
+}
+
+static void nwpserial_shutdown(struct uart_port *port)
+{
+	struct nwpserial_port *up;
+	up = container_of(port, struct nwpserial_port, port);
+
+	/* disable receiving */
+	up->port.ignore_status_mask |= NWPSERIAL_STATUS_RXVALID;
+
+	/* disable interrupts from this port */
+	up->ier = 0;
+	dcr_write(up->dcr_host, UART_IER, up->ier);
+
+	/* free irq */
+	free_irq(up->port.irq, port);
+}
+
+static int nwpserial_verify_port(struct uart_port *port,
+			struct serial_struct *ser)
+{
+	return -EINVAL;
+}
+
+static const char *nwpserial_type(struct uart_port *port)
+{
+	return port->type == PORT_NWPSERIAL ? "nwpserial" : NULL;
+}
+
+static void nwpserial_set_termios(struct uart_port *port,
+			struct ktermios *termios, struct ktermios *old)
+{
+	struct nwpserial_port *up;
+	up = container_of(port, struct nwpserial_port, port);
+
+	up->port.read_status_mask = NWPSERIAL_STATUS_RXVALID
+				| NWPSERIAL_STATUS_TXFULL;
+
+	up->port.ignore_status_mask = 0;
+	/* ignore all characters if CREAD is not set */
+	if ((termios->c_cflag & CREAD) == 0)
+		up->port.ignore_status_mask |= NWPSERIAL_STATUS_RXVALID;
+
+	/* Copy back the old hardware settings */
+	if (old)
+		tty_termios_copy_hw(termios, old);
+}
+
+static void nwpserial_break_ctl(struct uart_port *port, int ctl)
+{
+	/* N/A */
+}
+
+static void nwpserial_enable_ms(struct uart_port *port)
+{
+	/* N/A */
+}
+
+static void nwpserial_stop_rx(struct uart_port *port)
+{
+	struct nwpserial_port *up;
+	up = container_of(port, struct nwpserial_port, port);
+	/* don't forward any more data (like !CREAD) */
+	up->port.ignore_status_mask = NWPSERIAL_STATUS_RXVALID;
+}
+
+static void nwpserial_putchar(struct nwpserial_port *up, unsigned char c)
+{
+	/* check if tx buffer is full */
+	wait_for_bits(up, UART_LSR_THRE);
+	dcr_write(up->dcr_host, UART_TX, c);
+	up->port.icount.tx++;
+}
+
+static void nwpserial_start_tx(struct uart_port *port)
+{
+	struct nwpserial_port *up;
+	struct circ_buf *xmit;
+	up = container_of(port, struct nwpserial_port, port);
+	xmit  = &up->port.state->xmit;
+
+	if (port->x_char) {
+		nwpserial_putchar(up, up->port.x_char);
+		port->x_char = 0;
+	}
+
+	while (!(uart_circ_empty(xmit) || uart_tx_stopped(&up->port))) {
+		nwpserial_putchar(up, xmit->buf[xmit->tail]);
+		xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE-1);
+	}
+}
+
+static unsigned int nwpserial_get_mctrl(struct uart_port *port)
+{
+	return 0;
+}
+
+static void nwpserial_set_mctrl(struct uart_port *port, unsigned int mctrl)
+{
+	/* N/A */
+}
+
+static void nwpserial_stop_tx(struct uart_port *port)
+{
+	/* N/A */
+}
+
+static unsigned int nwpserial_tx_empty(struct uart_port *port)
+{
+	struct nwpserial_port *up;
+	unsigned long flags;
+	int ret;
+	up = container_of(port, struct nwpserial_port, port);
+
+	spin_lock_irqsave(&up->port.lock, flags);
+	ret = dcr_read(up->dcr_host, UART_LSR);
+	spin_unlock_irqrestore(&up->port.lock, flags);
+
+	return ret & UART_LSR_TEMT ? TIOCSER_TEMT : 0;
+}
+
+static struct uart_ops nwpserial_pops = {
+	.tx_empty     = nwpserial_tx_empty,
+	.set_mctrl    = nwpserial_set_mctrl,
+	.get_mctrl    = nwpserial_get_mctrl,
+	.stop_tx      = nwpserial_stop_tx,
+	.start_tx     = nwpserial_start_tx,
+	.stop_rx      = nwpserial_stop_rx,
+	.enable_ms    = nwpserial_enable_ms,
+	.break_ctl    = nwpserial_break_ctl,
+	.startup      = nwpserial_startup,
+	.shutdown     = nwpserial_shutdown,
+	.set_termios  = nwpserial_set_termios,
+	.type         = nwpserial_type,
+	.release_port = nwpserial_release_port,
+	.request_port = nwpserial_request_port,
+	.config_port  = nwpserial_config_port,
+	.verify_port  = nwpserial_verify_port,
+};
+
+static struct uart_driver nwpserial_reg = {
+	.owner       = THIS_MODULE,
+	.driver_name = "nwpserial",
+	.dev_name    = "ttySQ",
+	.major       = TTY_MAJOR,
+	.minor       = 68,
+	.nr          = NWPSERIAL_NR,
+	.cons        = NWPSERIAL_CONSOLE,
+};
+
+int nwpserial_register_port(struct uart_port *port)
+{
+	struct nwpserial_port *up = NULL;
+	int ret = -1;
+	int i;
+	static int first = 1;
+	int dcr_len;
+	int dcr_base;
+	struct device_node *dn;
+
+	mutex_lock(&nwpserial_mutex);
+
+	dn = port->dev->of_node;
+	if (dn == NULL)
+		goto out;
+
+	/* get dcr base. */
+	dcr_base = dcr_resource_start(dn, 0);
+
+	/* find matching entry */
+	for (i = 0; i < NWPSERIAL_NR; i++)
+		if (nwpserial_ports[i].port.iobase == dcr_base) {
+			up = &nwpserial_ports[i];
+			break;
+		}
+
+	/* we didn't find a mtching entry, search for a free port */
+	if (up == NULL)
+		for (i = 0; i < NWPSERIAL_NR; i++)
+			if (nwpserial_ports[i].port.type == PORT_UNKNOWN &&
+				nwpserial_ports[i].port.iobase == 0) {
+				up = &nwpserial_ports[i];
+				break;
+			}
+
+	if (up == NULL) {
+		ret = -EBUSY;
+		goto out;
+	}
+
+	if (first)
+		uart_register_driver(&nwpserial_reg);
+	first = 0;
+
+	up->port.membase      = port->membase;
+	up->port.irq          = port->irq;
+	up->port.uartclk      = port->uartclk;
+	up->port.fifosize     = port->fifosize;
+	up->port.regshift     = port->regshift;
+	up->port.iotype       = port->iotype;
+	up->port.flags        = port->flags;
+	up->port.mapbase      = port->mapbase;
+	up->port.private_data = port->private_data;
+
+	if (port->dev)
+		up->port.dev = port->dev;
+
+	if (up->port.iobase != dcr_base) {
+		up->port.ops          = &nwpserial_pops;
+		up->port.fifosize     = 16;
+
+		spin_lock_init(&up->port.lock);
+
+		up->port.iobase = dcr_base;
+		dcr_len = dcr_resource_len(dn, 0);
+
+		up->dcr_host = dcr_map(dn, dcr_base, dcr_len);
+		if (!DCR_MAP_OK(up->dcr_host)) {
+			printk(KERN_ERR "Cannot map DCR resources for NWPSERIAL");
+			goto out;
+		}
+	}
+
+	ret = uart_add_one_port(&nwpserial_reg, &up->port);
+	if (ret == 0)
+		ret = up->port.line;
+
+out:
+	mutex_unlock(&nwpserial_mutex);
+
+	return ret;
+}
+EXPORT_SYMBOL(nwpserial_register_port);
+
+void nwpserial_unregister_port(int line)
+{
+	struct nwpserial_port *up = &nwpserial_ports[line];
+	mutex_lock(&nwpserial_mutex);
+	uart_remove_one_port(&nwpserial_reg, &up->port);
+
+	up->port.type = PORT_UNKNOWN;
+
+	mutex_unlock(&nwpserial_mutex);
+}
+EXPORT_SYMBOL(nwpserial_unregister_port);
+
+#ifdef CONFIG_SERIAL_OF_PLATFORM_NWPSERIAL_CONSOLE
+static int __init nwpserial_console_init(void)
+{
+	struct nwpserial_port *up = NULL;
+	struct device_node *dn;
+	const char *name;
+	int dcr_base;
+	int dcr_len;
+	int i;
+
+	/* search for a free port */
+	for (i = 0; i < NWPSERIAL_NR; i++)
+		if (nwpserial_ports[i].port.type == PORT_UNKNOWN) {
+			up = &nwpserial_ports[i];
+			break;
+		}
+
+	if (up == NULL)
+		return -1;
+
+	name = of_get_property(of_chosen, "linux,stdout-path", NULL);
+	if (name == NULL)
+		return -1;
+
+	dn = of_find_node_by_path(name);
+	if (!dn)
+		return -1;
+
+	spin_lock_init(&up->port.lock);
+	up->port.ops = &nwpserial_pops;
+	up->port.type = PORT_NWPSERIAL;
+	up->port.fifosize = 16;
+
+	dcr_base = dcr_resource_start(dn, 0);
+	dcr_len = dcr_resource_len(dn, 0);
+	up->port.iobase = dcr_base;
+
+	up->dcr_host = dcr_map(dn, dcr_base, dcr_len);
+	if (!DCR_MAP_OK(up->dcr_host)) {
+		printk("Cannot map DCR resources for SERIAL");
+		return -1;
+	}
+	register_console(&nwpserial_console);
+	return 0;
+}
+console_initcall(nwpserial_console_init);
+#endif /* CONFIG_SERIAL_OF_PLATFORM_NWPSERIAL_CONSOLE */
diff --git a/ap/os/linux/linux-3.4.x/drivers/tty/serial/of_serial.c b/ap/os/linux/linux-3.4.x/drivers/tty/serial/of_serial.c
new file mode 100644
index 0000000..746e771
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/tty/serial/of_serial.c
@@ -0,0 +1,212 @@
+/*
+ *  Serial Port driver for Open Firmware platform devices
+ *
+ *    Copyright (C) 2006 Arnd Bergmann <arnd@arndb.de>, IBM Corp.
+ *
+ *  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.
+ *
+ */
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/serial_core.h>
+#include <linux/serial_8250.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+#include <linux/of_platform.h>
+#include <linux/nwpserial.h>
+
+struct of_serial_info {
+	int type;
+	int line;
+};
+
+/*
+ * Fill a struct uart_port for a given device node
+ */
+static int __devinit of_platform_serial_setup(struct platform_device *ofdev,
+					int type, struct uart_port *port)
+{
+	struct resource resource;
+	struct device_node *np = ofdev->dev.of_node;
+	u32 clk, spd, prop;
+	int ret;
+
+	memset(port, 0, sizeof *port);
+	if (of_property_read_u32(np, "clock-frequency", &clk)) {
+		dev_warn(&ofdev->dev, "no clock-frequency property set\n");
+		return -ENODEV;
+	}
+	/* If current-speed was set, then try not to change it. */
+	if (of_property_read_u32(np, "current-speed", &spd) == 0)
+		port->custom_divisor = clk / (16 * spd);
+
+	ret = of_address_to_resource(np, 0, &resource);
+	if (ret) {
+		dev_warn(&ofdev->dev, "invalid address\n");
+		return ret;
+	}
+
+	spin_lock_init(&port->lock);
+	port->mapbase = resource.start;
+
+	/* Check for shifted address mapping */
+	if (of_property_read_u32(np, "reg-offset", &prop) == 0)
+		port->mapbase += prop;
+
+	/* Check for registers offset within the devices address range */
+	if (of_property_read_u32(np, "reg-shift", &prop) == 0)
+		port->regshift = prop;
+
+	port->irq = irq_of_parse_and_map(np, 0);
+	port->iotype = UPIO_MEM;
+	if (of_property_read_u32(np, "reg-io-width", &prop) == 0) {
+		switch (prop) {
+		case 1:
+			port->iotype = UPIO_MEM;
+			break;
+		case 4:
+			port->iotype = UPIO_MEM32;
+			break;
+		default:
+			dev_warn(&ofdev->dev, "unsupported reg-io-width (%d)\n",
+				 prop);
+			return -EINVAL;
+		}
+	}
+
+	port->type = type;
+	port->uartclk = clk;
+	port->flags = UPF_SHARE_IRQ | UPF_BOOT_AUTOCONF | UPF_IOREMAP
+		| UPF_FIXED_PORT | UPF_FIXED_TYPE;
+	port->dev = &ofdev->dev;
+
+	return 0;
+}
+
+/*
+ * Try to register a serial port
+ */
+static struct of_device_id of_platform_serial_table[];
+static int __devinit of_platform_serial_probe(struct platform_device *ofdev)
+{
+	const struct of_device_id *match;
+	struct of_serial_info *info;
+	struct uart_port port;
+	int port_type;
+	int ret;
+
+	match = of_match_device(of_platform_serial_table, &ofdev->dev);
+	if (!match)
+		return -EINVAL;
+
+	if (of_find_property(ofdev->dev.of_node, "used-by-rtas", NULL))
+		return -EBUSY;
+
+	info = kmalloc(sizeof(*info), GFP_KERNEL);
+	if (info == NULL)
+		return -ENOMEM;
+
+	port_type = (unsigned long)match->data;
+	ret = of_platform_serial_setup(ofdev, port_type, &port);
+	if (ret)
+		goto out;
+
+	switch (port_type) {
+#ifdef CONFIG_SERIAL_8250
+	case PORT_8250 ... PORT_MAX_8250:
+		ret = serial8250_register_port(&port);
+		break;
+#endif
+#ifdef CONFIG_SERIAL_OF_PLATFORM_NWPSERIAL
+	case PORT_NWPSERIAL:
+		ret = nwpserial_register_port(&port);
+		break;
+#endif
+	default:
+		/* need to add code for these */
+	case PORT_UNKNOWN:
+		dev_info(&ofdev->dev, "Unknown serial port found, ignored\n");
+		ret = -ENODEV;
+		break;
+	}
+	if (ret < 0)
+		goto out;
+
+	info->type = port_type;
+	info->line = ret;
+	dev_set_drvdata(&ofdev->dev, info);
+	return 0;
+out:
+	kfree(info);
+	irq_dispose_mapping(port.irq);
+	return ret;
+}
+
+/*
+ * Release a line
+ */
+static int of_platform_serial_remove(struct platform_device *ofdev)
+{
+	struct of_serial_info *info = dev_get_drvdata(&ofdev->dev);
+	switch (info->type) {
+#ifdef CONFIG_SERIAL_8250
+	case PORT_8250 ... PORT_MAX_8250:
+		serial8250_unregister_port(info->line);
+		break;
+#endif
+#ifdef CONFIG_SERIAL_OF_PLATFORM_NWPSERIAL
+	case PORT_NWPSERIAL:
+		nwpserial_unregister_port(info->line);
+		break;
+#endif
+	default:
+		/* need to add code for these */
+		break;
+	}
+	kfree(info);
+	return 0;
+}
+
+/*
+ * A few common types, add more as needed.
+ */
+static struct of_device_id __devinitdata of_platform_serial_table[] = {
+	{ .compatible = "ns8250",   .data = (void *)PORT_8250, },
+	{ .compatible = "ns16450",  .data = (void *)PORT_16450, },
+	{ .compatible = "ns16550a", .data = (void *)PORT_16550A, },
+	{ .compatible = "ns16550",  .data = (void *)PORT_16550, },
+	{ .compatible = "ns16750",  .data = (void *)PORT_16750, },
+	{ .compatible = "ns16850",  .data = (void *)PORT_16850, },
+	{ .compatible = "nvidia,tegra20-uart", .data = (void *)PORT_TEGRA, },
+	{ .compatible = "altr,16550-FIFO32",
+		.data = (void *)PORT_ALTR_16550_F32, },
+	{ .compatible = "altr,16550-FIFO64",
+		.data = (void *)PORT_ALTR_16550_F64, },
+	{ .compatible = "altr,16550-FIFO128",
+		.data = (void *)PORT_ALTR_16550_F128, },
+#ifdef CONFIG_SERIAL_OF_PLATFORM_NWPSERIAL
+	{ .compatible = "ibm,qpace-nwp-serial",
+		.data = (void *)PORT_NWPSERIAL, },
+#endif
+	{ /* end of list */ },
+};
+
+static struct platform_driver of_platform_serial_driver = {
+	.driver = {
+		.name = "of_serial",
+		.owner = THIS_MODULE,
+		.of_match_table = of_platform_serial_table,
+	},
+	.probe = of_platform_serial_probe,
+	.remove = of_platform_serial_remove,
+};
+
+module_platform_driver(of_platform_serial_driver);
+
+MODULE_AUTHOR("Arnd Bergmann <arnd@arndb.de>");
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Serial Port driver for Open Firmware platform devices");
diff --git a/ap/os/linux/linux-3.4.x/drivers/tty/serial/omap-serial.c b/ap/os/linux/linux-3.4.x/drivers/tty/serial/omap-serial.c
new file mode 100644
index 0000000..f697492
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/tty/serial/omap-serial.c
@@ -0,0 +1,1680 @@
+/*
+ * Driver for OMAP-UART controller.
+ * Based on drivers/serial/8250.c
+ *
+ * Copyright (C) 2010 Texas Instruments.
+ *
+ * Authors:
+ *	Govindraj R	<govindraj.raja@ti.com>
+ *	Thara Gopinath	<thara@ti.com>
+ *
+ * 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.
+ *
+ * Note: This driver is made separate from 8250 driver as we cannot
+ * over load 8250 driver with omap platform specific configuration for
+ * features like DMA, it makes easier to implement features like DMA and
+ * hardware flow control and software flow control configuration with
+ * this driver as required for the omap-platform.
+ */
+
+#if defined(CONFIG_SERIAL_OMAP_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ)
+#define SUPPORT_SYSRQ
+#endif
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/console.h>
+#include <linux/serial_reg.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
+#include <linux/io.h>
+#include <linux/dma-mapping.h>
+#include <linux/clk.h>
+#include <linux/serial_core.h>
+#include <linux/irq.h>
+#include <linux/pm_runtime.h>
+#include <linux/of.h>
+
+#include <plat/dma.h>
+#include <plat/dmtimer.h>
+#include <plat/omap-serial.h>
+
+#define DEFAULT_CLK_SPEED 48000000 /* 48Mhz*/
+
+/* SCR register bitmasks */
+#define OMAP_UART_SCR_RX_TRIG_GRANU1_MASK		(1 << 7)
+
+/* FCR register bitmasks */
+#define OMAP_UART_FCR_RX_FIFO_TRIG_SHIFT		6
+#define OMAP_UART_FCR_RX_FIFO_TRIG_MASK			(0x3 << 6)
+
+static struct uart_omap_port *ui[OMAP_MAX_HSUART_PORTS];
+
+/* Forward declaration of functions */
+static void uart_tx_dma_callback(int lch, u16 ch_status, void *data);
+static void serial_omap_rxdma_poll(unsigned long uart_no);
+static int serial_omap_start_rxdma(struct uart_omap_port *up);
+static void serial_omap_mdr1_errataset(struct uart_omap_port *up, u8 mdr1);
+
+static struct workqueue_struct *serial_omap_uart_wq;
+
+static inline unsigned int serial_in(struct uart_omap_port *up, int offset)
+{
+	offset <<= up->port.regshift;
+	return readw(up->port.membase + offset);
+}
+
+static inline void serial_out(struct uart_omap_port *up, int offset, int value)
+{
+	offset <<= up->port.regshift;
+	writew(value, up->port.membase + offset);
+}
+
+static inline void serial_omap_clear_fifos(struct uart_omap_port *up)
+{
+	serial_out(up, UART_FCR, UART_FCR_ENABLE_FIFO);
+	serial_out(up, UART_FCR, UART_FCR_ENABLE_FIFO |
+		       UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT);
+	serial_out(up, UART_FCR, 0);
+}
+
+/*
+ * serial_omap_get_divisor - calculate divisor value
+ * @port: uart port info
+ * @baud: baudrate for which divisor needs to be calculated.
+ *
+ * We have written our own function to get the divisor so as to support
+ * 13x mode. 3Mbps Baudrate as an different divisor.
+ * Reference OMAP TRM Chapter 17:
+ * Table 17-1. UART Mode Baud Rates, Divisor Values, and Error Rates
+ * referring to oversampling - divisor value
+ * baudrate 460,800 to 3,686,400 all have divisor 13
+ * except 3,000,000 which has divisor value 16
+ */
+static unsigned int
+serial_omap_get_divisor(struct uart_port *port, unsigned int baud)
+{
+	unsigned int divisor;
+
+	if (baud > OMAP_MODE13X_SPEED && baud != 3000000)
+		divisor = 13;
+	else
+		divisor = 16;
+	return port->uartclk/(baud * divisor);
+}
+
+static void serial_omap_stop_rxdma(struct uart_omap_port *up)
+{
+	if (up->uart_dma.rx_dma_used) {
+		del_timer(&up->uart_dma.rx_timer);
+		omap_stop_dma(up->uart_dma.rx_dma_channel);
+		omap_free_dma(up->uart_dma.rx_dma_channel);
+		up->uart_dma.rx_dma_channel = OMAP_UART_DMA_CH_FREE;
+		up->uart_dma.rx_dma_used = false;
+		pm_runtime_mark_last_busy(&up->pdev->dev);
+		pm_runtime_put_autosuspend(&up->pdev->dev);
+	}
+}
+
+static void serial_omap_enable_ms(struct uart_port *port)
+{
+	struct uart_omap_port *up = (struct uart_omap_port *)port;
+
+	dev_dbg(up->port.dev, "serial_omap_enable_ms+%d\n", up->port.line);
+
+	pm_runtime_get_sync(&up->pdev->dev);
+	up->ier |= UART_IER_MSI;
+	serial_out(up, UART_IER, up->ier);
+	pm_runtime_put(&up->pdev->dev);
+}
+
+static void serial_omap_stop_tx(struct uart_port *port)
+{
+	struct uart_omap_port *up = (struct uart_omap_port *)port;
+	struct omap_uart_port_info *pdata = up->pdev->dev.platform_data;
+
+	if (up->use_dma &&
+		up->uart_dma.tx_dma_channel != OMAP_UART_DMA_CH_FREE) {
+		/*
+		 * Check if dma is still active. If yes do nothing,
+		 * return. Else stop dma
+		 */
+		if (omap_get_dma_active_status(up->uart_dma.tx_dma_channel))
+			return;
+		omap_stop_dma(up->uart_dma.tx_dma_channel);
+		omap_free_dma(up->uart_dma.tx_dma_channel);
+		up->uart_dma.tx_dma_channel = OMAP_UART_DMA_CH_FREE;
+		pm_runtime_mark_last_busy(&up->pdev->dev);
+		pm_runtime_put_autosuspend(&up->pdev->dev);
+	}
+
+	pm_runtime_get_sync(&up->pdev->dev);
+	if (up->ier & UART_IER_THRI) {
+		up->ier &= ~UART_IER_THRI;
+		serial_out(up, UART_IER, up->ier);
+	}
+
+	if (!up->use_dma && pdata && pdata->set_forceidle)
+		pdata->set_forceidle(up->pdev);
+
+	pm_runtime_mark_last_busy(&up->pdev->dev);
+	pm_runtime_put_autosuspend(&up->pdev->dev);
+}
+
+static void serial_omap_stop_rx(struct uart_port *port)
+{
+	struct uart_omap_port *up = (struct uart_omap_port *)port;
+
+	pm_runtime_get_sync(&up->pdev->dev);
+	if (up->use_dma)
+		serial_omap_stop_rxdma(up);
+	up->ier &= ~UART_IER_RLSI;
+	up->port.read_status_mask &= ~UART_LSR_DR;
+	serial_out(up, UART_IER, up->ier);
+	pm_runtime_mark_last_busy(&up->pdev->dev);
+	pm_runtime_put_autosuspend(&up->pdev->dev);
+}
+
+static inline void receive_chars(struct uart_omap_port *up,
+		unsigned int *status)
+{
+	struct tty_struct *tty = up->port.state->port.tty;
+	unsigned int flag, lsr = *status;
+	unsigned char ch = 0;
+	int max_count = 256;
+
+	do {
+		if (likely(lsr & UART_LSR_DR))
+			ch = serial_in(up, UART_RX);
+		flag = TTY_NORMAL;
+		up->port.icount.rx++;
+
+		if (unlikely(lsr & UART_LSR_BRK_ERROR_BITS)) {
+			/*
+			 * For statistics only
+			 */
+			if (lsr & UART_LSR_BI) {
+				lsr &= ~(UART_LSR_FE | UART_LSR_PE);
+				up->port.icount.brk++;
+				/*
+				 * We do the SysRQ and SAK checking
+				 * here because otherwise the break
+				 * may get masked by ignore_status_mask
+				 * or read_status_mask.
+				 */
+				if (uart_handle_break(&up->port))
+					goto ignore_char;
+			} else if (lsr & UART_LSR_PE) {
+				up->port.icount.parity++;
+			} else if (lsr & UART_LSR_FE) {
+				up->port.icount.frame++;
+			}
+
+			if (lsr & UART_LSR_OE)
+				up->port.icount.overrun++;
+
+			/*
+			 * Mask off conditions which should be ignored.
+			 */
+			lsr &= up->port.read_status_mask;
+
+#ifdef CONFIG_SERIAL_OMAP_CONSOLE
+			if (up->port.line == up->port.cons->index) {
+				/* Recover the break flag from console xmit */
+				lsr |= up->lsr_break_flag;
+			}
+#endif
+			if (lsr & UART_LSR_BI)
+				flag = TTY_BREAK;
+			else if (lsr & UART_LSR_PE)
+				flag = TTY_PARITY;
+			else if (lsr & UART_LSR_FE)
+				flag = TTY_FRAME;
+		}
+
+		if (uart_handle_sysrq_char(&up->port, ch))
+			goto ignore_char;
+		uart_insert_char(&up->port, lsr, UART_LSR_OE, ch, flag);
+ignore_char:
+		lsr = serial_in(up, UART_LSR);
+	} while ((lsr & (UART_LSR_DR | UART_LSR_BI)) && (max_count-- > 0));
+	spin_unlock(&up->port.lock);
+	tty_flip_buffer_push(tty);
+	spin_lock(&up->port.lock);
+}
+
+static void transmit_chars(struct uart_omap_port *up)
+{
+	struct circ_buf *xmit = &up->port.state->xmit;
+	int count;
+
+	if (up->port.x_char) {
+		serial_out(up, UART_TX, up->port.x_char);
+		up->port.icount.tx++;
+		up->port.x_char = 0;
+		return;
+	}
+	if (uart_circ_empty(xmit) || uart_tx_stopped(&up->port)) {
+		serial_omap_stop_tx(&up->port);
+		return;
+	}
+	count = up->port.fifosize / 4;
+	do {
+		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))
+		serial_omap_stop_tx(&up->port);
+}
+
+static inline void serial_omap_enable_ier_thri(struct uart_omap_port *up)
+{
+	if (!(up->ier & UART_IER_THRI)) {
+		up->ier |= UART_IER_THRI;
+		serial_out(up, UART_IER, up->ier);
+	}
+}
+
+static void serial_omap_start_tx(struct uart_port *port)
+{
+	struct uart_omap_port *up = (struct uart_omap_port *)port;
+	struct omap_uart_port_info *pdata = up->pdev->dev.platform_data;
+	struct circ_buf *xmit;
+	unsigned int start;
+	int ret = 0;
+
+	if (!up->use_dma) {
+		pm_runtime_get_sync(&up->pdev->dev);
+		serial_omap_enable_ier_thri(up);
+		if (pdata && pdata->set_noidle)
+			pdata->set_noidle(up->pdev);
+		pm_runtime_mark_last_busy(&up->pdev->dev);
+		pm_runtime_put_autosuspend(&up->pdev->dev);
+		return;
+	}
+
+	if (up->uart_dma.tx_dma_used)
+		return;
+
+	xmit = &up->port.state->xmit;
+
+	if (up->uart_dma.tx_dma_channel == OMAP_UART_DMA_CH_FREE) {
+		pm_runtime_get_sync(&up->pdev->dev);
+		ret = omap_request_dma(up->uart_dma.uart_dma_tx,
+				"UART Tx DMA",
+				(void *)uart_tx_dma_callback, up,
+				&(up->uart_dma.tx_dma_channel));
+
+		if (ret < 0) {
+			serial_omap_enable_ier_thri(up);
+			return;
+		}
+	}
+	spin_lock(&(up->uart_dma.tx_lock));
+	up->uart_dma.tx_dma_used = true;
+	spin_unlock(&(up->uart_dma.tx_lock));
+
+	start = up->uart_dma.tx_buf_dma_phys +
+				(xmit->tail & (UART_XMIT_SIZE - 1));
+
+	up->uart_dma.tx_buf_size = uart_circ_chars_pending(xmit);
+	/*
+	 * It is a circular buffer. See if the buffer has wounded back.
+	 * If yes it will have to be transferred in two separate dma
+	 * transfers
+	 */
+	if (start + up->uart_dma.tx_buf_size >=
+			up->uart_dma.tx_buf_dma_phys + UART_XMIT_SIZE)
+		up->uart_dma.tx_buf_size =
+			(up->uart_dma.tx_buf_dma_phys +
+			UART_XMIT_SIZE) - start;
+
+	omap_set_dma_dest_params(up->uart_dma.tx_dma_channel, 0,
+				OMAP_DMA_AMODE_CONSTANT,
+				up->uart_dma.uart_base, 0, 0);
+	omap_set_dma_src_params(up->uart_dma.tx_dma_channel, 0,
+				OMAP_DMA_AMODE_POST_INC, start, 0, 0);
+	omap_set_dma_transfer_params(up->uart_dma.tx_dma_channel,
+				OMAP_DMA_DATA_TYPE_S8,
+				up->uart_dma.tx_buf_size, 1,
+				OMAP_DMA_SYNC_ELEMENT,
+				up->uart_dma.uart_dma_tx, 0);
+	/* FIXME: Cache maintenance needed here? */
+	omap_start_dma(up->uart_dma.tx_dma_channel);
+}
+
+static unsigned int check_modem_status(struct uart_omap_port *up)
+{
+	unsigned int status;
+
+	status = serial_in(up, UART_MSR);
+	status |= up->msr_saved_flags;
+	up->msr_saved_flags = 0;
+	if ((status & UART_MSR_ANY_DELTA) == 0)
+		return status;
+
+	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;
+}
+
+/**
+ * serial_omap_irq() - This handles the interrupt from one port
+ * @irq: uart port irq number
+ * @dev_id: uart port info
+ */
+static inline irqreturn_t serial_omap_irq(int irq, void *dev_id)
+{
+	struct uart_omap_port *up = dev_id;
+	unsigned int iir, lsr;
+	unsigned long flags;
+
+	pm_runtime_get_sync(&up->pdev->dev);
+	iir = serial_in(up, UART_IIR);
+	if (iir & UART_IIR_NO_INT) {
+		pm_runtime_mark_last_busy(&up->pdev->dev);
+		pm_runtime_put_autosuspend(&up->pdev->dev);
+		return IRQ_NONE;
+	}
+
+	spin_lock_irqsave(&up->port.lock, flags);
+	lsr = serial_in(up, UART_LSR);
+	if (iir & UART_IIR_RLSI) {
+		if (!up->use_dma) {
+			if (lsr & UART_LSR_DR)
+				receive_chars(up, &lsr);
+		} else {
+			up->ier &= ~(UART_IER_RDI | UART_IER_RLSI);
+			serial_out(up, UART_IER, up->ier);
+			if ((serial_omap_start_rxdma(up) != 0) &&
+					(lsr & UART_LSR_DR))
+				receive_chars(up, &lsr);
+		}
+	}
+
+	check_modem_status(up);
+	if ((lsr & UART_LSR_THRE) && (iir & UART_IIR_THRI))
+		transmit_chars(up);
+
+	spin_unlock_irqrestore(&up->port.lock, flags);
+	pm_runtime_mark_last_busy(&up->pdev->dev);
+	pm_runtime_put_autosuspend(&up->pdev->dev);
+
+	up->port_activity = jiffies;
+	return IRQ_HANDLED;
+}
+
+static unsigned int serial_omap_tx_empty(struct uart_port *port)
+{
+	struct uart_omap_port *up = (struct uart_omap_port *)port;
+	unsigned long flags = 0;
+	unsigned int ret = 0;
+
+	pm_runtime_get_sync(&up->pdev->dev);
+	dev_dbg(up->port.dev, "serial_omap_tx_empty+%d\n", up->port.line);
+	spin_lock_irqsave(&up->port.lock, flags);
+	ret = serial_in(up, UART_LSR) & UART_LSR_TEMT ? TIOCSER_TEMT : 0;
+	spin_unlock_irqrestore(&up->port.lock, flags);
+	pm_runtime_put(&up->pdev->dev);
+	return ret;
+}
+
+static unsigned int serial_omap_get_mctrl(struct uart_port *port)
+{
+	struct uart_omap_port *up = (struct uart_omap_port *)port;
+	unsigned int status;
+	unsigned int ret = 0;
+
+	pm_runtime_get_sync(&up->pdev->dev);
+	status = check_modem_status(up);
+	pm_runtime_put(&up->pdev->dev);
+
+	dev_dbg(up->port.dev, "serial_omap_get_mctrl+%d\n", up->port.line);
+
+	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 serial_omap_set_mctrl(struct uart_port *port, unsigned int mctrl)
+{
+	struct uart_omap_port *up = (struct uart_omap_port *)port;
+	unsigned char mcr = 0;
+
+	dev_dbg(up->port.dev, "serial_omap_set_mctrl+%d\n", up->port.line);
+	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;
+
+	pm_runtime_get_sync(&up->pdev->dev);
+	up->mcr = serial_in(up, UART_MCR);
+	up->mcr |= mcr;
+	serial_out(up, UART_MCR, up->mcr);
+	pm_runtime_put(&up->pdev->dev);
+}
+
+static void serial_omap_break_ctl(struct uart_port *port, int break_state)
+{
+	struct uart_omap_port *up = (struct uart_omap_port *)port;
+	unsigned long flags = 0;
+
+	dev_dbg(up->port.dev, "serial_omap_break_ctl+%d\n", up->port.line);
+	pm_runtime_get_sync(&up->pdev->dev);
+	spin_lock_irqsave(&up->port.lock, flags);
+	if (break_state == -1)
+		up->lcr |= UART_LCR_SBC;
+	else
+		up->lcr &= ~UART_LCR_SBC;
+	serial_out(up, UART_LCR, up->lcr);
+	spin_unlock_irqrestore(&up->port.lock, flags);
+	pm_runtime_put(&up->pdev->dev);
+}
+
+static int serial_omap_startup(struct uart_port *port)
+{
+	struct uart_omap_port *up = (struct uart_omap_port *)port;
+	unsigned long flags = 0;
+	int retval;
+
+	/*
+	 * Allocate the IRQ
+	 */
+	retval = request_irq(up->port.irq, serial_omap_irq, up->port.irqflags,
+				up->name, up);
+	if (retval)
+		return retval;
+
+	dev_dbg(up->port.dev, "serial_omap_startup+%d\n", up->port.line);
+
+	pm_runtime_get_sync(&up->pdev->dev);
+	/*
+	 * Clear the FIFO buffers and disable them.
+	 * (they will be reenabled in set_termios())
+	 */
+	serial_omap_clear_fifos(up);
+	/* For Hardware flow control */
+	serial_out(up, UART_MCR, UART_MCR_RTS);
+
+	/*
+	 * Clear the interrupt registers.
+	 */
+	(void) serial_in(up, UART_LSR);
+	if (serial_in(up, UART_LSR) & UART_LSR_DR)
+		(void) serial_in(up, UART_RX);
+	(void) serial_in(up, UART_IIR);
+	(void) serial_in(up, UART_MSR);
+
+	/*
+	 * Now, initialize the UART
+	 */
+	serial_out(up, UART_LCR, UART_LCR_WLEN8);
+	spin_lock_irqsave(&up->port.lock, flags);
+	/*
+	 * Most PC uarts need OUT2 raised to enable interrupts.
+	 */
+	up->port.mctrl |= TIOCM_OUT2;
+	serial_omap_set_mctrl(&up->port, up->port.mctrl);
+	spin_unlock_irqrestore(&up->port.lock, flags);
+
+	up->msr_saved_flags = 0;
+	if (up->use_dma) {
+		free_page((unsigned long)up->port.state->xmit.buf);
+		up->port.state->xmit.buf = dma_alloc_coherent(NULL,
+			UART_XMIT_SIZE,
+			(dma_addr_t *)&(up->uart_dma.tx_buf_dma_phys),
+			0);
+		init_timer(&(up->uart_dma.rx_timer));
+		up->uart_dma.rx_timer.function = serial_omap_rxdma_poll;
+		up->uart_dma.rx_timer.data = up->port.line;
+		/* Currently the buffer size is 4KB. Can increase it */
+		up->uart_dma.rx_buf = dma_alloc_coherent(NULL,
+			up->uart_dma.rx_buf_size,
+			(dma_addr_t *)&(up->uart_dma.rx_buf_dma_phys), 0);
+	}
+	/*
+	 * Finally, enable interrupts. Note: Modem status interrupts
+	 * are set via set_termios(), which will be occurring imminently
+	 * anyway, so we don't enable them here.
+	 */
+	up->ier = UART_IER_RLSI | UART_IER_RDI;
+	serial_out(up, UART_IER, up->ier);
+
+	/* Enable module level wake up */
+	serial_out(up, UART_OMAP_WER, OMAP_UART_WER_MOD_WKUP);
+
+	pm_runtime_mark_last_busy(&up->pdev->dev);
+	pm_runtime_put_autosuspend(&up->pdev->dev);
+	up->port_activity = jiffies;
+	return 0;
+}
+
+static void serial_omap_shutdown(struct uart_port *port)
+{
+	struct uart_omap_port *up = (struct uart_omap_port *)port;
+	unsigned long flags = 0;
+
+	dev_dbg(up->port.dev, "serial_omap_shutdown+%d\n", up->port.line);
+
+	pm_runtime_get_sync(&up->pdev->dev);
+	/*
+	 * Disable interrupts from this port
+	 */
+	up->ier = 0;
+	serial_out(up, UART_IER, 0);
+
+	spin_lock_irqsave(&up->port.lock, flags);
+	up->port.mctrl &= ~TIOCM_OUT2;
+	serial_omap_set_mctrl(&up->port, up->port.mctrl);
+	spin_unlock_irqrestore(&up->port.lock, flags);
+
+	/*
+	 * Disable break condition and FIFOs
+	 */
+	serial_out(up, UART_LCR, serial_in(up, UART_LCR) & ~UART_LCR_SBC);
+	serial_omap_clear_fifos(up);
+
+	/*
+	 * Read data port to reset things, and then free the irq
+	 */
+	if (serial_in(up, UART_LSR) & UART_LSR_DR)
+		(void) serial_in(up, UART_RX);
+	if (up->use_dma) {
+		dma_free_coherent(up->port.dev,
+			UART_XMIT_SIZE,	up->port.state->xmit.buf,
+			up->uart_dma.tx_buf_dma_phys);
+		up->port.state->xmit.buf = NULL;
+		serial_omap_stop_rx(port);
+		dma_free_coherent(up->port.dev,
+			up->uart_dma.rx_buf_size, up->uart_dma.rx_buf,
+			up->uart_dma.rx_buf_dma_phys);
+		up->uart_dma.rx_buf = NULL;
+	}
+
+	pm_runtime_put(&up->pdev->dev);
+	free_irq(up->port.irq, up);
+}
+
+static inline void
+serial_omap_configure_xonxoff
+		(struct uart_omap_port *up, struct ktermios *termios)
+{
+	up->lcr = serial_in(up, UART_LCR);
+	serial_out(up, UART_LCR, UART_LCR_CONF_MODE_B);
+	up->efr = serial_in(up, UART_EFR);
+	serial_out(up, UART_EFR, up->efr & ~UART_EFR_ECB);
+
+	serial_out(up, UART_XON1, termios->c_cc[VSTART]);
+	serial_out(up, UART_XOFF1, termios->c_cc[VSTOP]);
+
+	/* clear SW control mode bits */
+	up->efr &= OMAP_UART_SW_CLR;
+
+	/*
+	 * IXON Flag:
+	 * Enable XON/XOFF flow control on output.
+	 * Transmit XON1, XOFF1
+	 */
+	if (termios->c_iflag & IXON)
+		up->efr |= OMAP_UART_SW_TX;
+
+	/*
+	 * IXOFF Flag:
+	 * Enable XON/XOFF flow control on input.
+	 * Receiver compares XON1, XOFF1.
+	 */
+	if (termios->c_iflag & IXOFF)
+		up->efr |= OMAP_UART_SW_RX;
+
+	serial_out(up, UART_EFR, up->efr | UART_EFR_ECB);
+	serial_out(up, UART_LCR, UART_LCR_CONF_MODE_A);
+
+	up->mcr = serial_in(up, UART_MCR);
+
+	/*
+	 * IXANY Flag:
+	 * Enable any character to restart output.
+	 * Operation resumes after receiving any
+	 * character after recognition of the XOFF character
+	 */
+	if (termios->c_iflag & IXANY)
+		up->mcr |= UART_MCR_XONANY;
+
+	serial_out(up, UART_MCR, up->mcr | UART_MCR_TCRTLR);
+	serial_out(up, UART_LCR, UART_LCR_CONF_MODE_B);
+	serial_out(up, UART_TI752_TCR, OMAP_UART_TCR_TRIG);
+	/* Enable special char function UARTi.EFR_REG[5] and
+	 * load the new software flow control mode IXON or IXOFF
+	 * and restore the UARTi.EFR_REG[4] ENHANCED_EN value.
+	 */
+	serial_out(up, UART_EFR, up->efr | UART_EFR_SCD);
+	serial_out(up, UART_LCR, UART_LCR_CONF_MODE_A);
+
+	serial_out(up, UART_MCR, up->mcr & ~UART_MCR_TCRTLR);
+	serial_out(up, UART_LCR, up->lcr);
+}
+
+static void serial_omap_uart_qos_work(struct work_struct *work)
+{
+	struct uart_omap_port *up = container_of(work, struct uart_omap_port,
+						qos_work);
+
+	pm_qos_update_request(&up->pm_qos_request, up->latency);
+}
+
+static void
+serial_omap_set_termios(struct uart_port *port, struct ktermios *termios,
+			struct ktermios *old)
+{
+	struct uart_omap_port *up = (struct uart_omap_port *)port;
+	unsigned char cval = 0;
+	unsigned char efr = 0;
+	unsigned long flags = 0;
+	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;
+
+	/*
+	 * Ask the core to calculate the divisor for us.
+	 */
+
+	baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk/13);
+	quot = serial_omap_get_divisor(port, baud);
+
+	/* calculate wakeup latency constraint */
+	up->calc_latency = (USEC_PER_SEC * up->port.fifosize) / (baud / 8);
+	up->latency = up->calc_latency;
+	schedule_work(&up->qos_work);
+
+	up->dll = quot & 0xff;
+	up->dlh = quot >> 8;
+	up->mdr1 = UART_OMAP_MDR1_DISABLE;
+
+	up->fcr = UART_FCR_R_TRIG_01 | UART_FCR_T_TRIG_01 |
+			UART_FCR_ENABLE_FIFO;
+	if (up->use_dma)
+		up->fcr |= UART_FCR_DMA_SELECT;
+
+	/*
+	 * Ok, we're now changing the port state. Do it with
+	 * interrupts disabled.
+	 */
+	pm_runtime_get_sync(&up->pdev->dev);
+	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;
+
+	/*
+	 * Characters 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;
+
+	/*
+	 * Modem status interrupts
+	 */
+	up->ier &= ~UART_IER_MSI;
+	if (UART_ENABLE_MS(&up->port, termios->c_cflag))
+		up->ier |= UART_IER_MSI;
+	serial_out(up, UART_IER, up->ier);
+	serial_out(up, UART_LCR, cval);		/* reset DLAB */
+	up->lcr = cval;
+	up->scr = OMAP_UART_SCR_TX_EMPTY;
+
+	/* FIFOs and DMA Settings */
+
+	/* FCR can be changed only when the
+	 * baud clock is not running
+	 * DLL_REG and DLH_REG set to 0.
+	 */
+	serial_out(up, UART_LCR, UART_LCR_CONF_MODE_A);
+	serial_out(up, UART_DLL, 0);
+	serial_out(up, UART_DLM, 0);
+	serial_out(up, UART_LCR, 0);
+
+	serial_out(up, UART_LCR, UART_LCR_CONF_MODE_B);
+
+	up->efr = serial_in(up, UART_EFR);
+	serial_out(up, UART_EFR, up->efr | UART_EFR_ECB);
+
+	serial_out(up, UART_LCR, UART_LCR_CONF_MODE_A);
+	up->mcr = serial_in(up, UART_MCR);
+	serial_out(up, UART_MCR, up->mcr | UART_MCR_TCRTLR);
+	/* FIFO ENABLE, DMA MODE */
+
+	up->scr |= OMAP_UART_SCR_RX_TRIG_GRANU1_MASK;
+
+	if (up->use_dma) {
+		serial_out(up, UART_TI752_TLR, 0);
+		up->scr |= UART_FCR_TRIGGER_4;
+	} else {
+		/* Set receive FIFO threshold to 1 byte */
+		up->fcr &= ~OMAP_UART_FCR_RX_FIFO_TRIG_MASK;
+		up->fcr |= (0x1 << OMAP_UART_FCR_RX_FIFO_TRIG_SHIFT);
+	}
+
+	serial_out(up, UART_FCR, up->fcr);
+	serial_out(up, UART_LCR, UART_LCR_CONF_MODE_B);
+
+	serial_out(up, UART_OMAP_SCR, up->scr);
+
+	serial_out(up, UART_EFR, up->efr);
+	serial_out(up, UART_LCR, UART_LCR_CONF_MODE_A);
+	serial_out(up, UART_MCR, up->mcr);
+
+	/* Protocol, Baud Rate, and Interrupt Settings */
+
+	if (up->errata & UART_ERRATA_i202_MDR1_ACCESS)
+		serial_omap_mdr1_errataset(up, up->mdr1);
+	else
+		serial_out(up, UART_OMAP_MDR1, up->mdr1);
+
+	serial_out(up, UART_LCR, UART_LCR_CONF_MODE_B);
+
+	up->efr = serial_in(up, UART_EFR);
+	serial_out(up, UART_EFR, up->efr | UART_EFR_ECB);
+
+	serial_out(up, UART_LCR, 0);
+	serial_out(up, UART_IER, 0);
+	serial_out(up, UART_LCR, UART_LCR_CONF_MODE_B);
+
+	serial_out(up, UART_DLL, up->dll);	/* LS of divisor */
+	serial_out(up, UART_DLM, up->dlh);	/* MS of divisor */
+
+	serial_out(up, UART_LCR, 0);
+	serial_out(up, UART_IER, up->ier);
+	serial_out(up, UART_LCR, UART_LCR_CONF_MODE_B);
+
+	serial_out(up, UART_EFR, up->efr);
+	serial_out(up, UART_LCR, cval);
+
+	if (baud > 230400 && baud != 3000000)
+		up->mdr1 = UART_OMAP_MDR1_13X_MODE;
+	else
+		up->mdr1 = UART_OMAP_MDR1_16X_MODE;
+
+	if (up->errata & UART_ERRATA_i202_MDR1_ACCESS)
+		serial_omap_mdr1_errataset(up, up->mdr1);
+	else
+		serial_out(up, UART_OMAP_MDR1, up->mdr1);
+
+	/* Hardware Flow Control Configuration */
+
+	if (termios->c_cflag & CRTSCTS) {
+		efr |= (UART_EFR_CTS | UART_EFR_RTS);
+		serial_out(up, UART_LCR, UART_LCR_CONF_MODE_A);
+
+		up->mcr = serial_in(up, UART_MCR);
+		serial_out(up, UART_MCR, up->mcr | UART_MCR_TCRTLR);
+
+		serial_out(up, UART_LCR, UART_LCR_CONF_MODE_B);
+		up->efr = serial_in(up, UART_EFR);
+		serial_out(up, UART_EFR, up->efr | UART_EFR_ECB);
+
+		serial_out(up, UART_TI752_TCR, OMAP_UART_TCR_TRIG);
+		serial_out(up, UART_EFR, efr); /* Enable AUTORTS and AUTOCTS */
+		serial_out(up, UART_LCR, UART_LCR_CONF_MODE_A);
+		serial_out(up, UART_MCR, up->mcr | UART_MCR_RTS);
+		serial_out(up, UART_LCR, cval);
+	}
+
+	serial_omap_set_mctrl(&up->port, up->port.mctrl);
+	/* Software Flow Control Configuration */
+	serial_omap_configure_xonxoff(up, termios);
+
+	spin_unlock_irqrestore(&up->port.lock, flags);
+	pm_runtime_put(&up->pdev->dev);
+	dev_dbg(up->port.dev, "serial_omap_set_termios+%d\n", up->port.line);
+}
+
+static void
+serial_omap_pm(struct uart_port *port, unsigned int state,
+	       unsigned int oldstate)
+{
+	struct uart_omap_port *up = (struct uart_omap_port *)port;
+	unsigned char efr;
+
+	dev_dbg(up->port.dev, "serial_omap_pm+%d\n", up->port.line);
+
+	pm_runtime_get_sync(&up->pdev->dev);
+	serial_out(up, UART_LCR, UART_LCR_CONF_MODE_B);
+	efr = serial_in(up, UART_EFR);
+	serial_out(up, UART_EFR, efr | UART_EFR_ECB);
+	serial_out(up, UART_LCR, 0);
+
+	serial_out(up, UART_IER, (state != 0) ? UART_IERX_SLEEP : 0);
+	serial_out(up, UART_LCR, UART_LCR_CONF_MODE_B);
+	serial_out(up, UART_EFR, efr);
+	serial_out(up, UART_LCR, 0);
+
+	if (!device_may_wakeup(&up->pdev->dev)) {
+		if (!state)
+			pm_runtime_forbid(&up->pdev->dev);
+		else
+			pm_runtime_allow(&up->pdev->dev);
+	}
+
+	pm_runtime_put(&up->pdev->dev);
+}
+
+static void serial_omap_release_port(struct uart_port *port)
+{
+	dev_dbg(port->dev, "serial_omap_release_port+\n");
+}
+
+static int serial_omap_request_port(struct uart_port *port)
+{
+	dev_dbg(port->dev, "serial_omap_request_port+\n");
+	return 0;
+}
+
+static void serial_omap_config_port(struct uart_port *port, int flags)
+{
+	struct uart_omap_port *up = (struct uart_omap_port *)port;
+
+	dev_dbg(up->port.dev, "serial_omap_config_port+%d\n",
+							up->port.line);
+	up->port.type = PORT_OMAP;
+}
+
+static int
+serial_omap_verify_port(struct uart_port *port, struct serial_struct *ser)
+{
+	/* we don't want the core code to modify any port params */
+	dev_dbg(port->dev, "serial_omap_verify_port+\n");
+	return -EINVAL;
+}
+
+static const char *
+serial_omap_type(struct uart_port *port)
+{
+	struct uart_omap_port *up = (struct uart_omap_port *)port;
+
+	dev_dbg(up->port.dev, "serial_omap_type+%d\n", up->port.line);
+	return up->name;
+}
+
+#define BOTH_EMPTY (UART_LSR_TEMT | UART_LSR_THRE)
+
+static inline void wait_for_xmitr(struct uart_omap_port *up)
+{
+	unsigned int status, tmout = 10000;
+
+	/* Wait up to 10ms for the character(s) to be sent. */
+	do {
+		status = serial_in(up, UART_LSR);
+
+		if (status & UART_LSR_BI)
+			up->lsr_break_flag = UART_LSR_BI;
+
+		if (--tmout == 0)
+			break;
+		udelay(1);
+	} while ((status & BOTH_EMPTY) != BOTH_EMPTY);
+
+	/* Wait up to 1s for flow control if necessary */
+	if (up->port.flags & UPF_CONS_FLOW) {
+		tmout = 1000000;
+		for (tmout = 1000000; tmout; tmout--) {
+			unsigned int msr = serial_in(up, UART_MSR);
+
+			up->msr_saved_flags |= msr & MSR_SAVE_FLAGS;
+			if (msr & UART_MSR_CTS)
+				break;
+
+			udelay(1);
+		}
+	}
+}
+
+#ifdef CONFIG_CONSOLE_POLL
+
+static void serial_omap_poll_put_char(struct uart_port *port, unsigned char ch)
+{
+	struct uart_omap_port *up = (struct uart_omap_port *)port;
+
+	pm_runtime_get_sync(&up->pdev->dev);
+	wait_for_xmitr(up);
+	serial_out(up, UART_TX, ch);
+	pm_runtime_put(&up->pdev->dev);
+}
+
+static int serial_omap_poll_get_char(struct uart_port *port)
+{
+	struct uart_omap_port *up = (struct uart_omap_port *)port;
+	unsigned int status;
+
+	pm_runtime_get_sync(&up->pdev->dev);
+	status = serial_in(up, UART_LSR);
+	if (!(status & UART_LSR_DR))
+		return NO_POLL_CHAR;
+
+	status = serial_in(up, UART_RX);
+	pm_runtime_put(&up->pdev->dev);
+	return status;
+}
+
+#endif /* CONFIG_CONSOLE_POLL */
+
+#ifdef CONFIG_SERIAL_OMAP_CONSOLE
+
+static struct uart_omap_port *serial_omap_console_ports[4];
+
+static struct uart_driver serial_omap_reg;
+
+static void serial_omap_console_putchar(struct uart_port *port, int ch)
+{
+	struct uart_omap_port *up = (struct uart_omap_port *)port;
+
+	wait_for_xmitr(up);
+	serial_out(up, UART_TX, ch);
+}
+
+static void
+serial_omap_console_write(struct console *co, const char *s,
+		unsigned int count)
+{
+	struct uart_omap_port *up = serial_omap_console_ports[co->index];
+	unsigned long flags;
+	unsigned int ier;
+	int locked = 1;
+
+	pm_runtime_get_sync(&up->pdev->dev);
+
+	if (up->port.sysrq || oops_in_progress)
+		locked = spin_trylock_irqsave(&up->port.lock, flags);
+	else
+		spin_lock_irqsave(&up->port.lock, flags);
+
+	/*
+	 * First save the IER then disable the interrupts
+	 */
+	ier = serial_in(up, UART_IER);
+	serial_out(up, UART_IER, 0);
+
+	uart_console_write(&up->port, s, count, serial_omap_console_putchar);
+
+	/*
+	 * Finally, wait for transmitter to become empty
+	 * and restore the IER
+	 */
+	wait_for_xmitr(up);
+	serial_out(up, UART_IER, ier);
+	/*
+	 * The receive handling will happen properly because the
+	 * receive ready bit will still be set; it is not cleared
+	 * on read.  However, modem control will not, we must
+	 * call it if we have saved something in the saved flags
+	 * while processing with interrupts off.
+	 */
+	if (up->msr_saved_flags)
+		check_modem_status(up);
+
+	pm_runtime_mark_last_busy(&up->pdev->dev);
+	pm_runtime_put_autosuspend(&up->pdev->dev);
+	if (locked)
+		spin_unlock_irqrestore(&up->port.lock, flags);
+}
+
+static int __init
+serial_omap_console_setup(struct console *co, char *options)
+{
+	struct uart_omap_port *up;
+	int baud = 115200;
+	int bits = 8;
+	int parity = 'n';
+	int flow = 'n';
+
+	if (serial_omap_console_ports[co->index] == NULL)
+		return -ENODEV;
+	up = serial_omap_console_ports[co->index];
+
+	if (options)
+		uart_parse_options(options, &baud, &parity, &bits, &flow);
+
+	return uart_set_options(&up->port, co, baud, parity, bits, flow);
+}
+
+static struct console serial_omap_console = {
+	.name		= OMAP_SERIAL_NAME,
+	.write		= serial_omap_console_write,
+	.device		= uart_console_device,
+	.setup		= serial_omap_console_setup,
+	.flags		= CON_PRINTBUFFER,
+	.index		= -1,
+	.data		= &serial_omap_reg,
+};
+
+static void serial_omap_add_console_port(struct uart_omap_port *up)
+{
+	serial_omap_console_ports[up->port.line] = up;
+}
+
+#define OMAP_CONSOLE	(&serial_omap_console)
+
+#else
+
+#define OMAP_CONSOLE	NULL
+
+static inline void serial_omap_add_console_port(struct uart_omap_port *up)
+{}
+
+#endif
+
+static struct uart_ops serial_omap_pops = {
+	.tx_empty	= serial_omap_tx_empty,
+	.set_mctrl	= serial_omap_set_mctrl,
+	.get_mctrl	= serial_omap_get_mctrl,
+	.stop_tx	= serial_omap_stop_tx,
+	.start_tx	= serial_omap_start_tx,
+	.stop_rx	= serial_omap_stop_rx,
+	.enable_ms	= serial_omap_enable_ms,
+	.break_ctl	= serial_omap_break_ctl,
+	.startup	= serial_omap_startup,
+	.shutdown	= serial_omap_shutdown,
+	.set_termios	= serial_omap_set_termios,
+	.pm		= serial_omap_pm,
+	.type		= serial_omap_type,
+	.release_port	= serial_omap_release_port,
+	.request_port	= serial_omap_request_port,
+	.config_port	= serial_omap_config_port,
+	.verify_port	= serial_omap_verify_port,
+#ifdef CONFIG_CONSOLE_POLL
+	.poll_put_char  = serial_omap_poll_put_char,
+	.poll_get_char  = serial_omap_poll_get_char,
+#endif
+};
+
+static struct uart_driver serial_omap_reg = {
+	.owner		= THIS_MODULE,
+	.driver_name	= "OMAP-SERIAL",
+	.dev_name	= OMAP_SERIAL_NAME,
+	.nr		= OMAP_MAX_HSUART_PORTS,
+	.cons		= OMAP_CONSOLE,
+};
+
+#ifdef CONFIG_PM_SLEEP
+static int serial_omap_suspend(struct device *dev)
+{
+	struct uart_omap_port *up = dev_get_drvdata(dev);
+
+	if (up) {
+		uart_suspend_port(&serial_omap_reg, &up->port);
+		flush_work_sync(&up->qos_work);
+	}
+
+	return 0;
+}
+
+static int serial_omap_resume(struct device *dev)
+{
+	struct uart_omap_port *up = dev_get_drvdata(dev);
+
+	if (up)
+		uart_resume_port(&serial_omap_reg, &up->port);
+	return 0;
+}
+#endif
+
+static void serial_omap_rxdma_poll(unsigned long uart_no)
+{
+	struct uart_omap_port *up = ui[uart_no];
+	unsigned int curr_dma_pos, curr_transmitted_size;
+	int ret = 0;
+
+	curr_dma_pos = omap_get_dma_dst_pos(up->uart_dma.rx_dma_channel);
+	if ((curr_dma_pos == up->uart_dma.prev_rx_dma_pos) ||
+			     (curr_dma_pos == 0)) {
+		if (jiffies_to_msecs(jiffies - up->port_activity) <
+						up->uart_dma.rx_timeout) {
+			mod_timer(&up->uart_dma.rx_timer, jiffies +
+				usecs_to_jiffies(up->uart_dma.rx_poll_rate));
+		} else {
+			serial_omap_stop_rxdma(up);
+			up->ier |= (UART_IER_RDI | UART_IER_RLSI);
+			serial_out(up, UART_IER, up->ier);
+		}
+		return;
+	}
+
+	curr_transmitted_size = curr_dma_pos -
+					up->uart_dma.prev_rx_dma_pos;
+	up->port.icount.rx += curr_transmitted_size;
+	tty_insert_flip_string(up->port.state->port.tty,
+			up->uart_dma.rx_buf +
+			(up->uart_dma.prev_rx_dma_pos -
+			up->uart_dma.rx_buf_dma_phys),
+			curr_transmitted_size);
+	tty_flip_buffer_push(up->port.state->port.tty);
+	up->uart_dma.prev_rx_dma_pos = curr_dma_pos;
+	if (up->uart_dma.rx_buf_size +
+			up->uart_dma.rx_buf_dma_phys == curr_dma_pos) {
+		ret = serial_omap_start_rxdma(up);
+		if (ret < 0) {
+			serial_omap_stop_rxdma(up);
+			up->ier |= (UART_IER_RDI | UART_IER_RLSI);
+			serial_out(up, UART_IER, up->ier);
+		}
+	} else  {
+		mod_timer(&up->uart_dma.rx_timer, jiffies +
+			usecs_to_jiffies(up->uart_dma.rx_poll_rate));
+	}
+	up->port_activity = jiffies;
+}
+
+static void uart_rx_dma_callback(int lch, u16 ch_status, void *data)
+{
+	return;
+}
+
+static int serial_omap_start_rxdma(struct uart_omap_port *up)
+{
+	int ret = 0;
+
+	if (up->uart_dma.rx_dma_channel == -1) {
+		pm_runtime_get_sync(&up->pdev->dev);
+		ret = omap_request_dma(up->uart_dma.uart_dma_rx,
+				"UART Rx DMA",
+				(void *)uart_rx_dma_callback, up,
+				&(up->uart_dma.rx_dma_channel));
+		if (ret < 0)
+			return ret;
+
+		omap_set_dma_src_params(up->uart_dma.rx_dma_channel, 0,
+				OMAP_DMA_AMODE_CONSTANT,
+				up->uart_dma.uart_base, 0, 0);
+		omap_set_dma_dest_params(up->uart_dma.rx_dma_channel, 0,
+				OMAP_DMA_AMODE_POST_INC,
+				up->uart_dma.rx_buf_dma_phys, 0, 0);
+		omap_set_dma_transfer_params(up->uart_dma.rx_dma_channel,
+				OMAP_DMA_DATA_TYPE_S8,
+				up->uart_dma.rx_buf_size, 1,
+				OMAP_DMA_SYNC_ELEMENT,
+				up->uart_dma.uart_dma_rx, 0);
+	}
+	up->uart_dma.prev_rx_dma_pos = up->uart_dma.rx_buf_dma_phys;
+	/* FIXME: Cache maintenance needed here? */
+	omap_start_dma(up->uart_dma.rx_dma_channel);
+	mod_timer(&up->uart_dma.rx_timer, jiffies +
+				usecs_to_jiffies(up->uart_dma.rx_poll_rate));
+	up->uart_dma.rx_dma_used = true;
+	return ret;
+}
+
+static void serial_omap_continue_tx(struct uart_omap_port *up)
+{
+	struct circ_buf *xmit = &up->port.state->xmit;
+	unsigned int start = up->uart_dma.tx_buf_dma_phys
+			+ (xmit->tail & (UART_XMIT_SIZE - 1));
+
+	if (uart_circ_empty(xmit))
+		return;
+
+	up->uart_dma.tx_buf_size = uart_circ_chars_pending(xmit);
+	/*
+	 * It is a circular buffer. See if the buffer has wounded back.
+	 * If yes it will have to be transferred in two separate dma
+	 * transfers
+	 */
+	if (start + up->uart_dma.tx_buf_size >=
+			up->uart_dma.tx_buf_dma_phys + UART_XMIT_SIZE)
+		up->uart_dma.tx_buf_size =
+			(up->uart_dma.tx_buf_dma_phys + UART_XMIT_SIZE) - start;
+	omap_set_dma_dest_params(up->uart_dma.tx_dma_channel, 0,
+				OMAP_DMA_AMODE_CONSTANT,
+				up->uart_dma.uart_base, 0, 0);
+	omap_set_dma_src_params(up->uart_dma.tx_dma_channel, 0,
+				OMAP_DMA_AMODE_POST_INC, start, 0, 0);
+	omap_set_dma_transfer_params(up->uart_dma.tx_dma_channel,
+				OMAP_DMA_DATA_TYPE_S8,
+				up->uart_dma.tx_buf_size, 1,
+				OMAP_DMA_SYNC_ELEMENT,
+				up->uart_dma.uart_dma_tx, 0);
+	/* FIXME: Cache maintenance needed here? */
+	omap_start_dma(up->uart_dma.tx_dma_channel);
+}
+
+static void uart_tx_dma_callback(int lch, u16 ch_status, void *data)
+{
+	struct uart_omap_port *up = (struct uart_omap_port *)data;
+	struct circ_buf *xmit = &up->port.state->xmit;
+
+	xmit->tail = (xmit->tail + up->uart_dma.tx_buf_size) & \
+			(UART_XMIT_SIZE - 1);
+	up->port.icount.tx += up->uart_dma.tx_buf_size;
+
+	if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
+		uart_write_wakeup(&up->port);
+
+	if (uart_circ_empty(xmit)) {
+		spin_lock(&(up->uart_dma.tx_lock));
+		serial_omap_stop_tx(&up->port);
+		up->uart_dma.tx_dma_used = false;
+		spin_unlock(&(up->uart_dma.tx_lock));
+	} else {
+		omap_stop_dma(up->uart_dma.tx_dma_channel);
+		serial_omap_continue_tx(up);
+	}
+	up->port_activity = jiffies;
+	return;
+}
+
+static struct omap_uart_port_info *of_get_uart_port_info(struct device *dev)
+{
+	struct omap_uart_port_info *omap_up_info;
+
+	omap_up_info = devm_kzalloc(dev, sizeof(*omap_up_info), GFP_KERNEL);
+	if (!omap_up_info)
+		return NULL; /* out of memory */
+
+	of_property_read_u32(dev->of_node, "clock-frequency",
+					 &omap_up_info->uartclk);
+	return omap_up_info;
+}
+
+static int serial_omap_probe(struct platform_device *pdev)
+{
+	struct uart_omap_port	*up;
+	struct resource		*mem, *irq, *dma_tx, *dma_rx;
+	struct omap_uart_port_info *omap_up_info = pdev->dev.platform_data;
+	int ret = -ENOSPC;
+
+	if (pdev->dev.of_node)
+		omap_up_info = of_get_uart_port_info(&pdev->dev);
+
+	mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!mem) {
+		dev_err(&pdev->dev, "no mem resource?\n");
+		return -ENODEV;
+	}
+
+	irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+	if (!irq) {
+		dev_err(&pdev->dev, "no irq resource?\n");
+		return -ENODEV;
+	}
+
+	if (!devm_request_mem_region(&pdev->dev, mem->start, resource_size(mem),
+				pdev->dev.driver->name)) {
+		dev_err(&pdev->dev, "memory region already claimed\n");
+		return -EBUSY;
+	}
+
+	dma_rx = platform_get_resource_byname(pdev, IORESOURCE_DMA, "rx");
+	if (!dma_rx)
+		return -ENXIO;
+
+	dma_tx = platform_get_resource_byname(pdev, IORESOURCE_DMA, "tx");
+	if (!dma_tx)
+		return -ENXIO;
+
+	up = devm_kzalloc(&pdev->dev, sizeof(*up), GFP_KERNEL);
+	if (!up)
+		return -ENOMEM;
+
+	up->pdev = pdev;
+	up->port.dev = &pdev->dev;
+	up->port.type = PORT_OMAP;
+	up->port.iotype = UPIO_MEM;
+	up->port.irq = irq->start;
+
+	up->port.regshift = 2;
+	up->port.fifosize = 64;
+	up->port.ops = &serial_omap_pops;
+
+	if (pdev->dev.of_node)
+		up->port.line = of_alias_get_id(pdev->dev.of_node, "serial");
+	else
+		up->port.line = pdev->id;
+
+	if (up->port.line < 0) {
+		dev_err(&pdev->dev, "failed to get alias/pdev id, errno %d\n",
+								up->port.line);
+		ret = -ENODEV;
+		goto err_port_line;
+	}
+
+	sprintf(up->name, "OMAP UART%d", up->port.line);
+	up->port.mapbase = mem->start;
+	up->port.membase = devm_ioremap(&pdev->dev, mem->start,
+						resource_size(mem));
+	if (!up->port.membase) {
+		dev_err(&pdev->dev, "can't ioremap UART\n");
+		ret = -ENOMEM;
+		goto err_ioremap;
+	}
+
+	up->port.flags = omap_up_info->flags;
+	up->port.uartclk = omap_up_info->uartclk;
+	if (!up->port.uartclk) {
+		up->port.uartclk = DEFAULT_CLK_SPEED;
+		dev_warn(&pdev->dev, "No clock speed specified: using default:"
+						"%d\n", DEFAULT_CLK_SPEED);
+	}
+	up->uart_dma.uart_base = mem->start;
+	up->errata = omap_up_info->errata;
+
+	if (omap_up_info->dma_enabled) {
+		up->uart_dma.uart_dma_tx = dma_tx->start;
+		up->uart_dma.uart_dma_rx = dma_rx->start;
+		up->use_dma = 1;
+		up->uart_dma.rx_buf_size = omap_up_info->dma_rx_buf_size;
+		up->uart_dma.rx_timeout = omap_up_info->dma_rx_timeout;
+		up->uart_dma.rx_poll_rate = omap_up_info->dma_rx_poll_rate;
+		spin_lock_init(&(up->uart_dma.tx_lock));
+		spin_lock_init(&(up->uart_dma.rx_lock));
+		up->uart_dma.tx_dma_channel = OMAP_UART_DMA_CH_FREE;
+		up->uart_dma.rx_dma_channel = OMAP_UART_DMA_CH_FREE;
+	}
+
+	up->latency = PM_QOS_CPU_DMA_LAT_DEFAULT_VALUE;
+	up->calc_latency = PM_QOS_CPU_DMA_LAT_DEFAULT_VALUE;
+	pm_qos_add_request(&up->pm_qos_request,
+		PM_QOS_CPU_DMA_LATENCY, up->latency);
+	serial_omap_uart_wq = create_singlethread_workqueue(up->name);
+	INIT_WORK(&up->qos_work, serial_omap_uart_qos_work);
+
+	pm_runtime_use_autosuspend(&pdev->dev);
+	pm_runtime_set_autosuspend_delay(&pdev->dev,
+			omap_up_info->autosuspend_timeout);
+
+	pm_runtime_irq_safe(&pdev->dev);
+	pm_runtime_enable(&pdev->dev);
+	pm_runtime_get_sync(&pdev->dev);
+
+	ui[up->port.line] = up;
+	serial_omap_add_console_port(up);
+
+	ret = uart_add_one_port(&serial_omap_reg, &up->port);
+	if (ret != 0)
+		goto err_add_port;
+
+	pm_runtime_put(&pdev->dev);
+	platform_set_drvdata(pdev, up);
+	return 0;
+
+err_add_port:
+	pm_runtime_put(&pdev->dev);
+	pm_runtime_disable(&pdev->dev);
+err_ioremap:
+err_port_line:
+	dev_err(&pdev->dev, "[UART%d]: failure [%s]: %d\n",
+				pdev->id, __func__, ret);
+	return ret;
+}
+
+static int serial_omap_remove(struct platform_device *dev)
+{
+	struct uart_omap_port *up = platform_get_drvdata(dev);
+
+	if (up) {
+		pm_runtime_disable(&up->pdev->dev);
+		uart_remove_one_port(&serial_omap_reg, &up->port);
+		pm_qos_remove_request(&up->pm_qos_request);
+	}
+
+	platform_set_drvdata(dev, NULL);
+	return 0;
+}
+
+/*
+ * Work Around for Errata i202 (2430, 3430, 3630, 4430 and 4460)
+ * The access to uart register after MDR1 Access
+ * causes UART to corrupt data.
+ *
+ * Need a delay =
+ * 5 L4 clock cycles + 5 UART functional clock cycle (@48MHz = ~0.2uS)
+ * give 10 times as much
+ */
+static void serial_omap_mdr1_errataset(struct uart_omap_port *up, u8 mdr1)
+{
+	u8 timeout = 255;
+
+	serial_out(up, UART_OMAP_MDR1, mdr1);
+	udelay(2);
+	serial_out(up, UART_FCR, up->fcr | UART_FCR_CLEAR_XMIT |
+			UART_FCR_CLEAR_RCVR);
+	/*
+	 * Wait for FIFO to empty: when empty, RX_FIFO_E bit is 0 and
+	 * TX_FIFO_E bit is 1.
+	 */
+	while (UART_LSR_THRE != (serial_in(up, UART_LSR) &
+				(UART_LSR_THRE | UART_LSR_DR))) {
+		timeout--;
+		if (!timeout) {
+			/* Should *never* happen. we warn and carry on */
+			dev_crit(&up->pdev->dev, "Errata i202: timedout %x\n",
+						serial_in(up, UART_LSR));
+			break;
+		}
+		udelay(1);
+	}
+}
+
+#ifdef CONFIG_PM_RUNTIME
+static void serial_omap_restore_context(struct uart_omap_port *up)
+{
+	if (up->errata & UART_ERRATA_i202_MDR1_ACCESS)
+		serial_omap_mdr1_errataset(up, UART_OMAP_MDR1_DISABLE);
+	else
+		serial_out(up, UART_OMAP_MDR1, UART_OMAP_MDR1_DISABLE);
+
+	serial_out(up, UART_LCR, UART_LCR_CONF_MODE_B); /* Config B mode */
+	serial_out(up, UART_EFR, UART_EFR_ECB);
+	serial_out(up, UART_LCR, 0x0); /* Operational mode */
+	serial_out(up, UART_IER, 0x0);
+	serial_out(up, UART_LCR, UART_LCR_CONF_MODE_B); /* Config B mode */
+	serial_out(up, UART_DLL, up->dll);
+	serial_out(up, UART_DLM, up->dlh);
+	serial_out(up, UART_LCR, 0x0); /* Operational mode */
+	serial_out(up, UART_IER, up->ier);
+	serial_out(up, UART_FCR, up->fcr);
+	serial_out(up, UART_LCR, UART_LCR_CONF_MODE_A);
+	serial_out(up, UART_MCR, up->mcr);
+	serial_out(up, UART_LCR, UART_LCR_CONF_MODE_B); /* Config B mode */
+	serial_out(up, UART_OMAP_SCR, up->scr);
+	serial_out(up, UART_EFR, up->efr);
+	serial_out(up, UART_LCR, up->lcr);
+	if (up->errata & UART_ERRATA_i202_MDR1_ACCESS)
+		serial_omap_mdr1_errataset(up, up->mdr1);
+	else
+		serial_out(up, UART_OMAP_MDR1, up->mdr1);
+}
+
+static int serial_omap_runtime_suspend(struct device *dev)
+{
+	struct uart_omap_port *up = dev_get_drvdata(dev);
+	struct omap_uart_port_info *pdata = dev->platform_data;
+
+	if (!up)
+		return -EINVAL;
+
+	if (!pdata || !pdata->enable_wakeup)
+		return 0;
+
+	if (pdata->get_context_loss_count)
+		up->context_loss_cnt = pdata->get_context_loss_count(dev);
+
+	if (device_may_wakeup(dev)) {
+		if (!up->wakeups_enabled) {
+			pdata->enable_wakeup(up->pdev, true);
+			up->wakeups_enabled = true;
+		}
+	} else {
+		if (up->wakeups_enabled) {
+			pdata->enable_wakeup(up->pdev, false);
+			up->wakeups_enabled = false;
+		}
+	}
+
+	/* Errata i291 */
+	if (up->use_dma && pdata->set_forceidle &&
+			(up->errata & UART_ERRATA_i291_DMA_FORCEIDLE))
+		pdata->set_forceidle(up->pdev);
+
+	up->latency = PM_QOS_CPU_DMA_LAT_DEFAULT_VALUE;
+	schedule_work(&up->qos_work);
+
+	return 0;
+}
+
+static int serial_omap_runtime_resume(struct device *dev)
+{
+	struct uart_omap_port *up = dev_get_drvdata(dev);
+	struct omap_uart_port_info *pdata = dev->platform_data;
+
+	if (up && pdata) {
+		if (pdata->get_context_loss_count) {
+			u32 loss_cnt = pdata->get_context_loss_count(dev);
+
+			if (up->context_loss_cnt != loss_cnt)
+				serial_omap_restore_context(up);
+		}
+
+		/* Errata i291 */
+		if (up->use_dma && pdata->set_noidle &&
+				(up->errata & UART_ERRATA_i291_DMA_FORCEIDLE))
+			pdata->set_noidle(up->pdev);
+
+		up->latency = up->calc_latency;
+		schedule_work(&up->qos_work);
+	}
+
+	return 0;
+}
+#endif
+
+static const struct dev_pm_ops serial_omap_dev_pm_ops = {
+	SET_SYSTEM_SLEEP_PM_OPS(serial_omap_suspend, serial_omap_resume)
+	SET_RUNTIME_PM_OPS(serial_omap_runtime_suspend,
+				serial_omap_runtime_resume, NULL)
+};
+
+#if defined(CONFIG_OF)
+static const struct of_device_id omap_serial_of_match[] = {
+	{ .compatible = "ti,omap2-uart" },
+	{ .compatible = "ti,omap3-uart" },
+	{ .compatible = "ti,omap4-uart" },
+	{},
+};
+MODULE_DEVICE_TABLE(of, omap_serial_of_match);
+#endif
+
+static struct platform_driver serial_omap_driver = {
+	.probe          = serial_omap_probe,
+	.remove         = serial_omap_remove,
+	.driver		= {
+		.name	= DRIVER_NAME,
+		.pm	= &serial_omap_dev_pm_ops,
+		.of_match_table = of_match_ptr(omap_serial_of_match),
+	},
+};
+
+static int __init serial_omap_init(void)
+{
+	int ret;
+
+	ret = uart_register_driver(&serial_omap_reg);
+	if (ret != 0)
+		return ret;
+	ret = platform_driver_register(&serial_omap_driver);
+	if (ret != 0)
+		uart_unregister_driver(&serial_omap_reg);
+	return ret;
+}
+
+static void __exit serial_omap_exit(void)
+{
+	platform_driver_unregister(&serial_omap_driver);
+	uart_unregister_driver(&serial_omap_reg);
+}
+
+module_init(serial_omap_init);
+module_exit(serial_omap_exit);
+
+MODULE_DESCRIPTION("OMAP High Speed UART driver");
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Texas Instruments Inc");
diff --git a/ap/os/linux/linux-3.4.x/drivers/tty/serial/pch_uart.c b/ap/os/linux/linux-3.4.x/drivers/tty/serial/pch_uart.c
new file mode 100644
index 0000000..e78d2a6
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/tty/serial/pch_uart.c
@@ -0,0 +1,1916 @@
+/*
+ *Copyright (C) 2011 LAPIS Semiconductor Co., Ltd.
+ *
+ *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; version 2 of the License.
+ *
+ *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, write to the Free Software
+ *Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307, USA.
+ */
+#include <linux/kernel.h>
+#include <linux/serial_reg.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/serial_core.h>
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/dmi.h>
+#include <linux/console.h>
+#include <linux/nmi.h>
+#include <linux/delay.h>
+
+#include <linux/debugfs.h>
+#include <linux/dmaengine.h>
+#include <linux/pch_dma.h>
+
+enum {
+	PCH_UART_HANDLED_RX_INT_SHIFT,
+	PCH_UART_HANDLED_TX_INT_SHIFT,
+	PCH_UART_HANDLED_RX_ERR_INT_SHIFT,
+	PCH_UART_HANDLED_RX_TRG_INT_SHIFT,
+	PCH_UART_HANDLED_MS_INT_SHIFT,
+};
+
+enum {
+	PCH_UART_8LINE,
+	PCH_UART_2LINE,
+};
+
+#define PCH_UART_DRIVER_DEVICE "ttyPCH"
+
+/* Set the max number of UART port
+ * Intel EG20T PCH: 4 port
+ * LAPIS Semiconductor ML7213 IOH: 3 port
+ * LAPIS Semiconductor ML7223 IOH: 2 port
+*/
+#define PCH_UART_NR	4
+
+#define PCH_UART_HANDLED_RX_INT	(1<<((PCH_UART_HANDLED_RX_INT_SHIFT)<<1))
+#define PCH_UART_HANDLED_TX_INT	(1<<((PCH_UART_HANDLED_TX_INT_SHIFT)<<1))
+#define PCH_UART_HANDLED_RX_ERR_INT	(1<<((\
+					PCH_UART_HANDLED_RX_ERR_INT_SHIFT)<<1))
+#define PCH_UART_HANDLED_RX_TRG_INT	(1<<((\
+					PCH_UART_HANDLED_RX_TRG_INT_SHIFT)<<1))
+#define PCH_UART_HANDLED_MS_INT	(1<<((PCH_UART_HANDLED_MS_INT_SHIFT)<<1))
+
+#define PCH_UART_RBR		0x00
+#define PCH_UART_THR		0x00
+
+#define PCH_UART_IER_MASK	(PCH_UART_IER_ERBFI|PCH_UART_IER_ETBEI|\
+				PCH_UART_IER_ELSI|PCH_UART_IER_EDSSI)
+#define PCH_UART_IER_ERBFI	0x00000001
+#define PCH_UART_IER_ETBEI	0x00000002
+#define PCH_UART_IER_ELSI	0x00000004
+#define PCH_UART_IER_EDSSI	0x00000008
+
+#define PCH_UART_IIR_IP			0x00000001
+#define PCH_UART_IIR_IID		0x00000006
+#define PCH_UART_IIR_MSI		0x00000000
+#define PCH_UART_IIR_TRI		0x00000002
+#define PCH_UART_IIR_RRI		0x00000004
+#define PCH_UART_IIR_REI		0x00000006
+#define PCH_UART_IIR_TOI		0x00000008
+#define PCH_UART_IIR_FIFO256		0x00000020
+#define PCH_UART_IIR_FIFO64		PCH_UART_IIR_FIFO256
+#define PCH_UART_IIR_FE			0x000000C0
+
+#define PCH_UART_FCR_FIFOE		0x00000001
+#define PCH_UART_FCR_RFR		0x00000002
+#define PCH_UART_FCR_TFR		0x00000004
+#define PCH_UART_FCR_DMS		0x00000008
+#define PCH_UART_FCR_FIFO256		0x00000020
+#define PCH_UART_FCR_RFTL		0x000000C0
+
+#define PCH_UART_FCR_RFTL1		0x00000000
+#define PCH_UART_FCR_RFTL64		0x00000040
+#define PCH_UART_FCR_RFTL128		0x00000080
+#define PCH_UART_FCR_RFTL224		0x000000C0
+#define PCH_UART_FCR_RFTL16		PCH_UART_FCR_RFTL64
+#define PCH_UART_FCR_RFTL32		PCH_UART_FCR_RFTL128
+#define PCH_UART_FCR_RFTL56		PCH_UART_FCR_RFTL224
+#define PCH_UART_FCR_RFTL4		PCH_UART_FCR_RFTL64
+#define PCH_UART_FCR_RFTL8		PCH_UART_FCR_RFTL128
+#define PCH_UART_FCR_RFTL14		PCH_UART_FCR_RFTL224
+#define PCH_UART_FCR_RFTL_SHIFT		6
+
+#define PCH_UART_LCR_WLS	0x00000003
+#define PCH_UART_LCR_STB	0x00000004
+#define PCH_UART_LCR_PEN	0x00000008
+#define PCH_UART_LCR_EPS	0x00000010
+#define PCH_UART_LCR_SP		0x00000020
+#define PCH_UART_LCR_SB		0x00000040
+#define PCH_UART_LCR_DLAB	0x00000080
+#define PCH_UART_LCR_NP		0x00000000
+#define PCH_UART_LCR_OP		PCH_UART_LCR_PEN
+#define PCH_UART_LCR_EP		(PCH_UART_LCR_PEN | PCH_UART_LCR_EPS)
+#define PCH_UART_LCR_1P		(PCH_UART_LCR_PEN | PCH_UART_LCR_SP)
+#define PCH_UART_LCR_0P		(PCH_UART_LCR_PEN | PCH_UART_LCR_EPS |\
+				PCH_UART_LCR_SP)
+
+#define PCH_UART_LCR_5BIT	0x00000000
+#define PCH_UART_LCR_6BIT	0x00000001
+#define PCH_UART_LCR_7BIT	0x00000002
+#define PCH_UART_LCR_8BIT	0x00000003
+
+#define PCH_UART_MCR_DTR	0x00000001
+#define PCH_UART_MCR_RTS	0x00000002
+#define PCH_UART_MCR_OUT	0x0000000C
+#define PCH_UART_MCR_LOOP	0x00000010
+#define PCH_UART_MCR_AFE	0x00000020
+
+#define PCH_UART_LSR_DR		0x00000001
+#define PCH_UART_LSR_ERR	(1<<7)
+
+#define PCH_UART_MSR_DCTS	0x00000001
+#define PCH_UART_MSR_DDSR	0x00000002
+#define PCH_UART_MSR_TERI	0x00000004
+#define PCH_UART_MSR_DDCD	0x00000008
+#define PCH_UART_MSR_CTS	0x00000010
+#define PCH_UART_MSR_DSR	0x00000020
+#define PCH_UART_MSR_RI		0x00000040
+#define PCH_UART_MSR_DCD	0x00000080
+#define PCH_UART_MSR_DELTA	(PCH_UART_MSR_DCTS | PCH_UART_MSR_DDSR |\
+				PCH_UART_MSR_TERI | PCH_UART_MSR_DDCD)
+
+#define PCH_UART_DLL		0x00
+#define PCH_UART_DLM		0x01
+
+#define PCH_UART_BRCSR		0x0E
+
+#define PCH_UART_IID_RLS	(PCH_UART_IIR_REI)
+#define PCH_UART_IID_RDR	(PCH_UART_IIR_RRI)
+#define PCH_UART_IID_RDR_TO	(PCH_UART_IIR_RRI | PCH_UART_IIR_TOI)
+#define PCH_UART_IID_THRE	(PCH_UART_IIR_TRI)
+#define PCH_UART_IID_MS		(PCH_UART_IIR_MSI)
+
+#define PCH_UART_HAL_PARITY_NONE	(PCH_UART_LCR_NP)
+#define PCH_UART_HAL_PARITY_ODD		(PCH_UART_LCR_OP)
+#define PCH_UART_HAL_PARITY_EVEN	(PCH_UART_LCR_EP)
+#define PCH_UART_HAL_PARITY_FIX1	(PCH_UART_LCR_1P)
+#define PCH_UART_HAL_PARITY_FIX0	(PCH_UART_LCR_0P)
+#define PCH_UART_HAL_5BIT		(PCH_UART_LCR_5BIT)
+#define PCH_UART_HAL_6BIT		(PCH_UART_LCR_6BIT)
+#define PCH_UART_HAL_7BIT		(PCH_UART_LCR_7BIT)
+#define PCH_UART_HAL_8BIT		(PCH_UART_LCR_8BIT)
+#define PCH_UART_HAL_STB1		0
+#define PCH_UART_HAL_STB2		(PCH_UART_LCR_STB)
+
+#define PCH_UART_HAL_CLR_TX_FIFO	(PCH_UART_FCR_TFR)
+#define PCH_UART_HAL_CLR_RX_FIFO	(PCH_UART_FCR_RFR)
+#define PCH_UART_HAL_CLR_ALL_FIFO	(PCH_UART_HAL_CLR_TX_FIFO | \
+					PCH_UART_HAL_CLR_RX_FIFO)
+
+#define PCH_UART_HAL_DMA_MODE0		0
+#define PCH_UART_HAL_FIFO_DIS		0
+#define PCH_UART_HAL_FIFO16		(PCH_UART_FCR_FIFOE)
+#define PCH_UART_HAL_FIFO256		(PCH_UART_FCR_FIFOE | \
+					PCH_UART_FCR_FIFO256)
+#define PCH_UART_HAL_FIFO64		(PCH_UART_HAL_FIFO256)
+#define PCH_UART_HAL_TRIGGER1		(PCH_UART_FCR_RFTL1)
+#define PCH_UART_HAL_TRIGGER64		(PCH_UART_FCR_RFTL64)
+#define PCH_UART_HAL_TRIGGER128		(PCH_UART_FCR_RFTL128)
+#define PCH_UART_HAL_TRIGGER224		(PCH_UART_FCR_RFTL224)
+#define PCH_UART_HAL_TRIGGER16		(PCH_UART_FCR_RFTL16)
+#define PCH_UART_HAL_TRIGGER32		(PCH_UART_FCR_RFTL32)
+#define PCH_UART_HAL_TRIGGER56		(PCH_UART_FCR_RFTL56)
+#define PCH_UART_HAL_TRIGGER4		(PCH_UART_FCR_RFTL4)
+#define PCH_UART_HAL_TRIGGER8		(PCH_UART_FCR_RFTL8)
+#define PCH_UART_HAL_TRIGGER14		(PCH_UART_FCR_RFTL14)
+#define PCH_UART_HAL_TRIGGER_L		(PCH_UART_FCR_RFTL64)
+#define PCH_UART_HAL_TRIGGER_M		(PCH_UART_FCR_RFTL128)
+#define PCH_UART_HAL_TRIGGER_H		(PCH_UART_FCR_RFTL224)
+
+#define PCH_UART_HAL_RX_INT		(PCH_UART_IER_ERBFI)
+#define PCH_UART_HAL_TX_INT		(PCH_UART_IER_ETBEI)
+#define PCH_UART_HAL_RX_ERR_INT		(PCH_UART_IER_ELSI)
+#define PCH_UART_HAL_MS_INT		(PCH_UART_IER_EDSSI)
+#define PCH_UART_HAL_ALL_INT		(PCH_UART_IER_MASK)
+
+#define PCH_UART_HAL_DTR		(PCH_UART_MCR_DTR)
+#define PCH_UART_HAL_RTS		(PCH_UART_MCR_RTS)
+#define PCH_UART_HAL_OUT		(PCH_UART_MCR_OUT)
+#define PCH_UART_HAL_LOOP		(PCH_UART_MCR_LOOP)
+#define PCH_UART_HAL_AFE		(PCH_UART_MCR_AFE)
+
+#define PCI_VENDOR_ID_ROHM		0x10DB
+
+#define BOTH_EMPTY (UART_LSR_TEMT | UART_LSR_THRE)
+
+#define DEFAULT_UARTCLK   1843200 /*   1.8432 MHz */
+#define CMITC_UARTCLK   192000000 /* 192.0000 MHz */
+#define FRI2_64_UARTCLK  64000000 /*  64.0000 MHz */
+#define FRI2_48_UARTCLK  48000000 /*  48.0000 MHz */
+#define NTC1_UARTCLK     64000000 /*  64.0000 MHz */
+
+struct pch_uart_buffer {
+	unsigned char *buf;
+	int size;
+};
+
+struct eg20t_port {
+	struct uart_port port;
+	int port_type;
+	void __iomem *membase;
+	resource_size_t mapbase;
+	unsigned int iobase;
+	struct pci_dev *pdev;
+	int fifo_size;
+	int uartclk;
+	int start_tx;
+	int start_rx;
+	int tx_empty;
+	int int_dis_flag;
+	int trigger;
+	int trigger_level;
+	struct pch_uart_buffer rxbuf;
+	unsigned int dmsr;
+	unsigned int fcr;
+	unsigned int mcr;
+	unsigned int use_dma;
+	unsigned int use_dma_flag;
+	struct dma_async_tx_descriptor	*desc_tx;
+	struct dma_async_tx_descriptor	*desc_rx;
+	struct pch_dma_slave		param_tx;
+	struct pch_dma_slave		param_rx;
+	struct dma_chan			*chan_tx;
+	struct dma_chan			*chan_rx;
+	struct scatterlist		*sg_tx_p;
+	int				nent;
+	struct scatterlist		sg_rx;
+	int				tx_dma_use;
+	void				*rx_buf_virt;
+	dma_addr_t			rx_buf_dma;
+
+	struct dentry	*debugfs;
+
+	/* protect the eg20t_port private structure and io access to membase */
+	spinlock_t lock;
+};
+
+/**
+ * struct pch_uart_driver_data - private data structure for UART-DMA
+ * @port_type:			The number of DMA channel
+ * @line_no:			UART port line number (0, 1, 2...)
+ */
+struct pch_uart_driver_data {
+	int port_type;
+	int line_no;
+};
+
+enum pch_uart_num_t {
+	pch_et20t_uart0 = 0,
+	pch_et20t_uart1,
+	pch_et20t_uart2,
+	pch_et20t_uart3,
+	pch_ml7213_uart0,
+	pch_ml7213_uart1,
+	pch_ml7213_uart2,
+	pch_ml7223_uart0,
+	pch_ml7223_uart1,
+	pch_ml7831_uart0,
+	pch_ml7831_uart1,
+};
+
+static struct pch_uart_driver_data drv_dat[] = {
+	[pch_et20t_uart0] = {PCH_UART_8LINE, 0},
+	[pch_et20t_uart1] = {PCH_UART_2LINE, 1},
+	[pch_et20t_uart2] = {PCH_UART_2LINE, 2},
+	[pch_et20t_uart3] = {PCH_UART_2LINE, 3},
+	[pch_ml7213_uart0] = {PCH_UART_8LINE, 0},
+	[pch_ml7213_uart1] = {PCH_UART_2LINE, 1},
+	[pch_ml7213_uart2] = {PCH_UART_2LINE, 2},
+	[pch_ml7223_uart0] = {PCH_UART_8LINE, 0},
+	[pch_ml7223_uart1] = {PCH_UART_2LINE, 1},
+	[pch_ml7831_uart0] = {PCH_UART_8LINE, 0},
+	[pch_ml7831_uart1] = {PCH_UART_2LINE, 1},
+};
+
+#ifdef CONFIG_SERIAL_PCH_UART_CONSOLE
+static struct eg20t_port *pch_uart_ports[PCH_UART_NR];
+#endif
+static unsigned int default_baud = 9600;
+static unsigned int user_uartclk = 0;
+static const int trigger_level_256[4] = { 1, 64, 128, 224 };
+static const int trigger_level_64[4] = { 1, 16, 32, 56 };
+static const int trigger_level_16[4] = { 1, 4, 8, 14 };
+static const int trigger_level_1[4] = { 1, 1, 1, 1 };
+
+#ifdef CONFIG_DEBUG_FS
+
+#define PCH_REGS_BUFSIZE	1024
+
+
+static ssize_t port_show_regs(struct file *file, char __user *user_buf,
+				size_t count, loff_t *ppos)
+{
+	struct eg20t_port *priv = file->private_data;
+	char *buf;
+	u32 len = 0;
+	ssize_t ret;
+	unsigned char lcr;
+
+	buf = kzalloc(PCH_REGS_BUFSIZE, GFP_KERNEL);
+	if (!buf)
+		return 0;
+
+	len += snprintf(buf + len, PCH_REGS_BUFSIZE - len,
+			"PCH EG20T port[%d] regs:\n", priv->port.line);
+
+	len += snprintf(buf + len, PCH_REGS_BUFSIZE - len,
+			"=================================\n");
+	len += snprintf(buf + len, PCH_REGS_BUFSIZE - len,
+			"IER: \t0x%02x\n", ioread8(priv->membase + UART_IER));
+	len += snprintf(buf + len, PCH_REGS_BUFSIZE - len,
+			"IIR: \t0x%02x\n", ioread8(priv->membase + UART_IIR));
+	len += snprintf(buf + len, PCH_REGS_BUFSIZE - len,
+			"LCR: \t0x%02x\n", ioread8(priv->membase + UART_LCR));
+	len += snprintf(buf + len, PCH_REGS_BUFSIZE - len,
+			"MCR: \t0x%02x\n", ioread8(priv->membase + UART_MCR));
+	len += snprintf(buf + len, PCH_REGS_BUFSIZE - len,
+			"LSR: \t0x%02x\n", ioread8(priv->membase + UART_LSR));
+	len += snprintf(buf + len, PCH_REGS_BUFSIZE - len,
+			"MSR: \t0x%02x\n", ioread8(priv->membase + UART_MSR));
+	len += snprintf(buf + len, PCH_REGS_BUFSIZE - len,
+			"BRCSR: \t0x%02x\n",
+			ioread8(priv->membase + PCH_UART_BRCSR));
+
+	lcr = ioread8(priv->membase + UART_LCR);
+	iowrite8(PCH_UART_LCR_DLAB, priv->membase + UART_LCR);
+	len += snprintf(buf + len, PCH_REGS_BUFSIZE - len,
+			"DLL: \t0x%02x\n", ioread8(priv->membase + UART_DLL));
+	len += snprintf(buf + len, PCH_REGS_BUFSIZE - len,
+			"DLM: \t0x%02x\n", ioread8(priv->membase + UART_DLM));
+	iowrite8(lcr, priv->membase + UART_LCR);
+
+	if (len > PCH_REGS_BUFSIZE)
+		len = PCH_REGS_BUFSIZE;
+
+	ret =  simple_read_from_buffer(user_buf, count, ppos, buf, len);
+	kfree(buf);
+	return ret;
+}
+
+static const struct file_operations port_regs_ops = {
+	.owner		= THIS_MODULE,
+	.open		= simple_open,
+	.read		= port_show_regs,
+	.llseek		= default_llseek,
+};
+#endif	/* CONFIG_DEBUG_FS */
+
+/* Return UART clock, checking for board specific clocks. */
+static int pch_uart_get_uartclk(void)
+{
+	const char *cmp;
+
+	if (user_uartclk)
+		return user_uartclk;
+
+	cmp = dmi_get_system_info(DMI_BOARD_NAME);
+	if (cmp && strstr(cmp, "CM-iTC"))
+		return CMITC_UARTCLK;
+
+	cmp = dmi_get_system_info(DMI_BIOS_VERSION);
+	if (cmp && strnstr(cmp, "FRI2", 4))
+		return FRI2_64_UARTCLK;
+
+	cmp = dmi_get_system_info(DMI_PRODUCT_NAME);
+	if (cmp && strstr(cmp, "Fish River Island II"))
+		return FRI2_48_UARTCLK;
+
+	/* Kontron COMe-mTT10 (nanoETXexpress-TT) */
+	cmp = dmi_get_system_info(DMI_BOARD_NAME);
+	if (cmp && (strstr(cmp, "COMe-mTT") ||
+		    strstr(cmp, "nanoETXexpress-TT")))
+		return NTC1_UARTCLK;
+
+	return DEFAULT_UARTCLK;
+}
+
+static void pch_uart_hal_enable_interrupt(struct eg20t_port *priv,
+					  unsigned int flag)
+{
+	u8 ier = ioread8(priv->membase + UART_IER);
+	ier |= flag & PCH_UART_IER_MASK;
+	iowrite8(ier, priv->membase + UART_IER);
+}
+
+static void pch_uart_hal_disable_interrupt(struct eg20t_port *priv,
+					   unsigned int flag)
+{
+	u8 ier = ioread8(priv->membase + UART_IER);
+	ier &= ~(flag & PCH_UART_IER_MASK);
+	iowrite8(ier, priv->membase + UART_IER);
+}
+
+static int pch_uart_hal_set_line(struct eg20t_port *priv, int baud,
+				 unsigned int parity, unsigned int bits,
+				 unsigned int stb)
+{
+	unsigned int dll, dlm, lcr;
+	int div;
+
+	div = DIV_ROUND_CLOSEST(priv->uartclk / 16, baud);
+	if (div < 0 || USHRT_MAX <= div) {
+		dev_err(priv->port.dev, "Invalid Baud(div=0x%x)\n", div);
+		return -EINVAL;
+	}
+
+	dll = (unsigned int)div & 0x00FFU;
+	dlm = ((unsigned int)div >> 8) & 0x00FFU;
+
+	if (parity & ~(PCH_UART_LCR_PEN | PCH_UART_LCR_EPS | PCH_UART_LCR_SP)) {
+		dev_err(priv->port.dev, "Invalid parity(0x%x)\n", parity);
+		return -EINVAL;
+	}
+
+	if (bits & ~PCH_UART_LCR_WLS) {
+		dev_err(priv->port.dev, "Invalid bits(0x%x)\n", bits);
+		return -EINVAL;
+	}
+
+	if (stb & ~PCH_UART_LCR_STB) {
+		dev_err(priv->port.dev, "Invalid STB(0x%x)\n", stb);
+		return -EINVAL;
+	}
+
+	lcr = parity;
+	lcr |= bits;
+	lcr |= stb;
+
+	dev_dbg(priv->port.dev, "%s:baud = %d, div = %04x, lcr = %02x (%lu)\n",
+		 __func__, baud, div, lcr, jiffies);
+	iowrite8(PCH_UART_LCR_DLAB, priv->membase + UART_LCR);
+	iowrite8(dll, priv->membase + PCH_UART_DLL);
+	iowrite8(dlm, priv->membase + PCH_UART_DLM);
+	iowrite8(lcr, priv->membase + UART_LCR);
+
+	return 0;
+}
+
+static int pch_uart_hal_fifo_reset(struct eg20t_port *priv,
+				    unsigned int flag)
+{
+	if (flag & ~(PCH_UART_FCR_TFR | PCH_UART_FCR_RFR)) {
+		dev_err(priv->port.dev, "%s:Invalid flag(0x%x)\n",
+			__func__, flag);
+		return -EINVAL;
+	}
+
+	iowrite8(PCH_UART_FCR_FIFOE | priv->fcr, priv->membase + UART_FCR);
+	iowrite8(PCH_UART_FCR_FIFOE | priv->fcr | flag,
+		 priv->membase + UART_FCR);
+	iowrite8(priv->fcr, priv->membase + UART_FCR);
+
+	return 0;
+}
+
+static int pch_uart_hal_set_fifo(struct eg20t_port *priv,
+				 unsigned int dmamode,
+				 unsigned int fifo_size, unsigned int trigger)
+{
+	u8 fcr;
+
+	if (dmamode & ~PCH_UART_FCR_DMS) {
+		dev_err(priv->port.dev, "%s:Invalid DMA Mode(0x%x)\n",
+			__func__, dmamode);
+		return -EINVAL;
+	}
+
+	if (fifo_size & ~(PCH_UART_FCR_FIFOE | PCH_UART_FCR_FIFO256)) {
+		dev_err(priv->port.dev, "%s:Invalid FIFO SIZE(0x%x)\n",
+			__func__, fifo_size);
+		return -EINVAL;
+	}
+
+	if (trigger & ~PCH_UART_FCR_RFTL) {
+		dev_err(priv->port.dev, "%s:Invalid TRIGGER(0x%x)\n",
+			__func__, trigger);
+		return -EINVAL;
+	}
+
+	switch (priv->fifo_size) {
+	case 256:
+		priv->trigger_level =
+		    trigger_level_256[trigger >> PCH_UART_FCR_RFTL_SHIFT];
+		break;
+	case 64:
+		priv->trigger_level =
+		    trigger_level_64[trigger >> PCH_UART_FCR_RFTL_SHIFT];
+		break;
+	case 16:
+		priv->trigger_level =
+		    trigger_level_16[trigger >> PCH_UART_FCR_RFTL_SHIFT];
+		break;
+	default:
+		priv->trigger_level =
+		    trigger_level_1[trigger >> PCH_UART_FCR_RFTL_SHIFT];
+		break;
+	}
+	fcr =
+	    dmamode | fifo_size | trigger | PCH_UART_FCR_RFR | PCH_UART_FCR_TFR;
+	iowrite8(PCH_UART_FCR_FIFOE, priv->membase + UART_FCR);
+	iowrite8(PCH_UART_FCR_FIFOE | PCH_UART_FCR_RFR | PCH_UART_FCR_TFR,
+		 priv->membase + UART_FCR);
+	iowrite8(fcr, priv->membase + UART_FCR);
+	priv->fcr = fcr;
+
+	return 0;
+}
+
+static u8 pch_uart_hal_get_modem(struct eg20t_port *priv)
+{
+	unsigned int msr = ioread8(priv->membase + UART_MSR);
+	priv->dmsr = msr & PCH_UART_MSR_DELTA;
+	return (u8)msr;
+}
+
+static void pch_uart_hal_write(struct eg20t_port *priv,
+			      const unsigned char *buf, int tx_size)
+{
+	int i;
+	unsigned int thr;
+
+	for (i = 0; i < tx_size;) {
+		thr = buf[i++];
+		iowrite8(thr, priv->membase + PCH_UART_THR);
+	}
+}
+
+static int pch_uart_hal_read(struct eg20t_port *priv, unsigned char *buf,
+			     int rx_size)
+{
+	int i;
+	u8 rbr, lsr;
+
+	lsr = ioread8(priv->membase + UART_LSR);
+	for (i = 0, lsr = ioread8(priv->membase + UART_LSR);
+	     i < rx_size && lsr & UART_LSR_DR;
+	     lsr = ioread8(priv->membase + UART_LSR)) {
+		rbr = ioread8(priv->membase + PCH_UART_RBR);
+		buf[i++] = rbr;
+	}
+	return i;
+}
+
+static unsigned int pch_uart_hal_get_iid(struct eg20t_port *priv)
+{
+	unsigned int iir;
+	int ret;
+
+	iir = ioread8(priv->membase + UART_IIR);
+	ret = (iir & (PCH_UART_IIR_IID | PCH_UART_IIR_TOI | PCH_UART_IIR_IP));
+	return ret;
+}
+
+static u8 pch_uart_hal_get_line_status(struct eg20t_port *priv)
+{
+	return ioread8(priv->membase + UART_LSR);
+}
+
+static void pch_uart_hal_set_break(struct eg20t_port *priv, int on)
+{
+	unsigned int lcr;
+
+	lcr = ioread8(priv->membase + UART_LCR);
+	if (on)
+		lcr |= PCH_UART_LCR_SB;
+	else
+		lcr &= ~PCH_UART_LCR_SB;
+
+	iowrite8(lcr, priv->membase + UART_LCR);
+}
+
+static int push_rx(struct eg20t_port *priv, const unsigned char *buf,
+		   int size)
+{
+	struct uart_port *port;
+	struct tty_struct *tty;
+
+	port = &priv->port;
+	tty = tty_port_tty_get(&port->state->port);
+	if (!tty) {
+		dev_dbg(priv->port.dev, "%s:tty is busy now", __func__);
+		return -EBUSY;
+	}
+
+	tty_insert_flip_string(tty, buf, size);
+	tty_flip_buffer_push(tty);
+	tty_kref_put(tty);
+
+	return 0;
+}
+
+static int pop_tx_x(struct eg20t_port *priv, unsigned char *buf)
+{
+	int ret = 0;
+	struct uart_port *port = &priv->port;
+
+	if (port->x_char) {
+		dev_dbg(priv->port.dev, "%s:X character send %02x (%lu)\n",
+			__func__, port->x_char, jiffies);
+		buf[0] = port->x_char;
+		port->x_char = 0;
+		ret = 1;
+	}
+
+	return ret;
+}
+
+static int dma_push_rx(struct eg20t_port *priv, int size)
+{
+	struct tty_struct *tty;
+	int room;
+	struct uart_port *port = &priv->port;
+
+	port = &priv->port;
+	tty = tty_port_tty_get(&port->state->port);
+	if (!tty) {
+		dev_dbg(priv->port.dev, "%s:tty is busy now", __func__);
+		return 0;
+	}
+
+	room = tty_buffer_request_room(tty, size);
+
+	if (room < size)
+		dev_warn(port->dev, "Rx overrun: dropping %u bytes\n",
+			 size - room);
+	if (!room)
+		goto out;
+
+	tty_insert_flip_string(tty, sg_virt(&priv->sg_rx), size);
+
+	port->icount.rx += room;
+out:
+	tty_kref_put(tty);
+
+	return room;
+}
+
+static void pch_free_dma(struct uart_port *port)
+{
+	struct eg20t_port *priv;
+	priv = container_of(port, struct eg20t_port, port);
+
+	if (priv->chan_tx) {
+		dma_release_channel(priv->chan_tx);
+		priv->chan_tx = NULL;
+	}
+	if (priv->chan_rx) {
+		dma_release_channel(priv->chan_rx);
+		priv->chan_rx = NULL;
+	}
+	if (sg_dma_address(&priv->sg_rx))
+		dma_free_coherent(port->dev, port->fifosize,
+				  sg_virt(&priv->sg_rx),
+				  sg_dma_address(&priv->sg_rx));
+
+	return;
+}
+
+static bool filter(struct dma_chan *chan, void *slave)
+{
+	struct pch_dma_slave *param = slave;
+
+	if ((chan->chan_id == param->chan_id) && (param->dma_dev ==
+						  chan->device->dev)) {
+		chan->private = param;
+		return true;
+	} else {
+		return false;
+	}
+}
+
+static void pch_request_dma(struct uart_port *port)
+{
+	dma_cap_mask_t mask;
+	struct dma_chan *chan;
+	struct pci_dev *dma_dev;
+	struct pch_dma_slave *param;
+	struct eg20t_port *priv =
+				container_of(port, struct eg20t_port, port);
+	dma_cap_zero(mask);
+	dma_cap_set(DMA_SLAVE, mask);
+
+	dma_dev = pci_get_bus_and_slot(priv->pdev->bus->number,
+				       PCI_DEVFN(0xa, 0)); /* Get DMA's dev
+								information */
+	/* Set Tx DMA */
+	param = &priv->param_tx;
+	param->dma_dev = &dma_dev->dev;
+	param->chan_id = priv->port.line * 2; /* Tx = 0, 2, 4, ... */
+
+	param->tx_reg = port->mapbase + UART_TX;
+	chan = dma_request_channel(mask, filter, param);
+	if (!chan) {
+		dev_err(priv->port.dev, "%s:dma_request_channel FAILS(Tx)\n",
+			__func__);
+		return;
+	}
+	priv->chan_tx = chan;
+
+	/* Set Rx DMA */
+	param = &priv->param_rx;
+	param->dma_dev = &dma_dev->dev;
+	param->chan_id = priv->port.line * 2 + 1; /* Rx = Tx + 1 */
+
+	param->rx_reg = port->mapbase + UART_RX;
+	chan = dma_request_channel(mask, filter, param);
+	if (!chan) {
+		dev_err(priv->port.dev, "%s:dma_request_channel FAILS(Rx)\n",
+			__func__);
+		dma_release_channel(priv->chan_tx);
+		priv->chan_tx = NULL;
+		return;
+	}
+
+	/* Get Consistent memory for DMA */
+	priv->rx_buf_virt = dma_alloc_coherent(port->dev, port->fifosize,
+				    &priv->rx_buf_dma, GFP_KERNEL);
+	priv->chan_rx = chan;
+}
+
+static void pch_dma_rx_complete(void *arg)
+{
+	struct eg20t_port *priv = arg;
+	struct uart_port *port = &priv->port;
+	struct tty_struct *tty = tty_port_tty_get(&port->state->port);
+	int count;
+
+	if (!tty) {
+		dev_dbg(priv->port.dev, "%s:tty is busy now", __func__);
+		return;
+	}
+
+	dma_sync_sg_for_cpu(port->dev, &priv->sg_rx, 1, DMA_FROM_DEVICE);
+	count = dma_push_rx(priv, priv->trigger_level);
+	if (count)
+		tty_flip_buffer_push(tty);
+	tty_kref_put(tty);
+	async_tx_ack(priv->desc_rx);
+	pch_uart_hal_enable_interrupt(priv, PCH_UART_HAL_RX_INT |
+					    PCH_UART_HAL_RX_ERR_INT);
+}
+
+static void pch_dma_tx_complete(void *arg)
+{
+	struct eg20t_port *priv = arg;
+	struct uart_port *port = &priv->port;
+	struct circ_buf *xmit = &port->state->xmit;
+	struct scatterlist *sg = priv->sg_tx_p;
+	int i;
+
+	for (i = 0; i < priv->nent; i++, sg++) {
+		xmit->tail += sg_dma_len(sg);
+		port->icount.tx += sg_dma_len(sg);
+	}
+	xmit->tail &= UART_XMIT_SIZE - 1;
+	async_tx_ack(priv->desc_tx);
+	dma_unmap_sg(port->dev, sg, priv->nent, DMA_TO_DEVICE);
+	priv->tx_dma_use = 0;
+	priv->nent = 0;
+	kfree(priv->sg_tx_p);
+	pch_uart_hal_enable_interrupt(priv, PCH_UART_HAL_TX_INT);
+}
+
+static int pop_tx(struct eg20t_port *priv, int size)
+{
+	int count = 0;
+	struct uart_port *port = &priv->port;
+	struct circ_buf *xmit = &port->state->xmit;
+
+	if (uart_tx_stopped(port) || uart_circ_empty(xmit) || count >= size)
+		goto pop_tx_end;
+
+	do {
+		int cnt_to_end =
+		    CIRC_CNT_TO_END(xmit->head, xmit->tail, UART_XMIT_SIZE);
+		int sz = min(size - count, cnt_to_end);
+		pch_uart_hal_write(priv, &xmit->buf[xmit->tail], sz);
+		xmit->tail = (xmit->tail + sz) & (UART_XMIT_SIZE - 1);
+		count += sz;
+	} while (!uart_circ_empty(xmit) && count < size);
+
+pop_tx_end:
+	dev_dbg(priv->port.dev, "%d characters. Remained %d characters.(%lu)\n",
+		 count, size - count, jiffies);
+
+	return count;
+}
+
+static int handle_rx_to(struct eg20t_port *priv)
+{
+	struct pch_uart_buffer *buf;
+	int rx_size;
+	int ret;
+	if (!priv->start_rx) {
+		pch_uart_hal_disable_interrupt(priv, PCH_UART_HAL_RX_INT |
+						     PCH_UART_HAL_RX_ERR_INT);
+		return 0;
+	}
+	buf = &priv->rxbuf;
+	do {
+		rx_size = pch_uart_hal_read(priv, buf->buf, buf->size);
+		ret = push_rx(priv, buf->buf, rx_size);
+		if (ret)
+			return 0;
+	} while (rx_size == buf->size);
+
+	return PCH_UART_HANDLED_RX_INT;
+}
+
+static int handle_rx(struct eg20t_port *priv)
+{
+	return handle_rx_to(priv);
+}
+
+static int dma_handle_rx(struct eg20t_port *priv)
+{
+	struct uart_port *port = &priv->port;
+	struct dma_async_tx_descriptor *desc;
+	struct scatterlist *sg;
+
+	priv = container_of(port, struct eg20t_port, port);
+	sg = &priv->sg_rx;
+
+	sg_init_table(&priv->sg_rx, 1); /* Initialize SG table */
+
+	sg_dma_len(sg) = priv->trigger_level;
+
+	sg_set_page(&priv->sg_rx, virt_to_page(priv->rx_buf_virt),
+		     sg_dma_len(sg), (unsigned long)priv->rx_buf_virt &
+		     ~PAGE_MASK);
+
+	sg_dma_address(sg) = priv->rx_buf_dma;
+
+	desc = dmaengine_prep_slave_sg(priv->chan_rx,
+			sg, 1, DMA_DEV_TO_MEM,
+			DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
+
+	if (!desc)
+		return 0;
+
+	priv->desc_rx = desc;
+	desc->callback = pch_dma_rx_complete;
+	desc->callback_param = priv;
+	desc->tx_submit(desc);
+	dma_async_issue_pending(priv->chan_rx);
+
+	return PCH_UART_HANDLED_RX_INT;
+}
+
+static unsigned int handle_tx(struct eg20t_port *priv)
+{
+	struct uart_port *port = &priv->port;
+	struct circ_buf *xmit = &port->state->xmit;
+	int fifo_size;
+	int tx_size;
+	int size;
+	int tx_empty;
+
+	if (!priv->start_tx) {
+		dev_info(priv->port.dev, "%s:Tx isn't started. (%lu)\n",
+			__func__, jiffies);
+		pch_uart_hal_disable_interrupt(priv, PCH_UART_HAL_TX_INT);
+		priv->tx_empty = 1;
+		return 0;
+	}
+
+	fifo_size = max(priv->fifo_size, 1);
+	tx_empty = 1;
+	if (pop_tx_x(priv, xmit->buf)) {
+		pch_uart_hal_write(priv, xmit->buf, 1);
+		port->icount.tx++;
+		tx_empty = 0;
+		fifo_size--;
+	}
+	size = min(xmit->head - xmit->tail, fifo_size);
+	if (size < 0)
+		size = fifo_size;
+
+	tx_size = pop_tx(priv, size);
+	if (tx_size > 0) {
+		port->icount.tx += tx_size;
+		tx_empty = 0;
+	}
+
+	priv->tx_empty = tx_empty;
+
+	if (tx_empty) {
+		pch_uart_hal_disable_interrupt(priv, PCH_UART_HAL_TX_INT);
+		uart_write_wakeup(port);
+	}
+
+	return PCH_UART_HANDLED_TX_INT;
+}
+
+static unsigned int dma_handle_tx(struct eg20t_port *priv)
+{
+	struct uart_port *port = &priv->port;
+	struct circ_buf *xmit = &port->state->xmit;
+	struct scatterlist *sg;
+	int nent;
+	int fifo_size;
+	int tx_empty;
+	struct dma_async_tx_descriptor *desc;
+	int num;
+	int i;
+	int bytes;
+	int size;
+	int rem;
+
+	if (!priv->start_tx) {
+		dev_info(priv->port.dev, "%s:Tx isn't started. (%lu)\n",
+			__func__, jiffies);
+		pch_uart_hal_disable_interrupt(priv, PCH_UART_HAL_TX_INT);
+		priv->tx_empty = 1;
+		return 0;
+	}
+
+	if (priv->tx_dma_use) {
+		dev_dbg(priv->port.dev, "%s:Tx is not completed. (%lu)\n",
+			__func__, jiffies);
+		pch_uart_hal_disable_interrupt(priv, PCH_UART_HAL_TX_INT);
+		priv->tx_empty = 1;
+		return 0;
+	}
+
+	fifo_size = max(priv->fifo_size, 1);
+	tx_empty = 1;
+	if (pop_tx_x(priv, xmit->buf)) {
+		pch_uart_hal_write(priv, xmit->buf, 1);
+		port->icount.tx++;
+		tx_empty = 0;
+		fifo_size--;
+	}
+
+	bytes = min((int)CIRC_CNT(xmit->head, xmit->tail,
+			     UART_XMIT_SIZE), CIRC_CNT_TO_END(xmit->head,
+			     xmit->tail, UART_XMIT_SIZE));
+	if (!bytes) {
+		dev_dbg(priv->port.dev, "%s 0 bytes return\n", __func__);
+		pch_uart_hal_disable_interrupt(priv, PCH_UART_HAL_TX_INT);
+		uart_write_wakeup(port);
+		return 0;
+	}
+
+	if (bytes > fifo_size) {
+		num = bytes / fifo_size + 1;
+		size = fifo_size;
+		rem = bytes % fifo_size;
+	} else {
+		num = 1;
+		size = bytes;
+		rem = bytes;
+	}
+
+	dev_dbg(priv->port.dev, "%s num=%d size=%d rem=%d\n",
+		__func__, num, size, rem);
+
+	priv->tx_dma_use = 1;
+
+	priv->sg_tx_p = kzalloc(sizeof(struct scatterlist)*num, GFP_ATOMIC);
+
+	sg_init_table(priv->sg_tx_p, num); /* Initialize SG table */
+	sg = priv->sg_tx_p;
+
+	for (i = 0; i < num; i++, sg++) {
+		if (i == (num - 1))
+			sg_set_page(sg, virt_to_page(xmit->buf),
+				    rem, fifo_size * i);
+		else
+			sg_set_page(sg, virt_to_page(xmit->buf),
+				    size, fifo_size * i);
+	}
+
+	sg = priv->sg_tx_p;
+	nent = dma_map_sg(port->dev, sg, num, DMA_TO_DEVICE);
+	if (!nent) {
+		dev_err(priv->port.dev, "%s:dma_map_sg Failed\n", __func__);
+		return 0;
+	}
+	priv->nent = nent;
+
+	for (i = 0; i < nent; i++, sg++) {
+		sg->offset = (xmit->tail & (UART_XMIT_SIZE - 1)) +
+			      fifo_size * i;
+		sg_dma_address(sg) = (sg_dma_address(sg) &
+				    ~(UART_XMIT_SIZE - 1)) + sg->offset;
+		if (i == (nent - 1))
+			sg_dma_len(sg) = rem;
+		else
+			sg_dma_len(sg) = size;
+	}
+
+	desc = dmaengine_prep_slave_sg(priv->chan_tx,
+					priv->sg_tx_p, nent, DMA_MEM_TO_DEV,
+					DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
+	if (!desc) {
+		dev_err(priv->port.dev, "%s:device_prep_slave_sg Failed\n",
+			__func__);
+		return 0;
+	}
+	dma_sync_sg_for_device(port->dev, priv->sg_tx_p, nent, DMA_TO_DEVICE);
+	priv->desc_tx = desc;
+	desc->callback = pch_dma_tx_complete;
+	desc->callback_param = priv;
+
+	desc->tx_submit(desc);
+
+	dma_async_issue_pending(priv->chan_tx);
+
+	return PCH_UART_HANDLED_TX_INT;
+}
+
+static void pch_uart_err_ir(struct eg20t_port *priv, unsigned int lsr)
+{
+	u8 fcr = ioread8(priv->membase + UART_FCR);
+	struct uart_port *port = &priv->port;
+	struct tty_struct *tty = tty_port_tty_get(&port->state->port);
+	char   *error_msg[5] = {};
+	int    i = 0;
+
+	/* Reset FIFO */
+	fcr |= UART_FCR_CLEAR_RCVR;
+	iowrite8(fcr, priv->membase + UART_FCR);
+
+	if (lsr & PCH_UART_LSR_ERR)
+		error_msg[i++] = "Error data in FIFO\n";
+
+	if (lsr & UART_LSR_FE) {
+		port->icount.frame++;
+		error_msg[i++] = "  Framing Error\n";
+	}
+
+	if (lsr & UART_LSR_PE) {
+		port->icount.parity++;
+		error_msg[i++] = "  Parity Error\n";
+	}
+
+	if (lsr & UART_LSR_OE) {
+		port->icount.overrun++;
+		error_msg[i++] = "  Overrun Error\n";
+	}
+
+	if (tty == NULL) {
+		for (i = 0; error_msg[i] != NULL; i++)
+			dev_err(&priv->pdev->dev, error_msg[i]);
+	} else {
+		tty_kref_put(tty);
+	}
+}
+
+static irqreturn_t pch_uart_interrupt(int irq, void *dev_id)
+{
+	struct eg20t_port *priv = dev_id;
+	unsigned int handled;
+	u8 lsr;
+	int ret = 0;
+	unsigned int iid;
+	unsigned long flags;
+
+	spin_lock_irqsave(&priv->lock, flags);
+	handled = 0;
+	while ((iid = pch_uart_hal_get_iid(priv)) > 1) {
+		switch (iid) {
+		case PCH_UART_IID_RLS:	/* Receiver Line Status */
+			lsr = pch_uart_hal_get_line_status(priv);
+			if (lsr & (PCH_UART_LSR_ERR | UART_LSR_FE |
+						UART_LSR_PE | UART_LSR_OE)) {
+				pch_uart_err_ir(priv, lsr);
+				ret = PCH_UART_HANDLED_RX_ERR_INT;
+			}
+			break;
+		case PCH_UART_IID_RDR:	/* Received Data Ready */
+			if (priv->use_dma) {
+				pch_uart_hal_disable_interrupt(priv,
+						PCH_UART_HAL_RX_INT |
+						PCH_UART_HAL_RX_ERR_INT);
+				ret = dma_handle_rx(priv);
+				if (!ret)
+					pch_uart_hal_enable_interrupt(priv,
+						PCH_UART_HAL_RX_INT |
+						PCH_UART_HAL_RX_ERR_INT);
+			} else {
+				ret = handle_rx(priv);
+			}
+			break;
+		case PCH_UART_IID_RDR_TO:	/* Received Data Ready
+						   (FIFO Timeout) */
+			ret = handle_rx_to(priv);
+			break;
+		case PCH_UART_IID_THRE:	/* Transmitter Holding Register
+						   Empty */
+			if (priv->use_dma)
+				ret = dma_handle_tx(priv);
+			else
+				ret = handle_tx(priv);
+			break;
+		case PCH_UART_IID_MS:	/* Modem Status */
+			ret = PCH_UART_HANDLED_MS_INT;
+			break;
+		default:	/* Never junp to this label */
+			dev_err(priv->port.dev, "%s:iid=%d (%lu)\n", __func__,
+				iid, jiffies);
+			ret = -1;
+			break;
+		}
+		handled |= (unsigned int)ret;
+	}
+	if (handled == 0 && iid <= 1) {
+		if (priv->int_dis_flag)
+			priv->int_dis_flag = 0;
+	}
+
+	spin_unlock_irqrestore(&priv->lock, flags);
+	return IRQ_RETVAL(handled);
+}
+
+/* This function tests whether the transmitter fifo and shifter for the port
+						described by 'port' is empty. */
+static unsigned int pch_uart_tx_empty(struct uart_port *port)
+{
+	struct eg20t_port *priv;
+
+	priv = container_of(port, struct eg20t_port, port);
+	if (priv->tx_empty)
+		return TIOCSER_TEMT;
+	else
+		return 0;
+}
+
+/* Returns the current state of modem control inputs. */
+static unsigned int pch_uart_get_mctrl(struct uart_port *port)
+{
+	struct eg20t_port *priv;
+	u8 modem;
+	unsigned int ret = 0;
+
+	priv = container_of(port, struct eg20t_port, port);
+	modem = pch_uart_hal_get_modem(priv);
+
+	if (modem & UART_MSR_DCD)
+		ret |= TIOCM_CAR;
+
+	if (modem & UART_MSR_RI)
+		ret |= TIOCM_RNG;
+
+	if (modem & UART_MSR_DSR)
+		ret |= TIOCM_DSR;
+
+	if (modem & UART_MSR_CTS)
+		ret |= TIOCM_CTS;
+
+	return ret;
+}
+
+static void pch_uart_set_mctrl(struct uart_port *port, unsigned int mctrl)
+{
+	u32 mcr = 0;
+	struct eg20t_port *priv = container_of(port, struct eg20t_port, port);
+
+	if (mctrl & TIOCM_DTR)
+		mcr |= UART_MCR_DTR;
+	if (mctrl & TIOCM_RTS)
+		mcr |= UART_MCR_RTS;
+	if (mctrl & TIOCM_LOOP)
+		mcr |= UART_MCR_LOOP;
+
+	if (priv->mcr & UART_MCR_AFE)
+		mcr |= UART_MCR_AFE;
+
+	if (mctrl)
+		iowrite8(mcr, priv->membase + UART_MCR);
+}
+
+static void pch_uart_stop_tx(struct uart_port *port)
+{
+	struct eg20t_port *priv;
+	priv = container_of(port, struct eg20t_port, port);
+	priv->start_tx = 0;
+	priv->tx_dma_use = 0;
+}
+
+static void pch_uart_start_tx(struct uart_port *port)
+{
+	struct eg20t_port *priv;
+
+	priv = container_of(port, struct eg20t_port, port);
+
+	if (priv->use_dma) {
+		if (priv->tx_dma_use) {
+			dev_dbg(priv->port.dev, "%s : Tx DMA is NOT empty.\n",
+				__func__);
+			return;
+		}
+	}
+
+	priv->start_tx = 1;
+	pch_uart_hal_enable_interrupt(priv, PCH_UART_HAL_TX_INT);
+}
+
+static void pch_uart_stop_rx(struct uart_port *port)
+{
+	struct eg20t_port *priv;
+	priv = container_of(port, struct eg20t_port, port);
+	priv->start_rx = 0;
+	pch_uart_hal_disable_interrupt(priv, PCH_UART_HAL_RX_INT |
+					     PCH_UART_HAL_RX_ERR_INT);
+	priv->int_dis_flag = 1;
+}
+
+/* Enable the modem status interrupts. */
+static void pch_uart_enable_ms(struct uart_port *port)
+{
+	struct eg20t_port *priv;
+	priv = container_of(port, struct eg20t_port, port);
+	pch_uart_hal_enable_interrupt(priv, PCH_UART_HAL_MS_INT);
+}
+
+/* Control the transmission of a break signal. */
+static void pch_uart_break_ctl(struct uart_port *port, int ctl)
+{
+	struct eg20t_port *priv;
+	unsigned long flags;
+
+	priv = container_of(port, struct eg20t_port, port);
+	spin_lock_irqsave(&priv->lock, flags);
+	pch_uart_hal_set_break(priv, ctl);
+	spin_unlock_irqrestore(&priv->lock, flags);
+}
+
+/* Grab any interrupt resources and initialise any low level driver state. */
+static int pch_uart_startup(struct uart_port *port)
+{
+	struct eg20t_port *priv;
+	int ret;
+	int fifo_size;
+	int trigger_level;
+
+	priv = container_of(port, struct eg20t_port, port);
+	priv->tx_empty = 1;
+
+	if (port->uartclk)
+		priv->uartclk = port->uartclk;
+	else
+		port->uartclk = priv->uartclk;
+
+	pch_uart_hal_disable_interrupt(priv, PCH_UART_HAL_ALL_INT);
+	ret = pch_uart_hal_set_line(priv, default_baud,
+			      PCH_UART_HAL_PARITY_NONE, PCH_UART_HAL_8BIT,
+			      PCH_UART_HAL_STB1);
+	if (ret)
+		return ret;
+
+	switch (priv->fifo_size) {
+	case 256:
+		fifo_size = PCH_UART_HAL_FIFO256;
+		break;
+	case 64:
+		fifo_size = PCH_UART_HAL_FIFO64;
+		break;
+	case 16:
+		fifo_size = PCH_UART_HAL_FIFO16;
+		break;
+	case 1:
+	default:
+		fifo_size = PCH_UART_HAL_FIFO_DIS;
+		break;
+	}
+
+	switch (priv->trigger) {
+	case PCH_UART_HAL_TRIGGER1:
+		trigger_level = 1;
+		break;
+	case PCH_UART_HAL_TRIGGER_L:
+		trigger_level = priv->fifo_size / 4;
+		break;
+	case PCH_UART_HAL_TRIGGER_M:
+		trigger_level = priv->fifo_size / 2;
+		break;
+	case PCH_UART_HAL_TRIGGER_H:
+	default:
+		trigger_level = priv->fifo_size - (priv->fifo_size / 8);
+		break;
+	}
+
+	priv->trigger_level = trigger_level;
+	ret = pch_uart_hal_set_fifo(priv, PCH_UART_HAL_DMA_MODE0,
+				    fifo_size, priv->trigger);
+	if (ret < 0)
+		return ret;
+
+	ret = request_irq(priv->port.irq, pch_uart_interrupt, IRQF_SHARED,
+			KBUILD_MODNAME, priv);
+	if (ret < 0)
+		return ret;
+
+	if (priv->use_dma)
+		pch_request_dma(port);
+
+	priv->start_rx = 1;
+	pch_uart_hal_enable_interrupt(priv, PCH_UART_HAL_RX_INT |
+					    PCH_UART_HAL_RX_ERR_INT);
+	uart_update_timeout(port, CS8, default_baud);
+
+	return 0;
+}
+
+static void pch_uart_shutdown(struct uart_port *port)
+{
+	struct eg20t_port *priv;
+	int ret;
+
+	priv = container_of(port, struct eg20t_port, port);
+	pch_uart_hal_disable_interrupt(priv, PCH_UART_HAL_ALL_INT);
+	pch_uart_hal_fifo_reset(priv, PCH_UART_HAL_CLR_ALL_FIFO);
+	ret = pch_uart_hal_set_fifo(priv, PCH_UART_HAL_DMA_MODE0,
+			      PCH_UART_HAL_FIFO_DIS, PCH_UART_HAL_TRIGGER1);
+	if (ret)
+		dev_err(priv->port.dev,
+			"pch_uart_hal_set_fifo Failed(ret=%d)\n", ret);
+
+	pch_free_dma(port);
+
+	free_irq(priv->port.irq, priv);
+}
+
+/* Change the port parameters, including word length, parity, stop
+ *bits.  Update read_status_mask and ignore_status_mask to indicate
+ *the types of events we are interested in receiving.  */
+static void pch_uart_set_termios(struct uart_port *port,
+				 struct ktermios *termios, struct ktermios *old)
+{
+	int baud;
+	int rtn;
+	unsigned int parity, bits, stb;
+	struct eg20t_port *priv;
+	unsigned long flags;
+
+	priv = container_of(port, struct eg20t_port, port);
+	switch (termios->c_cflag & CSIZE) {
+	case CS5:
+		bits = PCH_UART_HAL_5BIT;
+		break;
+	case CS6:
+		bits = PCH_UART_HAL_6BIT;
+		break;
+	case CS7:
+		bits = PCH_UART_HAL_7BIT;
+		break;
+	default:		/* CS8 */
+		bits = PCH_UART_HAL_8BIT;
+		break;
+	}
+	if (termios->c_cflag & CSTOPB)
+		stb = PCH_UART_HAL_STB2;
+	else
+		stb = PCH_UART_HAL_STB1;
+
+	if (termios->c_cflag & PARENB) {
+		if (termios->c_cflag & PARODD)
+			parity = PCH_UART_HAL_PARITY_ODD;
+		else
+			parity = PCH_UART_HAL_PARITY_EVEN;
+
+	} else
+		parity = PCH_UART_HAL_PARITY_NONE;
+
+	/* Only UART0 has auto hardware flow function */
+	if ((termios->c_cflag & CRTSCTS) && (priv->fifo_size == 256))
+		priv->mcr |= UART_MCR_AFE;
+	else
+		priv->mcr &= ~UART_MCR_AFE;
+
+	termios->c_cflag &= ~CMSPAR; /* Mark/Space parity is not supported */
+
+	baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk / 16);
+
+	spin_lock_irqsave(&priv->lock, flags);
+	spin_lock(&port->lock);
+
+	uart_update_timeout(port, termios->c_cflag, baud);
+	rtn = pch_uart_hal_set_line(priv, baud, parity, bits, stb);
+	if (rtn)
+		goto out;
+
+	pch_uart_set_mctrl(&priv->port, priv->port.mctrl);
+	/* Don't rewrite B0 */
+	if (tty_termios_baud_rate(termios))
+		tty_termios_encode_baud_rate(termios, baud, baud);
+
+out:
+	spin_unlock(&port->lock);
+	spin_unlock_irqrestore(&priv->lock, flags);
+}
+
+static const char *pch_uart_type(struct uart_port *port)
+{
+	return KBUILD_MODNAME;
+}
+
+static void pch_uart_release_port(struct uart_port *port)
+{
+	struct eg20t_port *priv;
+
+	priv = container_of(port, struct eg20t_port, port);
+	pci_iounmap(priv->pdev, priv->membase);
+	pci_release_regions(priv->pdev);
+}
+
+static int pch_uart_request_port(struct uart_port *port)
+{
+	struct eg20t_port *priv;
+	int ret;
+	void __iomem *membase;
+
+	priv = container_of(port, struct eg20t_port, port);
+	ret = pci_request_regions(priv->pdev, KBUILD_MODNAME);
+	if (ret < 0)
+		return -EBUSY;
+
+	membase = pci_iomap(priv->pdev, 1, 0);
+	if (!membase) {
+		pci_release_regions(priv->pdev);
+		return -EBUSY;
+	}
+	priv->membase = port->membase = membase;
+
+	return 0;
+}
+
+static void pch_uart_config_port(struct uart_port *port, int type)
+{
+	struct eg20t_port *priv;
+
+	priv = container_of(port, struct eg20t_port, port);
+	if (type & UART_CONFIG_TYPE) {
+		port->type = priv->port_type;
+		pch_uart_request_port(port);
+	}
+}
+
+static int pch_uart_verify_port(struct uart_port *port,
+				struct serial_struct *serinfo)
+{
+	struct eg20t_port *priv;
+
+	priv = container_of(port, struct eg20t_port, port);
+	if (serinfo->flags & UPF_LOW_LATENCY) {
+		dev_info(priv->port.dev,
+			"PCH UART : Use PIO Mode (without DMA)\n");
+		priv->use_dma = 0;
+		serinfo->flags &= ~UPF_LOW_LATENCY;
+	} else {
+#ifndef CONFIG_PCH_DMA
+		dev_err(priv->port.dev, "%s : PCH DMA is not Loaded.\n",
+			__func__);
+		return -EOPNOTSUPP;
+#endif
+		priv->use_dma_flag = 1;
+		dev_info(priv->port.dev, "PCH UART : Use DMA Mode\n");
+		if (!priv->use_dma)
+			pch_request_dma(port);
+		priv->use_dma = 1;
+	}
+
+	return 0;
+}
+
+static struct uart_ops pch_uart_ops = {
+	.tx_empty = pch_uart_tx_empty,
+	.set_mctrl = pch_uart_set_mctrl,
+	.get_mctrl = pch_uart_get_mctrl,
+	.stop_tx = pch_uart_stop_tx,
+	.start_tx = pch_uart_start_tx,
+	.stop_rx = pch_uart_stop_rx,
+	.enable_ms = pch_uart_enable_ms,
+	.break_ctl = pch_uart_break_ctl,
+	.startup = pch_uart_startup,
+	.shutdown = pch_uart_shutdown,
+	.set_termios = pch_uart_set_termios,
+/*	.pm		= pch_uart_pm,		Not supported yet */
+/*	.set_wake	= pch_uart_set_wake,	Not supported yet */
+	.type = pch_uart_type,
+	.release_port = pch_uart_release_port,
+	.request_port = pch_uart_request_port,
+	.config_port = pch_uart_config_port,
+	.verify_port = pch_uart_verify_port
+};
+
+#ifdef CONFIG_SERIAL_PCH_UART_CONSOLE
+
+/*
+ *	Wait for transmitter & holding register to empty
+ */
+static void wait_for_xmitr(struct eg20t_port *up, int bits)
+{
+	unsigned int status, tmout = 10000;
+
+	/* Wait up to 10ms for the character(s) to be sent. */
+	for (;;) {
+		status = ioread8(up->membase + UART_LSR);
+
+		if ((status & bits) == bits)
+			break;
+		if (--tmout == 0)
+			break;
+		udelay(1);
+	}
+
+	/* Wait up to 1s for flow control if necessary */
+	if (up->port.flags & UPF_CONS_FLOW) {
+		unsigned int tmout;
+		for (tmout = 1000000; tmout; tmout--) {
+			unsigned int msr = ioread8(up->membase + UART_MSR);
+			if (msr & UART_MSR_CTS)
+				break;
+			udelay(1);
+			touch_nmi_watchdog();
+		}
+	}
+}
+
+static void pch_console_putchar(struct uart_port *port, int ch)
+{
+	struct eg20t_port *priv =
+		container_of(port, struct eg20t_port, port);
+
+	wait_for_xmitr(priv, UART_LSR_THRE);
+	iowrite8(ch, priv->membase + PCH_UART_THR);
+}
+
+/*
+ *	Print a string to the serial port trying not to disturb
+ *	any possible real use of the port...
+ *
+ *	The console_lock must be held when we get here.
+ */
+static void
+pch_console_write(struct console *co, const char *s, unsigned int count)
+{
+	struct eg20t_port *priv;
+	unsigned long flags;
+	int priv_locked = 1;
+	int port_locked = 1;
+	u8 ier;
+
+	priv = pch_uart_ports[co->index];
+
+	touch_nmi_watchdog();
+
+	local_irq_save(flags);
+	if (priv->port.sysrq) {
+		spin_lock(&priv->lock);
+		/* serial8250_handle_port() already took the port lock */
+		port_locked = 0;
+	} else if (oops_in_progress) {
+		priv_locked = spin_trylock(&priv->lock);
+		port_locked = spin_trylock(&priv->port.lock);
+	} else {
+		spin_lock(&priv->lock);
+		spin_lock(&priv->port.lock);
+	}
+
+	/*
+	 *	First save the IER then disable the interrupts
+	 */
+	ier = ioread8(priv->membase + UART_IER);
+
+	pch_uart_hal_disable_interrupt(priv, PCH_UART_HAL_ALL_INT);
+
+	uart_console_write(&priv->port, s, count, pch_console_putchar);
+
+	/*
+	 *	Finally, wait for transmitter to become empty
+	 *	and restore the IER
+	 */
+	wait_for_xmitr(priv, BOTH_EMPTY);
+	iowrite8(ier, priv->membase + UART_IER);
+
+	if (port_locked)
+		spin_unlock(&priv->port.lock);
+	if (priv_locked)
+		spin_unlock(&priv->lock);
+	local_irq_restore(flags);
+}
+
+static int __init pch_console_setup(struct console *co, char *options)
+{
+	struct uart_port *port;
+	int baud = default_baud;
+	int bits = 8;
+	int parity = 'n';
+	int flow = 'n';
+
+	/*
+	 * 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 >= PCH_UART_NR)
+		co->index = 0;
+	port = &pch_uart_ports[co->index]->port;
+
+	if (!port || (!port->iobase && !port->membase))
+		return -ENODEV;
+
+	port->uartclk = pch_uart_get_uartclk();
+
+	if (options)
+		uart_parse_options(options, &baud, &parity, &bits, &flow);
+
+	return uart_set_options(port, co, baud, parity, bits, flow);
+}
+
+static struct uart_driver pch_uart_driver;
+
+static struct console pch_console = {
+	.name		= PCH_UART_DRIVER_DEVICE,
+	.write		= pch_console_write,
+	.device		= uart_console_device,
+	.setup		= pch_console_setup,
+	.flags		= CON_PRINTBUFFER | CON_ANYTIME,
+	.index		= -1,
+	.data		= &pch_uart_driver,
+};
+
+#define PCH_CONSOLE	(&pch_console)
+#else
+#define PCH_CONSOLE	NULL
+#endif
+
+static struct uart_driver pch_uart_driver = {
+	.owner = THIS_MODULE,
+	.driver_name = KBUILD_MODNAME,
+	.dev_name = PCH_UART_DRIVER_DEVICE,
+	.major = 0,
+	.minor = 0,
+	.nr = PCH_UART_NR,
+	.cons = PCH_CONSOLE,
+};
+
+static struct eg20t_port *pch_uart_init_port(struct pci_dev *pdev,
+					     const struct pci_device_id *id)
+{
+	struct eg20t_port *priv;
+	int ret;
+	unsigned int iobase;
+	unsigned int mapbase;
+	unsigned char *rxbuf;
+	int fifosize;
+	int port_type;
+	struct pch_uart_driver_data *board;
+	char name[32];	/* for debugfs file name */
+
+	board = &drv_dat[id->driver_data];
+	port_type = board->port_type;
+
+	priv = kzalloc(sizeof(struct eg20t_port), GFP_KERNEL);
+	if (priv == NULL)
+		goto init_port_alloc_err;
+
+	rxbuf = (unsigned char *)__get_free_page(GFP_KERNEL);
+	if (!rxbuf)
+		goto init_port_free_txbuf;
+
+	switch (port_type) {
+	case PORT_UNKNOWN:
+		fifosize = 256; /* EG20T/ML7213: UART0 */
+		break;
+	case PORT_8250:
+		fifosize = 64; /* EG20T:UART1~3  ML7213: UART1~2*/
+		break;
+	default:
+		dev_err(&pdev->dev, "Invalid Port Type(=%d)\n", port_type);
+		goto init_port_hal_free;
+	}
+
+	pci_enable_msi(pdev);
+	pci_set_master(pdev);
+
+	spin_lock_init(&priv->lock);
+
+	iobase = pci_resource_start(pdev, 0);
+	mapbase = pci_resource_start(pdev, 1);
+	priv->mapbase = mapbase;
+	priv->iobase = iobase;
+	priv->pdev = pdev;
+	priv->tx_empty = 1;
+	priv->rxbuf.buf = rxbuf;
+	priv->rxbuf.size = PAGE_SIZE;
+
+	priv->fifo_size = fifosize;
+	priv->uartclk = pch_uart_get_uartclk();
+	priv->port_type = PORT_MAX_8250 + port_type + 1;
+	priv->port.dev = &pdev->dev;
+	priv->port.iobase = iobase;
+	priv->port.membase = NULL;
+	priv->port.mapbase = mapbase;
+	priv->port.irq = pdev->irq;
+	priv->port.iotype = UPIO_PORT;
+	priv->port.ops = &pch_uart_ops;
+	priv->port.flags = UPF_BOOT_AUTOCONF;
+	priv->port.fifosize = fifosize;
+	priv->port.line = board->line_no;
+	priv->trigger = PCH_UART_HAL_TRIGGER_M;
+
+	spin_lock_init(&priv->port.lock);
+
+	pci_set_drvdata(pdev, priv);
+	priv->trigger_level = 1;
+	priv->fcr = 0;
+
+#ifdef CONFIG_SERIAL_PCH_UART_CONSOLE
+	pch_uart_ports[board->line_no] = priv;
+#endif
+	ret = uart_add_one_port(&pch_uart_driver, &priv->port);
+	if (ret < 0)
+		goto init_port_hal_free;
+
+#ifdef CONFIG_DEBUG_FS
+	snprintf(name, sizeof(name), "uart%d_regs", board->line_no);
+	priv->debugfs = debugfs_create_file(name, S_IFREG | S_IRUGO,
+				NULL, priv, &port_regs_ops);
+#endif
+
+	return priv;
+
+init_port_hal_free:
+#ifdef CONFIG_SERIAL_PCH_UART_CONSOLE
+	pch_uart_ports[board->line_no] = NULL;
+#endif
+	free_page((unsigned long)rxbuf);
+init_port_free_txbuf:
+	kfree(priv);
+init_port_alloc_err:
+
+	return NULL;
+}
+
+static void pch_uart_exit_port(struct eg20t_port *priv)
+{
+
+#ifdef CONFIG_DEBUG_FS
+	if (priv->debugfs)
+		debugfs_remove(priv->debugfs);
+#endif
+	uart_remove_one_port(&pch_uart_driver, &priv->port);
+	pci_set_drvdata(priv->pdev, NULL);
+	free_page((unsigned long)priv->rxbuf.buf);
+}
+
+static void pch_uart_pci_remove(struct pci_dev *pdev)
+{
+	struct eg20t_port *priv = pci_get_drvdata(pdev);
+
+	pci_disable_msi(pdev);
+
+#ifdef CONFIG_SERIAL_PCH_UART_CONSOLE
+	pch_uart_ports[priv->port.line] = NULL;
+#endif
+	pch_uart_exit_port(priv);
+	pci_disable_device(pdev);
+	kfree(priv);
+	return;
+}
+#ifdef CONFIG_PM
+static int pch_uart_pci_suspend(struct pci_dev *pdev, pm_message_t state)
+{
+	struct eg20t_port *priv = pci_get_drvdata(pdev);
+
+	uart_suspend_port(&pch_uart_driver, &priv->port);
+
+	pci_save_state(pdev);
+	pci_set_power_state(pdev, pci_choose_state(pdev, state));
+	return 0;
+}
+
+static int pch_uart_pci_resume(struct pci_dev *pdev)
+{
+	struct eg20t_port *priv = pci_get_drvdata(pdev);
+	int ret;
+
+	pci_set_power_state(pdev, PCI_D0);
+	pci_restore_state(pdev);
+
+	ret = pci_enable_device(pdev);
+	if (ret) {
+		dev_err(&pdev->dev,
+		"%s-pci_enable_device failed(ret=%d) ", __func__, ret);
+		return ret;
+	}
+
+	uart_resume_port(&pch_uart_driver, &priv->port);
+
+	return 0;
+}
+#else
+#define pch_uart_pci_suspend NULL
+#define pch_uart_pci_resume NULL
+#endif
+
+static DEFINE_PCI_DEVICE_TABLE(pch_uart_pci_id) = {
+	{PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x8811),
+	 .driver_data = pch_et20t_uart0},
+	{PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x8812),
+	 .driver_data = pch_et20t_uart1},
+	{PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x8813),
+	 .driver_data = pch_et20t_uart2},
+	{PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x8814),
+	 .driver_data = pch_et20t_uart3},
+	{PCI_DEVICE(PCI_VENDOR_ID_ROHM, 0x8027),
+	 .driver_data = pch_ml7213_uart0},
+	{PCI_DEVICE(PCI_VENDOR_ID_ROHM, 0x8028),
+	 .driver_data = pch_ml7213_uart1},
+	{PCI_DEVICE(PCI_VENDOR_ID_ROHM, 0x8029),
+	 .driver_data = pch_ml7213_uart2},
+	{PCI_DEVICE(PCI_VENDOR_ID_ROHM, 0x800C),
+	 .driver_data = pch_ml7223_uart0},
+	{PCI_DEVICE(PCI_VENDOR_ID_ROHM, 0x800D),
+	 .driver_data = pch_ml7223_uart1},
+	{PCI_DEVICE(PCI_VENDOR_ID_ROHM, 0x8811),
+	 .driver_data = pch_ml7831_uart0},
+	{PCI_DEVICE(PCI_VENDOR_ID_ROHM, 0x8812),
+	 .driver_data = pch_ml7831_uart1},
+	{0,},
+};
+
+static int __devinit pch_uart_pci_probe(struct pci_dev *pdev,
+					const struct pci_device_id *id)
+{
+	int ret;
+	struct eg20t_port *priv;
+
+	ret = pci_enable_device(pdev);
+	if (ret < 0)
+		goto probe_error;
+
+	priv = pch_uart_init_port(pdev, id);
+	if (!priv) {
+		ret = -EBUSY;
+		goto probe_disable_device;
+	}
+	pci_set_drvdata(pdev, priv);
+
+	return ret;
+
+probe_disable_device:
+	pci_disable_msi(pdev);
+	pci_disable_device(pdev);
+probe_error:
+	return ret;
+}
+
+static struct pci_driver pch_uart_pci_driver = {
+	.name = "pch_uart",
+	.id_table = pch_uart_pci_id,
+	.probe = pch_uart_pci_probe,
+	.remove = __devexit_p(pch_uart_pci_remove),
+	.suspend = pch_uart_pci_suspend,
+	.resume = pch_uart_pci_resume,
+};
+
+static int __init pch_uart_module_init(void)
+{
+	int ret;
+
+	/* register as UART driver */
+	ret = uart_register_driver(&pch_uart_driver);
+	if (ret < 0)
+		return ret;
+
+	/* register as PCI driver */
+	ret = pci_register_driver(&pch_uart_pci_driver);
+	if (ret < 0)
+		uart_unregister_driver(&pch_uart_driver);
+
+	return ret;
+}
+module_init(pch_uart_module_init);
+
+static void __exit pch_uart_module_exit(void)
+{
+	pci_unregister_driver(&pch_uart_pci_driver);
+	uart_unregister_driver(&pch_uart_driver);
+}
+module_exit(pch_uart_module_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("Intel EG20T PCH UART PCI Driver");
+module_param(default_baud, uint, S_IRUGO);
+MODULE_PARM_DESC(default_baud,
+                 "Default BAUD for initial driver state and console (default 9600)");
+module_param(user_uartclk, uint, S_IRUGO);
+MODULE_PARM_DESC(user_uartclk,
+                 "Override UART default or board specific UART clock");
diff --git a/ap/os/linux/linux-3.4.x/drivers/tty/serial/pmac_zilog.c b/ap/os/linux/linux-3.4.x/drivers/tty/serial/pmac_zilog.c
new file mode 100644
index 0000000..3f93c47
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/tty/serial/pmac_zilog.c
@@ -0,0 +1,2068 @@
+/*
+ * Driver for PowerMac Z85c30 based ESCC cell found in the
+ * "macio" ASICs of various PowerMac models
+ * 
+ * Copyright (C) 2003 Ben. Herrenschmidt (benh@kernel.crashing.org)
+ *
+ * Derived from drivers/macintosh/macserial.c by Paul Mackerras
+ * and drivers/serial/sunzilog.c by David S. Miller
+ *
+ * Hrm... actually, I ripped most of sunzilog (Thanks David !) and
+ * adapted special tweaks needed for us. I don't think it's worth
+ * merging back those though. The DMA code still has to get in
+ * and once done, I expect that driver to remain fairly stable in
+ * the long term, unless we change the driver model again...
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ * 2004-08-06 Harald Welte <laforge@gnumonks.org>
+ *	- Enable BREAK interrupt
+ *	- Add support for sysreq
+ *
+ * TODO:   - Add DMA support
+ *         - Defer port shutdown to a few seconds after close
+ *         - maybe put something right into uap->clk_divisor
+ */
+
+#undef DEBUG
+#undef DEBUG_HARD
+#undef USE_CTRL_O_SYSRQ
+
+#include <linux/module.h>
+#include <linux/tty.h>
+
+#include <linux/tty_flip.h>
+#include <linux/major.h>
+#include <linux/string.h>
+#include <linux/fcntl.h>
+#include <linux/mm.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/console.h>
+#include <linux/adb.h>
+#include <linux/pmu.h>
+#include <linux/bitops.h>
+#include <linux/sysrq.h>
+#include <linux/mutex.h>
+#include <asm/sections.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+
+#ifdef CONFIG_PPC_PMAC
+#include <asm/prom.h>
+#include <asm/machdep.h>
+#include <asm/pmac_feature.h>
+#include <asm/dbdma.h>
+#include <asm/macio.h>
+#else
+#include <linux/platform_device.h>
+#define of_machine_is_compatible(x) (0)
+#endif
+
+#if defined (CONFIG_SERIAL_PMACZILOG_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ)
+#define SUPPORT_SYSRQ
+#endif
+
+#include <linux/serial.h>
+#include <linux/serial_core.h>
+
+#include "pmac_zilog.h"
+
+/* Not yet implemented */
+#undef HAS_DBDMA
+
+static char version[] __initdata = "pmac_zilog: 0.6 (Benjamin Herrenschmidt <benh@kernel.crashing.org>)";
+MODULE_AUTHOR("Benjamin Herrenschmidt <benh@kernel.crashing.org>");
+MODULE_DESCRIPTION("Driver for the Mac and PowerMac serial ports.");
+MODULE_LICENSE("GPL");
+
+#ifdef CONFIG_SERIAL_PMACZILOG_TTYS
+#define PMACZILOG_MAJOR		TTY_MAJOR
+#define PMACZILOG_MINOR		64
+#define PMACZILOG_NAME		"ttyS"
+#else
+#define PMACZILOG_MAJOR		204
+#define PMACZILOG_MINOR		192
+#define PMACZILOG_NAME		"ttyPZ"
+#endif
+
+#define pmz_debug(fmt, arg...)	pr_debug("ttyPZ%d: " fmt, uap->port.line, ## arg)
+#define pmz_error(fmt, arg...)	pr_err("ttyPZ%d: " fmt, uap->port.line, ## arg)
+#define pmz_info(fmt, arg...)	pr_info("ttyPZ%d: " fmt, uap->port.line, ## arg)
+
+/*
+ * For the sake of early serial console, we can do a pre-probe
+ * (optional) of the ports at rather early boot time.
+ */
+static struct uart_pmac_port	pmz_ports[MAX_ZS_PORTS];
+static int			pmz_ports_count;
+
+static struct uart_driver pmz_uart_reg = {
+	.owner		=	THIS_MODULE,
+	.driver_name	=	PMACZILOG_NAME,
+	.dev_name	=	PMACZILOG_NAME,
+	.major		=	PMACZILOG_MAJOR,
+	.minor		=	PMACZILOG_MINOR,
+};
+
+
+/* 
+ * Load all registers to reprogram the port
+ * This function must only be called when the TX is not busy.  The UART
+ * port lock must be held and local interrupts disabled.
+ */
+static void pmz_load_zsregs(struct uart_pmac_port *uap, u8 *regs)
+{
+	int i;
+
+	/* Let pending transmits finish.  */
+	for (i = 0; i < 1000; i++) {
+		unsigned char stat = read_zsreg(uap, R1);
+		if (stat & ALL_SNT)
+			break;
+		udelay(100);
+	}
+
+	ZS_CLEARERR(uap);
+	zssync(uap);
+	ZS_CLEARFIFO(uap);
+	zssync(uap);
+	ZS_CLEARERR(uap);
+
+	/* Disable all interrupts.  */
+	write_zsreg(uap, R1,
+		    regs[R1] & ~(RxINT_MASK | TxINT_ENAB | EXT_INT_ENAB));
+
+	/* Set parity, sync config, stop bits, and clock divisor.  */
+	write_zsreg(uap, R4, regs[R4]);
+
+	/* Set misc. TX/RX control bits.  */
+	write_zsreg(uap, R10, regs[R10]);
+
+	/* Set TX/RX controls sans the enable bits.  */
+	write_zsreg(uap, R3, regs[R3] & ~RxENABLE);
+	write_zsreg(uap, R5, regs[R5] & ~TxENABLE);
+
+	/* now set R7 "prime" on ESCC */
+	write_zsreg(uap, R15, regs[R15] | EN85C30);
+	write_zsreg(uap, R7, regs[R7P]);
+
+	/* make sure we use R7 "non-prime" on ESCC */
+	write_zsreg(uap, R15, regs[R15] & ~EN85C30);
+
+	/* Synchronous mode config.  */
+	write_zsreg(uap, R6, regs[R6]);
+	write_zsreg(uap, R7, regs[R7]);
+
+	/* Disable baud generator.  */
+	write_zsreg(uap, R14, regs[R14] & ~BRENAB);
+
+	/* Clock mode control.  */
+	write_zsreg(uap, R11, regs[R11]);
+
+	/* Lower and upper byte of baud rate generator divisor.  */
+	write_zsreg(uap, R12, regs[R12]);
+	write_zsreg(uap, R13, regs[R13]);
+	
+	/* Now rewrite R14, with BRENAB (if set).  */
+	write_zsreg(uap, R14, regs[R14]);
+
+	/* Reset external status interrupts.  */
+	write_zsreg(uap, R0, RES_EXT_INT);
+	write_zsreg(uap, R0, RES_EXT_INT);
+
+	/* Rewrite R3/R5, this time without enables masked.  */
+	write_zsreg(uap, R3, regs[R3]);
+	write_zsreg(uap, R5, regs[R5]);
+
+	/* Rewrite R1, this time without IRQ enabled masked.  */
+	write_zsreg(uap, R1, regs[R1]);
+
+	/* Enable interrupts */
+	write_zsreg(uap, R9, regs[R9]);
+}
+
+/* 
+ * We do like sunzilog to avoid disrupting pending Tx
+ * Reprogram the Zilog channel HW registers with the copies found in the
+ * software state struct.  If the transmitter is busy, we defer this update
+ * until the next TX complete interrupt.  Else, we do it right now.
+ *
+ * The UART port lock must be held and local interrupts disabled.
+ */
+static void pmz_maybe_update_regs(struct uart_pmac_port *uap)
+{
+	if (!ZS_REGS_HELD(uap)) {
+		if (ZS_TX_ACTIVE(uap)) {
+			uap->flags |= PMACZILOG_FLAG_REGS_HELD;
+		} else {
+			pmz_debug("pmz: maybe_update_regs: updating\n");
+			pmz_load_zsregs(uap, uap->curregs);
+		}
+	}
+}
+
+static void pmz_interrupt_control(struct uart_pmac_port *uap, int enable)
+{
+	if (enable) {
+		uap->curregs[1] |= INT_ALL_Rx | TxINT_ENAB;
+		if (!ZS_IS_EXTCLK(uap))
+			uap->curregs[1] |= EXT_INT_ENAB;
+	} else {
+		uap->curregs[1] &= ~(EXT_INT_ENAB | TxINT_ENAB | RxINT_MASK);
+	}
+	write_zsreg(uap, R1, uap->curregs[1]);
+}
+
+static struct tty_struct *pmz_receive_chars(struct uart_pmac_port *uap)
+{
+	struct tty_struct *tty = NULL;
+	unsigned char ch, r1, drop, error, flag;
+	int loops = 0;
+
+	/* Sanity check, make sure the old bug is no longer happening */
+	if (uap->port.state == NULL || uap->port.state->port.tty == NULL) {
+		WARN_ON(1);
+		(void)read_zsdata(uap);
+		return NULL;
+	}
+	tty = uap->port.state->port.tty;
+
+	while (1) {
+		error = 0;
+		drop = 0;
+
+		r1 = read_zsreg(uap, R1);
+		ch = read_zsdata(uap);
+
+		if (r1 & (PAR_ERR | Rx_OVR | CRC_ERR)) {
+			write_zsreg(uap, R0, ERR_RES);
+			zssync(uap);
+		}
+
+		ch &= uap->parity_mask;
+		if (ch == 0 && uap->flags & PMACZILOG_FLAG_BREAK) {
+			uap->flags &= ~PMACZILOG_FLAG_BREAK;
+		}
+
+#if defined(CONFIG_MAGIC_SYSRQ) && defined(CONFIG_SERIAL_CORE_CONSOLE)
+#ifdef USE_CTRL_O_SYSRQ
+		/* Handle the SysRq ^O Hack */
+		if (ch == '\x0f') {
+			uap->port.sysrq = jiffies + HZ*5;
+			goto next_char;
+		}
+#endif /* USE_CTRL_O_SYSRQ */
+		if (uap->port.sysrq) {
+			int swallow;
+			spin_unlock(&uap->port.lock);
+			swallow = uart_handle_sysrq_char(&uap->port, ch);
+			spin_lock(&uap->port.lock);
+			if (swallow)
+				goto next_char;
+		}
+#endif /* CONFIG_MAGIC_SYSRQ && CONFIG_SERIAL_CORE_CONSOLE */
+
+		/* A real serial line, record the character and status.  */
+		if (drop)
+			goto next_char;
+
+		flag = TTY_NORMAL;
+		uap->port.icount.rx++;
+
+		if (r1 & (PAR_ERR | Rx_OVR | CRC_ERR | BRK_ABRT)) {
+			error = 1;
+			if (r1 & BRK_ABRT) {
+				pmz_debug("pmz: got break !\n");
+				r1 &= ~(PAR_ERR | CRC_ERR);
+				uap->port.icount.brk++;
+				if (uart_handle_break(&uap->port))
+					goto next_char;
+			}
+			else if (r1 & PAR_ERR)
+				uap->port.icount.parity++;
+			else if (r1 & CRC_ERR)
+				uap->port.icount.frame++;
+			if (r1 & Rx_OVR)
+				uap->port.icount.overrun++;
+			r1 &= uap->port.read_status_mask;
+			if (r1 & BRK_ABRT)
+				flag = TTY_BREAK;
+			else if (r1 & PAR_ERR)
+				flag = TTY_PARITY;
+			else if (r1 & CRC_ERR)
+				flag = TTY_FRAME;
+		}
+
+		if (uap->port.ignore_status_mask == 0xff ||
+		    (r1 & uap->port.ignore_status_mask) == 0) {
+			tty_insert_flip_char(tty, ch, flag);
+		}
+		if (r1 & Rx_OVR)
+			tty_insert_flip_char(tty, 0, TTY_OVERRUN);
+	next_char:
+		/* We can get stuck in an infinite loop getting char 0 when the
+		 * line is in a wrong HW state, we break that here.
+		 * When that happens, I disable the receive side of the driver.
+		 * Note that what I've been experiencing is a real irq loop where
+		 * I'm getting flooded regardless of the actual port speed.
+		 * Something strange is going on with the HW
+		 */
+		if ((++loops) > 1000)
+			goto flood;
+		ch = read_zsreg(uap, R0);
+		if (!(ch & Rx_CH_AV))
+			break;
+	}
+
+	return tty;
+ flood:
+	pmz_interrupt_control(uap, 0);
+	pmz_error("pmz: rx irq flood !\n");
+	return tty;
+}
+
+static void pmz_status_handle(struct uart_pmac_port *uap)
+{
+	unsigned char status;
+
+	status = read_zsreg(uap, R0);
+	write_zsreg(uap, R0, RES_EXT_INT);
+	zssync(uap);
+
+	if (ZS_IS_OPEN(uap) && ZS_WANTS_MODEM_STATUS(uap)) {
+		if (status & SYNC_HUNT)
+			uap->port.icount.dsr++;
+
+		/* The Zilog just gives us an interrupt when DCD/CTS/etc. change.
+		 * But it does not tell us which bit has changed, we have to keep
+		 * track of this ourselves.
+		 * The CTS input is inverted for some reason.  -- paulus
+		 */
+		if ((status ^ uap->prev_status) & DCD)
+			uart_handle_dcd_change(&uap->port,
+					       (status & DCD));
+		if ((status ^ uap->prev_status) & CTS)
+			uart_handle_cts_change(&uap->port,
+					       !(status & CTS));
+
+		wake_up_interruptible(&uap->port.state->port.delta_msr_wait);
+	}
+
+	if (status & BRK_ABRT)
+		uap->flags |= PMACZILOG_FLAG_BREAK;
+
+	uap->prev_status = status;
+}
+
+static void pmz_transmit_chars(struct uart_pmac_port *uap)
+{
+	struct circ_buf *xmit;
+
+	if (ZS_IS_CONS(uap)) {
+		unsigned char status = read_zsreg(uap, R0);
+
+		/* TX still busy?  Just wait for the next TX done interrupt.
+		 *
+		 * It can occur because of how we do serial console writes.  It would
+		 * be nice to transmit console writes just like we normally would for
+		 * a TTY line. (ie. buffered and TX interrupt driven).  That is not
+		 * easy because console writes cannot sleep.  One solution might be
+		 * to poll on enough port->xmit space becoming free.  -DaveM
+		 */
+		if (!(status & Tx_BUF_EMP))
+			return;
+	}
+
+	uap->flags &= ~PMACZILOG_FLAG_TX_ACTIVE;
+
+	if (ZS_REGS_HELD(uap)) {
+		pmz_load_zsregs(uap, uap->curregs);
+		uap->flags &= ~PMACZILOG_FLAG_REGS_HELD;
+	}
+
+	if (ZS_TX_STOPPED(uap)) {
+		uap->flags &= ~PMACZILOG_FLAG_TX_STOPPED;
+		goto ack_tx_int;
+	}
+
+	/* Under some circumstances, we see interrupts reported for
+	 * a closed channel. The interrupt mask in R1 is clear, but
+	 * R3 still signals the interrupts and we see them when taking
+	 * an interrupt for the other channel (this could be a qemu
+	 * bug but since the ESCC doc doesn't specify precsiely whether
+	 * R3 interrup status bits are masked by R1 interrupt enable
+	 * bits, better safe than sorry). --BenH.
+	 */
+	if (!ZS_IS_OPEN(uap))
+		goto ack_tx_int;
+
+	if (uap->port.x_char) {
+		uap->flags |= PMACZILOG_FLAG_TX_ACTIVE;
+		write_zsdata(uap, uap->port.x_char);
+		zssync(uap);
+		uap->port.icount.tx++;
+		uap->port.x_char = 0;
+		return;
+	}
+
+	if (uap->port.state == NULL)
+		goto ack_tx_int;
+	xmit = &uap->port.state->xmit;
+	if (uart_circ_empty(xmit)) {
+		uart_write_wakeup(&uap->port);
+		goto ack_tx_int;
+	}
+	if (uart_tx_stopped(&uap->port))
+		goto ack_tx_int;
+
+	uap->flags |= PMACZILOG_FLAG_TX_ACTIVE;
+	write_zsdata(uap, xmit->buf[xmit->tail]);
+	zssync(uap);
+
+	xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
+	uap->port.icount.tx++;
+
+	if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
+		uart_write_wakeup(&uap->port);
+
+	return;
+
+ack_tx_int:
+	write_zsreg(uap, R0, RES_Tx_P);
+	zssync(uap);
+}
+
+/* Hrm... we register that twice, fixme later.... */
+static irqreturn_t pmz_interrupt(int irq, void *dev_id)
+{
+	struct uart_pmac_port *uap = dev_id;
+	struct uart_pmac_port *uap_a;
+	struct uart_pmac_port *uap_b;
+	int rc = IRQ_NONE;
+	struct tty_struct *tty;
+	u8 r3;
+
+	uap_a = pmz_get_port_A(uap);
+	uap_b = uap_a->mate;
+
+	spin_lock(&uap_a->port.lock);
+	r3 = read_zsreg(uap_a, R3);
+
+#ifdef DEBUG_HARD
+	pmz_debug("irq, r3: %x\n", r3);
+#endif
+	/* Channel A */
+	tty = NULL;
+	if (r3 & (CHAEXT | CHATxIP | CHARxIP)) {
+		if (!ZS_IS_OPEN(uap_a)) {
+			pmz_debug("ChanA interrupt while not open !\n");
+			goto skip_a;
+		}
+		write_zsreg(uap_a, R0, RES_H_IUS);
+		zssync(uap_a);		
+		if (r3 & CHAEXT)
+			pmz_status_handle(uap_a);
+		if (r3 & CHARxIP)
+			tty = pmz_receive_chars(uap_a);
+		if (r3 & CHATxIP)
+			pmz_transmit_chars(uap_a);
+		rc = IRQ_HANDLED;
+	}
+ skip_a:
+	spin_unlock(&uap_a->port.lock);
+	if (tty != NULL)
+		tty_flip_buffer_push(tty);
+
+	if (!uap_b)
+		goto out;
+
+	spin_lock(&uap_b->port.lock);
+	tty = NULL;
+	if (r3 & (CHBEXT | CHBTxIP | CHBRxIP)) {
+		if (!ZS_IS_OPEN(uap_b)) {
+			pmz_debug("ChanB interrupt while not open !\n");
+			goto skip_b;
+		}
+		write_zsreg(uap_b, R0, RES_H_IUS);
+		zssync(uap_b);
+		if (r3 & CHBEXT)
+			pmz_status_handle(uap_b);
+		if (r3 & CHBRxIP)
+			tty = pmz_receive_chars(uap_b);
+		if (r3 & CHBTxIP)
+			pmz_transmit_chars(uap_b);
+		rc = IRQ_HANDLED;
+	}
+ skip_b:
+	spin_unlock(&uap_b->port.lock);
+	if (tty != NULL)
+		tty_flip_buffer_push(tty);
+
+ out:
+	return rc;
+}
+
+/*
+ * Peek the status register, lock not held by caller
+ */
+static inline u8 pmz_peek_status(struct uart_pmac_port *uap)
+{
+	unsigned long flags;
+	u8 status;
+	
+	spin_lock_irqsave(&uap->port.lock, flags);
+	status = read_zsreg(uap, R0);
+	spin_unlock_irqrestore(&uap->port.lock, flags);
+
+	return status;
+}
+
+/* 
+ * Check if transmitter is empty
+ * The port lock is not held.
+ */
+static unsigned int pmz_tx_empty(struct uart_port *port)
+{
+	unsigned char status;
+
+	status = pmz_peek_status(to_pmz(port));
+	if (status & Tx_BUF_EMP)
+		return TIOCSER_TEMT;
+	return 0;
+}
+
+/* 
+ * Set Modem Control (RTS & DTR) bits
+ * The port lock is held and interrupts are disabled.
+ * Note: Shall we really filter out RTS on external ports or
+ * should that be dealt at higher level only ?
+ */
+static void pmz_set_mctrl(struct uart_port *port, unsigned int mctrl)
+{
+	struct uart_pmac_port *uap = to_pmz(port);
+	unsigned char set_bits, clear_bits;
+
+        /* Do nothing for irda for now... */
+	if (ZS_IS_IRDA(uap))
+		return;
+	/* We get called during boot with a port not up yet */
+	if (!(ZS_IS_OPEN(uap) || ZS_IS_CONS(uap)))
+		return;
+
+	set_bits = clear_bits = 0;
+
+	if (ZS_IS_INTMODEM(uap)) {
+		if (mctrl & TIOCM_RTS)
+			set_bits |= RTS;
+		else
+			clear_bits |= RTS;
+	}
+	if (mctrl & TIOCM_DTR)
+		set_bits |= DTR;
+	else
+		clear_bits |= DTR;
+
+	/* NOTE: Not subject to 'transmitter active' rule.  */ 
+	uap->curregs[R5] |= set_bits;
+	uap->curregs[R5] &= ~clear_bits;
+
+	write_zsreg(uap, R5, uap->curregs[R5]);
+	pmz_debug("pmz_set_mctrl: set bits: %x, clear bits: %x -> %x\n",
+		  set_bits, clear_bits, uap->curregs[R5]);
+	zssync(uap);
+}
+
+/* 
+ * Get Modem Control bits (only the input ones, the core will
+ * or that with a cached value of the control ones)
+ * The port lock is held and interrupts are disabled.
+ */
+static unsigned int pmz_get_mctrl(struct uart_port *port)
+{
+	struct uart_pmac_port *uap = to_pmz(port);
+	unsigned char status;
+	unsigned int ret;
+
+	status = read_zsreg(uap, R0);
+
+	ret = 0;
+	if (status & DCD)
+		ret |= TIOCM_CAR;
+	if (status & SYNC_HUNT)
+		ret |= TIOCM_DSR;
+	if (!(status & CTS))
+		ret |= TIOCM_CTS;
+
+	return ret;
+}
+
+/* 
+ * Stop TX side. Dealt like sunzilog at next Tx interrupt,
+ * though for DMA, we will have to do a bit more.
+ * The port lock is held and interrupts are disabled.
+ */
+static void pmz_stop_tx(struct uart_port *port)
+{
+	to_pmz(port)->flags |= PMACZILOG_FLAG_TX_STOPPED;
+}
+
+/* 
+ * Kick the Tx side.
+ * The port lock is held and interrupts are disabled.
+ */
+static void pmz_start_tx(struct uart_port *port)
+{
+	struct uart_pmac_port *uap = to_pmz(port);
+	unsigned char status;
+
+	pmz_debug("pmz: start_tx()\n");
+
+	uap->flags |= PMACZILOG_FLAG_TX_ACTIVE;
+	uap->flags &= ~PMACZILOG_FLAG_TX_STOPPED;
+
+	status = read_zsreg(uap, R0);
+
+	/* TX busy?  Just wait for the TX done interrupt.  */
+	if (!(status & Tx_BUF_EMP))
+		return;
+
+	/* Send the first character to jump-start the TX done
+	 * IRQ sending engine.
+	 */
+	if (port->x_char) {
+		write_zsdata(uap, port->x_char);
+		zssync(uap);
+		port->icount.tx++;
+		port->x_char = 0;
+	} else {
+		struct circ_buf *xmit = &port->state->xmit;
+
+		write_zsdata(uap, xmit->buf[xmit->tail]);
+		zssync(uap);
+		xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
+		port->icount.tx++;
+
+		if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
+			uart_write_wakeup(&uap->port);
+	}
+	pmz_debug("pmz: start_tx() done.\n");
+}
+
+/* 
+ * Stop Rx side, basically disable emitting of
+ * Rx interrupts on the port. We don't disable the rx
+ * side of the chip proper though
+ * The port lock is held.
+ */
+static void pmz_stop_rx(struct uart_port *port)
+{
+	struct uart_pmac_port *uap = to_pmz(port);
+
+	pmz_debug("pmz: stop_rx()()\n");
+
+	/* Disable all RX interrupts.  */
+	uap->curregs[R1] &= ~RxINT_MASK;
+	pmz_maybe_update_regs(uap);
+
+	pmz_debug("pmz: stop_rx() done.\n");
+}
+
+/* 
+ * Enable modem status change interrupts
+ * The port lock is held.
+ */
+static void pmz_enable_ms(struct uart_port *port)
+{
+	struct uart_pmac_port *uap = to_pmz(port);
+	unsigned char new_reg;
+
+	if (ZS_IS_IRDA(uap))
+		return;
+	new_reg = uap->curregs[R15] | (DCDIE | SYNCIE | CTSIE);
+	if (new_reg != uap->curregs[R15]) {
+		uap->curregs[R15] = new_reg;
+
+		/* NOTE: Not subject to 'transmitter active' rule. */
+		write_zsreg(uap, R15, uap->curregs[R15]);
+	}
+}
+
+/* 
+ * Control break state emission
+ * The port lock is not held.
+ */
+static void pmz_break_ctl(struct uart_port *port, int break_state)
+{
+	struct uart_pmac_port *uap = to_pmz(port);
+	unsigned char set_bits, clear_bits, new_reg;
+	unsigned long flags;
+
+	set_bits = clear_bits = 0;
+
+	if (break_state)
+		set_bits |= SND_BRK;
+	else
+		clear_bits |= SND_BRK;
+
+	spin_lock_irqsave(&port->lock, flags);
+
+	new_reg = (uap->curregs[R5] | set_bits) & ~clear_bits;
+	if (new_reg != uap->curregs[R5]) {
+		uap->curregs[R5] = new_reg;
+		write_zsreg(uap, R5, uap->curregs[R5]);
+	}
+
+	spin_unlock_irqrestore(&port->lock, flags);
+}
+
+#ifdef CONFIG_PPC_PMAC
+
+/*
+ * Turn power on or off to the SCC and associated stuff
+ * (port drivers, modem, IR port, etc.)
+ * Returns the number of milliseconds we should wait before
+ * trying to use the port.
+ */
+static int pmz_set_scc_power(struct uart_pmac_port *uap, int state)
+{
+	int delay = 0;
+	int rc;
+
+	if (state) {
+		rc = pmac_call_feature(
+			PMAC_FTR_SCC_ENABLE, uap->node, uap->port_type, 1);
+		pmz_debug("port power on result: %d\n", rc);
+		if (ZS_IS_INTMODEM(uap)) {
+			rc = pmac_call_feature(
+				PMAC_FTR_MODEM_ENABLE, uap->node, 0, 1);
+			delay = 2500;	/* wait for 2.5s before using */
+			pmz_debug("modem power result: %d\n", rc);
+		}
+	} else {
+		/* TODO: Make that depend on a timer, don't power down
+		 * immediately
+		 */
+		if (ZS_IS_INTMODEM(uap)) {
+			rc = pmac_call_feature(
+				PMAC_FTR_MODEM_ENABLE, uap->node, 0, 0);
+			pmz_debug("port power off result: %d\n", rc);
+		}
+		pmac_call_feature(PMAC_FTR_SCC_ENABLE, uap->node, uap->port_type, 0);
+	}
+	return delay;
+}
+
+#else
+
+static int pmz_set_scc_power(struct uart_pmac_port *uap, int state)
+{
+	return 0;
+}
+
+#endif /* !CONFIG_PPC_PMAC */
+
+/*
+ * FixZeroBug....Works around a bug in the SCC receiving channel.
+ * Inspired from Darwin code, 15 Sept. 2000  -DanM
+ *
+ * The following sequence prevents a problem that is seen with O'Hare ASICs
+ * (most versions -- also with some Heathrow and Hydra ASICs) where a zero
+ * at the input to the receiver becomes 'stuck' and locks up the receiver.
+ * This problem can occur as a result of a zero bit at the receiver input
+ * coincident with any of the following events:
+ *
+ *	The SCC is initialized (hardware or software).
+ *	A framing error is detected.
+ *	The clocking option changes from synchronous or X1 asynchronous
+ *		clocking to X16, X32, or X64 asynchronous clocking.
+ *	The decoding mode is changed among NRZ, NRZI, FM0, or FM1.
+ *
+ * This workaround attempts to recover from the lockup condition by placing
+ * the SCC in synchronous loopback mode with a fast clock before programming
+ * any of the asynchronous modes.
+ */
+static void pmz_fix_zero_bug_scc(struct uart_pmac_port *uap)
+{
+	write_zsreg(uap, 9, ZS_IS_CHANNEL_A(uap) ? CHRA : CHRB);
+	zssync(uap);
+	udelay(10);
+	write_zsreg(uap, 9, (ZS_IS_CHANNEL_A(uap) ? CHRA : CHRB) | NV);
+	zssync(uap);
+
+	write_zsreg(uap, 4, X1CLK | MONSYNC);
+	write_zsreg(uap, 3, Rx8);
+	write_zsreg(uap, 5, Tx8 | RTS);
+	write_zsreg(uap, 9, NV);	/* Didn't we already do this? */
+	write_zsreg(uap, 11, RCBR | TCBR);
+	write_zsreg(uap, 12, 0);
+	write_zsreg(uap, 13, 0);
+	write_zsreg(uap, 14, (LOOPBAK | BRSRC));
+	write_zsreg(uap, 14, (LOOPBAK | BRSRC | BRENAB));
+	write_zsreg(uap, 3, Rx8 | RxENABLE);
+	write_zsreg(uap, 0, RES_EXT_INT);
+	write_zsreg(uap, 0, RES_EXT_INT);
+	write_zsreg(uap, 0, RES_EXT_INT);	/* to kill some time */
+
+	/* The channel should be OK now, but it is probably receiving
+	 * loopback garbage.
+	 * Switch to asynchronous mode, disable the receiver,
+	 * and discard everything in the receive buffer.
+	 */
+	write_zsreg(uap, 9, NV);
+	write_zsreg(uap, 4, X16CLK | SB_MASK);
+	write_zsreg(uap, 3, Rx8);
+
+	while (read_zsreg(uap, 0) & Rx_CH_AV) {
+		(void)read_zsreg(uap, 8);
+		write_zsreg(uap, 0, RES_EXT_INT);
+		write_zsreg(uap, 0, ERR_RES);
+	}
+}
+
+/*
+ * Real startup routine, powers up the hardware and sets up
+ * the SCC. Returns a delay in ms where you need to wait before
+ * actually using the port, this is typically the internal modem
+ * powerup delay. This routine expect the lock to be taken.
+ */
+static int __pmz_startup(struct uart_pmac_port *uap)
+{
+	int pwr_delay = 0;
+
+	memset(&uap->curregs, 0, sizeof(uap->curregs));
+
+	/* Power up the SCC & underlying hardware (modem/irda) */
+	pwr_delay = pmz_set_scc_power(uap, 1);
+
+	/* Nice buggy HW ... */
+	pmz_fix_zero_bug_scc(uap);
+
+	/* Reset the channel */
+	uap->curregs[R9] = 0;
+	write_zsreg(uap, 9, ZS_IS_CHANNEL_A(uap) ? CHRA : CHRB);
+	zssync(uap);
+	udelay(10);
+	write_zsreg(uap, 9, 0);
+	zssync(uap);
+
+	/* Clear the interrupt registers */
+	write_zsreg(uap, R1, 0);
+	write_zsreg(uap, R0, ERR_RES);
+	write_zsreg(uap, R0, ERR_RES);
+	write_zsreg(uap, R0, RES_H_IUS);
+	write_zsreg(uap, R0, RES_H_IUS);
+
+	/* Setup some valid baud rate */
+	uap->curregs[R4] = X16CLK | SB1;
+	uap->curregs[R3] = Rx8;
+	uap->curregs[R5] = Tx8 | RTS;
+	if (!ZS_IS_IRDA(uap))
+		uap->curregs[R5] |= DTR;
+	uap->curregs[R12] = 0;
+	uap->curregs[R13] = 0;
+	uap->curregs[R14] = BRENAB;
+
+	/* Clear handshaking, enable BREAK interrupts */
+	uap->curregs[R15] = BRKIE;
+
+	/* Master interrupt enable */
+	uap->curregs[R9] |= NV | MIE;
+
+	pmz_load_zsregs(uap, uap->curregs);
+
+	/* Enable receiver and transmitter.  */
+	write_zsreg(uap, R3, uap->curregs[R3] |= RxENABLE);
+	write_zsreg(uap, R5, uap->curregs[R5] |= TxENABLE);
+
+	/* Remember status for DCD/CTS changes */
+	uap->prev_status = read_zsreg(uap, R0);
+
+	return pwr_delay;
+}
+
+static void pmz_irda_reset(struct uart_pmac_port *uap)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&uap->port.lock, flags);
+	uap->curregs[R5] |= DTR;
+	write_zsreg(uap, R5, uap->curregs[R5]);
+	zssync(uap);
+	spin_unlock_irqrestore(&uap->port.lock, flags);
+	msleep(110);
+
+	spin_lock_irqsave(&uap->port.lock, flags);
+	uap->curregs[R5] &= ~DTR;
+	write_zsreg(uap, R5, uap->curregs[R5]);
+	zssync(uap);
+	spin_unlock_irqrestore(&uap->port.lock, flags);
+	msleep(10);
+}
+
+/*
+ * This is the "normal" startup routine, using the above one
+ * wrapped with the lock and doing a schedule delay
+ */
+static int pmz_startup(struct uart_port *port)
+{
+	struct uart_pmac_port *uap = to_pmz(port);
+	unsigned long flags;
+	int pwr_delay = 0;
+
+	pmz_debug("pmz: startup()\n");
+
+	uap->flags |= PMACZILOG_FLAG_IS_OPEN;
+
+	/* A console is never powered down. Else, power up and
+	 * initialize the chip
+	 */
+	if (!ZS_IS_CONS(uap)) {
+		spin_lock_irqsave(&port->lock, flags);
+		pwr_delay = __pmz_startup(uap);
+		spin_unlock_irqrestore(&port->lock, flags);
+	}	
+	sprintf(uap->irq_name, PMACZILOG_NAME"%d", uap->port.line);
+	if (request_irq(uap->port.irq, pmz_interrupt, IRQF_SHARED,
+			uap->irq_name, uap)) {
+		pmz_error("Unable to register zs interrupt handler.\n");
+		pmz_set_scc_power(uap, 0);
+		return -ENXIO;
+	}
+
+	/* Right now, we deal with delay by blocking here, I'll be
+	 * smarter later on
+	 */
+	if (pwr_delay != 0) {
+		pmz_debug("pmz: delaying %d ms\n", pwr_delay);
+		msleep(pwr_delay);
+	}
+
+	/* IrDA reset is done now */
+	if (ZS_IS_IRDA(uap))
+		pmz_irda_reset(uap);
+
+	/* Enable interrupt requests for the channel */
+	spin_lock_irqsave(&port->lock, flags);
+	pmz_interrupt_control(uap, 1);
+	spin_unlock_irqrestore(&port->lock, flags);
+
+	pmz_debug("pmz: startup() done.\n");
+
+	return 0;
+}
+
+static void pmz_shutdown(struct uart_port *port)
+{
+	struct uart_pmac_port *uap = to_pmz(port);
+	unsigned long flags;
+
+	pmz_debug("pmz: shutdown()\n");
+
+	spin_lock_irqsave(&port->lock, flags);
+
+	/* Disable interrupt requests for the channel */
+	pmz_interrupt_control(uap, 0);
+
+	if (!ZS_IS_CONS(uap)) {
+		/* Disable receiver and transmitter */
+		uap->curregs[R3] &= ~RxENABLE;
+		uap->curregs[R5] &= ~TxENABLE;
+
+		/* Disable break assertion */
+		uap->curregs[R5] &= ~SND_BRK;
+		pmz_maybe_update_regs(uap);
+	}
+
+	spin_unlock_irqrestore(&port->lock, flags);
+
+	/* Release interrupt handler */
+	free_irq(uap->port.irq, uap);
+
+	spin_lock_irqsave(&port->lock, flags);
+
+	uap->flags &= ~PMACZILOG_FLAG_IS_OPEN;
+
+	if (!ZS_IS_CONS(uap))
+		pmz_set_scc_power(uap, 0);	/* Shut the chip down */
+
+	spin_unlock_irqrestore(&port->lock, flags);
+
+	pmz_debug("pmz: shutdown() done.\n");
+}
+
+/* Shared by TTY driver and serial console setup.  The port lock is held
+ * and local interrupts are disabled.
+ */
+static void pmz_convert_to_zs(struct uart_pmac_port *uap, unsigned int cflag,
+			      unsigned int iflag, unsigned long baud)
+{
+	int brg;
+
+	/* Switch to external clocking for IrDA high clock rates. That
+	 * code could be re-used for Midi interfaces with different
+	 * multipliers
+	 */
+	if (baud >= 115200 && ZS_IS_IRDA(uap)) {
+		uap->curregs[R4] = X1CLK;
+		uap->curregs[R11] = RCTRxCP | TCTRxCP;
+		uap->curregs[R14] = 0; /* BRG off */
+		uap->curregs[R12] = 0;
+		uap->curregs[R13] = 0;
+		uap->flags |= PMACZILOG_FLAG_IS_EXTCLK;
+	} else {
+		switch (baud) {
+		case ZS_CLOCK/16:	/* 230400 */
+			uap->curregs[R4] = X16CLK;
+			uap->curregs[R11] = 0;
+			uap->curregs[R14] = 0;
+			break;
+		case ZS_CLOCK/32:	/* 115200 */
+			uap->curregs[R4] = X32CLK;
+			uap->curregs[R11] = 0;
+			uap->curregs[R14] = 0;
+			break;
+		default:
+			uap->curregs[R4] = X16CLK;
+			uap->curregs[R11] = TCBR | RCBR;
+			brg = BPS_TO_BRG(baud, ZS_CLOCK / 16);
+			uap->curregs[R12] = (brg & 255);
+			uap->curregs[R13] = ((brg >> 8) & 255);
+			uap->curregs[R14] = BRENAB;
+		}
+		uap->flags &= ~PMACZILOG_FLAG_IS_EXTCLK;
+	}
+
+	/* Character size, stop bits, and parity. */
+	uap->curregs[3] &= ~RxN_MASK;
+	uap->curregs[5] &= ~TxN_MASK;
+
+	switch (cflag & CSIZE) {
+	case CS5:
+		uap->curregs[3] |= Rx5;
+		uap->curregs[5] |= Tx5;
+		uap->parity_mask = 0x1f;
+		break;
+	case CS6:
+		uap->curregs[3] |= Rx6;
+		uap->curregs[5] |= Tx6;
+		uap->parity_mask = 0x3f;
+		break;
+	case CS7:
+		uap->curregs[3] |= Rx7;
+		uap->curregs[5] |= Tx7;
+		uap->parity_mask = 0x7f;
+		break;
+	case CS8:
+	default:
+		uap->curregs[3] |= Rx8;
+		uap->curregs[5] |= Tx8;
+		uap->parity_mask = 0xff;
+		break;
+	};
+	uap->curregs[4] &= ~(SB_MASK);
+	if (cflag & CSTOPB)
+		uap->curregs[4] |= SB2;
+	else
+		uap->curregs[4] |= SB1;
+	if (cflag & PARENB)
+		uap->curregs[4] |= PAR_ENAB;
+	else
+		uap->curregs[4] &= ~PAR_ENAB;
+	if (!(cflag & PARODD))
+		uap->curregs[4] |= PAR_EVEN;
+	else
+		uap->curregs[4] &= ~PAR_EVEN;
+
+	uap->port.read_status_mask = Rx_OVR;
+	if (iflag & INPCK)
+		uap->port.read_status_mask |= CRC_ERR | PAR_ERR;
+	if (iflag & (BRKINT | PARMRK))
+		uap->port.read_status_mask |= BRK_ABRT;
+
+	uap->port.ignore_status_mask = 0;
+	if (iflag & IGNPAR)
+		uap->port.ignore_status_mask |= CRC_ERR | PAR_ERR;
+	if (iflag & IGNBRK) {
+		uap->port.ignore_status_mask |= BRK_ABRT;
+		if (iflag & IGNPAR)
+			uap->port.ignore_status_mask |= Rx_OVR;
+	}
+
+	if ((cflag & CREAD) == 0)
+		uap->port.ignore_status_mask = 0xff;
+}
+
+
+/*
+ * Set the irda codec on the imac to the specified baud rate.
+ */
+static void pmz_irda_setup(struct uart_pmac_port *uap, unsigned long *baud)
+{
+	u8 cmdbyte;
+	int t, version;
+
+	switch (*baud) {
+	/* SIR modes */
+	case 2400:
+		cmdbyte = 0x53;
+		break;
+	case 4800:
+		cmdbyte = 0x52;
+		break;
+	case 9600:
+		cmdbyte = 0x51;
+		break;
+	case 19200:
+		cmdbyte = 0x50;
+		break;
+	case 38400:
+		cmdbyte = 0x4f;
+		break;
+	case 57600:
+		cmdbyte = 0x4e;
+		break;
+	case 115200:
+		cmdbyte = 0x4d;
+		break;
+	/* The FIR modes aren't really supported at this point, how
+	 * do we select the speed ? via the FCR on KeyLargo ?
+	 */
+	case 1152000:
+		cmdbyte = 0;
+		break;
+	case 4000000:
+		cmdbyte = 0;
+		break;
+	default: /* 9600 */
+		cmdbyte = 0x51;
+		*baud = 9600;
+		break;
+	}
+
+	/* Wait for transmitter to drain */
+	t = 10000;
+	while ((read_zsreg(uap, R0) & Tx_BUF_EMP) == 0
+	       || (read_zsreg(uap, R1) & ALL_SNT) == 0) {
+		if (--t <= 0) {
+			pmz_error("transmitter didn't drain\n");
+			return;
+		}
+		udelay(10);
+	}
+
+	/* Drain the receiver too */
+	t = 100;
+	(void)read_zsdata(uap);
+	(void)read_zsdata(uap);
+	(void)read_zsdata(uap);
+	mdelay(10);
+	while (read_zsreg(uap, R0) & Rx_CH_AV) {
+		read_zsdata(uap);
+		mdelay(10);
+		if (--t <= 0) {
+			pmz_error("receiver didn't drain\n");
+			return;
+		}
+	}
+
+	/* Switch to command mode */
+	uap->curregs[R5] |= DTR;
+	write_zsreg(uap, R5, uap->curregs[R5]);
+	zssync(uap);
+	mdelay(1);
+
+	/* Switch SCC to 19200 */
+	pmz_convert_to_zs(uap, CS8, 0, 19200);		
+	pmz_load_zsregs(uap, uap->curregs);
+	mdelay(1);
+
+	/* Write get_version command byte */
+	write_zsdata(uap, 1);
+	t = 5000;
+	while ((read_zsreg(uap, R0) & Rx_CH_AV) == 0) {
+		if (--t <= 0) {
+			pmz_error("irda_setup timed out on get_version byte\n");
+			goto out;
+		}
+		udelay(10);
+	}
+	version = read_zsdata(uap);
+
+	if (version < 4) {
+		pmz_info("IrDA: dongle version %d not supported\n", version);
+		goto out;
+	}
+
+	/* Send speed mode */
+	write_zsdata(uap, cmdbyte);
+	t = 5000;
+	while ((read_zsreg(uap, R0) & Rx_CH_AV) == 0) {
+		if (--t <= 0) {
+			pmz_error("irda_setup timed out on speed mode byte\n");
+			goto out;
+		}
+		udelay(10);
+	}
+	t = read_zsdata(uap);
+	if (t != cmdbyte)
+		pmz_error("irda_setup speed mode byte = %x (%x)\n", t, cmdbyte);
+
+	pmz_info("IrDA setup for %ld bps, dongle version: %d\n",
+		 *baud, version);
+
+	(void)read_zsdata(uap);
+	(void)read_zsdata(uap);
+	(void)read_zsdata(uap);
+
+ out:
+	/* Switch back to data mode */
+	uap->curregs[R5] &= ~DTR;
+	write_zsreg(uap, R5, uap->curregs[R5]);
+	zssync(uap);
+
+	(void)read_zsdata(uap);
+	(void)read_zsdata(uap);
+	(void)read_zsdata(uap);
+}
+
+
+static void __pmz_set_termios(struct uart_port *port, struct ktermios *termios,
+			      struct ktermios *old)
+{
+	struct uart_pmac_port *uap = to_pmz(port);
+	unsigned long baud;
+
+	pmz_debug("pmz: set_termios()\n");
+
+	memcpy(&uap->termios_cache, termios, sizeof(struct ktermios));
+
+	/* XXX Check which revs of machines actually allow 1 and 4Mb speeds
+	 * on the IR dongle. Note that the IRTTY driver currently doesn't know
+	 * about the FIR mode and high speed modes. So these are unused. For
+	 * implementing proper support for these, we should probably add some
+	 * DMA as well, at least on the Rx side, which isn't a simple thing
+	 * at this point.
+	 */
+	if (ZS_IS_IRDA(uap)) {
+		/* Calc baud rate */
+		baud = uart_get_baud_rate(port, termios, old, 1200, 4000000);
+		pmz_debug("pmz: switch IRDA to %ld bauds\n", baud);
+		/* Cet the irda codec to the right rate */
+		pmz_irda_setup(uap, &baud);
+		/* Set final baud rate */
+		pmz_convert_to_zs(uap, termios->c_cflag, termios->c_iflag, baud);
+		pmz_load_zsregs(uap, uap->curregs);
+		zssync(uap);
+	} else {
+		baud = uart_get_baud_rate(port, termios, old, 1200, 230400);
+		pmz_convert_to_zs(uap, termios->c_cflag, termios->c_iflag, baud);
+		/* Make sure modem status interrupts are correctly configured */
+		if (UART_ENABLE_MS(&uap->port, termios->c_cflag)) {
+			uap->curregs[R15] |= DCDIE | SYNCIE | CTSIE;
+			uap->flags |= PMACZILOG_FLAG_MODEM_STATUS;
+		} else {
+			uap->curregs[R15] &= ~(DCDIE | SYNCIE | CTSIE);
+			uap->flags &= ~PMACZILOG_FLAG_MODEM_STATUS;
+		}
+
+		/* Load registers to the chip */
+		pmz_maybe_update_regs(uap);
+	}
+	uart_update_timeout(port, termios->c_cflag, baud);
+
+	pmz_debug("pmz: set_termios() done.\n");
+}
+
+/* The port lock is not held.  */
+static void pmz_set_termios(struct uart_port *port, struct ktermios *termios,
+			    struct ktermios *old)
+{
+	struct uart_pmac_port *uap = to_pmz(port);
+	unsigned long flags;
+
+	spin_lock_irqsave(&port->lock, flags);	
+
+	/* Disable IRQs on the port */
+	pmz_interrupt_control(uap, 0);
+
+	/* Setup new port configuration */
+	__pmz_set_termios(port, termios, old);
+
+	/* Re-enable IRQs on the port */
+	if (ZS_IS_OPEN(uap))
+		pmz_interrupt_control(uap, 1);
+
+	spin_unlock_irqrestore(&port->lock, flags);
+}
+
+static const char *pmz_type(struct uart_port *port)
+{
+	struct uart_pmac_port *uap = to_pmz(port);
+
+	if (ZS_IS_IRDA(uap))
+		return "Z85c30 ESCC - Infrared port";
+	else if (ZS_IS_INTMODEM(uap))
+		return "Z85c30 ESCC - Internal modem";
+	return "Z85c30 ESCC - Serial port";
+}
+
+/* We do not request/release mappings of the registers here, this
+ * happens at early serial probe time.
+ */
+static void pmz_release_port(struct uart_port *port)
+{
+}
+
+static int pmz_request_port(struct uart_port *port)
+{
+	return 0;
+}
+
+/* These do not need to do anything interesting either.  */
+static void pmz_config_port(struct uart_port *port, int flags)
+{
+}
+
+/* We do not support letting the user mess with the divisor, IRQ, etc. */
+static int pmz_verify_port(struct uart_port *port, struct serial_struct *ser)
+{
+	return -EINVAL;
+}
+
+#ifdef CONFIG_CONSOLE_POLL
+
+static int pmz_poll_get_char(struct uart_port *port)
+{
+	struct uart_pmac_port *uap = (struct uart_pmac_port *)port;
+	int tries = 2;
+
+	while (tries) {
+		if ((read_zsreg(uap, R0) & Rx_CH_AV) != 0)
+			return read_zsdata(uap);
+		if (tries--)
+			udelay(5);
+	}
+
+	return NO_POLL_CHAR;
+}
+
+static void pmz_poll_put_char(struct uart_port *port, unsigned char c)
+{
+	struct uart_pmac_port *uap = (struct uart_pmac_port *)port;
+
+	/* Wait for the transmit buffer to empty. */
+	while ((read_zsreg(uap, R0) & Tx_BUF_EMP) == 0)
+		udelay(5);
+	write_zsdata(uap, c);
+}
+
+#endif /* CONFIG_CONSOLE_POLL */
+
+static struct uart_ops pmz_pops = {
+	.tx_empty	=	pmz_tx_empty,
+	.set_mctrl	=	pmz_set_mctrl,
+	.get_mctrl	=	pmz_get_mctrl,
+	.stop_tx	=	pmz_stop_tx,
+	.start_tx	=	pmz_start_tx,
+	.stop_rx	=	pmz_stop_rx,
+	.enable_ms	=	pmz_enable_ms,
+	.break_ctl	=	pmz_break_ctl,
+	.startup	=	pmz_startup,
+	.shutdown	=	pmz_shutdown,
+	.set_termios	=	pmz_set_termios,
+	.type		=	pmz_type,
+	.release_port	=	pmz_release_port,
+	.request_port	=	pmz_request_port,
+	.config_port	=	pmz_config_port,
+	.verify_port	=	pmz_verify_port,
+#ifdef CONFIG_CONSOLE_POLL
+	.poll_get_char	=	pmz_poll_get_char,
+	.poll_put_char	=	pmz_poll_put_char,
+#endif
+};
+
+#ifdef CONFIG_PPC_PMAC
+
+/*
+ * Setup one port structure after probing, HW is down at this point,
+ * Unlike sunzilog, we don't need to pre-init the spinlock as we don't
+ * register our console before uart_add_one_port() is called
+ */
+static int __init pmz_init_port(struct uart_pmac_port *uap)
+{
+	struct device_node *np = uap->node;
+	const char *conn;
+	const struct slot_names_prop {
+		int	count;
+		char	name[1];
+	} *slots;
+	int len;
+	struct resource r_ports, r_rxdma, r_txdma;
+
+	/*
+	 * Request & map chip registers
+	 */
+	if (of_address_to_resource(np, 0, &r_ports))
+		return -ENODEV;
+	uap->port.mapbase = r_ports.start;
+	uap->port.membase = ioremap(uap->port.mapbase, 0x1000);
+
+	uap->control_reg = uap->port.membase;
+	uap->data_reg = uap->control_reg + 0x10;
+	
+	/*
+	 * Request & map DBDMA registers
+	 */
+#ifdef HAS_DBDMA
+	if (of_address_to_resource(np, 1, &r_txdma) == 0 &&
+	    of_address_to_resource(np, 2, &r_rxdma) == 0)
+		uap->flags |= PMACZILOG_FLAG_HAS_DMA;
+#else
+	memset(&r_txdma, 0, sizeof(struct resource));
+	memset(&r_rxdma, 0, sizeof(struct resource));
+#endif	
+	if (ZS_HAS_DMA(uap)) {
+		uap->tx_dma_regs = ioremap(r_txdma.start, 0x100);
+		if (uap->tx_dma_regs == NULL) {	
+			uap->flags &= ~PMACZILOG_FLAG_HAS_DMA;
+			goto no_dma;
+		}
+		uap->rx_dma_regs = ioremap(r_rxdma.start, 0x100);
+		if (uap->rx_dma_regs == NULL) {	
+			iounmap(uap->tx_dma_regs);
+			uap->tx_dma_regs = NULL;
+			uap->flags &= ~PMACZILOG_FLAG_HAS_DMA;
+			goto no_dma;
+		}
+		uap->tx_dma_irq = irq_of_parse_and_map(np, 1);
+		uap->rx_dma_irq = irq_of_parse_and_map(np, 2);
+	}
+no_dma:
+
+	/*
+	 * Detect port type
+	 */
+	if (of_device_is_compatible(np, "cobalt"))
+		uap->flags |= PMACZILOG_FLAG_IS_INTMODEM;
+	conn = of_get_property(np, "AAPL,connector", &len);
+	if (conn && (strcmp(conn, "infrared") == 0))
+		uap->flags |= PMACZILOG_FLAG_IS_IRDA;
+	uap->port_type = PMAC_SCC_ASYNC;
+	/* 1999 Powerbook G3 has slot-names property instead */
+	slots = of_get_property(np, "slot-names", &len);
+	if (slots && slots->count > 0) {
+		if (strcmp(slots->name, "IrDA") == 0)
+			uap->flags |= PMACZILOG_FLAG_IS_IRDA;
+		else if (strcmp(slots->name, "Modem") == 0)
+			uap->flags |= PMACZILOG_FLAG_IS_INTMODEM;
+	}
+	if (ZS_IS_IRDA(uap))
+		uap->port_type = PMAC_SCC_IRDA;
+	if (ZS_IS_INTMODEM(uap)) {
+		struct device_node* i2c_modem =
+			of_find_node_by_name(NULL, "i2c-modem");
+		if (i2c_modem) {
+			const char* mid =
+				of_get_property(i2c_modem, "modem-id", NULL);
+			if (mid) switch(*mid) {
+			case 0x04 :
+			case 0x05 :
+			case 0x07 :
+			case 0x08 :
+			case 0x0b :
+			case 0x0c :
+				uap->port_type = PMAC_SCC_I2S1;
+			}
+			printk(KERN_INFO "pmac_zilog: i2c-modem detected, id: %d\n",
+				mid ? (*mid) : 0);
+			of_node_put(i2c_modem);
+		} else {
+			printk(KERN_INFO "pmac_zilog: serial modem detected\n");
+		}
+	}
+
+	/*
+	 * Init remaining bits of "port" structure
+	 */
+	uap->port.iotype = UPIO_MEM;
+	uap->port.irq = irq_of_parse_and_map(np, 0);
+	uap->port.uartclk = ZS_CLOCK;
+	uap->port.fifosize = 1;
+	uap->port.ops = &pmz_pops;
+	uap->port.type = PORT_PMAC_ZILOG;
+	uap->port.flags = 0;
+
+	/*
+	 * Fixup for the port on Gatwick for which the device-tree has
+	 * missing interrupts. Normally, the macio_dev would contain
+	 * fixed up interrupt info, but we use the device-tree directly
+	 * here due to early probing so we need the fixup too.
+	 */
+	if (uap->port.irq == 0 &&
+	    np->parent && np->parent->parent &&
+	    of_device_is_compatible(np->parent->parent, "gatwick")) {
+		/* IRQs on gatwick are offset by 64 */
+		uap->port.irq = irq_create_mapping(NULL, 64 + 15);
+		uap->tx_dma_irq = irq_create_mapping(NULL, 64 + 4);
+		uap->rx_dma_irq = irq_create_mapping(NULL, 64 + 5);
+	}
+
+	/* Setup some valid baud rate information in the register
+	 * shadows so we don't write crap there before baud rate is
+	 * first initialized.
+	 */
+	pmz_convert_to_zs(uap, CS8, 0, 9600);
+
+	return 0;
+}
+
+/*
+ * Get rid of a port on module removal
+ */
+static void pmz_dispose_port(struct uart_pmac_port *uap)
+{
+	struct device_node *np;
+
+	np = uap->node;
+	iounmap(uap->rx_dma_regs);
+	iounmap(uap->tx_dma_regs);
+	iounmap(uap->control_reg);
+	uap->node = NULL;
+	of_node_put(np);
+	memset(uap, 0, sizeof(struct uart_pmac_port));
+}
+
+/*
+ * Called upon match with an escc node in the device-tree.
+ */
+static int pmz_attach(struct macio_dev *mdev, const struct of_device_id *match)
+{
+	struct uart_pmac_port *uap;
+	int i;
+	
+	/* Iterate the pmz_ports array to find a matching entry
+	 */
+	for (i = 0; i < MAX_ZS_PORTS; i++)
+		if (pmz_ports[i].node == mdev->ofdev.dev.of_node)
+			break;
+	if (i >= MAX_ZS_PORTS)
+		return -ENODEV;
+
+
+	uap = &pmz_ports[i];
+	uap->dev = mdev;
+	uap->port.dev = &mdev->ofdev.dev;
+	dev_set_drvdata(&mdev->ofdev.dev, uap);
+
+	/* We still activate the port even when failing to request resources
+	 * to work around bugs in ancient Apple device-trees
+	 */
+	if (macio_request_resources(uap->dev, "pmac_zilog"))
+		printk(KERN_WARNING "%s: Failed to request resource"
+		       ", port still active\n",
+		       uap->node->name);
+	else
+		uap->flags |= PMACZILOG_FLAG_RSRC_REQUESTED;
+
+	return uart_add_one_port(&pmz_uart_reg, &uap->port);
+}
+
+/*
+ * That one should not be called, macio isn't really a hotswap device,
+ * we don't expect one of those serial ports to go away...
+ */
+static int pmz_detach(struct macio_dev *mdev)
+{
+	struct uart_pmac_port	*uap = dev_get_drvdata(&mdev->ofdev.dev);
+	
+	if (!uap)
+		return -ENODEV;
+
+	uart_remove_one_port(&pmz_uart_reg, &uap->port);
+
+	if (uap->flags & PMACZILOG_FLAG_RSRC_REQUESTED) {
+		macio_release_resources(uap->dev);
+		uap->flags &= ~PMACZILOG_FLAG_RSRC_REQUESTED;
+	}
+	dev_set_drvdata(&mdev->ofdev.dev, NULL);
+	uap->dev = NULL;
+	uap->port.dev = NULL;
+	
+	return 0;
+}
+
+
+static int pmz_suspend(struct macio_dev *mdev, pm_message_t pm_state)
+{
+	struct uart_pmac_port *uap = dev_get_drvdata(&mdev->ofdev.dev);
+
+	if (uap == NULL) {
+		printk("HRM... pmz_suspend with NULL uap\n");
+		return 0;
+	}
+
+	uart_suspend_port(&pmz_uart_reg, &uap->port);
+
+	return 0;
+}
+
+
+static int pmz_resume(struct macio_dev *mdev)
+{
+	struct uart_pmac_port *uap = dev_get_drvdata(&mdev->ofdev.dev);
+
+	if (uap == NULL)
+		return 0;
+
+	uart_resume_port(&pmz_uart_reg, &uap->port);
+
+	return 0;
+}
+
+/*
+ * Probe all ports in the system and build the ports array, we register
+ * with the serial layer later, so we get a proper struct device which
+ * allows the tty to attach properly. This is later than it used to be
+ * but the tty layer really wants it that way.
+ */
+static int __init pmz_probe(void)
+{
+	struct device_node	*node_p, *node_a, *node_b, *np;
+	int			count = 0;
+	int			rc;
+
+	/*
+	 * Find all escc chips in the system
+	 */
+	node_p = of_find_node_by_name(NULL, "escc");
+	while (node_p) {
+		/*
+		 * First get channel A/B node pointers
+		 * 
+		 * TODO: Add routines with proper locking to do that...
+		 */
+		node_a = node_b = NULL;
+		for (np = NULL; (np = of_get_next_child(node_p, np)) != NULL;) {
+			if (strncmp(np->name, "ch-a", 4) == 0)
+				node_a = of_node_get(np);
+			else if (strncmp(np->name, "ch-b", 4) == 0)
+				node_b = of_node_get(np);
+		}
+		if (!node_a && !node_b) {
+			of_node_put(node_a);
+			of_node_put(node_b);
+			printk(KERN_ERR "pmac_zilog: missing node %c for escc %s\n",
+				(!node_a) ? 'a' : 'b', node_p->full_name);
+			goto next;
+		}
+
+		/*
+		 * Fill basic fields in the port structures
+		 */
+		if (node_b != NULL) {
+			pmz_ports[count].mate		= &pmz_ports[count+1];
+			pmz_ports[count+1].mate		= &pmz_ports[count];
+		}
+		pmz_ports[count].flags		= PMACZILOG_FLAG_IS_CHANNEL_A;
+		pmz_ports[count].node		= node_a;
+		pmz_ports[count+1].node		= node_b;
+		pmz_ports[count].port.line	= count;
+		pmz_ports[count+1].port.line	= count+1;
+
+		/*
+		 * Setup the ports for real
+		 */
+		rc = pmz_init_port(&pmz_ports[count]);
+		if (rc == 0 && node_b != NULL)
+			rc = pmz_init_port(&pmz_ports[count+1]);
+		if (rc != 0) {
+			of_node_put(node_a);
+			of_node_put(node_b);
+			memset(&pmz_ports[count], 0, sizeof(struct uart_pmac_port));
+			memset(&pmz_ports[count+1], 0, sizeof(struct uart_pmac_port));
+			goto next;
+		}
+		count += 2;
+next:
+		node_p = of_find_node_by_name(node_p, "escc");
+	}
+	pmz_ports_count = count;
+
+	return 0;
+}
+
+#else
+
+extern struct platform_device scc_a_pdev, scc_b_pdev;
+
+static int __init pmz_init_port(struct uart_pmac_port *uap)
+{
+	struct resource *r_ports;
+	int irq;
+
+	r_ports = platform_get_resource(uap->pdev, IORESOURCE_MEM, 0);
+	irq = platform_get_irq(uap->pdev, 0);
+	if (!r_ports || !irq)
+		return -ENODEV;
+
+	uap->port.mapbase  = r_ports->start;
+	uap->port.membase  = (unsigned char __iomem *) r_ports->start;
+	uap->port.iotype   = UPIO_MEM;
+	uap->port.irq      = irq;
+	uap->port.uartclk  = ZS_CLOCK;
+	uap->port.fifosize = 1;
+	uap->port.ops      = &pmz_pops;
+	uap->port.type     = PORT_PMAC_ZILOG;
+	uap->port.flags    = 0;
+
+	uap->control_reg   = uap->port.membase;
+	uap->data_reg      = uap->control_reg + 4;
+	uap->port_type     = 0;
+
+	pmz_convert_to_zs(uap, CS8, 0, 9600);
+
+	return 0;
+}
+
+static int __init pmz_probe(void)
+{
+	int err;
+
+	pmz_ports_count = 0;
+
+	pmz_ports[0].port.line = 0;
+	pmz_ports[0].flags     = PMACZILOG_FLAG_IS_CHANNEL_A;
+	pmz_ports[0].pdev      = &scc_a_pdev;
+	err = pmz_init_port(&pmz_ports[0]);
+	if (err)
+		return err;
+	pmz_ports_count++;
+
+	pmz_ports[0].mate      = &pmz_ports[1];
+	pmz_ports[1].mate      = &pmz_ports[0];
+	pmz_ports[1].port.line = 1;
+	pmz_ports[1].flags     = 0;
+	pmz_ports[1].pdev      = &scc_b_pdev;
+	err = pmz_init_port(&pmz_ports[1]);
+	if (err)
+		return err;
+	pmz_ports_count++;
+
+	return 0;
+}
+
+static void pmz_dispose_port(struct uart_pmac_port *uap)
+{
+	memset(uap, 0, sizeof(struct uart_pmac_port));
+}
+
+static int __init pmz_attach(struct platform_device *pdev)
+{
+	struct uart_pmac_port *uap;
+	int i;
+
+	/* Iterate the pmz_ports array to find a matching entry */
+	for (i = 0; i < pmz_ports_count; i++)
+		if (pmz_ports[i].pdev == pdev)
+			break;
+	if (i >= pmz_ports_count)
+		return -ENODEV;
+
+	uap = &pmz_ports[i];
+	uap->port.dev = &pdev->dev;
+	platform_set_drvdata(pdev, uap);
+
+	return uart_add_one_port(&pmz_uart_reg, &uap->port);
+}
+
+static int __exit pmz_detach(struct platform_device *pdev)
+{
+	struct uart_pmac_port *uap = platform_get_drvdata(pdev);
+
+	if (!uap)
+		return -ENODEV;
+
+	uart_remove_one_port(&pmz_uart_reg, &uap->port);
+
+	platform_set_drvdata(pdev, NULL);
+	uap->port.dev = NULL;
+
+	return 0;
+}
+
+#endif /* !CONFIG_PPC_PMAC */
+
+#ifdef CONFIG_SERIAL_PMACZILOG_CONSOLE
+
+static void pmz_console_write(struct console *con, const char *s, unsigned int count);
+static int __init pmz_console_setup(struct console *co, char *options);
+
+static struct console pmz_console = {
+	.name	=	PMACZILOG_NAME,
+	.write	=	pmz_console_write,
+	.device	=	uart_console_device,
+	.setup	=	pmz_console_setup,
+	.flags	=	CON_PRINTBUFFER,
+	.index	=	-1,
+	.data   =	&pmz_uart_reg,
+};
+
+#define PMACZILOG_CONSOLE	&pmz_console
+#else /* CONFIG_SERIAL_PMACZILOG_CONSOLE */
+#define PMACZILOG_CONSOLE	(NULL)
+#endif /* CONFIG_SERIAL_PMACZILOG_CONSOLE */
+
+/*
+ * Register the driver, console driver and ports with the serial
+ * core
+ */
+static int __init pmz_register(void)
+{
+	pmz_uart_reg.nr = pmz_ports_count;
+	pmz_uart_reg.cons = PMACZILOG_CONSOLE;
+
+	/*
+	 * Register this driver with the serial core
+	 */
+	return uart_register_driver(&pmz_uart_reg);
+}
+
+#ifdef CONFIG_PPC_PMAC
+
+static struct of_device_id pmz_match[] = 
+{
+	{
+	.name		= "ch-a",
+	},
+	{
+	.name		= "ch-b",
+	},
+	{},
+};
+MODULE_DEVICE_TABLE (of, pmz_match);
+
+static struct macio_driver pmz_driver = {
+	.driver = {
+		.name 		= "pmac_zilog",
+		.owner		= THIS_MODULE,
+		.of_match_table	= pmz_match,
+	},
+	.probe		= pmz_attach,
+	.remove		= pmz_detach,
+	.suspend	= pmz_suspend,
+	.resume		= pmz_resume,
+};
+
+#else
+
+static struct platform_driver pmz_driver = {
+	.remove		= __exit_p(pmz_detach),
+	.driver		= {
+		.name		= "scc",
+		.owner		= THIS_MODULE,
+	},
+};
+
+#endif /* !CONFIG_PPC_PMAC */
+
+static int __init init_pmz(void)
+{
+	int rc, i;
+	printk(KERN_INFO "%s\n", version);
+
+	/* 
+	 * First, we need to do a direct OF-based probe pass. We
+	 * do that because we want serial console up before the
+	 * macio stuffs calls us back, and since that makes it
+	 * easier to pass the proper number of channels to
+	 * uart_register_driver()
+	 */
+	if (pmz_ports_count == 0)
+		pmz_probe();
+
+	/*
+	 * Bail early if no port found
+	 */
+	if (pmz_ports_count == 0)
+		return -ENODEV;
+
+	/*
+	 * Now we register with the serial layer
+	 */
+	rc = pmz_register();
+	if (rc) {
+		printk(KERN_ERR 
+			"pmac_zilog: Error registering serial device, disabling pmac_zilog.\n"
+		 	"pmac_zilog: Did another serial driver already claim the minors?\n"); 
+		/* effectively "pmz_unprobe()" */
+		for (i=0; i < pmz_ports_count; i++)
+			pmz_dispose_port(&pmz_ports[i]);
+		return rc;
+	}
+
+	/*
+	 * Then we register the macio driver itself
+	 */
+#ifdef CONFIG_PPC_PMAC
+	return macio_register_driver(&pmz_driver);
+#else
+	return platform_driver_probe(&pmz_driver, pmz_attach);
+#endif
+}
+
+static void __exit exit_pmz(void)
+{
+	int i;
+
+#ifdef CONFIG_PPC_PMAC
+	/* Get rid of macio-driver (detach from macio) */
+	macio_unregister_driver(&pmz_driver);
+#else
+	platform_driver_unregister(&pmz_driver);
+#endif
+
+	for (i = 0; i < pmz_ports_count; i++) {
+		struct uart_pmac_port *uport = &pmz_ports[i];
+#ifdef CONFIG_PPC_PMAC
+		if (uport->node != NULL)
+			pmz_dispose_port(uport);
+#else
+		if (uport->pdev != NULL)
+			pmz_dispose_port(uport);
+#endif
+	}
+	/* Unregister UART driver */
+	uart_unregister_driver(&pmz_uart_reg);
+}
+
+#ifdef CONFIG_SERIAL_PMACZILOG_CONSOLE
+
+static void pmz_console_putchar(struct uart_port *port, int ch)
+{
+	struct uart_pmac_port *uap = (struct uart_pmac_port *)port;
+
+	/* Wait for the transmit buffer to empty. */
+	while ((read_zsreg(uap, R0) & Tx_BUF_EMP) == 0)
+		udelay(5);
+	write_zsdata(uap, ch);
+}
+
+/*
+ * Print a string to the serial port trying not to disturb
+ * any possible real use of the port...
+ */
+static void pmz_console_write(struct console *con, const char *s, unsigned int count)
+{
+	struct uart_pmac_port *uap = &pmz_ports[con->index];
+	unsigned long flags;
+
+	spin_lock_irqsave(&uap->port.lock, flags);
+
+	/* Turn of interrupts and enable the transmitter. */
+	write_zsreg(uap, R1, uap->curregs[1] & ~TxINT_ENAB);
+	write_zsreg(uap, R5, uap->curregs[5] | TxENABLE | RTS | DTR);
+
+	uart_console_write(&uap->port, s, count, pmz_console_putchar);
+
+	/* Restore the values in the registers. */
+	write_zsreg(uap, R1, uap->curregs[1]);
+	/* Don't disable the transmitter. */
+
+	spin_unlock_irqrestore(&uap->port.lock, flags);
+}
+
+/*
+ * Setup the serial console
+ */
+static int __init pmz_console_setup(struct console *co, char *options)
+{
+	struct uart_pmac_port *uap;
+	struct uart_port *port;
+	int baud = 38400;
+	int bits = 8;
+	int parity = 'n';
+	int flow = 'n';
+	unsigned long pwr_delay;
+
+	/*
+	 * XServe's default to 57600 bps
+	 */
+	if (of_machine_is_compatible("RackMac1,1")
+	    || of_machine_is_compatible("RackMac1,2")
+	    || of_machine_is_compatible("MacRISC4"))
+		baud = 57600;
+
+	/*
+	 * 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 >= pmz_ports_count)
+		co->index = 0;
+	uap = &pmz_ports[co->index];
+#ifdef CONFIG_PPC_PMAC
+	if (uap->node == NULL)
+		return -ENODEV;
+#else
+	if (uap->pdev == NULL)
+		return -ENODEV;
+#endif
+	port = &uap->port;
+
+	/*
+	 * Mark port as beeing a console
+	 */
+	uap->flags |= PMACZILOG_FLAG_IS_CONS;
+
+	/*
+	 * Temporary fix for uart layer who didn't setup the spinlock yet
+	 */
+	spin_lock_init(&port->lock);
+
+	/*
+	 * Enable the hardware
+	 */
+	pwr_delay = __pmz_startup(uap);
+	if (pwr_delay)
+		mdelay(pwr_delay);
+	
+	if (options)
+		uart_parse_options(options, &baud, &parity, &bits, &flow);
+
+	return uart_set_options(port, co, baud, parity, bits, flow);
+}
+
+static int __init pmz_console_init(void)
+{
+	/* Probe ports */
+	pmz_probe();
+
+	if (pmz_ports_count == 0)
+		return -ENODEV;
+
+	/* TODO: Autoprobe console based on OF */
+	/* pmz_console.index = i; */
+	register_console(&pmz_console);
+
+	return 0;
+
+}
+console_initcall(pmz_console_init);
+#endif /* CONFIG_SERIAL_PMACZILOG_CONSOLE */
+
+module_init(init_pmz);
+module_exit(exit_pmz);
diff --git a/ap/os/linux/linux-3.4.x/drivers/tty/serial/pmac_zilog.h b/ap/os/linux/linux-3.4.x/drivers/tty/serial/pmac_zilog.h
new file mode 100644
index 0000000..3483242
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/tty/serial/pmac_zilog.h
@@ -0,0 +1,383 @@
+#ifndef __PMAC_ZILOG_H__
+#define __PMAC_ZILOG_H__
+
+/*
+ * At most 2 ESCCs with 2 ports each
+ */
+#define MAX_ZS_PORTS	4
+
+/* 
+ * We wrap our port structure around the generic uart_port.
+ */
+#define NUM_ZSREGS    17
+
+struct uart_pmac_port {
+	struct uart_port		port;
+	struct uart_pmac_port		*mate;
+
+#ifdef CONFIG_PPC_PMAC
+	/* macio_dev for the escc holding this port (maybe be null on
+	 * early inited port)
+	 */
+	struct macio_dev		*dev;
+	/* device node to this port, this points to one of 2 childs
+	 * of "escc" node (ie. ch-a or ch-b)
+	 */
+	struct device_node		*node;
+#else
+	struct platform_device		*pdev;
+#endif
+
+	/* Port type as obtained from device tree (IRDA, modem, ...) */
+	int				port_type;
+	u8				curregs[NUM_ZSREGS];
+
+	unsigned int			flags;
+#define PMACZILOG_FLAG_IS_CONS		0x00000001
+#define PMACZILOG_FLAG_IS_KGDB		0x00000002
+#define PMACZILOG_FLAG_MODEM_STATUS	0x00000004
+#define PMACZILOG_FLAG_IS_CHANNEL_A	0x00000008
+#define PMACZILOG_FLAG_REGS_HELD	0x00000010
+#define PMACZILOG_FLAG_TX_STOPPED	0x00000020
+#define PMACZILOG_FLAG_TX_ACTIVE	0x00000040
+#define PMACZILOG_FLAG_IS_IRDA		0x00000100
+#define PMACZILOG_FLAG_IS_INTMODEM	0x00000200
+#define PMACZILOG_FLAG_HAS_DMA		0x00000400
+#define PMACZILOG_FLAG_RSRC_REQUESTED	0x00000800
+#define PMACZILOG_FLAG_IS_OPEN		0x00002000
+#define PMACZILOG_FLAG_IS_EXTCLK	0x00008000
+#define PMACZILOG_FLAG_BREAK		0x00010000
+
+	unsigned char			parity_mask;
+	unsigned char			prev_status;
+
+	volatile u8			__iomem *control_reg;
+	volatile u8			__iomem *data_reg;
+
+#ifdef CONFIG_PPC_PMAC
+	unsigned int			tx_dma_irq;
+	unsigned int			rx_dma_irq;
+	volatile struct dbdma_regs	__iomem *tx_dma_regs;
+	volatile struct dbdma_regs	__iomem *rx_dma_regs;
+#endif
+
+	unsigned char			irq_name[8];
+
+	struct ktermios			termios_cache;
+};
+
+#define to_pmz(p) ((struct uart_pmac_port *)(p))
+
+static inline struct uart_pmac_port *pmz_get_port_A(struct uart_pmac_port *uap)
+{
+	if (uap->flags & PMACZILOG_FLAG_IS_CHANNEL_A)
+		return uap;
+	return uap->mate;
+}
+
+/*
+ * Register accessors. Note that we don't need to enforce a recovery
+ * delay on PCI PowerMac hardware, it's dealt in HW by the MacIO chip,
+ * though if we try to use this driver on older machines, we might have
+ * to add it back
+ */
+static inline u8 read_zsreg(struct uart_pmac_port *port, u8 reg)
+{
+	if (reg != 0)
+		writeb(reg, port->control_reg);
+	return readb(port->control_reg);
+}
+
+static inline void write_zsreg(struct uart_pmac_port *port, u8 reg, u8 value)
+{
+	if (reg != 0)
+		writeb(reg, port->control_reg);
+	writeb(value, port->control_reg);
+}
+
+static inline u8 read_zsdata(struct uart_pmac_port *port)
+{
+	return readb(port->data_reg);
+}
+
+static inline void write_zsdata(struct uart_pmac_port *port, u8 data)
+{
+	writeb(data, port->data_reg);
+}
+
+static inline void zssync(struct uart_pmac_port *port)
+{
+	(void)readb(port->control_reg);
+}
+
+/* Conversion routines to/from brg time constants from/to bits
+ * per second.
+ */
+#define BRG_TO_BPS(brg, freq) ((freq) / 2 / ((brg) + 2))
+#define BPS_TO_BRG(bps, freq) ((((freq) + (bps)) / (2 * (bps))) - 2)
+
+#define ZS_CLOCK         3686400	/* Z8530 RTxC input clock rate */
+
+/* The Zilog register set */
+
+#define	FLAG	0x7e
+
+/* Write Register 0 */
+#define	R0	0		/* Register selects */
+#define	R1	1
+#define	R2	2
+#define	R3	3
+#define	R4	4
+#define	R5	5
+#define	R6	6
+#define	R7	7
+#define	R8	8
+#define	R9	9
+#define	R10	10
+#define	R11	11
+#define	R12	12
+#define	R13	13
+#define	R14	14
+#define	R15	15
+#define	R7P	16
+
+#define	NULLCODE	0	/* Null Code */
+#define	POINT_HIGH	0x8	/* Select upper half of registers */
+#define	RES_EXT_INT	0x10	/* Reset Ext. Status Interrupts */
+#define	SEND_ABORT	0x18	/* HDLC Abort */
+#define	RES_RxINT_FC	0x20	/* Reset RxINT on First Character */
+#define	RES_Tx_P	0x28	/* Reset TxINT Pending */
+#define	ERR_RES		0x30	/* Error Reset */
+#define	RES_H_IUS	0x38	/* Reset highest IUS */
+
+#define	RES_Rx_CRC	0x40	/* Reset Rx CRC Checker */
+#define	RES_Tx_CRC	0x80	/* Reset Tx CRC Checker */
+#define	RES_EOM_L	0xC0	/* Reset EOM latch */
+
+/* Write Register 1 */
+
+#define	EXT_INT_ENAB	0x1	/* Ext Int Enable */
+#define	TxINT_ENAB	0x2	/* Tx Int Enable */
+#define	PAR_SPEC	0x4	/* Parity is special condition */
+
+#define	RxINT_DISAB	0	/* Rx Int Disable */
+#define	RxINT_FCERR	0x8	/* Rx Int on First Character Only or Error */
+#define	INT_ALL_Rx	0x10	/* Int on all Rx Characters or error */
+#define	INT_ERR_Rx	0x18	/* Int on error only */
+#define RxINT_MASK	0x18
+
+#define	WT_RDY_RT	0x20	/* W/Req reflects recv if 1, xmit if 0 */
+#define	WT_FN_RDYFN	0x40	/* W/Req pin is DMA request if 1, wait if 0 */
+#define	WT_RDY_ENAB	0x80	/* Enable W/Req pin */
+
+/* Write Register #2 (Interrupt Vector) */
+
+/* Write Register 3 */
+
+#define	RxENABLE	0x1	/* Rx Enable */
+#define	SYNC_L_INH	0x2	/* Sync Character Load Inhibit */
+#define	ADD_SM		0x4	/* Address Search Mode (SDLC) */
+#define	RxCRC_ENAB	0x8	/* Rx CRC Enable */
+#define	ENT_HM		0x10	/* Enter Hunt Mode */
+#define	AUTO_ENAB	0x20	/* Auto Enables */
+#define	Rx5		0x0	/* Rx 5 Bits/Character */
+#define	Rx7		0x40	/* Rx 7 Bits/Character */
+#define	Rx6		0x80	/* Rx 6 Bits/Character */
+#define	Rx8		0xc0	/* Rx 8 Bits/Character */
+#define RxN_MASK	0xc0
+
+/* Write Register 4 */
+
+#define	PAR_ENAB	0x1	/* Parity Enable */
+#define	PAR_EVEN	0x2	/* Parity Even/Odd* */
+
+#define	SYNC_ENAB	0	/* Sync Modes Enable */
+#define	SB1		0x4	/* 1 stop bit/char */
+#define	SB15		0x8	/* 1.5 stop bits/char */
+#define	SB2		0xc	/* 2 stop bits/char */
+#define SB_MASK		0xc
+
+#define	MONSYNC		0	/* 8 Bit Sync character */
+#define	BISYNC		0x10	/* 16 bit sync character */
+#define	SDLC		0x20	/* SDLC Mode (01111110 Sync Flag) */
+#define	EXTSYNC		0x30	/* External Sync Mode */
+
+#define	X1CLK		0x0	/* x1 clock mode */
+#define	X16CLK		0x40	/* x16 clock mode */
+#define	X32CLK		0x80	/* x32 clock mode */
+#define	X64CLK		0xC0	/* x64 clock mode */
+#define XCLK_MASK	0xC0
+
+/* Write Register 5 */
+
+#define	TxCRC_ENAB	0x1	/* Tx CRC Enable */
+#define	RTS		0x2	/* RTS */
+#define	SDLC_CRC	0x4	/* SDLC/CRC-16 */
+#define	TxENABLE	0x8	/* Tx Enable */
+#define	SND_BRK		0x10	/* Send Break */
+#define	Tx5		0x0	/* Tx 5 bits (or less)/character */
+#define	Tx7		0x20	/* Tx 7 bits/character */
+#define	Tx6		0x40	/* Tx 6 bits/character */
+#define	Tx8		0x60	/* Tx 8 bits/character */
+#define TxN_MASK	0x60
+#define	DTR		0x80	/* DTR */
+
+/* Write Register 6 (Sync bits 0-7/SDLC Address Field) */
+
+/* Write Register 7 (Sync bits 8-15/SDLC 01111110) */
+
+/* Write Register 7' (Some enhanced feature control) */
+#define	ENEXREAD	0x40	/* Enable read of some write registers */
+
+/* Write Register 8 (transmit buffer) */
+
+/* Write Register 9 (Master interrupt control) */
+#define	VIS	1	/* Vector Includes Status */
+#define	NV	2	/* No Vector */
+#define	DLC	4	/* Disable Lower Chain */
+#define	MIE	8	/* Master Interrupt Enable */
+#define	STATHI	0x10	/* Status high */
+#define	NORESET	0	/* No reset on write to R9 */
+#define	CHRB	0x40	/* Reset channel B */
+#define	CHRA	0x80	/* Reset channel A */
+#define	FHWRES	0xc0	/* Force hardware reset */
+
+/* Write Register 10 (misc control bits) */
+#define	BIT6	1	/* 6 bit/8bit sync */
+#define	LOOPMODE 2	/* SDLC Loop mode */
+#define	ABUNDER	4	/* Abort/flag on SDLC xmit underrun */
+#define	MARKIDLE 8	/* Mark/flag on idle */
+#define	GAOP	0x10	/* Go active on poll */
+#define	NRZ	0	/* NRZ mode */
+#define	NRZI	0x20	/* NRZI mode */
+#define	FM1	0x40	/* FM1 (transition = 1) */
+#define	FM0	0x60	/* FM0 (transition = 0) */
+#define	CRCPS	0x80	/* CRC Preset I/O */
+
+/* Write Register 11 (Clock Mode control) */
+#define	TRxCXT	0	/* TRxC = Xtal output */
+#define	TRxCTC	1	/* TRxC = Transmit clock */
+#define	TRxCBR	2	/* TRxC = BR Generator Output */
+#define	TRxCDP	3	/* TRxC = DPLL output */
+#define	TRxCOI	4	/* TRxC O/I */
+#define	TCRTxCP	0	/* Transmit clock = RTxC pin */
+#define	TCTRxCP	8	/* Transmit clock = TRxC pin */
+#define	TCBR	0x10	/* Transmit clock = BR Generator output */
+#define	TCDPLL	0x18	/* Transmit clock = DPLL output */
+#define	RCRTxCP	0	/* Receive clock = RTxC pin */
+#define	RCTRxCP	0x20	/* Receive clock = TRxC pin */
+#define	RCBR	0x40	/* Receive clock = BR Generator output */
+#define	RCDPLL	0x60	/* Receive clock = DPLL output */
+#define	RTxCX	0x80	/* RTxC Xtal/No Xtal */
+
+/* Write Register 12 (lower byte of baud rate generator time constant) */
+
+/* Write Register 13 (upper byte of baud rate generator time constant) */
+
+/* Write Register 14 (Misc control bits) */
+#define	BRENAB	1	/* Baud rate generator enable */
+#define	BRSRC	2	/* Baud rate generator source */
+#define	DTRREQ	4	/* DTR/Request function */
+#define	AUTOECHO 8	/* Auto Echo */
+#define	LOOPBAK	0x10	/* Local loopback */
+#define	SEARCH	0x20	/* Enter search mode */
+#define	RMC	0x40	/* Reset missing clock */
+#define	DISDPLL	0x60	/* Disable DPLL */
+#define	SSBR	0x80	/* Set DPLL source = BR generator */
+#define	SSRTxC	0xa0	/* Set DPLL source = RTxC */
+#define	SFMM	0xc0	/* Set FM mode */
+#define	SNRZI	0xe0	/* Set NRZI mode */
+
+/* Write Register 15 (external/status interrupt control) */
+#define	EN85C30	1	/* Enable some 85c30-enhanced registers */
+#define	ZCIE	2	/* Zero count IE */
+#define	ENSTFIFO 4	/* Enable status FIFO (SDLC) */
+#define	DCDIE	8	/* DCD IE */
+#define	SYNCIE	0x10	/* Sync/hunt IE */
+#define	CTSIE	0x20	/* CTS IE */
+#define	TxUIE	0x40	/* Tx Underrun/EOM IE */
+#define	BRKIE	0x80	/* Break/Abort IE */
+
+
+/* Read Register 0 */
+#define	Rx_CH_AV	0x1	/* Rx Character Available */
+#define	ZCOUNT		0x2	/* Zero count */
+#define	Tx_BUF_EMP	0x4	/* Tx Buffer empty */
+#define	DCD		0x8	/* DCD */
+#define	SYNC_HUNT	0x10	/* Sync/hunt */
+#define	CTS		0x20	/* CTS */
+#define	TxEOM		0x40	/* Tx underrun */
+#define	BRK_ABRT	0x80	/* Break/Abort */
+
+/* Read Register 1 */
+#define	ALL_SNT		0x1	/* All sent */
+/* Residue Data for 8 Rx bits/char programmed */
+#define	RES3		0x8	/* 0/3 */
+#define	RES4		0x4	/* 0/4 */
+#define	RES5		0xc	/* 0/5 */
+#define	RES6		0x2	/* 0/6 */
+#define	RES7		0xa	/* 0/7 */
+#define	RES8		0x6	/* 0/8 */
+#define	RES18		0xe	/* 1/8 */
+#define	RES28		0x0	/* 2/8 */
+/* Special Rx Condition Interrupts */
+#define	PAR_ERR		0x10	/* Parity error */
+#define	Rx_OVR		0x20	/* Rx Overrun Error */
+#define	CRC_ERR		0x40	/* CRC/Framing Error */
+#define	END_FR		0x80	/* End of Frame (SDLC) */
+
+/* Read Register 2 (channel b only) - Interrupt vector */
+#define	CHB_Tx_EMPTY	0x00
+#define	CHB_EXT_STAT	0x02
+#define	CHB_Rx_AVAIL	0x04
+#define	CHB_SPECIAL	0x06
+#define	CHA_Tx_EMPTY	0x08
+#define	CHA_EXT_STAT	0x0a
+#define	CHA_Rx_AVAIL	0x0c
+#define	CHA_SPECIAL	0x0e
+#define	STATUS_MASK	0x06
+
+/* Read Register 3 (interrupt pending register) ch a only */
+#define	CHBEXT	0x1		/* Channel B Ext/Stat IP */
+#define	CHBTxIP	0x2		/* Channel B Tx IP */
+#define	CHBRxIP	0x4		/* Channel B Rx IP */
+#define	CHAEXT	0x8		/* Channel A Ext/Stat IP */
+#define	CHATxIP	0x10		/* Channel A Tx IP */
+#define	CHARxIP	0x20		/* Channel A Rx IP */
+
+/* Read Register 8 (receive data register) */
+
+/* Read Register 10  (misc status bits) */
+#define	ONLOOP	2		/* On loop */
+#define	LOOPSEND 0x10		/* Loop sending */
+#define	CLK2MIS	0x40		/* Two clocks missing */
+#define	CLK1MIS	0x80		/* One clock missing */
+
+/* Read Register 12 (lower byte of baud rate generator constant) */
+
+/* Read Register 13 (upper byte of baud rate generator constant) */
+
+/* Read Register 15 (value of WR 15) */
+
+/* Misc macros */
+#define ZS_CLEARERR(port)    (write_zsreg(port, 0, ERR_RES))
+#define ZS_CLEARFIFO(port)   do { volatile unsigned char garbage; \
+				     garbage = read_zsdata(port); \
+				     garbage = read_zsdata(port); \
+				     garbage = read_zsdata(port); \
+				} while(0)
+
+#define ZS_IS_CONS(UP)			((UP)->flags & PMACZILOG_FLAG_IS_CONS)
+#define ZS_IS_KGDB(UP)			((UP)->flags & PMACZILOG_FLAG_IS_KGDB)
+#define ZS_IS_CHANNEL_A(UP)		((UP)->flags & PMACZILOG_FLAG_IS_CHANNEL_A)
+#define ZS_REGS_HELD(UP)		((UP)->flags & PMACZILOG_FLAG_REGS_HELD)
+#define ZS_TX_STOPPED(UP)		((UP)->flags & PMACZILOG_FLAG_TX_STOPPED)
+#define ZS_TX_ACTIVE(UP)		((UP)->flags & PMACZILOG_FLAG_TX_ACTIVE)
+#define ZS_WANTS_MODEM_STATUS(UP)	((UP)->flags & PMACZILOG_FLAG_MODEM_STATUS)
+#define ZS_IS_IRDA(UP)			((UP)->flags & PMACZILOG_FLAG_IS_IRDA)
+#define ZS_IS_INTMODEM(UP)		((UP)->flags & PMACZILOG_FLAG_IS_INTMODEM)
+#define ZS_HAS_DMA(UP)			((UP)->flags & PMACZILOG_FLAG_HAS_DMA)
+#define ZS_IS_OPEN(UP)			((UP)->flags & PMACZILOG_FLAG_IS_OPEN)
+#define ZS_IS_EXTCLK(UP)		((UP)->flags & PMACZILOG_FLAG_IS_EXTCLK)
+
+#endif /* __PMAC_ZILOG_H__ */
diff --git a/ap/os/linux/linux-3.4.x/drivers/tty/serial/pnx8xxx_uart.c b/ap/os/linux/linux-3.4.x/drivers/tty/serial/pnx8xxx_uart.c
new file mode 100644
index 0000000..0aa75a9
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/tty/serial/pnx8xxx_uart.c
@@ -0,0 +1,854 @@
+/*
+ * UART driver for PNX8XXX SoCs
+ *
+ * Author: Per Hallsmark per.hallsmark@mvista.com
+ * Ported to 2.6 kernel by EmbeddedAlley
+ * Reworked by Vitaly Wool <vitalywool@gmail.com>
+ *
+ * Based on drivers/char/serial.c, by Linus Torvalds, Theodore Ts'o.
+ * Copyright (C) 2000 Deep Blue Solutions Ltd.
+ *
+ * This file is licensed under the terms of the GNU General Public License
+ * version 2. This program is licensed "as is" without any warranty of
+ * any kind, whether express or implied.
+ *
+ */
+
+#if defined(CONFIG_SERIAL_PNX8XXX_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ)
+#define SUPPORT_SYSRQ
+#endif
+
+#include <linux/module.h>
+#include <linux/ioport.h>
+#include <linux/init.h>
+#include <linux/console.h>
+#include <linux/sysrq.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
+#include <linux/serial_core.h>
+#include <linux/serial.h>
+#include <linux/serial_pnx8xxx.h>
+
+#include <asm/io.h>
+#include <asm/irq.h>
+
+/* We'll be using StrongARM sa1100 serial port major/minor */
+#define SERIAL_PNX8XXX_MAJOR	204
+#define MINOR_START		5
+
+#define NR_PORTS		2
+
+#define PNX8XXX_ISR_PASS_LIMIT	256
+
+/*
+ * Convert from ignore_status_mask or read_status_mask to FIFO
+ * and interrupt status bits
+ */
+#define SM_TO_FIFO(x)	((x) >> 10)
+#define SM_TO_ISTAT(x)	((x) & 0x000001ff)
+#define FIFO_TO_SM(x)	((x) << 10)
+#define ISTAT_TO_SM(x)	((x) & 0x000001ff)
+
+/*
+ * This is the size of our serial port register set.
+ */
+#define UART_PORT_SIZE	0x1000
+
+/*
+ * 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)
+
+extern struct pnx8xxx_port pnx8xxx_ports[];
+
+static inline int serial_in(struct pnx8xxx_port *sport, int offset)
+{
+	return (__raw_readl(sport->port.membase + offset));
+}
+
+static inline void serial_out(struct pnx8xxx_port *sport, int offset, int value)
+{
+	__raw_writel(value, sport->port.membase + offset);
+}
+
+/*
+ * Handle any change of modem status signal since we were last called.
+ */
+static void pnx8xxx_mctrl_check(struct pnx8xxx_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 pnx8xxx_timeout(unsigned long data)
+{
+	struct pnx8xxx_port *sport = (struct pnx8xxx_port *)data;
+	unsigned long flags;
+
+	if (sport->port.state) {
+		spin_lock_irqsave(&sport->port.lock, flags);
+		pnx8xxx_mctrl_check(sport);
+		spin_unlock_irqrestore(&sport->port.lock, flags);
+
+		mod_timer(&sport->timer, jiffies + MCTRL_TIMEOUT);
+	}
+}
+
+/*
+ * interrupts disabled on entry
+ */
+static void pnx8xxx_stop_tx(struct uart_port *port)
+{
+	struct pnx8xxx_port *sport = (struct pnx8xxx_port *)port;
+	u32 ien;
+
+	/* Disable TX intr */
+	ien = serial_in(sport, PNX8XXX_IEN);
+	serial_out(sport, PNX8XXX_IEN, ien & ~PNX8XXX_UART_INT_ALLTX);
+
+	/* Clear all pending TX intr */
+	serial_out(sport, PNX8XXX_ICLR, PNX8XXX_UART_INT_ALLTX);
+}
+
+/*
+ * interrupts may not be disabled on entry
+ */
+static void pnx8xxx_start_tx(struct uart_port *port)
+{
+	struct pnx8xxx_port *sport = (struct pnx8xxx_port *)port;
+	u32 ien;
+
+	/* Clear all pending TX intr */
+	serial_out(sport, PNX8XXX_ICLR, PNX8XXX_UART_INT_ALLTX);
+
+	/* Enable TX intr */
+	ien = serial_in(sport, PNX8XXX_IEN);
+	serial_out(sport, PNX8XXX_IEN, ien | PNX8XXX_UART_INT_ALLTX);
+}
+
+/*
+ * Interrupts enabled
+ */
+static void pnx8xxx_stop_rx(struct uart_port *port)
+{
+	struct pnx8xxx_port *sport = (struct pnx8xxx_port *)port;
+	u32 ien;
+
+	/* Disable RX intr */
+	ien = serial_in(sport, PNX8XXX_IEN);
+	serial_out(sport, PNX8XXX_IEN, ien & ~PNX8XXX_UART_INT_ALLRX);
+
+	/* Clear all pending RX intr */
+	serial_out(sport, PNX8XXX_ICLR, PNX8XXX_UART_INT_ALLRX);
+}
+
+/*
+ * Set the modem control timer to fire immediately.
+ */
+static void pnx8xxx_enable_ms(struct uart_port *port)
+{
+	struct pnx8xxx_port *sport = (struct pnx8xxx_port *)port;
+
+	mod_timer(&sport->timer, jiffies);
+}
+
+static void pnx8xxx_rx_chars(struct pnx8xxx_port *sport)
+{
+	struct tty_struct *tty = sport->port.state->port.tty;
+	unsigned int status, ch, flg;
+
+	status = FIFO_TO_SM(serial_in(sport, PNX8XXX_FIFO)) |
+		 ISTAT_TO_SM(serial_in(sport, PNX8XXX_ISTAT));
+	while (status & FIFO_TO_SM(PNX8XXX_UART_FIFO_RXFIFO)) {
+		ch = serial_in(sport, PNX8XXX_FIFO) & 0xff;
+
+		sport->port.icount.rx++;
+
+		flg = TTY_NORMAL;
+
+		/*
+		 * note that the error handling code is
+		 * out of the main execution path
+		 */
+		if (status & (FIFO_TO_SM(PNX8XXX_UART_FIFO_RXFE |
+					PNX8XXX_UART_FIFO_RXPAR |
+					PNX8XXX_UART_FIFO_RXBRK) |
+			      ISTAT_TO_SM(PNX8XXX_UART_INT_RXOVRN))) {
+			if (status & FIFO_TO_SM(PNX8XXX_UART_FIFO_RXBRK)) {
+				status &= ~(FIFO_TO_SM(PNX8XXX_UART_FIFO_RXFE) |
+					FIFO_TO_SM(PNX8XXX_UART_FIFO_RXPAR));
+				sport->port.icount.brk++;
+				if (uart_handle_break(&sport->port))
+					goto ignore_char;
+			} else if (status & FIFO_TO_SM(PNX8XXX_UART_FIFO_RXPAR))
+				sport->port.icount.parity++;
+			else if (status & FIFO_TO_SM(PNX8XXX_UART_FIFO_RXFE))
+				sport->port.icount.frame++;
+			if (status & ISTAT_TO_SM(PNX8XXX_UART_INT_RXOVRN))
+				sport->port.icount.overrun++;
+
+			status &= sport->port.read_status_mask;
+
+			if (status & FIFO_TO_SM(PNX8XXX_UART_FIFO_RXPAR))
+				flg = TTY_PARITY;
+			else if (status & FIFO_TO_SM(PNX8XXX_UART_FIFO_RXFE))
+				flg = TTY_FRAME;
+
+#ifdef SUPPORT_SYSRQ
+			sport->port.sysrq = 0;
+#endif
+		}
+
+		if (uart_handle_sysrq_char(&sport->port, ch))
+			goto ignore_char;
+
+		uart_insert_char(&sport->port, status,
+				ISTAT_TO_SM(PNX8XXX_UART_INT_RXOVRN), ch, flg);
+
+	ignore_char:
+		serial_out(sport, PNX8XXX_LCR, serial_in(sport, PNX8XXX_LCR) |
+				PNX8XXX_UART_LCR_RX_NEXT);
+		status = FIFO_TO_SM(serial_in(sport, PNX8XXX_FIFO)) |
+			 ISTAT_TO_SM(serial_in(sport, PNX8XXX_ISTAT));
+	}
+	tty_flip_buffer_push(tty);
+}
+
+static void pnx8xxx_tx_chars(struct pnx8xxx_port *sport)
+{
+	struct circ_buf *xmit = &sport->port.state->xmit;
+
+	if (sport->port.x_char) {
+		serial_out(sport, PNX8XXX_FIFO, sport->port.x_char);
+		sport->port.icount.tx++;
+		sport->port.x_char = 0;
+		return;
+	}
+
+	/*
+	 * Check the modem control lines before
+	 * transmitting anything.
+	 */
+	pnx8xxx_mctrl_check(sport);
+
+	if (uart_circ_empty(xmit) || uart_tx_stopped(&sport->port)) {
+		pnx8xxx_stop_tx(&sport->port);
+		return;
+	}
+
+	/*
+	 * TX while bytes available
+	 */
+	while (((serial_in(sport, PNX8XXX_FIFO) &
+					PNX8XXX_UART_FIFO_TXFIFO) >> 16) < 16) {
+		serial_out(sport, PNX8XXX_FIFO, xmit->buf[xmit->tail]);
+		xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
+		sport->port.icount.tx++;
+		if (uart_circ_empty(xmit))
+			break;
+	}
+
+	if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
+		uart_write_wakeup(&sport->port);
+
+	if (uart_circ_empty(xmit))
+		pnx8xxx_stop_tx(&sport->port);
+}
+
+static irqreturn_t pnx8xxx_int(int irq, void *dev_id)
+{
+	struct pnx8xxx_port *sport = dev_id;
+	unsigned int status;
+
+	spin_lock(&sport->port.lock);
+	/* Get the interrupts */
+	status  = serial_in(sport, PNX8XXX_ISTAT) & serial_in(sport, PNX8XXX_IEN);
+
+	/* Byte or break signal received */
+	if (status & (PNX8XXX_UART_INT_RX | PNX8XXX_UART_INT_BREAK))
+		pnx8xxx_rx_chars(sport);
+
+	/* TX holding register empty - transmit a byte */
+	if (status & PNX8XXX_UART_INT_TX)
+		pnx8xxx_tx_chars(sport);
+
+	/* Clear the ISTAT register */
+	serial_out(sport, PNX8XXX_ICLR, status);
+
+	spin_unlock(&sport->port.lock);
+	return IRQ_HANDLED;
+}
+
+/*
+ * Return TIOCSER_TEMT when transmitter is not busy.
+ */
+static unsigned int pnx8xxx_tx_empty(struct uart_port *port)
+{
+	struct pnx8xxx_port *sport = (struct pnx8xxx_port *)port;
+
+	return serial_in(sport, PNX8XXX_FIFO) & PNX8XXX_UART_FIFO_TXFIFO_STA ? 0 : TIOCSER_TEMT;
+}
+
+static unsigned int pnx8xxx_get_mctrl(struct uart_port *port)
+{
+	struct pnx8xxx_port *sport = (struct pnx8xxx_port *)port;
+	unsigned int mctrl = TIOCM_DSR;
+	unsigned int msr;
+
+	/* REVISIT */
+
+	msr = serial_in(sport, PNX8XXX_MCR);
+
+	mctrl |= msr & PNX8XXX_UART_MCR_CTS ? TIOCM_CTS : 0;
+	mctrl |= msr & PNX8XXX_UART_MCR_DCD ? TIOCM_CAR : 0;
+
+	return mctrl;
+}
+
+static void pnx8xxx_set_mctrl(struct uart_port *port, unsigned int mctrl)
+{
+#if	0	/* FIXME */
+	struct pnx8xxx_port *sport = (struct pnx8xxx_port *)port;
+	unsigned int msr;
+#endif
+}
+
+/*
+ * Interrupts always disabled.
+ */
+static void pnx8xxx_break_ctl(struct uart_port *port, int break_state)
+{
+	struct pnx8xxx_port *sport = (struct pnx8xxx_port *)port;
+	unsigned long flags;
+	unsigned int lcr;
+
+	spin_lock_irqsave(&sport->port.lock, flags);
+	lcr = serial_in(sport, PNX8XXX_LCR);
+	if (break_state == -1)
+		lcr |= PNX8XXX_UART_LCR_TXBREAK;
+	else
+		lcr &= ~PNX8XXX_UART_LCR_TXBREAK;
+	serial_out(sport, PNX8XXX_LCR, lcr);
+	spin_unlock_irqrestore(&sport->port.lock, flags);
+}
+
+static int pnx8xxx_startup(struct uart_port *port)
+{
+	struct pnx8xxx_port *sport = (struct pnx8xxx_port *)port;
+	int retval;
+
+	/*
+	 * Allocate the IRQ
+	 */
+	retval = request_irq(sport->port.irq, pnx8xxx_int, 0,
+			     "pnx8xxx-uart", sport);
+	if (retval)
+		return retval;
+
+	/*
+	 * Finally, clear and enable interrupts
+	 */
+
+	serial_out(sport, PNX8XXX_ICLR, PNX8XXX_UART_INT_ALLRX |
+			     PNX8XXX_UART_INT_ALLTX);
+
+	serial_out(sport, PNX8XXX_IEN, serial_in(sport, PNX8XXX_IEN) |
+			    PNX8XXX_UART_INT_ALLRX |
+			    PNX8XXX_UART_INT_ALLTX);
+
+	/*
+	 * Enable modem status interrupts
+	 */
+	spin_lock_irq(&sport->port.lock);
+	pnx8xxx_enable_ms(&sport->port);
+	spin_unlock_irq(&sport->port.lock);
+
+	return 0;
+}
+
+static void pnx8xxx_shutdown(struct uart_port *port)
+{
+	struct pnx8xxx_port *sport = (struct pnx8xxx_port *)port;
+	int lcr;
+
+	/*
+	 * Stop our timer.
+	 */
+	del_timer_sync(&sport->timer);
+
+	/*
+	 * Disable all interrupts
+	 */
+	serial_out(sport, PNX8XXX_IEN, 0);
+
+	/*
+	 * Reset the Tx and Rx FIFOS, disable the break condition
+	 */
+	lcr = serial_in(sport, PNX8XXX_LCR);
+	lcr &= ~PNX8XXX_UART_LCR_TXBREAK;
+	lcr |= PNX8XXX_UART_LCR_TX_RST | PNX8XXX_UART_LCR_RX_RST;
+	serial_out(sport, PNX8XXX_LCR, lcr);
+
+	/*
+	 * Clear all interrupts
+	 */
+	serial_out(sport, PNX8XXX_ICLR, PNX8XXX_UART_INT_ALLRX |
+			     PNX8XXX_UART_INT_ALLTX);
+
+	/*
+	 * Free the interrupt
+	 */
+	free_irq(sport->port.irq, sport);
+}
+
+static void
+pnx8xxx_set_termios(struct uart_port *port, struct ktermios *termios,
+		   struct ktermios *old)
+{
+	struct pnx8xxx_port *sport = (struct pnx8xxx_port *)port;
+	unsigned long flags;
+	unsigned int lcr_fcr, old_ien, baud, quot;
+	unsigned int old_csize = old ? old->c_cflag & CSIZE : CS8;
+
+	/*
+	 * We 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)
+		lcr_fcr = PNX8XXX_UART_LCR_8BIT;
+	else
+		lcr_fcr = 0;
+
+	if (termios->c_cflag & CSTOPB)
+		lcr_fcr |= PNX8XXX_UART_LCR_2STOPB;
+	if (termios->c_cflag & PARENB) {
+		lcr_fcr |= PNX8XXX_UART_LCR_PAREN;
+		if (!(termios->c_cflag & PARODD))
+			lcr_fcr |= PNX8XXX_UART_LCR_PAREVN;
+	}
+
+	/*
+	 * Ask the core to calculate the divisor for us.
+	 */
+	baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk/16);
+	quot = uart_get_divisor(port, baud);
+
+	spin_lock_irqsave(&sport->port.lock, flags);
+
+	sport->port.read_status_mask = ISTAT_TO_SM(PNX8XXX_UART_INT_RXOVRN) |
+				ISTAT_TO_SM(PNX8XXX_UART_INT_EMPTY) |
+				ISTAT_TO_SM(PNX8XXX_UART_INT_RX);
+	if (termios->c_iflag & INPCK)
+		sport->port.read_status_mask |=
+			FIFO_TO_SM(PNX8XXX_UART_FIFO_RXFE) |
+			FIFO_TO_SM(PNX8XXX_UART_FIFO_RXPAR);
+	if (termios->c_iflag & (BRKINT | PARMRK))
+		sport->port.read_status_mask |=
+			ISTAT_TO_SM(PNX8XXX_UART_INT_BREAK);
+
+	/*
+	 * Characters to ignore
+	 */
+	sport->port.ignore_status_mask = 0;
+	if (termios->c_iflag & IGNPAR)
+		sport->port.ignore_status_mask |=
+			FIFO_TO_SM(PNX8XXX_UART_FIFO_RXFE) |
+			FIFO_TO_SM(PNX8XXX_UART_FIFO_RXPAR);
+	if (termios->c_iflag & IGNBRK) {
+		sport->port.ignore_status_mask |=
+			ISTAT_TO_SM(PNX8XXX_UART_INT_BREAK);
+		/*
+		 * 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 |=
+				ISTAT_TO_SM(PNX8XXX_UART_INT_RXOVRN);
+	}
+
+	/*
+	 * ignore all characters if CREAD is not set
+	 */
+	if ((termios->c_cflag & CREAD) == 0)
+		sport->port.ignore_status_mask |=
+			ISTAT_TO_SM(PNX8XXX_UART_INT_RX);
+
+	del_timer_sync(&sport->timer);
+
+	/*
+	 * Update the per-port timeout.
+	 */
+	uart_update_timeout(port, termios->c_cflag, baud);
+
+	/*
+	 * disable interrupts and drain transmitter
+	 */
+	old_ien = serial_in(sport, PNX8XXX_IEN);
+	serial_out(sport, PNX8XXX_IEN, old_ien & ~(PNX8XXX_UART_INT_ALLTX |
+					PNX8XXX_UART_INT_ALLRX));
+
+	while (serial_in(sport, PNX8XXX_FIFO) & PNX8XXX_UART_FIFO_TXFIFO_STA)
+		barrier();
+
+	/* then, disable everything */
+	serial_out(sport, PNX8XXX_IEN, 0);
+
+	/* Reset the Rx and Tx FIFOs too */
+	lcr_fcr |= PNX8XXX_UART_LCR_TX_RST;
+	lcr_fcr |= PNX8XXX_UART_LCR_RX_RST;
+
+	/* set the parity, stop bits and data size */
+	serial_out(sport, PNX8XXX_LCR, lcr_fcr);
+
+	/* set the baud rate */
+	quot -= 1;
+	serial_out(sport, PNX8XXX_BAUD, quot);
+
+	serial_out(sport, PNX8XXX_ICLR, -1);
+
+	serial_out(sport, PNX8XXX_IEN, old_ien);
+
+	if (UART_ENABLE_MS(&sport->port, termios->c_cflag))
+		pnx8xxx_enable_ms(&sport->port);
+
+	spin_unlock_irqrestore(&sport->port.lock, flags);
+}
+
+static const char *pnx8xxx_type(struct uart_port *port)
+{
+	struct pnx8xxx_port *sport = (struct pnx8xxx_port *)port;
+
+	return sport->port.type == PORT_PNX8XXX ? "PNX8XXX" : NULL;
+}
+
+/*
+ * Release the memory region(s) being used by 'port'.
+ */
+static void pnx8xxx_release_port(struct uart_port *port)
+{
+	struct pnx8xxx_port *sport = (struct pnx8xxx_port *)port;
+
+	release_mem_region(sport->port.mapbase, UART_PORT_SIZE);
+}
+
+/*
+ * Request the memory region(s) being used by 'port'.
+ */
+static int pnx8xxx_request_port(struct uart_port *port)
+{
+	struct pnx8xxx_port *sport = (struct pnx8xxx_port *)port;
+	return request_mem_region(sport->port.mapbase, UART_PORT_SIZE,
+			"pnx8xxx-uart") != NULL ? 0 : -EBUSY;
+}
+
+/*
+ * Configure/autoconfigure the port.
+ */
+static void pnx8xxx_config_port(struct uart_port *port, int flags)
+{
+	struct pnx8xxx_port *sport = (struct pnx8xxx_port *)port;
+
+	if (flags & UART_CONFIG_TYPE &&
+	    pnx8xxx_request_port(&sport->port) == 0)
+		sport->port.type = PORT_PNX8XXX;
+}
+
+/*
+ * Verify the new serial_struct (for TIOCSSERIAL).
+ * The only change we allow are to the flags and type, and
+ * even then only between PORT_PNX8XXX and PORT_UNKNOWN
+ */
+static int
+pnx8xxx_verify_port(struct uart_port *port, struct serial_struct *ser)
+{
+	struct pnx8xxx_port *sport = (struct pnx8xxx_port *)port;
+	int ret = 0;
+
+	if (ser->type != PORT_UNKNOWN && ser->type != PORT_PNX8XXX)
+		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 pnx8xxx_pops = {
+	.tx_empty	= pnx8xxx_tx_empty,
+	.set_mctrl	= pnx8xxx_set_mctrl,
+	.get_mctrl	= pnx8xxx_get_mctrl,
+	.stop_tx	= pnx8xxx_stop_tx,
+	.start_tx	= pnx8xxx_start_tx,
+	.stop_rx	= pnx8xxx_stop_rx,
+	.enable_ms	= pnx8xxx_enable_ms,
+	.break_ctl	= pnx8xxx_break_ctl,
+	.startup	= pnx8xxx_startup,
+	.shutdown	= pnx8xxx_shutdown,
+	.set_termios	= pnx8xxx_set_termios,
+	.type		= pnx8xxx_type,
+	.release_port	= pnx8xxx_release_port,
+	.request_port	= pnx8xxx_request_port,
+	.config_port	= pnx8xxx_config_port,
+	.verify_port	= pnx8xxx_verify_port,
+};
+
+
+/*
+ * Setup the PNX8XXX serial ports.
+ *
+ * Note also that we support "console=ttySx" where "x" is either 0 or 1.
+ */
+static void __init pnx8xxx_init_ports(void)
+{
+	static int first = 1;
+	int i;
+
+	if (!first)
+		return;
+	first = 0;
+
+	for (i = 0; i < NR_PORTS; i++) {
+		init_timer(&pnx8xxx_ports[i].timer);
+		pnx8xxx_ports[i].timer.function = pnx8xxx_timeout;
+		pnx8xxx_ports[i].timer.data     = (unsigned long)&pnx8xxx_ports[i];
+		pnx8xxx_ports[i].port.ops = &pnx8xxx_pops;
+	}
+}
+
+#ifdef CONFIG_SERIAL_PNX8XXX_CONSOLE
+
+static void pnx8xxx_console_putchar(struct uart_port *port, int ch)
+{
+	struct pnx8xxx_port *sport = (struct pnx8xxx_port *)port;
+	int status;
+
+	do {
+		/* Wait for UART_TX register to empty */
+		status = serial_in(sport, PNX8XXX_FIFO);
+	} while (status & PNX8XXX_UART_FIFO_TXFIFO);
+	serial_out(sport, PNX8XXX_FIFO, ch);
+}
+
+/*
+ * Interrupts are disabled on entering
+ */static void
+pnx8xxx_console_write(struct console *co, const char *s, unsigned int count)
+{
+	struct pnx8xxx_port *sport = &pnx8xxx_ports[co->index];
+	unsigned int old_ien, status;
+
+	/*
+	 *	First, save IEN and then disable interrupts
+	 */
+	old_ien = serial_in(sport, PNX8XXX_IEN);
+	serial_out(sport, PNX8XXX_IEN, old_ien & ~(PNX8XXX_UART_INT_ALLTX |
+					PNX8XXX_UART_INT_ALLRX));
+
+	uart_console_write(&sport->port, s, count, pnx8xxx_console_putchar);
+
+	/*
+	 *	Finally, wait for transmitter to become empty
+	 *	and restore IEN
+	 */
+	do {
+		/* Wait for UART_TX register to empty */
+		status = serial_in(sport, PNX8XXX_FIFO);
+	} while (status & PNX8XXX_UART_FIFO_TXFIFO);
+
+	/* Clear TX and EMPTY interrupt */
+	serial_out(sport, PNX8XXX_ICLR, PNX8XXX_UART_INT_TX |
+			     PNX8XXX_UART_INT_EMPTY);
+
+	serial_out(sport, PNX8XXX_IEN, old_ien);
+}
+
+static int __init
+pnx8xxx_console_setup(struct console *co, char *options)
+{
+	struct pnx8xxx_port *sport;
+	int baud = 38400;
+	int bits = 8;
+	int parity = 'n';
+	int flow = 'n';
+
+	/*
+	 * 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 = &pnx8xxx_ports[co->index];
+
+	if (options)
+		uart_parse_options(options, &baud, &parity, &bits, &flow);
+
+	return uart_set_options(&sport->port, co, baud, parity, bits, flow);
+}
+
+static struct uart_driver pnx8xxx_reg;
+static struct console pnx8xxx_console = {
+	.name		= "ttyS",
+	.write		= pnx8xxx_console_write,
+	.device		= uart_console_device,
+	.setup		= pnx8xxx_console_setup,
+	.flags		= CON_PRINTBUFFER,
+	.index		= -1,
+	.data		= &pnx8xxx_reg,
+};
+
+static int __init pnx8xxx_rs_console_init(void)
+{
+	pnx8xxx_init_ports();
+	register_console(&pnx8xxx_console);
+	return 0;
+}
+console_initcall(pnx8xxx_rs_console_init);
+
+#define PNX8XXX_CONSOLE	&pnx8xxx_console
+#else
+#define PNX8XXX_CONSOLE	NULL
+#endif
+
+static struct uart_driver pnx8xxx_reg = {
+	.owner			= THIS_MODULE,
+	.driver_name		= "ttyS",
+	.dev_name		= "ttyS",
+	.major			= SERIAL_PNX8XXX_MAJOR,
+	.minor			= MINOR_START,
+	.nr			= NR_PORTS,
+	.cons			= PNX8XXX_CONSOLE,
+};
+
+static int pnx8xxx_serial_suspend(struct platform_device *pdev, pm_message_t state)
+{
+	struct pnx8xxx_port *sport = platform_get_drvdata(pdev);
+
+	return uart_suspend_port(&pnx8xxx_reg, &sport->port);
+}
+
+static int pnx8xxx_serial_resume(struct platform_device *pdev)
+{
+	struct pnx8xxx_port *sport = platform_get_drvdata(pdev);
+
+	return uart_resume_port(&pnx8xxx_reg, &sport->port);
+}
+
+static int pnx8xxx_serial_probe(struct platform_device *pdev)
+{
+	struct resource *res = pdev->resource;
+	int i;
+
+	for (i = 0; i < pdev->num_resources; i++, res++) {
+		if (!(res->flags & IORESOURCE_MEM))
+			continue;
+
+		for (i = 0; i < NR_PORTS; i++) {
+			if (pnx8xxx_ports[i].port.mapbase != res->start)
+				continue;
+
+			pnx8xxx_ports[i].port.dev = &pdev->dev;
+			uart_add_one_port(&pnx8xxx_reg, &pnx8xxx_ports[i].port);
+			platform_set_drvdata(pdev, &pnx8xxx_ports[i]);
+			break;
+		}
+	}
+
+	return 0;
+}
+
+static int pnx8xxx_serial_remove(struct platform_device *pdev)
+{
+	struct pnx8xxx_port *sport = platform_get_drvdata(pdev);
+
+	platform_set_drvdata(pdev, NULL);
+
+	if (sport)
+		uart_remove_one_port(&pnx8xxx_reg, &sport->port);
+
+	return 0;
+}
+
+static struct platform_driver pnx8xxx_serial_driver = {
+	.driver		= {
+		.name	= "pnx8xxx-uart",
+		.owner	= THIS_MODULE,
+	},
+	.probe		= pnx8xxx_serial_probe,
+	.remove		= pnx8xxx_serial_remove,
+	.suspend	= pnx8xxx_serial_suspend,
+	.resume		= pnx8xxx_serial_resume,
+};
+
+static int __init pnx8xxx_serial_init(void)
+{
+	int ret;
+
+	printk(KERN_INFO "Serial: PNX8XXX driver\n");
+
+	pnx8xxx_init_ports();
+
+	ret = uart_register_driver(&pnx8xxx_reg);
+	if (ret == 0) {
+		ret = platform_driver_register(&pnx8xxx_serial_driver);
+		if (ret)
+			uart_unregister_driver(&pnx8xxx_reg);
+	}
+	return ret;
+}
+
+static void __exit pnx8xxx_serial_exit(void)
+{
+	platform_driver_unregister(&pnx8xxx_serial_driver);
+	uart_unregister_driver(&pnx8xxx_reg);
+}
+
+module_init(pnx8xxx_serial_init);
+module_exit(pnx8xxx_serial_exit);
+
+MODULE_AUTHOR("Embedded Alley Solutions, Inc.");
+MODULE_DESCRIPTION("PNX8XXX SoCs serial port driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS_CHARDEV_MAJOR(SERIAL_PNX8XXX_MAJOR);
+MODULE_ALIAS("platform:pnx8xxx-uart");
diff --git a/ap/os/linux/linux-3.4.x/drivers/tty/serial/pxa.c b/ap/os/linux/linux-3.4.x/drivers/tty/serial/pxa.c
new file mode 100644
index 0000000..5847a4b
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/tty/serial/pxa.c
@@ -0,0 +1,922 @@
+/*
+ *  Based on drivers/serial/8250.c by Russell King.
+ *
+ *  Author:	Nicolas Pitre
+ *  Created:	Feb 20, 2003
+ *  Copyright:	(C) 2003 Monta Vista Software, 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.
+ *
+ * Note 1: This driver is made separate from the already too overloaded
+ * 8250.c because it needs some kirks of its own and that'll make it
+ * easier to add DMA support.
+ *
+ * Note 2: I'm too sick of device allocation policies for serial ports.
+ * If someone else wants to request an "official" allocation of major/minor
+ * for this driver please be my guest.  And don't forget that new hardware
+ * to come from Intel might have more than 3 or 4 of those UARTs.  Let's
+ * hope for a better port registration and dynamic device allocation scheme
+ * with the serial core maintainer satisfaction to appear soon.
+ */
+
+
+#if defined(CONFIG_SERIAL_PXA_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ)
+#define SUPPORT_SYSRQ
+#endif
+
+#include <linux/module.h>
+#include <linux/ioport.h>
+#include <linux/init.h>
+#include <linux/console.h>
+#include <linux/sysrq.h>
+#include <linux/serial_reg.h>
+#include <linux/circ_buf.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
+#include <linux/serial_core.h>
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <linux/slab.h>
+
+#define PXA_NAME_LEN		8
+
+struct uart_pxa_port {
+	struct uart_port        port;
+	unsigned char           ier;
+	unsigned char           lcr;
+	unsigned char           mcr;
+	unsigned int            lsr_break_flag;
+	struct clk		*clk;
+	char			name[PXA_NAME_LEN];
+};
+
+static inline unsigned int serial_in(struct uart_pxa_port *up, int offset)
+{
+	offset <<= 2;
+	return readl(up->port.membase + offset);
+}
+
+static inline void serial_out(struct uart_pxa_port *up, int offset, int value)
+{
+	offset <<= 2;
+	writel(value, up->port.membase + offset);
+}
+
+static void serial_pxa_enable_ms(struct uart_port *port)
+{
+	struct uart_pxa_port *up = (struct uart_pxa_port *)port;
+
+	up->ier |= UART_IER_MSI;
+	serial_out(up, UART_IER, up->ier);
+}
+
+static void serial_pxa_stop_tx(struct uart_port *port)
+{
+	struct uart_pxa_port *up = (struct uart_pxa_port *)port;
+
+	if (up->ier & UART_IER_THRI) {
+		up->ier &= ~UART_IER_THRI;
+		serial_out(up, UART_IER, up->ier);
+	}
+}
+
+static void serial_pxa_stop_rx(struct uart_port *port)
+{
+	struct uart_pxa_port *up = (struct uart_pxa_port *)port;
+
+	up->ier &= ~UART_IER_RLSI;
+	up->port.read_status_mask &= ~UART_LSR_DR;
+	serial_out(up, UART_IER, up->ier);
+}
+
+static inline void receive_chars(struct uart_pxa_port *up, int *status)
+{
+	struct tty_struct *tty = up->port.state->port.tty;
+	unsigned int ch, flag;
+	int max_count = 256;
+
+	do {
+		/* work around Errata #20 according to
+		 * Intel(R) PXA27x Processor Family
+		 * Specification Update (May 2005)
+		 *
+		 * Step 2
+		 * Disable the Reciever Time Out Interrupt via IER[RTOEI]
+		 */
+		up->ier &= ~UART_IER_RTOIE;
+		serial_out(up, UART_IER, up->ier);
+
+		ch = serial_in(up, UART_RX);
+		flag = TTY_NORMAL;
+		up->port.icount.rx++;
+
+		if (unlikely(*status & (UART_LSR_BI | UART_LSR_PE |
+				       UART_LSR_FE | UART_LSR_OE))) {
+			/*
+			 * For statistics only
+			 */
+			if (*status & UART_LSR_BI) {
+				*status &= ~(UART_LSR_FE | UART_LSR_PE);
+				up->port.icount.brk++;
+				/*
+				 * We do the SysRQ and SAK checking
+				 * here because otherwise the break
+				 * may get masked by ignore_status_mask
+				 * or read_status_mask.
+				 */
+				if (uart_handle_break(&up->port))
+					goto ignore_char;
+			} else if (*status & UART_LSR_PE)
+				up->port.icount.parity++;
+			else if (*status & UART_LSR_FE)
+				up->port.icount.frame++;
+			if (*status & UART_LSR_OE)
+				up->port.icount.overrun++;
+
+			/*
+			 * Mask off conditions which should be ignored.
+			 */
+			*status &= up->port.read_status_mask;
+
+#ifdef CONFIG_SERIAL_PXA_CONSOLE
+			if (up->port.line == up->port.cons->index) {
+				/* Recover the break flag from console xmit */
+				*status |= up->lsr_break_flag;
+				up->lsr_break_flag = 0;
+			}
+#endif
+			if (*status & UART_LSR_BI) {
+				flag = TTY_BREAK;
+			} else if (*status & UART_LSR_PE)
+				flag = TTY_PARITY;
+			else if (*status & UART_LSR_FE)
+				flag = TTY_FRAME;
+		}
+
+		if (uart_handle_sysrq_char(&up->port, ch))
+			goto ignore_char;
+
+		uart_insert_char(&up->port, *status, UART_LSR_OE, ch, flag);
+
+	ignore_char:
+		*status = serial_in(up, UART_LSR);
+	} while ((*status & UART_LSR_DR) && (max_count-- > 0));
+	tty_flip_buffer_push(tty);
+
+	/* work around Errata #20 according to
+	 * Intel(R) PXA27x Processor Family
+	 * Specification Update (May 2005)
+	 *
+	 * Step 6:
+	 * No more data in FIFO: Re-enable RTO interrupt via IER[RTOIE]
+	 */
+	up->ier |= UART_IER_RTOIE;
+	serial_out(up, UART_IER, up->ier);
+}
+
+static void transmit_chars(struct uart_pxa_port *up)
+{
+	struct circ_buf *xmit = &up->port.state->xmit;
+	int count;
+
+	if (up->port.x_char) {
+		serial_out(up, UART_TX, up->port.x_char);
+		up->port.icount.tx++;
+		up->port.x_char = 0;
+		return;
+	}
+	if (uart_circ_empty(xmit) || uart_tx_stopped(&up->port)) {
+		serial_pxa_stop_tx(&up->port);
+		return;
+	}
+
+	count = up->port.fifosize / 2;
+	do {
+		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))
+		serial_pxa_stop_tx(&up->port);
+}
+
+static void serial_pxa_start_tx(struct uart_port *port)
+{
+	struct uart_pxa_port *up = (struct uart_pxa_port *)port;
+
+	if (!(up->ier & UART_IER_THRI)) {
+		up->ier |= UART_IER_THRI;
+		serial_out(up, UART_IER, up->ier);
+	}
+}
+
+static inline void check_modem_status(struct uart_pxa_port *up)
+{
+	int status;
+
+	status = serial_in(up, UART_MSR);
+
+	if ((status & UART_MSR_ANY_DELTA) == 0)
+		return;
+
+	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);
+}
+
+/*
+ * This handles the interrupt from one port.
+ */
+static inline irqreturn_t serial_pxa_irq(int irq, void *dev_id)
+{
+	struct uart_pxa_port *up = dev_id;
+	unsigned int iir, lsr;
+
+	iir = serial_in(up, UART_IIR);
+	if (iir & UART_IIR_NO_INT)
+		return IRQ_NONE;
+	lsr = serial_in(up, UART_LSR);
+	if (lsr & UART_LSR_DR)
+		receive_chars(up, &lsr);
+	check_modem_status(up);
+	if (lsr & UART_LSR_THRE)
+		transmit_chars(up);
+	return IRQ_HANDLED;
+}
+
+static unsigned int serial_pxa_tx_empty(struct uart_port *port)
+{
+	struct uart_pxa_port *up = (struct uart_pxa_port *)port;
+	unsigned long flags;
+	unsigned int ret;
+
+	spin_lock_irqsave(&up->port.lock, flags);
+	ret = serial_in(up, UART_LSR) & UART_LSR_TEMT ? TIOCSER_TEMT : 0;
+	spin_unlock_irqrestore(&up->port.lock, flags);
+
+	return ret;
+}
+
+static unsigned int serial_pxa_get_mctrl(struct uart_port *port)
+{
+	struct uart_pxa_port *up = (struct uart_pxa_port *)port;
+	unsigned char status;
+	unsigned int ret;
+
+	status = serial_in(up, UART_MSR);
+
+	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 serial_pxa_set_mctrl(struct uart_port *port, unsigned int mctrl)
+{
+	struct uart_pxa_port *up = (struct uart_pxa_port *)port;
+	unsigned char mcr = 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 |= up->mcr;
+
+	serial_out(up, UART_MCR, mcr);
+}
+
+static void serial_pxa_break_ctl(struct uart_port *port, int break_state)
+{
+	struct uart_pxa_port *up = (struct uart_pxa_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;
+	serial_out(up, UART_LCR, up->lcr);
+	spin_unlock_irqrestore(&up->port.lock, flags);
+}
+
+#if 0
+static void serial_pxa_dma_init(struct pxa_uart *up)
+{
+	up->rxdma =
+		pxa_request_dma(up->name, DMA_PRIO_LOW, pxa_receive_dma, up);
+	if (up->rxdma < 0)
+		goto out;
+	up->txdma =
+		pxa_request_dma(up->name, DMA_PRIO_LOW, pxa_transmit_dma, up);
+	if (up->txdma < 0)
+		goto err_txdma;
+	up->dmadesc = kmalloc(4 * sizeof(pxa_dma_desc), GFP_KERNEL);
+	if (!up->dmadesc)
+		goto err_alloc;
+
+	/* ... */
+err_alloc:
+	pxa_free_dma(up->txdma);
+err_rxdma:
+	pxa_free_dma(up->rxdma);
+out:
+	return;
+}
+#endif
+
+static int serial_pxa_startup(struct uart_port *port)
+{
+	struct uart_pxa_port *up = (struct uart_pxa_port *)port;
+	unsigned long flags;
+	int retval;
+
+	if (port->line == 3) /* HWUART */
+		up->mcr |= UART_MCR_AFE;
+	else
+		up->mcr = 0;
+
+	up->port.uartclk = clk_get_rate(up->clk);
+
+	/*
+	 * Allocate the IRQ
+	 */
+	retval = request_irq(up->port.irq, serial_pxa_irq, 0, up->name, up);
+	if (retval)
+		return retval;
+
+	/*
+	 * Clear the FIFO buffers and disable them.
+	 * (they will be reenabled in set_termios())
+	 */
+	serial_out(up, UART_FCR, UART_FCR_ENABLE_FIFO);
+	serial_out(up, UART_FCR, UART_FCR_ENABLE_FIFO |
+			UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT);
+	serial_out(up, UART_FCR, 0);
+
+	/*
+	 * Clear the interrupt registers.
+	 */
+	(void) serial_in(up, UART_LSR);
+	(void) serial_in(up, UART_RX);
+	(void) serial_in(up, UART_IIR);
+	(void) serial_in(up, UART_MSR);
+
+	/*
+	 * Now, initialize the UART
+	 */
+	serial_out(up, UART_LCR, UART_LCR_WLEN8);
+
+	spin_lock_irqsave(&up->port.lock, flags);
+	up->port.mctrl |= TIOCM_OUT2;
+	serial_pxa_set_mctrl(&up->port, up->port.mctrl);
+	spin_unlock_irqrestore(&up->port.lock, flags);
+
+	/*
+	 * Finally, enable interrupts.  Note: Modem status interrupts
+	 * are set via set_termios(), which will be occurring imminently
+	 * anyway, so we don't enable them here.
+	 */
+	up->ier = UART_IER_RLSI | UART_IER_RDI | UART_IER_RTOIE | UART_IER_UUE;
+	serial_out(up, UART_IER, up->ier);
+
+	/*
+	 * And clear the interrupt registers again for luck.
+	 */
+	(void) serial_in(up, UART_LSR);
+	(void) serial_in(up, UART_RX);
+	(void) serial_in(up, UART_IIR);
+	(void) serial_in(up, UART_MSR);
+
+	return 0;
+}
+
+static void serial_pxa_shutdown(struct uart_port *port)
+{
+	struct uart_pxa_port *up = (struct uart_pxa_port *)port;
+	unsigned long flags;
+
+	free_irq(up->port.irq, up);
+
+	/*
+	 * Disable interrupts from this port
+	 */
+	up->ier = 0;
+	serial_out(up, UART_IER, 0);
+
+	spin_lock_irqsave(&up->port.lock, flags);
+	up->port.mctrl &= ~TIOCM_OUT2;
+	serial_pxa_set_mctrl(&up->port, up->port.mctrl);
+	spin_unlock_irqrestore(&up->port.lock, flags);
+
+	/*
+	 * Disable break condition and FIFOs
+	 */
+	serial_out(up, UART_LCR, serial_in(up, UART_LCR) & ~UART_LCR_SBC);
+	serial_out(up, UART_FCR, UART_FCR_ENABLE_FIFO |
+				  UART_FCR_CLEAR_RCVR |
+				  UART_FCR_CLEAR_XMIT);
+	serial_out(up, UART_FCR, 0);
+}
+
+static void
+serial_pxa_set_termios(struct uart_port *port, struct ktermios *termios,
+		       struct ktermios *old)
+{
+	struct uart_pxa_port *up = (struct uart_pxa_port *)port;
+	unsigned char cval, fcr = 0;
+	unsigned long flags;
+	unsigned int baud, quot;
+	unsigned int dll;
+
+	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;
+
+	/*
+	 * Ask the core to calculate the divisor for us.
+	 */
+	baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk/16);
+	quot = uart_get_divisor(port, baud);
+
+	if ((up->port.uartclk / quot) < (2400 * 16))
+		fcr = UART_FCR_ENABLE_FIFO | UART_FCR_PXAR1;
+	else if ((up->port.uartclk / quot) < (230400 * 16))
+		fcr = UART_FCR_ENABLE_FIFO | UART_FCR_PXAR8;
+	else
+		fcr = UART_FCR_ENABLE_FIFO | UART_FCR_PXAR32;
+
+	/*
+	 * Ok, we're now changing the port state.  Do it with
+	 * interrupts disabled.
+	 */
+	spin_lock_irqsave(&up->port.lock, flags);
+
+	/*
+	 * Ensure the port will be enabled.
+	 * This is required especially for serial console.
+	 */
+	up->ier |= UART_IER_UUE;
+
+	/*
+	 * 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;
+
+	/*
+	 * Characters 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;
+
+	serial_out(up, UART_IER, up->ier);
+
+	if (termios->c_cflag & CRTSCTS)
+		up->mcr |= UART_MCR_AFE;
+	else
+		up->mcr &= ~UART_MCR_AFE;
+
+	serial_out(up, UART_LCR, cval | UART_LCR_DLAB);	/* set DLAB */
+	serial_out(up, UART_DLL, quot & 0xff);		/* LS of divisor */
+
+	/*
+	 * work around Errata #75 according to Intel(R) PXA27x Processor Family
+	 * Specification Update (Nov 2005)
+	 */
+	dll = serial_in(up, UART_DLL);
+	WARN_ON(dll != (quot & 0xff));
+
+	serial_out(up, UART_DLM, quot >> 8);		/* MS of divisor */
+	serial_out(up, UART_LCR, cval);			/* reset DLAB */
+	up->lcr = cval;					/* Save LCR */
+	serial_pxa_set_mctrl(&up->port, up->port.mctrl);
+	serial_out(up, UART_FCR, fcr);
+	spin_unlock_irqrestore(&up->port.lock, flags);
+}
+
+static void
+serial_pxa_pm(struct uart_port *port, unsigned int state,
+	      unsigned int oldstate)
+{
+	struct uart_pxa_port *up = (struct uart_pxa_port *)port;
+
+	if (!state)
+		clk_prepare_enable(up->clk);
+	else
+		clk_disable_unprepare(up->clk);
+}
+
+static void serial_pxa_release_port(struct uart_port *port)
+{
+}
+
+static int serial_pxa_request_port(struct uart_port *port)
+{
+	return 0;
+}
+
+static void serial_pxa_config_port(struct uart_port *port, int flags)
+{
+	struct uart_pxa_port *up = (struct uart_pxa_port *)port;
+	up->port.type = PORT_PXA;
+}
+
+static int
+serial_pxa_verify_port(struct uart_port *port, struct serial_struct *ser)
+{
+	/* we don't want the core code to modify any port params */
+	return -EINVAL;
+}
+
+static const char *
+serial_pxa_type(struct uart_port *port)
+{
+	struct uart_pxa_port *up = (struct uart_pxa_port *)port;
+	return up->name;
+}
+
+static struct uart_pxa_port *serial_pxa_ports[4];
+static struct uart_driver serial_pxa_reg;
+
+#ifdef CONFIG_SERIAL_PXA_CONSOLE
+
+#define BOTH_EMPTY (UART_LSR_TEMT | UART_LSR_THRE)
+
+/*
+ *	Wait for transmitter & holding register to empty
+ */
+static inline void wait_for_xmitr(struct uart_pxa_port *up)
+{
+	unsigned int status, tmout = 10000;
+
+	/* Wait up to 10ms for the character(s) to be sent. */
+	do {
+		status = serial_in(up, UART_LSR);
+
+		if (status & UART_LSR_BI)
+			up->lsr_break_flag = UART_LSR_BI;
+
+		if (--tmout == 0)
+			break;
+		udelay(1);
+	} while ((status & BOTH_EMPTY) != BOTH_EMPTY);
+
+	/* Wait up to 1s for flow control if necessary */
+	if (up->port.flags & UPF_CONS_FLOW) {
+		tmout = 1000000;
+		while (--tmout &&
+		       ((serial_in(up, UART_MSR) & UART_MSR_CTS) == 0))
+			udelay(1);
+	}
+}
+
+static void serial_pxa_console_putchar(struct uart_port *port, int ch)
+{
+	struct uart_pxa_port *up = (struct uart_pxa_port *)port;
+
+	wait_for_xmitr(up);
+	serial_out(up, UART_TX, ch);
+}
+
+/*
+ * Print a string to the serial port trying not to disturb
+ * any possible real use of the port...
+ *
+ *	The console_lock must be held when we get here.
+ */
+static void
+serial_pxa_console_write(struct console *co, const char *s, unsigned int count)
+{
+	struct uart_pxa_port *up = serial_pxa_ports[co->index];
+	unsigned int ier;
+
+	clk_prepare_enable(up->clk);
+
+	/*
+	 *	First save the IER then disable the interrupts
+	 */
+	ier = serial_in(up, UART_IER);
+	serial_out(up, UART_IER, UART_IER_UUE);
+
+	uart_console_write(&up->port, s, count, serial_pxa_console_putchar);
+
+	/*
+	 *	Finally, wait for transmitter to become empty
+	 *	and restore the IER
+	 */
+	wait_for_xmitr(up);
+	serial_out(up, UART_IER, ier);
+
+	clk_disable_unprepare(up->clk);
+}
+
+static int __init
+serial_pxa_console_setup(struct console *co, char *options)
+{
+	struct uart_pxa_port *up;
+	int baud = 9600;
+	int bits = 8;
+	int parity = 'n';
+	int flow = 'n';
+
+	if (co->index == -1 || co->index >= serial_pxa_reg.nr)
+		co->index = 0;
+	up = serial_pxa_ports[co->index];
+	if (!up)
+		return -ENODEV;
+
+	if (options)
+		uart_parse_options(options, &baud, &parity, &bits, &flow);
+
+	return uart_set_options(&up->port, co, baud, parity, bits, flow);
+}
+
+static struct console serial_pxa_console = {
+	.name		= "ttyS",
+	.write		= serial_pxa_console_write,
+	.device		= uart_console_device,
+	.setup		= serial_pxa_console_setup,
+	.flags		= CON_PRINTBUFFER,
+	.index		= -1,
+	.data		= &serial_pxa_reg,
+};
+
+#define PXA_CONSOLE	&serial_pxa_console
+#else
+#define PXA_CONSOLE	NULL
+#endif
+
+struct uart_ops serial_pxa_pops = {
+	.tx_empty	= serial_pxa_tx_empty,
+	.set_mctrl	= serial_pxa_set_mctrl,
+	.get_mctrl	= serial_pxa_get_mctrl,
+	.stop_tx	= serial_pxa_stop_tx,
+	.start_tx	= serial_pxa_start_tx,
+	.stop_rx	= serial_pxa_stop_rx,
+	.enable_ms	= serial_pxa_enable_ms,
+	.break_ctl	= serial_pxa_break_ctl,
+	.startup	= serial_pxa_startup,
+	.shutdown	= serial_pxa_shutdown,
+	.set_termios	= serial_pxa_set_termios,
+	.pm		= serial_pxa_pm,
+	.type		= serial_pxa_type,
+	.release_port	= serial_pxa_release_port,
+	.request_port	= serial_pxa_request_port,
+	.config_port	= serial_pxa_config_port,
+	.verify_port	= serial_pxa_verify_port,
+};
+
+static struct uart_driver serial_pxa_reg = {
+	.owner		= THIS_MODULE,
+	.driver_name	= "PXA serial",
+	.dev_name	= "ttyS",
+	.major		= TTY_MAJOR,
+	.minor		= 64,
+	.nr		= 4,
+	.cons		= PXA_CONSOLE,
+};
+
+#ifdef CONFIG_PM
+static int serial_pxa_suspend(struct device *dev)
+{
+        struct uart_pxa_port *sport = dev_get_drvdata(dev);
+
+        if (sport)
+                uart_suspend_port(&serial_pxa_reg, &sport->port);
+
+        return 0;
+}
+
+static int serial_pxa_resume(struct device *dev)
+{
+        struct uart_pxa_port *sport = dev_get_drvdata(dev);
+
+        if (sport)
+                uart_resume_port(&serial_pxa_reg, &sport->port);
+
+        return 0;
+}
+
+static const struct dev_pm_ops serial_pxa_pm_ops = {
+	.suspend	= serial_pxa_suspend,
+	.resume		= serial_pxa_resume,
+};
+#endif
+
+static struct of_device_id serial_pxa_dt_ids[] = {
+	{ .compatible = "mrvl,pxa-uart", },
+	{ .compatible = "mrvl,mmp-uart", },
+	{}
+};
+MODULE_DEVICE_TABLE(of, serial_pxa_dt_ids);
+
+static int serial_pxa_probe_dt(struct platform_device *pdev,
+			       struct uart_pxa_port *sport)
+{
+	struct device_node *np = pdev->dev.of_node;
+	int ret;
+
+	if (!np)
+		return 1;
+
+	ret = of_alias_get_id(np, "serial");
+	if (ret < 0) {
+		dev_err(&pdev->dev, "failed to get alias id, errno %d\n", ret);
+		return ret;
+	}
+	sport->port.line = ret;
+	return 0;
+}
+
+static int serial_pxa_probe(struct platform_device *dev)
+{
+	struct uart_pxa_port *sport;
+	struct resource *mmres, *irqres;
+	int ret;
+
+	mmres = platform_get_resource(dev, IORESOURCE_MEM, 0);
+	irqres = platform_get_resource(dev, IORESOURCE_IRQ, 0);
+	if (!mmres || !irqres)
+		return -ENODEV;
+
+	sport = kzalloc(sizeof(struct uart_pxa_port), GFP_KERNEL);
+	if (!sport)
+		return -ENOMEM;
+
+	sport->clk = clk_get(&dev->dev, NULL);
+	if (IS_ERR(sport->clk)) {
+		ret = PTR_ERR(sport->clk);
+		goto err_free;
+	}
+
+	sport->port.type = PORT_PXA;
+	sport->port.iotype = UPIO_MEM;
+	sport->port.mapbase = mmres->start;
+	sport->port.irq = irqres->start;
+	sport->port.fifosize = 64;
+	sport->port.ops = &serial_pxa_pops;
+	sport->port.dev = &dev->dev;
+	sport->port.flags = UPF_IOREMAP | UPF_BOOT_AUTOCONF;
+	sport->port.uartclk = clk_get_rate(sport->clk);
+
+	ret = serial_pxa_probe_dt(dev, sport);
+	if (ret > 0)
+		sport->port.line = dev->id;
+	else if (ret < 0)
+		goto err_clk;
+	snprintf(sport->name, PXA_NAME_LEN - 1, "UART%d", sport->port.line + 1);
+
+	sport->port.membase = ioremap(mmres->start, resource_size(mmres));
+	if (!sport->port.membase) {
+		ret = -ENOMEM;
+		goto err_clk;
+	}
+
+	serial_pxa_ports[sport->port.line] = sport;
+
+	uart_add_one_port(&serial_pxa_reg, &sport->port);
+	platform_set_drvdata(dev, sport);
+
+	return 0;
+
+ err_clk:
+	clk_put(sport->clk);
+ err_free:
+	kfree(sport);
+	return ret;
+}
+
+static int serial_pxa_remove(struct platform_device *dev)
+{
+	struct uart_pxa_port *sport = platform_get_drvdata(dev);
+
+	platform_set_drvdata(dev, NULL);
+
+	uart_remove_one_port(&serial_pxa_reg, &sport->port);
+	clk_put(sport->clk);
+	kfree(sport);
+
+	return 0;
+}
+
+static struct platform_driver serial_pxa_driver = {
+        .probe          = serial_pxa_probe,
+        .remove         = serial_pxa_remove,
+
+	.driver		= {
+	        .name	= "pxa2xx-uart",
+		.owner	= THIS_MODULE,
+#ifdef CONFIG_PM
+		.pm	= &serial_pxa_pm_ops,
+#endif
+		.of_match_table = serial_pxa_dt_ids,
+	},
+};
+
+int __init serial_pxa_init(void)
+{
+	int ret;
+
+	ret = uart_register_driver(&serial_pxa_reg);
+	if (ret != 0)
+		return ret;
+
+	ret = platform_driver_register(&serial_pxa_driver);
+	if (ret != 0)
+		uart_unregister_driver(&serial_pxa_reg);
+
+	return ret;
+}
+
+void __exit serial_pxa_exit(void)
+{
+	platform_driver_unregister(&serial_pxa_driver);
+	uart_unregister_driver(&serial_pxa_reg);
+}
+
+module_init(serial_pxa_init);
+module_exit(serial_pxa_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:pxa2xx-uart");
diff --git a/ap/os/linux/linux-3.4.x/drivers/tty/serial/sa1100.c b/ap/os/linux/linux-3.4.x/drivers/tty/serial/sa1100.c
new file mode 100644
index 0000000..2ca5959
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/tty/serial/sa1100.c
@@ -0,0 +1,917 @@
+/*
+ *  Driver for SA11x0 serial ports
+ *
+ *  Based on drivers/char/serial.c, by Linus Torvalds, Theodore Ts'o.
+ *
+ *  Copyright (C) 2000 Deep Blue Solutions Ltd.
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#if defined(CONFIG_SERIAL_SA1100_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ)
+#define SUPPORT_SYSRQ
+#endif
+
+#include <linux/module.h>
+#include <linux/ioport.h>
+#include <linux/init.h>
+#include <linux/console.h>
+#include <linux/sysrq.h>
+#include <linux/platform_device.h>
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
+#include <linux/serial_core.h>
+#include <linux/serial.h>
+#include <linux/io.h>
+
+#include <asm/irq.h>
+#include <mach/hardware.h>
+#include <mach/irqs.h>
+#include <asm/mach/serial_sa1100.h>
+
+/* We've been assigned a range on the "Low-density serial ports" major */
+#define SERIAL_SA1100_MAJOR	204
+#define MINOR_START		5
+
+#define NR_PORTS		3
+
+#define SA1100_ISR_PASS_LIMIT	256
+
+/*
+ * Convert from ignore_status_mask or read_status_mask to UTSR[01]
+ */
+#define SM_TO_UTSR0(x)	((x) & 0xff)
+#define SM_TO_UTSR1(x)	((x) >> 8)
+#define UTSR0_TO_SM(x)	((x))
+#define UTSR1_TO_SM(x)	((x) << 8)
+
+#define UART_GET_UTCR0(sport)	__raw_readl((sport)->port.membase + UTCR0)
+#define UART_GET_UTCR1(sport)	__raw_readl((sport)->port.membase + UTCR1)
+#define UART_GET_UTCR2(sport)	__raw_readl((sport)->port.membase + UTCR2)
+#define UART_GET_UTCR3(sport)	__raw_readl((sport)->port.membase + UTCR3)
+#define UART_GET_UTSR0(sport)	__raw_readl((sport)->port.membase + UTSR0)
+#define UART_GET_UTSR1(sport)	__raw_readl((sport)->port.membase + UTSR1)
+#define UART_GET_CHAR(sport)	__raw_readl((sport)->port.membase + UTDR)
+
+#define UART_PUT_UTCR0(sport,v)	__raw_writel((v),(sport)->port.membase + UTCR0)
+#define UART_PUT_UTCR1(sport,v)	__raw_writel((v),(sport)->port.membase + UTCR1)
+#define UART_PUT_UTCR2(sport,v)	__raw_writel((v),(sport)->port.membase + UTCR2)
+#define UART_PUT_UTCR3(sport,v)	__raw_writel((v),(sport)->port.membase + UTCR3)
+#define UART_PUT_UTSR0(sport,v)	__raw_writel((v),(sport)->port.membase + UTSR0)
+#define UART_PUT_UTSR1(sport,v)	__raw_writel((v),(sport)->port.membase + UTSR1)
+#define UART_PUT_CHAR(sport,v)	__raw_writel((v),(sport)->port.membase + UTDR)
+
+/*
+ * This is the size of our serial port register set.
+ */
+#define UART_PORT_SIZE	0x24
+
+/*
+ * 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)
+
+struct sa1100_port {
+	struct uart_port	port;
+	struct timer_list	timer;
+	unsigned int		old_status;
+};
+
+/*
+ * Handle any change of modem status signal since we were last called.
+ */
+static void sa1100_mctrl_check(struct sa1100_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 sa1100_timeout(unsigned long data)
+{
+	struct sa1100_port *sport = (struct sa1100_port *)data;
+	unsigned long flags;
+
+	if (sport->port.state) {
+		spin_lock_irqsave(&sport->port.lock, flags);
+		sa1100_mctrl_check(sport);
+		spin_unlock_irqrestore(&sport->port.lock, flags);
+
+		mod_timer(&sport->timer, jiffies + MCTRL_TIMEOUT);
+	}
+}
+
+/*
+ * interrupts disabled on entry
+ */
+static void sa1100_stop_tx(struct uart_port *port)
+{
+	struct sa1100_port *sport = (struct sa1100_port *)port;
+	u32 utcr3;
+
+	utcr3 = UART_GET_UTCR3(sport);
+	UART_PUT_UTCR3(sport, utcr3 & ~UTCR3_TIE);
+	sport->port.read_status_mask &= ~UTSR0_TO_SM(UTSR0_TFS);
+}
+
+/*
+ * port locked and interrupts disabled
+ */
+static void sa1100_start_tx(struct uart_port *port)
+{
+	struct sa1100_port *sport = (struct sa1100_port *)port;
+	u32 utcr3;
+
+	utcr3 = UART_GET_UTCR3(sport);
+	sport->port.read_status_mask |= UTSR0_TO_SM(UTSR0_TFS);
+	UART_PUT_UTCR3(sport, utcr3 | UTCR3_TIE);
+}
+
+/*
+ * Interrupts enabled
+ */
+static void sa1100_stop_rx(struct uart_port *port)
+{
+	struct sa1100_port *sport = (struct sa1100_port *)port;
+	u32 utcr3;
+
+	utcr3 = UART_GET_UTCR3(sport);
+	UART_PUT_UTCR3(sport, utcr3 & ~UTCR3_RIE);
+}
+
+/*
+ * Set the modem control timer to fire immediately.
+ */
+static void sa1100_enable_ms(struct uart_port *port)
+{
+	struct sa1100_port *sport = (struct sa1100_port *)port;
+
+	mod_timer(&sport->timer, jiffies);
+}
+
+static void
+sa1100_rx_chars(struct sa1100_port *sport)
+{
+	struct tty_struct *tty = sport->port.state->port.tty;
+	unsigned int status, ch, flg;
+
+	status = UTSR1_TO_SM(UART_GET_UTSR1(sport)) |
+		 UTSR0_TO_SM(UART_GET_UTSR0(sport));
+	while (status & UTSR1_TO_SM(UTSR1_RNE)) {
+		ch = UART_GET_CHAR(sport);
+
+		sport->port.icount.rx++;
+
+		flg = TTY_NORMAL;
+
+		/*
+		 * note that the error handling code is
+		 * out of the main execution path
+		 */
+		if (status & UTSR1_TO_SM(UTSR1_PRE | UTSR1_FRE | UTSR1_ROR)) {
+			if (status & UTSR1_TO_SM(UTSR1_PRE))
+				sport->port.icount.parity++;
+			else if (status & UTSR1_TO_SM(UTSR1_FRE))
+				sport->port.icount.frame++;
+			if (status & UTSR1_TO_SM(UTSR1_ROR))
+				sport->port.icount.overrun++;
+
+			status &= sport->port.read_status_mask;
+
+			if (status & UTSR1_TO_SM(UTSR1_PRE))
+				flg = TTY_PARITY;
+			else if (status & UTSR1_TO_SM(UTSR1_FRE))
+				flg = TTY_FRAME;
+
+#ifdef SUPPORT_SYSRQ
+			sport->port.sysrq = 0;
+#endif
+		}
+
+		if (uart_handle_sysrq_char(&sport->port, ch))
+			goto ignore_char;
+
+		uart_insert_char(&sport->port, status, UTSR1_TO_SM(UTSR1_ROR), ch, flg);
+
+	ignore_char:
+		status = UTSR1_TO_SM(UART_GET_UTSR1(sport)) |
+			 UTSR0_TO_SM(UART_GET_UTSR0(sport));
+	}
+	tty_flip_buffer_push(tty);
+}
+
+static void sa1100_tx_chars(struct sa1100_port *sport)
+{
+	struct circ_buf *xmit = &sport->port.state->xmit;
+
+	if (sport->port.x_char) {
+		UART_PUT_CHAR(sport, sport->port.x_char);
+		sport->port.icount.tx++;
+		sport->port.x_char = 0;
+		return;
+	}
+
+	/*
+	 * Check the modem control lines before
+	 * transmitting anything.
+	 */
+	sa1100_mctrl_check(sport);
+
+	if (uart_circ_empty(xmit) || uart_tx_stopped(&sport->port)) {
+		sa1100_stop_tx(&sport->port);
+		return;
+	}
+
+	/*
+	 * Tried using FIFO (not checking TNF) for fifo fill:
+	 * still had the '4 bytes repeated' problem.
+	 */
+	while (UART_GET_UTSR1(sport) & UTSR1_TNF) {
+		UART_PUT_CHAR(sport, xmit->buf[xmit->tail]);
+		xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
+		sport->port.icount.tx++;
+		if (uart_circ_empty(xmit))
+			break;
+	}
+
+	if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
+		uart_write_wakeup(&sport->port);
+
+	if (uart_circ_empty(xmit))
+		sa1100_stop_tx(&sport->port);
+}
+
+static irqreturn_t sa1100_int(int irq, void *dev_id)
+{
+	struct sa1100_port *sport = dev_id;
+	unsigned int status, pass_counter = 0;
+
+	spin_lock(&sport->port.lock);
+	status = UART_GET_UTSR0(sport);
+	status &= SM_TO_UTSR0(sport->port.read_status_mask) | ~UTSR0_TFS;
+	do {
+		if (status & (UTSR0_RFS | UTSR0_RID)) {
+			/* Clear the receiver idle bit, if set */
+			if (status & UTSR0_RID)
+				UART_PUT_UTSR0(sport, UTSR0_RID);
+			sa1100_rx_chars(sport);
+		}
+
+		/* Clear the relevant break bits */
+		if (status & (UTSR0_RBB | UTSR0_REB))
+			UART_PUT_UTSR0(sport, status & (UTSR0_RBB | UTSR0_REB));
+
+		if (status & UTSR0_RBB)
+			sport->port.icount.brk++;
+
+		if (status & UTSR0_REB)
+			uart_handle_break(&sport->port);
+
+		if (status & UTSR0_TFS)
+			sa1100_tx_chars(sport);
+		if (pass_counter++ > SA1100_ISR_PASS_LIMIT)
+			break;
+		status = UART_GET_UTSR0(sport);
+		status &= SM_TO_UTSR0(sport->port.read_status_mask) |
+			  ~UTSR0_TFS;
+	} while (status & (UTSR0_TFS | UTSR0_RFS | UTSR0_RID));
+	spin_unlock(&sport->port.lock);
+
+	return IRQ_HANDLED;
+}
+
+/*
+ * Return TIOCSER_TEMT when transmitter is not busy.
+ */
+static unsigned int sa1100_tx_empty(struct uart_port *port)
+{
+	struct sa1100_port *sport = (struct sa1100_port *)port;
+
+	return UART_GET_UTSR1(sport) & UTSR1_TBY ? 0 : TIOCSER_TEMT;
+}
+
+static unsigned int sa1100_get_mctrl(struct uart_port *port)
+{
+	return TIOCM_CTS | TIOCM_DSR | TIOCM_CAR;
+}
+
+static void sa1100_set_mctrl(struct uart_port *port, unsigned int mctrl)
+{
+}
+
+/*
+ * Interrupts always disabled.
+ */
+static void sa1100_break_ctl(struct uart_port *port, int break_state)
+{
+	struct sa1100_port *sport = (struct sa1100_port *)port;
+	unsigned long flags;
+	unsigned int utcr3;
+
+	spin_lock_irqsave(&sport->port.lock, flags);
+	utcr3 = UART_GET_UTCR3(sport);
+	if (break_state == -1)
+		utcr3 |= UTCR3_BRK;
+	else
+		utcr3 &= ~UTCR3_BRK;
+	UART_PUT_UTCR3(sport, utcr3);
+	spin_unlock_irqrestore(&sport->port.lock, flags);
+}
+
+static int sa1100_startup(struct uart_port *port)
+{
+	struct sa1100_port *sport = (struct sa1100_port *)port;
+	int retval;
+
+	/*
+	 * Allocate the IRQ
+	 */
+	retval = request_irq(sport->port.irq, sa1100_int, 0,
+			     "sa11x0-uart", sport);
+	if (retval)
+		return retval;
+
+	/*
+	 * Finally, clear and enable interrupts
+	 */
+	UART_PUT_UTSR0(sport, -1);
+	UART_PUT_UTCR3(sport, UTCR3_RXE | UTCR3_TXE | UTCR3_RIE);
+
+	/*
+	 * Enable modem status interrupts
+	 */
+	spin_lock_irq(&sport->port.lock);
+	sa1100_enable_ms(&sport->port);
+	spin_unlock_irq(&sport->port.lock);
+
+	return 0;
+}
+
+static void sa1100_shutdown(struct uart_port *port)
+{
+	struct sa1100_port *sport = (struct sa1100_port *)port;
+
+	/*
+	 * Stop our timer.
+	 */
+	del_timer_sync(&sport->timer);
+
+	/*
+	 * Free the interrupt
+	 */
+	free_irq(sport->port.irq, sport);
+
+	/*
+	 * Disable all interrupts, port and break condition.
+	 */
+	UART_PUT_UTCR3(sport, 0);
+}
+
+static void
+sa1100_set_termios(struct uart_port *port, struct ktermios *termios,
+		   struct ktermios *old)
+{
+	struct sa1100_port *sport = (struct sa1100_port *)port;
+	unsigned long flags;
+	unsigned int utcr0, old_utcr3, baud, quot;
+	unsigned int old_csize = old ? old->c_cflag & CSIZE : CS8;
+
+	/*
+	 * We 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)
+		utcr0 = UTCR0_DSS;
+	else
+		utcr0 = 0;
+
+	if (termios->c_cflag & CSTOPB)
+		utcr0 |= UTCR0_SBS;
+	if (termios->c_cflag & PARENB) {
+		utcr0 |= UTCR0_PE;
+		if (!(termios->c_cflag & PARODD))
+			utcr0 |= UTCR0_OES;
+	}
+
+	/*
+	 * Ask the core to calculate the divisor for us.
+	 */
+	baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk/16); 
+	quot = uart_get_divisor(port, baud);
+
+	spin_lock_irqsave(&sport->port.lock, flags);
+
+	sport->port.read_status_mask &= UTSR0_TO_SM(UTSR0_TFS);
+	sport->port.read_status_mask |= UTSR1_TO_SM(UTSR1_ROR);
+	if (termios->c_iflag & INPCK)
+		sport->port.read_status_mask |=
+				UTSR1_TO_SM(UTSR1_FRE | UTSR1_PRE);
+	if (termios->c_iflag & (BRKINT | PARMRK))
+		sport->port.read_status_mask |=
+				UTSR0_TO_SM(UTSR0_RBB | UTSR0_REB);
+
+	/*
+	 * Characters to ignore
+	 */
+	sport->port.ignore_status_mask = 0;
+	if (termios->c_iflag & IGNPAR)
+		sport->port.ignore_status_mask |=
+				UTSR1_TO_SM(UTSR1_FRE | UTSR1_PRE);
+	if (termios->c_iflag & IGNBRK) {
+		sport->port.ignore_status_mask |=
+				UTSR0_TO_SM(UTSR0_RBB | UTSR0_REB);
+		/*
+		 * 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 |=
+				UTSR1_TO_SM(UTSR1_ROR);
+	}
+
+	del_timer_sync(&sport->timer);
+
+	/*
+	 * Update the per-port timeout.
+	 */
+	uart_update_timeout(port, termios->c_cflag, baud);
+
+	/*
+	 * disable interrupts and drain transmitter
+	 */
+	old_utcr3 = UART_GET_UTCR3(sport);
+	UART_PUT_UTCR3(sport, old_utcr3 & ~(UTCR3_RIE | UTCR3_TIE));
+
+	while (UART_GET_UTSR1(sport) & UTSR1_TBY)
+		barrier();
+
+	/* then, disable everything */
+	UART_PUT_UTCR3(sport, 0);
+
+	/* set the parity, stop bits and data size */
+	UART_PUT_UTCR0(sport, utcr0);
+
+	/* set the baud rate */
+	quot -= 1;
+	UART_PUT_UTCR1(sport, ((quot & 0xf00) >> 8));
+	UART_PUT_UTCR2(sport, (quot & 0xff));
+
+	UART_PUT_UTSR0(sport, -1);
+
+	UART_PUT_UTCR3(sport, old_utcr3);
+
+	if (UART_ENABLE_MS(&sport->port, termios->c_cflag))
+		sa1100_enable_ms(&sport->port);
+
+	spin_unlock_irqrestore(&sport->port.lock, flags);
+}
+
+static const char *sa1100_type(struct uart_port *port)
+{
+	struct sa1100_port *sport = (struct sa1100_port *)port;
+
+	return sport->port.type == PORT_SA1100 ? "SA1100" : NULL;
+}
+
+/*
+ * Release the memory region(s) being used by 'port'.
+ */
+static void sa1100_release_port(struct uart_port *port)
+{
+	struct sa1100_port *sport = (struct sa1100_port *)port;
+
+	release_mem_region(sport->port.mapbase, UART_PORT_SIZE);
+}
+
+/*
+ * Request the memory region(s) being used by 'port'.
+ */
+static int sa1100_request_port(struct uart_port *port)
+{
+	struct sa1100_port *sport = (struct sa1100_port *)port;
+
+	return request_mem_region(sport->port.mapbase, UART_PORT_SIZE,
+			"sa11x0-uart") != NULL ? 0 : -EBUSY;
+}
+
+/*
+ * Configure/autoconfigure the port.
+ */
+static void sa1100_config_port(struct uart_port *port, int flags)
+{
+	struct sa1100_port *sport = (struct sa1100_port *)port;
+
+	if (flags & UART_CONFIG_TYPE &&
+	    sa1100_request_port(&sport->port) == 0)
+		sport->port.type = PORT_SA1100;
+}
+
+/*
+ * Verify the new serial_struct (for TIOCSSERIAL).
+ * The only change we allow are to the flags and type, and
+ * even then only between PORT_SA1100 and PORT_UNKNOWN
+ */
+static int
+sa1100_verify_port(struct uart_port *port, struct serial_struct *ser)
+{
+	struct sa1100_port *sport = (struct sa1100_port *)port;
+	int ret = 0;
+
+	if (ser->type != PORT_UNKNOWN && ser->type != PORT_SA1100)
+		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 sa1100_pops = {
+	.tx_empty	= sa1100_tx_empty,
+	.set_mctrl	= sa1100_set_mctrl,
+	.get_mctrl	= sa1100_get_mctrl,
+	.stop_tx	= sa1100_stop_tx,
+	.start_tx	= sa1100_start_tx,
+	.stop_rx	= sa1100_stop_rx,
+	.enable_ms	= sa1100_enable_ms,
+	.break_ctl	= sa1100_break_ctl,
+	.startup	= sa1100_startup,
+	.shutdown	= sa1100_shutdown,
+	.set_termios	= sa1100_set_termios,
+	.type		= sa1100_type,
+	.release_port	= sa1100_release_port,
+	.request_port	= sa1100_request_port,
+	.config_port	= sa1100_config_port,
+	.verify_port	= sa1100_verify_port,
+};
+
+static struct sa1100_port sa1100_ports[NR_PORTS];
+
+/*
+ * Setup the SA1100 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=ttySAx" where "x" is either 0 or 1.
+ * Which serial port this ends up being depends on the machine you're
+ * running this kernel on.  I'm not convinced that this is a good idea,
+ * but that's the way it traditionally works.
+ *
+ * Note that NanoEngine UART3 becomes UART2, and UART2 is no longer
+ * used here.
+ */
+static void __init sa1100_init_ports(void)
+{
+	static int first = 1;
+	int i;
+
+	if (!first)
+		return;
+	first = 0;
+
+	for (i = 0; i < NR_PORTS; i++) {
+		sa1100_ports[i].port.uartclk   = 3686400;
+		sa1100_ports[i].port.ops       = &sa1100_pops;
+		sa1100_ports[i].port.fifosize  = 8;
+		sa1100_ports[i].port.line      = i;
+		sa1100_ports[i].port.iotype    = UPIO_MEM;
+		init_timer(&sa1100_ports[i].timer);
+		sa1100_ports[i].timer.function = sa1100_timeout;
+		sa1100_ports[i].timer.data     = (unsigned long)&sa1100_ports[i];
+	}
+
+	/*
+	 * make transmit lines outputs, so that when the port
+	 * is closed, the output is in the MARK state.
+	 */
+	PPDR |= PPC_TXD1 | PPC_TXD3;
+	PPSR |= PPC_TXD1 | PPC_TXD3;
+}
+
+void __devinit sa1100_register_uart_fns(struct sa1100_port_fns *fns)
+{
+	if (fns->get_mctrl)
+		sa1100_pops.get_mctrl = fns->get_mctrl;
+	if (fns->set_mctrl)
+		sa1100_pops.set_mctrl = fns->set_mctrl;
+
+	sa1100_pops.pm       = fns->pm;
+	sa1100_pops.set_wake = fns->set_wake;
+}
+
+void __init sa1100_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 1:
+		sa1100_ports[idx].port.membase = (void __iomem *)&Ser1UTCR0;
+		sa1100_ports[idx].port.mapbase = _Ser1UTCR0;
+		sa1100_ports[idx].port.irq     = IRQ_Ser1UART;
+		sa1100_ports[idx].port.flags   = UPF_BOOT_AUTOCONF;
+		break;
+
+	case 2:
+		sa1100_ports[idx].port.membase = (void __iomem *)&Ser2UTCR0;
+		sa1100_ports[idx].port.mapbase = _Ser2UTCR0;
+		sa1100_ports[idx].port.irq     = IRQ_Ser2ICP;
+		sa1100_ports[idx].port.flags   = UPF_BOOT_AUTOCONF;
+		break;
+
+	case 3:
+		sa1100_ports[idx].port.membase = (void __iomem *)&Ser3UTCR0;
+		sa1100_ports[idx].port.mapbase = _Ser3UTCR0;
+		sa1100_ports[idx].port.irq     = IRQ_Ser3UART;
+		sa1100_ports[idx].port.flags   = UPF_BOOT_AUTOCONF;
+		break;
+
+	default:
+		printk(KERN_ERR "%s: bad port number %d\n", __func__, port);
+	}
+}
+
+
+#ifdef CONFIG_SERIAL_SA1100_CONSOLE
+static void sa1100_console_putchar(struct uart_port *port, int ch)
+{
+	struct sa1100_port *sport = (struct sa1100_port *)port;
+
+	while (!(UART_GET_UTSR1(sport) & UTSR1_TNF))
+		barrier();
+	UART_PUT_CHAR(sport, ch);
+}
+
+/*
+ * Interrupts are disabled on entering
+ */
+static void
+sa1100_console_write(struct console *co, const char *s, unsigned int count)
+{
+	struct sa1100_port *sport = &sa1100_ports[co->index];
+	unsigned int old_utcr3, status;
+
+	/*
+	 *	First, save UTCR3 and then disable interrupts
+	 */
+	old_utcr3 = UART_GET_UTCR3(sport);
+	UART_PUT_UTCR3(sport, (old_utcr3 & ~(UTCR3_RIE | UTCR3_TIE)) |
+				UTCR3_TXE);
+
+	uart_console_write(&sport->port, s, count, sa1100_console_putchar);
+
+	/*
+	 *	Finally, wait for transmitter to become empty
+	 *	and restore UTCR3
+	 */
+	do {
+		status = UART_GET_UTSR1(sport);
+	} while (status & UTSR1_TBY);
+	UART_PUT_UTCR3(sport, old_utcr3);
+}
+
+/*
+ * If the port was already initialised (eg, by a boot loader),
+ * try to determine the current setup.
+ */
+static void __init
+sa1100_console_get_options(struct sa1100_port *sport, int *baud,
+			   int *parity, int *bits)
+{
+	unsigned int utcr3;
+
+	utcr3 = UART_GET_UTCR3(sport) & (UTCR3_RXE | UTCR3_TXE);
+	if (utcr3 == (UTCR3_RXE | UTCR3_TXE)) {
+		/* ok, the port was enabled */
+		unsigned int utcr0, quot;
+
+		utcr0 = UART_GET_UTCR0(sport);
+
+		*parity = 'n';
+		if (utcr0 & UTCR0_PE) {
+			if (utcr0 & UTCR0_OES)
+				*parity = 'e';
+			else
+				*parity = 'o';
+		}
+
+		if (utcr0 & UTCR0_DSS)
+			*bits = 8;
+		else
+			*bits = 7;
+
+		quot = UART_GET_UTCR2(sport) | UART_GET_UTCR1(sport) << 8;
+		quot &= 0xfff;
+		*baud = sport->port.uartclk / (16 * (quot + 1));
+	}
+}
+
+static int __init
+sa1100_console_setup(struct console *co, char *options)
+{
+	struct sa1100_port *sport;
+	int baud = 9600;
+	int bits = 8;
+	int parity = 'n';
+	int flow = 'n';
+
+	/*
+	 * 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 = &sa1100_ports[co->index];
+
+	if (options)
+		uart_parse_options(options, &baud, &parity, &bits, &flow);
+	else
+		sa1100_console_get_options(sport, &baud, &parity, &bits);
+
+	return uart_set_options(&sport->port, co, baud, parity, bits, flow);
+}
+
+static struct uart_driver sa1100_reg;
+static struct console sa1100_console = {
+	.name		= "ttySA",
+	.write		= sa1100_console_write,
+	.device		= uart_console_device,
+	.setup		= sa1100_console_setup,
+	.flags		= CON_PRINTBUFFER,
+	.index		= -1,
+	.data		= &sa1100_reg,
+};
+
+static int __init sa1100_rs_console_init(void)
+{
+	sa1100_init_ports();
+	register_console(&sa1100_console);
+	return 0;
+}
+console_initcall(sa1100_rs_console_init);
+
+#define SA1100_CONSOLE	&sa1100_console
+#else
+#define SA1100_CONSOLE	NULL
+#endif
+
+static struct uart_driver sa1100_reg = {
+	.owner			= THIS_MODULE,
+	.driver_name		= "ttySA",
+	.dev_name		= "ttySA",
+	.major			= SERIAL_SA1100_MAJOR,
+	.minor			= MINOR_START,
+	.nr			= NR_PORTS,
+	.cons			= SA1100_CONSOLE,
+};
+
+static int sa1100_serial_suspend(struct platform_device *dev, pm_message_t state)
+{
+	struct sa1100_port *sport = platform_get_drvdata(dev);
+
+	if (sport)
+		uart_suspend_port(&sa1100_reg, &sport->port);
+
+	return 0;
+}
+
+static int sa1100_serial_resume(struct platform_device *dev)
+{
+	struct sa1100_port *sport = platform_get_drvdata(dev);
+
+	if (sport)
+		uart_resume_port(&sa1100_reg, &sport->port);
+
+	return 0;
+}
+
+static int sa1100_serial_probe(struct platform_device *dev)
+{
+	struct resource *res = dev->resource;
+	int i;
+
+	for (i = 0; i < dev->num_resources; i++, res++)
+		if (res->flags & IORESOURCE_MEM)
+			break;
+
+	if (i < dev->num_resources) {
+		for (i = 0; i < NR_PORTS; i++) {
+			if (sa1100_ports[i].port.mapbase != res->start)
+				continue;
+
+			sa1100_ports[i].port.dev = &dev->dev;
+			uart_add_one_port(&sa1100_reg, &sa1100_ports[i].port);
+			platform_set_drvdata(dev, &sa1100_ports[i]);
+			break;
+		}
+	}
+
+	return 0;
+}
+
+static int sa1100_serial_remove(struct platform_device *pdev)
+{
+	struct sa1100_port *sport = platform_get_drvdata(pdev);
+
+	platform_set_drvdata(pdev, NULL);
+
+	if (sport)
+		uart_remove_one_port(&sa1100_reg, &sport->port);
+
+	return 0;
+}
+
+static struct platform_driver sa11x0_serial_driver = {
+	.probe		= sa1100_serial_probe,
+	.remove		= sa1100_serial_remove,
+	.suspend	= sa1100_serial_suspend,
+	.resume		= sa1100_serial_resume,
+	.driver		= {
+		.name	= "sa11x0-uart",
+		.owner	= THIS_MODULE,
+	},
+};
+
+static int __init sa1100_serial_init(void)
+{
+	int ret;
+
+	printk(KERN_INFO "Serial: SA11x0 driver\n");
+
+	sa1100_init_ports();
+
+	ret = uart_register_driver(&sa1100_reg);
+	if (ret == 0) {
+		ret = platform_driver_register(&sa11x0_serial_driver);
+		if (ret)
+			uart_unregister_driver(&sa1100_reg);
+	}
+	return ret;
+}
+
+static void __exit sa1100_serial_exit(void)
+{
+	platform_driver_unregister(&sa11x0_serial_driver);
+	uart_unregister_driver(&sa1100_reg);
+}
+
+module_init(sa1100_serial_init);
+module_exit(sa1100_serial_exit);
+
+MODULE_AUTHOR("Deep Blue Solutions Ltd");
+MODULE_DESCRIPTION("SA1100 generic serial port driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS_CHARDEV_MAJOR(SERIAL_SA1100_MAJOR);
+MODULE_ALIAS("platform:sa11x0-uart");
diff --git a/ap/os/linux/linux-3.4.x/drivers/tty/serial/samsung.c b/ap/os/linux/linux-3.4.x/drivers/tty/serial/samsung.c
new file mode 100644
index 0000000..1b7d2c0
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/tty/serial/samsung.c
@@ -0,0 +1,1704 @@
+/*
+ * Driver core for Samsung SoC onboard UARTs.
+ *
+ * Ben Dooks, Copyright (c) 2003-2008 Simtec Electronics
+ *	http://armlinux.simtec.co.uk/
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+*/
+
+/* Hote on 2410 error handling
+ *
+ * The s3c2410 manual has a love/hate affair with the contents of the
+ * UERSTAT register in the UART blocks, and keeps marking some of the
+ * error bits as reserved. Having checked with the s3c2410x01,
+ * it copes with BREAKs properly, so I am happy to ignore the RESERVED
+ * feature from the latter versions of the manual.
+ *
+ * If it becomes aparrent that latter versions of the 2410 remove these
+ * bits, then action will have to be taken to differentiate the versions
+ * and change the policy on BREAK
+ *
+ * BJD, 04-Nov-2004
+*/
+
+#if defined(CONFIG_SERIAL_SAMSUNG_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ)
+#define SUPPORT_SYSRQ
+#endif
+
+#include <linux/module.h>
+#include <linux/ioport.h>
+#include <linux/io.h>
+#include <linux/platform_device.h>
+#include <linux/init.h>
+#include <linux/sysrq.h>
+#include <linux/console.h>
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
+#include <linux/serial_core.h>
+#include <linux/serial.h>
+#include <linux/delay.h>
+#include <linux/clk.h>
+#include <linux/cpufreq.h>
+#include <linux/of.h>
+
+#include <asm/irq.h>
+
+#include <mach/hardware.h>
+#include <mach/map.h>
+
+#include <plat/regs-serial.h>
+#include <plat/clock.h>
+
+#include "samsung.h"
+
+/* UART name and device definitions */
+
+#define S3C24XX_SERIAL_NAME	"ttySAC"
+#define S3C24XX_SERIAL_MAJOR	204
+#define S3C24XX_SERIAL_MINOR	64
+
+/* macros to change one thing to another */
+
+#define tx_enabled(port) ((port)->unused[0])
+#define rx_enabled(port) ((port)->unused[1])
+
+/* flag to ignore all characters coming in */
+#define RXSTAT_DUMMY_READ (0x10000000)
+
+static inline struct s3c24xx_uart_port *to_ourport(struct uart_port *port)
+{
+	return container_of(port, struct s3c24xx_uart_port, port);
+}
+
+/* translate a port to the device name */
+
+static inline const char *s3c24xx_serial_portname(struct uart_port *port)
+{
+	return to_platform_device(port->dev)->name;
+}
+
+static int s3c24xx_serial_txempty_nofifo(struct uart_port *port)
+{
+	return (rd_regl(port, S3C2410_UTRSTAT) & S3C2410_UTRSTAT_TXE);
+}
+
+/*
+ * s3c64xx and later SoC's include the interrupt mask and status registers in
+ * the controller itself, unlike the s3c24xx SoC's which have these registers
+ * in the interrupt controller. Check if the port type is s3c64xx or higher.
+ */
+static int s3c24xx_serial_has_interrupt_mask(struct uart_port *port)
+{
+	return to_ourport(port)->info->type == PORT_S3C6400;
+}
+
+static void s3c24xx_serial_rx_enable(struct uart_port *port)
+{
+	unsigned long flags;
+	unsigned int ucon, ufcon;
+	int count = 10000;
+
+	spin_lock_irqsave(&port->lock, flags);
+
+	while (--count && !s3c24xx_serial_txempty_nofifo(port))
+		udelay(100);
+
+	ufcon = rd_regl(port, S3C2410_UFCON);
+	ufcon |= S3C2410_UFCON_RESETRX;
+	wr_regl(port, S3C2410_UFCON, ufcon);
+
+	ucon = rd_regl(port, S3C2410_UCON);
+	ucon |= S3C2410_UCON_RXIRQMODE;
+	wr_regl(port, S3C2410_UCON, ucon);
+
+	rx_enabled(port) = 1;
+	spin_unlock_irqrestore(&port->lock, flags);
+}
+
+static void s3c24xx_serial_rx_disable(struct uart_port *port)
+{
+	unsigned long flags;
+	unsigned int ucon;
+
+	spin_lock_irqsave(&port->lock, flags);
+
+	ucon = rd_regl(port, S3C2410_UCON);
+	ucon &= ~S3C2410_UCON_RXIRQMODE;
+	wr_regl(port, S3C2410_UCON, ucon);
+
+	rx_enabled(port) = 0;
+	spin_unlock_irqrestore(&port->lock, flags);
+}
+
+static void s3c24xx_serial_stop_tx(struct uart_port *port)
+{
+	struct s3c24xx_uart_port *ourport = to_ourport(port);
+
+	if (tx_enabled(port)) {
+		if (s3c24xx_serial_has_interrupt_mask(port))
+			__set_bit(S3C64XX_UINTM_TXD,
+				portaddrl(port, S3C64XX_UINTM));
+		else
+			disable_irq_nosync(ourport->tx_irq);
+		tx_enabled(port) = 0;
+		if (port->flags & UPF_CONS_FLOW)
+			s3c24xx_serial_rx_enable(port);
+	}
+}
+
+static void s3c24xx_serial_start_tx(struct uart_port *port)
+{
+	struct s3c24xx_uart_port *ourport = to_ourport(port);
+
+	if (!tx_enabled(port)) {
+		if (port->flags & UPF_CONS_FLOW)
+			s3c24xx_serial_rx_disable(port);
+
+		if (s3c24xx_serial_has_interrupt_mask(port))
+			__clear_bit(S3C64XX_UINTM_TXD,
+				portaddrl(port, S3C64XX_UINTM));
+		else
+			enable_irq(ourport->tx_irq);
+		tx_enabled(port) = 1;
+	}
+}
+
+static void s3c24xx_serial_stop_rx(struct uart_port *port)
+{
+	struct s3c24xx_uart_port *ourport = to_ourport(port);
+
+	if (rx_enabled(port)) {
+		dbg("s3c24xx_serial_stop_rx: port=%p\n", port);
+		if (s3c24xx_serial_has_interrupt_mask(port))
+			__set_bit(S3C64XX_UINTM_RXD,
+				portaddrl(port, S3C64XX_UINTM));
+		else
+			disable_irq_nosync(ourport->rx_irq);
+		rx_enabled(port) = 0;
+	}
+}
+
+static void s3c24xx_serial_enable_ms(struct uart_port *port)
+{
+}
+
+static inline struct s3c24xx_uart_info *s3c24xx_port_to_info(struct uart_port *port)
+{
+	return to_ourport(port)->info;
+}
+
+static inline struct s3c2410_uartcfg *s3c24xx_port_to_cfg(struct uart_port *port)
+{
+	struct s3c24xx_uart_port *ourport;
+
+	if (port->dev == NULL)
+		return NULL;
+
+	ourport = container_of(port, struct s3c24xx_uart_port, port);
+	return ourport->cfg;
+}
+
+static int s3c24xx_serial_rx_fifocnt(struct s3c24xx_uart_port *ourport,
+				     unsigned long ufstat)
+{
+	struct s3c24xx_uart_info *info = ourport->info;
+
+	if (ufstat & info->rx_fifofull)
+		return ourport->port.fifosize;
+
+	return (ufstat & info->rx_fifomask) >> info->rx_fifoshift;
+}
+
+
+/* ? - where has parity gone?? */
+#define S3C2410_UERSTAT_PARITY (0x1000)
+
+static irqreturn_t
+s3c24xx_serial_rx_chars(int irq, void *dev_id)
+{
+	struct s3c24xx_uart_port *ourport = dev_id;
+	struct uart_port *port = &ourport->port;
+	struct tty_struct *tty = port->state->port.tty;
+	unsigned int ufcon, ch, flag, ufstat, uerstat;
+	int max_count = 64;
+
+	while (max_count-- > 0) {
+		ufcon = rd_regl(port, S3C2410_UFCON);
+		ufstat = rd_regl(port, S3C2410_UFSTAT);
+
+		if (s3c24xx_serial_rx_fifocnt(ourport, ufstat) == 0)
+			break;
+
+		uerstat = rd_regl(port, S3C2410_UERSTAT);
+		ch = rd_regb(port, S3C2410_URXH);
+
+		if (port->flags & UPF_CONS_FLOW) {
+			int txe = s3c24xx_serial_txempty_nofifo(port);
+
+			if (rx_enabled(port)) {
+				if (!txe) {
+					rx_enabled(port) = 0;
+					continue;
+				}
+			} else {
+				if (txe) {
+					ufcon |= S3C2410_UFCON_RESETRX;
+					wr_regl(port, S3C2410_UFCON, ufcon);
+					rx_enabled(port) = 1;
+					goto out;
+				}
+				continue;
+			}
+		}
+
+		/* insert the character into the buffer */
+
+		flag = TTY_NORMAL;
+		port->icount.rx++;
+
+		if (unlikely(uerstat & S3C2410_UERSTAT_ANY)) {
+			dbg("rxerr: port ch=0x%02x, rxs=0x%08x\n",
+			    ch, uerstat);
+
+			/* check for break */
+			if (uerstat & S3C2410_UERSTAT_BREAK) {
+				dbg("break!\n");
+				port->icount.brk++;
+				if (uart_handle_break(port))
+				    goto ignore_char;
+			}
+
+			if (uerstat & S3C2410_UERSTAT_FRAME)
+				port->icount.frame++;
+			if (uerstat & S3C2410_UERSTAT_OVERRUN)
+				port->icount.overrun++;
+
+			uerstat &= port->read_status_mask;
+
+			if (uerstat & S3C2410_UERSTAT_BREAK)
+				flag = TTY_BREAK;
+			else if (uerstat & S3C2410_UERSTAT_PARITY)
+				flag = TTY_PARITY;
+			else if (uerstat & (S3C2410_UERSTAT_FRAME |
+					    S3C2410_UERSTAT_OVERRUN))
+				flag = TTY_FRAME;
+		}
+
+		if (uart_handle_sysrq_char(port, ch))
+			goto ignore_char;
+
+		uart_insert_char(port, uerstat, S3C2410_UERSTAT_OVERRUN,
+				 ch, flag);
+
+ ignore_char:
+		continue;
+	}
+	tty_flip_buffer_push(tty);
+
+ out:
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t s3c24xx_serial_tx_chars(int irq, void *id)
+{
+	struct s3c24xx_uart_port *ourport = id;
+	struct uart_port *port = &ourport->port;
+	struct circ_buf *xmit = &port->state->xmit;
+	int count = 256;
+
+	if (port->x_char) {
+		wr_regb(port, S3C2410_UTXH, port->x_char);
+		port->icount.tx++;
+		port->x_char = 0;
+		goto out;
+	}
+
+	/* if there isn't anything more to transmit, or the uart is now
+	 * stopped, disable the uart and exit
+	*/
+
+	if (uart_circ_empty(xmit) || uart_tx_stopped(port)) {
+		s3c24xx_serial_stop_tx(port);
+		goto out;
+	}
+
+	/* try and drain the buffer... */
+
+	while (!uart_circ_empty(xmit) && count-- > 0) {
+		if (rd_regl(port, S3C2410_UFSTAT) & ourport->info->tx_fifofull)
+			break;
+
+		wr_regb(port, S3C2410_UTXH, xmit->buf[xmit->tail]);
+		xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
+		port->icount.tx++;
+	}
+
+	if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
+		uart_write_wakeup(port);
+
+	if (uart_circ_empty(xmit))
+		s3c24xx_serial_stop_tx(port);
+
+ out:
+	return IRQ_HANDLED;
+}
+
+/* interrupt handler for s3c64xx and later SoC's.*/
+static irqreturn_t s3c64xx_serial_handle_irq(int irq, void *id)
+{
+	struct s3c24xx_uart_port *ourport = id;
+	struct uart_port *port = &ourport->port;
+	unsigned int pend = rd_regl(port, S3C64XX_UINTP);
+	unsigned long flags;
+	irqreturn_t ret = IRQ_HANDLED;
+
+	spin_lock_irqsave(&port->lock, flags);
+	if (pend & S3C64XX_UINTM_RXD_MSK) {
+		ret = s3c24xx_serial_rx_chars(irq, id);
+		wr_regl(port, S3C64XX_UINTP, S3C64XX_UINTM_RXD_MSK);
+	}
+	if (pend & S3C64XX_UINTM_TXD_MSK) {
+		ret = s3c24xx_serial_tx_chars(irq, id);
+		wr_regl(port, S3C64XX_UINTP, S3C64XX_UINTM_TXD_MSK);
+	}
+	spin_unlock_irqrestore(&port->lock, flags);
+	return ret;
+}
+
+static unsigned int s3c24xx_serial_tx_empty(struct uart_port *port)
+{
+	struct s3c24xx_uart_info *info = s3c24xx_port_to_info(port);
+	unsigned long ufstat = rd_regl(port, S3C2410_UFSTAT);
+	unsigned long ufcon = rd_regl(port, S3C2410_UFCON);
+
+	if (ufcon & S3C2410_UFCON_FIFOMODE) {
+		if ((ufstat & info->tx_fifomask) != 0 ||
+		    (ufstat & info->tx_fifofull))
+			return 0;
+
+		return 1;
+	}
+
+	return s3c24xx_serial_txempty_nofifo(port);
+}
+
+/* no modem control lines */
+static unsigned int s3c24xx_serial_get_mctrl(struct uart_port *port)
+{
+	unsigned int umstat = rd_regb(port, S3C2410_UMSTAT);
+
+	if (umstat & S3C2410_UMSTAT_CTS)
+		return TIOCM_CAR | TIOCM_DSR | TIOCM_CTS;
+	else
+		return TIOCM_CAR | TIOCM_DSR;
+}
+
+static void s3c24xx_serial_set_mctrl(struct uart_port *port, unsigned int mctrl)
+{
+	/* todo - possibly remove AFC and do manual CTS */
+}
+
+static void s3c24xx_serial_break_ctl(struct uart_port *port, int break_state)
+{
+	unsigned long flags;
+	unsigned int ucon;
+
+	spin_lock_irqsave(&port->lock, flags);
+
+	ucon = rd_regl(port, S3C2410_UCON);
+
+	if (break_state)
+		ucon |= S3C2410_UCON_SBREAK;
+	else
+		ucon &= ~S3C2410_UCON_SBREAK;
+
+	wr_regl(port, S3C2410_UCON, ucon);
+
+	spin_unlock_irqrestore(&port->lock, flags);
+}
+
+static void s3c24xx_serial_shutdown(struct uart_port *port)
+{
+	struct s3c24xx_uart_port *ourport = to_ourport(port);
+
+	if (ourport->tx_claimed) {
+		if (!s3c24xx_serial_has_interrupt_mask(port))
+			free_irq(ourport->tx_irq, ourport);
+		tx_enabled(port) = 0;
+		ourport->tx_claimed = 0;
+	}
+
+	if (ourport->rx_claimed) {
+		if (!s3c24xx_serial_has_interrupt_mask(port))
+			free_irq(ourport->rx_irq, ourport);
+		ourport->rx_claimed = 0;
+		rx_enabled(port) = 0;
+	}
+
+	/* Clear pending interrupts and mask all interrupts */
+	if (s3c24xx_serial_has_interrupt_mask(port)) {
+		wr_regl(port, S3C64XX_UINTP, 0xf);
+		wr_regl(port, S3C64XX_UINTM, 0xf);
+	}
+}
+
+static int s3c24xx_serial_startup(struct uart_port *port)
+{
+	struct s3c24xx_uart_port *ourport = to_ourport(port);
+	int ret;
+
+	dbg("s3c24xx_serial_startup: port=%p (%08lx,%p)\n",
+	    port->mapbase, port->membase);
+
+	rx_enabled(port) = 1;
+
+	ret = request_irq(ourport->rx_irq, s3c24xx_serial_rx_chars, 0,
+			  s3c24xx_serial_portname(port), ourport);
+
+	if (ret != 0) {
+		printk(KERN_ERR "cannot get irq %d\n", ourport->rx_irq);
+		return ret;
+	}
+
+	ourport->rx_claimed = 1;
+
+	dbg("requesting tx irq...\n");
+
+	tx_enabled(port) = 1;
+
+	ret = request_irq(ourport->tx_irq, s3c24xx_serial_tx_chars, 0,
+			  s3c24xx_serial_portname(port), ourport);
+
+	if (ret) {
+		printk(KERN_ERR "cannot get irq %d\n", ourport->tx_irq);
+		goto err;
+	}
+
+	ourport->tx_claimed = 1;
+
+	dbg("s3c24xx_serial_startup ok\n");
+
+	/* the port reset code should have done the correct
+	 * register setup for the port controls */
+
+	return ret;
+
+ err:
+	s3c24xx_serial_shutdown(port);
+	return ret;
+}
+
+static int s3c64xx_serial_startup(struct uart_port *port)
+{
+	struct s3c24xx_uart_port *ourport = to_ourport(port);
+	int ret;
+
+	dbg("s3c64xx_serial_startup: port=%p (%08lx,%p)\n",
+	    port->mapbase, port->membase);
+
+	ret = request_irq(port->irq, s3c64xx_serial_handle_irq, IRQF_SHARED,
+			  s3c24xx_serial_portname(port), ourport);
+	if (ret) {
+		printk(KERN_ERR "cannot get irq %d\n", port->irq);
+		return ret;
+	}
+
+	/* For compatibility with s3c24xx Soc's */
+	rx_enabled(port) = 1;
+	ourport->rx_claimed = 1;
+	tx_enabled(port) = 0;
+	ourport->tx_claimed = 1;
+
+	/* Enable Rx Interrupt */
+	__clear_bit(S3C64XX_UINTM_RXD, portaddrl(port, S3C64XX_UINTM));
+	dbg("s3c64xx_serial_startup ok\n");
+	return ret;
+}
+
+/* power power management control */
+
+static void s3c24xx_serial_pm(struct uart_port *port, unsigned int level,
+			      unsigned int old)
+{
+	struct s3c24xx_uart_port *ourport = to_ourport(port);
+	int timeout = 10000;
+
+	ourport->pm_level = level;
+
+	switch (level) {
+	case 3:
+		while (--timeout && !s3c24xx_serial_txempty_nofifo(port))
+			udelay(100);
+
+		if (!IS_ERR(ourport->baudclk) && ourport->baudclk != NULL)
+			clk_disable(ourport->baudclk);
+
+		clk_disable(ourport->clk);
+		break;
+
+	case 0:
+		clk_enable(ourport->clk);
+
+		if (!IS_ERR(ourport->baudclk) && ourport->baudclk != NULL)
+			clk_enable(ourport->baudclk);
+
+		break;
+	default:
+		printk(KERN_ERR "s3c24xx_serial: unknown pm %d\n", level);
+	}
+}
+
+/* baud rate calculation
+ *
+ * The UARTs on the S3C2410/S3C2440 can take their clocks from a number
+ * of different sources, including the peripheral clock ("pclk") and an
+ * external clock ("uclk"). The S3C2440 also adds the core clock ("fclk")
+ * with a programmable extra divisor.
+ *
+ * The following code goes through the clock sources, and calculates the
+ * baud clocks (and the resultant actual baud rates) and then tries to
+ * pick the closest one and select that.
+ *
+*/
+
+#define MAX_CLK_NAME_LENGTH 15
+
+static inline int s3c24xx_serial_getsource(struct uart_port *port)
+{
+	struct s3c24xx_uart_info *info = s3c24xx_port_to_info(port);
+	unsigned int ucon;
+
+	if (info->num_clks == 1)
+		return 0;
+
+	ucon = rd_regl(port, S3C2410_UCON);
+	ucon &= info->clksel_mask;
+	return ucon >> info->clksel_shift;
+}
+
+static void s3c24xx_serial_setsource(struct uart_port *port,
+			unsigned int clk_sel)
+{
+	struct s3c24xx_uart_info *info = s3c24xx_port_to_info(port);
+	unsigned int ucon;
+
+	if (info->num_clks == 1)
+		return;
+
+	ucon = rd_regl(port, S3C2410_UCON);
+	if ((ucon & info->clksel_mask) >> info->clksel_shift == clk_sel)
+		return;
+
+	ucon &= ~info->clksel_mask;
+	ucon |= clk_sel << info->clksel_shift;
+	wr_regl(port, S3C2410_UCON, ucon);
+}
+
+static unsigned int s3c24xx_serial_getclk(struct s3c24xx_uart_port *ourport,
+			unsigned int req_baud, struct clk **best_clk,
+			unsigned int *clk_num)
+{
+	struct s3c24xx_uart_info *info = ourport->info;
+	struct clk *clk;
+	unsigned long rate;
+	unsigned int cnt, baud, quot, clk_sel, best_quot = 0;
+	char clkname[MAX_CLK_NAME_LENGTH];
+	int calc_deviation, deviation = (1 << 30) - 1;
+
+	*best_clk = NULL;
+	clk_sel = (ourport->cfg->clk_sel) ? ourport->cfg->clk_sel :
+			ourport->info->def_clk_sel;
+	for (cnt = 0; cnt < info->num_clks; cnt++) {
+		if (!(clk_sel & (1 << cnt)))
+			continue;
+
+		sprintf(clkname, "clk_uart_baud%d", cnt);
+		clk = clk_get(ourport->port.dev, clkname);
+		if (IS_ERR_OR_NULL(clk))
+			continue;
+
+		rate = clk_get_rate(clk);
+		if (!rate)
+			continue;
+
+		if (ourport->info->has_divslot) {
+			unsigned long div = rate / req_baud;
+
+			/* The UDIVSLOT register on the newer UARTs allows us to
+			 * get a divisor adjustment of 1/16th on the baud clock.
+			 *
+			 * We don't keep the UDIVSLOT value (the 16ths we
+			 * calculated by not multiplying the baud by 16) as it
+			 * is easy enough to recalculate.
+			 */
+
+			quot = div / 16;
+			baud = rate / div;
+		} else {
+			quot = (rate + (8 * req_baud)) / (16 * req_baud);
+			baud = rate / (quot * 16);
+		}
+		quot--;
+
+		calc_deviation = req_baud - baud;
+		if (calc_deviation < 0)
+			calc_deviation = -calc_deviation;
+
+		if (calc_deviation < deviation) {
+			*best_clk = clk;
+			best_quot = quot;
+			*clk_num = cnt;
+			deviation = calc_deviation;
+		}
+	}
+
+	return best_quot;
+}
+
+/* udivslot_table[]
+ *
+ * This table takes the fractional value of the baud divisor and gives
+ * the recommended setting for the UDIVSLOT register.
+ */
+static u16 udivslot_table[16] = {
+	[0] = 0x0000,
+	[1] = 0x0080,
+	[2] = 0x0808,
+	[3] = 0x0888,
+	[4] = 0x2222,
+	[5] = 0x4924,
+	[6] = 0x4A52,
+	[7] = 0x54AA,
+	[8] = 0x5555,
+	[9] = 0xD555,
+	[10] = 0xD5D5,
+	[11] = 0xDDD5,
+	[12] = 0xDDDD,
+	[13] = 0xDFDD,
+	[14] = 0xDFDF,
+	[15] = 0xFFDF,
+};
+
+static void s3c24xx_serial_set_termios(struct uart_port *port,
+				       struct ktermios *termios,
+				       struct ktermios *old)
+{
+	struct s3c2410_uartcfg *cfg = s3c24xx_port_to_cfg(port);
+	struct s3c24xx_uart_port *ourport = to_ourport(port);
+	struct clk *clk = NULL;
+	unsigned long flags;
+	unsigned int baud, quot, clk_sel = 0;
+	unsigned int ulcon;
+	unsigned int umcon;
+	unsigned int udivslot = 0;
+
+	/*
+	 * We don't support modem control lines.
+	 */
+	termios->c_cflag &= ~(HUPCL | CMSPAR);
+	termios->c_cflag |= CLOCAL;
+
+	/*
+	 * Ask the core to calculate the divisor for us.
+	 */
+
+	baud = uart_get_baud_rate(port, termios, old, 0, 115200*8);
+	quot = s3c24xx_serial_getclk(ourport, baud, &clk, &clk_sel);
+	if (baud == 38400 && (port->flags & UPF_SPD_MASK) == UPF_SPD_CUST)
+		quot = port->custom_divisor;
+	if (!clk)
+		return;
+
+	/* check to see if we need  to change clock source */
+
+	if (ourport->baudclk != clk) {
+		s3c24xx_serial_setsource(port, clk_sel);
+
+		if (ourport->baudclk != NULL && !IS_ERR(ourport->baudclk)) {
+			clk_disable(ourport->baudclk);
+			ourport->baudclk  = NULL;
+		}
+
+		clk_enable(clk);
+
+		ourport->baudclk = clk;
+		ourport->baudclk_rate = clk ? clk_get_rate(clk) : 0;
+	}
+
+	if (ourport->info->has_divslot) {
+		unsigned int div = ourport->baudclk_rate / baud;
+
+		if (cfg->has_fracval) {
+			udivslot = (div & 15);
+			dbg("fracval = %04x\n", udivslot);
+		} else {
+			udivslot = udivslot_table[div & 15];
+			dbg("udivslot = %04x (div %d)\n", udivslot, div & 15);
+		}
+	}
+
+	switch (termios->c_cflag & CSIZE) {
+	case CS5:
+		dbg("config: 5bits/char\n");
+		ulcon = S3C2410_LCON_CS5;
+		break;
+	case CS6:
+		dbg("config: 6bits/char\n");
+		ulcon = S3C2410_LCON_CS6;
+		break;
+	case CS7:
+		dbg("config: 7bits/char\n");
+		ulcon = S3C2410_LCON_CS7;
+		break;
+	case CS8:
+	default:
+		dbg("config: 8bits/char\n");
+		ulcon = S3C2410_LCON_CS8;
+		break;
+	}
+
+	/* preserve original lcon IR settings */
+	ulcon |= (cfg->ulcon & S3C2410_LCON_IRM);
+
+	if (termios->c_cflag & CSTOPB)
+		ulcon |= S3C2410_LCON_STOPB;
+
+	umcon = (termios->c_cflag & CRTSCTS) ? S3C2410_UMCOM_AFC : 0;
+
+	if (termios->c_cflag & PARENB) {
+		if (termios->c_cflag & PARODD)
+			ulcon |= S3C2410_LCON_PODD;
+		else
+			ulcon |= S3C2410_LCON_PEVEN;
+	} else {
+		ulcon |= S3C2410_LCON_PNONE;
+	}
+
+	spin_lock_irqsave(&port->lock, flags);
+
+	dbg("setting ulcon to %08x, brddiv to %d, udivslot %08x\n",
+	    ulcon, quot, udivslot);
+
+	wr_regl(port, S3C2410_ULCON, ulcon);
+	wr_regl(port, S3C2410_UBRDIV, quot);
+	wr_regl(port, S3C2410_UMCON, umcon);
+
+	if (ourport->info->has_divslot)
+		wr_regl(port, S3C2443_DIVSLOT, udivslot);
+
+	dbg("uart: ulcon = 0x%08x, ucon = 0x%08x, ufcon = 0x%08x\n",
+	    rd_regl(port, S3C2410_ULCON),
+	    rd_regl(port, S3C2410_UCON),
+	    rd_regl(port, S3C2410_UFCON));
+
+	/*
+	 * Update the per-port timeout.
+	 */
+	uart_update_timeout(port, termios->c_cflag, baud);
+
+	/*
+	 * Which character status flags are we interested in?
+	 */
+	port->read_status_mask = S3C2410_UERSTAT_OVERRUN;
+	if (termios->c_iflag & INPCK)
+		port->read_status_mask |= S3C2410_UERSTAT_FRAME | S3C2410_UERSTAT_PARITY;
+
+	/*
+	 * Which character status flags should we ignore?
+	 */
+	port->ignore_status_mask = 0;
+	if (termios->c_iflag & IGNPAR)
+		port->ignore_status_mask |= S3C2410_UERSTAT_OVERRUN;
+	if (termios->c_iflag & IGNBRK && termios->c_iflag & IGNPAR)
+		port->ignore_status_mask |= S3C2410_UERSTAT_FRAME;
+
+	/*
+	 * Ignore all characters if CREAD is not set.
+	 */
+	if ((termios->c_cflag & CREAD) == 0)
+		port->ignore_status_mask |= RXSTAT_DUMMY_READ;
+
+	spin_unlock_irqrestore(&port->lock, flags);
+}
+
+static const char *s3c24xx_serial_type(struct uart_port *port)
+{
+	switch (port->type) {
+	case PORT_S3C2410:
+		return "S3C2410";
+	case PORT_S3C2440:
+		return "S3C2440";
+	case PORT_S3C2412:
+		return "S3C2412";
+	case PORT_S3C6400:
+		return "S3C6400/10";
+	default:
+		return NULL;
+	}
+}
+
+#define MAP_SIZE (0x100)
+
+static void s3c24xx_serial_release_port(struct uart_port *port)
+{
+	release_mem_region(port->mapbase, MAP_SIZE);
+}
+
+static int s3c24xx_serial_request_port(struct uart_port *port)
+{
+	const char *name = s3c24xx_serial_portname(port);
+	return request_mem_region(port->mapbase, MAP_SIZE, name) ? 0 : -EBUSY;
+}
+
+static void s3c24xx_serial_config_port(struct uart_port *port, int flags)
+{
+	struct s3c24xx_uart_info *info = s3c24xx_port_to_info(port);
+
+	if (flags & UART_CONFIG_TYPE &&
+	    s3c24xx_serial_request_port(port) == 0)
+		port->type = info->type;
+}
+
+/*
+ * verify the new serial_struct (for TIOCSSERIAL).
+ */
+static int
+s3c24xx_serial_verify_port(struct uart_port *port, struct serial_struct *ser)
+{
+	struct s3c24xx_uart_info *info = s3c24xx_port_to_info(port);
+
+	if (ser->type != PORT_UNKNOWN && ser->type != info->type)
+		return -EINVAL;
+
+	return 0;
+}
+
+
+#ifdef CONFIG_SERIAL_SAMSUNG_CONSOLE
+
+static struct console s3c24xx_serial_console;
+
+#define S3C24XX_SERIAL_CONSOLE &s3c24xx_serial_console
+#else
+#define S3C24XX_SERIAL_CONSOLE NULL
+#endif
+
+static struct uart_ops s3c24xx_serial_ops = {
+	.pm		= s3c24xx_serial_pm,
+	.tx_empty	= s3c24xx_serial_tx_empty,
+	.get_mctrl	= s3c24xx_serial_get_mctrl,
+	.set_mctrl	= s3c24xx_serial_set_mctrl,
+	.stop_tx	= s3c24xx_serial_stop_tx,
+	.start_tx	= s3c24xx_serial_start_tx,
+	.stop_rx	= s3c24xx_serial_stop_rx,
+	.enable_ms	= s3c24xx_serial_enable_ms,
+	.break_ctl	= s3c24xx_serial_break_ctl,
+	.startup	= s3c24xx_serial_startup,
+	.shutdown	= s3c24xx_serial_shutdown,
+	.set_termios	= s3c24xx_serial_set_termios,
+	.type		= s3c24xx_serial_type,
+	.release_port	= s3c24xx_serial_release_port,
+	.request_port	= s3c24xx_serial_request_port,
+	.config_port	= s3c24xx_serial_config_port,
+	.verify_port	= s3c24xx_serial_verify_port,
+};
+
+static struct uart_driver s3c24xx_uart_drv = {
+	.owner		= THIS_MODULE,
+	.driver_name	= "s3c2410_serial",
+	.nr		= CONFIG_SERIAL_SAMSUNG_UARTS,
+	.cons		= S3C24XX_SERIAL_CONSOLE,
+	.dev_name	= S3C24XX_SERIAL_NAME,
+	.major		= S3C24XX_SERIAL_MAJOR,
+	.minor		= S3C24XX_SERIAL_MINOR,
+};
+
+static struct s3c24xx_uart_port s3c24xx_serial_ports[CONFIG_SERIAL_SAMSUNG_UARTS] = {
+	[0] = {
+		.port = {
+			.lock		= __SPIN_LOCK_UNLOCKED(s3c24xx_serial_ports[0].port.lock),
+			.iotype		= UPIO_MEM,
+			.uartclk	= 0,
+			.fifosize	= 16,
+			.ops		= &s3c24xx_serial_ops,
+			.flags		= UPF_BOOT_AUTOCONF,
+			.line		= 0,
+		}
+	},
+	[1] = {
+		.port = {
+			.lock		= __SPIN_LOCK_UNLOCKED(s3c24xx_serial_ports[1].port.lock),
+			.iotype		= UPIO_MEM,
+			.uartclk	= 0,
+			.fifosize	= 16,
+			.ops		= &s3c24xx_serial_ops,
+			.flags		= UPF_BOOT_AUTOCONF,
+			.line		= 1,
+		}
+	},
+#if CONFIG_SERIAL_SAMSUNG_UARTS > 2
+
+	[2] = {
+		.port = {
+			.lock		= __SPIN_LOCK_UNLOCKED(s3c24xx_serial_ports[2].port.lock),
+			.iotype		= UPIO_MEM,
+			.uartclk	= 0,
+			.fifosize	= 16,
+			.ops		= &s3c24xx_serial_ops,
+			.flags		= UPF_BOOT_AUTOCONF,
+			.line		= 2,
+		}
+	},
+#endif
+#if CONFIG_SERIAL_SAMSUNG_UARTS > 3
+	[3] = {
+		.port = {
+			.lock		= __SPIN_LOCK_UNLOCKED(s3c24xx_serial_ports[3].port.lock),
+			.iotype		= UPIO_MEM,
+			.uartclk	= 0,
+			.fifosize	= 16,
+			.ops		= &s3c24xx_serial_ops,
+			.flags		= UPF_BOOT_AUTOCONF,
+			.line		= 3,
+		}
+	}
+#endif
+};
+
+/* s3c24xx_serial_resetport
+ *
+ * reset the fifos and other the settings.
+*/
+
+static void s3c24xx_serial_resetport(struct uart_port *port,
+				   struct s3c2410_uartcfg *cfg)
+{
+	struct s3c24xx_uart_info *info = s3c24xx_port_to_info(port);
+	unsigned long ucon = rd_regl(port, S3C2410_UCON);
+	unsigned int ucon_mask;
+
+	ucon_mask = info->clksel_mask;
+	if (info->type == PORT_S3C2440)
+		ucon_mask |= S3C2440_UCON0_DIVMASK;
+
+	ucon &= ucon_mask;
+	wr_regl(port, S3C2410_UCON,  ucon | cfg->ucon);
+	wr_regl(port, S3C2410_ULCON, cfg->ulcon);
+
+	/* reset both fifos */
+	wr_regl(port, S3C2410_UFCON, cfg->ufcon | S3C2410_UFCON_RESETBOTH);
+	wr_regl(port, S3C2410_UFCON, cfg->ufcon);
+
+	/* some delay is required after fifo reset */
+	udelay(1);
+}
+
+
+#ifdef CONFIG_CPU_FREQ
+
+static int s3c24xx_serial_cpufreq_transition(struct notifier_block *nb,
+					     unsigned long val, void *data)
+{
+	struct s3c24xx_uart_port *port;
+	struct uart_port *uport;
+
+	port = container_of(nb, struct s3c24xx_uart_port, freq_transition);
+	uport = &port->port;
+
+	/* check to see if port is enabled */
+
+	if (port->pm_level != 0)
+		return 0;
+
+	/* try and work out if the baudrate is changing, we can detect
+	 * a change in rate, but we do not have support for detecting
+	 * a disturbance in the clock-rate over the change.
+	 */
+
+	if (IS_ERR(port->clk))
+		goto exit;
+
+	if (port->baudclk_rate == clk_get_rate(port->clk))
+		goto exit;
+
+	if (val == CPUFREQ_PRECHANGE) {
+		/* we should really shut the port down whilst the
+		 * frequency change is in progress. */
+
+	} else if (val == CPUFREQ_POSTCHANGE) {
+		struct ktermios *termios;
+		struct tty_struct *tty;
+
+		if (uport->state == NULL)
+			goto exit;
+
+		tty = uport->state->port.tty;
+
+		if (tty == NULL)
+			goto exit;
+
+		termios = tty->termios;
+
+		if (termios == NULL) {
+			printk(KERN_WARNING "%s: no termios?\n", __func__);
+			goto exit;
+		}
+
+		s3c24xx_serial_set_termios(uport, termios, NULL);
+	}
+
+ exit:
+	return 0;
+}
+
+static inline int s3c24xx_serial_cpufreq_register(struct s3c24xx_uart_port *port)
+{
+	port->freq_transition.notifier_call = s3c24xx_serial_cpufreq_transition;
+
+	return cpufreq_register_notifier(&port->freq_transition,
+					 CPUFREQ_TRANSITION_NOTIFIER);
+}
+
+static inline void s3c24xx_serial_cpufreq_deregister(struct s3c24xx_uart_port *port)
+{
+	cpufreq_unregister_notifier(&port->freq_transition,
+				    CPUFREQ_TRANSITION_NOTIFIER);
+}
+
+#else
+static inline int s3c24xx_serial_cpufreq_register(struct s3c24xx_uart_port *port)
+{
+	return 0;
+}
+
+static inline void s3c24xx_serial_cpufreq_deregister(struct s3c24xx_uart_port *port)
+{
+}
+#endif
+
+/* s3c24xx_serial_init_port
+ *
+ * initialise a single serial port from the platform device given
+ */
+
+static int s3c24xx_serial_init_port(struct s3c24xx_uart_port *ourport,
+				    struct platform_device *platdev)
+{
+	struct uart_port *port = &ourport->port;
+	struct s3c2410_uartcfg *cfg = ourport->cfg;
+	struct resource *res;
+	int ret;
+
+	dbg("s3c24xx_serial_init_port: port=%p, platdev=%p\n", port, platdev);
+
+	if (platdev == NULL)
+		return -ENODEV;
+
+	if (port->mapbase != 0)
+		return 0;
+
+	/* setup info for port */
+	port->dev	= &platdev->dev;
+
+	/* Startup sequence is different for s3c64xx and higher SoC's */
+	if (s3c24xx_serial_has_interrupt_mask(port))
+		s3c24xx_serial_ops.startup = s3c64xx_serial_startup;
+
+	port->uartclk = 1;
+
+	if (cfg->uart_flags & UPF_CONS_FLOW) {
+		dbg("s3c24xx_serial_init_port: enabling flow control\n");
+		port->flags |= UPF_CONS_FLOW;
+	}
+
+	/* sort our the physical and virtual addresses for each UART */
+
+	res = platform_get_resource(platdev, IORESOURCE_MEM, 0);
+	if (res == NULL) {
+		printk(KERN_ERR "failed to find memory resource for uart\n");
+		return -EINVAL;
+	}
+
+	dbg("resource %p (%lx..%lx)\n", res, res->start, res->end);
+
+	port->mapbase = res->start;
+	port->membase = S3C_VA_UART + (res->start & 0xfffff);
+	ret = platform_get_irq(platdev, 0);
+	if (ret < 0)
+		port->irq = 0;
+	else {
+		port->irq = ret;
+		ourport->rx_irq = ret;
+		ourport->tx_irq = ret + 1;
+	}
+	
+	ret = platform_get_irq(platdev, 1);
+	if (ret > 0)
+		ourport->tx_irq = ret;
+
+	ourport->clk	= clk_get(&platdev->dev, "uart");
+
+	/* Keep all interrupts masked and cleared */
+	if (s3c24xx_serial_has_interrupt_mask(port)) {
+		wr_regl(port, S3C64XX_UINTM, 0xf);
+		wr_regl(port, S3C64XX_UINTP, 0xf);
+		wr_regl(port, S3C64XX_UINTSP, 0xf);
+	}
+
+	dbg("port: map=%08x, mem=%08x, irq=%d (%d,%d), clock=%ld\n",
+	    port->mapbase, port->membase, port->irq,
+	    ourport->rx_irq, ourport->tx_irq, port->uartclk);
+
+	/* reset the fifos (and setup the uart) */
+	s3c24xx_serial_resetport(port, cfg);
+	return 0;
+}
+
+static ssize_t s3c24xx_serial_show_clksrc(struct device *dev,
+					  struct device_attribute *attr,
+					  char *buf)
+{
+	struct uart_port *port = s3c24xx_dev_to_port(dev);
+	struct s3c24xx_uart_port *ourport = to_ourport(port);
+
+	return snprintf(buf, PAGE_SIZE, "* %s\n", ourport->baudclk->name);
+}
+
+static DEVICE_ATTR(clock_source, S_IRUGO, s3c24xx_serial_show_clksrc, NULL);
+
+
+/* Device driver serial port probe */
+
+static const struct of_device_id s3c24xx_uart_dt_match[];
+static int probe_index;
+
+static inline struct s3c24xx_serial_drv_data *s3c24xx_get_driver_data(
+			struct platform_device *pdev)
+{
+#ifdef CONFIG_OF
+	if (pdev->dev.of_node) {
+		const struct of_device_id *match;
+		match = of_match_node(s3c24xx_uart_dt_match, pdev->dev.of_node);
+		return (struct s3c24xx_serial_drv_data *)match->data;
+	}
+#endif
+	return (struct s3c24xx_serial_drv_data *)
+			platform_get_device_id(pdev)->driver_data;
+}
+
+static int s3c24xx_serial_probe(struct platform_device *pdev)
+{
+	struct s3c24xx_uart_port *ourport;
+	int ret;
+
+	dbg("s3c24xx_serial_probe(%p) %d\n", pdev, probe_index);
+
+	ourport = &s3c24xx_serial_ports[probe_index];
+
+	ourport->drv_data = s3c24xx_get_driver_data(pdev);
+	if (!ourport->drv_data) {
+		dev_err(&pdev->dev, "could not find driver data\n");
+		return -ENODEV;
+	}
+
+	ourport->info = ourport->drv_data->info;
+	ourport->cfg = (pdev->dev.platform_data) ?
+			(struct s3c2410_uartcfg *)pdev->dev.platform_data :
+			ourport->drv_data->def_cfg;
+
+	ourport->port.fifosize = (ourport->info->fifosize) ?
+		ourport->info->fifosize :
+		ourport->drv_data->fifosize[probe_index];
+
+	probe_index++;
+
+	dbg("%s: initialising port %p...\n", __func__, ourport);
+
+	ret = s3c24xx_serial_init_port(ourport, pdev);
+	if (ret < 0)
+		goto probe_err;
+
+	dbg("%s: adding port\n", __func__);
+	uart_add_one_port(&s3c24xx_uart_drv, &ourport->port);
+	platform_set_drvdata(pdev, &ourport->port);
+
+	ret = device_create_file(&pdev->dev, &dev_attr_clock_source);
+	if (ret < 0)
+		dev_err(&pdev->dev, "failed to add clock source attr.\n");
+
+	ret = s3c24xx_serial_cpufreq_register(ourport);
+	if (ret < 0)
+		dev_err(&pdev->dev, "failed to add cpufreq notifier\n");
+
+	return 0;
+
+ probe_err:
+	return ret;
+}
+
+static int __devexit s3c24xx_serial_remove(struct platform_device *dev)
+{
+	struct uart_port *port = s3c24xx_dev_to_port(&dev->dev);
+
+	if (port) {
+		s3c24xx_serial_cpufreq_deregister(to_ourport(port));
+		device_remove_file(&dev->dev, &dev_attr_clock_source);
+		uart_remove_one_port(&s3c24xx_uart_drv, port);
+	}
+
+	return 0;
+}
+
+/* UART power management code */
+#ifdef CONFIG_PM_SLEEP
+static int s3c24xx_serial_suspend(struct device *dev)
+{
+	struct uart_port *port = s3c24xx_dev_to_port(dev);
+
+	if (port)
+		uart_suspend_port(&s3c24xx_uart_drv, port);
+
+	return 0;
+}
+
+static int s3c24xx_serial_resume(struct device *dev)
+{
+	struct uart_port *port = s3c24xx_dev_to_port(dev);
+	struct s3c24xx_uart_port *ourport = to_ourport(port);
+
+	if (port) {
+		clk_enable(ourport->clk);
+		s3c24xx_serial_resetport(port, s3c24xx_port_to_cfg(port));
+		clk_disable(ourport->clk);
+
+		uart_resume_port(&s3c24xx_uart_drv, port);
+	}
+
+	return 0;
+}
+
+static const struct dev_pm_ops s3c24xx_serial_pm_ops = {
+	.suspend = s3c24xx_serial_suspend,
+	.resume = s3c24xx_serial_resume,
+};
+#define SERIAL_SAMSUNG_PM_OPS	(&s3c24xx_serial_pm_ops)
+
+#else /* !CONFIG_PM_SLEEP */
+
+#define SERIAL_SAMSUNG_PM_OPS	NULL
+#endif /* CONFIG_PM_SLEEP */
+
+/* Console code */
+
+#ifdef CONFIG_SERIAL_SAMSUNG_CONSOLE
+
+static struct uart_port *cons_uart;
+
+static int
+s3c24xx_serial_console_txrdy(struct uart_port *port, unsigned int ufcon)
+{
+	struct s3c24xx_uart_info *info = s3c24xx_port_to_info(port);
+	unsigned long ufstat, utrstat;
+
+	if (ufcon & S3C2410_UFCON_FIFOMODE) {
+		/* fifo mode - check amount of data in fifo registers... */
+
+		ufstat = rd_regl(port, S3C2410_UFSTAT);
+		return (ufstat & info->tx_fifofull) ? 0 : 1;
+	}
+
+	/* in non-fifo mode, we go and use the tx buffer empty */
+
+	utrstat = rd_regl(port, S3C2410_UTRSTAT);
+	return (utrstat & S3C2410_UTRSTAT_TXE) ? 1 : 0;
+}
+
+static void
+s3c24xx_serial_console_putchar(struct uart_port *port, int ch)
+{
+	unsigned int ufcon = rd_regl(cons_uart, S3C2410_UFCON);
+	while (!s3c24xx_serial_console_txrdy(port, ufcon))
+		barrier();
+	wr_regb(cons_uart, S3C2410_UTXH, ch);
+}
+
+static void
+s3c24xx_serial_console_write(struct console *co, const char *s,
+			     unsigned int count)
+{
+	uart_console_write(cons_uart, s, count, s3c24xx_serial_console_putchar);
+}
+
+static void __init
+s3c24xx_serial_get_options(struct uart_port *port, int *baud,
+			   int *parity, int *bits)
+{
+	struct clk *clk;
+	unsigned int ulcon;
+	unsigned int ucon;
+	unsigned int ubrdiv;
+	unsigned long rate;
+	unsigned int clk_sel;
+	char clk_name[MAX_CLK_NAME_LENGTH];
+
+	ulcon  = rd_regl(port, S3C2410_ULCON);
+	ucon   = rd_regl(port, S3C2410_UCON);
+	ubrdiv = rd_regl(port, S3C2410_UBRDIV);
+
+	dbg("s3c24xx_serial_get_options: port=%p\n"
+	    "registers: ulcon=%08x, ucon=%08x, ubdriv=%08x\n",
+	    port, ulcon, ucon, ubrdiv);
+
+	if ((ucon & 0xf) != 0) {
+		/* consider the serial port configured if the tx/rx mode set */
+
+		switch (ulcon & S3C2410_LCON_CSMASK) {
+		case S3C2410_LCON_CS5:
+			*bits = 5;
+			break;
+		case S3C2410_LCON_CS6:
+			*bits = 6;
+			break;
+		case S3C2410_LCON_CS7:
+			*bits = 7;
+			break;
+		default:
+		case S3C2410_LCON_CS8:
+			*bits = 8;
+			break;
+		}
+
+		switch (ulcon & S3C2410_LCON_PMASK) {
+		case S3C2410_LCON_PEVEN:
+			*parity = 'e';
+			break;
+
+		case S3C2410_LCON_PODD:
+			*parity = 'o';
+			break;
+
+		case S3C2410_LCON_PNONE:
+		default:
+			*parity = 'n';
+		}
+
+		/* now calculate the baud rate */
+
+		clk_sel = s3c24xx_serial_getsource(port);
+		sprintf(clk_name, "clk_uart_baud%d", clk_sel);
+
+		clk = clk_get(port->dev, clk_name);
+		if (!IS_ERR(clk) && clk != NULL)
+			rate = clk_get_rate(clk);
+		else
+			rate = 1;
+
+		*baud = rate / (16 * (ubrdiv + 1));
+		dbg("calculated baud %d\n", *baud);
+	}
+
+}
+
+static int __init
+s3c24xx_serial_console_setup(struct console *co, char *options)
+{
+	struct uart_port *port;
+	int baud = 9600;
+	int bits = 8;
+	int parity = 'n';
+	int flow = 'n';
+
+	dbg("s3c24xx_serial_console_setup: co=%p (%d), %s\n",
+	    co, co->index, options);
+
+	/* is this a valid port */
+
+	if (co->index == -1 || co->index >= CONFIG_SERIAL_SAMSUNG_UARTS)
+		co->index = 0;
+
+	port = &s3c24xx_serial_ports[co->index].port;
+
+	/* is the port configured? */
+
+	if (port->mapbase == 0x0)
+		return -ENODEV;
+
+	cons_uart = port;
+
+	dbg("s3c24xx_serial_console_setup: port=%p (%d)\n", port, co->index);
+
+	/*
+	 * Check whether an invalid uart number has been specified, and
+	 * if so, search for the first available port that does have
+	 * console support.
+	 */
+	if (options)
+		uart_parse_options(options, &baud, &parity, &bits, &flow);
+	else
+		s3c24xx_serial_get_options(port, &baud, &parity, &bits);
+
+	dbg("s3c24xx_serial_console_setup: baud %d\n", baud);
+
+	return uart_set_options(port, co, baud, parity, bits, flow);
+}
+
+static struct console s3c24xx_serial_console = {
+	.name		= S3C24XX_SERIAL_NAME,
+	.device		= uart_console_device,
+	.flags		= CON_PRINTBUFFER,
+	.index		= -1,
+	.write		= s3c24xx_serial_console_write,
+	.setup		= s3c24xx_serial_console_setup,
+	.data		= &s3c24xx_uart_drv,
+};
+#endif /* CONFIG_SERIAL_SAMSUNG_CONSOLE */
+
+#ifdef CONFIG_CPU_S3C2410
+static struct s3c24xx_serial_drv_data s3c2410_serial_drv_data = {
+	.info = &(struct s3c24xx_uart_info) {
+		.name		= "Samsung S3C2410 UART",
+		.type		= PORT_S3C2410,
+		.fifosize	= 16,
+		.rx_fifomask	= S3C2410_UFSTAT_RXMASK,
+		.rx_fifoshift	= S3C2410_UFSTAT_RXSHIFT,
+		.rx_fifofull	= S3C2410_UFSTAT_RXFULL,
+		.tx_fifofull	= S3C2410_UFSTAT_TXFULL,
+		.tx_fifomask	= S3C2410_UFSTAT_TXMASK,
+		.tx_fifoshift	= S3C2410_UFSTAT_TXSHIFT,
+		.def_clk_sel	= S3C2410_UCON_CLKSEL0,
+		.num_clks	= 2,
+		.clksel_mask	= S3C2410_UCON_CLKMASK,
+		.clksel_shift	= S3C2410_UCON_CLKSHIFT,
+	},
+	.def_cfg = &(struct s3c2410_uartcfg) {
+		.ucon		= S3C2410_UCON_DEFAULT,
+		.ufcon		= S3C2410_UFCON_DEFAULT,
+	},
+};
+#define S3C2410_SERIAL_DRV_DATA ((kernel_ulong_t)&s3c2410_serial_drv_data)
+#else
+#define S3C2410_SERIAL_DRV_DATA (kernel_ulong_t)NULL
+#endif
+
+#ifdef CONFIG_CPU_S3C2412
+static struct s3c24xx_serial_drv_data s3c2412_serial_drv_data = {
+	.info = &(struct s3c24xx_uart_info) {
+		.name		= "Samsung S3C2412 UART",
+		.type		= PORT_S3C2412,
+		.fifosize	= 64,
+		.has_divslot	= 1,
+		.rx_fifomask	= S3C2440_UFSTAT_RXMASK,
+		.rx_fifoshift	= S3C2440_UFSTAT_RXSHIFT,
+		.rx_fifofull	= S3C2440_UFSTAT_RXFULL,
+		.tx_fifofull	= S3C2440_UFSTAT_TXFULL,
+		.tx_fifomask	= S3C2440_UFSTAT_TXMASK,
+		.tx_fifoshift	= S3C2440_UFSTAT_TXSHIFT,
+		.def_clk_sel	= S3C2410_UCON_CLKSEL2,
+		.num_clks	= 4,
+		.clksel_mask	= S3C2412_UCON_CLKMASK,
+		.clksel_shift	= S3C2412_UCON_CLKSHIFT,
+	},
+	.def_cfg = &(struct s3c2410_uartcfg) {
+		.ucon		= S3C2410_UCON_DEFAULT,
+		.ufcon		= S3C2410_UFCON_DEFAULT,
+	},
+};
+#define S3C2412_SERIAL_DRV_DATA ((kernel_ulong_t)&s3c2412_serial_drv_data)
+#else
+#define S3C2412_SERIAL_DRV_DATA (kernel_ulong_t)NULL
+#endif
+
+#if defined(CONFIG_CPU_S3C2440) || defined(CONFIG_CPU_S3C2416) || \
+	defined(CONFIG_CPU_S3C2443) || defined(CONFIG_CPU_S3C2442)
+static struct s3c24xx_serial_drv_data s3c2440_serial_drv_data = {
+	.info = &(struct s3c24xx_uart_info) {
+		.name		= "Samsung S3C2440 UART",
+		.type		= PORT_S3C2440,
+		.fifosize	= 64,
+		.has_divslot	= 1,
+		.rx_fifomask	= S3C2440_UFSTAT_RXMASK,
+		.rx_fifoshift	= S3C2440_UFSTAT_RXSHIFT,
+		.rx_fifofull	= S3C2440_UFSTAT_RXFULL,
+		.tx_fifofull	= S3C2440_UFSTAT_TXFULL,
+		.tx_fifomask	= S3C2440_UFSTAT_TXMASK,
+		.tx_fifoshift	= S3C2440_UFSTAT_TXSHIFT,
+		.def_clk_sel	= S3C2410_UCON_CLKSEL2,
+		.num_clks	= 4,
+		.clksel_mask	= S3C2412_UCON_CLKMASK,
+		.clksel_shift	= S3C2412_UCON_CLKSHIFT,
+	},
+	.def_cfg = &(struct s3c2410_uartcfg) {
+		.ucon		= S3C2410_UCON_DEFAULT,
+		.ufcon		= S3C2410_UFCON_DEFAULT,
+	},
+};
+#define S3C2440_SERIAL_DRV_DATA ((kernel_ulong_t)&s3c2440_serial_drv_data)
+#else
+#define S3C2440_SERIAL_DRV_DATA (kernel_ulong_t)NULL
+#endif
+
+#if defined(CONFIG_CPU_S3C6400) || defined(CONFIG_CPU_S3C6410) || \
+	defined(CONFIG_CPU_S5P6440) || defined(CONFIG_CPU_S5P6450) || \
+	defined(CONFIG_CPU_S5PC100)
+static struct s3c24xx_serial_drv_data s3c6400_serial_drv_data = {
+	.info = &(struct s3c24xx_uart_info) {
+		.name		= "Samsung S3C6400 UART",
+		.type		= PORT_S3C6400,
+		.fifosize	= 64,
+		.has_divslot	= 1,
+		.rx_fifomask	= S3C2440_UFSTAT_RXMASK,
+		.rx_fifoshift	= S3C2440_UFSTAT_RXSHIFT,
+		.rx_fifofull	= S3C2440_UFSTAT_RXFULL,
+		.tx_fifofull	= S3C2440_UFSTAT_TXFULL,
+		.tx_fifomask	= S3C2440_UFSTAT_TXMASK,
+		.tx_fifoshift	= S3C2440_UFSTAT_TXSHIFT,
+		.def_clk_sel	= S3C2410_UCON_CLKSEL2,
+		.num_clks	= 4,
+		.clksel_mask	= S3C6400_UCON_CLKMASK,
+		.clksel_shift	= S3C6400_UCON_CLKSHIFT,
+	},
+	.def_cfg = &(struct s3c2410_uartcfg) {
+		.ucon		= S3C2410_UCON_DEFAULT,
+		.ufcon		= S3C2410_UFCON_DEFAULT,
+	},
+};
+#define S3C6400_SERIAL_DRV_DATA ((kernel_ulong_t)&s3c6400_serial_drv_data)
+#else
+#define S3C6400_SERIAL_DRV_DATA (kernel_ulong_t)NULL
+#endif
+
+#ifdef CONFIG_CPU_S5PV210
+static struct s3c24xx_serial_drv_data s5pv210_serial_drv_data = {
+	.info = &(struct s3c24xx_uart_info) {
+		.name		= "Samsung S5PV210 UART",
+		.type		= PORT_S3C6400,
+		.has_divslot	= 1,
+		.rx_fifomask	= S5PV210_UFSTAT_RXMASK,
+		.rx_fifoshift	= S5PV210_UFSTAT_RXSHIFT,
+		.rx_fifofull	= S5PV210_UFSTAT_RXFULL,
+		.tx_fifofull	= S5PV210_UFSTAT_TXFULL,
+		.tx_fifomask	= S5PV210_UFSTAT_TXMASK,
+		.tx_fifoshift	= S5PV210_UFSTAT_TXSHIFT,
+		.def_clk_sel	= S3C2410_UCON_CLKSEL0,
+		.num_clks	= 2,
+		.clksel_mask	= S5PV210_UCON_CLKMASK,
+		.clksel_shift	= S5PV210_UCON_CLKSHIFT,
+	},
+	.def_cfg = &(struct s3c2410_uartcfg) {
+		.ucon		= S5PV210_UCON_DEFAULT,
+		.ufcon		= S5PV210_UFCON_DEFAULT,
+	},
+	.fifosize = { 256, 64, 16, 16 },
+};
+#define S5PV210_SERIAL_DRV_DATA ((kernel_ulong_t)&s5pv210_serial_drv_data)
+#else
+#define S5PV210_SERIAL_DRV_DATA	(kernel_ulong_t)NULL
+#endif
+
+#if defined(CONFIG_CPU_EXYNOS4210) || defined(CONFIG_SOC_EXYNOS4212) || \
+	defined(CONFIG_SOC_EXYNOS4412) || defined(CONFIG_SOC_EXYNOS5250)
+static struct s3c24xx_serial_drv_data exynos4210_serial_drv_data = {
+	.info = &(struct s3c24xx_uart_info) {
+		.name		= "Samsung Exynos4 UART",
+		.type		= PORT_S3C6400,
+		.has_divslot	= 1,
+		.rx_fifomask	= S5PV210_UFSTAT_RXMASK,
+		.rx_fifoshift	= S5PV210_UFSTAT_RXSHIFT,
+		.rx_fifofull	= S5PV210_UFSTAT_RXFULL,
+		.tx_fifofull	= S5PV210_UFSTAT_TXFULL,
+		.tx_fifomask	= S5PV210_UFSTAT_TXMASK,
+		.tx_fifoshift	= S5PV210_UFSTAT_TXSHIFT,
+		.def_clk_sel	= S3C2410_UCON_CLKSEL0,
+		.num_clks	= 1,
+		.clksel_mask	= 0,
+		.clksel_shift	= 0,
+	},
+	.def_cfg = &(struct s3c2410_uartcfg) {
+		.ucon		= S5PV210_UCON_DEFAULT,
+		.ufcon		= S5PV210_UFCON_DEFAULT,
+		.has_fracval	= 1,
+	},
+	.fifosize = { 256, 64, 16, 16 },
+};
+#define EXYNOS4210_SERIAL_DRV_DATA ((kernel_ulong_t)&exynos4210_serial_drv_data)
+#else
+#define EXYNOS4210_SERIAL_DRV_DATA (kernel_ulong_t)NULL
+#endif
+
+static struct platform_device_id s3c24xx_serial_driver_ids[] = {
+	{
+		.name		= "s3c2410-uart",
+		.driver_data	= S3C2410_SERIAL_DRV_DATA,
+	}, {
+		.name		= "s3c2412-uart",
+		.driver_data	= S3C2412_SERIAL_DRV_DATA,
+	}, {
+		.name		= "s3c2440-uart",
+		.driver_data	= S3C2440_SERIAL_DRV_DATA,
+	}, {
+		.name		= "s3c6400-uart",
+		.driver_data	= S3C6400_SERIAL_DRV_DATA,
+	}, {
+		.name		= "s5pv210-uart",
+		.driver_data	= S5PV210_SERIAL_DRV_DATA,
+	}, {
+		.name		= "exynos4210-uart",
+		.driver_data	= EXYNOS4210_SERIAL_DRV_DATA,
+	},
+	{ },
+};
+MODULE_DEVICE_TABLE(platform, s3c24xx_serial_driver_ids);
+
+#ifdef CONFIG_OF
+static const struct of_device_id s3c24xx_uart_dt_match[] = {
+	{ .compatible = "samsung,exynos4210-uart",
+		.data = (void *)EXYNOS4210_SERIAL_DRV_DATA },
+	{},
+};
+MODULE_DEVICE_TABLE(of, s3c24xx_uart_dt_match);
+#else
+#define s3c24xx_uart_dt_match NULL
+#endif
+
+static struct platform_driver samsung_serial_driver = {
+	.probe		= s3c24xx_serial_probe,
+	.remove		= __devexit_p(s3c24xx_serial_remove),
+	.id_table	= s3c24xx_serial_driver_ids,
+	.driver		= {
+		.name	= "samsung-uart",
+		.owner	= THIS_MODULE,
+		.pm	= SERIAL_SAMSUNG_PM_OPS,
+		.of_match_table	= s3c24xx_uart_dt_match,
+	},
+};
+
+/* module initialisation code */
+
+static int __init s3c24xx_serial_modinit(void)
+{
+	int ret;
+
+	ret = uart_register_driver(&s3c24xx_uart_drv);
+	if (ret < 0) {
+		printk(KERN_ERR "failed to register UART driver\n");
+		return -1;
+	}
+
+	return platform_driver_register(&samsung_serial_driver);
+}
+
+static void __exit s3c24xx_serial_modexit(void)
+{
+	uart_unregister_driver(&s3c24xx_uart_drv);
+}
+
+module_init(s3c24xx_serial_modinit);
+module_exit(s3c24xx_serial_modexit);
+
+MODULE_ALIAS("platform:samsung-uart");
+MODULE_DESCRIPTION("Samsung SoC Serial port driver");
+MODULE_AUTHOR("Ben Dooks <ben@simtec.co.uk>");
+MODULE_LICENSE("GPL v2");
diff --git a/ap/os/linux/linux-3.4.x/drivers/tty/serial/samsung.h b/ap/os/linux/linux-3.4.x/drivers/tty/serial/samsung.h
new file mode 100644
index 0000000..1a4bca3
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/tty/serial/samsung.h
@@ -0,0 +1,97 @@
+/*
+ * Driver for Samsung SoC onboard UARTs.
+ *
+ * Ben Dooks, Copyright (c) 2003-2008 Simtec Electronics
+ *	http://armlinux.simtec.co.uk/
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+*/
+
+struct s3c24xx_uart_info {
+	char			*name;
+	unsigned int		type;
+	unsigned int		fifosize;
+	unsigned long		rx_fifomask;
+	unsigned long		rx_fifoshift;
+	unsigned long		rx_fifofull;
+	unsigned long		tx_fifomask;
+	unsigned long		tx_fifoshift;
+	unsigned long		tx_fifofull;
+	unsigned int		def_clk_sel;
+	unsigned long		num_clks;
+	unsigned long		clksel_mask;
+	unsigned long		clksel_shift;
+
+	/* uart port features */
+
+	unsigned int		has_divslot:1;
+
+	/* uart controls */
+	int (*reset_port)(struct uart_port *, struct s3c2410_uartcfg *);
+};
+
+struct s3c24xx_serial_drv_data {
+	struct s3c24xx_uart_info	*info;
+	struct s3c2410_uartcfg		*def_cfg;
+	unsigned int			fifosize[CONFIG_SERIAL_SAMSUNG_UARTS];
+};
+
+struct s3c24xx_uart_port {
+	unsigned char			rx_claimed;
+	unsigned char			tx_claimed;
+	unsigned int			pm_level;
+	unsigned long			baudclk_rate;
+
+	unsigned int			rx_irq;
+	unsigned int			tx_irq;
+
+	struct s3c24xx_uart_info	*info;
+	struct clk			*clk;
+	struct clk			*baudclk;
+	struct uart_port		port;
+	struct s3c24xx_serial_drv_data	*drv_data;
+
+	/* reference to platform data */
+	struct s3c2410_uartcfg		*cfg;
+
+#ifdef CONFIG_CPU_FREQ
+	struct notifier_block		freq_transition;
+#endif
+};
+
+/* conversion functions */
+
+#define s3c24xx_dev_to_port(__dev) (struct uart_port *)dev_get_drvdata(__dev)
+
+/* register access controls */
+
+#define portaddr(port, reg) ((port)->membase + (reg))
+#define portaddrl(port, reg) ((unsigned long *)((port)->membase + (reg)))
+
+#define rd_regb(port, reg) (__raw_readb(portaddr(port, reg)))
+#define rd_regl(port, reg) (__raw_readl(portaddr(port, reg)))
+
+#define wr_regb(port, reg, val) __raw_writeb(val, portaddr(port, reg))
+#define wr_regl(port, reg, val) __raw_writel(val, portaddr(port, reg))
+
+#ifdef CONFIG_SERIAL_SAMSUNG_DEBUG
+
+extern void printascii(const char *);
+
+static void dbg(const char *fmt, ...)
+{
+	va_list va;
+	char buff[256];
+
+	va_start(va, fmt);
+	vsprintf(buff, fmt, va);
+	va_end(va);
+
+	printascii(buff);
+}
+
+#else
+#define dbg(x...) do { } while (0)
+#endif
diff --git a/ap/os/linux/linux-3.4.x/drivers/tty/serial/sb1250-duart.c b/ap/os/linux/linux-3.4.x/drivers/tty/serial/sb1250-duart.c
new file mode 100644
index 0000000..0be8a2f
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/tty/serial/sb1250-duart.c
@@ -0,0 +1,975 @@
+/*
+ *	Support for the asynchronous serial interface (DUART) included
+ *	in the BCM1250 and derived System-On-a-Chip (SOC) devices.
+ *
+ *	Copyright (c) 2007  Maciej W. Rozycki
+ *
+ *	Derived from drivers/char/sb1250_duart.c for which the following
+ *	copyright applies:
+ *
+ *	Copyright (c) 2000, 2001, 2002, 2003, 2004  Broadcom Corporation
+ *
+ *	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.
+ *
+ *	References:
+ *
+ *	"BCM1250/BCM1125/BCM1125H User Manual", Broadcom Corporation
+ */
+
+#if defined(CONFIG_SERIAL_SB1250_DUART_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ)
+#define SUPPORT_SYSRQ
+#endif
+
+#include <linux/compiler.h>
+#include <linux/console.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/ioport.h>
+#include <linux/kernel.h>
+#include <linux/major.h>
+#include <linux/serial.h>
+#include <linux/serial_core.h>
+#include <linux/spinlock.h>
+#include <linux/sysrq.h>
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
+#include <linux/types.h>
+
+#include <linux/atomic.h>
+#include <asm/io.h>
+#include <asm/war.h>
+
+#include <asm/sibyte/sb1250.h>
+#include <asm/sibyte/sb1250_uart.h>
+#include <asm/sibyte/swarm.h>
+
+
+#if defined(CONFIG_SIBYTE_BCM1x55) || defined(CONFIG_SIBYTE_BCM1x80)
+#include <asm/sibyte/bcm1480_regs.h>
+#include <asm/sibyte/bcm1480_int.h>
+
+#define SBD_CHANREGS(line)	A_BCM1480_DUART_CHANREG((line), 0)
+#define SBD_CTRLREGS(line)	A_BCM1480_DUART_CTRLREG((line), 0)
+#define SBD_INT(line)		(K_BCM1480_INT_UART_0 + (line))
+
+#define DUART_CHANREG_SPACING	BCM1480_DUART_CHANREG_SPACING
+
+#define R_DUART_IMRREG(line)	R_BCM1480_DUART_IMRREG(line)
+#define R_DUART_INCHREG(line)	R_BCM1480_DUART_INCHREG(line)
+#define R_DUART_ISRREG(line)	R_BCM1480_DUART_ISRREG(line)
+
+#elif defined(CONFIG_SIBYTE_SB1250) || defined(CONFIG_SIBYTE_BCM112X)
+#include <asm/sibyte/sb1250_regs.h>
+#include <asm/sibyte/sb1250_int.h>
+
+#define SBD_CHANREGS(line)	A_DUART_CHANREG((line), 0)
+#define SBD_CTRLREGS(line)	A_DUART_CTRLREG(0)
+#define SBD_INT(line)		(K_INT_UART_0 + (line))
+
+#else
+#error invalid SB1250 UART configuration
+
+#endif
+
+
+MODULE_AUTHOR("Maciej W. Rozycki <macro@linux-mips.org>");
+MODULE_DESCRIPTION("BCM1xxx on-chip DUART serial driver");
+MODULE_LICENSE("GPL");
+
+
+#define DUART_MAX_CHIP 2
+#define DUART_MAX_SIDE 2
+
+/*
+ * Per-port state.
+ */
+struct sbd_port {
+	struct sbd_duart	*duart;
+	struct uart_port	port;
+	unsigned char __iomem	*memctrl;
+	int			tx_stopped;
+	int			initialised;
+};
+
+/*
+ * Per-DUART state for the shared register space.
+ */
+struct sbd_duart {
+	struct sbd_port		sport[2];
+	unsigned long		mapctrl;
+	atomic_t		map_guard;
+};
+
+#define to_sport(uport) container_of(uport, struct sbd_port, port)
+
+static struct sbd_duart sbd_duarts[DUART_MAX_CHIP];
+
+
+/*
+ * Reading and writing SB1250 DUART registers.
+ *
+ * There are three register spaces: two per-channel ones and
+ * a shared one.  We have to define accessors appropriately.
+ * All registers are 64-bit and all but the Baud Rate Clock
+ * registers only define 8 least significant bits.  There is
+ * also a workaround to take into account.  Raw accessors use
+ * the full register width, but cooked ones truncate it
+ * intentionally so that the rest of the driver does not care.
+ */
+static u64 __read_sbdchn(struct sbd_port *sport, int reg)
+{
+	void __iomem *csr = sport->port.membase + reg;
+
+	return __raw_readq(csr);
+}
+
+static u64 __read_sbdshr(struct sbd_port *sport, int reg)
+{
+	void __iomem *csr = sport->memctrl + reg;
+
+	return __raw_readq(csr);
+}
+
+static void __write_sbdchn(struct sbd_port *sport, int reg, u64 value)
+{
+	void __iomem *csr = sport->port.membase + reg;
+
+	__raw_writeq(value, csr);
+}
+
+static void __write_sbdshr(struct sbd_port *sport, int reg, u64 value)
+{
+	void __iomem *csr = sport->memctrl + reg;
+
+	__raw_writeq(value, csr);
+}
+
+/*
+ * In bug 1956, we get glitches that can mess up uart registers.  This
+ * "read-mode-reg after any register access" is an accepted workaround.
+ */
+static void __war_sbd1956(struct sbd_port *sport)
+{
+	__read_sbdchn(sport, R_DUART_MODE_REG_1);
+	__read_sbdchn(sport, R_DUART_MODE_REG_2);
+}
+
+static unsigned char read_sbdchn(struct sbd_port *sport, int reg)
+{
+	unsigned char retval;
+
+	retval = __read_sbdchn(sport, reg);
+	if (SIBYTE_1956_WAR)
+		__war_sbd1956(sport);
+	return retval;
+}
+
+static unsigned char read_sbdshr(struct sbd_port *sport, int reg)
+{
+	unsigned char retval;
+
+	retval = __read_sbdshr(sport, reg);
+	if (SIBYTE_1956_WAR)
+		__war_sbd1956(sport);
+	return retval;
+}
+
+static void write_sbdchn(struct sbd_port *sport, int reg, unsigned int value)
+{
+	__write_sbdchn(sport, reg, value);
+	if (SIBYTE_1956_WAR)
+		__war_sbd1956(sport);
+}
+
+static void write_sbdshr(struct sbd_port *sport, int reg, unsigned int value)
+{
+	__write_sbdshr(sport, reg, value);
+	if (SIBYTE_1956_WAR)
+		__war_sbd1956(sport);
+}
+
+
+static int sbd_receive_ready(struct sbd_port *sport)
+{
+	return read_sbdchn(sport, R_DUART_STATUS) & M_DUART_RX_RDY;
+}
+
+static int sbd_receive_drain(struct sbd_port *sport)
+{
+	int loops = 10000;
+
+	while (sbd_receive_ready(sport) && --loops)
+		read_sbdchn(sport, R_DUART_RX_HOLD);
+	return loops;
+}
+
+static int __maybe_unused sbd_transmit_ready(struct sbd_port *sport)
+{
+	return read_sbdchn(sport, R_DUART_STATUS) & M_DUART_TX_RDY;
+}
+
+static int __maybe_unused sbd_transmit_drain(struct sbd_port *sport)
+{
+	int loops = 10000;
+
+	while (!sbd_transmit_ready(sport) && --loops)
+		udelay(2);
+	return loops;
+}
+
+static int sbd_transmit_empty(struct sbd_port *sport)
+{
+	return read_sbdchn(sport, R_DUART_STATUS) & M_DUART_TX_EMT;
+}
+
+static int sbd_line_drain(struct sbd_port *sport)
+{
+	int loops = 10000;
+
+	while (!sbd_transmit_empty(sport) && --loops)
+		udelay(2);
+	return loops;
+}
+
+
+static unsigned int sbd_tx_empty(struct uart_port *uport)
+{
+	struct sbd_port *sport = to_sport(uport);
+
+	return sbd_transmit_empty(sport) ? TIOCSER_TEMT : 0;
+}
+
+static unsigned int sbd_get_mctrl(struct uart_port *uport)
+{
+	struct sbd_port *sport = to_sport(uport);
+	unsigned int mctrl, status;
+
+	status = read_sbdshr(sport, R_DUART_IN_PORT);
+	status >>= (uport->line) % 2;
+	mctrl = (!(status & M_DUART_IN_PIN0_VAL) ? TIOCM_CTS : 0) |
+		(!(status & M_DUART_IN_PIN4_VAL) ? TIOCM_CAR : 0) |
+		(!(status & M_DUART_RIN0_PIN) ? TIOCM_RNG : 0) |
+		(!(status & M_DUART_IN_PIN2_VAL) ? TIOCM_DSR : 0);
+	return mctrl;
+}
+
+static void sbd_set_mctrl(struct uart_port *uport, unsigned int mctrl)
+{
+	struct sbd_port *sport = to_sport(uport);
+	unsigned int clr = 0, set = 0, mode2;
+
+	if (mctrl & TIOCM_DTR)
+		set |= M_DUART_SET_OPR2;
+	else
+		clr |= M_DUART_CLR_OPR2;
+	if (mctrl & TIOCM_RTS)
+		set |= M_DUART_SET_OPR0;
+	else
+		clr |= M_DUART_CLR_OPR0;
+	clr <<= (uport->line) % 2;
+	set <<= (uport->line) % 2;
+
+	mode2 = read_sbdchn(sport, R_DUART_MODE_REG_2);
+	mode2 &= ~M_DUART_CHAN_MODE;
+	if (mctrl & TIOCM_LOOP)
+		mode2 |= V_DUART_CHAN_MODE_LCL_LOOP;
+	else
+		mode2 |= V_DUART_CHAN_MODE_NORMAL;
+
+	write_sbdshr(sport, R_DUART_CLEAR_OPR, clr);
+	write_sbdshr(sport, R_DUART_SET_OPR, set);
+	write_sbdchn(sport, R_DUART_MODE_REG_2, mode2);
+}
+
+static void sbd_stop_tx(struct uart_port *uport)
+{
+	struct sbd_port *sport = to_sport(uport);
+
+	write_sbdchn(sport, R_DUART_CMD, M_DUART_TX_DIS);
+	sport->tx_stopped = 1;
+};
+
+static void sbd_start_tx(struct uart_port *uport)
+{
+	struct sbd_port *sport = to_sport(uport);
+	unsigned int mask;
+
+	/* Enable tx interrupts.  */
+	mask = read_sbdshr(sport, R_DUART_IMRREG((uport->line) % 2));
+	mask |= M_DUART_IMR_TX;
+	write_sbdshr(sport, R_DUART_IMRREG((uport->line) % 2), mask);
+
+	/* Go!, go!, go!...  */
+	write_sbdchn(sport, R_DUART_CMD, M_DUART_TX_EN);
+	sport->tx_stopped = 0;
+};
+
+static void sbd_stop_rx(struct uart_port *uport)
+{
+	struct sbd_port *sport = to_sport(uport);
+
+	write_sbdshr(sport, R_DUART_IMRREG((uport->line) % 2), 0);
+};
+
+static void sbd_enable_ms(struct uart_port *uport)
+{
+	struct sbd_port *sport = to_sport(uport);
+
+	write_sbdchn(sport, R_DUART_AUXCTL_X,
+		     M_DUART_CIN_CHNG_ENA | M_DUART_CTS_CHNG_ENA);
+}
+
+static void sbd_break_ctl(struct uart_port *uport, int break_state)
+{
+	struct sbd_port *sport = to_sport(uport);
+
+	if (break_state == -1)
+		write_sbdchn(sport, R_DUART_CMD, V_DUART_MISC_CMD_START_BREAK);
+	else
+		write_sbdchn(sport, R_DUART_CMD, V_DUART_MISC_CMD_STOP_BREAK);
+}
+
+
+static void sbd_receive_chars(struct sbd_port *sport)
+{
+	struct uart_port *uport = &sport->port;
+	struct uart_icount *icount;
+	unsigned int status, ch, flag;
+	int count;
+
+	for (count = 16; count; count--) {
+		status = read_sbdchn(sport, R_DUART_STATUS);
+		if (!(status & M_DUART_RX_RDY))
+			break;
+
+		ch = read_sbdchn(sport, R_DUART_RX_HOLD);
+
+		flag = TTY_NORMAL;
+
+		icount = &uport->icount;
+		icount->rx++;
+
+		if (unlikely(status &
+			     (M_DUART_RCVD_BRK | M_DUART_FRM_ERR |
+			      M_DUART_PARITY_ERR | M_DUART_OVRUN_ERR))) {
+			if (status & M_DUART_RCVD_BRK) {
+				icount->brk++;
+				if (uart_handle_break(uport))
+					continue;
+			} else if (status & M_DUART_FRM_ERR)
+				icount->frame++;
+			else if (status & M_DUART_PARITY_ERR)
+				icount->parity++;
+			if (status & M_DUART_OVRUN_ERR)
+				icount->overrun++;
+
+			status &= uport->read_status_mask;
+			if (status & M_DUART_RCVD_BRK)
+				flag = TTY_BREAK;
+			else if (status & M_DUART_FRM_ERR)
+				flag = TTY_FRAME;
+			else if (status & M_DUART_PARITY_ERR)
+				flag = TTY_PARITY;
+		}
+
+		if (uart_handle_sysrq_char(uport, ch))
+			continue;
+
+		uart_insert_char(uport, status, M_DUART_OVRUN_ERR, ch, flag);
+	}
+
+	tty_flip_buffer_push(uport->state->port.tty);
+}
+
+static void sbd_transmit_chars(struct sbd_port *sport)
+{
+	struct uart_port *uport = &sport->port;
+	struct circ_buf *xmit = &sport->port.state->xmit;
+	unsigned int mask;
+	int stop_tx;
+
+	/* XON/XOFF chars.  */
+	if (sport->port.x_char) {
+		write_sbdchn(sport, R_DUART_TX_HOLD, sport->port.x_char);
+		sport->port.icount.tx++;
+		sport->port.x_char = 0;
+		return;
+	}
+
+	/* If nothing to do or stopped or hardware stopped.  */
+	stop_tx = (uart_circ_empty(xmit) || uart_tx_stopped(&sport->port));
+
+	/* Send char.  */
+	if (!stop_tx) {
+		write_sbdchn(sport, R_DUART_TX_HOLD, 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);
+	}
+
+	/* Are we are done?  */
+	if (stop_tx || uart_circ_empty(xmit)) {
+		/* Disable tx interrupts.  */
+		mask = read_sbdshr(sport, R_DUART_IMRREG((uport->line) % 2));
+		mask &= ~M_DUART_IMR_TX;
+		write_sbdshr(sport, R_DUART_IMRREG((uport->line) % 2), mask);
+	}
+}
+
+static void sbd_status_handle(struct sbd_port *sport)
+{
+	struct uart_port *uport = &sport->port;
+	unsigned int delta;
+
+	delta = read_sbdshr(sport, R_DUART_INCHREG((uport->line) % 2));
+	delta >>= (uport->line) % 2;
+
+	if (delta & (M_DUART_IN_PIN0_VAL << S_DUART_IN_PIN_CHNG))
+		uart_handle_cts_change(uport, !(delta & M_DUART_IN_PIN0_VAL));
+
+	if (delta & (M_DUART_IN_PIN2_VAL << S_DUART_IN_PIN_CHNG))
+		uport->icount.dsr++;
+
+	if (delta & ((M_DUART_IN_PIN2_VAL | M_DUART_IN_PIN0_VAL) <<
+		     S_DUART_IN_PIN_CHNG))
+		wake_up_interruptible(&uport->state->port.delta_msr_wait);
+}
+
+static irqreturn_t sbd_interrupt(int irq, void *dev_id)
+{
+	struct sbd_port *sport = dev_id;
+	struct uart_port *uport = &sport->port;
+	irqreturn_t status = IRQ_NONE;
+	unsigned int intstat;
+	int count;
+
+	for (count = 16; count; count--) {
+		intstat = read_sbdshr(sport,
+				      R_DUART_ISRREG((uport->line) % 2));
+		intstat &= read_sbdshr(sport,
+				       R_DUART_IMRREG((uport->line) % 2));
+		intstat &= M_DUART_ISR_ALL;
+		if (!intstat)
+			break;
+
+		if (intstat & M_DUART_ISR_RX)
+			sbd_receive_chars(sport);
+		if (intstat & M_DUART_ISR_IN)
+			sbd_status_handle(sport);
+		if (intstat & M_DUART_ISR_TX)
+			sbd_transmit_chars(sport);
+
+		status = IRQ_HANDLED;
+	}
+
+	return status;
+}
+
+
+static int sbd_startup(struct uart_port *uport)
+{
+	struct sbd_port *sport = to_sport(uport);
+	unsigned int mode1;
+	int ret;
+
+	ret = request_irq(sport->port.irq, sbd_interrupt,
+			  IRQF_SHARED, "sb1250-duart", sport);
+	if (ret)
+		return ret;
+
+	/* Clear the receive FIFO.  */
+	sbd_receive_drain(sport);
+
+	/* Clear the interrupt registers.  */
+	write_sbdchn(sport, R_DUART_CMD, V_DUART_MISC_CMD_RESET_BREAK_INT);
+	read_sbdshr(sport, R_DUART_INCHREG((uport->line) % 2));
+
+	/* Set rx/tx interrupt to FIFO available.  */
+	mode1 = read_sbdchn(sport, R_DUART_MODE_REG_1);
+	mode1 &= ~(M_DUART_RX_IRQ_SEL_RXFULL | M_DUART_TX_IRQ_SEL_TXEMPT);
+	write_sbdchn(sport, R_DUART_MODE_REG_1, mode1);
+
+	/* Disable tx, enable rx.  */
+	write_sbdchn(sport, R_DUART_CMD, M_DUART_TX_DIS | M_DUART_RX_EN);
+	sport->tx_stopped = 1;
+
+	/* Enable interrupts.  */
+	write_sbdshr(sport, R_DUART_IMRREG((uport->line) % 2),
+		     M_DUART_IMR_IN | M_DUART_IMR_RX);
+
+	return 0;
+}
+
+static void sbd_shutdown(struct uart_port *uport)
+{
+	struct sbd_port *sport = to_sport(uport);
+
+	write_sbdchn(sport, R_DUART_CMD, M_DUART_TX_DIS | M_DUART_RX_DIS);
+	sport->tx_stopped = 1;
+	free_irq(sport->port.irq, sport);
+}
+
+
+static void sbd_init_port(struct sbd_port *sport)
+{
+	struct uart_port *uport = &sport->port;
+
+	if (sport->initialised)
+		return;
+
+	/* There is no DUART reset feature, so just set some sane defaults.  */
+	write_sbdchn(sport, R_DUART_CMD, V_DUART_MISC_CMD_RESET_TX);
+	write_sbdchn(sport, R_DUART_CMD, V_DUART_MISC_CMD_RESET_RX);
+	write_sbdchn(sport, R_DUART_MODE_REG_1, V_DUART_BITS_PER_CHAR_8);
+	write_sbdchn(sport, R_DUART_MODE_REG_2, 0);
+	write_sbdchn(sport, R_DUART_FULL_CTL,
+		     V_DUART_INT_TIME(0) | V_DUART_SIG_FULL(15));
+	write_sbdchn(sport, R_DUART_OPCR_X, 0);
+	write_sbdchn(sport, R_DUART_AUXCTL_X, 0);
+	write_sbdshr(sport, R_DUART_IMRREG((uport->line) % 2), 0);
+
+	sport->initialised = 1;
+}
+
+static void sbd_set_termios(struct uart_port *uport, struct ktermios *termios,
+			    struct ktermios *old_termios)
+{
+	struct sbd_port *sport = to_sport(uport);
+	unsigned int mode1 = 0, mode2 = 0, aux = 0;
+	unsigned int mode1mask = 0, mode2mask = 0, auxmask = 0;
+	unsigned int oldmode1, oldmode2, oldaux;
+	unsigned int baud, brg;
+	unsigned int command;
+
+	mode1mask |= ~(M_DUART_PARITY_MODE | M_DUART_PARITY_TYPE_ODD |
+		       M_DUART_BITS_PER_CHAR);
+	mode2mask |= ~M_DUART_STOP_BIT_LEN_2;
+	auxmask |= ~M_DUART_CTS_CHNG_ENA;
+
+	/* Byte size.  */
+	switch (termios->c_cflag & CSIZE) {
+	case CS5:
+	case CS6:
+		/* Unsupported, leave unchanged.  */
+		mode1mask |= M_DUART_PARITY_MODE;
+		break;
+	case CS7:
+		mode1 |= V_DUART_BITS_PER_CHAR_7;
+		break;
+	case CS8:
+	default:
+		mode1 |= V_DUART_BITS_PER_CHAR_8;
+		break;
+	}
+
+	/* Parity and stop bits.  */
+	if (termios->c_cflag & CSTOPB)
+		mode2 |= M_DUART_STOP_BIT_LEN_2;
+	else
+		mode2 |= M_DUART_STOP_BIT_LEN_1;
+	if (termios->c_cflag & PARENB)
+		mode1 |= V_DUART_PARITY_MODE_ADD;
+	else
+		mode1 |= V_DUART_PARITY_MODE_NONE;
+	if (termios->c_cflag & PARODD)
+		mode1 |= M_DUART_PARITY_TYPE_ODD;
+	else
+		mode1 |= M_DUART_PARITY_TYPE_EVEN;
+
+	baud = uart_get_baud_rate(uport, termios, old_termios, 1200, 5000000);
+	brg = V_DUART_BAUD_RATE(baud);
+	/* The actual lower bound is 1221bps, so compensate.  */
+	if (brg > M_DUART_CLK_COUNTER)
+		brg = M_DUART_CLK_COUNTER;
+
+	uart_update_timeout(uport, termios->c_cflag, baud);
+
+	uport->read_status_mask = M_DUART_OVRUN_ERR;
+	if (termios->c_iflag & INPCK)
+		uport->read_status_mask |= M_DUART_FRM_ERR |
+					   M_DUART_PARITY_ERR;
+	if (termios->c_iflag & (BRKINT | PARMRK))
+		uport->read_status_mask |= M_DUART_RCVD_BRK;
+
+	uport->ignore_status_mask = 0;
+	if (termios->c_iflag & IGNPAR)
+		uport->ignore_status_mask |= M_DUART_FRM_ERR |
+					     M_DUART_PARITY_ERR;
+	if (termios->c_iflag & IGNBRK) {
+		uport->ignore_status_mask |= M_DUART_RCVD_BRK;
+		if (termios->c_iflag & IGNPAR)
+			uport->ignore_status_mask |= M_DUART_OVRUN_ERR;
+	}
+
+	if (termios->c_cflag & CREAD)
+		command = M_DUART_RX_EN;
+	else
+		command = M_DUART_RX_DIS;
+
+	if (termios->c_cflag & CRTSCTS)
+		aux |= M_DUART_CTS_CHNG_ENA;
+	else
+		aux &= ~M_DUART_CTS_CHNG_ENA;
+
+	spin_lock(&uport->lock);
+
+	if (sport->tx_stopped)
+		command |= M_DUART_TX_DIS;
+	else
+		command |= M_DUART_TX_EN;
+
+	oldmode1 = read_sbdchn(sport, R_DUART_MODE_REG_1) & mode1mask;
+	oldmode2 = read_sbdchn(sport, R_DUART_MODE_REG_2) & mode2mask;
+	oldaux = read_sbdchn(sport, R_DUART_AUXCTL_X) & auxmask;
+
+	if (!sport->tx_stopped)
+		sbd_line_drain(sport);
+	write_sbdchn(sport, R_DUART_CMD, M_DUART_TX_DIS | M_DUART_RX_DIS);
+
+	write_sbdchn(sport, R_DUART_MODE_REG_1, mode1 | oldmode1);
+	write_sbdchn(sport, R_DUART_MODE_REG_2, mode2 | oldmode2);
+	write_sbdchn(sport, R_DUART_CLK_SEL, brg);
+	write_sbdchn(sport, R_DUART_AUXCTL_X, aux | oldaux);
+
+	write_sbdchn(sport, R_DUART_CMD, command);
+
+	spin_unlock(&uport->lock);
+}
+
+
+static const char *sbd_type(struct uart_port *uport)
+{
+	return "SB1250 DUART";
+}
+
+static void sbd_release_port(struct uart_port *uport)
+{
+	struct sbd_port *sport = to_sport(uport);
+	struct sbd_duart *duart = sport->duart;
+	int map_guard;
+
+	iounmap(sport->memctrl);
+	sport->memctrl = NULL;
+	iounmap(uport->membase);
+	uport->membase = NULL;
+
+	map_guard = atomic_add_return(-1, &duart->map_guard);
+	if (!map_guard)
+		release_mem_region(duart->mapctrl, DUART_CHANREG_SPACING);
+	release_mem_region(uport->mapbase, DUART_CHANREG_SPACING);
+}
+
+static int sbd_map_port(struct uart_port *uport)
+{
+	const char *err = KERN_ERR "sbd: Cannot map MMIO\n";
+	struct sbd_port *sport = to_sport(uport);
+	struct sbd_duart *duart = sport->duart;
+
+	if (!uport->membase)
+		uport->membase = ioremap_nocache(uport->mapbase,
+						 DUART_CHANREG_SPACING);
+	if (!uport->membase) {
+		printk(err);
+		return -ENOMEM;
+	}
+
+	if (!sport->memctrl)
+		sport->memctrl = ioremap_nocache(duart->mapctrl,
+						 DUART_CHANREG_SPACING);
+	if (!sport->memctrl) {
+		printk(err);
+		iounmap(uport->membase);
+		uport->membase = NULL;
+		return -ENOMEM;
+	}
+
+	return 0;
+}
+
+static int sbd_request_port(struct uart_port *uport)
+{
+	const char *err = KERN_ERR "sbd: Unable to reserve MMIO resource\n";
+	struct sbd_duart *duart = to_sport(uport)->duart;
+	int map_guard;
+	int ret = 0;
+
+	if (!request_mem_region(uport->mapbase, DUART_CHANREG_SPACING,
+				"sb1250-duart")) {
+		printk(err);
+		return -EBUSY;
+	}
+	map_guard = atomic_add_return(1, &duart->map_guard);
+	if (map_guard == 1) {
+		if (!request_mem_region(duart->mapctrl, DUART_CHANREG_SPACING,
+					"sb1250-duart")) {
+			atomic_add(-1, &duart->map_guard);
+			printk(err);
+			ret = -EBUSY;
+		}
+	}
+	if (!ret) {
+		ret = sbd_map_port(uport);
+		if (ret) {
+			map_guard = atomic_add_return(-1, &duart->map_guard);
+			if (!map_guard)
+				release_mem_region(duart->mapctrl,
+						   DUART_CHANREG_SPACING);
+		}
+	}
+	if (ret) {
+		release_mem_region(uport->mapbase, DUART_CHANREG_SPACING);
+		return ret;
+	}
+	return 0;
+}
+
+static void sbd_config_port(struct uart_port *uport, int flags)
+{
+	struct sbd_port *sport = to_sport(uport);
+
+	if (flags & UART_CONFIG_TYPE) {
+		if (sbd_request_port(uport))
+			return;
+
+		uport->type = PORT_SB1250_DUART;
+
+		sbd_init_port(sport);
+	}
+}
+
+static int sbd_verify_port(struct uart_port *uport, struct serial_struct *ser)
+{
+	int ret = 0;
+
+	if (ser->type != PORT_UNKNOWN && ser->type != PORT_SB1250_DUART)
+		ret = -EINVAL;
+	if (ser->irq != uport->irq)
+		ret = -EINVAL;
+	if (ser->baud_base != uport->uartclk / 16)
+		ret = -EINVAL;
+	return ret;
+}
+
+
+static const struct uart_ops sbd_ops = {
+	.tx_empty	= sbd_tx_empty,
+	.set_mctrl	= sbd_set_mctrl,
+	.get_mctrl	= sbd_get_mctrl,
+	.stop_tx	= sbd_stop_tx,
+	.start_tx	= sbd_start_tx,
+	.stop_rx	= sbd_stop_rx,
+	.enable_ms	= sbd_enable_ms,
+	.break_ctl	= sbd_break_ctl,
+	.startup	= sbd_startup,
+	.shutdown	= sbd_shutdown,
+	.set_termios	= sbd_set_termios,
+	.type		= sbd_type,
+	.release_port	= sbd_release_port,
+	.request_port	= sbd_request_port,
+	.config_port	= sbd_config_port,
+	.verify_port	= sbd_verify_port,
+};
+
+/* Initialize SB1250 DUART port structures.  */
+static void __init sbd_probe_duarts(void)
+{
+	static int probed;
+	int chip, side;
+	int max_lines, line;
+
+	if (probed)
+		return;
+
+	/* Set the number of available units based on the SOC type.  */
+	switch (soc_type) {
+	case K_SYS_SOC_TYPE_BCM1x55:
+	case K_SYS_SOC_TYPE_BCM1x80:
+		max_lines = 4;
+		break;
+	default:
+		/* Assume at least two serial ports at the normal address.  */
+		max_lines = 2;
+		break;
+	}
+
+	probed = 1;
+
+	for (chip = 0, line = 0; chip < DUART_MAX_CHIP && line < max_lines;
+	     chip++) {
+		sbd_duarts[chip].mapctrl = SBD_CTRLREGS(line);
+
+		for (side = 0; side < DUART_MAX_SIDE && line < max_lines;
+		     side++, line++) {
+			struct sbd_port *sport = &sbd_duarts[chip].sport[side];
+			struct uart_port *uport = &sport->port;
+
+			sport->duart	= &sbd_duarts[chip];
+
+			uport->irq	= SBD_INT(line);
+			uport->uartclk	= 100000000 / 20 * 16;
+			uport->fifosize	= 16;
+			uport->iotype	= UPIO_MEM;
+			uport->flags	= UPF_BOOT_AUTOCONF;
+			uport->ops	= &sbd_ops;
+			uport->line	= line;
+			uport->mapbase	= SBD_CHANREGS(line);
+		}
+	}
+}
+
+
+#ifdef CONFIG_SERIAL_SB1250_DUART_CONSOLE
+/*
+ * Serial console stuff.  Very basic, polling driver for doing serial
+ * console output.  The console_lock is held by the caller, so we
+ * shouldn't be interrupted for more console activity.
+ */
+static void sbd_console_putchar(struct uart_port *uport, int ch)
+{
+	struct sbd_port *sport = to_sport(uport);
+
+	sbd_transmit_drain(sport);
+	write_sbdchn(sport, R_DUART_TX_HOLD, ch);
+}
+
+static void sbd_console_write(struct console *co, const char *s,
+			      unsigned int count)
+{
+	int chip = co->index / DUART_MAX_SIDE;
+	int side = co->index % DUART_MAX_SIDE;
+	struct sbd_port *sport = &sbd_duarts[chip].sport[side];
+	struct uart_port *uport = &sport->port;
+	unsigned long flags;
+	unsigned int mask;
+
+	/* Disable transmit interrupts and enable the transmitter. */
+	spin_lock_irqsave(&uport->lock, flags);
+	mask = read_sbdshr(sport, R_DUART_IMRREG((uport->line) % 2));
+	write_sbdshr(sport, R_DUART_IMRREG((uport->line) % 2),
+		     mask & ~M_DUART_IMR_TX);
+	write_sbdchn(sport, R_DUART_CMD, M_DUART_TX_EN);
+	spin_unlock_irqrestore(&uport->lock, flags);
+
+	uart_console_write(&sport->port, s, count, sbd_console_putchar);
+
+	/* Restore transmit interrupts and the transmitter enable. */
+	spin_lock_irqsave(&uport->lock, flags);
+	sbd_line_drain(sport);
+	if (sport->tx_stopped)
+		write_sbdchn(sport, R_DUART_CMD, M_DUART_TX_DIS);
+	write_sbdshr(sport, R_DUART_IMRREG((uport->line) % 2), mask);
+	spin_unlock_irqrestore(&uport->lock, flags);
+}
+
+static int __init sbd_console_setup(struct console *co, char *options)
+{
+	int chip = co->index / DUART_MAX_SIDE;
+	int side = co->index % DUART_MAX_SIDE;
+	struct sbd_port *sport = &sbd_duarts[chip].sport[side];
+	struct uart_port *uport = &sport->port;
+	int baud = 115200;
+	int bits = 8;
+	int parity = 'n';
+	int flow = 'n';
+	int ret;
+
+	if (!sport->duart)
+		return -ENXIO;
+
+	ret = sbd_map_port(uport);
+	if (ret)
+		return ret;
+
+	sbd_init_port(sport);
+
+	if (options)
+		uart_parse_options(options, &baud, &parity, &bits, &flow);
+	return uart_set_options(uport, co, baud, parity, bits, flow);
+}
+
+static struct uart_driver sbd_reg;
+static struct console sbd_console = {
+	.name	= "duart",
+	.write	= sbd_console_write,
+	.device	= uart_console_device,
+	.setup	= sbd_console_setup,
+	.flags	= CON_PRINTBUFFER,
+	.index	= -1,
+	.data	= &sbd_reg
+};
+
+static int __init sbd_serial_console_init(void)
+{
+	sbd_probe_duarts();
+	register_console(&sbd_console);
+
+	return 0;
+}
+
+console_initcall(sbd_serial_console_init);
+
+#define SERIAL_SB1250_DUART_CONSOLE	&sbd_console
+#else
+#define SERIAL_SB1250_DUART_CONSOLE	NULL
+#endif /* CONFIG_SERIAL_SB1250_DUART_CONSOLE */
+
+
+static struct uart_driver sbd_reg = {
+	.owner		= THIS_MODULE,
+	.driver_name	= "sb1250_duart",
+	.dev_name	= "duart",
+	.major		= TTY_MAJOR,
+	.minor		= SB1250_DUART_MINOR_BASE,
+	.nr		= DUART_MAX_CHIP * DUART_MAX_SIDE,
+	.cons		= SERIAL_SB1250_DUART_CONSOLE,
+};
+
+/* Set up the driver and register it.  */
+static int __init sbd_init(void)
+{
+	int i, ret;
+
+	sbd_probe_duarts();
+
+	ret = uart_register_driver(&sbd_reg);
+	if (ret)
+		return ret;
+
+	for (i = 0; i < DUART_MAX_CHIP * DUART_MAX_SIDE; i++) {
+		struct sbd_duart *duart = &sbd_duarts[i / DUART_MAX_SIDE];
+		struct sbd_port *sport = &duart->sport[i % DUART_MAX_SIDE];
+		struct uart_port *uport = &sport->port;
+
+		if (sport->duart)
+			uart_add_one_port(&sbd_reg, uport);
+	}
+
+	return 0;
+}
+
+/* Unload the driver.  Unregister stuff, get ready to go away.  */
+static void __exit sbd_exit(void)
+{
+	int i;
+
+	for (i = DUART_MAX_CHIP * DUART_MAX_SIDE - 1; i >= 0; i--) {
+		struct sbd_duart *duart = &sbd_duarts[i / DUART_MAX_SIDE];
+		struct sbd_port *sport = &duart->sport[i % DUART_MAX_SIDE];
+		struct uart_port *uport = &sport->port;
+
+		if (sport->duart)
+			uart_remove_one_port(&sbd_reg, uport);
+	}
+
+	uart_unregister_driver(&sbd_reg);
+}
+
+module_init(sbd_init);
+module_exit(sbd_exit);
diff --git a/ap/os/linux/linux-3.4.x/drivers/tty/serial/sc26xx.c b/ap/os/linux/linux-3.4.x/drivers/tty/serial/sc26xx.c
new file mode 100644
index 0000000..e0b4b0a
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/tty/serial/sc26xx.c
@@ -0,0 +1,745 @@
+/*
+ * SC268xx.c: Serial driver for Philiphs SC2681/SC2692 devices.
+ *
+ * Copyright (C) 2006,2007 Thomas Bogendörfer (tsbogend@alpha.franken.de)
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
+#include <linux/major.h>
+#include <linux/circ_buf.h>
+#include <linux/serial.h>
+#include <linux/sysrq.h>
+#include <linux/console.h>
+#include <linux/spinlock.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/irq.h>
+
+#if defined(CONFIG_MAGIC_SYSRQ)
+#define SUPPORT_SYSRQ
+#endif
+
+#include <linux/serial_core.h>
+
+#define SC26XX_MAJOR         204
+#define SC26XX_MINOR_START   205
+#define SC26XX_NR            2
+
+struct uart_sc26xx_port {
+	struct uart_port      port[2];
+	u8     dsr_mask[2];
+	u8     cts_mask[2];
+	u8     dcd_mask[2];
+	u8     ri_mask[2];
+	u8     dtr_mask[2];
+	u8     rts_mask[2];
+	u8     imr;
+};
+
+/* register common to both ports */
+#define RD_ISR      0x14
+#define RD_IPR      0x34
+
+#define WR_ACR      0x10
+#define WR_IMR      0x14
+#define WR_OPCR     0x34
+#define WR_OPR_SET  0x38
+#define WR_OPR_CLR  0x3C
+
+/* access common register */
+#define READ_SC(p, r)        readb((p)->membase + RD_##r)
+#define WRITE_SC(p, r, v)    writeb((v), (p)->membase + WR_##r)
+
+/* register per port */
+#define RD_PORT_MRx 0x00
+#define RD_PORT_SR  0x04
+#define RD_PORT_RHR 0x0c
+
+#define WR_PORT_MRx 0x00
+#define WR_PORT_CSR 0x04
+#define WR_PORT_CR  0x08
+#define WR_PORT_THR 0x0c
+
+/* SR bits */
+#define SR_BREAK    (1 << 7)
+#define SR_FRAME    (1 << 6)
+#define SR_PARITY   (1 << 5)
+#define SR_OVERRUN  (1 << 4)
+#define SR_TXRDY    (1 << 2)
+#define SR_RXRDY    (1 << 0)
+
+#define CR_RES_MR   (1 << 4)
+#define CR_RES_RX   (2 << 4)
+#define CR_RES_TX   (3 << 4)
+#define CR_STRT_BRK (6 << 4)
+#define CR_STOP_BRK (7 << 4)
+#define CR_DIS_TX   (1 << 3)
+#define CR_ENA_TX   (1 << 2)
+#define CR_DIS_RX   (1 << 1)
+#define CR_ENA_RX   (1 << 0)
+
+/* ISR bits */
+#define ISR_RXRDYB  (1 << 5)
+#define ISR_TXRDYB  (1 << 4)
+#define ISR_RXRDYA  (1 << 1)
+#define ISR_TXRDYA  (1 << 0)
+
+/* IMR bits */
+#define IMR_RXRDY   (1 << 1)
+#define IMR_TXRDY   (1 << 0)
+
+/* access port register */
+static inline u8 read_sc_port(struct uart_port *p, u8 reg)
+{
+	return readb(p->membase + p->line * 0x20 + reg);
+}
+
+static inline void write_sc_port(struct uart_port *p, u8 reg, u8 val)
+{
+	writeb(val, p->membase + p->line * 0x20 + reg);
+}
+
+#define READ_SC_PORT(p, r)     read_sc_port(p, RD_PORT_##r)
+#define WRITE_SC_PORT(p, r, v) write_sc_port(p, WR_PORT_##r, v)
+
+static void sc26xx_enable_irq(struct uart_port *port, int mask)
+{
+	struct uart_sc26xx_port *up;
+	int line = port->line;
+
+	port -= line;
+	up = container_of(port, struct uart_sc26xx_port, port[0]);
+
+	up->imr |= mask << (line * 4);
+	WRITE_SC(port, IMR, up->imr);
+}
+
+static void sc26xx_disable_irq(struct uart_port *port, int mask)
+{
+	struct uart_sc26xx_port *up;
+	int line = port->line;
+
+	port -= line;
+	up = container_of(port, struct uart_sc26xx_port, port[0]);
+
+	up->imr &= ~(mask << (line * 4));
+	WRITE_SC(port, IMR, up->imr);
+}
+
+static struct tty_struct *receive_chars(struct uart_port *port)
+{
+	struct tty_struct *tty = NULL;
+	int limit = 10000;
+	unsigned char ch;
+	char flag;
+	u8 status;
+
+	if (port->state != NULL)		/* Unopened serial console */
+		tty = port->state->port.tty;
+
+	while (limit-- > 0) {
+		status = READ_SC_PORT(port, SR);
+		if (!(status & SR_RXRDY))
+			break;
+		ch = READ_SC_PORT(port, RHR);
+
+		flag = TTY_NORMAL;
+		port->icount.rx++;
+
+		if (unlikely(status & (SR_BREAK | SR_FRAME |
+				       SR_PARITY | SR_OVERRUN))) {
+			if (status & SR_BREAK) {
+				status &= ~(SR_PARITY | SR_FRAME);
+				port->icount.brk++;
+				if (uart_handle_break(port))
+					continue;
+			} else if (status & SR_PARITY)
+				port->icount.parity++;
+			else if (status & SR_FRAME)
+				port->icount.frame++;
+			if (status & SR_OVERRUN)
+				port->icount.overrun++;
+
+			status &= port->read_status_mask;
+			if (status & SR_BREAK)
+				flag = TTY_BREAK;
+			else if (status & SR_PARITY)
+				flag = TTY_PARITY;
+			else if (status & SR_FRAME)
+				flag = TTY_FRAME;
+		}
+
+		if (uart_handle_sysrq_char(port, ch))
+			continue;
+
+		if (status & port->ignore_status_mask)
+			continue;
+
+		tty_insert_flip_char(tty, ch, flag);
+	}
+	return tty;
+}
+
+static void transmit_chars(struct uart_port *port)
+{
+	struct circ_buf *xmit;
+
+	if (!port->state)
+		return;
+
+	xmit = &port->state->xmit;
+	if (uart_circ_empty(xmit) || uart_tx_stopped(port)) {
+		sc26xx_disable_irq(port, IMR_TXRDY);
+		return;
+	}
+	while (!uart_circ_empty(xmit)) {
+		if (!(READ_SC_PORT(port, SR) & SR_TXRDY))
+			break;
+
+		WRITE_SC_PORT(port, THR, xmit->buf[xmit->tail]);
+		xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
+		port->icount.tx++;
+	}
+	if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
+		uart_write_wakeup(port);
+}
+
+static irqreturn_t sc26xx_interrupt(int irq, void *dev_id)
+{
+	struct uart_sc26xx_port *up = dev_id;
+	struct tty_struct *tty;
+	unsigned long flags;
+	u8 isr;
+
+	spin_lock_irqsave(&up->port[0].lock, flags);
+
+	tty = NULL;
+	isr = READ_SC(&up->port[0], ISR);
+	if (isr & ISR_TXRDYA)
+	    transmit_chars(&up->port[0]);
+	if (isr & ISR_RXRDYA)
+	    tty = receive_chars(&up->port[0]);
+
+	spin_unlock(&up->port[0].lock);
+
+	if (tty)
+		tty_flip_buffer_push(tty);
+
+	spin_lock(&up->port[1].lock);
+
+	tty = NULL;
+	if (isr & ISR_TXRDYB)
+	    transmit_chars(&up->port[1]);
+	if (isr & ISR_RXRDYB)
+	    tty = receive_chars(&up->port[1]);
+
+	spin_unlock_irqrestore(&up->port[1].lock, flags);
+
+	if (tty)
+		tty_flip_buffer_push(tty);
+
+	return IRQ_HANDLED;
+}
+
+/* port->lock is not held.  */
+static unsigned int sc26xx_tx_empty(struct uart_port *port)
+{
+	return (READ_SC_PORT(port, SR) & SR_TXRDY) ? TIOCSER_TEMT : 0;
+}
+
+/* port->lock held by caller.  */
+static void sc26xx_set_mctrl(struct uart_port *port, unsigned int mctrl)
+{
+	struct uart_sc26xx_port *up;
+	int line = port->line;
+
+	port -= line;
+	up = container_of(port, struct uart_sc26xx_port, port[0]);
+
+	if (up->dtr_mask[line]) {
+		if (mctrl & TIOCM_DTR)
+			WRITE_SC(port, OPR_SET, up->dtr_mask[line]);
+		else
+			WRITE_SC(port, OPR_CLR, up->dtr_mask[line]);
+	}
+	if (up->rts_mask[line]) {
+		if (mctrl & TIOCM_RTS)
+			WRITE_SC(port, OPR_SET, up->rts_mask[line]);
+		else
+			WRITE_SC(port, OPR_CLR, up->rts_mask[line]);
+	}
+}
+
+/* port->lock is held by caller and interrupts are disabled.  */
+static unsigned int sc26xx_get_mctrl(struct uart_port *port)
+{
+	struct uart_sc26xx_port *up;
+	int line = port->line;
+	unsigned int mctrl = TIOCM_DSR | TIOCM_CTS | TIOCM_CAR;
+	u8 ipr;
+
+	port -= line;
+	up = container_of(port, struct uart_sc26xx_port, port[0]);
+	ipr = READ_SC(port, IPR) ^ 0xff;
+
+	if (up->dsr_mask[line]) {
+		mctrl &= ~TIOCM_DSR;
+		mctrl |= ipr & up->dsr_mask[line] ? TIOCM_DSR : 0;
+	}
+	if (up->cts_mask[line]) {
+		mctrl &= ~TIOCM_CTS;
+		mctrl |= ipr & up->cts_mask[line] ? TIOCM_CTS : 0;
+	}
+	if (up->dcd_mask[line]) {
+		mctrl &= ~TIOCM_CAR;
+		mctrl |= ipr & up->dcd_mask[line] ? TIOCM_CAR : 0;
+	}
+	if (up->ri_mask[line]) {
+		mctrl &= ~TIOCM_RNG;
+		mctrl |= ipr & up->ri_mask[line] ? TIOCM_RNG : 0;
+	}
+	return mctrl;
+}
+
+/* port->lock held by caller.  */
+static void sc26xx_stop_tx(struct uart_port *port)
+{
+	return;
+}
+
+/* port->lock held by caller.  */
+static void sc26xx_start_tx(struct uart_port *port)
+{
+	struct circ_buf *xmit = &port->state->xmit;
+
+	while (!uart_circ_empty(xmit)) {
+		if (!(READ_SC_PORT(port, SR) & SR_TXRDY)) {
+			sc26xx_enable_irq(port, IMR_TXRDY);
+			break;
+		}
+		WRITE_SC_PORT(port, THR, xmit->buf[xmit->tail]);
+		xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
+		port->icount.tx++;
+	}
+}
+
+/* port->lock held by caller.  */
+static void sc26xx_stop_rx(struct uart_port *port)
+{
+}
+
+/* port->lock held by caller.  */
+static void sc26xx_enable_ms(struct uart_port *port)
+{
+}
+
+/* port->lock is not held.  */
+static void sc26xx_break_ctl(struct uart_port *port, int break_state)
+{
+	if (break_state == -1)
+		WRITE_SC_PORT(port, CR, CR_STRT_BRK);
+	else
+		WRITE_SC_PORT(port, CR, CR_STOP_BRK);
+}
+
+/* port->lock is not held.  */
+static int sc26xx_startup(struct uart_port *port)
+{
+	sc26xx_disable_irq(port, IMR_TXRDY | IMR_RXRDY);
+	WRITE_SC(port, OPCR, 0);
+
+	/* reset tx and rx */
+	WRITE_SC_PORT(port, CR, CR_RES_RX);
+	WRITE_SC_PORT(port, CR, CR_RES_TX);
+
+	/* start rx/tx */
+	WRITE_SC_PORT(port, CR, CR_ENA_TX | CR_ENA_RX);
+
+	/* enable irqs */
+	sc26xx_enable_irq(port, IMR_RXRDY);
+	return 0;
+}
+
+/* port->lock is not held.  */
+static void sc26xx_shutdown(struct uart_port *port)
+{
+	/* disable interrupst */
+	sc26xx_disable_irq(port, IMR_TXRDY | IMR_RXRDY);
+
+	/* stop tx/rx */
+	WRITE_SC_PORT(port, CR, CR_DIS_TX | CR_DIS_RX);
+}
+
+/* port->lock is not held.  */
+static void sc26xx_set_termios(struct uart_port *port, struct ktermios *termios,
+			      struct ktermios *old)
+{
+	unsigned int baud = uart_get_baud_rate(port, termios, old, 0, 4000000);
+	unsigned int quot = uart_get_divisor(port, baud);
+	unsigned int iflag, cflag;
+	unsigned long flags;
+	u8 mr1, mr2, csr;
+
+	spin_lock_irqsave(&port->lock, flags);
+
+	while ((READ_SC_PORT(port, SR) & ((1 << 3) | (1 << 2))) != 0xc)
+		udelay(2);
+
+	WRITE_SC_PORT(port, CR, CR_DIS_TX | CR_DIS_RX);
+
+	iflag = termios->c_iflag;
+	cflag = termios->c_cflag;
+
+	port->read_status_mask = SR_OVERRUN;
+	if (iflag & INPCK)
+		port->read_status_mask |= SR_PARITY | SR_FRAME;
+	if (iflag & (BRKINT | PARMRK))
+		port->read_status_mask |= SR_BREAK;
+
+	port->ignore_status_mask = 0;
+	if (iflag & IGNBRK)
+		port->ignore_status_mask |= SR_BREAK;
+	if ((cflag & CREAD) == 0)
+		port->ignore_status_mask |= SR_BREAK | SR_FRAME |
+					    SR_PARITY | SR_OVERRUN;
+
+	switch (cflag & CSIZE) {
+	case CS5:
+		mr1 = 0x00;
+		break;
+	case CS6:
+		mr1 = 0x01;
+		break;
+	case CS7:
+		mr1 = 0x02;
+		break;
+	default:
+	case CS8:
+		mr1 = 0x03;
+		break;
+	}
+	mr2 = 0x07;
+	if (cflag & CSTOPB)
+		mr2 = 0x0f;
+	if (cflag & PARENB) {
+		if (cflag & PARODD)
+			mr1 |= (1 << 2);
+	} else
+		mr1 |= (2 << 3);
+
+	switch (baud) {
+	case 50:
+		csr = 0x00;
+		break;
+	case 110:
+		csr = 0x11;
+		break;
+	case 134:
+		csr = 0x22;
+		break;
+	case 200:
+		csr = 0x33;
+		break;
+	case 300:
+		csr = 0x44;
+		break;
+	case 600:
+		csr = 0x55;
+		break;
+	case 1200:
+		csr = 0x66;
+		break;
+	case 2400:
+		csr = 0x88;
+		break;
+	case 4800:
+		csr = 0x99;
+		break;
+	default:
+	case 9600:
+		csr = 0xbb;
+		break;
+	case 19200:
+		csr = 0xcc;
+		break;
+	}
+
+	WRITE_SC_PORT(port, CR, CR_RES_MR);
+	WRITE_SC_PORT(port, MRx, mr1);
+	WRITE_SC_PORT(port, MRx, mr2);
+
+	WRITE_SC(port, ACR, 0x80);
+	WRITE_SC_PORT(port, CSR, csr);
+
+	/* reset tx and rx */
+	WRITE_SC_PORT(port, CR, CR_RES_RX);
+	WRITE_SC_PORT(port, CR, CR_RES_TX);
+
+	WRITE_SC_PORT(port, CR, CR_ENA_TX | CR_ENA_RX);
+	while ((READ_SC_PORT(port, SR) & ((1 << 3) | (1 << 2))) != 0xc)
+		udelay(2);
+
+	/* XXX */
+	uart_update_timeout(port, cflag,
+			    (port->uartclk / (16 * quot)));
+
+	spin_unlock_irqrestore(&port->lock, flags);
+}
+
+static const char *sc26xx_type(struct uart_port *port)
+{
+	return "SC26XX";
+}
+
+static void sc26xx_release_port(struct uart_port *port)
+{
+}
+
+static int sc26xx_request_port(struct uart_port *port)
+{
+	return 0;
+}
+
+static void sc26xx_config_port(struct uart_port *port, int flags)
+{
+}
+
+static int sc26xx_verify_port(struct uart_port *port, struct serial_struct *ser)
+{
+	return -EINVAL;
+}
+
+static struct uart_ops sc26xx_ops = {
+	.tx_empty	= sc26xx_tx_empty,
+	.set_mctrl	= sc26xx_set_mctrl,
+	.get_mctrl	= sc26xx_get_mctrl,
+	.stop_tx	= sc26xx_stop_tx,
+	.start_tx	= sc26xx_start_tx,
+	.stop_rx	= sc26xx_stop_rx,
+	.enable_ms	= sc26xx_enable_ms,
+	.break_ctl	= sc26xx_break_ctl,
+	.startup	= sc26xx_startup,
+	.shutdown	= sc26xx_shutdown,
+	.set_termios	= sc26xx_set_termios,
+	.type		= sc26xx_type,
+	.release_port	= sc26xx_release_port,
+	.request_port	= sc26xx_request_port,
+	.config_port	= sc26xx_config_port,
+	.verify_port	= sc26xx_verify_port,
+};
+
+static struct uart_port *sc26xx_port;
+
+#ifdef CONFIG_SERIAL_SC26XX_CONSOLE
+static void sc26xx_console_putchar(struct uart_port *port, char c)
+{
+	unsigned long flags;
+	int limit = 1000000;
+
+	spin_lock_irqsave(&port->lock, flags);
+
+	while (limit-- > 0) {
+		if (READ_SC_PORT(port, SR) & SR_TXRDY) {
+			WRITE_SC_PORT(port, THR, c);
+			break;
+		}
+		udelay(2);
+	}
+
+	spin_unlock_irqrestore(&port->lock, flags);
+}
+
+static void sc26xx_console_write(struct console *con, const char *s, unsigned n)
+{
+	struct uart_port *port = sc26xx_port;
+	int i;
+
+	for (i = 0; i < n; i++) {
+		if (*s == '\n')
+			sc26xx_console_putchar(port, '\r');
+		sc26xx_console_putchar(port, *s++);
+	}
+}
+
+static int __init sc26xx_console_setup(struct console *con, char *options)
+{
+	struct uart_port *port = sc26xx_port;
+	int baud = 9600;
+	int bits = 8;
+	int parity = 'n';
+	int flow = 'n';
+
+	if (port->type != PORT_SC26XX)
+		return -1;
+
+	printk(KERN_INFO "Console: ttySC%d (SC26XX)\n", con->index);
+	if (options)
+		uart_parse_options(options, &baud, &parity, &bits, &flow);
+
+	return uart_set_options(port, con, baud, parity, bits, flow);
+}
+
+static struct uart_driver sc26xx_reg;
+static struct console sc26xx_console = {
+	.name	=	"ttySC",
+	.write	=	sc26xx_console_write,
+	.device	=	uart_console_device,
+	.setup  =       sc26xx_console_setup,
+	.flags	=	CON_PRINTBUFFER,
+	.index	=	-1,
+	.data	=	&sc26xx_reg,
+};
+#define SC26XX_CONSOLE   &sc26xx_console
+#else
+#define SC26XX_CONSOLE   NULL
+#endif
+
+static struct uart_driver sc26xx_reg = {
+	.owner			= THIS_MODULE,
+	.driver_name		= "SC26xx",
+	.dev_name		= "ttySC",
+	.major			= SC26XX_MAJOR,
+	.minor			= SC26XX_MINOR_START,
+	.nr			= SC26XX_NR,
+	.cons                   = SC26XX_CONSOLE,
+};
+
+static u8 sc26xx_flags2mask(unsigned int flags, unsigned int bitpos)
+{
+	unsigned int bit = (flags >> bitpos) & 15;
+
+	return bit ? (1 << (bit - 1)) : 0;
+}
+
+static void __devinit sc26xx_init_masks(struct uart_sc26xx_port *up,
+					int line, unsigned int data)
+{
+	up->dtr_mask[line] = sc26xx_flags2mask(data,  0);
+	up->rts_mask[line] = sc26xx_flags2mask(data,  4);
+	up->dsr_mask[line] = sc26xx_flags2mask(data,  8);
+	up->cts_mask[line] = sc26xx_flags2mask(data, 12);
+	up->dcd_mask[line] = sc26xx_flags2mask(data, 16);
+	up->ri_mask[line]  = sc26xx_flags2mask(data, 20);
+}
+
+static int __devinit sc26xx_probe(struct platform_device *dev)
+{
+	struct resource *res;
+	struct uart_sc26xx_port *up;
+	unsigned int *sc26xx_data = dev->dev.platform_data;
+	int err;
+
+	res = platform_get_resource(dev, IORESOURCE_MEM, 0);
+	if (!res)
+		return -ENODEV;
+
+	up = kzalloc(sizeof *up, GFP_KERNEL);
+	if (unlikely(!up))
+		return -ENOMEM;
+
+	up->port[0].line = 0;
+	up->port[0].ops = &sc26xx_ops;
+	up->port[0].type = PORT_SC26XX;
+	up->port[0].uartclk = (29491200 / 16); /* arbitrary */
+
+	up->port[0].mapbase = res->start;
+	up->port[0].membase = ioremap_nocache(up->port[0].mapbase, 0x40);
+	up->port[0].iotype = UPIO_MEM;
+	up->port[0].irq = platform_get_irq(dev, 0);
+
+	up->port[0].dev = &dev->dev;
+
+	sc26xx_init_masks(up, 0, sc26xx_data[0]);
+
+	sc26xx_port = &up->port[0];
+
+	up->port[1].line = 1;
+	up->port[1].ops = &sc26xx_ops;
+	up->port[1].type = PORT_SC26XX;
+	up->port[1].uartclk = (29491200 / 16); /* arbitrary */
+
+	up->port[1].mapbase = up->port[0].mapbase;
+	up->port[1].membase = up->port[0].membase;
+	up->port[1].iotype = UPIO_MEM;
+	up->port[1].irq = up->port[0].irq;
+
+	up->port[1].dev = &dev->dev;
+
+	sc26xx_init_masks(up, 1, sc26xx_data[1]);
+
+	err = uart_register_driver(&sc26xx_reg);
+	if (err)
+		goto out_free_port;
+
+	sc26xx_reg.tty_driver->name_base = sc26xx_reg.minor;
+
+	err = uart_add_one_port(&sc26xx_reg, &up->port[0]);
+	if (err)
+		goto out_unregister_driver;
+
+	err = uart_add_one_port(&sc26xx_reg, &up->port[1]);
+	if (err)
+		goto out_remove_port0;
+
+	err = request_irq(up->port[0].irq, sc26xx_interrupt, 0, "sc26xx", up);
+	if (err)
+		goto out_remove_ports;
+
+	dev_set_drvdata(&dev->dev, up);
+	return 0;
+
+out_remove_ports:
+	uart_remove_one_port(&sc26xx_reg, &up->port[1]);
+out_remove_port0:
+	uart_remove_one_port(&sc26xx_reg, &up->port[0]);
+
+out_unregister_driver:
+	uart_unregister_driver(&sc26xx_reg);
+
+out_free_port:
+	kfree(up);
+	sc26xx_port = NULL;
+	return err;
+}
+
+
+static int __exit sc26xx_driver_remove(struct platform_device *dev)
+{
+	struct uart_sc26xx_port *up = dev_get_drvdata(&dev->dev);
+
+	free_irq(up->port[0].irq, up);
+
+	uart_remove_one_port(&sc26xx_reg, &up->port[0]);
+	uart_remove_one_port(&sc26xx_reg, &up->port[1]);
+
+	uart_unregister_driver(&sc26xx_reg);
+
+	kfree(up);
+	sc26xx_port = NULL;
+
+	dev_set_drvdata(&dev->dev, NULL);
+	return 0;
+}
+
+static struct platform_driver sc26xx_driver = {
+	.probe	= sc26xx_probe,
+	.remove	= __devexit_p(sc26xx_driver_remove),
+	.driver	= {
+		.name	= "SC26xx",
+		.owner	= THIS_MODULE,
+	},
+};
+
+module_platform_driver(sc26xx_driver);
+
+MODULE_AUTHOR("Thomas Bogendörfer");
+MODULE_DESCRIPTION("SC681/SC2692 serial driver");
+MODULE_VERSION("1.0");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:SC26xx");
diff --git a/ap/os/linux/linux-3.4.x/drivers/tty/serial/serial_core.c b/ap/os/linux/linux-3.4.x/drivers/tty/serial/serial_core.c
new file mode 100644
index 0000000..9fc2007
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/tty/serial/serial_core.c
@@ -0,0 +1,2627 @@
+/*
+ *  Driver core for serial ports
+ *
+ *  Based on drivers/char/serial.c, by Linus Torvalds, Theodore Ts'o.
+ *
+ *  Copyright 1999 ARM Limited
+ *  Copyright (C) 2000-2001 Deep Blue Solutions Ltd.
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+#include <linux/module.h>
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <linux/console.h>
+#include <linux/proc_fs.h>
+#include <linux/seq_file.h>
+#include <linux/device.h>
+#include <linux/serial.h> /* for serial_state and serial_icounter_struct */
+#include <linux/serial_core.h>
+#include <linux/delay.h>
+#include <linux/mutex.h>
+#include <linux/wakelock.h>
+#include <linux/soc/zte/pm/drv_idle.h>
+
+#include <asm/irq.h>
+#include <asm/uaccess.h>
+#include <linux/platform_device.h>
+#include "zx29_uart.h"
+#include <mach/zx29_uart_def.h>
+
+/*
+ * This is used to lock changes in serial line configuration.
+ */
+static DEFINE_MUTEX(port_mutex);
+
+/*
+ * lockdep: port->lock is initialized in two places, but we
+ *          want only one lock-class:
+ */
+static struct lock_class_key port_lock_key;
+
+#define HIGH_BITS_OFFSET	((sizeof(long)-sizeof(int))*8)
+
+#ifdef CONFIG_SERIAL_CORE_CONSOLE
+#define uart_console(port)	((port)->cons && (port)->cons->index == (port)->line)
+#else
+#define uart_console(port)	(0)
+#endif
+
+static void uart_change_speed(struct tty_struct *tty, struct uart_state *state,
+					struct ktermios *old_termios);
+static void uart_wait_until_sent(struct tty_struct *tty, int timeout);
+static void uart_change_pm(struct uart_state *state, int pm_state);
+
+static void uart_port_shutdown(struct tty_port *port);
+
+/*
+ * This routine is used by the interrupt handler to schedule processing in
+ * the software interrupt portion of the driver.
+ */
+void uart_write_wakeup(struct uart_port *port)
+{
+	struct uart_state *state = port->state;
+	/*
+	 * This means you called this function _after_ the port was
+	 * closed.  No cookie for you.
+	 */
+	BUG_ON(!state);
+	tty_wakeup(state->port.tty);
+}
+
+static void uart_stop(struct tty_struct *tty)
+{
+	struct uart_state *state = tty->driver_data;
+	struct uart_port *port = state->uart_port;
+	unsigned long flags;
+
+	raw_spin_lock_irqsave(&port->lock, flags);
+	port->ops->stop_tx(port);
+	raw_spin_unlock_irqrestore(&port->lock, flags);
+}
+
+static void __uart_start(struct tty_struct *tty)
+{
+	struct uart_state *state = tty->driver_data;
+	struct uart_port *port = state->uart_port;
+
+	if (port->ops->wake_peer)
+		port->ops->wake_peer(port);
+
+	if (!uart_circ_empty(&state->xmit) && state->xmit.buf &&
+	    !tty->stopped && !tty->hw_stopped){
+		port->ops->start_tx(port);
+	
+		zx_cpuidle_set_busy(IDLE_FLAG_UART);
+
+		if(!uart_console(port))
+		wake_lock_timeout(&port->port_wakelock,100);
+
+	}
+}
+
+static void uart_start(struct tty_struct *tty)
+{
+	struct uart_state *state = tty->driver_data;
+	struct uart_port *port = state->uart_port;
+	unsigned long flags;
+
+	raw_spin_lock_irqsave(&port->lock, flags);
+	__uart_start(tty);
+	raw_spin_unlock_irqrestore(&port->lock, flags);
+}
+
+static inline void
+uart_update_mctrl(struct uart_port *port, unsigned int set, unsigned int clear)
+{
+	unsigned long flags;
+	unsigned int old;
+
+	raw_spin_lock_irqsave(&port->lock, flags);
+	old = port->mctrl;
+	port->mctrl = (old & ~clear) | set;
+	if (old != port->mctrl)
+		port->ops->set_mctrl(port, port->mctrl);
+	raw_spin_unlock_irqrestore(&port->lock, flags);
+}
+
+#define uart_set_mctrl(port, set)	uart_update_mctrl(port, set, 0)
+#define uart_clear_mctrl(port, clear)	uart_update_mctrl(port, 0, clear)
+
+/*
+ * Startup the port.  This will be called once per open.  All calls
+ * will be serialised by the per-port mutex.
+ */
+static int uart_port_startup(struct tty_struct *tty, struct uart_state *state,
+		int init_hw)
+{
+	struct uart_port *uport = state->uart_port;
+	struct tty_port *port = &state->port;
+	unsigned long page;
+	int retval = 0;
+
+	if (uport->type == PORT_UNKNOWN)
+		return 1;
+
+	/*
+	 * Initialise and allocate the transmit and temporary
+	 * buffer.
+	 */
+	if (!state->xmit.buf) {
+		/* This is protected by the per port mutex */
+		page = get_zeroed_page(GFP_KERNEL);
+		if (!page)
+			return -ENOMEM;
+
+		state->xmit.buf = (unsigned char *) page;
+		uart_circ_clear(&state->xmit);
+	}
+	
+	retval = uport->ops->startup(uport);
+	if (retval == 0) {
+		if (uart_console(uport) && uport->cons->cflag) {
+			tty->termios->c_cflag = uport->cons->cflag;
+			uport->cons->cflag = 0;
+		}
+		/*
+		 * Initialise the hardware port settings.
+		 */
+		uart_change_speed(tty, state, NULL);
+
+		if (init_hw) {
+			/*
+			 * Setup the RTS and DTR signals once the
+			 * port is open and ready to respond.
+			 */
+			if (tty->termios->c_cflag & CBAUD)
+				uart_set_mctrl(uport, TIOCM_RTS | TIOCM_DTR);
+		}
+
+		if (port->flags & ASYNC_CTS_FLOW) {
+			raw_spin_lock_irq(&uport->lock);
+			if (!(uport->ops->get_mctrl(uport) & TIOCM_CTS))
+				tty->hw_stopped = 1;
+			raw_spin_unlock_irq(&uport->lock);
+		}
+	}
+
+	/*
+	 * This is to allow setserial on this port. People may want to set
+	 * port/irq/type and then reconfigure the port properly if it failed
+	 * now.
+	 */
+	if (retval && capable(CAP_SYS_ADMIN))
+		return 1;
+
+	return retval;
+}
+
+static int uart_startup(struct tty_struct *tty, struct uart_state *state,
+		int init_hw)
+{
+	struct tty_port *port = &state->port;
+	int retval;
+
+	if (port->flags & ASYNC_INITIALIZED)
+		return 0;
+
+	/*
+	 * Set the TTY IO error marker - we will only clear this
+	 * once we have successfully opened the port.
+	 */
+	set_bit(TTY_IO_ERROR, &tty->flags);
+
+	retval = uart_port_startup(tty, state, init_hw);
+	if (!retval) {
+		set_bit(ASYNCB_INITIALIZED, &port->flags);
+		clear_bit(TTY_IO_ERROR, &tty->flags);
+	} else if (retval > 0)
+		retval = 0;
+
+	return retval;
+}
+
+/*
+ * This routine will shutdown a serial port; interrupts are disabled, and
+ * DTR is dropped if the hangup on close termio flag is on.  Calls to
+ * uart_shutdown are serialised by the per-port semaphore.
+ */
+static void uart_shutdown(struct tty_struct *tty, struct uart_state *state)
+{
+	struct uart_port *uport = state->uart_port;
+	struct tty_port *port = &state->port;
+
+	/*
+	 * Set the TTY IO error marker
+	 */
+	if (tty)
+		set_bit(TTY_IO_ERROR, &tty->flags);
+
+	if (test_and_clear_bit(ASYNCB_INITIALIZED, &port->flags)) {
+		/*
+		 * Turn off DTR and RTS early.
+		 */
+		if (!tty || (tty->termios->c_cflag & HUPCL))
+			uart_clear_mctrl(uport, TIOCM_DTR | TIOCM_RTS);
+
+		uart_port_shutdown(port);
+	}
+
+	/*
+	 * It's possible for shutdown to be called after suspend if we get
+	 * a DCD drop (hangup) at just the right time.  Clear suspended bit so
+	 * we don't try to resume a port that has been shutdown.
+	 */
+	clear_bit(ASYNCB_SUSPENDED, &port->flags);
+
+	/*
+	 * Free the transmit buffer page.
+	 */
+	if (state->xmit.buf) {
+		free_page((unsigned long)state->xmit.buf);
+		state->xmit.buf = NULL;
+	}
+}
+
+/**
+ *	uart_update_timeout - update per-port FIFO timeout.
+ *	@port:  uart_port structure describing the port
+ *	@cflag: termios cflag value
+ *	@baud:  speed of the port
+ *
+ *	Set the port FIFO timeout value.  The @cflag value should
+ *	reflect the actual hardware settings.
+ */
+void
+uart_update_timeout(struct uart_port *port, unsigned int cflag,
+		    unsigned int baud)
+{
+	unsigned int bits;
+
+	/* byte size and parity */
+	switch (cflag & CSIZE) {
+	case CS5:
+		bits = 7;
+		break;
+	case CS6:
+		bits = 8;
+		break;
+	case CS7:
+		bits = 9;
+		break;
+	default:
+		bits = 10;
+		break; /* CS8 */
+	}
+
+	if (cflag & CSTOPB)
+		bits++;
+	if (cflag & PARENB)
+		bits++;
+
+	/*
+	 * The total number of bits to be transmitted in the fifo.
+	 */
+	bits = bits * port->fifosize;
+
+	/*
+	 * Figure the timeout to send the above number of bits.
+	 * Add .02 seconds of slop
+	 */
+	port->timeout = (HZ * bits) / baud + HZ/50;
+}
+
+EXPORT_SYMBOL(uart_update_timeout);
+
+/**
+ *	uart_get_baud_rate - return baud rate for a particular port
+ *	@port: uart_port structure describing the port in question.
+ *	@termios: desired termios settings.
+ *	@old: old termios (or NULL)
+ *	@min: minimum acceptable baud rate
+ *	@max: maximum acceptable baud rate
+ *
+ *	Decode the termios structure into a numeric baud rate,
+ *	taking account of the magic 38400 baud rate (with spd_*
+ *	flags), and mapping the %B0 rate to 9600 baud.
+ *
+ *	If the new baud rate is invalid, try the old termios setting.
+ *	If it's still invalid, we try 9600 baud.
+ *
+ *	Update the @termios structure to reflect the baud rate
+ *	we're actually going to be using. Don't do this for the case
+ *	where B0 is requested ("hang up").
+ */
+unsigned int
+uart_get_baud_rate(struct uart_port *port, struct ktermios *termios,
+		   struct ktermios *old, unsigned int min, unsigned int max)
+{
+	unsigned int try, baud, altbaud = 38400;
+	int hung_up = 0;
+	upf_t flags = port->flags & UPF_SPD_MASK;
+
+	if (flags == UPF_SPD_HI)
+		altbaud = 57600;
+	else if (flags == UPF_SPD_VHI)
+		altbaud = 115200;
+	else if (flags == UPF_SPD_SHI)
+		altbaud = 230400;
+	else if (flags == UPF_SPD_WARP)
+		altbaud = 460800;
+
+	for (try = 0; try < 2; try++) {
+		baud = tty_termios_baud_rate(termios);
+
+		/*
+		 * The spd_hi, spd_vhi, spd_shi, spd_warp kludge...
+		 * Die! Die! Die!
+		 */
+		if (try == 0 && baud == 38400)
+			baud = altbaud;
+
+		/*
+		 * Special case: B0 rate.
+		 */
+		if (baud == 0) {
+			hung_up = 1;
+			baud = 9600;
+		}
+
+		if (baud >= min && baud <= max)
+			return baud;
+
+		/*
+		 * Oops, the quotient was zero.  Try again with
+		 * the old baud rate if possible.
+		 */
+		termios->c_cflag &= ~CBAUD;
+		if (old) {
+			baud = tty_termios_baud_rate(old);
+			if (!hung_up)
+				tty_termios_encode_baud_rate(termios,
+								baud, baud);
+			old = NULL;
+			continue;
+		}
+
+		/*
+		 * As a last resort, if the range cannot be met then clip to
+		 * the nearest chip supported rate.
+		 */
+		if (!hung_up) {
+			if (baud <= min)
+				tty_termios_encode_baud_rate(termios,
+							min + 1, min + 1);
+			else
+				tty_termios_encode_baud_rate(termios,
+							max - 1, max - 1);
+		}
+	}
+	/* Should never happen */
+	WARN_ON(1);
+	return 0;
+}
+
+EXPORT_SYMBOL(uart_get_baud_rate);
+
+/**
+ *	uart_get_divisor - return uart clock divisor
+ *	@port: uart_port structure describing the port.
+ *	@baud: desired baud rate
+ *
+ *	Calculate the uart clock divisor for the port.
+ */
+unsigned int
+uart_get_divisor(struct uart_port *port, unsigned int baud)
+{
+	unsigned int quot;
+
+	/*
+	 * Old custom speed handling.
+	 */
+	if (baud == 38400 && (port->flags & UPF_SPD_MASK) == UPF_SPD_CUST)
+		quot = port->custom_divisor;
+	else
+		quot = DIV_ROUND_CLOSEST(port->uartclk, 16 * baud);
+
+	return quot;
+}
+
+EXPORT_SYMBOL(uart_get_divisor);
+
+/* FIXME: Consistent locking policy */
+static void uart_change_speed(struct tty_struct *tty, struct uart_state *state,
+					struct ktermios *old_termios)
+{
+	struct tty_port *port = &state->port;
+	struct uart_port *uport = state->uart_port;
+	struct ktermios *termios;
+
+	/*
+	 * If we have no tty, termios, or the port does not exist,
+	 * then we can't set the parameters for this port.
+	 */
+	if (!tty || !tty->termios || uport->type == PORT_UNKNOWN)
+		return;
+
+	termios = tty->termios;
+
+	/*
+	 * Set flags based on termios cflag
+	 */
+	if (termios->c_cflag & CRTSCTS)
+		set_bit(ASYNCB_CTS_FLOW, &port->flags);
+	else
+		clear_bit(ASYNCB_CTS_FLOW, &port->flags);
+
+	if (termios->c_cflag & CLOCAL)
+		clear_bit(ASYNCB_CHECK_CD, &port->flags);
+	else
+		set_bit(ASYNCB_CHECK_CD, &port->flags);
+
+	uport->ops->set_termios(uport, termios, old_termios);
+}
+
+static inline int __uart_put_char(struct uart_port *port,
+				struct circ_buf *circ, unsigned char c)
+{
+	unsigned long flags;
+	int ret = 0;
+
+	if (!circ->buf)
+		return 0;
+
+	raw_spin_lock_irqsave(&port->lock, flags);
+	if (uart_circ_chars_free(circ) != 0) {
+		circ->buf[circ->head] = c;
+		circ->head = (circ->head + 1) & (UART_XMIT_SIZE - 1);
+		ret = 1;
+	}
+	raw_spin_unlock_irqrestore(&port->lock, flags);
+	return ret;
+}
+
+static int uart_put_char(struct tty_struct *tty, unsigned char ch)
+{
+	struct uart_state *state = tty->driver_data;
+
+	return __uart_put_char(state->uart_port, &state->xmit, ch);
+}
+
+static void uart_flush_chars(struct tty_struct *tty)
+{
+	uart_start(tty);
+}
+
+static int uart_write(struct tty_struct *tty,
+					const unsigned char *buf, int count)
+{
+	struct uart_state *state = tty->driver_data;
+	struct uart_port *port;
+	struct circ_buf *circ;
+	struct platform_device *pdev;
+	struct zx29_uart_platdata *pdata;
+	unsigned long flags;
+	int c, ret = 0;
+
+	/*
+	 * This means you called this function _after_ the port was
+	 * closed.  No cookie for you.
+	 */
+	if (!state) {
+		WARN_ON(1);
+		return -EL3HLT;
+	}
+	
+	port = state->uart_port;	
+	if(!port){
+		BUG_ON(1);
+	}
+	pdev = port->private_data;
+	if(!pdev){
+		BUG_ON(1);
+	}
+
+	pdata = pdev->dev.platform_data;
+	if(!pdata){
+		BUG_ON(1);
+	}	
+	//port = state->uart_port;
+
+
+	raw_spin_lock_irqsave(&port->lock, flags);
+	//maybe application close this port, so circ->buf maybe NULL
+	circ = &state->xmit;
+
+	if (!circ->buf){		
+		raw_spin_unlock_irqrestore(&port->lock, flags);
+		return count;	
+	}
+	while (1) {
+		c = CIRC_SPACE_TO_END(circ->head, circ->tail, UART_XMIT_SIZE);
+		/*if uart ctsrts enabled and uart is not CONSOLE, then if buf is empty and the other side's
+		*ctsrts not enabled, return EBUSY */
+		if((pdata->uart_ctsrtsuse) && (pdev->id != DEBUG_CONSOLE)){
+			if((!c) && (tty->hw_stopped == 1)){
+				printk(KERN_INFO"uart_write, %s buf is empty or get CTS change\n",tty->name);
+				ret = -EBUSY;
+				break;
+			}
+		}
+		/*if uart sofrware control enabled and uart is not CONSOLE, then if buf is empty and the other side's
+		*ctsrts not enabled, return EBUSY */
+		if (I_IXON(tty) && (pdev->id != DEBUG_CONSOLE)){
+			if((!c) && (tty->stopped == 1)){
+				printk(KERN_INFO"uart_write, %s buf is empty or get XOFF\n",tty->name);
+				ret = -EBUSY;
+				break;
+			}
+		}
+		if (count < c)
+			c = count;
+		if (c <= 0)
+			break;
+		memcpy(circ->buf + circ->head, buf, c);
+		circ->head = (circ->head + c) & (UART_XMIT_SIZE - 1);
+		buf += c;
+		count -= c;
+		ret += c;
+	}
+	raw_spin_unlock_irqrestore(&port->lock, flags);
+
+	uart_start(tty);
+	return ret;
+}
+
+static int uart_write_room(struct tty_struct *tty)
+{
+	struct uart_state *state = tty->driver_data;
+	unsigned long flags;
+	int ret;
+
+	raw_spin_lock_irqsave(&state->uart_port->lock, flags);
+	ret = uart_circ_chars_free(&state->xmit);
+	raw_spin_unlock_irqrestore(&state->uart_port->lock, flags);
+	return ret;
+}
+
+static int uart_chars_in_buffer(struct tty_struct *tty)
+{
+	struct uart_state *state = tty->driver_data;
+	unsigned long flags;
+	int ret;
+
+	raw_spin_lock_irqsave(&state->uart_port->lock, flags);
+	ret = uart_circ_chars_pending(&state->xmit);
+	raw_spin_unlock_irqrestore(&state->uart_port->lock, flags);
+	return ret;
+}
+
+static void uart_flush_buffer(struct tty_struct *tty)
+{
+	struct uart_state *state = tty->driver_data;
+	struct uart_port *port;
+	unsigned long flags;
+
+	/*
+	 * This means you called this function _after_ the port was
+	 * closed.  No cookie for you.
+	 */
+	if (!state) {
+		WARN_ON(1);
+		return;
+	}
+
+	port = state->uart_port;
+	pr_debug("uart_flush_buffer(%d) called\n", tty->index);
+
+	raw_spin_lock_irqsave(&port->lock, flags);
+	uart_circ_clear(&state->xmit);
+	if (port->ops->flush_buffer)
+		port->ops->flush_buffer(port);
+	raw_spin_unlock_irqrestore(&port->lock, flags);
+	tty_wakeup(tty);
+}
+
+/*
+ * This function is used to send a high-priority XON/XOFF character to
+ * the device
+ */
+static void uart_send_xchar(struct tty_struct *tty, char ch)
+{
+	struct uart_state *state = tty->driver_data;
+	struct uart_port *port = state->uart_port;
+	unsigned long flags;
+
+	if (port->ops->send_xchar)
+		port->ops->send_xchar(port, ch);
+	else {
+		port->x_char = ch;
+		if (ch) {
+			raw_spin_lock_irqsave(&port->lock, flags);
+			port->ops->start_tx(port);
+			raw_spin_unlock_irqrestore(&port->lock, flags);
+		}
+	}
+}
+
+static void uart_throttle(struct tty_struct *tty)
+{
+	struct uart_state *state = tty->driver_data;
+
+	if (I_IXOFF(tty))
+		uart_send_xchar(tty, STOP_CHAR(tty));
+
+	if (tty->termios->c_cflag & CRTSCTS)
+		uart_clear_mctrl(state->uart_port, TIOCM_RTS);
+}
+
+static void uart_unthrottle(struct tty_struct *tty)
+{
+	struct uart_state *state = tty->driver_data;
+	struct uart_port *port = state->uart_port;
+
+	if (I_IXOFF(tty)) {
+		if (port->x_char)
+			port->x_char = 0;
+		else
+			uart_send_xchar(tty, START_CHAR(tty));
+	}
+
+	if (tty->termios->c_cflag & CRTSCTS)
+		uart_set_mctrl(port, TIOCM_RTS);
+}
+
+static int uart_get_info(struct uart_state *state,
+			 struct serial_struct __user *retinfo)
+{
+	struct uart_port *uport = state->uart_port;
+	struct tty_port *port = &state->port;
+	struct serial_struct tmp;
+
+	memset(&tmp, 0, sizeof(tmp));
+
+	/* Ensure the state we copy is consistent and no hardware changes
+	   occur as we go */
+	mutex_lock(&port->mutex);
+
+	tmp.type	    = uport->type;
+	tmp.line	    = uport->line;
+	tmp.port	    = uport->iobase;
+	if (HIGH_BITS_OFFSET)
+		tmp.port_high = (long) uport->iobase >> HIGH_BITS_OFFSET;
+	tmp.irq		    = uport->irq;
+	tmp.flags	    = uport->flags;
+	tmp.xmit_fifo_size  = uport->fifosize;
+	tmp.baud_base	    = uport->uartclk / 16;
+	tmp.close_delay	    = jiffies_to_msecs(port->close_delay) / 10;
+	tmp.closing_wait    = port->closing_wait == ASYNC_CLOSING_WAIT_NONE ?
+				ASYNC_CLOSING_WAIT_NONE :
+				jiffies_to_msecs(port->closing_wait) / 10;
+	tmp.custom_divisor  = uport->custom_divisor;
+	tmp.hub6	    = uport->hub6;
+	tmp.io_type         = uport->iotype;
+	tmp.iomem_reg_shift = uport->regshift;
+	tmp.iomem_base      = (void *)(unsigned long)uport->mapbase;
+
+	mutex_unlock(&port->mutex);
+
+	if (copy_to_user(retinfo, &tmp, sizeof(*retinfo)))
+		return -EFAULT;
+	return 0;
+}
+
+static int uart_set_info(struct tty_struct *tty, struct uart_state *state,
+			 struct serial_struct __user *newinfo)
+{
+	struct serial_struct new_serial;
+	struct uart_port *uport = state->uart_port;
+	struct tty_port *port = &state->port;
+	unsigned long new_port;
+	unsigned int change_irq, change_port, closing_wait;
+	unsigned int old_custom_divisor, close_delay;
+	upf_t old_flags, new_flags;
+	int retval = 0;
+
+	if (copy_from_user(&new_serial, newinfo, sizeof(new_serial)))
+		return -EFAULT;
+
+	new_port = new_serial.port;
+	if (HIGH_BITS_OFFSET)
+		new_port += (unsigned long) new_serial.port_high << HIGH_BITS_OFFSET;
+
+	new_serial.irq = irq_canonicalize(new_serial.irq);
+	close_delay = msecs_to_jiffies(new_serial.close_delay * 10);
+	closing_wait = new_serial.closing_wait == ASYNC_CLOSING_WAIT_NONE ?
+			ASYNC_CLOSING_WAIT_NONE :
+			msecs_to_jiffies(new_serial.closing_wait * 10);
+
+	/*
+	 * This semaphore protects port->count.  It is also
+	 * very useful to prevent opens.  Also, take the
+	 * port configuration semaphore to make sure that a
+	 * module insertion/removal doesn't change anything
+	 * under us.
+	 */
+	mutex_lock(&port->mutex);
+
+	change_irq  = !(uport->flags & UPF_FIXED_PORT)
+		&& new_serial.irq != uport->irq;
+
+	/*
+	 * Since changing the 'type' of the port changes its resource
+	 * allocations, we should treat type changes the same as
+	 * IO port changes.
+	 */
+	change_port = !(uport->flags & UPF_FIXED_PORT)
+		&& (new_port != uport->iobase ||
+		    (unsigned long)new_serial.iomem_base != uport->mapbase ||
+		    new_serial.hub6 != uport->hub6 ||
+		    new_serial.io_type != uport->iotype ||
+		    new_serial.iomem_reg_shift != uport->regshift ||
+		    new_serial.type != uport->type);
+
+	old_flags = uport->flags;
+	new_flags = new_serial.flags;
+	old_custom_divisor = uport->custom_divisor;
+
+	if (!capable(CAP_SYS_ADMIN)) {
+		retval = -EPERM;
+		if (change_irq || change_port ||
+		    (new_serial.baud_base != uport->uartclk / 16) ||
+		    (close_delay != port->close_delay) ||
+		    (closing_wait != port->closing_wait) ||
+		    (new_serial.xmit_fifo_size &&
+		     new_serial.xmit_fifo_size != uport->fifosize) ||
+		    (((new_flags ^ old_flags) & ~UPF_USR_MASK) != 0))
+			goto exit;
+		uport->flags = ((uport->flags & ~UPF_USR_MASK) |
+			       (new_flags & UPF_USR_MASK));
+		uport->custom_divisor = new_serial.custom_divisor;
+		goto check_and_exit;
+	}
+
+	/*
+	 * Ask the low level driver to verify the settings.
+	 */
+	if (uport->ops->verify_port)
+		retval = uport->ops->verify_port(uport, &new_serial);
+
+	if ((new_serial.irq >= nr_irqs) || (new_serial.irq < 0) ||
+	    (new_serial.baud_base < 9600))
+		retval = -EINVAL;
+
+	if (retval)
+		goto exit;
+
+	if (change_port || change_irq) {
+		retval = -EBUSY;
+
+		/*
+		 * Make sure that we are the sole user of this port.
+		 */
+		if (tty_port_users(port) > 1)
+			goto exit;
+
+		/*
+		 * We need to shutdown the serial port at the old
+		 * port/type/irq combination.
+		 */
+		uart_shutdown(tty, state);
+	}
+
+	if (change_port) {
+		unsigned long old_iobase, old_mapbase;
+		unsigned int old_type, old_iotype, old_hub6, old_shift;
+
+		old_iobase = uport->iobase;
+		old_mapbase = uport->mapbase;
+		old_type = uport->type;
+		old_hub6 = uport->hub6;
+		old_iotype = uport->iotype;
+		old_shift = uport->regshift;
+
+		/*
+		 * Free and release old regions
+		 */
+		if (old_type != PORT_UNKNOWN)
+			uport->ops->release_port(uport);
+
+		uport->iobase = new_port;
+		uport->type = new_serial.type;
+		uport->hub6 = new_serial.hub6;
+		uport->iotype = new_serial.io_type;
+		uport->regshift = new_serial.iomem_reg_shift;
+		uport->mapbase = (unsigned long)new_serial.iomem_base;
+
+		/*
+		 * Claim and map the new regions
+		 */
+		if (uport->type != PORT_UNKNOWN) {
+			retval = uport->ops->request_port(uport);
+		} else {
+			/* Always success - Jean II */
+			retval = 0;
+		}
+
+		/*
+		 * If we fail to request resources for the
+		 * new port, try to restore the old settings.
+		 */
+		if (retval && old_type != PORT_UNKNOWN) {
+			uport->iobase = old_iobase;
+			uport->type = old_type;
+			uport->hub6 = old_hub6;
+			uport->iotype = old_iotype;
+			uport->regshift = old_shift;
+			uport->mapbase = old_mapbase;
+			retval = uport->ops->request_port(uport);
+			/*
+			 * If we failed to restore the old settings,
+			 * we fail like this.
+			 */
+			if (retval)
+				uport->type = PORT_UNKNOWN;
+
+			/*
+			 * We failed anyway.
+			 */
+			retval = -EBUSY;
+			/* Added to return the correct error -Ram Gupta */
+			goto exit;
+		}
+	}
+
+	if (change_irq)
+		uport->irq      = new_serial.irq;
+	if (!(uport->flags & UPF_FIXED_PORT))
+		uport->uartclk  = new_serial.baud_base * 16;
+	uport->flags            = (uport->flags & ~UPF_CHANGE_MASK) |
+				 (new_flags & UPF_CHANGE_MASK);
+	uport->custom_divisor   = new_serial.custom_divisor;
+	port->close_delay     = close_delay;
+	port->closing_wait    = closing_wait;
+	if (new_serial.xmit_fifo_size)
+		uport->fifosize = new_serial.xmit_fifo_size;
+	if (port->tty)
+		port->tty->low_latency =
+			(uport->flags & UPF_LOW_LATENCY) ? 1 : 0;
+
+ check_and_exit:
+	retval = 0;
+	if (uport->type == PORT_UNKNOWN)
+		goto exit;
+	if (port->flags & ASYNC_INITIALIZED) {
+		if (((old_flags ^ uport->flags) & UPF_SPD_MASK) ||
+		    old_custom_divisor != uport->custom_divisor) {
+			/*
+			 * If they're setting up a custom divisor or speed,
+			 * instead of clearing it, then bitch about it. No
+			 * need to rate-limit; it's CAP_SYS_ADMIN only.
+			 */
+			if (uport->flags & UPF_SPD_MASK) {
+				char buf[64];
+				printk(KERN_NOTICE
+				       "%s sets custom speed on %s. This "
+				       "is deprecated.\n", current->comm,
+				       tty_name(port->tty, buf));
+			}
+			uart_change_speed(tty, state, NULL);
+		}
+	} else
+		retval = uart_startup(tty, state, 1);
+ exit:
+	mutex_unlock(&port->mutex);
+	return retval;
+}
+
+/**
+ *	uart_get_lsr_info	-	get line status register info
+ *	@tty: tty associated with the UART
+ *	@state: UART being queried
+ *	@value: returned modem value
+ *
+ *	Note: uart_ioctl protects us against hangups.
+ */
+static int uart_get_lsr_info(struct tty_struct *tty,
+			struct uart_state *state, unsigned int __user *value)
+{
+	struct uart_port *uport = state->uart_port;
+	unsigned int result;
+
+	result = uport->ops->tx_empty(uport);
+
+	/*
+	 * If we're about to load something into the transmit
+	 * register, we'll pretend the transmitter isn't empty to
+	 * avoid a race condition (depending on when the transmit
+	 * interrupt happens).
+	 */
+	if (uport->x_char ||
+	    ((uart_circ_chars_pending(&state->xmit) > 0) &&
+	     !tty->stopped && !tty->hw_stopped))
+		result &= ~TIOCSER_TEMT;
+
+	return put_user(result, value);
+}
+
+static int uart_tiocmget(struct tty_struct *tty)
+{
+	struct uart_state *state = tty->driver_data;
+	struct tty_port *port = &state->port;
+	struct uart_port *uport = state->uart_port;
+	int result = -EIO;
+
+	mutex_lock(&port->mutex);
+	if (!(tty->flags & (1 << TTY_IO_ERROR))) {
+		result = uport->mctrl;
+		raw_spin_lock_irq(&uport->lock);
+		result |= uport->ops->get_mctrl(uport);
+		raw_spin_unlock_irq(&uport->lock);
+	}
+	mutex_unlock(&port->mutex);
+
+	return result;
+}
+
+static int
+uart_tiocmset(struct tty_struct *tty, unsigned int set, unsigned int clear)
+{
+	struct uart_state *state = tty->driver_data;
+	struct uart_port *uport = state->uart_port;
+	struct tty_port *port = &state->port;
+	int ret = -EIO;
+
+	mutex_lock(&port->mutex);
+	if (!(tty->flags & (1 << TTY_IO_ERROR))) {
+		uart_update_mctrl(uport, set, clear);
+		ret = 0;
+	}
+	mutex_unlock(&port->mutex);
+	return ret;
+}
+
+static int uart_break_ctl(struct tty_struct *tty, int break_state)
+{
+	struct uart_state *state = tty->driver_data;
+	struct tty_port *port = &state->port;
+	struct uart_port *uport = state->uart_port;
+
+	mutex_lock(&port->mutex);
+
+	if (uport->type != PORT_UNKNOWN)
+		uport->ops->break_ctl(uport, break_state);
+
+	mutex_unlock(&port->mutex);
+	return 0;
+}
+
+static int uart_do_autoconfig(struct tty_struct *tty,struct uart_state *state)
+{
+	struct uart_port *uport = state->uart_port;
+	struct tty_port *port = &state->port;
+	int flags, ret;
+
+	if (!capable(CAP_SYS_ADMIN))
+		return -EPERM;
+
+	/*
+	 * Take the per-port semaphore.  This prevents count from
+	 * changing, and hence any extra opens of the port while
+	 * we're auto-configuring.
+	 */
+	if (mutex_lock_interruptible(&port->mutex))
+		return -ERESTARTSYS;
+
+	ret = -EBUSY;
+	if (tty_port_users(port) == 1) {
+		uart_shutdown(tty, state);
+
+		/*
+		 * If we already have a port type configured,
+		 * we must release its resources.
+		 */
+		if (uport->type != PORT_UNKNOWN)
+			uport->ops->release_port(uport);
+
+		flags = UART_CONFIG_TYPE;
+		if (uport->flags & UPF_AUTO_IRQ)
+			flags |= UART_CONFIG_IRQ;
+
+		/*
+		 * This will claim the ports resources if
+		 * a port is found.
+		 */
+		uport->ops->config_port(uport, flags);
+
+		ret = uart_startup(tty, state, 1);
+	}
+	mutex_unlock(&port->mutex);
+	return ret;
+}
+
+/*
+ * Wait for any of the 4 modem inputs (DCD,RI,DSR,CTS) to change
+ * - mask passed in arg for lines of interest
+ *   (use |'ed TIOCM_RNG/DSR/CD/CTS for masking)
+ * Caller should use TIOCGICOUNT to see which one it was
+ *
+ * FIXME: This wants extracting into a common all driver implementation
+ * of TIOCMWAIT using tty_port.
+ */
+static int
+uart_wait_modem_status(struct uart_state *state, unsigned long arg)
+{
+	struct uart_port *uport = state->uart_port;
+	struct tty_port *port = &state->port;
+	DECLARE_WAITQUEUE(wait, current);
+	struct uart_icount cprev, cnow;
+	int ret;
+
+	/*
+	 * note the counters on entry
+	 */
+	raw_spin_lock_irq(&uport->lock);
+	memcpy(&cprev, &uport->icount, sizeof(struct uart_icount));
+
+	/*
+	 * Force modem status interrupts on
+	 */
+	uport->ops->enable_ms(uport);
+	raw_spin_unlock_irq(&uport->lock);
+
+	add_wait_queue(&port->delta_msr_wait, &wait);
+	for (;;) {
+		raw_spin_lock_irq(&uport->lock);
+		memcpy(&cnow, &uport->icount, sizeof(struct uart_icount));
+		raw_spin_unlock_irq(&uport->lock);
+
+		set_current_state(TASK_INTERRUPTIBLE);
+
+		if (((arg & TIOCM_RNG) && (cnow.rng != cprev.rng)) ||
+		    ((arg & TIOCM_DSR) && (cnow.dsr != cprev.dsr)) ||
+		    ((arg & TIOCM_CD)  && (cnow.dcd != cprev.dcd)) ||
+		    ((arg & TIOCM_CTS) && (cnow.cts != cprev.cts))) {
+			ret = 0;
+			break;
+		}
+
+		schedule();
+
+		/* see if a signal did it */
+		if (signal_pending(current)) {
+			ret = -ERESTARTSYS;
+			break;
+		}
+
+		cprev = cnow;
+	}
+
+	current->state = TASK_RUNNING;
+	remove_wait_queue(&port->delta_msr_wait, &wait);
+
+	return ret;
+}
+
+/*
+ * Get counter of input serial line interrupts (DCD,RI,DSR,CTS)
+ * Return: write counters to the user passed counter struct
+ * NB: both 1->0 and 0->1 transitions are counted except for
+ *     RI where only 0->1 is counted.
+ */
+static int uart_get_icount(struct tty_struct *tty,
+			  struct serial_icounter_struct *icount)
+{
+	struct uart_state *state = tty->driver_data;
+	struct uart_icount cnow;
+	struct uart_port *uport = state->uart_port;
+
+	raw_spin_lock_irq(&uport->lock);
+	memcpy(&cnow, &uport->icount, sizeof(struct uart_icount));
+	raw_spin_unlock_irq(&uport->lock);
+
+	icount->cts         = cnow.cts;
+	icount->dsr         = cnow.dsr;
+	icount->rng         = cnow.rng;
+	icount->dcd         = cnow.dcd;
+	icount->rx          = cnow.rx;
+	icount->tx          = cnow.tx;
+	icount->frame       = cnow.frame;
+	icount->overrun     = cnow.overrun;
+	icount->parity      = cnow.parity;
+	icount->brk         = cnow.brk;
+	icount->buf_overrun = cnow.buf_overrun;
+
+	return 0;
+}
+
+/*
+ * Called via sys_ioctl.  We can use spin_lock_irq() here.
+ */
+static int
+uart_ioctl(struct tty_struct *tty, unsigned int cmd,
+	   unsigned long arg)
+{
+	struct uart_state *state = tty->driver_data;
+	struct tty_port *port = &state->port;
+	void __user *uarg = (void __user *)arg;
+	int ret = -ENOIOCTLCMD;
+
+
+	/*
+	 * These ioctls don't rely on the hardware to be present.
+	 */
+	switch (cmd) {
+	case TIOCGSERIAL:
+		ret = uart_get_info(state, uarg);
+		break;
+
+	case TIOCSSERIAL:
+		ret = uart_set_info(tty, state, uarg);
+		break;
+
+	case TIOCSERCONFIG:
+		ret = uart_do_autoconfig(tty, state);
+		break;
+
+	case TIOCSERGWILD: /* obsolete */
+	case TIOCSERSWILD: /* obsolete */
+		ret = 0;
+		break;
+	}
+
+	if (ret != -ENOIOCTLCMD)
+		goto out;
+
+	if (tty->flags & (1 << TTY_IO_ERROR)) {
+		ret = -EIO;
+		goto out;
+	}
+
+	/*
+	 * The following should only be used when hardware is present.
+	 */
+	switch (cmd) {
+	case TIOCMIWAIT:
+		ret = uart_wait_modem_status(state, arg);
+		break;
+	}
+
+	if (ret != -ENOIOCTLCMD)
+		goto out;
+
+	mutex_lock(&port->mutex);
+
+	if (tty->flags & (1 << TTY_IO_ERROR)) {
+		ret = -EIO;
+		goto out_up;
+	}
+
+	/*
+	 * All these rely on hardware being present and need to be
+	 * protected against the tty being hung up.
+	 */
+	switch (cmd) {
+	case TIOCSERGETLSR: /* Get line status register */
+		ret = uart_get_lsr_info(tty, state, uarg);
+		break;
+
+	default: {
+		struct uart_port *uport = state->uart_port;
+		if (uport->ops->ioctl)
+			ret = uport->ops->ioctl(uport, cmd, arg);
+		break;
+	}
+	}
+out_up:
+	mutex_unlock(&port->mutex);
+out:
+	return ret;
+}
+
+static void uart_set_ldisc(struct tty_struct *tty)
+{
+	struct uart_state *state = tty->driver_data;
+	struct uart_port *uport = state->uart_port;
+
+	if (uport->ops->set_ldisc)
+		uport->ops->set_ldisc(uport, tty->termios->c_line);
+}
+
+static void uart_set_termios(struct tty_struct *tty,
+						struct ktermios *old_termios)
+{
+	struct uart_state *state = tty->driver_data;
+	unsigned long flags;
+	unsigned int cflag = tty->termios->c_cflag;
+
+
+	/*
+	 * These are the bits that are used to setup various
+	 * flags in the low level driver. We can ignore the Bfoo
+	 * bits in c_cflag; c_[io]speed will always be set
+	 * appropriately by set_termios() in tty_ioctl.c
+	 */
+#define RELEVANT_IFLAG(iflag)	((iflag) & (IGNBRK|BRKINT|IGNPAR|PARMRK|INPCK))
+	if ((cflag ^ old_termios->c_cflag) == 0 &&
+	    tty->termios->c_ospeed == old_termios->c_ospeed &&
+	    tty->termios->c_ispeed == old_termios->c_ispeed &&
+	    RELEVANT_IFLAG(tty->termios->c_iflag ^ old_termios->c_iflag) == 0) {
+		return;
+	}
+
+	uart_change_speed(tty, state, old_termios);
+
+	/* Handle transition to B0 status */
+	if ((old_termios->c_cflag & CBAUD) && !(cflag & CBAUD))
+		uart_clear_mctrl(state->uart_port, TIOCM_RTS | TIOCM_DTR);
+	/* Handle transition away from B0 status */
+	else if (!(old_termios->c_cflag & CBAUD) && (cflag & CBAUD)) {
+		unsigned int mask = TIOCM_DTR;
+		if (!(cflag & CRTSCTS) ||
+		    !test_bit(TTY_THROTTLED, &tty->flags))
+			mask |= TIOCM_RTS;
+		uart_set_mctrl(state->uart_port, mask);
+	}
+
+	/* Handle turning off CRTSCTS */
+	if ((old_termios->c_cflag & CRTSCTS) && !(cflag & CRTSCTS)) {
+		raw_spin_lock_irqsave(&state->uart_port->lock, flags);
+		tty->hw_stopped = 0;
+		__uart_start(tty);
+		raw_spin_unlock_irqrestore(&state->uart_port->lock, flags);
+	}
+	/* Handle turning on CRTSCTS */
+	else if (!(old_termios->c_cflag & CRTSCTS) && (cflag & CRTSCTS)) {
+		raw_spin_lock_irqsave(&state->uart_port->lock, flags);
+		if (!(state->uart_port->ops->get_mctrl(state->uart_port) & TIOCM_CTS)) {
+			tty->hw_stopped = 1;
+			state->uart_port->ops->stop_tx(state->uart_port);
+		}
+		raw_spin_unlock_irqrestore(&state->uart_port->lock, flags);
+	}
+}
+#ifdef CONFIG_CPU_IDLE
+extern void uart_rxd_int_disable(struct uart_port *port);
+#endif
+/*
+ * In 2.4.5, calls to this will be serialized via the BKL in
+ *  linux/drivers/char/tty_io.c:tty_release()
+ *  linux/drivers/char/tty_io.c:do_tty_handup()
+ */
+static void uart_close(struct tty_struct *tty, struct file *filp)
+{
+	struct uart_state *state = tty->driver_data;
+	struct tty_port *port;
+	struct uart_port *uport;
+	unsigned long flags;
+
+	if (!state)
+		return;
+
+	uport = state->uart_port;
+	port = &state->port;
+
+	printk("uart_close(%d) called\n", uport->line);
+#ifdef CONFIG_CPU_IDLE	
+	if(uport->line == 0)
+	uart_rxd_int_disable(uport);
+#endif	
+	if (tty_port_close_start(port, tty, filp) == 0)
+		return;
+
+	/*
+	 * At this point, we stop accepting input.  To do this, we
+	 * disable the receive line status interrupts.
+	 */
+	if (port->flags & ASYNC_INITIALIZED) {
+		unsigned long flags;
+		raw_spin_lock_irqsave(&uport->lock, flags);
+		uport->ops->stop_rx(uport);
+		raw_spin_unlock_irqrestore(&uport->lock, flags);
+		/*
+		 * Before we drop DTR, make sure the UART transmitter
+		 * has completely drained; this is especially
+		 * important if there is a transmit FIFO!
+		 */
+		uart_wait_until_sent(tty, uport->timeout);
+	}
+
+	mutex_lock(&port->mutex);
+	uart_shutdown(tty, state);
+	uart_flush_buffer(tty);
+
+	tty_ldisc_flush(tty);
+
+	tty_port_tty_set(port, NULL);
+	raw_spin_lock_irqsave(&port->lock, flags);
+	tty->closing = 0;
+
+	if (port->blocked_open) {
+		raw_spin_unlock_irqrestore(&port->lock, flags);
+		if (port->close_delay)
+			msleep_interruptible(
+					jiffies_to_msecs(port->close_delay));
+		raw_spin_lock_irqsave(&port->lock, flags);
+	} else if (!uart_console(uport)) {
+		raw_spin_unlock_irqrestore(&port->lock, flags);
+		uart_change_pm(state, 3);
+		raw_spin_lock_irqsave(&port->lock, flags);
+	}
+
+	/*
+	 * Wake up anyone trying to open this port.
+	 */
+	clear_bit(ASYNCB_NORMAL_ACTIVE, &port->flags);
+	clear_bit(ASYNCB_CLOSING, &port->flags);
+	raw_spin_unlock_irqrestore(&port->lock, flags);
+	wake_up_interruptible(&port->open_wait);
+	wake_up_interruptible(&port->close_wait);
+
+	mutex_unlock(&port->mutex);
+}
+
+static void uart_wait_until_sent(struct tty_struct *tty, int timeout)
+{
+	struct uart_state *state = tty->driver_data;
+	struct uart_port *port = state->uart_port;
+	unsigned long char_time, expire;
+
+	if (port->type == PORT_UNKNOWN || port->fifosize == 0)
+		return;
+
+	/*
+	 * Set the check interval to be 1/5 of the estimated time to
+	 * send a single character, and make it at least 1.  The check
+	 * interval should also be less than the timeout.
+	 *
+	 * Note: we have to use pretty tight timings here to satisfy
+	 * the NIST-PCTS.
+	 */
+	char_time = (port->timeout - HZ/50) / port->fifosize;
+	char_time = char_time / 5;
+	if (char_time == 0)
+		char_time = 1;
+	if (timeout && timeout < char_time)
+		char_time = timeout;
+
+	/*
+	 * If the transmitter hasn't cleared in twice the approximate
+	 * amount of time to send the entire FIFO, it probably won't
+	 * ever clear.  This assumes the UART isn't doing flow
+	 * control, which is currently the case.  Hence, if it ever
+	 * takes longer than port->timeout, this is probably due to a
+	 * UART bug of some kind.  So, we clamp the timeout parameter at
+	 * 2*port->timeout.
+	 */
+	if (timeout == 0 || timeout > 2 * port->timeout)
+		timeout = 2 * port->timeout;
+
+	expire = jiffies + timeout;
+
+	pr_debug("uart_wait_until_sent(%d), jiffies=%lu, expire=%lu...\n",
+		port->line, jiffies, expire);
+
+	/*
+	 * Check whether the transmitter is empty every 'char_time'.
+	 * 'timeout' / 'expire' give us the maximum amount of time
+	 * we wait.
+	 */
+	while (!port->ops->tx_empty(port)) {
+		msleep_interruptible(jiffies_to_msecs(char_time));
+		if (signal_pending(current))
+			break;
+		if (time_after(jiffies, expire))
+			break;
+	}
+}
+
+/*
+ * This is called with the BKL held in
+ *  linux/drivers/char/tty_io.c:do_tty_hangup()
+ * We're called from the eventd thread, so we can sleep for
+ * a _short_ time only.
+ */
+static void uart_hangup(struct tty_struct *tty)
+{
+	struct uart_state *state = tty->driver_data;
+	struct tty_port *port = &state->port;
+	unsigned long flags;
+
+	pr_debug("uart_hangup(%d)\n", state->uart_port->line);
+
+	mutex_lock(&port->mutex);
+	if (port->flags & ASYNC_NORMAL_ACTIVE) {
+		uart_flush_buffer(tty);
+		uart_shutdown(tty, state);
+		raw_spin_lock_irqsave(&port->lock, flags);
+		port->count = 0;
+		clear_bit(ASYNCB_NORMAL_ACTIVE, &port->flags);
+		raw_spin_unlock_irqrestore(&port->lock, flags);
+		tty_port_tty_set(port, NULL);
+		wake_up_interruptible(&port->open_wait);
+		wake_up_interruptible(&port->delta_msr_wait);
+	}
+	mutex_unlock(&port->mutex);
+}
+
+static int uart_port_activate(struct tty_port *port, struct tty_struct *tty)
+{
+	return 0;
+}
+
+static void uart_port_shutdown(struct tty_port *port)
+{
+	struct uart_state *state = container_of(port, struct uart_state, port);
+	struct uart_port *uport = state->uart_port;
+
+	/*
+	 * clear delta_msr_wait queue to avoid mem leaks: we may free
+	 * the irq here so the queue might never be woken up.  Note
+	 * that we won't end up waiting on delta_msr_wait again since
+	 * any outstanding file descriptors should be pointing at
+	 * hung_up_tty_fops now.
+	 */
+	wake_up_interruptible(&port->delta_msr_wait);
+
+	/*
+	 * Free the IRQ and disable the port.
+	 */
+	uport->ops->shutdown(uport);
+
+	/*
+	 * Ensure that the IRQ handler isn't running on another CPU.
+	 */
+	synchronize_irq(uport->irq);
+}
+
+static int uart_carrier_raised(struct tty_port *port)
+{
+	struct uart_state *state = container_of(port, struct uart_state, port);
+	struct uart_port *uport = state->uart_port;
+	int mctrl;
+	raw_spin_lock_irq(&uport->lock);
+	uport->ops->enable_ms(uport);
+	mctrl = uport->ops->get_mctrl(uport);
+	raw_spin_unlock_irq(&uport->lock);
+	if (mctrl & TIOCM_CAR)
+		return 1;
+	return 0;
+}
+
+static void uart_dtr_rts(struct tty_port *port, int onoff)
+{
+	struct uart_state *state = container_of(port, struct uart_state, port);
+	struct uart_port *uport = state->uart_port;
+
+	if (onoff)
+		uart_set_mctrl(uport, TIOCM_DTR | TIOCM_RTS);
+	else
+		uart_clear_mctrl(uport, TIOCM_DTR | TIOCM_RTS);
+}
+
+/*
+ * calls to uart_open are serialised by the BKL in
+ *   fs/char_dev.c:chrdev_open()
+ * Note that if this fails, then uart_close() _will_ be called.
+ *
+ * In time, we want to scrap the "opening nonpresent ports"
+ * behaviour and implement an alternative way for setserial
+ * to set base addresses/ports/types.  This will allow us to
+ * get rid of a certain amount of extra tests.
+ */
+static int uart_open(struct tty_struct *tty, struct file *filp)
+{
+	struct uart_driver *drv = (struct uart_driver *)tty->driver->driver_state;
+	int retval, line = tty->index;
+	struct uart_state *state = drv->state + line;
+	struct tty_port *port = &state->port;
+
+	pr_debug("uart_open(%d) called\n", line);
+
+	/*
+	 * We take the semaphore here to guarantee that we won't be re-entered
+	 * while allocating the state structure, or while we request any IRQs
+	 * that the driver may need.  This also has the nice side-effect that
+	 * it delays the action of uart_hangup, so we can guarantee that
+	 * state->port.tty will always contain something reasonable.
+	 */
+	if (mutex_lock_interruptible(&port->mutex)) {
+		retval = -ERESTARTSYS;
+		goto end;
+	}
+
+	port->count++;
+	if (!state->uart_port || state->uart_port->flags & UPF_DEAD) {
+		retval = -ENXIO;
+		goto err_dec_count;
+	}
+
+	/*
+	 * Once we set tty->driver_data here, we are guaranteed that
+	 * uart_close() will decrement the driver module use count.
+	 * Any failures from here onwards should not touch the count.
+	 */
+	tty->driver_data = state;
+	state->uart_port->state = state;
+	tty->low_latency = (state->uart_port->flags & UPF_LOW_LATENCY) ? 1 : 0;
+	tty_port_tty_set(port, tty);
+
+	/*
+	 * If the port is in the middle of closing, bail out now.
+	 */
+	if (tty_hung_up_p(filp)) {
+		retval = -EAGAIN;
+		goto err_dec_count;
+	}
+
+	/*
+	 * Make sure the device is in D0 state.
+	 */
+	if (port->count == 1)
+		uart_change_pm(state, 0);
+
+	/*
+	 * Start up the serial port.
+	 */
+	retval = uart_startup(tty, state, 0);
+
+	/*
+	 * If we succeeded, wait until the port is ready.
+	 */
+	mutex_unlock(&port->mutex);
+	if (retval == 0)
+		retval = tty_port_block_til_ready(port, tty, filp);
+
+end:
+	return retval;
+err_dec_count:
+	port->count--;
+	mutex_unlock(&port->mutex);
+	goto end;
+}
+
+static const char *uart_type(struct uart_port *port)
+{
+	const char *str = NULL;
+
+	if (port->ops->type)
+		str = port->ops->type(port);
+
+	if (!str)
+		str = "unknown";
+
+	return str;
+}
+
+#ifdef CONFIG_PROC_FS
+
+static void uart_line_info(struct seq_file *m, struct uart_driver *drv, int i)
+{
+	struct uart_state *state = drv->state + i;
+	struct tty_port *port = &state->port;
+	int pm_state;
+	struct uart_port *uport = state->uart_port;
+	char stat_buf[32];
+	unsigned int status;
+	int mmio;
+
+	if (!uport)
+		return;
+
+	mmio = uport->iotype >= UPIO_MEM;
+	seq_printf(m, "%d: uart:%s %s%08llX irq:%d",
+			uport->line, uart_type(uport),
+			mmio ? "mmio:0x" : "port:",
+			mmio ? (unsigned long long)uport->mapbase
+			     : (unsigned long long)uport->iobase,
+			uport->irq);
+
+	if (uport->type == PORT_UNKNOWN) {
+		seq_putc(m, '\n');
+		return;
+	}
+
+	if (capable(CAP_SYS_ADMIN)) {
+		mutex_lock(&port->mutex);
+		pm_state = state->pm_state;
+		if (pm_state)
+			uart_change_pm(state, 0);
+		raw_spin_lock_irq(&uport->lock);
+		status = uport->ops->get_mctrl(uport);
+		raw_spin_unlock_irq(&uport->lock);
+		if (pm_state)
+			uart_change_pm(state, pm_state);
+		mutex_unlock(&port->mutex);
+
+		seq_printf(m, " tx:%d rx:%d",
+				uport->icount.tx, uport->icount.rx);
+		if (uport->icount.frame)
+			seq_printf(m, " fe:%d",
+				uport->icount.frame);
+		if (uport->icount.parity)
+			seq_printf(m, " pe:%d",
+				uport->icount.parity);
+		if (uport->icount.brk)
+			seq_printf(m, " brk:%d",
+				uport->icount.brk);
+		if (uport->icount.overrun)
+			seq_printf(m, " oe:%d",
+				uport->icount.overrun);
+
+#define INFOBIT(bit, str) \
+	if (uport->mctrl & (bit)) \
+		strncat(stat_buf, (str), sizeof(stat_buf) - \
+			strlen(stat_buf) - 2)
+#define STATBIT(bit, str) \
+	if (status & (bit)) \
+		strncat(stat_buf, (str), sizeof(stat_buf) - \
+		       strlen(stat_buf) - 2)
+
+		stat_buf[0] = '\0';
+		stat_buf[1] = '\0';
+		INFOBIT(TIOCM_RTS, "|RTS");
+		STATBIT(TIOCM_CTS, "|CTS");
+		INFOBIT(TIOCM_DTR, "|DTR");
+		STATBIT(TIOCM_DSR, "|DSR");
+		STATBIT(TIOCM_CAR, "|CD");
+		STATBIT(TIOCM_RNG, "|RI");
+		if (stat_buf[0])
+			stat_buf[0] = ' ';
+
+		seq_puts(m, stat_buf);
+	}
+	seq_putc(m, '\n');
+#undef STATBIT
+#undef INFOBIT
+}
+
+static int uart_proc_show(struct seq_file *m, void *v)
+{
+	struct tty_driver *ttydrv = m->private;
+	struct uart_driver *drv = ttydrv->driver_state;
+	int i;
+
+	seq_printf(m, "serinfo:1.0 driver%s%s revision:%s\n",
+			"", "", "");
+	for (i = 0; i < drv->nr; i++)
+		uart_line_info(m, drv, i);
+	return 0;
+}
+
+static int uart_proc_open(struct inode *inode, struct file *file)
+{
+	return single_open(file, uart_proc_show, PDE(inode)->data);
+}
+
+static const struct file_operations uart_proc_fops = {
+	.owner		= THIS_MODULE,
+	.open		= uart_proc_open,
+	.read		= seq_read,
+	.llseek		= seq_lseek,
+	.release	= single_release,
+};
+#endif
+
+#if defined(CONFIG_SERIAL_CORE_CONSOLE) || defined(CONFIG_CONSOLE_POLL)
+/*
+ *	uart_console_write - write a console message to a serial port
+ *	@port: the port to write the message
+ *	@s: array of characters
+ *	@count: number of characters in string to write
+ *	@write: function to write character to port
+ */
+void uart_console_write(struct uart_port *port, const char *s,
+			unsigned int count,
+			void (*putchar)(struct uart_port *, int))
+{
+	unsigned int i;
+
+	for (i = 0; i < count; i++, s++) {
+		if (*s == '\n')
+			putchar(port, '\r');
+		putchar(port, *s);
+	}
+}
+EXPORT_SYMBOL_GPL(uart_console_write);
+
+/*
+ *	Check whether an invalid uart number has been specified, and
+ *	if so, search for the first available port that does have
+ *	console support.
+ */
+struct uart_port * __init
+uart_get_console(struct uart_port *ports, int nr, struct console *co)
+{
+	int idx = co->index;
+
+	if (idx < 0 || idx >= nr || (ports[idx].iobase == 0 &&
+				     ports[idx].membase == NULL))
+		for (idx = 0; idx < nr; idx++)
+			if (ports[idx].iobase != 0 ||
+			    ports[idx].membase != NULL)
+				break;
+
+	co->index = idx;
+
+	return ports + idx;
+}
+
+/**
+ *	uart_parse_options - Parse serial port baud/parity/bits/flow contro.
+ *	@options: pointer to option string
+ *	@baud: pointer to an 'int' variable for the baud rate.
+ *	@parity: pointer to an 'int' variable for the parity.
+ *	@bits: pointer to an 'int' variable for the number of data bits.
+ *	@flow: pointer to an 'int' variable for the flow control character.
+ *
+ *	uart_parse_options decodes a string containing the serial console
+ *	options.  The format of the string is <baud><parity><bits><flow>,
+ *	eg: 115200n8r
+ */
+void
+uart_parse_options(char *options, int *baud, int *parity, int *bits, int *flow)
+{
+	char *s = options;
+
+	*baud = simple_strtoul(s, NULL, 10);
+	while (*s >= '0' && *s <= '9')
+		s++;
+	if (*s)
+		*parity = *s++;
+	if (*s)
+		*bits = *s++ - '0';
+	if (*s)
+		*flow = *s;
+}
+EXPORT_SYMBOL_GPL(uart_parse_options);
+
+struct baud_rates {
+	unsigned int rate;
+	unsigned int cflag;
+};
+
+static const struct baud_rates baud_rates[] = {
+	{ 921600, B921600 },
+	{ 460800, B460800 },
+	{ 230400, B230400 },
+	{ 115200, B115200 },
+	{  57600, B57600  },
+	{  38400, B38400  },
+	{  19200, B19200  },
+	{   9600, B9600   },
+	{   4800, B4800   },
+	{   2400, B2400   },
+	{   1200, B1200   },
+	{      0, B38400  }
+};
+
+/**
+ *	uart_set_options - setup the serial console parameters
+ *	@port: pointer to the serial ports uart_port structure
+ *	@co: console pointer
+ *	@baud: baud rate
+ *	@parity: parity character - 'n' (none), 'o' (odd), 'e' (even)
+ *	@bits: number of data bits
+ *	@flow: flow control character - 'r' (rts)
+ */
+int
+uart_set_options(struct uart_port *port, struct console *co,
+		 int baud, int parity, int bits, int flow)
+{
+	struct ktermios termios;
+	static struct ktermios dummy;
+	int i;
+
+	/*
+	 * Ensure that the serial console lock is initialised
+	 * early.
+	 */
+	raw_spin_lock_init(&port->lock);
+	lockdep_set_class(&port->lock, &port_lock_key);
+
+	memset(&termios, 0, sizeof(struct ktermios));
+
+	termios.c_cflag = CREAD | HUPCL | CLOCAL;
+
+	/*
+	 * Construct a cflag setting.
+	 */
+	for (i = 0; baud_rates[i].rate; i++)
+		if (baud_rates[i].rate <= baud)
+			break;
+
+	termios.c_cflag |= baud_rates[i].cflag;
+
+	if (bits == 7)
+		termios.c_cflag |= CS7;
+	else
+		termios.c_cflag |= CS8;
+
+	switch (parity) {
+	case 'o': case 'O':
+		termios.c_cflag |= PARODD;
+		/*fall through*/
+	case 'e': case 'E':
+		termios.c_cflag |= PARENB;
+		break;
+	}
+
+	if (flow == 'r')
+		termios.c_cflag |= CRTSCTS;
+
+	/*
+	 * some uarts on other side don't support no flow control.
+	 * So we set * DTR in host uart to make them happy
+	 */
+	port->mctrl |= TIOCM_DTR;
+
+	port->ops->set_termios(port, &termios, &dummy);
+	/*
+	 * Allow the setting of the UART parameters with a NULL console
+	 * too:
+	 */
+	if (co)
+		co->cflag = termios.c_cflag;
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(uart_set_options);
+#endif /* CONFIG_SERIAL_CORE_CONSOLE */
+
+/**
+ * uart_change_pm - set power state of the port
+ *
+ * @state: port descriptor
+ * @pm_state: new state
+ *
+ * Locking: port->mutex has to be held
+ */
+static void uart_change_pm(struct uart_state *state, int pm_state)
+{
+	struct uart_port *port = state->uart_port;
+
+	if (state->pm_state != pm_state) {
+		if (port->ops->pm)
+			port->ops->pm(port, pm_state, state->pm_state);
+		state->pm_state = pm_state;
+	}
+}
+
+struct uart_match {
+	struct uart_port *port;
+	struct uart_driver *driver;
+};
+
+static int serial_match_port(struct device *dev, void *data)
+{
+	struct uart_match *match = data;
+	struct tty_driver *tty_drv = match->driver->tty_driver;
+	dev_t devt = MKDEV(tty_drv->major, tty_drv->minor_start) +
+		match->port->line;
+
+	return dev->devt == devt; /* Actually, only one tty per port */
+}
+
+int uart_suspend_port(struct uart_driver *drv, struct uart_port *uport)
+{
+	struct uart_state *state = drv->state + uport->line;
+	struct tty_port *port = &state->port;
+	struct device *tty_dev;
+	struct uart_match match = {uport, drv};
+
+	mutex_lock(&port->mutex);
+
+	tty_dev = device_find_child(uport->dev, &match, serial_match_port);
+#ifdef CONFIG_KLOCWORK
+	if(tty_dev == NULL)
+		BUG_ON(1);
+#endif	
+	if (device_may_wakeup(tty_dev)) {
+		if (!enable_irq_wake(uport->irq))
+			uport->irq_wake = 1;
+		put_device(tty_dev);
+		mutex_unlock(&port->mutex);
+		return 0;
+	}
+	put_device(tty_dev);
+
+	if (console_suspend_enabled || !uart_console(uport))
+		uport->suspended = 1;
+
+	if (port->flags & ASYNC_INITIALIZED) {
+		const struct uart_ops *ops = uport->ops;
+		int tries;
+
+		if (console_suspend_enabled || !uart_console(uport)) {
+			set_bit(ASYNCB_SUSPENDED, &port->flags);
+			clear_bit(ASYNCB_INITIALIZED, &port->flags);
+
+			raw_spin_lock_irq(&uport->lock);
+			ops->stop_tx(uport);
+			ops->set_mctrl(uport, 0);
+			ops->stop_rx(uport);
+			raw_spin_unlock_irq(&uport->lock);
+		}
+
+		/*
+		 * Wait for the transmitter to empty.
+		 */
+		for (tries = 3; !ops->tx_empty(uport) && tries; tries--)
+			msleep(10);
+		if (!tries)
+			printk(KERN_ERR "%s%s%s%d: Unable to drain "
+					"transmitter\n",
+			       uport->dev ? dev_name(uport->dev) : "",
+			       uport->dev ? ": " : "",
+			       drv->dev_name,
+			       drv->tty_driver->name_base + uport->line);
+
+		if (console_suspend_enabled || !uart_console(uport))
+			ops->shutdown(uport);
+	}
+
+	/*
+	 * Disable the console device before suspending.
+	 */
+	if (console_suspend_enabled && uart_console(uport))
+		console_stop(uport->cons);
+
+	if (console_suspend_enabled || !uart_console(uport))
+		uart_change_pm(state, 3);
+
+	mutex_unlock(&port->mutex);
+
+	return 0;
+}
+
+int uart_resume_port(struct uart_driver *drv, struct uart_port *uport)
+{
+	struct uart_state *state = drv->state + uport->line;
+	struct tty_port *port = &state->port;
+	struct device *tty_dev;
+	struct uart_match match = {uport, drv};
+	struct ktermios termios;
+
+	mutex_lock(&port->mutex);
+
+	tty_dev = device_find_child(uport->dev, &match, serial_match_port);
+#ifdef CONFIG_KLOCWORK
+	if(tty_dev == NULL)
+		BUG_ON(1);
+#endif	
+	if (!uport->suspended && device_may_wakeup(tty_dev)) {
+		if (uport->irq_wake) {
+			disable_irq_wake(uport->irq);
+			uport->irq_wake = 0;
+		}
+		put_device(tty_dev);
+		mutex_unlock(&port->mutex);
+		return 0;
+	}
+	put_device(tty_dev);
+	uport->suspended = 0;
+
+	/*
+	 * Re-enable the console device after suspending.
+	 */
+	if (uart_console(uport)) {
+		/*
+		 * First try to use the console cflag setting.
+		 */
+		memset(&termios, 0, sizeof(struct ktermios));
+		termios.c_cflag = uport->cons->cflag;
+
+		/*
+		 * If that's unset, use the tty termios setting.
+		 */
+		if (port->tty && port->tty->termios && termios.c_cflag == 0)
+			termios = *(port->tty->termios);
+
+		if (console_suspend_enabled)
+			uart_change_pm(state, 0);
+		uport->ops->set_termios(uport, &termios, NULL);
+		if (console_suspend_enabled)
+			console_start(uport->cons);
+	}
+
+	if (port->flags & ASYNC_SUSPENDED) {
+		const struct uart_ops *ops = uport->ops;
+		int ret;
+
+		uart_change_pm(state, 0);
+		raw_spin_lock_irq(&uport->lock);
+		ops->set_mctrl(uport, 0);
+		raw_spin_unlock_irq(&uport->lock);
+		if (console_suspend_enabled || !uart_console(uport)) {
+			/* Protected by port mutex for now */
+			struct tty_struct *tty = port->tty;
+			ret = ops->startup(uport);
+			if (ret == 0) {
+				if (tty)
+					uart_change_speed(tty, state, NULL);
+				raw_spin_lock_irq(&uport->lock);
+				ops->set_mctrl(uport, uport->mctrl);
+				ops->start_tx(uport);
+				raw_spin_unlock_irq(&uport->lock);
+				set_bit(ASYNCB_INITIALIZED, &port->flags);
+			} else {
+				/*
+				 * Failed to resume - maybe hardware went away?
+				 * Clear the "initialized" flag so we won't try
+				 * to call the low level drivers shutdown method.
+				 */
+				uart_shutdown(tty, state);
+			}
+		}
+
+		clear_bit(ASYNCB_SUSPENDED, &port->flags);
+	}
+
+	mutex_unlock(&port->mutex);
+
+	return 0;
+}
+
+static inline void
+uart_report_port(struct uart_driver *drv, struct uart_port *port)
+{
+	char address[64];
+
+	switch (port->iotype) {
+	case UPIO_PORT:
+		snprintf(address, sizeof(address), "I/O 0x%lx", port->iobase);
+		break;
+	case UPIO_HUB6:
+		snprintf(address, sizeof(address),
+			 "I/O 0x%lx offset 0x%x", port->iobase, port->hub6);
+		break;
+	case UPIO_MEM:
+	case UPIO_MEM32:
+	case UPIO_AU:
+	case UPIO_TSI:
+		snprintf(address, sizeof(address),
+			 "MMIO 0x%llx", (unsigned long long)port->mapbase);
+		break;
+	default:
+		strlcpy(address, "*unknown*", sizeof(address));
+		break;
+	}
+
+	printk(KERN_INFO "%s%s%s%d at %s (irq = %d) is a %s\n",
+	       port->dev ? dev_name(port->dev) : "",
+	       port->dev ? ": " : "",
+	       drv->dev_name,
+	       drv->tty_driver->name_base + port->line,
+	       address, port->irq, uart_type(port));
+}
+
+static void
+uart_configure_port(struct uart_driver *drv, struct uart_state *state,
+		    struct uart_port *port)
+{
+	unsigned int flags;
+
+	/*
+	 * If there isn't a port here, don't do anything further.
+	 */
+	if (!port->iobase && !port->mapbase && !port->membase)
+		return;
+
+	/*
+	 * Now do the auto configuration stuff.  Note that config_port
+	 * is expected to claim the resources and map the port for us.
+	 */
+	flags = 0;
+	if (port->flags & UPF_AUTO_IRQ)
+		flags |= UART_CONFIG_IRQ;
+	if (port->flags & UPF_BOOT_AUTOCONF) {
+		if (!(port->flags & UPF_FIXED_TYPE)) {
+			port->type = PORT_UNKNOWN;
+			flags |= UART_CONFIG_TYPE;
+		}
+		port->ops->config_port(port, flags);
+	}
+
+	if (port->type != PORT_UNKNOWN) {
+		unsigned long flags;
+
+		uart_report_port(drv, port);
+
+		/* Power up port for set_mctrl() */
+		uart_change_pm(state, 0);
+
+		/*
+		 * Ensure that the modem control lines are de-activated.
+		 * keep the DTR setting that is set in uart_set_options()
+		 * We probably don't need a spinlock around this, but
+		 */
+		raw_spin_lock_irqsave(&port->lock, flags);
+		port->ops->set_mctrl(port, port->mctrl & TIOCM_DTR);
+		raw_spin_unlock_irqrestore(&port->lock, flags);
+
+		/*
+		 * If this driver supports console, and it hasn't been
+		 * successfully registered yet, try to re-register it.
+		 * It may be that the port was not available.
+		 */
+		if (port->cons && !(port->cons->flags & CON_ENABLED))
+			register_console(port->cons);
+
+		/*
+		 * Power down all ports by default, except the
+		 * console if we have one.
+		 */
+		if (!uart_console(port))
+			uart_change_pm(state, 3);
+	}
+}
+
+#ifdef CONFIG_CONSOLE_POLL
+
+static int uart_poll_init(struct tty_driver *driver, int line, char *options)
+{
+	struct uart_driver *drv = driver->driver_state;
+	struct uart_state *state = drv->state + line;
+	struct uart_port *port;
+	int baud = 9600;
+	int bits = 8;
+	int parity = 'n';
+	int flow = 'n';
+
+	if (!state || !state->uart_port)
+		return -1;
+
+	port = state->uart_port;
+	if (!(port->ops->poll_get_char && port->ops->poll_put_char))
+		return -1;
+
+	if (options) {
+		uart_parse_options(options, &baud, &parity, &bits, &flow);
+		return uart_set_options(port, NULL, baud, parity, bits, flow);
+	}
+
+	return 0;
+}
+
+static int uart_poll_get_char(struct tty_driver *driver, int line)
+{
+	struct uart_driver *drv = driver->driver_state;
+	struct uart_state *state = drv->state + line;
+	struct uart_port *port;
+
+	if (!state || !state->uart_port)
+		return -1;
+
+	port = state->uart_port;
+	return port->ops->poll_get_char(port);
+}
+
+static void uart_poll_put_char(struct tty_driver *driver, int line, char ch)
+{
+	struct uart_driver *drv = driver->driver_state;
+	struct uart_state *state = drv->state + line;
+	struct uart_port *port;
+
+	if (!state || !state->uart_port)
+		return;
+
+	port = state->uart_port;
+	port->ops->poll_put_char(port, ch);
+}
+#endif
+
+static const struct tty_operations uart_ops = {
+	.open		= uart_open,
+	.close		= uart_close,
+	.write		= uart_write,
+	.put_char	= uart_put_char,
+	.flush_chars	= uart_flush_chars,
+	.write_room	= uart_write_room,
+	.chars_in_buffer= uart_chars_in_buffer,
+	.flush_buffer	= uart_flush_buffer,
+	.ioctl		= uart_ioctl,
+	.throttle	= uart_throttle,
+	.unthrottle	= uart_unthrottle,
+	.send_xchar	= uart_send_xchar,
+	.set_termios	= uart_set_termios,
+	.set_ldisc	= uart_set_ldisc,
+	.stop		= uart_stop,
+	.start		= uart_start,
+	.hangup		= uart_hangup,
+	.break_ctl	= uart_break_ctl,
+	.wait_until_sent= uart_wait_until_sent,
+#ifdef CONFIG_PROC_FS
+	.proc_fops	= &uart_proc_fops,
+#endif
+	.tiocmget	= uart_tiocmget,
+	.tiocmset	= uart_tiocmset,
+	.get_icount	= uart_get_icount,
+#ifdef CONFIG_CONSOLE_POLL
+	.poll_init	= uart_poll_init,
+	.poll_get_char	= uart_poll_get_char,
+	.poll_put_char	= uart_poll_put_char,
+#endif
+};
+
+static const struct tty_port_operations uart_port_ops = {
+	.activate	= uart_port_activate,
+	.shutdown	= uart_port_shutdown,
+	.carrier_raised = uart_carrier_raised,
+	.dtr_rts	= uart_dtr_rts,
+};
+
+/**
+ *	uart_register_driver - register a driver with the uart core layer
+ *	@drv: low level driver structure
+ *
+ *	Register a uart driver with the core driver.  We in turn register
+ *	with the tty layer, and initialise the core driver per-port state.
+ *
+ *	We have a proc file in /proc/tty/driver which is named after the
+ *	normal driver.
+ *
+ *	drv->port should be NULL, and the per-port structures should be
+ *	registered using uart_add_one_port after this call has succeeded.
+ */
+int uart_register_driver(struct uart_driver *drv)
+{
+	struct tty_driver *normal;
+	int i, retval;
+
+	BUG_ON(drv->state);
+
+	/*
+	 * Maybe we should be using a slab cache for this, especially if
+	 * we have a large number of ports to handle.
+	 */
+	drv->state = kzalloc(sizeof(struct uart_state) * drv->nr, GFP_KERNEL);
+	if (!drv->state)
+		goto out;
+
+	normal = alloc_tty_driver(drv->nr);
+	if (!normal)
+		goto out_kfree;
+
+	drv->tty_driver = normal;
+
+	normal->driver_name	= drv->driver_name;
+	normal->name		= drv->dev_name;
+	normal->major		= drv->major;
+	normal->minor_start	= drv->minor;
+	normal->type		= TTY_DRIVER_TYPE_SERIAL;
+	normal->subtype		= SERIAL_TYPE_NORMAL;
+	normal->init_termios	= tty_std_termios;
+	//normal->init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL;
+	//normal->init_termios.c_ispeed = normal->init_termios.c_ospeed = 9600;
+	normal->init_termios.c_cflag &= ~CRTSCTS; //no flow control	
+    normal->init_termios.c_cflag &= ~CSIZE; //¿ØÖÆÄ£Ê½, ÆÁ±Î×Ö·û´óСλ
+    normal->init_termios.c_cflag &= ~PARENB; //no parity check
+    normal->init_termios.c_cflag &= ~CSTOPB;
+    normal->init_termios.c_cc[VMIN] = 1;       //¿ØÖÆ×Ö·û, ËùÒª¶ÁÈ¡×Ö·ûµÄ×îСÊýÁ¿
+    normal->init_termios.c_cc[VTIME] = 1;      //¿ØÖÆ×Ö·û, ¶ÁÈ¡µÚÒ»¸ö×Ö·ûµÄµÈ´ýʱ¼ä£¬unit: (1/10)second
+    normal->init_termios.c_oflag &= ~OPOST;            //Êä³öģʽ, ԭʼÊý¾ÝÊä³ö
+    normal->init_termios.c_lflag &= 0;            
+	normal->flags		= TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV;
+	normal->driver_state    = drv;
+	tty_set_operations(normal, &uart_ops);
+
+	/*
+	 * Initialise the UART state(s).
+	 */
+	for (i = 0; i < drv->nr; i++) {
+		struct uart_state *state = drv->state + i;
+		struct tty_port *port = &state->port;
+
+		tty_port_init(port);
+		port->ops = &uart_port_ops;
+		port->close_delay     = HZ / 2;	/* .5 seconds */
+		port->closing_wait    = 30 * HZ;/* 30 seconds */
+	}
+
+	retval = tty_register_driver(normal);
+	if (retval >= 0)
+		return retval;
+
+	put_tty_driver(normal);
+out_kfree:
+	kfree(drv->state);
+out:
+	return -ENOMEM;
+}
+
+/**
+ *	uart_unregister_driver - remove a driver from the uart core layer
+ *	@drv: low level driver structure
+ *
+ *	Remove all references to a driver from the core driver.  The low
+ *	level driver must have removed all its ports via the
+ *	uart_remove_one_port() if it registered them with uart_add_one_port().
+ *	(ie, drv->port == NULL)
+ */
+void uart_unregister_driver(struct uart_driver *drv)
+{
+	struct tty_driver *p = drv->tty_driver;
+	tty_unregister_driver(p);
+	put_tty_driver(p);
+	kfree(drv->state);
+	drv->state = NULL;
+	drv->tty_driver = NULL;
+}
+
+struct tty_driver *uart_console_device(struct console *co, int *index)
+{
+	struct uart_driver *p = co->data;
+	*index = co->index;
+	return p->tty_driver;
+}
+
+/**
+ *	uart_add_one_port - attach a driver-defined port structure
+ *	@drv: pointer to the uart low level driver structure for this port
+ *	@uport: uart port structure to use for this port.
+ *
+ *	This allows the driver to register its own uart_port structure
+ *	with the core driver.  The main purpose is to allow the low
+ *	level uart drivers to expand uart_port, rather than having yet
+ *	more levels of structures.
+ */
+int uart_add_one_port(struct uart_driver *drv, struct uart_port *uport)
+{
+	struct uart_state *state;
+	struct tty_port *port;
+	int ret = 0;
+	struct device *tty_dev;
+
+	BUG_ON(in_interrupt());
+
+	if (uport->line >= drv->nr)
+		return -EINVAL;
+
+	state = drv->state + uport->line;
+	port = &state->port;
+
+	mutex_lock(&port_mutex);
+	mutex_lock(&port->mutex);
+	if (state->uart_port) {
+		ret = -EINVAL;
+		goto out;
+	}
+
+	state->uart_port = uport;
+	state->pm_state = -1;
+
+	uport->cons = drv->cons;
+	uport->state = state;
+
+	/*
+	 * If this port is a console, then the spinlock is already
+	 * initialised.
+	 */
+	if (!(uart_console(uport) && (uport->cons->flags & CON_ENABLED))) {
+		raw_spin_lock_init(&uport->lock);
+		lockdep_set_class(&uport->lock, &port_lock_key);
+	}
+
+	uart_configure_port(drv, state, uport);
+
+	/*
+	 * Register the port whether it's detected or not.  This allows
+	 * setserial to be used to alter this ports parameters.
+	 */
+	tty_dev = tty_register_device(drv->tty_driver, uport->line, uport->dev);
+	if (likely(!IS_ERR(tty_dev))) {
+		device_set_wakeup_capable(tty_dev, 1);
+	} else {
+		printk(KERN_ERR "Cannot register tty device on line %d\n",
+		       uport->line);
+	}
+
+	/*
+	 * Ensure UPF_DEAD is not set.
+	 */
+	uport->flags &= ~UPF_DEAD;
+
+ out:
+	mutex_unlock(&port->mutex);
+	mutex_unlock(&port_mutex);
+
+	return ret;
+}
+
+/**
+ *	uart_remove_one_port - detach a driver defined port structure
+ *	@drv: pointer to the uart low level driver structure for this port
+ *	@uport: uart port structure for this port
+ *
+ *	This unhooks (and hangs up) the specified port structure from the
+ *	core driver.  No further calls will be made to the low-level code
+ *	for this port.
+ */
+int uart_remove_one_port(struct uart_driver *drv, struct uart_port *uport)
+{
+	struct uart_state *state = drv->state + uport->line;
+	struct tty_port *port = &state->port;
+
+	BUG_ON(in_interrupt());
+
+	if (state->uart_port != uport)
+		printk(KERN_ALERT "Removing wrong port: %p != %p\n",
+			state->uart_port, uport);
+
+	mutex_lock(&port_mutex);
+
+	/*
+	 * Mark the port "dead" - this prevents any opens from
+	 * succeeding while we shut down the port.
+	 */
+	mutex_lock(&port->mutex);
+	uport->flags |= UPF_DEAD;
+	mutex_unlock(&port->mutex);
+
+	/*
+	 * Remove the devices from the tty layer
+	 */
+	tty_unregister_device(drv->tty_driver, uport->line);
+
+	if (port->tty)
+		tty_vhangup(port->tty);
+
+	/*
+	 * Free the port IO and memory resources, if any.
+	 */
+	if (uport->type != PORT_UNKNOWN)
+		uport->ops->release_port(uport);
+
+	/*
+	 * Indicate that there isn't a port here anymore.
+	 */
+	uport->type = PORT_UNKNOWN;
+
+	state->uart_port = NULL;
+	mutex_unlock(&port_mutex);
+
+	return 0;
+}
+
+/*
+ *	Are the two ports equivalent?
+ */
+int uart_match_port(struct uart_port *port1, struct uart_port *port2)
+{
+	if (port1->iotype != port2->iotype)
+		return 0;
+
+	switch (port1->iotype) {
+	case UPIO_PORT:
+		return (port1->iobase == port2->iobase);
+	case UPIO_HUB6:
+		return (port1->iobase == port2->iobase) &&
+		       (port1->hub6   == port2->hub6);
+	case UPIO_MEM:
+	case UPIO_MEM32:
+	case UPIO_AU:
+	case UPIO_TSI:
+		return (port1->mapbase == port2->mapbase);
+	}
+	return 0;
+}
+EXPORT_SYMBOL(uart_match_port);
+
+/**
+ *	uart_handle_dcd_change - handle a change of carrier detect state
+ *	@uport: uart_port structure for the open port
+ *	@status: new carrier detect status, nonzero if active
+ */
+void uart_handle_dcd_change(struct uart_port *uport, unsigned int status)
+{
+	struct uart_state *state = uport->state;
+	struct tty_port *port = &state->port;
+	struct tty_ldisc *ld = tty_ldisc_ref(port->tty);
+	struct pps_event_time ts;
+
+	if (ld && ld->ops->dcd_change)
+		pps_get_ts(&ts);
+
+	uport->icount.dcd++;
+#ifdef CONFIG_HARD_PPS
+	if ((uport->flags & UPF_HARDPPS_CD) && status)
+		hardpps();
+#endif
+
+	if (port->flags & ASYNC_CHECK_CD) {
+		if (status)
+			wake_up_interruptible(&port->open_wait);
+		else if (port->tty)
+			tty_hangup(port->tty);
+	}
+
+	if (ld && ld->ops->dcd_change)
+		ld->ops->dcd_change(port->tty, status, &ts);
+	if (ld)
+		tty_ldisc_deref(ld);
+}
+EXPORT_SYMBOL_GPL(uart_handle_dcd_change);
+
+/**
+ *	uart_handle_cts_change - handle a change of clear-to-send state
+ *	@uport: uart_port structure for the open port
+ *	@status: new clear to send status, nonzero if active
+ */
+void uart_handle_cts_change(struct uart_port *uport, unsigned int status)
+{
+	struct tty_port *port = &uport->state->port;
+	struct tty_struct *tty = port->tty;
+
+	uport->icount.cts++;
+
+	if (port->flags & ASYNC_CTS_FLOW) {
+		if (tty->hw_stopped) {
+			if (status) {
+				tty->hw_stopped = 0;
+				uport->ops->start_tx(uport);
+				uart_write_wakeup(uport);
+			}
+		} else {
+			if (!status) {
+				tty->hw_stopped = 1;
+				uport->ops->stop_tx(uport);
+			}
+		}
+	}
+}
+EXPORT_SYMBOL_GPL(uart_handle_cts_change);
+
+/**
+ * uart_insert_char - push a char to the uart layer
+ *
+ * User is responsible to call tty_flip_buffer_push when they are done with
+ * insertion.
+ *
+ * @port: corresponding port
+ * @status: state of the serial port RX buffer (LSR for 8250)
+ * @overrun: mask of overrun bits in @status
+ * @ch: character to push
+ * @flag: flag for the character (see TTY_NORMAL and friends)
+ */
+void uart_insert_char(struct uart_port *port, unsigned int status,
+		 unsigned int overrun, unsigned int ch, unsigned int flag)
+{
+	struct tty_struct *tty = port->state->port.tty;
+
+	if ((status & port->ignore_status_mask & ~overrun) == 0)
+		tty_insert_flip_char(tty, ch, flag);
+
+	/*
+	 * Overrun is special.  Since it's reported immediately,
+	 * it doesn't affect the current character.
+	 */
+	if (status & ~port->ignore_status_mask & overrun)
+		tty_insert_flip_char(tty, 0, TTY_OVERRUN);
+}
+EXPORT_SYMBOL_GPL(uart_insert_char);
+
+EXPORT_SYMBOL(uart_write_wakeup);
+EXPORT_SYMBOL(uart_register_driver);
+EXPORT_SYMBOL(uart_unregister_driver);
+EXPORT_SYMBOL(uart_suspend_port);
+EXPORT_SYMBOL(uart_resume_port);
+EXPORT_SYMBOL(uart_add_one_port);
+EXPORT_SYMBOL(uart_remove_one_port);
+
+MODULE_DESCRIPTION("Serial driver core");
+MODULE_LICENSE("GPL");
diff --git a/ap/os/linux/linux-3.4.x/drivers/tty/serial/serial_ks8695.c b/ap/os/linux/linux-3.4.x/drivers/tty/serial/serial_ks8695.c
new file mode 100644
index 0000000..7c13639
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/tty/serial/serial_ks8695.c
@@ -0,0 +1,704 @@
+/*
+ *  Driver for KS8695 serial ports
+ *
+ *  Based on drivers/serial/serial_amba.c, by Kam Lee.
+ *
+ *  Copyright 2002-2005 Micrel 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.
+ *
+ */
+#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 <asm/io.h>
+#include <asm/irq.h>
+#include <asm/mach/irq.h>
+
+#include <mach/regs-uart.h>
+#include <mach/regs-irq.h>
+
+#if defined(CONFIG_SERIAL_KS8695_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ)
+#define SUPPORT_SYSRQ
+#endif
+
+#include <linux/serial_core.h>
+
+
+#define SERIAL_KS8695_MAJOR	204
+#define SERIAL_KS8695_MINOR	16
+#define SERIAL_KS8695_DEVNAME	"ttyAM"
+
+#define SERIAL_KS8695_NR	1
+
+/*
+ * Access macros for the KS8695 UART
+ */
+#define UART_GET_CHAR(p)	(__raw_readl((p)->membase + KS8695_URRB) & 0xFF)
+#define UART_PUT_CHAR(p, c)	__raw_writel((c), (p)->membase + KS8695_URTH)
+#define UART_GET_FCR(p)		__raw_readl((p)->membase + KS8695_URFC)
+#define UART_PUT_FCR(p, c)	__raw_writel((c), (p)->membase + KS8695_URFC)
+#define UART_GET_MSR(p)		__raw_readl((p)->membase + KS8695_URMS)
+#define UART_GET_LSR(p)		__raw_readl((p)->membase + KS8695_URLS)
+#define UART_GET_LCR(p)		__raw_readl((p)->membase + KS8695_URLC)
+#define UART_PUT_LCR(p, c)	__raw_writel((c), (p)->membase + KS8695_URLC)
+#define UART_GET_MCR(p)		__raw_readl((p)->membase + KS8695_URMC)
+#define UART_PUT_MCR(p, c)	__raw_writel((c), (p)->membase + KS8695_URMC)
+#define UART_GET_BRDR(p)	__raw_readl((p)->membase + KS8695_URBD)
+#define UART_PUT_BRDR(p, c)	__raw_writel((c), (p)->membase + KS8695_URBD)
+
+#define KS8695_CLR_TX_INT()	__raw_writel(1 << KS8695_IRQ_UART_TX, KS8695_IRQ_VA + KS8695_INTST)
+
+#define UART_DUMMY_LSR_RX	0x100
+#define UART_PORT_SIZE		(KS8695_USR - KS8695_URRB + 4)
+
+static inline int tx_enabled(struct uart_port *port)
+{
+	return port->unused[0] & 1;
+}
+
+static inline int rx_enabled(struct uart_port *port)
+{
+	return port->unused[0] & 2;
+}
+
+static inline int ms_enabled(struct uart_port *port)
+{
+	return port->unused[0] & 4;
+}
+
+static inline void ms_enable(struct uart_port *port, int enabled)
+{
+	if(enabled)
+		port->unused[0] |= 4;
+	else
+		port->unused[0] &= ~4;
+}
+
+static inline void rx_enable(struct uart_port *port, int enabled)
+{
+	if(enabled)
+		port->unused[0] |= 2;
+	else
+		port->unused[0] &= ~2;
+}
+
+static inline void tx_enable(struct uart_port *port, int enabled)
+{
+	if(enabled)
+		port->unused[0] |= 1;
+	else
+		port->unused[0] &= ~1;
+}
+
+
+#ifdef SUPPORT_SYSRQ
+static struct console ks8695_console;
+#endif
+
+static void ks8695uart_stop_tx(struct uart_port *port)
+{
+	if (tx_enabled(port)) {
+		/* use disable_irq_nosync() and not disable_irq() to avoid self
+		 * imposed deadlock by not waiting for irq handler to end,
+		 * since this ks8695uart_stop_tx() is called from interrupt context.
+		 */
+		disable_irq_nosync(KS8695_IRQ_UART_TX);
+		tx_enable(port, 0);
+	}
+}
+
+static void ks8695uart_start_tx(struct uart_port *port)
+{
+	if (!tx_enabled(port)) {
+		enable_irq(KS8695_IRQ_UART_TX);
+		tx_enable(port, 1);
+	}
+}
+
+static void ks8695uart_stop_rx(struct uart_port *port)
+{
+	if (rx_enabled(port)) {
+		disable_irq(KS8695_IRQ_UART_RX);
+		rx_enable(port, 0);
+	}
+}
+
+static void ks8695uart_enable_ms(struct uart_port *port)
+{
+	if (!ms_enabled(port)) {
+		enable_irq(KS8695_IRQ_UART_MODEM_STATUS);
+		ms_enable(port,1);
+	}
+}
+
+static void ks8695uart_disable_ms(struct uart_port *port)
+{
+	if (ms_enabled(port)) {
+		disable_irq(KS8695_IRQ_UART_MODEM_STATUS);
+		ms_enable(port,0);
+	}
+}
+
+static irqreturn_t ks8695uart_rx_chars(int irq, void *dev_id)
+{
+	struct uart_port *port = dev_id;
+	struct tty_struct *tty = port->state->port.tty;
+	unsigned int status, ch, lsr, flg, max_count = 256;
+
+	status = UART_GET_LSR(port);		/* clears pending LSR interrupts */
+	while ((status & URLS_URDR) && max_count--) {
+		ch = UART_GET_CHAR(port);
+		flg = TTY_NORMAL;
+
+		port->icount.rx++;
+
+		/*
+		 * Note that the error handling code is
+		 * out of the main execution path
+		 */
+		lsr = UART_GET_LSR(port) | UART_DUMMY_LSR_RX;
+		if (unlikely(lsr & (URLS_URBI | URLS_URPE | URLS_URFE | URLS_URROE))) {
+			if (lsr & URLS_URBI) {
+				lsr &= ~(URLS_URFE | URLS_URPE);
+				port->icount.brk++;
+				if (uart_handle_break(port))
+					goto ignore_char;
+			}
+			if (lsr & URLS_URPE)
+				port->icount.parity++;
+			if (lsr & URLS_URFE)
+				port->icount.frame++;
+			if (lsr & URLS_URROE)
+				port->icount.overrun++;
+
+			lsr &= port->read_status_mask;
+
+			if (lsr & URLS_URBI)
+				flg = TTY_BREAK;
+			else if (lsr & URLS_URPE)
+				flg = TTY_PARITY;
+			else if (lsr & URLS_URFE)
+				flg = TTY_FRAME;
+		}
+
+		if (uart_handle_sysrq_char(port, ch))
+			goto ignore_char;
+
+		uart_insert_char(port, lsr, URLS_URROE, ch, flg);
+
+ignore_char:
+		status = UART_GET_LSR(port);
+	}
+	tty_flip_buffer_push(tty);
+
+	return IRQ_HANDLED;
+}
+
+
+static irqreturn_t ks8695uart_tx_chars(int irq, void *dev_id)
+{
+	struct uart_port *port = dev_id;
+	struct circ_buf *xmit = &port->state->xmit;
+	unsigned int count;
+
+	if (port->x_char) {
+		KS8695_CLR_TX_INT();
+		UART_PUT_CHAR(port, port->x_char);
+		port->icount.tx++;
+		port->x_char = 0;
+		return IRQ_HANDLED;
+	}
+
+	if (uart_tx_stopped(port) || uart_circ_empty(xmit)) {
+		ks8695uart_stop_tx(port);
+		return IRQ_HANDLED;
+	}
+
+	count = 16;	/* fifo size */
+	while (!uart_circ_empty(xmit) && (count-- > 0)) {
+		KS8695_CLR_TX_INT();
+		UART_PUT_CHAR(port, xmit->buf[xmit->tail]);
+
+		xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
+		port->icount.tx++;
+	}
+
+	if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
+		uart_write_wakeup(port);
+
+	if (uart_circ_empty(xmit))
+		ks8695uart_stop_tx(port);
+
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t ks8695uart_modem_status(int irq, void *dev_id)
+{
+	struct uart_port *port = dev_id;
+	unsigned int status;
+
+	/*
+	 * clear modem interrupt by reading MSR
+	 */
+	status = UART_GET_MSR(port);
+
+	if (status & URMS_URDDCD)
+		uart_handle_dcd_change(port, status & URMS_URDDCD);
+
+	if (status & URMS_URDDST)
+		port->icount.dsr++;
+
+	if (status & URMS_URDCTS)
+		uart_handle_cts_change(port, status & URMS_URDCTS);
+
+	if (status & URMS_URTERI)
+		port->icount.rng++;
+
+	wake_up_interruptible(&port->state->port.delta_msr_wait);
+
+	return IRQ_HANDLED;
+}
+
+static unsigned int ks8695uart_tx_empty(struct uart_port *port)
+{
+	return (UART_GET_LSR(port) & URLS_URTE) ? TIOCSER_TEMT : 0;
+}
+
+static unsigned int ks8695uart_get_mctrl(struct uart_port *port)
+{
+	unsigned int result = 0;
+	unsigned int status;
+
+	status = UART_GET_MSR(port);
+	if (status & URMS_URDCD)
+		result |= TIOCM_CAR;
+	if (status & URMS_URDSR)
+		result |= TIOCM_DSR;
+	if (status & URMS_URCTS)
+		result |= TIOCM_CTS;
+	if (status & URMS_URRI)
+		result |= TIOCM_RI;
+
+	return result;
+}
+
+static void ks8695uart_set_mctrl(struct uart_port *port, u_int mctrl)
+{
+	unsigned int mcr;
+
+	mcr = UART_GET_MCR(port);
+	if (mctrl & TIOCM_RTS)
+		mcr |= URMC_URRTS;
+	else
+		mcr &= ~URMC_URRTS;
+
+	if (mctrl & TIOCM_DTR)
+		mcr |= URMC_URDTR;
+	else
+		mcr &= ~URMC_URDTR;
+
+	UART_PUT_MCR(port, mcr);
+}
+
+static void ks8695uart_break_ctl(struct uart_port *port, int break_state)
+{
+	unsigned int lcr;
+
+	lcr = UART_GET_LCR(port);
+
+	if (break_state == -1)
+		lcr |= URLC_URSBC;
+	else
+		lcr &= ~URLC_URSBC;
+
+	UART_PUT_LCR(port, lcr);
+}
+
+static int ks8695uart_startup(struct uart_port *port)
+{
+	int retval;
+
+	set_irq_flags(KS8695_IRQ_UART_TX, IRQF_VALID | IRQF_NOAUTOEN);
+	tx_enable(port, 0);
+	rx_enable(port, 1);
+	ms_enable(port, 1);
+
+	/*
+	 * Allocate the IRQ
+	 */
+	retval = request_irq(KS8695_IRQ_UART_TX, ks8695uart_tx_chars, 0, "UART TX", port);
+	if (retval)
+		goto err_tx;
+
+	retval = request_irq(KS8695_IRQ_UART_RX, ks8695uart_rx_chars, 0, "UART RX", port);
+	if (retval)
+		goto err_rx;
+
+	retval = request_irq(KS8695_IRQ_UART_LINE_STATUS, ks8695uart_rx_chars, 0, "UART LineStatus", port);
+	if (retval)
+		goto err_ls;
+
+	retval = request_irq(KS8695_IRQ_UART_MODEM_STATUS, ks8695uart_modem_status, 0, "UART ModemStatus", port);
+	if (retval)
+		goto err_ms;
+
+	return 0;
+
+err_ms:
+	free_irq(KS8695_IRQ_UART_LINE_STATUS, port);
+err_ls:
+	free_irq(KS8695_IRQ_UART_RX, port);
+err_rx:
+	free_irq(KS8695_IRQ_UART_TX, port);
+err_tx:
+	return retval;
+}
+
+static void ks8695uart_shutdown(struct uart_port *port)
+{
+	/*
+	 * Free the interrupt
+	 */
+	free_irq(KS8695_IRQ_UART_RX, port);
+	free_irq(KS8695_IRQ_UART_TX, port);
+	free_irq(KS8695_IRQ_UART_MODEM_STATUS, port);
+	free_irq(KS8695_IRQ_UART_LINE_STATUS, port);
+
+	/* disable break condition and fifos */
+	UART_PUT_LCR(port, UART_GET_LCR(port) & ~URLC_URSBC);
+	UART_PUT_FCR(port, UART_GET_FCR(port) & ~URFC_URFE);
+}
+
+static void ks8695uart_set_termios(struct uart_port *port, struct ktermios *termios, struct ktermios *old)
+{
+	unsigned int lcr, fcr = 0;
+	unsigned long flags;
+	unsigned int baud, quot;
+
+	/*
+	 * Ask the core to calculate the divisor for us.
+	 */
+	baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk/16);
+	quot = uart_get_divisor(port, baud);
+
+	switch (termios->c_cflag & CSIZE) {
+	case CS5:
+		lcr = URCL_5;
+		break;
+	case CS6:
+		lcr = URCL_6;
+		break;
+	case CS7:
+		lcr = URCL_7;
+		break;
+	default:
+		lcr = URCL_8;
+		break;
+	}
+
+	/* stop bits */
+	if (termios->c_cflag & CSTOPB)
+		lcr |= URLC_URSB;
+
+	/* parity */
+	if (termios->c_cflag & PARENB) {
+		if (termios->c_cflag & CMSPAR) {	/* Mark or Space parity */
+			if (termios->c_cflag & PARODD)
+				lcr |= URPE_MARK;
+			else
+				lcr |= URPE_SPACE;
+		}
+		else if (termios->c_cflag & PARODD)
+			lcr |= URPE_ODD;
+		else
+			lcr |= URPE_EVEN;
+	}
+
+	if (port->fifosize > 1)
+		fcr = URFC_URFRT_8 | URFC_URTFR | URFC_URRFR | URFC_URFE;
+
+	spin_lock_irqsave(&port->lock, flags);
+
+	/*
+	 * Update the per-port timeout.
+	 */
+	uart_update_timeout(port, termios->c_cflag, baud);
+
+	port->read_status_mask = URLS_URROE;
+	if (termios->c_iflag & INPCK)
+		port->read_status_mask |= (URLS_URFE | URLS_URPE);
+	if (termios->c_iflag & (BRKINT | PARMRK))
+		port->read_status_mask |= URLS_URBI;
+
+	/*
+	 * Characters to ignore
+	 */
+	port->ignore_status_mask = 0;
+	if (termios->c_iflag & IGNPAR)
+		port->ignore_status_mask |= (URLS_URFE | URLS_URPE);
+	if (termios->c_iflag & IGNBRK) {
+		port->ignore_status_mask |= URLS_URBI;
+		/*
+		 * If we're ignoring parity and break indicators,
+		 * ignore overruns too (for real raw support).
+		 */
+		if (termios->c_iflag & IGNPAR)
+			port->ignore_status_mask |= URLS_URROE;
+	}
+
+	/*
+	 * Ignore all characters if CREAD is not set.
+	 */
+	if ((termios->c_cflag & CREAD) == 0)
+		port->ignore_status_mask |= UART_DUMMY_LSR_RX;
+
+	/* first, disable everything */
+	if (UART_ENABLE_MS(port, termios->c_cflag))
+		ks8695uart_enable_ms(port);
+	else
+		ks8695uart_disable_ms(port);
+
+	/* Set baud rate */
+	UART_PUT_BRDR(port, quot);
+
+	UART_PUT_LCR(port, lcr);
+	UART_PUT_FCR(port, fcr);
+
+	spin_unlock_irqrestore(&port->lock, flags);
+}
+
+static const char *ks8695uart_type(struct uart_port *port)
+{
+	return port->type == PORT_KS8695 ? "KS8695" : NULL;
+}
+
+/*
+ * Release the memory region(s) being used by 'port'
+ */
+static void ks8695uart_release_port(struct uart_port *port)
+{
+	release_mem_region(port->mapbase, UART_PORT_SIZE);
+}
+
+/*
+ * Request the memory region(s) being used by 'port'
+ */
+static int ks8695uart_request_port(struct uart_port *port)
+{
+	return request_mem_region(port->mapbase, UART_PORT_SIZE,
+			"serial_ks8695") != NULL ? 0 : -EBUSY;
+}
+
+/*
+ * Configure/autoconfigure the port.
+ */
+static void ks8695uart_config_port(struct uart_port *port, int flags)
+{
+	if (flags & UART_CONFIG_TYPE) {
+		port->type = PORT_KS8695;
+		ks8695uart_request_port(port);
+	}
+}
+
+/*
+ * verify the new serial_struct (for TIOCSSERIAL).
+ */
+static int ks8695uart_verify_port(struct uart_port *port, struct serial_struct *ser)
+{
+	int ret = 0;
+
+	if (ser->type != PORT_UNKNOWN && ser->type != PORT_KS8695)
+		ret = -EINVAL;
+	if (ser->irq != port->irq)
+		ret = -EINVAL;
+	if (ser->baud_base < 9600)
+		ret = -EINVAL;
+	return ret;
+}
+
+static struct uart_ops ks8695uart_pops = {
+	.tx_empty	= ks8695uart_tx_empty,
+	.set_mctrl	= ks8695uart_set_mctrl,
+	.get_mctrl	= ks8695uart_get_mctrl,
+	.stop_tx	= ks8695uart_stop_tx,
+	.start_tx	= ks8695uart_start_tx,
+	.stop_rx	= ks8695uart_stop_rx,
+	.enable_ms	= ks8695uart_enable_ms,
+	.break_ctl	= ks8695uart_break_ctl,
+	.startup	= ks8695uart_startup,
+	.shutdown	= ks8695uart_shutdown,
+	.set_termios	= ks8695uart_set_termios,
+	.type		= ks8695uart_type,
+	.release_port	= ks8695uart_release_port,
+	.request_port	= ks8695uart_request_port,
+	.config_port	= ks8695uart_config_port,
+	.verify_port	= ks8695uart_verify_port,
+};
+
+static struct uart_port ks8695uart_ports[SERIAL_KS8695_NR] = {
+	{
+		.membase	= (void *) KS8695_UART_VA,
+		.mapbase	= KS8695_UART_VA,
+		.iotype		= SERIAL_IO_MEM,
+		.irq		= KS8695_IRQ_UART_TX,
+		.uartclk	= KS8695_CLOCK_RATE * 16,
+		.fifosize	= 16,
+		.ops		= &ks8695uart_pops,
+		.flags		= ASYNC_BOOT_AUTOCONF,
+		.line		= 0,
+	}
+};
+
+#ifdef CONFIG_SERIAL_KS8695_CONSOLE
+static void ks8695_console_putchar(struct uart_port *port, int ch)
+{
+	while (!(UART_GET_LSR(port) & URLS_URTHRE))
+		barrier();
+
+	UART_PUT_CHAR(port, ch);
+}
+
+static void ks8695_console_write(struct console *co, const char *s, u_int count)
+{
+	struct uart_port *port = ks8695uart_ports + co->index;
+
+	uart_console_write(port, s, count, ks8695_console_putchar);
+}
+
+static void __init ks8695_console_get_options(struct uart_port *port, int *baud, int *parity, int *bits)
+{
+	unsigned int lcr;
+
+	lcr = UART_GET_LCR(port);
+
+	switch (lcr & URLC_PARITY) {
+		case URPE_ODD:
+			*parity = 'o';
+			break;
+		case URPE_EVEN:
+			*parity = 'e';
+			break;
+		default:
+			*parity = 'n';
+	}
+
+	switch (lcr & URLC_URCL) {
+		case URCL_5:
+			*bits = 5;
+			break;
+		case URCL_6:
+			*bits = 6;
+			break;
+		case URCL_7:
+			*bits = 7;
+			break;
+		default:
+			*bits = 8;
+	}
+
+	*baud = port->uartclk / (UART_GET_BRDR(port) & 0x0FFF);
+	*baud /= 16;
+	*baud &= 0xFFFFFFF0;
+}
+
+static int __init ks8695_console_setup(struct console *co, char *options)
+{
+	struct uart_port *port;
+	int baud = 115200;
+	int bits = 8;
+	int parity = 'n';
+	int flow = 'n';
+
+	/*
+	 * Check whether an invalid uart number has been specified, and
+	 * if so, search for the first available port that does have
+	 * console support.
+	 */
+	port = uart_get_console(ks8695uart_ports, SERIAL_KS8695_NR, co);
+
+	if (options)
+		uart_parse_options(options, &baud, &parity, &bits, &flow);
+	else
+		ks8695_console_get_options(port, &baud, &parity, &bits);
+
+	return uart_set_options(port, co, baud, parity, bits, flow);
+}
+
+static struct uart_driver ks8695_reg;
+
+static struct console ks8695_console = {
+	.name		= SERIAL_KS8695_DEVNAME,
+	.write		= ks8695_console_write,
+	.device		= uart_console_device,
+	.setup		= ks8695_console_setup,
+	.flags		= CON_PRINTBUFFER,
+	.index		= -1,
+	.data		= &ks8695_reg,
+};
+
+static int __init ks8695_console_init(void)
+{
+	add_preferred_console(SERIAL_KS8695_DEVNAME, 0, NULL);
+	register_console(&ks8695_console);
+	return 0;
+}
+
+console_initcall(ks8695_console_init);
+
+#define KS8695_CONSOLE	&ks8695_console
+#else
+#define KS8695_CONSOLE	NULL
+#endif
+
+static struct uart_driver ks8695_reg = {
+	.owner			= THIS_MODULE,
+	.driver_name		= "serial_ks8695",
+	.dev_name		= SERIAL_KS8695_DEVNAME,
+	.major			= SERIAL_KS8695_MAJOR,
+	.minor			= SERIAL_KS8695_MINOR,
+	.nr			= SERIAL_KS8695_NR,
+	.cons			= KS8695_CONSOLE,
+};
+
+static int __init ks8695uart_init(void)
+{
+	int i, ret;
+
+	printk(KERN_INFO "Serial: Micrel KS8695 UART driver\n");
+
+	ret = uart_register_driver(&ks8695_reg);
+	if (ret)
+		return ret;
+
+	for (i = 0; i < SERIAL_KS8695_NR; i++)
+		uart_add_one_port(&ks8695_reg, &ks8695uart_ports[0]);
+
+	return 0;
+}
+
+static void __exit ks8695uart_exit(void)
+{
+	int i;
+
+	for (i = 0; i < SERIAL_KS8695_NR; i++)
+		uart_remove_one_port(&ks8695_reg, &ks8695uart_ports[0]);
+	uart_unregister_driver(&ks8695_reg);
+}
+
+module_init(ks8695uart_init);
+module_exit(ks8695uart_exit);
+
+MODULE_DESCRIPTION("KS8695 serial port driver");
+MODULE_AUTHOR("Micrel Inc.");
+MODULE_LICENSE("GPL");
diff --git a/ap/os/linux/linux-3.4.x/drivers/tty/serial/serial_txx9.c b/ap/os/linux/linux-3.4.x/drivers/tty/serial/serial_txx9.c
new file mode 100644
index 0000000..34bd345
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/tty/serial/serial_txx9.c
@@ -0,0 +1,1344 @@
+/*
+ * Derived from many drivers using generic_serial interface,
+ * especially serial_tx3912.c by Steven J. Hill and r39xx_serial.c
+ * (was in Linux/VR tree) by Jim Pick.
+ *
+ *  Copyright (C) 1999 Harald Koerfgen
+ *  Copyright (C) 2000 Jim Pick <jim@jimpick.com>
+ *  Copyright (C) 2001 Steven J. Hill (sjhill@realitydiluted.com)
+ *  Copyright (C) 2000-2002 Toshiba Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ *  Serial driver for TX3927/TX4927/TX4925/TX4938 internal SIO controller
+ */
+
+#if defined(CONFIG_SERIAL_TXX9_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ)
+#define SUPPORT_SYSRQ
+#endif
+
+#include <linux/module.h>
+#include <linux/ioport.h>
+#include <linux/init.h>
+#include <linux/console.h>
+#include <linux/delay.h>
+#include <linux/platform_device.h>
+#include <linux/pci.h>
+#include <linux/serial_core.h>
+#include <linux/serial.h>
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
+
+#include <asm/io.h>
+
+static char *serial_version = "1.11";
+static char *serial_name = "TX39/49 Serial driver";
+
+#define PASS_LIMIT	256
+
+#if !defined(CONFIG_SERIAL_TXX9_STDSERIAL)
+/* "ttyS" is used for standard serial driver */
+#define TXX9_TTY_NAME "ttyTX"
+#define TXX9_TTY_MINOR_START	196
+#define TXX9_TTY_MAJOR	204
+#else
+/* acts like standard serial driver */
+#define TXX9_TTY_NAME "ttyS"
+#define TXX9_TTY_MINOR_START	64
+#define TXX9_TTY_MAJOR	TTY_MAJOR
+#endif
+
+/* flag aliases */
+#define UPF_TXX9_HAVE_CTS_LINE	UPF_BUGGY_UART
+#define UPF_TXX9_USE_SCLK	UPF_MAGIC_MULTIPLIER
+
+#ifdef CONFIG_PCI
+/* support for Toshiba TC86C001 SIO */
+#define ENABLE_SERIAL_TXX9_PCI
+#endif
+
+/*
+ * Number of serial ports
+ */
+#define UART_NR  CONFIG_SERIAL_TXX9_NR_UARTS
+
+struct uart_txx9_port {
+	struct uart_port	port;
+	/* No additional info for now */
+};
+
+#define TXX9_REGION_SIZE	0x24
+
+/* TXX9 Serial Registers */
+#define TXX9_SILCR	0x00
+#define TXX9_SIDICR	0x04
+#define TXX9_SIDISR	0x08
+#define TXX9_SICISR	0x0c
+#define TXX9_SIFCR	0x10
+#define TXX9_SIFLCR	0x14
+#define TXX9_SIBGR	0x18
+#define TXX9_SITFIFO	0x1c
+#define TXX9_SIRFIFO	0x20
+
+/* SILCR : Line Control */
+#define TXX9_SILCR_SCS_MASK	0x00000060
+#define TXX9_SILCR_SCS_IMCLK	0x00000000
+#define TXX9_SILCR_SCS_IMCLK_BG	0x00000020
+#define TXX9_SILCR_SCS_SCLK	0x00000040
+#define TXX9_SILCR_SCS_SCLK_BG	0x00000060
+#define TXX9_SILCR_UEPS	0x00000010
+#define TXX9_SILCR_UPEN	0x00000008
+#define TXX9_SILCR_USBL_MASK	0x00000004
+#define TXX9_SILCR_USBL_1BIT	0x00000000
+#define TXX9_SILCR_USBL_2BIT	0x00000004
+#define TXX9_SILCR_UMODE_MASK	0x00000003
+#define TXX9_SILCR_UMODE_8BIT	0x00000000
+#define TXX9_SILCR_UMODE_7BIT	0x00000001
+
+/* SIDICR : DMA/Int. Control */
+#define TXX9_SIDICR_TDE	0x00008000
+#define TXX9_SIDICR_RDE	0x00004000
+#define TXX9_SIDICR_TIE	0x00002000
+#define TXX9_SIDICR_RIE	0x00001000
+#define TXX9_SIDICR_SPIE	0x00000800
+#define TXX9_SIDICR_CTSAC	0x00000600
+#define TXX9_SIDICR_STIE_MASK	0x0000003f
+#define TXX9_SIDICR_STIE_OERS		0x00000020
+#define TXX9_SIDICR_STIE_CTSS		0x00000010
+#define TXX9_SIDICR_STIE_RBRKD	0x00000008
+#define TXX9_SIDICR_STIE_TRDY		0x00000004
+#define TXX9_SIDICR_STIE_TXALS	0x00000002
+#define TXX9_SIDICR_STIE_UBRKD	0x00000001
+
+/* SIDISR : DMA/Int. Status */
+#define TXX9_SIDISR_UBRK	0x00008000
+#define TXX9_SIDISR_UVALID	0x00004000
+#define TXX9_SIDISR_UFER	0x00002000
+#define TXX9_SIDISR_UPER	0x00001000
+#define TXX9_SIDISR_UOER	0x00000800
+#define TXX9_SIDISR_ERI	0x00000400
+#define TXX9_SIDISR_TOUT	0x00000200
+#define TXX9_SIDISR_TDIS	0x00000100
+#define TXX9_SIDISR_RDIS	0x00000080
+#define TXX9_SIDISR_STIS	0x00000040
+#define TXX9_SIDISR_RFDN_MASK	0x0000001f
+
+/* SICISR : Change Int. Status */
+#define TXX9_SICISR_OERS	0x00000020
+#define TXX9_SICISR_CTSS	0x00000010
+#define TXX9_SICISR_RBRKD	0x00000008
+#define TXX9_SICISR_TRDY	0x00000004
+#define TXX9_SICISR_TXALS	0x00000002
+#define TXX9_SICISR_UBRKD	0x00000001
+
+/* SIFCR : FIFO Control */
+#define TXX9_SIFCR_SWRST	0x00008000
+#define TXX9_SIFCR_RDIL_MASK	0x00000180
+#define TXX9_SIFCR_RDIL_1	0x00000000
+#define TXX9_SIFCR_RDIL_4	0x00000080
+#define TXX9_SIFCR_RDIL_8	0x00000100
+#define TXX9_SIFCR_RDIL_12	0x00000180
+#define TXX9_SIFCR_RDIL_MAX	0x00000180
+#define TXX9_SIFCR_TDIL_MASK	0x00000018
+#define TXX9_SIFCR_TDIL_MASK	0x00000018
+#define TXX9_SIFCR_TDIL_1	0x00000000
+#define TXX9_SIFCR_TDIL_4	0x00000001
+#define TXX9_SIFCR_TDIL_8	0x00000010
+#define TXX9_SIFCR_TDIL_MAX	0x00000010
+#define TXX9_SIFCR_TFRST	0x00000004
+#define TXX9_SIFCR_RFRST	0x00000002
+#define TXX9_SIFCR_FRSTE	0x00000001
+#define TXX9_SIO_TX_FIFO	8
+#define TXX9_SIO_RX_FIFO	16
+
+/* SIFLCR : Flow Control */
+#define TXX9_SIFLCR_RCS	0x00001000
+#define TXX9_SIFLCR_TES	0x00000800
+#define TXX9_SIFLCR_RTSSC	0x00000200
+#define TXX9_SIFLCR_RSDE	0x00000100
+#define TXX9_SIFLCR_TSDE	0x00000080
+#define TXX9_SIFLCR_RTSTL_MASK	0x0000001e
+#define TXX9_SIFLCR_RTSTL_MAX	0x0000001e
+#define TXX9_SIFLCR_TBRK	0x00000001
+
+/* SIBGR : Baudrate Control */
+#define TXX9_SIBGR_BCLK_MASK	0x00000300
+#define TXX9_SIBGR_BCLK_T0	0x00000000
+#define TXX9_SIBGR_BCLK_T2	0x00000100
+#define TXX9_SIBGR_BCLK_T4	0x00000200
+#define TXX9_SIBGR_BCLK_T6	0x00000300
+#define TXX9_SIBGR_BRD_MASK	0x000000ff
+
+static inline unsigned int sio_in(struct uart_txx9_port *up, int offset)
+{
+	switch (up->port.iotype) {
+	default:
+		return __raw_readl(up->port.membase + offset);
+	case UPIO_PORT:
+		return inl(up->port.iobase + offset);
+	}
+}
+
+static inline void
+sio_out(struct uart_txx9_port *up, int offset, int value)
+{
+	switch (up->port.iotype) {
+	default:
+		__raw_writel(value, up->port.membase + offset);
+		break;
+	case UPIO_PORT:
+		outl(value, up->port.iobase + offset);
+		break;
+	}
+}
+
+static inline void
+sio_mask(struct uart_txx9_port *up, int offset, unsigned int value)
+{
+	sio_out(up, offset, sio_in(up, offset) & ~value);
+}
+static inline void
+sio_set(struct uart_txx9_port *up, int offset, unsigned int value)
+{
+	sio_out(up, offset, sio_in(up, offset) | value);
+}
+
+static inline void
+sio_quot_set(struct uart_txx9_port *up, int quot)
+{
+	quot >>= 1;
+	if (quot < 256)
+		sio_out(up, TXX9_SIBGR, quot | TXX9_SIBGR_BCLK_T0);
+	else if (quot < (256 << 2))
+		sio_out(up, TXX9_SIBGR, (quot >> 2) | TXX9_SIBGR_BCLK_T2);
+	else if (quot < (256 << 4))
+		sio_out(up, TXX9_SIBGR, (quot >> 4) | TXX9_SIBGR_BCLK_T4);
+	else if (quot < (256 << 6))
+		sio_out(up, TXX9_SIBGR, (quot >> 6) | TXX9_SIBGR_BCLK_T6);
+	else
+		sio_out(up, TXX9_SIBGR, 0xff | TXX9_SIBGR_BCLK_T6);
+}
+
+static struct uart_txx9_port *to_uart_txx9_port(struct uart_port *port)
+{
+	return container_of(port, struct uart_txx9_port, port);
+}
+
+static void serial_txx9_stop_tx(struct uart_port *port)
+{
+	struct uart_txx9_port *up = to_uart_txx9_port(port);
+	sio_mask(up, TXX9_SIDICR, TXX9_SIDICR_TIE);
+}
+
+static void serial_txx9_start_tx(struct uart_port *port)
+{
+	struct uart_txx9_port *up = to_uart_txx9_port(port);
+	sio_set(up, TXX9_SIDICR, TXX9_SIDICR_TIE);
+}
+
+static void serial_txx9_stop_rx(struct uart_port *port)
+{
+	struct uart_txx9_port *up = to_uart_txx9_port(port);
+	up->port.read_status_mask &= ~TXX9_SIDISR_RDIS;
+}
+
+static void serial_txx9_enable_ms(struct uart_port *port)
+{
+	/* TXX9-SIO can not control DTR... */
+}
+
+static void serial_txx9_initialize(struct uart_port *port)
+{
+	struct uart_txx9_port *up = to_uart_txx9_port(port);
+	unsigned int tmout = 10000;
+
+	sio_out(up, TXX9_SIFCR, TXX9_SIFCR_SWRST);
+	/* TX4925 BUG WORKAROUND.  Accessing SIOC register
+	 * immediately after soft reset causes bus error. */
+	mmiowb();
+	udelay(1);
+	while ((sio_in(up, TXX9_SIFCR) & TXX9_SIFCR_SWRST) && --tmout)
+		udelay(1);
+	/* TX Int by FIFO Empty, RX Int by Receiving 1 char. */
+	sio_set(up, TXX9_SIFCR,
+		TXX9_SIFCR_TDIL_MAX | TXX9_SIFCR_RDIL_1);
+	/* initial settings */
+	sio_out(up, TXX9_SILCR,
+		TXX9_SILCR_UMODE_8BIT | TXX9_SILCR_USBL_1BIT |
+		((up->port.flags & UPF_TXX9_USE_SCLK) ?
+		 TXX9_SILCR_SCS_SCLK_BG : TXX9_SILCR_SCS_IMCLK_BG));
+	sio_quot_set(up, uart_get_divisor(port, 9600));
+	sio_out(up, TXX9_SIFLCR, TXX9_SIFLCR_RTSTL_MAX /* 15 */);
+	sio_out(up, TXX9_SIDICR, 0);
+}
+
+static inline void
+receive_chars(struct uart_txx9_port *up, unsigned int *status)
+{
+	struct tty_struct *tty = up->port.state->port.tty;
+	unsigned char ch;
+	unsigned int disr = *status;
+	int max_count = 256;
+	char flag;
+	unsigned int next_ignore_status_mask;
+
+	do {
+		ch = sio_in(up, TXX9_SIRFIFO);
+		flag = TTY_NORMAL;
+		up->port.icount.rx++;
+
+		/* mask out RFDN_MASK bit added by previous overrun */
+		next_ignore_status_mask =
+			up->port.ignore_status_mask & ~TXX9_SIDISR_RFDN_MASK;
+		if (unlikely(disr & (TXX9_SIDISR_UBRK | TXX9_SIDISR_UPER |
+				     TXX9_SIDISR_UFER | TXX9_SIDISR_UOER))) {
+			/*
+			 * For statistics only
+			 */
+			if (disr & TXX9_SIDISR_UBRK) {
+				disr &= ~(TXX9_SIDISR_UFER | TXX9_SIDISR_UPER);
+				up->port.icount.brk++;
+				/*
+				 * We do the SysRQ and SAK checking
+				 * here because otherwise the break
+				 * may get masked by ignore_status_mask
+				 * or read_status_mask.
+				 */
+				if (uart_handle_break(&up->port))
+					goto ignore_char;
+			} else if (disr & TXX9_SIDISR_UPER)
+				up->port.icount.parity++;
+			else if (disr & TXX9_SIDISR_UFER)
+				up->port.icount.frame++;
+			if (disr & TXX9_SIDISR_UOER) {
+				up->port.icount.overrun++;
+				/*
+				 * The receiver read buffer still hold
+				 * a char which caused overrun.
+				 * Ignore next char by adding RFDN_MASK
+				 * to ignore_status_mask temporarily.
+				 */
+				next_ignore_status_mask |=
+					TXX9_SIDISR_RFDN_MASK;
+			}
+
+			/*
+			 * Mask off conditions which should be ingored.
+			 */
+			disr &= up->port.read_status_mask;
+
+			if (disr & TXX9_SIDISR_UBRK) {
+				flag = TTY_BREAK;
+			} else if (disr & TXX9_SIDISR_UPER)
+				flag = TTY_PARITY;
+			else if (disr & TXX9_SIDISR_UFER)
+				flag = TTY_FRAME;
+		}
+		if (uart_handle_sysrq_char(&up->port, ch))
+			goto ignore_char;
+
+		uart_insert_char(&up->port, disr, TXX9_SIDISR_UOER, ch, flag);
+
+	ignore_char:
+		up->port.ignore_status_mask = next_ignore_status_mask;
+		disr = sio_in(up, TXX9_SIDISR);
+	} while (!(disr & TXX9_SIDISR_UVALID) && (max_count-- > 0));
+	spin_unlock(&up->port.lock);
+	tty_flip_buffer_push(tty);
+	spin_lock(&up->port.lock);
+	*status = disr;
+}
+
+static inline void transmit_chars(struct uart_txx9_port *up)
+{
+	struct circ_buf *xmit = &up->port.state->xmit;
+	int count;
+
+	if (up->port.x_char) {
+		sio_out(up, TXX9_SITFIFO, up->port.x_char);
+		up->port.icount.tx++;
+		up->port.x_char = 0;
+		return;
+	}
+	if (uart_circ_empty(xmit) || uart_tx_stopped(&up->port)) {
+		serial_txx9_stop_tx(&up->port);
+		return;
+	}
+
+	count = TXX9_SIO_TX_FIFO;
+	do {
+		sio_out(up, TXX9_SITFIFO, 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))
+		serial_txx9_stop_tx(&up->port);
+}
+
+static irqreturn_t serial_txx9_interrupt(int irq, void *dev_id)
+{
+	int pass_counter = 0;
+	struct uart_txx9_port *up = dev_id;
+	unsigned int status;
+
+	while (1) {
+		spin_lock(&up->port.lock);
+		status = sio_in(up, TXX9_SIDISR);
+		if (!(sio_in(up, TXX9_SIDICR) & TXX9_SIDICR_TIE))
+			status &= ~TXX9_SIDISR_TDIS;
+		if (!(status & (TXX9_SIDISR_TDIS | TXX9_SIDISR_RDIS |
+				TXX9_SIDISR_TOUT))) {
+			spin_unlock(&up->port.lock);
+			break;
+		}
+
+		if (status & TXX9_SIDISR_RDIS)
+			receive_chars(up, &status);
+		if (status & TXX9_SIDISR_TDIS)
+			transmit_chars(up);
+		/* Clear TX/RX Int. Status */
+		sio_mask(up, TXX9_SIDISR,
+			 TXX9_SIDISR_TDIS | TXX9_SIDISR_RDIS |
+			 TXX9_SIDISR_TOUT);
+		spin_unlock(&up->port.lock);
+
+		if (pass_counter++ > PASS_LIMIT)
+			break;
+	}
+
+	return pass_counter ? IRQ_HANDLED : IRQ_NONE;
+}
+
+static unsigned int serial_txx9_tx_empty(struct uart_port *port)
+{
+	struct uart_txx9_port *up = to_uart_txx9_port(port);
+	unsigned long flags;
+	unsigned int ret;
+
+	spin_lock_irqsave(&up->port.lock, flags);
+	ret = (sio_in(up, TXX9_SICISR) & TXX9_SICISR_TXALS) ? TIOCSER_TEMT : 0;
+	spin_unlock_irqrestore(&up->port.lock, flags);
+
+	return ret;
+}
+
+static unsigned int serial_txx9_get_mctrl(struct uart_port *port)
+{
+	struct uart_txx9_port *up = to_uart_txx9_port(port);
+	unsigned int ret;
+
+	/* no modem control lines */
+	ret = TIOCM_CAR | TIOCM_DSR;
+	ret |= (sio_in(up, TXX9_SIFLCR) & TXX9_SIFLCR_RTSSC) ? 0 : TIOCM_RTS;
+	ret |= (sio_in(up, TXX9_SICISR) & TXX9_SICISR_CTSS) ? 0 : TIOCM_CTS;
+
+	return ret;
+}
+
+static void serial_txx9_set_mctrl(struct uart_port *port, unsigned int mctrl)
+{
+	struct uart_txx9_port *up = to_uart_txx9_port(port);
+
+	if (mctrl & TIOCM_RTS)
+		sio_mask(up, TXX9_SIFLCR, TXX9_SIFLCR_RTSSC);
+	else
+		sio_set(up, TXX9_SIFLCR, TXX9_SIFLCR_RTSSC);
+}
+
+static void serial_txx9_break_ctl(struct uart_port *port, int break_state)
+{
+	struct uart_txx9_port *up = to_uart_txx9_port(port);
+	unsigned long flags;
+
+	spin_lock_irqsave(&up->port.lock, flags);
+	if (break_state == -1)
+		sio_set(up, TXX9_SIFLCR, TXX9_SIFLCR_TBRK);
+	else
+		sio_mask(up, TXX9_SIFLCR, TXX9_SIFLCR_TBRK);
+	spin_unlock_irqrestore(&up->port.lock, flags);
+}
+
+#if defined(CONFIG_SERIAL_TXX9_CONSOLE) || (CONFIG_CONSOLE_POLL)
+/*
+ *	Wait for transmitter & holding register to empty
+ */
+static void wait_for_xmitr(struct uart_txx9_port *up)
+{
+	unsigned int tmout = 10000;
+
+	/* Wait up to 10ms for the character(s) to be sent. */
+	while (--tmout &&
+	       !(sio_in(up, TXX9_SICISR) & TXX9_SICISR_TXALS))
+		udelay(1);
+
+	/* Wait up to 1s for flow control if necessary */
+	if (up->port.flags & UPF_CONS_FLOW) {
+		tmout = 1000000;
+		while (--tmout &&
+		       (sio_in(up, TXX9_SICISR) & TXX9_SICISR_CTSS))
+			udelay(1);
+	}
+}
+#endif
+
+#ifdef CONFIG_CONSOLE_POLL
+/*
+ * Console polling routines for writing and reading from the uart while
+ * in an interrupt or debug context.
+ */
+
+static int serial_txx9_get_poll_char(struct uart_port *port)
+{
+	unsigned int ier;
+	unsigned char c;
+	struct uart_txx9_port *up = to_uart_txx9_port(port);
+
+	/*
+	 *	First save the IER then disable the interrupts
+	 */
+	ier = sio_in(up, TXX9_SIDICR);
+	sio_out(up, TXX9_SIDICR, 0);
+
+	while (sio_in(up, TXX9_SIDISR) & TXX9_SIDISR_UVALID)
+		;
+
+	c = sio_in(up, TXX9_SIRFIFO);
+
+	/*
+	 *	Finally, clear RX interrupt status
+	 *	and restore the IER
+	 */
+	sio_mask(up, TXX9_SIDISR, TXX9_SIDISR_RDIS);
+	sio_out(up, TXX9_SIDICR, ier);
+	return c;
+}
+
+
+static void serial_txx9_put_poll_char(struct uart_port *port, unsigned char c)
+{
+	unsigned int ier;
+	struct uart_txx9_port *up = to_uart_txx9_port(port);
+
+	/*
+	 *	First save the IER then disable the interrupts
+	 */
+	ier = sio_in(up, TXX9_SIDICR);
+	sio_out(up, TXX9_SIDICR, 0);
+
+	wait_for_xmitr(up);
+	/*
+	 *	Send the character out.
+	 *	If a LF, also do CR...
+	 */
+	sio_out(up, TXX9_SITFIFO, c);
+	if (c == 10) {
+		wait_for_xmitr(up);
+		sio_out(up, TXX9_SITFIFO, 13);
+	}
+
+	/*
+	 *	Finally, wait for transmitter to become empty
+	 *	and restore the IER
+	 */
+	wait_for_xmitr(up);
+	sio_out(up, TXX9_SIDICR, ier);
+}
+
+#endif /* CONFIG_CONSOLE_POLL */
+
+static int serial_txx9_startup(struct uart_port *port)
+{
+	struct uart_txx9_port *up = to_uart_txx9_port(port);
+	unsigned long flags;
+	int retval;
+
+	/*
+	 * Clear the FIFO buffers and disable them.
+	 * (they will be reenabled in set_termios())
+	 */
+	sio_set(up, TXX9_SIFCR,
+		TXX9_SIFCR_TFRST | TXX9_SIFCR_RFRST | TXX9_SIFCR_FRSTE);
+	/* clear reset */
+	sio_mask(up, TXX9_SIFCR,
+		 TXX9_SIFCR_TFRST | TXX9_SIFCR_RFRST | TXX9_SIFCR_FRSTE);
+	sio_out(up, TXX9_SIDICR, 0);
+
+	/*
+	 * Clear the interrupt registers.
+	 */
+	sio_out(up, TXX9_SIDISR, 0);
+
+	retval = request_irq(up->port.irq, serial_txx9_interrupt,
+			     IRQF_SHARED, "serial_txx9", up);
+	if (retval)
+		return retval;
+
+	/*
+	 * Now, initialize the UART
+	 */
+	spin_lock_irqsave(&up->port.lock, flags);
+	serial_txx9_set_mctrl(&up->port, up->port.mctrl);
+	spin_unlock_irqrestore(&up->port.lock, flags);
+
+	/* Enable RX/TX */
+	sio_mask(up, TXX9_SIFLCR, TXX9_SIFLCR_RSDE | TXX9_SIFLCR_TSDE);
+
+	/*
+	 * Finally, enable interrupts.
+	 */
+	sio_set(up, TXX9_SIDICR, TXX9_SIDICR_RIE);
+
+	return 0;
+}
+
+static void serial_txx9_shutdown(struct uart_port *port)
+{
+	struct uart_txx9_port *up = to_uart_txx9_port(port);
+	unsigned long flags;
+
+	/*
+	 * Disable interrupts from this port
+	 */
+	sio_out(up, TXX9_SIDICR, 0);	/* disable all intrs */
+
+	spin_lock_irqsave(&up->port.lock, flags);
+	serial_txx9_set_mctrl(&up->port, up->port.mctrl);
+	spin_unlock_irqrestore(&up->port.lock, flags);
+
+	/*
+	 * Disable break condition
+	 */
+	sio_mask(up, TXX9_SIFLCR, TXX9_SIFLCR_TBRK);
+
+#ifdef CONFIG_SERIAL_TXX9_CONSOLE
+	if (up->port.cons && up->port.line == up->port.cons->index) {
+		free_irq(up->port.irq, up);
+		return;
+	}
+#endif
+	/* reset FIFOs */
+	sio_set(up, TXX9_SIFCR,
+		TXX9_SIFCR_TFRST | TXX9_SIFCR_RFRST | TXX9_SIFCR_FRSTE);
+	/* clear reset */
+	sio_mask(up, TXX9_SIFCR,
+		 TXX9_SIFCR_TFRST | TXX9_SIFCR_RFRST | TXX9_SIFCR_FRSTE);
+
+	/* Disable RX/TX */
+	sio_set(up, TXX9_SIFLCR, TXX9_SIFLCR_RSDE | TXX9_SIFLCR_TSDE);
+
+	free_irq(up->port.irq, up);
+}
+
+static void
+serial_txx9_set_termios(struct uart_port *port, struct ktermios *termios,
+		       struct ktermios *old)
+{
+	struct uart_txx9_port *up = to_uart_txx9_port(port);
+	unsigned int cval, fcr = 0;
+	unsigned long flags;
+	unsigned int baud, quot;
+
+	/*
+	 * We don't support modem control lines.
+	 */
+	termios->c_cflag &= ~(HUPCL | CMSPAR);
+	termios->c_cflag |= CLOCAL;
+
+	cval = sio_in(up, TXX9_SILCR);
+	/* byte size and parity */
+	cval &= ~TXX9_SILCR_UMODE_MASK;
+	switch (termios->c_cflag & CSIZE) {
+	case CS7:
+		cval |= TXX9_SILCR_UMODE_7BIT;
+		break;
+	default:
+	case CS5:	/* not supported */
+	case CS6:	/* not supported */
+	case CS8:
+		cval |= TXX9_SILCR_UMODE_8BIT;
+		break;
+	}
+
+	cval &= ~TXX9_SILCR_USBL_MASK;
+	if (termios->c_cflag & CSTOPB)
+		cval |= TXX9_SILCR_USBL_2BIT;
+	else
+		cval |= TXX9_SILCR_USBL_1BIT;
+	cval &= ~(TXX9_SILCR_UPEN | TXX9_SILCR_UEPS);
+	if (termios->c_cflag & PARENB)
+		cval |= TXX9_SILCR_UPEN;
+	if (!(termios->c_cflag & PARODD))
+		cval |= TXX9_SILCR_UEPS;
+
+	/*
+	 * Ask the core to calculate the divisor for us.
+	 */
+	baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk/16/2);
+	quot = uart_get_divisor(port, baud);
+
+	/* Set up FIFOs */
+	/* TX Int by FIFO Empty, RX Int by Receiving 1 char. */
+	fcr = TXX9_SIFCR_TDIL_MAX | TXX9_SIFCR_RDIL_1;
+
+	/*
+	 * 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 = TXX9_SIDISR_UOER |
+		TXX9_SIDISR_TDIS | TXX9_SIDISR_RDIS;
+	if (termios->c_iflag & INPCK)
+		up->port.read_status_mask |= TXX9_SIDISR_UFER | TXX9_SIDISR_UPER;
+	if (termios->c_iflag & (BRKINT | PARMRK))
+		up->port.read_status_mask |= TXX9_SIDISR_UBRK;
+
+	/*
+	 * Characteres to ignore
+	 */
+	up->port.ignore_status_mask = 0;
+	if (termios->c_iflag & IGNPAR)
+		up->port.ignore_status_mask |= TXX9_SIDISR_UPER | TXX9_SIDISR_UFER;
+	if (termios->c_iflag & IGNBRK) {
+		up->port.ignore_status_mask |= TXX9_SIDISR_UBRK;
+		/*
+		 * 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 |= TXX9_SIDISR_UOER;
+	}
+
+	/*
+	 * ignore all characters if CREAD is not set
+	 */
+	if ((termios->c_cflag & CREAD) == 0)
+		up->port.ignore_status_mask |= TXX9_SIDISR_RDIS;
+
+	/* CTS flow control flag */
+	if ((termios->c_cflag & CRTSCTS) &&
+	    (up->port.flags & UPF_TXX9_HAVE_CTS_LINE)) {
+		sio_set(up, TXX9_SIFLCR,
+			TXX9_SIFLCR_RCS | TXX9_SIFLCR_TES);
+	} else {
+		sio_mask(up, TXX9_SIFLCR,
+			 TXX9_SIFLCR_RCS | TXX9_SIFLCR_TES);
+	}
+
+	sio_out(up, TXX9_SILCR, cval);
+	sio_quot_set(up, quot);
+	sio_out(up, TXX9_SIFCR, fcr);
+
+	serial_txx9_set_mctrl(&up->port, up->port.mctrl);
+	spin_unlock_irqrestore(&up->port.lock, flags);
+}
+
+static void
+serial_txx9_pm(struct uart_port *port, unsigned int state,
+	      unsigned int oldstate)
+{
+	/*
+	 * If oldstate was -1 this is called from
+	 * uart_configure_port().  In this case do not initialize the
+	 * port now, because the port was already initialized (for
+	 * non-console port) or should not be initialized here (for
+	 * console port).  If we initialized the port here we lose
+	 * serial console settings.
+	 */
+	if (state == 0 && oldstate != -1)
+		serial_txx9_initialize(port);
+}
+
+static int serial_txx9_request_resource(struct uart_txx9_port *up)
+{
+	unsigned int size = TXX9_REGION_SIZE;
+	int ret = 0;
+
+	switch (up->port.iotype) {
+	default:
+		if (!up->port.mapbase)
+			break;
+
+		if (!request_mem_region(up->port.mapbase, size, "serial_txx9")) {
+			ret = -EBUSY;
+			break;
+		}
+
+		if (up->port.flags & UPF_IOREMAP) {
+			up->port.membase = ioremap(up->port.mapbase, size);
+			if (!up->port.membase) {
+				release_mem_region(up->port.mapbase, size);
+				ret = -ENOMEM;
+			}
+		}
+		break;
+
+	case UPIO_PORT:
+		if (!request_region(up->port.iobase, size, "serial_txx9"))
+			ret = -EBUSY;
+		break;
+	}
+	return ret;
+}
+
+static void serial_txx9_release_resource(struct uart_txx9_port *up)
+{
+	unsigned int size = TXX9_REGION_SIZE;
+
+	switch (up->port.iotype) {
+	default:
+		if (!up->port.mapbase)
+			break;
+
+		if (up->port.flags & UPF_IOREMAP) {
+			iounmap(up->port.membase);
+			up->port.membase = NULL;
+		}
+
+		release_mem_region(up->port.mapbase, size);
+		break;
+
+	case UPIO_PORT:
+		release_region(up->port.iobase, size);
+		break;
+	}
+}
+
+static void serial_txx9_release_port(struct uart_port *port)
+{
+	struct uart_txx9_port *up = to_uart_txx9_port(port);
+	serial_txx9_release_resource(up);
+}
+
+static int serial_txx9_request_port(struct uart_port *port)
+{
+	struct uart_txx9_port *up = to_uart_txx9_port(port);
+	return serial_txx9_request_resource(up);
+}
+
+static void serial_txx9_config_port(struct uart_port *port, int uflags)
+{
+	struct uart_txx9_port *up = to_uart_txx9_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 = serial_txx9_request_resource(up);
+	if (ret < 0)
+		return;
+	port->type = PORT_TXX9;
+	up->port.fifosize = TXX9_SIO_TX_FIFO;
+
+#ifdef CONFIG_SERIAL_TXX9_CONSOLE
+	if (up->port.line == up->port.cons->index)
+		return;
+#endif
+	serial_txx9_initialize(port);
+}
+
+static const char *
+serial_txx9_type(struct uart_port *port)
+{
+	return "txx9";
+}
+
+static struct uart_ops serial_txx9_pops = {
+	.tx_empty	= serial_txx9_tx_empty,
+	.set_mctrl	= serial_txx9_set_mctrl,
+	.get_mctrl	= serial_txx9_get_mctrl,
+	.stop_tx	= serial_txx9_stop_tx,
+	.start_tx	= serial_txx9_start_tx,
+	.stop_rx	= serial_txx9_stop_rx,
+	.enable_ms	= serial_txx9_enable_ms,
+	.break_ctl	= serial_txx9_break_ctl,
+	.startup	= serial_txx9_startup,
+	.shutdown	= serial_txx9_shutdown,
+	.set_termios	= serial_txx9_set_termios,
+	.pm		= serial_txx9_pm,
+	.type		= serial_txx9_type,
+	.release_port	= serial_txx9_release_port,
+	.request_port	= serial_txx9_request_port,
+	.config_port	= serial_txx9_config_port,
+#ifdef CONFIG_CONSOLE_POLL
+	.poll_get_char	= serial_txx9_get_poll_char,
+	.poll_put_char	= serial_txx9_put_poll_char,
+#endif
+};
+
+static struct uart_txx9_port serial_txx9_ports[UART_NR];
+
+static void __init serial_txx9_register_ports(struct uart_driver *drv,
+					      struct device *dev)
+{
+	int i;
+
+	for (i = 0; i < UART_NR; i++) {
+		struct uart_txx9_port *up = &serial_txx9_ports[i];
+
+		up->port.line = i;
+		up->port.ops = &serial_txx9_pops;
+		up->port.dev = dev;
+		if (up->port.iobase || up->port.mapbase)
+			uart_add_one_port(drv, &up->port);
+	}
+}
+
+#ifdef CONFIG_SERIAL_TXX9_CONSOLE
+
+static void serial_txx9_console_putchar(struct uart_port *port, int ch)
+{
+	struct uart_txx9_port *up = to_uart_txx9_port(port);
+
+	wait_for_xmitr(up);
+	sio_out(up, TXX9_SITFIFO, ch);
+}
+
+/*
+ *	Print a string to the serial port trying not to disturb
+ *	any possible real use of the port...
+ *
+ *	The console_lock must be held when we get here.
+ */
+static void
+serial_txx9_console_write(struct console *co, const char *s, unsigned int count)
+{
+	struct uart_txx9_port *up = &serial_txx9_ports[co->index];
+	unsigned int ier, flcr;
+
+	/*
+	 *	First save the UER then disable the interrupts
+	 */
+	ier = sio_in(up, TXX9_SIDICR);
+	sio_out(up, TXX9_SIDICR, 0);
+	/*
+	 *	Disable flow-control if enabled (and unnecessary)
+	 */
+	flcr = sio_in(up, TXX9_SIFLCR);
+	if (!(up->port.flags & UPF_CONS_FLOW) && (flcr & TXX9_SIFLCR_TES))
+		sio_out(up, TXX9_SIFLCR, flcr & ~TXX9_SIFLCR_TES);
+
+	uart_console_write(&up->port, s, count, serial_txx9_console_putchar);
+
+	/*
+	 *	Finally, wait for transmitter to become empty
+	 *	and restore the IER
+	 */
+	wait_for_xmitr(up);
+	sio_out(up, TXX9_SIFLCR, flcr);
+	sio_out(up, TXX9_SIDICR, ier);
+}
+
+static int __init serial_txx9_console_setup(struct console *co, char *options)
+{
+	struct uart_port *port;
+	struct uart_txx9_port *up;
+	int baud = 9600;
+	int bits = 8;
+	int parity = 'n';
+	int flow = 'n';
+
+	/*
+	 * 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 >= UART_NR)
+		co->index = 0;
+	up = &serial_txx9_ports[co->index];
+	port = &up->port;
+	if (!port->ops)
+		return -ENODEV;
+
+	serial_txx9_initialize(&up->port);
+
+	if (options)
+		uart_parse_options(options, &baud, &parity, &bits, &flow);
+
+	return uart_set_options(port, co, baud, parity, bits, flow);
+}
+
+static struct uart_driver serial_txx9_reg;
+static struct console serial_txx9_console = {
+	.name		= TXX9_TTY_NAME,
+	.write		= serial_txx9_console_write,
+	.device		= uart_console_device,
+	.setup		= serial_txx9_console_setup,
+	.flags		= CON_PRINTBUFFER,
+	.index		= -1,
+	.data		= &serial_txx9_reg,
+};
+
+static int __init serial_txx9_console_init(void)
+{
+	register_console(&serial_txx9_console);
+	return 0;
+}
+console_initcall(serial_txx9_console_init);
+
+#define SERIAL_TXX9_CONSOLE	&serial_txx9_console
+#else
+#define SERIAL_TXX9_CONSOLE	NULL
+#endif
+
+static struct uart_driver serial_txx9_reg = {
+	.owner			= THIS_MODULE,
+	.driver_name		= "serial_txx9",
+	.dev_name		= TXX9_TTY_NAME,
+	.major			= TXX9_TTY_MAJOR,
+	.minor			= TXX9_TTY_MINOR_START,
+	.nr			= UART_NR,
+	.cons			= SERIAL_TXX9_CONSOLE,
+};
+
+int __init early_serial_txx9_setup(struct uart_port *port)
+{
+	if (port->line >= ARRAY_SIZE(serial_txx9_ports))
+		return -ENODEV;
+
+	serial_txx9_ports[port->line].port = *port;
+	serial_txx9_ports[port->line].port.ops = &serial_txx9_pops;
+	serial_txx9_ports[port->line].port.flags |=
+		UPF_BOOT_AUTOCONF | UPF_FIXED_PORT;
+	return 0;
+}
+
+static DEFINE_MUTEX(serial_txx9_mutex);
+
+/**
+ *	serial_txx9_register_port - register a serial port
+ *	@port: serial port template
+ *
+ *	Configure the serial port specified by the request.
+ *
+ *	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 __devinit serial_txx9_register_port(struct uart_port *port)
+{
+	int i;
+	struct uart_txx9_port *uart;
+	int ret = -ENOSPC;
+
+	mutex_lock(&serial_txx9_mutex);
+	for (i = 0; i < UART_NR; i++) {
+		uart = &serial_txx9_ports[i];
+		if (uart_match_port(&uart->port, port)) {
+			uart_remove_one_port(&serial_txx9_reg, &uart->port);
+			break;
+		}
+	}
+	if (i == UART_NR) {
+		/* Find unused port */
+		for (i = 0; i < UART_NR; i++) {
+			uart = &serial_txx9_ports[i];
+			if (!(uart->port.iobase || uart->port.mapbase))
+				break;
+		}
+	}
+	if (i < UART_NR) {
+		uart->port.iobase = port->iobase;
+		uart->port.membase = port->membase;
+		uart->port.irq      = port->irq;
+		uart->port.uartclk  = port->uartclk;
+		uart->port.iotype   = port->iotype;
+		uart->port.flags    = port->flags
+			| UPF_BOOT_AUTOCONF | UPF_FIXED_PORT;
+		uart->port.mapbase  = port->mapbase;
+		if (port->dev)
+			uart->port.dev = port->dev;
+		ret = uart_add_one_port(&serial_txx9_reg, &uart->port);
+		if (ret == 0)
+			ret = uart->port.line;
+	}
+	mutex_unlock(&serial_txx9_mutex);
+	return ret;
+}
+
+/**
+ *	serial_txx9_unregister_port - remove a txx9 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 __devexit serial_txx9_unregister_port(int line)
+{
+	struct uart_txx9_port *uart = &serial_txx9_ports[line];
+
+	mutex_lock(&serial_txx9_mutex);
+	uart_remove_one_port(&serial_txx9_reg, &uart->port);
+	uart->port.flags = 0;
+	uart->port.type = PORT_UNKNOWN;
+	uart->port.iobase = 0;
+	uart->port.mapbase = 0;
+	uart->port.membase = NULL;
+	uart->port.dev = NULL;
+	mutex_unlock(&serial_txx9_mutex);
+}
+
+/*
+ * Register a set of serial devices attached to a platform device.
+ */
+static int __devinit serial_txx9_probe(struct platform_device *dev)
+{
+	struct uart_port *p = dev->dev.platform_data;
+	struct uart_port port;
+	int ret, i;
+
+	memset(&port, 0, sizeof(struct uart_port));
+	for (i = 0; p && p->uartclk != 0; p++, i++) {
+		port.iobase	= p->iobase;
+		port.membase	= p->membase;
+		port.irq	= p->irq;
+		port.uartclk	= p->uartclk;
+		port.iotype	= p->iotype;
+		port.flags	= p->flags;
+		port.mapbase	= p->mapbase;
+		port.dev	= &dev->dev;
+		ret = serial_txx9_register_port(&port);
+		if (ret < 0) {
+			dev_err(&dev->dev, "unable to register port at index %d "
+				"(IO%lx MEM%llx IRQ%d): %d\n", i,
+				p->iobase, (unsigned long long)p->mapbase,
+				p->irq, ret);
+		}
+	}
+	return 0;
+}
+
+/*
+ * Remove serial ports registered against a platform device.
+ */
+static int __devexit serial_txx9_remove(struct platform_device *dev)
+{
+	int i;
+
+	for (i = 0; i < UART_NR; i++) {
+		struct uart_txx9_port *up = &serial_txx9_ports[i];
+
+		if (up->port.dev == &dev->dev)
+			serial_txx9_unregister_port(i);
+	}
+	return 0;
+}
+
+#ifdef CONFIG_PM
+static int serial_txx9_suspend(struct platform_device *dev, pm_message_t state)
+{
+	int i;
+
+	for (i = 0; i < UART_NR; i++) {
+		struct uart_txx9_port *up = &serial_txx9_ports[i];
+
+		if (up->port.type != PORT_UNKNOWN && up->port.dev == &dev->dev)
+			uart_suspend_port(&serial_txx9_reg, &up->port);
+	}
+
+	return 0;
+}
+
+static int serial_txx9_resume(struct platform_device *dev)
+{
+	int i;
+
+	for (i = 0; i < UART_NR; i++) {
+		struct uart_txx9_port *up = &serial_txx9_ports[i];
+
+		if (up->port.type != PORT_UNKNOWN && up->port.dev == &dev->dev)
+			uart_resume_port(&serial_txx9_reg, &up->port);
+	}
+
+	return 0;
+}
+#endif
+
+static struct platform_driver serial_txx9_plat_driver = {
+	.probe		= serial_txx9_probe,
+	.remove		= __devexit_p(serial_txx9_remove),
+#ifdef CONFIG_PM
+	.suspend	= serial_txx9_suspend,
+	.resume		= serial_txx9_resume,
+#endif
+	.driver		= {
+		.name	= "serial_txx9",
+		.owner	= THIS_MODULE,
+	},
+};
+
+#ifdef ENABLE_SERIAL_TXX9_PCI
+/*
+ * Probe one serial board.  Unfortunately, there is no rhyme nor reason
+ * to the arrangement of serial ports on a PCI card.
+ */
+static int __devinit
+pciserial_txx9_init_one(struct pci_dev *dev, const struct pci_device_id *ent)
+{
+	struct uart_port port;
+	int line;
+	int rc;
+
+	rc = pci_enable_device(dev);
+	if (rc)
+		return rc;
+
+	memset(&port, 0, sizeof(port));
+	port.ops = &serial_txx9_pops;
+	port.flags |= UPF_TXX9_HAVE_CTS_LINE;
+	port.uartclk = 66670000;
+	port.irq = dev->irq;
+	port.iotype = UPIO_PORT;
+	port.iobase = pci_resource_start(dev, 1);
+	port.dev = &dev->dev;
+	line = serial_txx9_register_port(&port);
+	if (line < 0) {
+		printk(KERN_WARNING "Couldn't register serial port %s: %d\n", pci_name(dev), line);
+		pci_disable_device(dev);
+		return line;
+	}
+	pci_set_drvdata(dev, &serial_txx9_ports[line]);
+
+	return 0;
+}
+
+static void __devexit pciserial_txx9_remove_one(struct pci_dev *dev)
+{
+	struct uart_txx9_port *up = pci_get_drvdata(dev);
+
+	pci_set_drvdata(dev, NULL);
+
+	if (up) {
+		serial_txx9_unregister_port(up->port.line);
+		pci_disable_device(dev);
+	}
+}
+
+#ifdef CONFIG_PM
+static int pciserial_txx9_suspend_one(struct pci_dev *dev, pm_message_t state)
+{
+	struct uart_txx9_port *up = pci_get_drvdata(dev);
+
+	if (up)
+		uart_suspend_port(&serial_txx9_reg, &up->port);
+	pci_save_state(dev);
+	pci_set_power_state(dev, pci_choose_state(dev, state));
+	return 0;
+}
+
+static int pciserial_txx9_resume_one(struct pci_dev *dev)
+{
+	struct uart_txx9_port *up = pci_get_drvdata(dev);
+
+	pci_set_power_state(dev, PCI_D0);
+	pci_restore_state(dev);
+	if (up)
+		uart_resume_port(&serial_txx9_reg, &up->port);
+	return 0;
+}
+#endif
+
+static const struct pci_device_id serial_txx9_pci_tbl[] = {
+	{ PCI_DEVICE(PCI_VENDOR_ID_TOSHIBA_2, PCI_DEVICE_ID_TOSHIBA_TC86C001_MISC) },
+	{ 0, }
+};
+
+static struct pci_driver serial_txx9_pci_driver = {
+	.name		= "serial_txx9",
+	.probe		= pciserial_txx9_init_one,
+	.remove		= __devexit_p(pciserial_txx9_remove_one),
+#ifdef CONFIG_PM
+	.suspend	= pciserial_txx9_suspend_one,
+	.resume		= pciserial_txx9_resume_one,
+#endif
+	.id_table	= serial_txx9_pci_tbl,
+};
+
+MODULE_DEVICE_TABLE(pci, serial_txx9_pci_tbl);
+#endif /* ENABLE_SERIAL_TXX9_PCI */
+
+static struct platform_device *serial_txx9_plat_devs;
+
+static int __init serial_txx9_init(void)
+{
+	int ret;
+
+ 	printk(KERN_INFO "%s version %s\n", serial_name, serial_version);
+
+	ret = uart_register_driver(&serial_txx9_reg);
+	if (ret)
+		goto out;
+
+	serial_txx9_plat_devs = platform_device_alloc("serial_txx9", -1);
+	if (!serial_txx9_plat_devs) {
+		ret = -ENOMEM;
+		goto unreg_uart_drv;
+	}
+
+	ret = platform_device_add(serial_txx9_plat_devs);
+	if (ret)
+		goto put_dev;
+
+	serial_txx9_register_ports(&serial_txx9_reg,
+				   &serial_txx9_plat_devs->dev);
+
+	ret = platform_driver_register(&serial_txx9_plat_driver);
+	if (ret)
+		goto del_dev;
+
+#ifdef ENABLE_SERIAL_TXX9_PCI
+	ret = pci_register_driver(&serial_txx9_pci_driver);
+#endif
+	if (ret == 0)
+		goto out;
+
+ del_dev:
+	platform_device_del(serial_txx9_plat_devs);
+ put_dev:
+	platform_device_put(serial_txx9_plat_devs);
+ unreg_uart_drv:
+	uart_unregister_driver(&serial_txx9_reg);
+ out:
+	return ret;
+}
+
+static void __exit serial_txx9_exit(void)
+{
+	int i;
+
+#ifdef ENABLE_SERIAL_TXX9_PCI
+	pci_unregister_driver(&serial_txx9_pci_driver);
+#endif
+	platform_driver_unregister(&serial_txx9_plat_driver);
+	platform_device_unregister(serial_txx9_plat_devs);
+	for (i = 0; i < UART_NR; i++) {
+		struct uart_txx9_port *up = &serial_txx9_ports[i];
+		if (up->port.iobase || up->port.mapbase)
+			uart_remove_one_port(&serial_txx9_reg, &up->port);
+	}
+
+	uart_unregister_driver(&serial_txx9_reg);
+}
+
+module_init(serial_txx9_init);
+module_exit(serial_txx9_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("TX39/49 serial driver");
+
+MODULE_ALIAS_CHARDEV_MAJOR(TXX9_TTY_MAJOR);
diff --git a/ap/os/linux/linux-3.4.x/drivers/tty/serial/sh-sci.c b/ap/os/linux/linux-3.4.x/drivers/tty/serial/sh-sci.c
new file mode 100644
index 0000000..3158e17
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/tty/serial/sh-sci.c
@@ -0,0 +1,2466 @@
+/*
+ * SuperH on-chip serial module support.  (SCI with no FIFO / with FIFO)
+ *
+ *  Copyright (C) 2002 - 2011  Paul Mundt
+ *  Modified to support SH7720 SCIF. Markus Brunner, Mark Jonas (Jul 2007).
+ *
+ * based off of the old drivers/char/sh-sci.c by:
+ *
+ *   Copyright (C) 1999, 2000  Niibe Yutaka
+ *   Copyright (C) 2000  Sugioka Toshinobu
+ *   Modified to support multiple serial ports. Stuart Menefy (May 2000).
+ *   Modified to support SecureEdge. David McCullough (2002)
+ *   Modified to support SH7300 SCIF. Takashi Kusuda (Jun 2003).
+ *   Removed SH7300 support (Jul 2007).
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License.  See the file "COPYING" in the main directory of this archive
+ * for more details.
+ */
+#if defined(CONFIG_SERIAL_SH_SCI_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ)
+#define SUPPORT_SYSRQ
+#endif
+
+#undef DEBUG
+
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <linux/timer.h>
+#include <linux/interrupt.h>
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
+#include <linux/serial.h>
+#include <linux/major.h>
+#include <linux/string.h>
+#include <linux/sysrq.h>
+#include <linux/ioport.h>
+#include <linux/mm.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/console.h>
+#include <linux/platform_device.h>
+#include <linux/serial_sci.h>
+#include <linux/notifier.h>
+#include <linux/pm_runtime.h>
+#include <linux/cpufreq.h>
+#include <linux/clk.h>
+#include <linux/ctype.h>
+#include <linux/err.h>
+#include <linux/dmaengine.h>
+#include <linux/dma-mapping.h>
+#include <linux/scatterlist.h>
+#include <linux/slab.h>
+#include <linux/gpio.h>
+
+#ifdef CONFIG_SUPERH
+#include <asm/sh_bios.h>
+#endif
+
+#include "sh-sci.h"
+
+struct sci_port {
+	struct uart_port	port;
+
+	/* Platform configuration */
+	struct plat_sci_port	*cfg;
+
+	/* Break timer */
+	struct timer_list	break_timer;
+	int			break_flag;
+
+	/* Interface clock */
+	struct clk		*iclk;
+	/* Function clock */
+	struct clk		*fclk;
+
+	char			*irqstr[SCIx_NR_IRQS];
+	char			*gpiostr[SCIx_NR_FNS];
+
+	struct dma_chan			*chan_tx;
+	struct dma_chan			*chan_rx;
+
+#ifdef CONFIG_SERIAL_SH_SCI_DMA
+	struct dma_async_tx_descriptor	*desc_tx;
+	struct dma_async_tx_descriptor	*desc_rx[2];
+	dma_cookie_t			cookie_tx;
+	dma_cookie_t			cookie_rx[2];
+	dma_cookie_t			active_rx;
+	struct scatterlist		sg_tx;
+	unsigned int			sg_len_tx;
+	struct scatterlist		sg_rx[2];
+	size_t				buf_len_rx;
+	struct sh_dmae_slave		param_tx;
+	struct sh_dmae_slave		param_rx;
+	struct work_struct		work_tx;
+	struct work_struct		work_rx;
+	struct timer_list		rx_timer;
+	unsigned int			rx_timeout;
+#endif
+
+	struct notifier_block		freq_transition;
+
+#ifdef CONFIG_SERIAL_SH_SCI_CONSOLE
+	unsigned short saved_smr;
+	unsigned short saved_fcr;
+	unsigned char saved_brr;
+#endif
+};
+
+/* Function prototypes */
+static void sci_start_tx(struct uart_port *port);
+static void sci_stop_tx(struct uart_port *port);
+static void sci_start_rx(struct uart_port *port);
+
+#define SCI_NPORTS CONFIG_SERIAL_SH_SCI_NR_UARTS
+
+static struct sci_port sci_ports[SCI_NPORTS];
+static struct uart_driver sci_uart_driver;
+
+static inline struct sci_port *
+to_sci_port(struct uart_port *uart)
+{
+	return container_of(uart, struct sci_port, port);
+}
+
+struct plat_sci_reg {
+	u8 offset, size;
+};
+
+/* Helper for invalidating specific entries of an inherited map. */
+#define sci_reg_invalid	{ .offset = 0, .size = 0 }
+
+static struct plat_sci_reg sci_regmap[SCIx_NR_REGTYPES][SCIx_NR_REGS] = {
+	[SCIx_PROBE_REGTYPE] = {
+		[0 ... SCIx_NR_REGS - 1] = sci_reg_invalid,
+	},
+
+	/*
+	 * Common SCI definitions, dependent on the port's regshift
+	 * value.
+	 */
+	[SCIx_SCI_REGTYPE] = {
+		[SCSMR]		= { 0x00,  8 },
+		[SCBRR]		= { 0x01,  8 },
+		[SCSCR]		= { 0x02,  8 },
+		[SCxTDR]	= { 0x03,  8 },
+		[SCxSR]		= { 0x04,  8 },
+		[SCxRDR]	= { 0x05,  8 },
+		[SCFCR]		= sci_reg_invalid,
+		[SCFDR]		= sci_reg_invalid,
+		[SCTFDR]	= sci_reg_invalid,
+		[SCRFDR]	= sci_reg_invalid,
+		[SCSPTR]	= sci_reg_invalid,
+		[SCLSR]		= sci_reg_invalid,
+	},
+
+	/*
+	 * Common definitions for legacy IrDA ports, dependent on
+	 * regshift value.
+	 */
+	[SCIx_IRDA_REGTYPE] = {
+		[SCSMR]		= { 0x00,  8 },
+		[SCBRR]		= { 0x01,  8 },
+		[SCSCR]		= { 0x02,  8 },
+		[SCxTDR]	= { 0x03,  8 },
+		[SCxSR]		= { 0x04,  8 },
+		[SCxRDR]	= { 0x05,  8 },
+		[SCFCR]		= { 0x06,  8 },
+		[SCFDR]		= { 0x07, 16 },
+		[SCTFDR]	= sci_reg_invalid,
+		[SCRFDR]	= sci_reg_invalid,
+		[SCSPTR]	= sci_reg_invalid,
+		[SCLSR]		= sci_reg_invalid,
+	},
+
+	/*
+	 * Common SCIFA definitions.
+	 */
+	[SCIx_SCIFA_REGTYPE] = {
+		[SCSMR]		= { 0x00, 16 },
+		[SCBRR]		= { 0x04,  8 },
+		[SCSCR]		= { 0x08, 16 },
+		[SCxTDR]	= { 0x20,  8 },
+		[SCxSR]		= { 0x14, 16 },
+		[SCxRDR]	= { 0x24,  8 },
+		[SCFCR]		= { 0x18, 16 },
+		[SCFDR]		= { 0x1c, 16 },
+		[SCTFDR]	= sci_reg_invalid,
+		[SCRFDR]	= sci_reg_invalid,
+		[SCSPTR]	= sci_reg_invalid,
+		[SCLSR]		= sci_reg_invalid,
+	},
+
+	/*
+	 * Common SCIFB definitions.
+	 */
+	[SCIx_SCIFB_REGTYPE] = {
+		[SCSMR]		= { 0x00, 16 },
+		[SCBRR]		= { 0x04,  8 },
+		[SCSCR]		= { 0x08, 16 },
+		[SCxTDR]	= { 0x40,  8 },
+		[SCxSR]		= { 0x14, 16 },
+		[SCxRDR]	= { 0x60,  8 },
+		[SCFCR]		= { 0x18, 16 },
+		[SCFDR]		= { 0x1c, 16 },
+		[SCTFDR]	= sci_reg_invalid,
+		[SCRFDR]	= sci_reg_invalid,
+		[SCSPTR]	= sci_reg_invalid,
+		[SCLSR]		= sci_reg_invalid,
+	},
+
+	/*
+	 * Common SH-2(A) SCIF definitions for ports with FIFO data
+	 * count registers.
+	 */
+	[SCIx_SH2_SCIF_FIFODATA_REGTYPE] = {
+		[SCSMR]		= { 0x00, 16 },
+		[SCBRR]		= { 0x04,  8 },
+		[SCSCR]		= { 0x08, 16 },
+		[SCxTDR]	= { 0x0c,  8 },
+		[SCxSR]		= { 0x10, 16 },
+		[SCxRDR]	= { 0x14,  8 },
+		[SCFCR]		= { 0x18, 16 },
+		[SCFDR]		= { 0x1c, 16 },
+		[SCTFDR]	= sci_reg_invalid,
+		[SCRFDR]	= sci_reg_invalid,
+		[SCSPTR]	= { 0x20, 16 },
+		[SCLSR]		= { 0x24, 16 },
+	},
+
+	/*
+	 * Common SH-3 SCIF definitions.
+	 */
+	[SCIx_SH3_SCIF_REGTYPE] = {
+		[SCSMR]		= { 0x00,  8 },
+		[SCBRR]		= { 0x02,  8 },
+		[SCSCR]		= { 0x04,  8 },
+		[SCxTDR]	= { 0x06,  8 },
+		[SCxSR]		= { 0x08, 16 },
+		[SCxRDR]	= { 0x0a,  8 },
+		[SCFCR]		= { 0x0c,  8 },
+		[SCFDR]		= { 0x0e, 16 },
+		[SCTFDR]	= sci_reg_invalid,
+		[SCRFDR]	= sci_reg_invalid,
+		[SCSPTR]	= sci_reg_invalid,
+		[SCLSR]		= sci_reg_invalid,
+	},
+
+	/*
+	 * Common SH-4(A) SCIF(B) definitions.
+	 */
+	[SCIx_SH4_SCIF_REGTYPE] = {
+		[SCSMR]		= { 0x00, 16 },
+		[SCBRR]		= { 0x04,  8 },
+		[SCSCR]		= { 0x08, 16 },
+		[SCxTDR]	= { 0x0c,  8 },
+		[SCxSR]		= { 0x10, 16 },
+		[SCxRDR]	= { 0x14,  8 },
+		[SCFCR]		= { 0x18, 16 },
+		[SCFDR]		= { 0x1c, 16 },
+		[SCTFDR]	= sci_reg_invalid,
+		[SCRFDR]	= sci_reg_invalid,
+		[SCSPTR]	= { 0x20, 16 },
+		[SCLSR]		= { 0x24, 16 },
+	},
+
+	/*
+	 * Common SH-4(A) SCIF(B) definitions for ports without an SCSPTR
+	 * register.
+	 */
+	[SCIx_SH4_SCIF_NO_SCSPTR_REGTYPE] = {
+		[SCSMR]		= { 0x00, 16 },
+		[SCBRR]		= { 0x04,  8 },
+		[SCSCR]		= { 0x08, 16 },
+		[SCxTDR]	= { 0x0c,  8 },
+		[SCxSR]		= { 0x10, 16 },
+		[SCxRDR]	= { 0x14,  8 },
+		[SCFCR]		= { 0x18, 16 },
+		[SCFDR]		= { 0x1c, 16 },
+		[SCTFDR]	= sci_reg_invalid,
+		[SCRFDR]	= sci_reg_invalid,
+		[SCSPTR]	= sci_reg_invalid,
+		[SCLSR]		= { 0x24, 16 },
+	},
+
+	/*
+	 * Common SH-4(A) SCIF(B) definitions for ports with FIFO data
+	 * count registers.
+	 */
+	[SCIx_SH4_SCIF_FIFODATA_REGTYPE] = {
+		[SCSMR]		= { 0x00, 16 },
+		[SCBRR]		= { 0x04,  8 },
+		[SCSCR]		= { 0x08, 16 },
+		[SCxTDR]	= { 0x0c,  8 },
+		[SCxSR]		= { 0x10, 16 },
+		[SCxRDR]	= { 0x14,  8 },
+		[SCFCR]		= { 0x18, 16 },
+		[SCFDR]		= { 0x1c, 16 },
+		[SCTFDR]	= { 0x1c, 16 },	/* aliased to SCFDR */
+		[SCRFDR]	= { 0x20, 16 },
+		[SCSPTR]	= { 0x24, 16 },
+		[SCLSR]		= { 0x28, 16 },
+	},
+
+	/*
+	 * SH7705-style SCIF(B) ports, lacking both SCSPTR and SCLSR
+	 * registers.
+	 */
+	[SCIx_SH7705_SCIF_REGTYPE] = {
+		[SCSMR]		= { 0x00, 16 },
+		[SCBRR]		= { 0x04,  8 },
+		[SCSCR]		= { 0x08, 16 },
+		[SCxTDR]	= { 0x20,  8 },
+		[SCxSR]		= { 0x14, 16 },
+		[SCxRDR]	= { 0x24,  8 },
+		[SCFCR]		= { 0x18, 16 },
+		[SCFDR]		= { 0x1c, 16 },
+		[SCTFDR]	= sci_reg_invalid,
+		[SCRFDR]	= sci_reg_invalid,
+		[SCSPTR]	= sci_reg_invalid,
+		[SCLSR]		= sci_reg_invalid,
+	},
+};
+
+#define sci_getreg(up, offset)		(sci_regmap[to_sci_port(up)->cfg->regtype] + offset)
+
+/*
+ * The "offset" here is rather misleading, in that it refers to an enum
+ * value relative to the port mapping rather than the fixed offset
+ * itself, which needs to be manually retrieved from the platform's
+ * register map for the given port.
+ */
+static unsigned int sci_serial_in(struct uart_port *p, int offset)
+{
+	struct plat_sci_reg *reg = sci_getreg(p, offset);
+
+	if (reg->size == 8)
+		return ioread8(p->membase + (reg->offset << p->regshift));
+	else if (reg->size == 16)
+		return ioread16(p->membase + (reg->offset << p->regshift));
+	else
+		WARN(1, "Invalid register access\n");
+
+	return 0;
+}
+
+static void sci_serial_out(struct uart_port *p, int offset, int value)
+{
+	struct plat_sci_reg *reg = sci_getreg(p, offset);
+
+	if (reg->size == 8)
+		iowrite8(value, p->membase + (reg->offset << p->regshift));
+	else if (reg->size == 16)
+		iowrite16(value, p->membase + (reg->offset << p->regshift));
+	else
+		WARN(1, "Invalid register access\n");
+}
+
+static int sci_probe_regmap(struct plat_sci_port *cfg)
+{
+	switch (cfg->type) {
+	case PORT_SCI:
+		cfg->regtype = SCIx_SCI_REGTYPE;
+		break;
+	case PORT_IRDA:
+		cfg->regtype = SCIx_IRDA_REGTYPE;
+		break;
+	case PORT_SCIFA:
+		cfg->regtype = SCIx_SCIFA_REGTYPE;
+		break;
+	case PORT_SCIFB:
+		cfg->regtype = SCIx_SCIFB_REGTYPE;
+		break;
+	case PORT_SCIF:
+		/*
+		 * The SH-4 is a bit of a misnomer here, although that's
+		 * where this particular port layout originated. This
+		 * configuration (or some slight variation thereof)
+		 * remains the dominant model for all SCIFs.
+		 */
+		cfg->regtype = SCIx_SH4_SCIF_REGTYPE;
+		break;
+	default:
+		printk(KERN_ERR "Can't probe register map for given port\n");
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static void sci_port_enable(struct sci_port *sci_port)
+{
+	if (!sci_port->port.dev)
+		return;
+
+	pm_runtime_get_sync(sci_port->port.dev);
+
+	clk_enable(sci_port->iclk);
+	sci_port->port.uartclk = clk_get_rate(sci_port->iclk);
+	clk_enable(sci_port->fclk);
+}
+
+static void sci_port_disable(struct sci_port *sci_port)
+{
+	if (!sci_port->port.dev)
+		return;
+
+	clk_disable(sci_port->fclk);
+	clk_disable(sci_port->iclk);
+
+	pm_runtime_put_sync(sci_port->port.dev);
+}
+
+#if defined(CONFIG_CONSOLE_POLL) || defined(CONFIG_SERIAL_SH_SCI_CONSOLE)
+
+#ifdef CONFIG_CONSOLE_POLL
+static int sci_poll_get_char(struct uart_port *port)
+{
+	unsigned short status;
+	int c;
+
+	do {
+		status = serial_port_in(port, SCxSR);
+		if (status & SCxSR_ERRORS(port)) {
+			serial_port_out(port, SCxSR, SCxSR_ERROR_CLEAR(port));
+			continue;
+		}
+		break;
+	} while (1);
+
+	if (!(status & SCxSR_RDxF(port)))
+		return NO_POLL_CHAR;
+
+	c = serial_port_in(port, SCxRDR);
+
+	/* Dummy read */
+	serial_port_in(port, SCxSR);
+	serial_port_out(port, SCxSR, SCxSR_RDxF_CLEAR(port));
+
+	return c;
+}
+#endif
+
+static void sci_poll_put_char(struct uart_port *port, unsigned char c)
+{
+	unsigned short status;
+
+	do {
+		status = serial_port_in(port, SCxSR);
+	} while (!(status & SCxSR_TDxE(port)));
+
+	serial_port_out(port, SCxTDR, c);
+	serial_port_out(port, SCxSR, SCxSR_TDxE_CLEAR(port) & ~SCxSR_TEND(port));
+}
+#endif /* CONFIG_CONSOLE_POLL || CONFIG_SERIAL_SH_SCI_CONSOLE */
+
+static void sci_init_pins(struct uart_port *port, unsigned int cflag)
+{
+	struct sci_port *s = to_sci_port(port);
+	struct plat_sci_reg *reg = sci_regmap[s->cfg->regtype] + SCSPTR;
+
+	/*
+	 * Use port-specific handler if provided.
+	 */
+	if (s->cfg->ops && s->cfg->ops->init_pins) {
+		s->cfg->ops->init_pins(port, cflag);
+		return;
+	}
+
+	/*
+	 * For the generic path SCSPTR is necessary. Bail out if that's
+	 * unavailable, too.
+	 */
+	if (!reg->size)
+		return;
+
+	if ((s->cfg->capabilities & SCIx_HAVE_RTSCTS) &&
+	    ((!(cflag & CRTSCTS)))) {
+		unsigned short status;
+
+		status = serial_port_in(port, SCSPTR);
+		status &= ~SCSPTR_CTSIO;
+		status |= SCSPTR_RTSIO;
+		serial_port_out(port, SCSPTR, status); /* Set RTS = 1 */
+	}
+}
+
+static int sci_txfill(struct uart_port *port)
+{
+	struct plat_sci_reg *reg;
+
+	reg = sci_getreg(port, SCTFDR);
+	if (reg->size)
+		return serial_port_in(port, SCTFDR) & 0xff;
+
+	reg = sci_getreg(port, SCFDR);
+	if (reg->size)
+		return serial_port_in(port, SCFDR) >> 8;
+
+	return !(serial_port_in(port, SCxSR) & SCI_TDRE);
+}
+
+static int sci_txroom(struct uart_port *port)
+{
+	return port->fifosize - sci_txfill(port);
+}
+
+static int sci_rxfill(struct uart_port *port)
+{
+	struct plat_sci_reg *reg;
+
+	reg = sci_getreg(port, SCRFDR);
+	if (reg->size)
+		return serial_port_in(port, SCRFDR) & 0xff;
+
+	reg = sci_getreg(port, SCFDR);
+	if (reg->size)
+		return serial_port_in(port, SCFDR) & ((port->fifosize << 1) - 1);
+
+	return (serial_port_in(port, SCxSR) & SCxSR_RDxF(port)) != 0;
+}
+
+/*
+ * SCI helper for checking the state of the muxed port/RXD pins.
+ */
+static inline int sci_rxd_in(struct uart_port *port)
+{
+	struct sci_port *s = to_sci_port(port);
+
+	if (s->cfg->port_reg <= 0)
+		return 1;
+
+	return !!__raw_readb(s->cfg->port_reg);
+}
+
+/* ********************************************************************** *
+ *                   the interrupt related routines                       *
+ * ********************************************************************** */
+
+static void sci_transmit_chars(struct uart_port *port)
+{
+	struct circ_buf *xmit = &port->state->xmit;
+	unsigned int stopped = uart_tx_stopped(port);
+	unsigned short status;
+	unsigned short ctrl;
+	int count;
+
+	status = serial_port_in(port, SCxSR);
+	if (!(status & SCxSR_TDxE(port))) {
+		ctrl = serial_port_in(port, SCSCR);
+		if (uart_circ_empty(xmit))
+			ctrl &= ~SCSCR_TIE;
+		else
+			ctrl |= SCSCR_TIE;
+		serial_port_out(port, SCSCR, ctrl);
+		return;
+	}
+
+	count = sci_txroom(port);
+
+	do {
+		unsigned char c;
+
+		if (port->x_char) {
+			c = port->x_char;
+			port->x_char = 0;
+		} else if (!uart_circ_empty(xmit) && !stopped) {
+			c = xmit->buf[xmit->tail];
+			xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
+		} else {
+			break;
+		}
+
+		serial_port_out(port, SCxTDR, c);
+
+		port->icount.tx++;
+	} while (--count > 0);
+
+	serial_port_out(port, SCxSR, SCxSR_TDxE_CLEAR(port));
+
+	if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
+		uart_write_wakeup(port);
+	if (uart_circ_empty(xmit)) {
+		sci_stop_tx(port);
+	} else {
+		ctrl = serial_port_in(port, SCSCR);
+
+		if (port->type != PORT_SCI) {
+			serial_port_in(port, SCxSR); /* Dummy read */
+			serial_port_out(port, SCxSR, SCxSR_TDxE_CLEAR(port));
+		}
+
+		ctrl |= SCSCR_TIE;
+		serial_port_out(port, SCSCR, ctrl);
+	}
+}
+
+/* On SH3, SCIF may read end-of-break as a space->mark char */
+#define STEPFN(c)  ({int __c = (c); (((__c-1)|(__c)) == -1); })
+
+static void sci_receive_chars(struct uart_port *port)
+{
+	struct sci_port *sci_port = to_sci_port(port);
+	struct tty_struct *tty = port->state->port.tty;
+	int i, count, copied = 0;
+	unsigned short status;
+	unsigned char flag;
+
+	status = serial_port_in(port, SCxSR);
+	if (!(status & SCxSR_RDxF(port)))
+		return;
+
+	while (1) {
+		/* Don't copy more bytes than there is room for in the buffer */
+		count = tty_buffer_request_room(tty, sci_rxfill(port));
+
+		/* If for any reason we can't copy more data, we're done! */
+		if (count == 0)
+			break;
+
+		if (port->type == PORT_SCI) {
+			char c = serial_port_in(port, SCxRDR);
+			if (uart_handle_sysrq_char(port, c) ||
+			    sci_port->break_flag)
+				count = 0;
+			else
+				tty_insert_flip_char(tty, c, TTY_NORMAL);
+		} else {
+			for (i = 0; i < count; i++) {
+				char c = serial_port_in(port, SCxRDR);
+
+				status = serial_port_in(port, SCxSR);
+#if defined(CONFIG_CPU_SH3)
+				/* Skip "chars" during break */
+				if (sci_port->break_flag) {
+					if ((c == 0) &&
+					    (status & SCxSR_FER(port))) {
+						count--; i--;
+						continue;
+					}
+
+					/* Nonzero => end-of-break */
+					dev_dbg(port->dev, "debounce<%02x>\n", c);
+					sci_port->break_flag = 0;
+
+					if (STEPFN(c)) {
+						count--; i--;
+						continue;
+					}
+				}
+#endif /* CONFIG_CPU_SH3 */
+				if (uart_handle_sysrq_char(port, c)) {
+					count--; i--;
+					continue;
+				}
+
+				/* Store data and status */
+				if (status & SCxSR_FER(port)) {
+					flag = TTY_FRAME;
+					port->icount.frame++;
+					dev_notice(port->dev, "frame error\n");
+				} else if (status & SCxSR_PER(port)) {
+					flag = TTY_PARITY;
+					port->icount.parity++;
+					dev_notice(port->dev, "parity error\n");
+				} else
+					flag = TTY_NORMAL;
+
+				tty_insert_flip_char(tty, c, flag);
+			}
+		}
+
+		serial_port_in(port, SCxSR); /* dummy read */
+		serial_port_out(port, SCxSR, SCxSR_RDxF_CLEAR(port));
+
+		copied += count;
+		port->icount.rx += count;
+	}
+
+	if (copied) {
+		/* Tell the rest of the system the news. New characters! */
+		tty_flip_buffer_push(tty);
+	} else {
+		serial_port_in(port, SCxSR); /* dummy read */
+		serial_port_out(port, SCxSR, SCxSR_RDxF_CLEAR(port));
+	}
+}
+
+#define SCI_BREAK_JIFFIES (HZ/20)
+
+/*
+ * The sci generates interrupts during the break,
+ * 1 per millisecond or so during the break period, for 9600 baud.
+ * So dont bother disabling interrupts.
+ * But dont want more than 1 break event.
+ * Use a kernel timer to periodically poll the rx line until
+ * the break is finished.
+ */
+static inline void sci_schedule_break_timer(struct sci_port *port)
+{
+	mod_timer(&port->break_timer, jiffies + SCI_BREAK_JIFFIES);
+}
+
+/* Ensure that two consecutive samples find the break over. */
+static void sci_break_timer(unsigned long data)
+{
+	struct sci_port *port = (struct sci_port *)data;
+
+	sci_port_enable(port);
+
+	if (sci_rxd_in(&port->port) == 0) {
+		port->break_flag = 1;
+		sci_schedule_break_timer(port);
+	} else if (port->break_flag == 1) {
+		/* break is over. */
+		port->break_flag = 2;
+		sci_schedule_break_timer(port);
+	} else
+		port->break_flag = 0;
+
+	sci_port_disable(port);
+}
+
+static int sci_handle_errors(struct uart_port *port)
+{
+	int copied = 0;
+	unsigned short status = serial_port_in(port, SCxSR);
+	struct tty_struct *tty = port->state->port.tty;
+	struct sci_port *s = to_sci_port(port);
+
+	/*
+	 * Handle overruns, if supported.
+	 */
+	if (s->cfg->overrun_bit != SCIx_NOT_SUPPORTED) {
+		if (status & (1 << s->cfg->overrun_bit)) {
+			port->icount.overrun++;
+
+			/* overrun error */
+			if (tty_insert_flip_char(tty, 0, TTY_OVERRUN))
+				copied++;
+
+			dev_notice(port->dev, "overrun error");
+		}
+	}
+
+	if (status & SCxSR_FER(port)) {
+		if (sci_rxd_in(port) == 0) {
+			/* Notify of BREAK */
+			struct sci_port *sci_port = to_sci_port(port);
+
+			if (!sci_port->break_flag) {
+				port->icount.brk++;
+
+				sci_port->break_flag = 1;
+				sci_schedule_break_timer(sci_port);
+
+				/* Do sysrq handling. */
+				if (uart_handle_break(port))
+					return 0;
+
+				dev_dbg(port->dev, "BREAK detected\n");
+
+				if (tty_insert_flip_char(tty, 0, TTY_BREAK))
+					copied++;
+			}
+
+		} else {
+			/* frame error */
+			port->icount.frame++;
+
+			if (tty_insert_flip_char(tty, 0, TTY_FRAME))
+				copied++;
+
+			dev_notice(port->dev, "frame error\n");
+		}
+	}
+
+	if (status & SCxSR_PER(port)) {
+		/* parity error */
+		port->icount.parity++;
+
+		if (tty_insert_flip_char(tty, 0, TTY_PARITY))
+			copied++;
+
+		dev_notice(port->dev, "parity error");
+	}
+
+	if (copied)
+		tty_flip_buffer_push(tty);
+
+	return copied;
+}
+
+static int sci_handle_fifo_overrun(struct uart_port *port)
+{
+	struct tty_struct *tty = port->state->port.tty;
+	struct sci_port *s = to_sci_port(port);
+	struct plat_sci_reg *reg;
+	int copied = 0;
+
+	reg = sci_getreg(port, SCLSR);
+	if (!reg->size)
+		return 0;
+
+	if ((serial_port_in(port, SCLSR) & (1 << s->cfg->overrun_bit))) {
+		serial_port_out(port, SCLSR, 0);
+
+		port->icount.overrun++;
+
+		tty_insert_flip_char(tty, 0, TTY_OVERRUN);
+		tty_flip_buffer_push(tty);
+
+		dev_notice(port->dev, "overrun error\n");
+		copied++;
+	}
+
+	return copied;
+}
+
+static int sci_handle_breaks(struct uart_port *port)
+{
+	int copied = 0;
+	unsigned short status = serial_port_in(port, SCxSR);
+	struct tty_struct *tty = port->state->port.tty;
+	struct sci_port *s = to_sci_port(port);
+
+	if (uart_handle_break(port))
+		return 0;
+
+	if (!s->break_flag && status & SCxSR_BRK(port)) {
+#if defined(CONFIG_CPU_SH3)
+		/* Debounce break */
+		s->break_flag = 1;
+#endif
+
+		port->icount.brk++;
+
+		/* Notify of BREAK */
+		if (tty_insert_flip_char(tty, 0, TTY_BREAK))
+			copied++;
+
+		dev_dbg(port->dev, "BREAK detected\n");
+	}
+
+	if (copied)
+		tty_flip_buffer_push(tty);
+
+	copied += sci_handle_fifo_overrun(port);
+
+	return copied;
+}
+
+static irqreturn_t sci_rx_interrupt(int irq, void *ptr)
+{
+#ifdef CONFIG_SERIAL_SH_SCI_DMA
+	struct uart_port *port = ptr;
+	struct sci_port *s = to_sci_port(port);
+
+	if (s->chan_rx) {
+		u16 scr = serial_port_in(port, SCSCR);
+		u16 ssr = serial_port_in(port, SCxSR);
+
+		/* Disable future Rx interrupts */
+		if (port->type == PORT_SCIFA || port->type == PORT_SCIFB) {
+			disable_irq_nosync(irq);
+			scr |= 0x4000;
+		} else {
+			scr &= ~SCSCR_RIE;
+		}
+		serial_port_out(port, SCSCR, scr);
+		/* Clear current interrupt */
+		serial_port_out(port, SCxSR, ssr & ~(1 | SCxSR_RDxF(port)));
+		dev_dbg(port->dev, "Rx IRQ %lu: setup t-out in %u jiffies\n",
+			jiffies, s->rx_timeout);
+		mod_timer(&s->rx_timer, jiffies + s->rx_timeout);
+
+		return IRQ_HANDLED;
+	}
+#endif
+
+	/* I think sci_receive_chars has to be called irrespective
+	 * of whether the I_IXOFF is set, otherwise, how is the interrupt
+	 * to be disabled?
+	 */
+	sci_receive_chars(ptr);
+
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t sci_tx_interrupt(int irq, void *ptr)
+{
+	struct uart_port *port = ptr;
+	unsigned long flags;
+
+	spin_lock_irqsave(&port->lock, flags);
+	sci_transmit_chars(port);
+	spin_unlock_irqrestore(&port->lock, flags);
+
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t sci_er_interrupt(int irq, void *ptr)
+{
+	struct uart_port *port = ptr;
+
+	/* Handle errors */
+	if (port->type == PORT_SCI) {
+		if (sci_handle_errors(port)) {
+			/* discard character in rx buffer */
+			serial_port_in(port, SCxSR);
+			serial_port_out(port, SCxSR, SCxSR_RDxF_CLEAR(port));
+		}
+	} else {
+		sci_handle_fifo_overrun(port);
+		sci_rx_interrupt(irq, ptr);
+	}
+
+	serial_port_out(port, SCxSR, SCxSR_ERROR_CLEAR(port));
+
+	/* Kick the transmission */
+	sci_tx_interrupt(irq, ptr);
+
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t sci_br_interrupt(int irq, void *ptr)
+{
+	struct uart_port *port = ptr;
+
+	/* Handle BREAKs */
+	sci_handle_breaks(port);
+	serial_port_out(port, SCxSR, SCxSR_BREAK_CLEAR(port));
+
+	return IRQ_HANDLED;
+}
+
+static inline unsigned long port_rx_irq_mask(struct uart_port *port)
+{
+	/*
+	 * Not all ports (such as SCIFA) will support REIE. Rather than
+	 * special-casing the port type, we check the port initialization
+	 * IRQ enable mask to see whether the IRQ is desired at all. If
+	 * it's unset, it's logically inferred that there's no point in
+	 * testing for it.
+	 */
+	return SCSCR_RIE | (to_sci_port(port)->cfg->scscr & SCSCR_REIE);
+}
+
+static irqreturn_t sci_mpxed_interrupt(int irq, void *ptr)
+{
+	unsigned short ssr_status, scr_status, err_enabled;
+	struct uart_port *port = ptr;
+	struct sci_port *s = to_sci_port(port);
+	irqreturn_t ret = IRQ_NONE;
+
+	ssr_status = serial_port_in(port, SCxSR);
+	scr_status = serial_port_in(port, SCSCR);
+	err_enabled = scr_status & port_rx_irq_mask(port);
+
+	/* Tx Interrupt */
+	if ((ssr_status & SCxSR_TDxE(port)) && (scr_status & SCSCR_TIE) &&
+	    !s->chan_tx)
+		ret = sci_tx_interrupt(irq, ptr);
+
+	/*
+	 * Rx Interrupt: if we're using DMA, the DMA controller clears RDF /
+	 * DR flags
+	 */
+	if (((ssr_status & SCxSR_RDxF(port)) || s->chan_rx) &&
+	    (scr_status & SCSCR_RIE))
+		ret = sci_rx_interrupt(irq, ptr);
+
+	/* Error Interrupt */
+	if ((ssr_status & SCxSR_ERRORS(port)) && err_enabled)
+		ret = sci_er_interrupt(irq, ptr);
+
+	/* Break Interrupt */
+	if ((ssr_status & SCxSR_BRK(port)) && err_enabled)
+		ret = sci_br_interrupt(irq, ptr);
+
+	return ret;
+}
+
+/*
+ * Here we define a transition notifier so that we can update all of our
+ * ports' baud rate when the peripheral clock changes.
+ */
+static int sci_notifier(struct notifier_block *self,
+			unsigned long phase, void *p)
+{
+	struct sci_port *sci_port;
+	unsigned long flags;
+
+	sci_port = container_of(self, struct sci_port, freq_transition);
+
+	if ((phase == CPUFREQ_POSTCHANGE) ||
+	    (phase == CPUFREQ_RESUMECHANGE)) {
+		struct uart_port *port = &sci_port->port;
+
+		spin_lock_irqsave(&port->lock, flags);
+		port->uartclk = clk_get_rate(sci_port->iclk);
+		spin_unlock_irqrestore(&port->lock, flags);
+	}
+
+	return NOTIFY_OK;
+}
+
+static struct sci_irq_desc {
+	const char	*desc;
+	irq_handler_t	handler;
+} sci_irq_desc[] = {
+	/*
+	 * Split out handlers, the default case.
+	 */
+	[SCIx_ERI_IRQ] = {
+		.desc = "rx err",
+		.handler = sci_er_interrupt,
+	},
+
+	[SCIx_RXI_IRQ] = {
+		.desc = "rx full",
+		.handler = sci_rx_interrupt,
+	},
+
+	[SCIx_TXI_IRQ] = {
+		.desc = "tx empty",
+		.handler = sci_tx_interrupt,
+	},
+
+	[SCIx_BRI_IRQ] = {
+		.desc = "break",
+		.handler = sci_br_interrupt,
+	},
+
+	/*
+	 * Special muxed handler.
+	 */
+	[SCIx_MUX_IRQ] = {
+		.desc = "mux",
+		.handler = sci_mpxed_interrupt,
+	},
+};
+
+static int sci_request_irq(struct sci_port *port)
+{
+	struct uart_port *up = &port->port;
+	int i, j, ret = 0;
+
+	for (i = j = 0; i < SCIx_NR_IRQS; i++, j++) {
+		struct sci_irq_desc *desc;
+		unsigned int irq;
+
+		if (SCIx_IRQ_IS_MUXED(port)) {
+			i = SCIx_MUX_IRQ;
+			irq = up->irq;
+		} else
+			irq = port->cfg->irqs[i];
+
+		desc = sci_irq_desc + i;
+		port->irqstr[j] = kasprintf(GFP_KERNEL, "%s:%s",
+					    dev_name(up->dev), desc->desc);
+		if (!port->irqstr[j]) {
+			dev_err(up->dev, "Failed to allocate %s IRQ string\n",
+				desc->desc);
+			goto out_nomem;
+		}
+
+		ret = request_irq(irq, desc->handler, up->irqflags,
+				  port->irqstr[j], port);
+		if (unlikely(ret)) {
+			dev_err(up->dev, "Can't allocate %s IRQ\n", desc->desc);
+			goto out_noirq;
+		}
+	}
+
+	return 0;
+
+out_noirq:
+	while (--i >= 0)
+		free_irq(port->cfg->irqs[i], port);
+
+out_nomem:
+	while (--j >= 0)
+		kfree(port->irqstr[j]);
+
+	return ret;
+}
+
+static void sci_free_irq(struct sci_port *port)
+{
+	int i;
+
+	/*
+	 * Intentionally in reverse order so we iterate over the muxed
+	 * IRQ first.
+	 */
+	for (i = 0; i < SCIx_NR_IRQS; i++) {
+		free_irq(port->cfg->irqs[i], port);
+		kfree(port->irqstr[i]);
+
+		if (SCIx_IRQ_IS_MUXED(port)) {
+			/* If there's only one IRQ, we're done. */
+			return;
+		}
+	}
+}
+
+static const char *sci_gpio_names[SCIx_NR_FNS] = {
+	"sck", "rxd", "txd", "cts", "rts",
+};
+
+static const char *sci_gpio_str(unsigned int index)
+{
+	return sci_gpio_names[index];
+}
+
+static void __devinit sci_init_gpios(struct sci_port *port)
+{
+	struct uart_port *up = &port->port;
+	int i;
+
+	if (!port->cfg)
+		return;
+
+	for (i = 0; i < SCIx_NR_FNS; i++) {
+		const char *desc;
+		int ret;
+
+		if (!port->cfg->gpios[i])
+			continue;
+
+		desc = sci_gpio_str(i);
+
+		port->gpiostr[i] = kasprintf(GFP_KERNEL, "%s:%s",
+					     dev_name(up->dev), desc);
+
+		/*
+		 * If we've failed the allocation, we can still continue
+		 * on with a NULL string.
+		 */
+		if (!port->gpiostr[i])
+			dev_notice(up->dev, "%s string allocation failure\n",
+				   desc);
+
+		ret = gpio_request(port->cfg->gpios[i], port->gpiostr[i]);
+		if (unlikely(ret != 0)) {
+			dev_notice(up->dev, "failed %s gpio request\n", desc);
+
+			/*
+			 * If we can't get the GPIO for whatever reason,
+			 * no point in keeping the verbose string around.
+			 */
+			kfree(port->gpiostr[i]);
+		}
+	}
+}
+
+static void sci_free_gpios(struct sci_port *port)
+{
+	int i;
+
+	for (i = 0; i < SCIx_NR_FNS; i++)
+		if (port->cfg->gpios[i]) {
+			gpio_free(port->cfg->gpios[i]);
+			kfree(port->gpiostr[i]);
+		}
+}
+
+static unsigned int sci_tx_empty(struct uart_port *port)
+{
+	unsigned short status = serial_port_in(port, SCxSR);
+	unsigned short in_tx_fifo = sci_txfill(port);
+
+	return (status & SCxSR_TEND(port)) && !in_tx_fifo ? TIOCSER_TEMT : 0;
+}
+
+/*
+ * Modem control is a bit of a mixed bag for SCI(F) ports. Generally
+ * CTS/RTS is supported in hardware by at least one port and controlled
+ * via SCSPTR (SCxPCR for SCIFA/B parts), or external pins (presently
+ * handled via the ->init_pins() op, which is a bit of a one-way street,
+ * lacking any ability to defer pin control -- this will later be
+ * converted over to the GPIO framework).
+ *
+ * Other modes (such as loopback) are supported generically on certain
+ * port types, but not others. For these it's sufficient to test for the
+ * existence of the support register and simply ignore the port type.
+ */
+static void sci_set_mctrl(struct uart_port *port, unsigned int mctrl)
+{
+	if (mctrl & TIOCM_LOOP) {
+		struct plat_sci_reg *reg;
+
+		/*
+		 * Standard loopback mode for SCFCR ports.
+		 */
+		reg = sci_getreg(port, SCFCR);
+		if (reg->size)
+			serial_port_out(port, SCFCR, serial_port_in(port, SCFCR) | 1);
+	}
+}
+
+static unsigned int sci_get_mctrl(struct uart_port *port)
+{
+	/*
+	 * CTS/RTS is handled in hardware when supported, while nothing
+	 * else is wired up. Keep it simple and simply assert DSR/CAR.
+	 */
+	return TIOCM_DSR | TIOCM_CAR;
+}
+
+#ifdef CONFIG_SERIAL_SH_SCI_DMA
+static void sci_dma_tx_complete(void *arg)
+{
+	struct sci_port *s = arg;
+	struct uart_port *port = &s->port;
+	struct circ_buf *xmit = &port->state->xmit;
+	unsigned long flags;
+
+	dev_dbg(port->dev, "%s(%d)\n", __func__, port->line);
+
+	spin_lock_irqsave(&port->lock, flags);
+
+	xmit->tail += sg_dma_len(&s->sg_tx);
+	xmit->tail &= UART_XMIT_SIZE - 1;
+
+	port->icount.tx += sg_dma_len(&s->sg_tx);
+
+	async_tx_ack(s->desc_tx);
+	s->desc_tx = NULL;
+
+	if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
+		uart_write_wakeup(port);
+
+	if (!uart_circ_empty(xmit)) {
+		s->cookie_tx = 0;
+		schedule_work(&s->work_tx);
+	} else {
+		s->cookie_tx = -EINVAL;
+		if (port->type == PORT_SCIFA || port->type == PORT_SCIFB) {
+			u16 ctrl = serial_port_in(port, SCSCR);
+			serial_port_out(port, SCSCR, ctrl & ~SCSCR_TIE);
+		}
+	}
+
+	spin_unlock_irqrestore(&port->lock, flags);
+}
+
+/* Locking: called with port lock held */
+static int sci_dma_rx_push(struct sci_port *s, struct tty_struct *tty,
+			   size_t count)
+{
+	struct uart_port *port = &s->port;
+	int i, active, room;
+
+	room = tty_buffer_request_room(tty, count);
+
+	if (s->active_rx == s->cookie_rx[0]) {
+		active = 0;
+	} else if (s->active_rx == s->cookie_rx[1]) {
+		active = 1;
+	} else {
+		dev_err(port->dev, "cookie %d not found!\n", s->active_rx);
+		return 0;
+	}
+
+	if (room < count)
+		dev_warn(port->dev, "Rx overrun: dropping %u bytes\n",
+			 count - room);
+	if (!room)
+		return room;
+
+	for (i = 0; i < room; i++)
+		tty_insert_flip_char(tty, ((u8 *)sg_virt(&s->sg_rx[active]))[i],
+				     TTY_NORMAL);
+
+	port->icount.rx += room;
+
+	return room;
+}
+
+static void sci_dma_rx_complete(void *arg)
+{
+	struct sci_port *s = arg;
+	struct uart_port *port = &s->port;
+	struct tty_struct *tty = port->state->port.tty;
+	unsigned long flags;
+	int count;
+
+	dev_dbg(port->dev, "%s(%d) active #%d\n", __func__, port->line, s->active_rx);
+
+	spin_lock_irqsave(&port->lock, flags);
+
+	count = sci_dma_rx_push(s, tty, s->buf_len_rx);
+
+	mod_timer(&s->rx_timer, jiffies + s->rx_timeout);
+
+	spin_unlock_irqrestore(&port->lock, flags);
+
+	if (count)
+		tty_flip_buffer_push(tty);
+
+	schedule_work(&s->work_rx);
+}
+
+static void sci_rx_dma_release(struct sci_port *s, bool enable_pio)
+{
+	struct dma_chan *chan = s->chan_rx;
+	struct uart_port *port = &s->port;
+
+	s->chan_rx = NULL;
+	s->cookie_rx[0] = s->cookie_rx[1] = -EINVAL;
+	dma_release_channel(chan);
+	if (sg_dma_address(&s->sg_rx[0]))
+		dma_free_coherent(port->dev, s->buf_len_rx * 2,
+				  sg_virt(&s->sg_rx[0]), sg_dma_address(&s->sg_rx[0]));
+	if (enable_pio)
+		sci_start_rx(port);
+}
+
+static void sci_tx_dma_release(struct sci_port *s, bool enable_pio)
+{
+	struct dma_chan *chan = s->chan_tx;
+	struct uart_port *port = &s->port;
+
+	s->chan_tx = NULL;
+	s->cookie_tx = -EINVAL;
+	dma_release_channel(chan);
+	if (enable_pio)
+		sci_start_tx(port);
+}
+
+static void sci_submit_rx(struct sci_port *s)
+{
+	struct dma_chan *chan = s->chan_rx;
+	int i;
+
+	for (i = 0; i < 2; i++) {
+		struct scatterlist *sg = &s->sg_rx[i];
+		struct dma_async_tx_descriptor *desc;
+
+		desc = dmaengine_prep_slave_sg(chan,
+			sg, 1, DMA_DEV_TO_MEM, DMA_PREP_INTERRUPT);
+
+		if (desc) {
+			s->desc_rx[i] = desc;
+			desc->callback = sci_dma_rx_complete;
+			desc->callback_param = s;
+			s->cookie_rx[i] = desc->tx_submit(desc);
+		}
+
+		if (!desc || s->cookie_rx[i] < 0) {
+			if (i) {
+				async_tx_ack(s->desc_rx[0]);
+				s->cookie_rx[0] = -EINVAL;
+			}
+			if (desc) {
+				async_tx_ack(desc);
+				s->cookie_rx[i] = -EINVAL;
+			}
+			dev_warn(s->port.dev,
+				 "failed to re-start DMA, using PIO\n");
+			sci_rx_dma_release(s, true);
+			return;
+		}
+		dev_dbg(s->port.dev, "%s(): cookie %d to #%d\n", __func__,
+			s->cookie_rx[i], i);
+	}
+
+	s->active_rx = s->cookie_rx[0];
+
+	dma_async_issue_pending(chan);
+}
+
+static void work_fn_rx(struct work_struct *work)
+{
+	struct sci_port *s = container_of(work, struct sci_port, work_rx);
+	struct uart_port *port = &s->port;
+	struct dma_async_tx_descriptor *desc;
+	int new;
+
+	if (s->active_rx == s->cookie_rx[0]) {
+		new = 0;
+	} else if (s->active_rx == s->cookie_rx[1]) {
+		new = 1;
+	} else {
+		dev_err(port->dev, "cookie %d not found!\n", s->active_rx);
+		return;
+	}
+	desc = s->desc_rx[new];
+
+	if (dma_async_is_tx_complete(s->chan_rx, s->active_rx, NULL, NULL) !=
+	    DMA_SUCCESS) {
+		/* Handle incomplete DMA receive */
+		struct tty_struct *tty = port->state->port.tty;
+		struct dma_chan *chan = s->chan_rx;
+		struct sh_desc *sh_desc = container_of(desc, struct sh_desc,
+						       async_tx);
+		unsigned long flags;
+		int count;
+
+		chan->device->device_control(chan, DMA_TERMINATE_ALL, 0);
+		dev_dbg(port->dev, "Read %u bytes with cookie %d\n",
+			sh_desc->partial, sh_desc->cookie);
+
+		spin_lock_irqsave(&port->lock, flags);
+		count = sci_dma_rx_push(s, tty, sh_desc->partial);
+		spin_unlock_irqrestore(&port->lock, flags);
+
+		if (count)
+			tty_flip_buffer_push(tty);
+
+		sci_submit_rx(s);
+
+		return;
+	}
+
+	s->cookie_rx[new] = desc->tx_submit(desc);
+	if (s->cookie_rx[new] < 0) {
+		dev_warn(port->dev, "Failed submitting Rx DMA descriptor\n");
+		sci_rx_dma_release(s, true);
+		return;
+	}
+
+	s->active_rx = s->cookie_rx[!new];
+
+	dev_dbg(port->dev, "%s: cookie %d #%d, new active #%d\n", __func__,
+		s->cookie_rx[new], new, s->active_rx);
+}
+
+static void work_fn_tx(struct work_struct *work)
+{
+	struct sci_port *s = container_of(work, struct sci_port, work_tx);
+	struct dma_async_tx_descriptor *desc;
+	struct dma_chan *chan = s->chan_tx;
+	struct uart_port *port = &s->port;
+	struct circ_buf *xmit = &port->state->xmit;
+	struct scatterlist *sg = &s->sg_tx;
+
+	/*
+	 * DMA is idle now.
+	 * Port xmit buffer is already mapped, and it is one page... Just adjust
+	 * offsets and lengths. Since it is a circular buffer, we have to
+	 * transmit till the end, and then the rest. Take the port lock to get a
+	 * consistent xmit buffer state.
+	 */
+	spin_lock_irq(&port->lock);
+	sg->offset = xmit->tail & (UART_XMIT_SIZE - 1);
+	sg_dma_address(sg) = (sg_dma_address(sg) & ~(UART_XMIT_SIZE - 1)) +
+		sg->offset;
+	sg_dma_len(sg) = min((int)CIRC_CNT(xmit->head, xmit->tail, UART_XMIT_SIZE),
+		CIRC_CNT_TO_END(xmit->head, xmit->tail, UART_XMIT_SIZE));
+	spin_unlock_irq(&port->lock);
+
+	BUG_ON(!sg_dma_len(sg));
+
+	desc = dmaengine_prep_slave_sg(chan,
+			sg, s->sg_len_tx, DMA_MEM_TO_DEV,
+			DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
+	if (!desc) {
+		/* switch to PIO */
+		sci_tx_dma_release(s, true);
+		return;
+	}
+
+	dma_sync_sg_for_device(port->dev, sg, 1, DMA_TO_DEVICE);
+
+	spin_lock_irq(&port->lock);
+	s->desc_tx = desc;
+	desc->callback = sci_dma_tx_complete;
+	desc->callback_param = s;
+	spin_unlock_irq(&port->lock);
+	s->cookie_tx = desc->tx_submit(desc);
+	if (s->cookie_tx < 0) {
+		dev_warn(port->dev, "Failed submitting Tx DMA descriptor\n");
+		/* switch to PIO */
+		sci_tx_dma_release(s, true);
+		return;
+	}
+
+	dev_dbg(port->dev, "%s: %p: %d...%d, cookie %d\n", __func__,
+		xmit->buf, xmit->tail, xmit->head, s->cookie_tx);
+
+	dma_async_issue_pending(chan);
+}
+#endif
+
+static void sci_start_tx(struct uart_port *port)
+{
+	struct sci_port *s = to_sci_port(port);
+	unsigned short ctrl;
+
+#ifdef CONFIG_SERIAL_SH_SCI_DMA
+	if (port->type == PORT_SCIFA || port->type == PORT_SCIFB) {
+		u16 new, scr = serial_port_in(port, SCSCR);
+		if (s->chan_tx)
+			new = scr | 0x8000;
+		else
+			new = scr & ~0x8000;
+		if (new != scr)
+			serial_port_out(port, SCSCR, new);
+	}
+
+	if (s->chan_tx && !uart_circ_empty(&s->port.state->xmit) &&
+	    s->cookie_tx < 0) {
+		s->cookie_tx = 0;
+		schedule_work(&s->work_tx);
+	}
+#endif
+
+	if (!s->chan_tx || port->type == PORT_SCIFA || port->type == PORT_SCIFB) {
+		/* Set TIE (Transmit Interrupt Enable) bit in SCSCR */
+		ctrl = serial_port_in(port, SCSCR);
+		serial_port_out(port, SCSCR, ctrl | SCSCR_TIE);
+	}
+}
+
+static void sci_stop_tx(struct uart_port *port)
+{
+	unsigned short ctrl;
+
+	/* Clear TIE (Transmit Interrupt Enable) bit in SCSCR */
+	ctrl = serial_port_in(port, SCSCR);
+
+	if (port->type == PORT_SCIFA || port->type == PORT_SCIFB)
+		ctrl &= ~0x8000;
+
+	ctrl &= ~SCSCR_TIE;
+
+	serial_port_out(port, SCSCR, ctrl);
+}
+
+static void sci_start_rx(struct uart_port *port)
+{
+	unsigned short ctrl;
+
+	ctrl = serial_port_in(port, SCSCR) | port_rx_irq_mask(port);
+
+	if (port->type == PORT_SCIFA || port->type == PORT_SCIFB)
+		ctrl &= ~0x4000;
+
+	serial_port_out(port, SCSCR, ctrl);
+}
+
+static void sci_stop_rx(struct uart_port *port)
+{
+	unsigned short ctrl;
+
+	ctrl = serial_port_in(port, SCSCR);
+
+	if (port->type == PORT_SCIFA || port->type == PORT_SCIFB)
+		ctrl &= ~0x4000;
+
+	ctrl &= ~port_rx_irq_mask(port);
+
+	serial_port_out(port, SCSCR, ctrl);
+}
+
+static void sci_enable_ms(struct uart_port *port)
+{
+	/*
+	 * Not supported by hardware, always a nop.
+	 */
+}
+
+static void sci_break_ctl(struct uart_port *port, int break_state)
+{
+	/*
+	 * Not supported by hardware. Most parts couple break and rx
+	 * interrupts together, with break detection always enabled.
+	 */
+}
+
+#ifdef CONFIG_SERIAL_SH_SCI_DMA
+static bool filter(struct dma_chan *chan, void *slave)
+{
+	struct sh_dmae_slave *param = slave;
+
+	dev_dbg(chan->device->dev, "%s: slave ID %d\n", __func__,
+		param->slave_id);
+
+	chan->private = param;
+	return true;
+}
+
+static void rx_timer_fn(unsigned long arg)
+{
+	struct sci_port *s = (struct sci_port *)arg;
+	struct uart_port *port = &s->port;
+	u16 scr = serial_port_in(port, SCSCR);
+
+	if (port->type == PORT_SCIFA || port->type == PORT_SCIFB) {
+		scr &= ~0x4000;
+		enable_irq(s->cfg->irqs[1]);
+	}
+	serial_port_out(port, SCSCR, scr | SCSCR_RIE);
+	dev_dbg(port->dev, "DMA Rx timed out\n");
+	schedule_work(&s->work_rx);
+}
+
+static void sci_request_dma(struct uart_port *port)
+{
+	struct sci_port *s = to_sci_port(port);
+	struct sh_dmae_slave *param;
+	struct dma_chan *chan;
+	dma_cap_mask_t mask;
+	int nent;
+
+	dev_dbg(port->dev, "%s: port %d\n", __func__,
+		port->line);
+
+	if (s->cfg->dma_slave_tx <= 0 || s->cfg->dma_slave_rx <= 0)
+		return;
+
+	dma_cap_zero(mask);
+	dma_cap_set(DMA_SLAVE, mask);
+
+	param = &s->param_tx;
+
+	/* Slave ID, e.g., SHDMA_SLAVE_SCIF0_TX */
+	param->slave_id = s->cfg->dma_slave_tx;
+
+	s->cookie_tx = -EINVAL;
+	chan = dma_request_channel(mask, filter, param);
+	dev_dbg(port->dev, "%s: TX: got channel %p\n", __func__, chan);
+	if (chan) {
+		s->chan_tx = chan;
+		sg_init_table(&s->sg_tx, 1);
+		/* UART circular tx buffer is an aligned page. */
+		BUG_ON((int)port->state->xmit.buf & ~PAGE_MASK);
+		sg_set_page(&s->sg_tx, virt_to_page(port->state->xmit.buf),
+			    UART_XMIT_SIZE, (int)port->state->xmit.buf & ~PAGE_MASK);
+		nent = dma_map_sg(port->dev, &s->sg_tx, 1, DMA_TO_DEVICE);
+		if (!nent)
+			sci_tx_dma_release(s, false);
+		else
+			dev_dbg(port->dev, "%s: mapped %d@%p to %x\n", __func__,
+				sg_dma_len(&s->sg_tx),
+				port->state->xmit.buf, sg_dma_address(&s->sg_tx));
+
+		s->sg_len_tx = nent;
+
+		INIT_WORK(&s->work_tx, work_fn_tx);
+	}
+
+	param = &s->param_rx;
+
+	/* Slave ID, e.g., SHDMA_SLAVE_SCIF0_RX */
+	param->slave_id = s->cfg->dma_slave_rx;
+
+	chan = dma_request_channel(mask, filter, param);
+	dev_dbg(port->dev, "%s: RX: got channel %p\n", __func__, chan);
+	if (chan) {
+		dma_addr_t dma[2];
+		void *buf[2];
+		int i;
+
+		s->chan_rx = chan;
+
+		s->buf_len_rx = 2 * max(16, (int)port->fifosize);
+		buf[0] = dma_alloc_coherent(port->dev, s->buf_len_rx * 2,
+					    &dma[0], GFP_KERNEL);
+
+		if (!buf[0]) {
+			dev_warn(port->dev,
+				 "failed to allocate dma buffer, using PIO\n");
+			sci_rx_dma_release(s, true);
+			return;
+		}
+
+		buf[1] = buf[0] + s->buf_len_rx;
+		dma[1] = dma[0] + s->buf_len_rx;
+
+		for (i = 0; i < 2; i++) {
+			struct scatterlist *sg = &s->sg_rx[i];
+
+			sg_init_table(sg, 1);
+			sg_set_page(sg, virt_to_page(buf[i]), s->buf_len_rx,
+				    (int)buf[i] & ~PAGE_MASK);
+			sg_dma_address(sg) = dma[i];
+		}
+
+		INIT_WORK(&s->work_rx, work_fn_rx);
+		setup_timer(&s->rx_timer, rx_timer_fn, (unsigned long)s);
+
+		sci_submit_rx(s);
+	}
+}
+
+static void sci_free_dma(struct uart_port *port)
+{
+	struct sci_port *s = to_sci_port(port);
+
+	if (s->chan_tx)
+		sci_tx_dma_release(s, false);
+	if (s->chan_rx)
+		sci_rx_dma_release(s, false);
+}
+#else
+static inline void sci_request_dma(struct uart_port *port)
+{
+}
+
+static inline void sci_free_dma(struct uart_port *port)
+{
+}
+#endif
+
+static int sci_startup(struct uart_port *port)
+{
+	struct sci_port *s = to_sci_port(port);
+	int ret;
+
+	dev_dbg(port->dev, "%s(%d)\n", __func__, port->line);
+
+	pm_runtime_put_noidle(port->dev);
+
+	sci_port_enable(s);
+
+	ret = sci_request_irq(s);
+	if (unlikely(ret < 0))
+		return ret;
+
+	sci_request_dma(port);
+
+	sci_start_tx(port);
+	sci_start_rx(port);
+
+	return 0;
+}
+
+static void sci_shutdown(struct uart_port *port)
+{
+	struct sci_port *s = to_sci_port(port);
+
+	dev_dbg(port->dev, "%s(%d)\n", __func__, port->line);
+
+	sci_stop_rx(port);
+	sci_stop_tx(port);
+
+	sci_free_dma(port);
+	sci_free_irq(s);
+
+	sci_port_disable(s);
+
+	pm_runtime_get_noresume(port->dev);
+}
+
+static unsigned int sci_scbrr_calc(unsigned int algo_id, unsigned int bps,
+				   unsigned long freq)
+{
+	switch (algo_id) {
+	case SCBRR_ALGO_1:
+		return ((freq + 16 * bps) / (16 * bps) - 1);
+	case SCBRR_ALGO_2:
+		return ((freq + 16 * bps) / (32 * bps) - 1);
+	case SCBRR_ALGO_3:
+		return (((freq * 2) + 16 * bps) / (16 * bps) - 1);
+	case SCBRR_ALGO_4:
+		return (((freq * 2) + 16 * bps) / (32 * bps) - 1);
+	case SCBRR_ALGO_5:
+		return (((freq * 1000 / 32) / bps) - 1);
+	}
+
+	/* Warn, but use a safe default */
+	WARN_ON(1);
+
+	return ((freq + 16 * bps) / (32 * bps) - 1);
+}
+
+static void sci_reset(struct uart_port *port)
+{
+	struct plat_sci_reg *reg;
+	unsigned int status;
+
+	do {
+		status = serial_port_in(port, SCxSR);
+	} while (!(status & SCxSR_TEND(port)));
+
+	serial_port_out(port, SCSCR, 0x00);	/* TE=0, RE=0, CKE1=0 */
+
+	reg = sci_getreg(port, SCFCR);
+	if (reg->size)
+		serial_port_out(port, SCFCR, SCFCR_RFRST | SCFCR_TFRST);
+}
+
+static void sci_set_termios(struct uart_port *port, struct ktermios *termios,
+			    struct ktermios *old)
+{
+	struct sci_port *s = to_sci_port(port);
+	struct plat_sci_reg *reg;
+	unsigned int baud, smr_val, max_baud;
+	int t = -1;
+
+	/*
+	 * earlyprintk comes here early on with port->uartclk set to zero.
+	 * the clock framework is not up and running at this point so here
+	 * we assume that 115200 is the maximum baud rate. please note that
+	 * the baud rate is not programmed during earlyprintk - it is assumed
+	 * that the previous boot loader has enabled required clocks and
+	 * setup the baud rate generator hardware for us already.
+	 */
+	max_baud = port->uartclk ? port->uartclk / 16 : 115200;
+
+	baud = uart_get_baud_rate(port, termios, old, 0, max_baud);
+	if (likely(baud && port->uartclk))
+		t = sci_scbrr_calc(s->cfg->scbrr_algo_id, baud, port->uartclk);
+
+	sci_port_enable(s);
+
+	sci_reset(port);
+
+	smr_val = serial_port_in(port, SCSMR) & 3;
+
+	if ((termios->c_cflag & CSIZE) == CS7)
+		smr_val |= 0x40;
+	if (termios->c_cflag & PARENB)
+		smr_val |= 0x20;
+	if (termios->c_cflag & PARODD)
+		smr_val |= 0x30;
+	if (termios->c_cflag & CSTOPB)
+		smr_val |= 0x08;
+
+	uart_update_timeout(port, termios->c_cflag, baud);
+
+	serial_port_out(port, SCSMR, smr_val);
+
+	dev_dbg(port->dev, "%s: SMR %x, t %x, SCSCR %x\n", __func__, smr_val, t,
+		s->cfg->scscr);
+
+	if (t > 0) {
+		if (t >= 256) {
+			serial_port_out(port, SCSMR, (serial_port_in(port, SCSMR) & ~3) | 1);
+			t >>= 2;
+		} else
+			serial_port_out(port, SCSMR, serial_port_in(port, SCSMR) & ~3);
+
+		serial_port_out(port, SCBRR, t);
+		udelay((1000000+(baud-1)) / baud); /* Wait one bit interval */
+	}
+
+	sci_init_pins(port, termios->c_cflag);
+
+	reg = sci_getreg(port, SCFCR);
+	if (reg->size) {
+		unsigned short ctrl = serial_port_in(port, SCFCR);
+
+		if (s->cfg->capabilities & SCIx_HAVE_RTSCTS) {
+			if (termios->c_cflag & CRTSCTS)
+				ctrl |= SCFCR_MCE;
+			else
+				ctrl &= ~SCFCR_MCE;
+		}
+
+		/*
+		 * As we've done a sci_reset() above, ensure we don't
+		 * interfere with the FIFOs while toggling MCE. As the
+		 * reset values could still be set, simply mask them out.
+		 */
+		ctrl &= ~(SCFCR_RFRST | SCFCR_TFRST);
+
+		serial_port_out(port, SCFCR, ctrl);
+	}
+
+	serial_port_out(port, SCSCR, s->cfg->scscr);
+
+#ifdef CONFIG_SERIAL_SH_SCI_DMA
+	/*
+	 * Calculate delay for 1.5 DMA buffers: see
+	 * drivers/serial/serial_core.c::uart_update_timeout(). With 10 bits
+	 * (CS8), 250Hz, 115200 baud and 64 bytes FIFO, the above function
+	 * calculates 1 jiffie for the data plus 5 jiffies for the "slop(e)."
+	 * Then below we calculate 3 jiffies (12ms) for 1.5 DMA buffers (3 FIFO
+	 * sizes), but it has been found out experimentally, that this is not
+	 * enough: the driver too often needlessly runs on a DMA timeout. 20ms
+	 * as a minimum seem to work perfectly.
+	 */
+	if (s->chan_rx) {
+		s->rx_timeout = (port->timeout - HZ / 50) * s->buf_len_rx * 3 /
+			port->fifosize / 2;
+		dev_dbg(port->dev,
+			"DMA Rx t-out %ums, tty t-out %u jiffies\n",
+			s->rx_timeout * 1000 / HZ, port->timeout);
+		if (s->rx_timeout < msecs_to_jiffies(20))
+			s->rx_timeout = msecs_to_jiffies(20);
+	}
+#endif
+
+	if ((termios->c_cflag & CREAD) != 0)
+		sci_start_rx(port);
+
+	sci_port_disable(s);
+}
+
+static const char *sci_type(struct uart_port *port)
+{
+	switch (port->type) {
+	case PORT_IRDA:
+		return "irda";
+	case PORT_SCI:
+		return "sci";
+	case PORT_SCIF:
+		return "scif";
+	case PORT_SCIFA:
+		return "scifa";
+	case PORT_SCIFB:
+		return "scifb";
+	}
+
+	return NULL;
+}
+
+static inline unsigned long sci_port_size(struct uart_port *port)
+{
+	/*
+	 * Pick an arbitrary size that encapsulates all of the base
+	 * registers by default. This can be optimized later, or derived
+	 * from platform resource data at such a time that ports begin to
+	 * behave more erratically.
+	 */
+	return 64;
+}
+
+static int sci_remap_port(struct uart_port *port)
+{
+	unsigned long size = sci_port_size(port);
+
+	/*
+	 * Nothing to do if there's already an established membase.
+	 */
+	if (port->membase)
+		return 0;
+
+	if (port->flags & UPF_IOREMAP) {
+		port->membase = ioremap_nocache(port->mapbase, size);
+		if (unlikely(!port->membase)) {
+			dev_err(port->dev, "can't remap port#%d\n", port->line);
+			return -ENXIO;
+		}
+	} else {
+		/*
+		 * For the simple (and majority of) cases where we don't
+		 * need to do any remapping, just cast the cookie
+		 * directly.
+		 */
+		port->membase = (void __iomem *)port->mapbase;
+	}
+
+	return 0;
+}
+
+static void sci_release_port(struct uart_port *port)
+{
+	if (port->flags & UPF_IOREMAP) {
+		iounmap(port->membase);
+		port->membase = NULL;
+	}
+
+	release_mem_region(port->mapbase, sci_port_size(port));
+}
+
+static int sci_request_port(struct uart_port *port)
+{
+	unsigned long size = sci_port_size(port);
+	struct resource *res;
+	int ret;
+
+	res = request_mem_region(port->mapbase, size, dev_name(port->dev));
+	if (unlikely(res == NULL))
+		return -EBUSY;
+
+	ret = sci_remap_port(port);
+	if (unlikely(ret != 0)) {
+		release_resource(res);
+		return ret;
+	}
+
+	return 0;
+}
+
+static void sci_config_port(struct uart_port *port, int flags)
+{
+	if (flags & UART_CONFIG_TYPE) {
+		struct sci_port *sport = to_sci_port(port);
+
+		port->type = sport->cfg->type;
+		sci_request_port(port);
+	}
+}
+
+static int sci_verify_port(struct uart_port *port, struct serial_struct *ser)
+{
+	struct sci_port *s = to_sci_port(port);
+
+	if (ser->irq != s->cfg->irqs[SCIx_TXI_IRQ] || ser->irq > nr_irqs)
+		return -EINVAL;
+	if (ser->baud_base < 2400)
+		/* No paper tape reader for Mitch.. */
+		return -EINVAL;
+
+	return 0;
+}
+
+static struct uart_ops sci_uart_ops = {
+	.tx_empty	= sci_tx_empty,
+	.set_mctrl	= sci_set_mctrl,
+	.get_mctrl	= sci_get_mctrl,
+	.start_tx	= sci_start_tx,
+	.stop_tx	= sci_stop_tx,
+	.stop_rx	= sci_stop_rx,
+	.enable_ms	= sci_enable_ms,
+	.break_ctl	= sci_break_ctl,
+	.startup	= sci_startup,
+	.shutdown	= sci_shutdown,
+	.set_termios	= sci_set_termios,
+	.type		= sci_type,
+	.release_port	= sci_release_port,
+	.request_port	= sci_request_port,
+	.config_port	= sci_config_port,
+	.verify_port	= sci_verify_port,
+#ifdef CONFIG_CONSOLE_POLL
+	.poll_get_char	= sci_poll_get_char,
+	.poll_put_char	= sci_poll_put_char,
+#endif
+};
+
+static int __devinit sci_init_single(struct platform_device *dev,
+				     struct sci_port *sci_port,
+				     unsigned int index,
+				     struct plat_sci_port *p)
+{
+	struct uart_port *port = &sci_port->port;
+	int ret;
+
+	sci_port->cfg	= p;
+
+	port->ops	= &sci_uart_ops;
+	port->iotype	= UPIO_MEM;
+	port->line	= index;
+
+	switch (p->type) {
+	case PORT_SCIFB:
+		port->fifosize = 256;
+		break;
+	case PORT_SCIFA:
+		port->fifosize = 64;
+		break;
+	case PORT_SCIF:
+		port->fifosize = 16;
+		break;
+	default:
+		port->fifosize = 1;
+		break;
+	}
+
+	if (p->regtype == SCIx_PROBE_REGTYPE) {
+		ret = sci_probe_regmap(p);
+		if (unlikely(ret))
+			return ret;
+	}
+
+	if (dev) {
+		sci_port->iclk = clk_get(&dev->dev, "sci_ick");
+		if (IS_ERR(sci_port->iclk)) {
+			sci_port->iclk = clk_get(&dev->dev, "peripheral_clk");
+			if (IS_ERR(sci_port->iclk)) {
+				dev_err(&dev->dev, "can't get iclk\n");
+				return PTR_ERR(sci_port->iclk);
+			}
+		}
+
+		/*
+		 * The function clock is optional, ignore it if we can't
+		 * find it.
+		 */
+		sci_port->fclk = clk_get(&dev->dev, "sci_fck");
+		if (IS_ERR(sci_port->fclk))
+			sci_port->fclk = NULL;
+
+		port->dev = &dev->dev;
+
+		sci_init_gpios(sci_port);
+
+		pm_runtime_irq_safe(&dev->dev);
+		pm_runtime_get_noresume(&dev->dev);
+		pm_runtime_enable(&dev->dev);
+	}
+
+	sci_port->break_timer.data = (unsigned long)sci_port;
+	sci_port->break_timer.function = sci_break_timer;
+	init_timer(&sci_port->break_timer);
+
+	/*
+	 * Establish some sensible defaults for the error detection.
+	 */
+	if (!p->error_mask)
+		p->error_mask = (p->type == PORT_SCI) ?
+			SCI_DEFAULT_ERROR_MASK : SCIF_DEFAULT_ERROR_MASK;
+
+	/*
+	 * Establish sensible defaults for the overrun detection, unless
+	 * the part has explicitly disabled support for it.
+	 */
+	if (p->overrun_bit != SCIx_NOT_SUPPORTED) {
+		if (p->type == PORT_SCI)
+			p->overrun_bit = 5;
+		else if (p->scbrr_algo_id == SCBRR_ALGO_4)
+			p->overrun_bit = 9;
+		else
+			p->overrun_bit = 0;
+
+		/*
+		 * Make the error mask inclusive of overrun detection, if
+		 * supported.
+		 */
+		p->error_mask |= (1 << p->overrun_bit);
+	}
+
+	port->mapbase		= p->mapbase;
+	port->type		= p->type;
+	port->flags		= p->flags;
+	port->regshift		= p->regshift;
+
+	/*
+	 * The UART port needs an IRQ value, so we peg this to the RX IRQ
+	 * for the multi-IRQ ports, which is where we are primarily
+	 * concerned with the shutdown path synchronization.
+	 *
+	 * For the muxed case there's nothing more to do.
+	 */
+	port->irq		= p->irqs[SCIx_RXI_IRQ];
+	port->irqflags		= 0;
+
+	port->serial_in		= sci_serial_in;
+	port->serial_out	= sci_serial_out;
+
+	if (p->dma_slave_tx > 0 && p->dma_slave_rx > 0)
+		dev_dbg(port->dev, "DMA tx %d, rx %d\n",
+			p->dma_slave_tx, p->dma_slave_rx);
+
+	return 0;
+}
+
+#ifdef CONFIG_SERIAL_SH_SCI_CONSOLE
+static void serial_console_putchar(struct uart_port *port, int ch)
+{
+	sci_poll_put_char(port, ch);
+}
+
+/*
+ *	Print a string to the serial port trying not to disturb
+ *	any possible real use of the port...
+ */
+static void serial_console_write(struct console *co, const char *s,
+				 unsigned count)
+{
+	struct sci_port *sci_port = &sci_ports[co->index];
+	struct uart_port *port = &sci_port->port;
+	unsigned short bits;
+
+	sci_port_enable(sci_port);
+
+	uart_console_write(port, s, count, serial_console_putchar);
+
+	/* wait until fifo is empty and last bit has been transmitted */
+	bits = SCxSR_TDxE(port) | SCxSR_TEND(port);
+	while ((serial_port_in(port, SCxSR) & bits) != bits)
+		cpu_relax();
+
+	sci_port_disable(sci_port);
+}
+
+static int __devinit serial_console_setup(struct console *co, char *options)
+{
+	struct sci_port *sci_port;
+	struct uart_port *port;
+	int baud = 115200;
+	int bits = 8;
+	int parity = 'n';
+	int flow = 'n';
+	int ret;
+
+	/*
+	 * Refuse to handle any bogus ports.
+	 */
+	if (co->index < 0 || co->index >= SCI_NPORTS)
+		return -ENODEV;
+
+	sci_port = &sci_ports[co->index];
+	port = &sci_port->port;
+
+	/*
+	 * Refuse to handle uninitialized ports.
+	 */
+	if (!port->ops)
+		return -ENODEV;
+
+	ret = sci_remap_port(port);
+	if (unlikely(ret != 0))
+		return ret;
+
+	sci_port_enable(sci_port);
+
+	if (options)
+		uart_parse_options(options, &baud, &parity, &bits, &flow);
+
+	sci_port_disable(sci_port);
+
+	return uart_set_options(port, co, baud, parity, bits, flow);
+}
+
+static struct console serial_console = {
+	.name		= "ttySC",
+	.device		= uart_console_device,
+	.write		= serial_console_write,
+	.setup		= serial_console_setup,
+	.flags		= CON_PRINTBUFFER,
+	.index		= -1,
+	.data		= &sci_uart_driver,
+};
+
+static struct console early_serial_console = {
+	.name           = "early_ttySC",
+	.write          = serial_console_write,
+	.flags          = CON_PRINTBUFFER,
+	.index		= -1,
+};
+
+static char early_serial_buf[32];
+
+static int __devinit sci_probe_earlyprintk(struct platform_device *pdev)
+{
+	struct plat_sci_port *cfg = pdev->dev.platform_data;
+
+	if (early_serial_console.data)
+		return -EEXIST;
+
+	early_serial_console.index = pdev->id;
+
+	sci_init_single(NULL, &sci_ports[pdev->id], pdev->id, cfg);
+
+	serial_console_setup(&early_serial_console, early_serial_buf);
+
+	if (!strstr(early_serial_buf, "keep"))
+		early_serial_console.flags |= CON_BOOT;
+
+	register_console(&early_serial_console);
+	return 0;
+}
+
+#define uart_console(port)	((port)->cons->index == (port)->line)
+
+static int sci_runtime_suspend(struct device *dev)
+{
+	struct sci_port *sci_port = dev_get_drvdata(dev);
+	struct uart_port *port = &sci_port->port;
+
+	if (uart_console(port)) {
+		struct plat_sci_reg *reg;
+
+		sci_port->saved_smr = serial_port_in(port, SCSMR);
+		sci_port->saved_brr = serial_port_in(port, SCBRR);
+
+		reg = sci_getreg(port, SCFCR);
+		if (reg->size)
+			sci_port->saved_fcr = serial_port_in(port, SCFCR);
+		else
+			sci_port->saved_fcr = 0;
+	}
+	return 0;
+}
+
+static int sci_runtime_resume(struct device *dev)
+{
+	struct sci_port *sci_port = dev_get_drvdata(dev);
+	struct uart_port *port = &sci_port->port;
+
+	if (uart_console(port)) {
+		sci_reset(port);
+		serial_port_out(port, SCSMR, sci_port->saved_smr);
+		serial_port_out(port, SCBRR, sci_port->saved_brr);
+
+		if (sci_port->saved_fcr)
+			serial_port_out(port, SCFCR, sci_port->saved_fcr);
+
+		serial_port_out(port, SCSCR, sci_port->cfg->scscr);
+	}
+	return 0;
+}
+
+#define SCI_CONSOLE	(&serial_console)
+
+#else
+static inline int __devinit sci_probe_earlyprintk(struct platform_device *pdev)
+{
+	return -EINVAL;
+}
+
+#define SCI_CONSOLE	NULL
+#define sci_runtime_suspend	NULL
+#define sci_runtime_resume	NULL
+
+#endif /* CONFIG_SERIAL_SH_SCI_CONSOLE */
+
+static char banner[] __initdata =
+	KERN_INFO "SuperH SCI(F) driver initialized\n";
+
+static struct uart_driver sci_uart_driver = {
+	.owner		= THIS_MODULE,
+	.driver_name	= "sci",
+	.dev_name	= "ttySC",
+	.major		= SCI_MAJOR,
+	.minor		= SCI_MINOR_START,
+	.nr		= SCI_NPORTS,
+	.cons		= SCI_CONSOLE,
+};
+
+static int sci_remove(struct platform_device *dev)
+{
+	struct sci_port *port = platform_get_drvdata(dev);
+
+	cpufreq_unregister_notifier(&port->freq_transition,
+				    CPUFREQ_TRANSITION_NOTIFIER);
+
+	sci_free_gpios(port);
+
+	uart_remove_one_port(&sci_uart_driver, &port->port);
+
+	clk_put(port->iclk);
+	clk_put(port->fclk);
+
+	pm_runtime_disable(&dev->dev);
+	return 0;
+}
+
+static int __devinit sci_probe_single(struct platform_device *dev,
+				      unsigned int index,
+				      struct plat_sci_port *p,
+				      struct sci_port *sciport)
+{
+	int ret;
+
+	/* Sanity check */
+	if (unlikely(index >= SCI_NPORTS)) {
+		dev_notice(&dev->dev, "Attempting to register port "
+			   "%d when only %d are available.\n",
+			   index+1, SCI_NPORTS);
+		dev_notice(&dev->dev, "Consider bumping "
+			   "CONFIG_SERIAL_SH_SCI_NR_UARTS!\n");
+		return 0;
+	}
+
+	ret = sci_init_single(dev, sciport, index, p);
+	if (ret)
+		return ret;
+
+	return uart_add_one_port(&sci_uart_driver, &sciport->port);
+}
+
+static int __devinit sci_probe(struct platform_device *dev)
+{
+	struct plat_sci_port *p = dev->dev.platform_data;
+	struct sci_port *sp = &sci_ports[dev->id];
+	int ret;
+
+	/*
+	 * If we've come here via earlyprintk initialization, head off to
+	 * the special early probe. We don't have sufficient device state
+	 * to make it beyond this yet.
+	 */
+	if (is_early_platform_device(dev))
+		return sci_probe_earlyprintk(dev);
+
+	platform_set_drvdata(dev, sp);
+
+	ret = sci_probe_single(dev, dev->id, p, sp);
+	if (ret)
+		goto err_unreg;
+
+	sp->freq_transition.notifier_call = sci_notifier;
+
+	ret = cpufreq_register_notifier(&sp->freq_transition,
+					CPUFREQ_TRANSITION_NOTIFIER);
+	if (unlikely(ret < 0))
+		goto err_unreg;
+
+#ifdef CONFIG_SH_STANDARD_BIOS
+	sh_bios_gdb_detach();
+#endif
+
+	return 0;
+
+err_unreg:
+	sci_remove(dev);
+	return ret;
+}
+
+static int sci_suspend(struct device *dev)
+{
+	struct sci_port *sport = dev_get_drvdata(dev);
+
+	if (sport)
+		uart_suspend_port(&sci_uart_driver, &sport->port);
+
+	return 0;
+}
+
+static int sci_resume(struct device *dev)
+{
+	struct sci_port *sport = dev_get_drvdata(dev);
+
+	if (sport)
+		uart_resume_port(&sci_uart_driver, &sport->port);
+
+	return 0;
+}
+
+static const struct dev_pm_ops sci_dev_pm_ops = {
+	.runtime_suspend = sci_runtime_suspend,
+	.runtime_resume = sci_runtime_resume,
+	.suspend	= sci_suspend,
+	.resume		= sci_resume,
+};
+
+static struct platform_driver sci_driver = {
+	.probe		= sci_probe,
+	.remove		= sci_remove,
+	.driver		= {
+		.name	= "sh-sci",
+		.owner	= THIS_MODULE,
+		.pm	= &sci_dev_pm_ops,
+	},
+};
+
+static int __init sci_init(void)
+{
+	int ret;
+
+	printk(banner);
+
+	ret = uart_register_driver(&sci_uart_driver);
+	if (likely(ret == 0)) {
+		ret = platform_driver_register(&sci_driver);
+		if (unlikely(ret))
+			uart_unregister_driver(&sci_uart_driver);
+	}
+
+	return ret;
+}
+
+static void __exit sci_exit(void)
+{
+	platform_driver_unregister(&sci_driver);
+	uart_unregister_driver(&sci_uart_driver);
+}
+
+#ifdef CONFIG_SERIAL_SH_SCI_CONSOLE
+early_platform_init_buffer("earlyprintk", &sci_driver,
+			   early_serial_buf, ARRAY_SIZE(early_serial_buf));
+#endif
+module_init(sci_init);
+module_exit(sci_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:sh-sci");
+MODULE_AUTHOR("Paul Mundt");
+MODULE_DESCRIPTION("SuperH SCI(F) serial driver");
diff --git a/ap/os/linux/linux-3.4.x/drivers/tty/serial/sh-sci.h b/ap/os/linux/linux-3.4.x/drivers/tty/serial/sh-sci.h
new file mode 100644
index 0000000..4c22a15
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/tty/serial/sh-sci.h
@@ -0,0 +1,40 @@
+#include <linux/serial_core.h>
+#include <linux/io.h>
+#include <linux/gpio.h>
+
+#define SCxSR_TEND(port)	(((port)->type == PORT_SCI) ? SCI_TEND   : SCIF_TEND)
+#define SCxSR_RDxF(port)	(((port)->type == PORT_SCI) ? SCI_RDRF   : SCIF_RDF)
+#define SCxSR_TDxE(port)	(((port)->type == PORT_SCI) ? SCI_TDRE   : SCIF_TDFE)
+#define SCxSR_FER(port)		(((port)->type == PORT_SCI) ? SCI_FER    : SCIF_FER)
+#define SCxSR_PER(port)		(((port)->type == PORT_SCI) ? SCI_PER    : SCIF_PER)
+#define SCxSR_BRK(port)		(((port)->type == PORT_SCI) ? 0x00       : SCIF_BRK)
+
+#define SCxSR_ERRORS(port)	(to_sci_port(port)->cfg->error_mask)
+
+#if defined(CONFIG_CPU_SUBTYPE_SH7705) || \
+    defined(CONFIG_CPU_SUBTYPE_SH7720) || \
+    defined(CONFIG_CPU_SUBTYPE_SH7721) || \
+    defined(CONFIG_ARCH_SH73A0) || \
+    defined(CONFIG_ARCH_SH7367) || \
+    defined(CONFIG_ARCH_SH7377) || \
+    defined(CONFIG_ARCH_SH7372) || \
+    defined(CONFIG_ARCH_R8A7740)
+
+# define SCxSR_RDxF_CLEAR(port)	 (serial_port_in(port, SCxSR) & 0xfffc)
+# define SCxSR_ERROR_CLEAR(port) (serial_port_in(port, SCxSR) & 0xfd73)
+# define SCxSR_TDxE_CLEAR(port)	 (serial_port_in(port, SCxSR) & 0xffdf)
+# define SCxSR_BREAK_CLEAR(port) (serial_port_in(port, SCxSR) & 0xffe3)
+#else
+# define SCxSR_RDxF_CLEAR(port)	 (((port)->type == PORT_SCI) ? 0xbc : 0x00fc)
+# define SCxSR_ERROR_CLEAR(port) (((port)->type == PORT_SCI) ? 0xc4 : 0x0073)
+# define SCxSR_TDxE_CLEAR(port)  (((port)->type == PORT_SCI) ? 0x78 : 0x00df)
+# define SCxSR_BREAK_CLEAR(port) (((port)->type == PORT_SCI) ? 0xc4 : 0x00e3)
+#endif
+
+/* SCFCR */
+#define SCFCR_RFRST 0x0002
+#define SCFCR_TFRST 0x0004
+#define SCFCR_MCE   0x0008
+
+#define SCI_MAJOR		204
+#define SCI_MINOR_START		8
diff --git a/ap/os/linux/linux-3.4.x/drivers/tty/serial/sirfsoc_uart.c b/ap/os/linux/linux-3.4.x/drivers/tty/serial/sirfsoc_uart.c
new file mode 100644
index 0000000..5b3eda2
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/tty/serial/sirfsoc_uart.c
@@ -0,0 +1,777 @@
+/*
+ * Driver for CSR SiRFprimaII onboard UARTs.
+ *
+ * Copyright (c) 2011 Cambridge Silicon Radio Limited, a CSR plc group company.
+ *
+ * Licensed under GPLv2 or later.
+ */
+
+#include <linux/module.h>
+#include <linux/ioport.h>
+#include <linux/platform_device.h>
+#include <linux/init.h>
+#include <linux/sysrq.h>
+#include <linux/console.h>
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
+#include <linux/serial_core.h>
+#include <linux/serial.h>
+#include <linux/clk.h>
+#include <linux/of.h>
+#include <linux/slab.h>
+#include <linux/io.h>
+#include <asm/irq.h>
+#include <asm/mach/irq.h>
+#include <linux/pinctrl/consumer.h>
+
+#include "sirfsoc_uart.h"
+
+static unsigned int
+sirfsoc_uart_pio_tx_chars(struct sirfsoc_uart_port *sirfport, int count);
+static unsigned int
+sirfsoc_uart_pio_rx_chars(struct uart_port *port, unsigned int max_rx_count);
+static struct uart_driver sirfsoc_uart_drv;
+
+static const struct sirfsoc_baudrate_to_regv baudrate_to_regv[] = {
+	{4000000, 2359296},
+	{3500000, 1310721},
+	{3000000, 1572865},
+	{2500000, 1245186},
+	{2000000, 1572866},
+	{1500000, 1245188},
+	{1152000, 1638404},
+	{1000000, 1572869},
+	{921600, 1114120},
+	{576000, 1245196},
+	{500000, 1245198},
+	{460800, 1572876},
+	{230400, 1310750},
+	{115200, 1310781},
+	{57600, 1310843},
+	{38400, 1114328},
+	{19200, 1114545},
+	{9600, 1114979},
+};
+
+static struct sirfsoc_uart_port sirfsoc_uart_ports[SIRFSOC_UART_NR] = {
+	[0] = {
+		.port = {
+			.iotype		= UPIO_MEM,
+			.flags		= UPF_BOOT_AUTOCONF,
+			.line		= 0,
+		},
+	},
+	[1] = {
+		.port = {
+			.iotype		= UPIO_MEM,
+			.flags		= UPF_BOOT_AUTOCONF,
+			.line		= 1,
+		},
+	},
+	[2] = {
+		.port = {
+			.iotype		= UPIO_MEM,
+			.flags		= UPF_BOOT_AUTOCONF,
+			.line		= 2,
+		},
+	},
+};
+
+static inline struct sirfsoc_uart_port *to_sirfport(struct uart_port *port)
+{
+	return container_of(port, struct sirfsoc_uart_port, port);
+}
+
+static inline unsigned int sirfsoc_uart_tx_empty(struct uart_port *port)
+{
+	unsigned long reg;
+	reg = rd_regl(port, SIRFUART_TX_FIFO_STATUS);
+	if (reg & SIRFUART_FIFOEMPTY_MASK(port))
+		return TIOCSER_TEMT;
+	else
+		return 0;
+}
+
+static unsigned int sirfsoc_uart_get_mctrl(struct uart_port *port)
+{
+	struct sirfsoc_uart_port *sirfport = to_sirfport(port);
+	if (!(sirfport->ms_enabled)) {
+		goto cts_asserted;
+	} else if (sirfport->hw_flow_ctrl) {
+		if (!(rd_regl(port, SIRFUART_AFC_CTRL) &
+						SIRFUART_CTS_IN_STATUS))
+			goto cts_asserted;
+		else
+			goto cts_deasserted;
+	}
+cts_deasserted:
+	return TIOCM_CAR | TIOCM_DSR;
+cts_asserted:
+	return TIOCM_CAR | TIOCM_DSR | TIOCM_CTS;
+}
+
+static void sirfsoc_uart_set_mctrl(struct uart_port *port, unsigned int mctrl)
+{
+	struct sirfsoc_uart_port *sirfport = to_sirfport(port);
+	unsigned int assert = mctrl & TIOCM_RTS;
+	unsigned int val = assert ? SIRFUART_AFC_CTRL_RX_THD : 0x0;
+	unsigned int current_val;
+	if (sirfport->hw_flow_ctrl) {
+		current_val = rd_regl(port, SIRFUART_AFC_CTRL) & ~0xFF;
+		val |= current_val;
+		wr_regl(port, SIRFUART_AFC_CTRL, val);
+	}
+}
+
+static void sirfsoc_uart_stop_tx(struct uart_port *port)
+{
+	unsigned int regv;
+	regv = rd_regl(port, SIRFUART_INT_EN);
+	wr_regl(port, SIRFUART_INT_EN, regv & ~SIRFUART_TX_INT_EN);
+}
+
+void sirfsoc_uart_start_tx(struct uart_port *port)
+{
+	struct sirfsoc_uart_port *sirfport = to_sirfport(port);
+	unsigned long regv;
+	sirfsoc_uart_pio_tx_chars(sirfport, 1);
+	wr_regl(port, SIRFUART_TX_FIFO_OP, SIRFUART_TX_FIFO_START);
+	regv = rd_regl(port, SIRFUART_INT_EN);
+	wr_regl(port, SIRFUART_INT_EN, regv | SIRFUART_TX_INT_EN);
+}
+
+static void sirfsoc_uart_stop_rx(struct uart_port *port)
+{
+	unsigned long regv;
+	wr_regl(port, SIRFUART_RX_FIFO_OP, 0);
+	regv = rd_regl(port, SIRFUART_INT_EN);
+	wr_regl(port, SIRFUART_INT_EN, regv & ~SIRFUART_RX_IO_INT_EN);
+}
+
+static void sirfsoc_uart_disable_ms(struct uart_port *port)
+{
+	struct sirfsoc_uart_port *sirfport = to_sirfport(port);
+	unsigned long reg;
+	sirfport->ms_enabled = 0;
+	if (!sirfport->hw_flow_ctrl)
+		return;
+	reg = rd_regl(port, SIRFUART_AFC_CTRL);
+	wr_regl(port, SIRFUART_AFC_CTRL, reg & ~0x3FF);
+	reg = rd_regl(port, SIRFUART_INT_EN);
+	wr_regl(port, SIRFUART_INT_EN, reg & ~SIRFUART_CTS_INT_EN);
+}
+
+static void sirfsoc_uart_enable_ms(struct uart_port *port)
+{
+	struct sirfsoc_uart_port *sirfport = to_sirfport(port);
+	unsigned long reg;
+	unsigned long flg;
+	if (!sirfport->hw_flow_ctrl)
+		return;
+	flg = SIRFUART_AFC_RX_EN | SIRFUART_AFC_TX_EN;
+	reg = rd_regl(port, SIRFUART_AFC_CTRL);
+	wr_regl(port, SIRFUART_AFC_CTRL, reg | flg);
+	reg = rd_regl(port, SIRFUART_INT_EN);
+	wr_regl(port, SIRFUART_INT_EN, reg | SIRFUART_CTS_INT_EN);
+	uart_handle_cts_change(port,
+		!(rd_regl(port, SIRFUART_AFC_CTRL) & SIRFUART_CTS_IN_STATUS));
+	sirfport->ms_enabled = 1;
+}
+
+static void sirfsoc_uart_break_ctl(struct uart_port *port, int break_state)
+{
+	unsigned long ulcon = rd_regl(port, SIRFUART_LINE_CTRL);
+	if (break_state)
+		ulcon |= SIRFUART_SET_BREAK;
+	else
+		ulcon &= ~SIRFUART_SET_BREAK;
+	wr_regl(port, SIRFUART_LINE_CTRL, ulcon);
+}
+
+static unsigned int
+sirfsoc_uart_pio_rx_chars(struct uart_port *port, unsigned int max_rx_count)
+{
+	unsigned int ch, rx_count = 0;
+	struct tty_struct *tty;
+
+	tty = tty_port_tty_get(&port->state->port);
+	if (!tty)
+		return -ENODEV;
+
+	while (!(rd_regl(port, SIRFUART_RX_FIFO_STATUS) &
+					SIRFUART_FIFOEMPTY_MASK(port))) {
+		ch = rd_regl(port, SIRFUART_RX_FIFO_DATA) | SIRFUART_DUMMY_READ;
+		if (unlikely(uart_handle_sysrq_char(port, ch)))
+			continue;
+		uart_insert_char(port, 0, 0, ch, TTY_NORMAL);
+		rx_count++;
+		if (rx_count >= max_rx_count)
+			break;
+	}
+
+	port->icount.rx += rx_count;
+	tty_flip_buffer_push(tty);
+	tty_kref_put(tty);
+
+	return rx_count;
+}
+
+static unsigned int
+sirfsoc_uart_pio_tx_chars(struct sirfsoc_uart_port *sirfport, int count)
+{
+	struct uart_port *port = &sirfport->port;
+	struct circ_buf *xmit = &port->state->xmit;
+	unsigned int num_tx = 0;
+	while (!uart_circ_empty(xmit) &&
+		!(rd_regl(port, SIRFUART_TX_FIFO_STATUS) &
+					SIRFUART_FIFOFULL_MASK(port)) &&
+		count--) {
+		wr_regl(port, SIRFUART_TX_FIFO_DATA, xmit->buf[xmit->tail]);
+		xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
+		port->icount.tx++;
+		num_tx++;
+	}
+	if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
+		uart_write_wakeup(port);
+	return num_tx;
+}
+
+static irqreturn_t sirfsoc_uart_isr(int irq, void *dev_id)
+{
+	unsigned long intr_status;
+	unsigned long cts_status;
+	unsigned long flag = TTY_NORMAL;
+	struct sirfsoc_uart_port *sirfport = (struct sirfsoc_uart_port *)dev_id;
+	struct uart_port *port = &sirfport->port;
+	struct uart_state *state = port->state;
+	struct circ_buf *xmit = &port->state->xmit;
+	intr_status = rd_regl(port, SIRFUART_INT_STATUS);
+	wr_regl(port, SIRFUART_INT_STATUS, intr_status);
+	intr_status &= rd_regl(port, SIRFUART_INT_EN);
+	if (unlikely(intr_status & (SIRFUART_ERR_INT_STAT))) {
+		if (intr_status & SIRFUART_RXD_BREAK) {
+			if (uart_handle_break(port))
+				goto recv_char;
+			uart_insert_char(port, intr_status,
+					SIRFUART_RX_OFLOW, 0, TTY_BREAK);
+			return IRQ_HANDLED;
+		}
+		if (intr_status & SIRFUART_RX_OFLOW)
+			port->icount.overrun++;
+		if (intr_status & SIRFUART_FRM_ERR) {
+			port->icount.frame++;
+			flag = TTY_FRAME;
+		}
+		if (intr_status & SIRFUART_PARITY_ERR)
+			flag = TTY_PARITY;
+		wr_regl(port, SIRFUART_RX_FIFO_OP, SIRFUART_RX_FIFO_RESET);
+		wr_regl(port, SIRFUART_RX_FIFO_OP, 0);
+		wr_regl(port, SIRFUART_RX_FIFO_OP, SIRFUART_RX_FIFO_START);
+		intr_status &= port->read_status_mask;
+		uart_insert_char(port, intr_status,
+					SIRFUART_RX_OFLOW_INT, 0, flag);
+	}
+recv_char:
+	if (intr_status & SIRFUART_CTS_INT_EN) {
+		cts_status = !(rd_regl(port, SIRFUART_AFC_CTRL) &
+							SIRFUART_CTS_IN_STATUS);
+		if (cts_status != 0) {
+			uart_handle_cts_change(port, 1);
+		} else {
+			uart_handle_cts_change(port, 0);
+			wake_up_interruptible(&state->port.delta_msr_wait);
+		}
+	}
+	if (intr_status & SIRFUART_RX_IO_INT_EN)
+		sirfsoc_uart_pio_rx_chars(port, SIRFSOC_UART_IO_RX_MAX_CNT);
+	if (intr_status & SIRFUART_TX_INT_EN) {
+		if (uart_circ_empty(xmit) || uart_tx_stopped(port)) {
+			return IRQ_HANDLED;
+		} else {
+			sirfsoc_uart_pio_tx_chars(sirfport,
+					SIRFSOC_UART_IO_TX_REASONABLE_CNT);
+			if ((uart_circ_empty(xmit)) &&
+				(rd_regl(port, SIRFUART_TX_FIFO_STATUS) &
+						SIRFUART_FIFOEMPTY_MASK(port)))
+				sirfsoc_uart_stop_tx(port);
+		}
+	}
+	return IRQ_HANDLED;
+}
+
+static void sirfsoc_uart_start_rx(struct uart_port *port)
+{
+	unsigned long regv;
+	regv = rd_regl(port, SIRFUART_INT_EN);
+	wr_regl(port, SIRFUART_INT_EN, regv | SIRFUART_RX_IO_INT_EN);
+	wr_regl(port, SIRFUART_RX_FIFO_OP, SIRFUART_RX_FIFO_RESET);
+	wr_regl(port, SIRFUART_RX_FIFO_OP, 0);
+	wr_regl(port, SIRFUART_RX_FIFO_OP, SIRFUART_RX_FIFO_START);
+}
+
+static unsigned int
+sirfsoc_calc_sample_div(unsigned long baud_rate,
+			unsigned long ioclk_rate, unsigned long *setted_baud)
+{
+	unsigned long min_delta = ~0UL;
+	unsigned short sample_div;
+	unsigned int regv = 0;
+	unsigned long ioclk_div;
+	unsigned long baud_tmp;
+	int temp_delta;
+
+	for (sample_div = SIRF_MIN_SAMPLE_DIV;
+			sample_div <= SIRF_MAX_SAMPLE_DIV; sample_div++) {
+		ioclk_div = (ioclk_rate / (baud_rate * (sample_div + 1))) - 1;
+		if (ioclk_div > SIRF_IOCLK_DIV_MAX)
+			continue;
+		baud_tmp = ioclk_rate / ((ioclk_div + 1) * (sample_div + 1));
+		temp_delta = baud_tmp - baud_rate;
+		temp_delta = (temp_delta > 0) ? temp_delta : -temp_delta;
+		if (temp_delta < min_delta) {
+			regv = regv & (~SIRF_IOCLK_DIV_MASK);
+			regv = regv | ioclk_div;
+			regv = regv & (~SIRF_SAMPLE_DIV_MASK);
+			regv = regv | (sample_div << SIRF_SAMPLE_DIV_SHIFT);
+			min_delta = temp_delta;
+			*setted_baud = baud_tmp;
+		}
+	}
+	return regv;
+}
+
+static void sirfsoc_uart_set_termios(struct uart_port *port,
+				       struct ktermios *termios,
+				       struct ktermios *old)
+{
+	struct sirfsoc_uart_port *sirfport = to_sirfport(port);
+	unsigned long	ioclk_rate;
+	unsigned long	config_reg = 0;
+	unsigned long	baud_rate;
+	unsigned long	setted_baud;
+	unsigned long	flags;
+	unsigned long	ic;
+	unsigned int	clk_div_reg = 0;
+	unsigned long	temp_reg_val;
+	unsigned long	rx_time_out;
+	int		threshold_div;
+	int		temp;
+
+	ioclk_rate = 150000000;
+	switch (termios->c_cflag & CSIZE) {
+	default:
+	case CS8:
+		config_reg |= SIRFUART_DATA_BIT_LEN_8;
+		break;
+	case CS7:
+		config_reg |= SIRFUART_DATA_BIT_LEN_7;
+		break;
+	case CS6:
+		config_reg |= SIRFUART_DATA_BIT_LEN_6;
+		break;
+	case CS5:
+		config_reg |= SIRFUART_DATA_BIT_LEN_5;
+		break;
+	}
+	if (termios->c_cflag & CSTOPB)
+		config_reg |= SIRFUART_STOP_BIT_LEN_2;
+	baud_rate = uart_get_baud_rate(port, termios, old, 0, 4000000);
+	spin_lock_irqsave(&port->lock, flags);
+	port->read_status_mask = SIRFUART_RX_OFLOW_INT;
+	port->ignore_status_mask = 0;
+	/* read flags */
+	if (termios->c_iflag & INPCK)
+		port->read_status_mask |=
+			SIRFUART_FRM_ERR_INT | SIRFUART_PARITY_ERR_INT;
+	if (termios->c_iflag & (BRKINT | PARMRK))
+		port->read_status_mask |= SIRFUART_RXD_BREAK_INT;
+	/* ignore flags */
+	if (termios->c_iflag & IGNPAR)
+		port->ignore_status_mask |=
+			SIRFUART_FRM_ERR_INT | SIRFUART_PARITY_ERR_INT;
+	if ((termios->c_cflag & CREAD) == 0)
+		port->ignore_status_mask |= SIRFUART_DUMMY_READ;
+	/* enable parity if PARENB is set*/
+	if (termios->c_cflag & PARENB) {
+		if (termios->c_cflag & CMSPAR) {
+			if (termios->c_cflag & PARODD)
+				config_reg |= SIRFUART_STICK_BIT_MARK;
+			else
+				config_reg |= SIRFUART_STICK_BIT_SPACE;
+		} else if (termios->c_cflag & PARODD) {
+			config_reg |= SIRFUART_STICK_BIT_ODD;
+		} else {
+			config_reg |= SIRFUART_STICK_BIT_EVEN;
+		}
+	}
+	/* Hardware Flow Control Settings */
+	if (UART_ENABLE_MS(port, termios->c_cflag)) {
+		if (!sirfport->ms_enabled)
+			sirfsoc_uart_enable_ms(port);
+	} else {
+		if (sirfport->ms_enabled)
+			sirfsoc_uart_disable_ms(port);
+	}
+
+	/* common rate: fast calculation */
+	for (ic = 0; ic < SIRF_BAUD_RATE_SUPPORT_NR; ic++)
+		if (baud_rate == baudrate_to_regv[ic].baud_rate)
+			clk_div_reg = baudrate_to_regv[ic].reg_val;
+	setted_baud = baud_rate;
+	/* arbitary rate setting */
+	if (unlikely(clk_div_reg == 0))
+		clk_div_reg = sirfsoc_calc_sample_div(baud_rate, ioclk_rate,
+								&setted_baud);
+	wr_regl(port, SIRFUART_DIVISOR, clk_div_reg);
+
+	if (tty_termios_baud_rate(termios))
+		tty_termios_encode_baud_rate(termios, setted_baud, setted_baud);
+
+	/* set receive timeout */
+	rx_time_out = SIRFSOC_UART_RX_TIMEOUT(baud_rate, 20000);
+	rx_time_out = (rx_time_out > 0xFFFF) ? 0xFFFF : rx_time_out;
+	config_reg |= SIRFUART_RECV_TIMEOUT(rx_time_out);
+	temp_reg_val = rd_regl(port, SIRFUART_TX_FIFO_OP);
+	wr_regl(port, SIRFUART_RX_FIFO_OP, 0);
+	wr_regl(port, SIRFUART_TX_FIFO_OP,
+				temp_reg_val & ~SIRFUART_TX_FIFO_START);
+	wr_regl(port, SIRFUART_TX_DMA_IO_CTRL, SIRFUART_TX_MODE_IO);
+	wr_regl(port, SIRFUART_RX_DMA_IO_CTRL, SIRFUART_RX_MODE_IO);
+	wr_regl(port, SIRFUART_LINE_CTRL, config_reg);
+
+	/* Reset Rx/Tx FIFO Threshold level for proper baudrate */
+	if (baud_rate < 1000000)
+		threshold_div = 1;
+	else
+		threshold_div = 2;
+	temp = port->line == 1 ? 16 : 64;
+	wr_regl(port, SIRFUART_TX_FIFO_CTRL, temp / threshold_div);
+	wr_regl(port, SIRFUART_RX_FIFO_CTRL, temp / threshold_div);
+	temp_reg_val |= SIRFUART_TX_FIFO_START;
+	wr_regl(port, SIRFUART_TX_FIFO_OP, temp_reg_val);
+	uart_update_timeout(port, termios->c_cflag, baud_rate);
+	sirfsoc_uart_start_rx(port);
+	wr_regl(port, SIRFUART_TX_RX_EN, SIRFUART_TX_EN | SIRFUART_RX_EN);
+	spin_unlock_irqrestore(&port->lock, flags);
+}
+
+static void startup_uart_controller(struct uart_port *port)
+{
+	unsigned long temp_regv;
+	int temp;
+	temp_regv = rd_regl(port, SIRFUART_TX_DMA_IO_CTRL);
+	wr_regl(port, SIRFUART_TX_DMA_IO_CTRL, temp_regv | SIRFUART_TX_MODE_IO);
+	temp_regv = rd_regl(port, SIRFUART_RX_DMA_IO_CTRL);
+	wr_regl(port, SIRFUART_RX_DMA_IO_CTRL, temp_regv | SIRFUART_RX_MODE_IO);
+	wr_regl(port, SIRFUART_TX_DMA_IO_LEN, 0);
+	wr_regl(port, SIRFUART_RX_DMA_IO_LEN, 0);
+	wr_regl(port, SIRFUART_TX_RX_EN, SIRFUART_RX_EN | SIRFUART_TX_EN);
+	wr_regl(port, SIRFUART_TX_FIFO_OP, SIRFUART_TX_FIFO_RESET);
+	wr_regl(port, SIRFUART_TX_FIFO_OP, 0);
+	wr_regl(port, SIRFUART_RX_FIFO_OP, SIRFUART_RX_FIFO_RESET);
+	wr_regl(port, SIRFUART_RX_FIFO_OP, 0);
+	temp = port->line == 1 ? 16 : 64;
+	wr_regl(port, SIRFUART_TX_FIFO_CTRL, temp);
+	wr_regl(port, SIRFUART_RX_FIFO_CTRL, temp);
+}
+
+static int sirfsoc_uart_startup(struct uart_port *port)
+{
+	struct sirfsoc_uart_port *sirfport	= to_sirfport(port);
+	unsigned int index			= port->line;
+	int ret;
+	set_irq_flags(port->irq, IRQF_VALID | IRQF_NOAUTOEN);
+	ret = request_irq(port->irq,
+				sirfsoc_uart_isr,
+				0,
+				SIRFUART_PORT_NAME,
+				sirfport);
+	if (ret != 0) {
+		dev_err(port->dev, "UART%d request IRQ line (%d) failed.\n",
+							index, port->irq);
+		goto irq_err;
+	}
+	startup_uart_controller(port);
+	enable_irq(port->irq);
+irq_err:
+	return ret;
+}
+
+static void sirfsoc_uart_shutdown(struct uart_port *port)
+{
+	struct sirfsoc_uart_port *sirfport = to_sirfport(port);
+	wr_regl(port, SIRFUART_INT_EN, 0);
+	free_irq(port->irq, sirfport);
+	if (sirfport->ms_enabled) {
+		sirfsoc_uart_disable_ms(port);
+		sirfport->ms_enabled = 0;
+	}
+}
+
+static const char *sirfsoc_uart_type(struct uart_port *port)
+{
+	return port->type == SIRFSOC_PORT_TYPE ? SIRFUART_PORT_NAME : NULL;
+}
+
+static int sirfsoc_uart_request_port(struct uart_port *port)
+{
+	void *ret;
+	ret = request_mem_region(port->mapbase,
+				SIRFUART_MAP_SIZE, SIRFUART_PORT_NAME);
+	return ret ? 0 : -EBUSY;
+}
+
+static void sirfsoc_uart_release_port(struct uart_port *port)
+{
+	release_mem_region(port->mapbase, SIRFUART_MAP_SIZE);
+}
+
+static void sirfsoc_uart_config_port(struct uart_port *port, int flags)
+{
+	if (flags & UART_CONFIG_TYPE) {
+		port->type = SIRFSOC_PORT_TYPE;
+		sirfsoc_uart_request_port(port);
+	}
+}
+
+static struct uart_ops sirfsoc_uart_ops = {
+	.tx_empty	= sirfsoc_uart_tx_empty,
+	.get_mctrl	= sirfsoc_uart_get_mctrl,
+	.set_mctrl	= sirfsoc_uart_set_mctrl,
+	.stop_tx	= sirfsoc_uart_stop_tx,
+	.start_tx	= sirfsoc_uart_start_tx,
+	.stop_rx	= sirfsoc_uart_stop_rx,
+	.enable_ms	= sirfsoc_uart_enable_ms,
+	.break_ctl	= sirfsoc_uart_break_ctl,
+	.startup	= sirfsoc_uart_startup,
+	.shutdown	= sirfsoc_uart_shutdown,
+	.set_termios	= sirfsoc_uart_set_termios,
+	.type		= sirfsoc_uart_type,
+	.release_port	= sirfsoc_uart_release_port,
+	.request_port	= sirfsoc_uart_request_port,
+	.config_port	= sirfsoc_uart_config_port,
+};
+
+#ifdef CONFIG_SERIAL_SIRFSOC_CONSOLE
+static int __init sirfsoc_uart_console_setup(struct console *co, char *options)
+{
+	unsigned int baud = 115200;
+	unsigned int bits = 8;
+	unsigned int parity = 'n';
+	unsigned int flow = 'n';
+	struct uart_port *port = &sirfsoc_uart_ports[co->index].port;
+
+	if (co->index < 0 || co->index >= SIRFSOC_UART_NR)
+		return -EINVAL;
+
+	if (!port->mapbase)
+		return -ENODEV;
+
+	if (options)
+		uart_parse_options(options, &baud, &parity, &bits, &flow);
+	port->cons = co;
+	return uart_set_options(port, co, baud, parity, bits, flow);
+}
+
+static void sirfsoc_uart_console_putchar(struct uart_port *port, int ch)
+{
+	while (rd_regl(port,
+		SIRFUART_TX_FIFO_STATUS) & SIRFUART_FIFOFULL_MASK(port))
+		cpu_relax();
+	wr_regb(port, SIRFUART_TX_FIFO_DATA, ch);
+}
+
+static void sirfsoc_uart_console_write(struct console *co, const char *s,
+							unsigned int count)
+{
+	struct uart_port *port = &sirfsoc_uart_ports[co->index].port;
+	uart_console_write(port, s, count, sirfsoc_uart_console_putchar);
+}
+
+static struct console sirfsoc_uart_console = {
+	.name		= SIRFSOC_UART_NAME,
+	.device		= uart_console_device,
+	.flags		= CON_PRINTBUFFER,
+	.index		= -1,
+	.write		= sirfsoc_uart_console_write,
+	.setup		= sirfsoc_uart_console_setup,
+	.data           = &sirfsoc_uart_drv,
+};
+
+static int __init sirfsoc_uart_console_init(void)
+{
+	register_console(&sirfsoc_uart_console);
+	return 0;
+}
+console_initcall(sirfsoc_uart_console_init);
+#endif
+
+static struct uart_driver sirfsoc_uart_drv = {
+	.owner		= THIS_MODULE,
+	.driver_name	= SIRFUART_PORT_NAME,
+	.nr		= SIRFSOC_UART_NR,
+	.dev_name	= SIRFSOC_UART_NAME,
+	.major		= SIRFSOC_UART_MAJOR,
+	.minor		= SIRFSOC_UART_MINOR,
+#ifdef CONFIG_SERIAL_SIRFSOC_CONSOLE
+	.cons			= &sirfsoc_uart_console,
+#else
+	.cons			= NULL,
+#endif
+};
+
+int sirfsoc_uart_probe(struct platform_device *pdev)
+{
+	struct sirfsoc_uart_port *sirfport;
+	struct uart_port *port;
+	struct resource *res;
+	int ret;
+
+	if (of_property_read_u32(pdev->dev.of_node, "cell-index", &pdev->id)) {
+		dev_err(&pdev->dev,
+			"Unable to find cell-index in uart node.\n");
+		ret = -EFAULT;
+		goto err;
+	}
+
+	sirfport = &sirfsoc_uart_ports[pdev->id];
+	port = &sirfport->port;
+	port->dev = &pdev->dev;
+	port->private_data = sirfport;
+
+	if (of_find_property(pdev->dev.of_node, "hw_flow_ctrl", NULL))
+		sirfport->hw_flow_ctrl = 1;
+
+	if (of_property_read_u32(pdev->dev.of_node,
+			"fifosize",
+			&port->fifosize)) {
+		dev_err(&pdev->dev,
+			"Unable to find fifosize in uart node.\n");
+		ret = -EFAULT;
+		goto err;
+	}
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (res == NULL) {
+		dev_err(&pdev->dev, "Insufficient resources.\n");
+		ret = -EFAULT;
+		goto err;
+	}
+	port->mapbase = res->start;
+	port->membase = devm_ioremap(&pdev->dev, res->start, resource_size(res));
+	if (!port->membase) {
+		dev_err(&pdev->dev, "Cannot remap resource.\n");
+		ret = -ENOMEM;
+		goto err;
+	}
+	res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+	if (res == NULL) {
+		dev_err(&pdev->dev, "Insufficient resources.\n");
+		ret = -EFAULT;
+		goto irq_err;
+	}
+	port->irq = res->start;
+
+	if (sirfport->hw_flow_ctrl) {
+		sirfport->p = pinctrl_get_select_default(&pdev->dev);
+		ret = IS_ERR(sirfport->p);
+		if (ret)
+			goto pin_err;
+	}
+
+	port->ops = &sirfsoc_uart_ops;
+	spin_lock_init(&port->lock);
+
+	platform_set_drvdata(pdev, sirfport);
+	ret = uart_add_one_port(&sirfsoc_uart_drv, port);
+	if (ret != 0) {
+		dev_err(&pdev->dev, "Cannot add UART port(%d).\n", pdev->id);
+		goto port_err;
+	}
+
+	return 0;
+
+port_err:
+	platform_set_drvdata(pdev, NULL);
+	if (sirfport->hw_flow_ctrl)
+		pinctrl_put(sirfport->p);
+pin_err:
+irq_err:
+	devm_iounmap(&pdev->dev, port->membase);
+err:
+	return ret;
+}
+
+static int sirfsoc_uart_remove(struct platform_device *pdev)
+{
+	struct sirfsoc_uart_port *sirfport = platform_get_drvdata(pdev);
+	struct uart_port *port = &sirfport->port;
+	platform_set_drvdata(pdev, NULL);
+	if (sirfport->hw_flow_ctrl)
+		pinctrl_put(sirfport->p);
+	devm_iounmap(&pdev->dev, port->membase);
+	uart_remove_one_port(&sirfsoc_uart_drv, port);
+	return 0;
+}
+
+static int
+sirfsoc_uart_suspend(struct platform_device *pdev, pm_message_t state)
+{
+	struct sirfsoc_uart_port *sirfport = platform_get_drvdata(pdev);
+	struct uart_port *port = &sirfport->port;
+	uart_suspend_port(&sirfsoc_uart_drv, port);
+	return 0;
+}
+
+static int sirfsoc_uart_resume(struct platform_device *pdev)
+{
+	struct sirfsoc_uart_port *sirfport = platform_get_drvdata(pdev);
+	struct uart_port *port = &sirfport->port;
+	uart_resume_port(&sirfsoc_uart_drv, port);
+	return 0;
+}
+
+static struct of_device_id sirfsoc_uart_ids[] __devinitdata = {
+	{ .compatible = "sirf,prima2-uart", },
+	{}
+};
+MODULE_DEVICE_TABLE(of, sirfsoc_serial_of_match);
+
+static struct platform_driver sirfsoc_uart_driver = {
+	.probe		= sirfsoc_uart_probe,
+	.remove		= __devexit_p(sirfsoc_uart_remove),
+	.suspend	= sirfsoc_uart_suspend,
+	.resume		= sirfsoc_uart_resume,
+	.driver		= {
+		.name	= SIRFUART_PORT_NAME,
+		.owner	= THIS_MODULE,
+		.of_match_table = sirfsoc_uart_ids,
+	},
+};
+
+static int __init sirfsoc_uart_init(void)
+{
+	int ret = 0;
+
+	ret = uart_register_driver(&sirfsoc_uart_drv);
+	if (ret)
+		goto out;
+
+	ret = platform_driver_register(&sirfsoc_uart_driver);
+	if (ret)
+		uart_unregister_driver(&sirfsoc_uart_drv);
+out:
+	return ret;
+}
+module_init(sirfsoc_uart_init);
+
+static void __exit sirfsoc_uart_exit(void)
+{
+	platform_driver_unregister(&sirfsoc_uart_driver);
+	uart_unregister_driver(&sirfsoc_uart_drv);
+}
+module_exit(sirfsoc_uart_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Bin Shi <Bin.Shi@csr.com>, Rong Wang<Rong.Wang@csr.com>");
+MODULE_DESCRIPTION("CSR SiRFprimaII Uart Driver");
diff --git a/ap/os/linux/linux-3.4.x/drivers/tty/serial/sirfsoc_uart.h b/ap/os/linux/linux-3.4.x/drivers/tty/serial/sirfsoc_uart.h
new file mode 100644
index 0000000..6e207fd
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/tty/serial/sirfsoc_uart.h
@@ -0,0 +1,185 @@
+/*
+ * Drivers for CSR SiRFprimaII onboard UARTs.
+ *
+ * Copyright (c) 2011 Cambridge Silicon Radio Limited, a CSR plc group company.
+ *
+ * Licensed under GPLv2 or later.
+ */
+#include <linux/bitops.h>
+
+/* UART Register Offset Define */
+#define SIRFUART_LINE_CTRL			0x0040
+#define SIRFUART_TX_RX_EN			0x004c
+#define SIRFUART_DIVISOR			0x0050
+#define SIRFUART_INT_EN				0x0054
+#define SIRFUART_INT_STATUS			0x0058
+#define SIRFUART_TX_DMA_IO_CTRL			0x0100
+#define SIRFUART_TX_DMA_IO_LEN			0x0104
+#define SIRFUART_TX_FIFO_CTRL			0x0108
+#define SIRFUART_TX_FIFO_LEVEL_CHK		0x010C
+#define SIRFUART_TX_FIFO_OP			0x0110
+#define SIRFUART_TX_FIFO_STATUS			0x0114
+#define SIRFUART_TX_FIFO_DATA			0x0118
+#define SIRFUART_RX_DMA_IO_CTRL			0x0120
+#define SIRFUART_RX_DMA_IO_LEN			0x0124
+#define SIRFUART_RX_FIFO_CTRL			0x0128
+#define SIRFUART_RX_FIFO_LEVEL_CHK		0x012C
+#define SIRFUART_RX_FIFO_OP			0x0130
+#define SIRFUART_RX_FIFO_STATUS			0x0134
+#define SIRFUART_RX_FIFO_DATA			0x0138
+#define SIRFUART_AFC_CTRL			0x0140
+#define SIRFUART_SWH_DMA_IO			0x0148
+
+/* UART Line Control Register */
+#define SIRFUART_DATA_BIT_LEN_MASK		0x3
+#define SIRFUART_DATA_BIT_LEN_5			BIT(0)
+#define SIRFUART_DATA_BIT_LEN_6			1
+#define SIRFUART_DATA_BIT_LEN_7			2
+#define SIRFUART_DATA_BIT_LEN_8			3
+#define SIRFUART_STOP_BIT_LEN_1			0
+#define SIRFUART_STOP_BIT_LEN_2			BIT(2)
+#define SIRFUART_PARITY_EN			BIT(3)
+#define SIRFUART_EVEN_BIT			BIT(4)
+#define SIRFUART_STICK_BIT_MASK			(7 << 3)
+#define SIRFUART_STICK_BIT_NONE			(0 << 3)
+#define SIRFUART_STICK_BIT_EVEN			BIT(3)
+#define SIRFUART_STICK_BIT_ODD			(3 << 3)
+#define SIRFUART_STICK_BIT_MARK			(5 << 3)
+#define SIRFUART_STICK_BIT_SPACE		(7 << 3)
+#define SIRFUART_SET_BREAK			BIT(6)
+#define SIRFUART_LOOP_BACK			BIT(7)
+#define SIRFUART_PARITY_MASK			(7 << 3)
+#define SIRFUART_DUMMY_READ			BIT(16)
+
+#define SIRFSOC_UART_RX_TIMEOUT(br, to)	(((br) * (((to) + 999) / 1000)) / 1000)
+#define SIRFUART_RECV_TIMEOUT_MASK	(0xFFFF << 16)
+#define SIRFUART_RECV_TIMEOUT(x)	(((x) & 0xFFFF) << 16)
+
+/* UART Auto Flow Control */
+#define SIRFUART_AFC_RX_THD_MASK		0x000000FF
+#define SIRFUART_AFC_RX_EN			BIT(8)
+#define SIRFUART_AFC_TX_EN			BIT(9)
+#define SIRFUART_CTS_CTRL			BIT(10)
+#define SIRFUART_RTS_CTRL			BIT(11)
+#define SIRFUART_CTS_IN_STATUS			BIT(12)
+#define SIRFUART_RTS_OUT_STATUS			BIT(13)
+
+/* UART Interrupt Enable Register */
+#define SIRFUART_RX_DONE_INT			BIT(0)
+#define SIRFUART_TX_DONE_INT			BIT(1)
+#define SIRFUART_RX_OFLOW_INT			BIT(2)
+#define SIRFUART_TX_ALLOUT_INT			BIT(3)
+#define SIRFUART_RX_IO_DMA_INT			BIT(4)
+#define SIRFUART_TX_IO_DMA_INT			BIT(5)
+#define SIRFUART_RXFIFO_FULL_INT		BIT(6)
+#define SIRFUART_TXFIFO_EMPTY_INT		BIT(7)
+#define SIRFUART_RXFIFO_THD_INT			BIT(8)
+#define SIRFUART_TXFIFO_THD_INT			BIT(9)
+#define SIRFUART_FRM_ERR_INT			BIT(10)
+#define SIRFUART_RXD_BREAK_INT			BIT(11)
+#define SIRFUART_RX_TIMEOUT_INT			BIT(12)
+#define SIRFUART_PARITY_ERR_INT			BIT(13)
+#define SIRFUART_CTS_INT_EN			BIT(14)
+#define SIRFUART_RTS_INT_EN			BIT(15)
+
+/* UART Interrupt Status Register */
+#define SIRFUART_RX_DONE			BIT(0)
+#define SIRFUART_TX_DONE			BIT(1)
+#define SIRFUART_RX_OFLOW			BIT(2)
+#define SIRFUART_TX_ALL_EMPTY			BIT(3)
+#define SIRFUART_DMA_IO_RX_DONE			BIT(4)
+#define SIRFUART_DMA_IO_TX_DONE			BIT(5)
+#define SIRFUART_RXFIFO_FULL			BIT(6)
+#define SIRFUART_TXFIFO_EMPTY			BIT(7)
+#define SIRFUART_RXFIFO_THD_REACH		BIT(8)
+#define SIRFUART_TXFIFO_THD_REACH		BIT(9)
+#define SIRFUART_FRM_ERR			BIT(10)
+#define SIRFUART_RXD_BREAK			BIT(11)
+#define SIRFUART_RX_TIMEOUT			BIT(12)
+#define SIRFUART_PARITY_ERR			BIT(13)
+#define SIRFUART_CTS_CHANGE			BIT(14)
+#define SIRFUART_RTS_CHANGE			BIT(15)
+#define SIRFUART_PLUG_IN			BIT(16)
+
+#define SIRFUART_ERR_INT_STAT					\
+				(SIRFUART_RX_OFLOW |		\
+				SIRFUART_FRM_ERR |		\
+				SIRFUART_RXD_BREAK |		\
+				SIRFUART_PARITY_ERR)
+#define SIRFUART_ERR_INT_EN					\
+				(SIRFUART_RX_OFLOW_INT |	\
+				SIRFUART_FRM_ERR_INT |		\
+				SIRFUART_RXD_BREAK_INT |	\
+				SIRFUART_PARITY_ERR_INT)
+#define SIRFUART_TX_INT_EN	SIRFUART_TXFIFO_EMPTY_INT
+#define SIRFUART_RX_IO_INT_EN					\
+				(SIRFUART_RX_TIMEOUT_INT |	\
+				SIRFUART_RXFIFO_THD_INT |	\
+				SIRFUART_RXFIFO_FULL_INT |	\
+				SIRFUART_ERR_INT_EN)
+
+/* UART FIFO Register */
+#define SIRFUART_TX_FIFO_STOP			0x0
+#define SIRFUART_TX_FIFO_RESET			0x1
+#define SIRFUART_TX_FIFO_START			0x2
+#define SIRFUART_RX_FIFO_STOP			0x0
+#define SIRFUART_RX_FIFO_RESET			0x1
+#define SIRFUART_RX_FIFO_START			0x2
+#define SIRFUART_TX_MODE_DMA			0
+#define SIRFUART_TX_MODE_IO			1
+#define SIRFUART_RX_MODE_DMA			0
+#define SIRFUART_RX_MODE_IO			1
+
+#define SIRFUART_RX_EN				0x1
+#define SIRFUART_TX_EN				0x2
+
+/* Generic Definitions */
+#define SIRFSOC_UART_NAME			"ttySiRF"
+#define SIRFSOC_UART_MAJOR			0
+#define SIRFSOC_UART_MINOR			0
+#define SIRFUART_PORT_NAME			"sirfsoc-uart"
+#define SIRFUART_MAP_SIZE			0x200
+#define SIRFSOC_UART_NR				3
+#define SIRFSOC_PORT_TYPE			0xa5
+
+/* Baud Rate Calculation */
+#define SIRF_MIN_SAMPLE_DIV			0xf
+#define SIRF_MAX_SAMPLE_DIV			0x3f
+#define SIRF_IOCLK_DIV_MAX			0xffff
+#define SIRF_SAMPLE_DIV_SHIFT			16
+#define SIRF_IOCLK_DIV_MASK			0xffff
+#define SIRF_SAMPLE_DIV_MASK			0x3f0000
+#define SIRF_BAUD_RATE_SUPPORT_NR		18
+
+/* For Fast Baud Rate Calculation */
+struct sirfsoc_baudrate_to_regv {
+	unsigned int baud_rate;
+	unsigned int reg_val;
+};
+
+struct sirfsoc_uart_port {
+	unsigned char			hw_flow_ctrl;
+	unsigned char			ms_enabled;
+
+	struct uart_port		port;
+	struct pinctrl			*p;
+};
+
+/* Hardware Flow Control */
+#define SIRFUART_AFC_CTRL_RX_THD	0x70
+
+/* Register Access Control */
+#define portaddr(port, reg)		((port)->membase + (reg))
+#define rd_regb(port, reg)		(__raw_readb(portaddr(port, reg)))
+#define rd_regl(port, reg)		(__raw_readl(portaddr(port, reg)))
+#define wr_regb(port, reg, val)		__raw_writeb(val, portaddr(port, reg))
+#define wr_regl(port, reg, val)		__raw_writel(val, portaddr(port, reg))
+
+/* UART Port Mask */
+#define SIRFUART_FIFOLEVEL_MASK(port)	((port->line == 1) ? (0x1f) : (0x7f))
+#define SIRFUART_FIFOFULL_MASK(port)	((port->line == 1) ? (0x20) : (0x80))
+#define SIRFUART_FIFOEMPTY_MASK(port)	((port->line == 1) ? (0x40) : (0x100))
+
+/* I/O Mode */
+#define SIRFSOC_UART_IO_RX_MAX_CNT		256
+#define SIRFSOC_UART_IO_TX_REASONABLE_CNT	6
diff --git a/ap/os/linux/linux-3.4.x/drivers/tty/serial/sn_console.c b/ap/os/linux/linux-3.4.x/drivers/tty/serial/sn_console.c
new file mode 100644
index 0000000..1c6de9f
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/tty/serial/sn_console.c
@@ -0,0 +1,1087 @@
+/*
+ * C-Brick Serial Port (and console) driver for SGI Altix machines.
+ *
+ * This driver is NOT suitable for talking to the l1-controller for
+ * anything other than 'console activities' --- please use the l1
+ * driver for that.
+ *
+ *
+ * Copyright (c) 2004-2006 Silicon Graphics, Inc.  All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Further, this software is distributed without any warranty that it is
+ * free of the rightful claim of any third person regarding infringement
+ * or the like.  Any license provided herein, whether implied or
+ * otherwise, applies only to this software file.  Patent licenses, if
+ * any, provided herein do not apply to combinations of this program with
+ * other software, or any other product whatsoever.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program; if not, write the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston MA 02111-1307, USA.
+ *
+ * Contact information:  Silicon Graphics, Inc., 1500 Crittenden Lane,
+ * Mountain View, CA  94043, or:
+ *
+ * http://www.sgi.com
+ *
+ * For further information regarding this notice, see:
+ *
+ * http://oss.sgi.com/projects/GenInfo/NoticeExplan
+ */
+
+#include <linux/interrupt.h>
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
+#include <linux/serial.h>
+#include <linux/console.h>
+#include <linux/module.h>
+#include <linux/sysrq.h>
+#include <linux/circ_buf.h>
+#include <linux/serial_reg.h>
+#include <linux/delay.h> /* for mdelay */
+#include <linux/miscdevice.h>
+#include <linux/serial_core.h>
+
+#include <asm/io.h>
+#include <asm/sn/simulator.h>
+#include <asm/sn/sn_sal.h>
+
+/* number of characters we can transmit to the SAL console at a time */
+#define SN_SAL_MAX_CHARS 120
+
+/* 64K, when we're asynch, it must be at least printk's LOG_BUF_LEN to
+ * avoid losing chars, (always has to be a power of 2) */
+#define SN_SAL_BUFFER_SIZE (64 * (1 << 10))
+
+#define SN_SAL_UART_FIFO_DEPTH 16
+#define SN_SAL_UART_FIFO_SPEED_CPS (9600/10)
+
+/* sn_transmit_chars() calling args */
+#define TRANSMIT_BUFFERED	0
+#define TRANSMIT_RAW		1
+
+/* To use dynamic numbers only and not use the assigned major and minor,
+ * define the following.. */
+				  /* #define USE_DYNAMIC_MINOR 1 *//* use dynamic minor number */
+#define USE_DYNAMIC_MINOR 0	/* Don't rely on misc_register dynamic minor */
+
+/* Device name we're using */
+#define DEVICE_NAME "ttySG"
+#define DEVICE_NAME_DYNAMIC "ttySG0"	/* need full name for misc_register */
+/* The major/minor we are using, ignored for USE_DYNAMIC_MINOR */
+#define DEVICE_MAJOR 204
+#define DEVICE_MINOR 40
+
+#ifdef CONFIG_MAGIC_SYSRQ
+static char sysrq_serial_str[] = "\eSYS";
+static char *sysrq_serial_ptr = sysrq_serial_str;
+static unsigned long sysrq_requested;
+#endif /* CONFIG_MAGIC_SYSRQ */
+
+/*
+ * Port definition - this kinda drives it all
+ */
+struct sn_cons_port {
+	struct timer_list sc_timer;
+	struct uart_port sc_port;
+	struct sn_sal_ops {
+		int (*sal_puts_raw) (const char *s, int len);
+		int (*sal_puts) (const char *s, int len);
+		int (*sal_getc) (void);
+		int (*sal_input_pending) (void);
+		void (*sal_wakeup_transmit) (struct sn_cons_port *, int);
+	} *sc_ops;
+	unsigned long sc_interrupt_timeout;
+	int sc_is_asynch;
+};
+
+static struct sn_cons_port sal_console_port;
+static int sn_process_input;
+
+/* Only used if USE_DYNAMIC_MINOR is set to 1 */
+static struct miscdevice misc;	/* used with misc_register for dynamic */
+
+extern void early_sn_setup(void);
+
+#undef DEBUG
+#ifdef DEBUG
+static int sn_debug_printf(const char *fmt, ...);
+#define DPRINTF(x...) sn_debug_printf(x)
+#else
+#define DPRINTF(x...) do { } while (0)
+#endif
+
+/* Prototypes */
+static int snt_hw_puts_raw(const char *, int);
+static int snt_hw_puts_buffered(const char *, int);
+static int snt_poll_getc(void);
+static int snt_poll_input_pending(void);
+static int snt_intr_getc(void);
+static int snt_intr_input_pending(void);
+static void sn_transmit_chars(struct sn_cons_port *, int);
+
+/* A table for polling:
+ */
+static struct sn_sal_ops poll_ops = {
+	.sal_puts_raw = snt_hw_puts_raw,
+	.sal_puts = snt_hw_puts_raw,
+	.sal_getc = snt_poll_getc,
+	.sal_input_pending = snt_poll_input_pending
+};
+
+/* A table for interrupts enabled */
+static struct sn_sal_ops intr_ops = {
+	.sal_puts_raw = snt_hw_puts_raw,
+	.sal_puts = snt_hw_puts_buffered,
+	.sal_getc = snt_intr_getc,
+	.sal_input_pending = snt_intr_input_pending,
+	.sal_wakeup_transmit = sn_transmit_chars
+};
+
+/* the console does output in two distinctly different ways:
+ * synchronous (raw) and asynchronous (buffered).  initially, early_printk
+ * does synchronous output.  any data written goes directly to the SAL
+ * to be output (incidentally, it is internally buffered by the SAL)
+ * after interrupts and timers are initialized and available for use,
+ * the console init code switches to asynchronous output.  this is
+ * also the earliest opportunity to begin polling for console input.
+ * after console initialization, console output and tty (serial port)
+ * output is buffered and sent to the SAL asynchronously (either by
+ * timer callback or by UART interrupt) */
+
+/* routines for running the console in polling mode */
+
+/**
+ * snt_poll_getc - Get a character from the console in polling mode
+ *
+ */
+static int snt_poll_getc(void)
+{
+	int ch;
+
+	ia64_sn_console_getc(&ch);
+	return ch;
+}
+
+/**
+ * snt_poll_input_pending - Check if any input is waiting - polling mode.
+ *
+ */
+static int snt_poll_input_pending(void)
+{
+	int status, input;
+
+	status = ia64_sn_console_check(&input);
+	return !status && input;
+}
+
+/* routines for an interrupt driven console (normal) */
+
+/**
+ * snt_intr_getc - Get a character from the console, interrupt mode
+ *
+ */
+static int snt_intr_getc(void)
+{
+	return ia64_sn_console_readc();
+}
+
+/**
+ * snt_intr_input_pending - Check if input is pending, interrupt mode
+ *
+ */
+static int snt_intr_input_pending(void)
+{
+	return ia64_sn_console_intr_status() & SAL_CONSOLE_INTR_RECV;
+}
+
+/* these functions are polled and interrupt */
+
+/**
+ * snt_hw_puts_raw - Send raw string to the console, polled or interrupt mode
+ * @s: String
+ * @len: Length
+ *
+ */
+static int snt_hw_puts_raw(const char *s, int len)
+{
+	/* this will call the PROM and not return until this is done */
+	return ia64_sn_console_putb(s, len);
+}
+
+/**
+ * snt_hw_puts_buffered - Send string to console, polled or interrupt mode
+ * @s: String
+ * @len: Length
+ *
+ */
+static int snt_hw_puts_buffered(const char *s, int len)
+{
+	/* queue data to the PROM */
+	return ia64_sn_console_xmit_chars((char *)s, len);
+}
+
+/* uart interface structs
+ * These functions are associated with the uart_port that the serial core
+ * infrastructure calls.
+ *
+ * Note: Due to how the console works, many routines are no-ops.
+ */
+
+/**
+ * snp_type - What type of console are we?
+ * @port: Port to operate with (we ignore since we only have one port)
+ *
+ */
+static const char *snp_type(struct uart_port *port)
+{
+	return ("SGI SN L1");
+}
+
+/**
+ * snp_tx_empty - Is the transmitter empty?  We pretend we're always empty
+ * @port: Port to operate on (we ignore since we only have one port)
+ *
+ */
+static unsigned int snp_tx_empty(struct uart_port *port)
+{
+	return 1;
+}
+
+/**
+ * snp_stop_tx - stop the transmitter - no-op for us
+ * @port: Port to operat eon - we ignore - no-op function
+ *
+ */
+static void snp_stop_tx(struct uart_port *port)
+{
+}
+
+/**
+ * snp_release_port - Free i/o and resources for port - no-op for us
+ * @port: Port to operate on - we ignore - no-op function
+ *
+ */
+static void snp_release_port(struct uart_port *port)
+{
+}
+
+/**
+ * snp_enable_ms - Force modem status interrupts on - no-op for us
+ * @port: Port to operate on - we ignore - no-op function
+ *
+ */
+static void snp_enable_ms(struct uart_port *port)
+{
+}
+
+/**
+ * snp_shutdown - shut down the port - free irq and disable - no-op for us
+ * @port: Port to shut down - we ignore
+ *
+ */
+static void snp_shutdown(struct uart_port *port)
+{
+}
+
+/**
+ * snp_set_mctrl - set control lines (dtr, rts, etc) - no-op for our console
+ * @port: Port to operate on - we ignore
+ * @mctrl: Lines to set/unset - we ignore
+ *
+ */
+static void snp_set_mctrl(struct uart_port *port, unsigned int mctrl)
+{
+}
+
+/**
+ * snp_get_mctrl - get contorl line info, we just return a static value
+ * @port: port to operate on - we only have one port so we ignore this
+ *
+ */
+static unsigned int snp_get_mctrl(struct uart_port *port)
+{
+	return TIOCM_CAR | TIOCM_RNG | TIOCM_DSR | TIOCM_CTS;
+}
+
+/**
+ * snp_stop_rx - Stop the receiver - we ignor ethis
+ * @port: Port to operate on - we ignore
+ *
+ */
+static void snp_stop_rx(struct uart_port *port)
+{
+}
+
+/**
+ * snp_start_tx - Start transmitter
+ * @port: Port to operate on
+ *
+ */
+static void snp_start_tx(struct uart_port *port)
+{
+	if (sal_console_port.sc_ops->sal_wakeup_transmit)
+		sal_console_port.sc_ops->sal_wakeup_transmit(&sal_console_port,
+							     TRANSMIT_BUFFERED);
+
+}
+
+/**
+ * snp_break_ctl - handle breaks - ignored by us
+ * @port: Port to operate on
+ * @break_state: Break state
+ *
+ */
+static void snp_break_ctl(struct uart_port *port, int break_state)
+{
+}
+
+/**
+ * snp_startup - Start up the serial port - always return 0 (We're always on)
+ * @port: Port to operate on
+ *
+ */
+static int snp_startup(struct uart_port *port)
+{
+	return 0;
+}
+
+/**
+ * snp_set_termios - set termios stuff - we ignore these
+ * @port: port to operate on
+ * @termios: New settings
+ * @termios: Old
+ *
+ */
+static void
+snp_set_termios(struct uart_port *port, struct ktermios *termios,
+		struct ktermios *old)
+{
+}
+
+/**
+ * snp_request_port - allocate resources for port - ignored by us
+ * @port: port to operate on
+ *
+ */
+static int snp_request_port(struct uart_port *port)
+{
+	return 0;
+}
+
+/**
+ * snp_config_port - allocate resources, set up - we ignore,  we're always on
+ * @port: Port to operate on
+ * @flags: flags used for port setup
+ *
+ */
+static void snp_config_port(struct uart_port *port, int flags)
+{
+}
+
+/* Associate the uart functions above - given to serial core */
+
+static struct uart_ops sn_console_ops = {
+	.tx_empty = snp_tx_empty,
+	.set_mctrl = snp_set_mctrl,
+	.get_mctrl = snp_get_mctrl,
+	.stop_tx = snp_stop_tx,
+	.start_tx = snp_start_tx,
+	.stop_rx = snp_stop_rx,
+	.enable_ms = snp_enable_ms,
+	.break_ctl = snp_break_ctl,
+	.startup = snp_startup,
+	.shutdown = snp_shutdown,
+	.set_termios = snp_set_termios,
+	.pm = NULL,
+	.type = snp_type,
+	.release_port = snp_release_port,
+	.request_port = snp_request_port,
+	.config_port = snp_config_port,
+	.verify_port = NULL,
+};
+
+/* End of uart struct functions and defines */
+
+#ifdef DEBUG
+
+/**
+ * sn_debug_printf - close to hardware debugging printf
+ * @fmt: printf format
+ *
+ * This is as "close to the metal" as we can get, used when the driver
+ * itself may be broken.
+ *
+ */
+static int sn_debug_printf(const char *fmt, ...)
+{
+	static char printk_buf[1024];
+	int printed_len;
+	va_list args;
+
+	va_start(args, fmt);
+	printed_len = vsnprintf(printk_buf, sizeof(printk_buf), fmt, args);
+
+	if (!sal_console_port.sc_ops) {
+		sal_console_port.sc_ops = &poll_ops;
+		early_sn_setup();
+	}
+	sal_console_port.sc_ops->sal_puts_raw(printk_buf, printed_len);
+
+	va_end(args);
+	return printed_len;
+}
+#endif				/* DEBUG */
+
+/*
+ * Interrupt handling routines.
+ */
+
+/**
+ * sn_receive_chars - Grab characters, pass them to tty layer
+ * @port: Port to operate on
+ * @flags: irq flags
+ *
+ * Note: If we're not registered with the serial core infrastructure yet,
+ * we don't try to send characters to it...
+ *
+ */
+static void
+sn_receive_chars(struct sn_cons_port *port, unsigned long flags)
+{
+	int ch;
+	struct tty_struct *tty;
+
+	if (!port) {
+		printk(KERN_ERR "sn_receive_chars - port NULL so can't receive\n");
+		return;
+	}
+
+	if (!port->sc_ops) {
+		printk(KERN_ERR "sn_receive_chars - port->sc_ops  NULL so can't receive\n");
+		return;
+	}
+
+	if (port->sc_port.state) {
+		/* The serial_core stuffs are initialized, use them */
+		tty = port->sc_port.state->port.tty;
+	}
+	else {
+		/* Not registered yet - can't pass to tty layer.  */
+		tty = NULL;
+	}
+
+	while (port->sc_ops->sal_input_pending()) {
+		ch = port->sc_ops->sal_getc();
+		if (ch < 0) {
+			printk(KERN_ERR "sn_console: An error occurred while "
+			       "obtaining data from the console (0x%0x)\n", ch);
+			break;
+		}
+#ifdef CONFIG_MAGIC_SYSRQ
+                if (sysrq_requested) {
+                        unsigned long sysrq_timeout = sysrq_requested + HZ*5;
+
+                        sysrq_requested = 0;
+                        if (ch && time_before(jiffies, sysrq_timeout)) {
+                                spin_unlock_irqrestore(&port->sc_port.lock, flags);
+                                handle_sysrq(ch);
+                                spin_lock_irqsave(&port->sc_port.lock, flags);
+                                /* ignore actual sysrq command char */
+                                continue;
+                        }
+                }
+                if (ch == *sysrq_serial_ptr) {
+                        if (!(*++sysrq_serial_ptr)) {
+                                sysrq_requested = jiffies;
+                                sysrq_serial_ptr = sysrq_serial_str;
+                        }
+			/*
+			 * ignore the whole sysrq string except for the
+			 * leading escape
+			 */
+			if (ch != '\e')
+				continue;
+                }
+                else
+			sysrq_serial_ptr = sysrq_serial_str;
+#endif /* CONFIG_MAGIC_SYSRQ */
+
+		/* record the character to pass up to the tty layer */
+		if (tty) {
+			if(tty_insert_flip_char(tty, ch, TTY_NORMAL) == 0)
+				break;
+		}
+		port->sc_port.icount.rx++;
+	}
+
+	if (tty)
+		tty_flip_buffer_push(tty);
+}
+
+/**
+ * sn_transmit_chars - grab characters from serial core, send off
+ * @port: Port to operate on
+ * @raw: Transmit raw or buffered
+ *
+ * Note: If we're early, before we're registered with serial core, the
+ * writes are going through sn_sal_console_write because that's how
+ * register_console has been set up.  We currently could have asynch
+ * polls calling this function due to sn_sal_switch_to_asynch but we can
+ * ignore them until we register with the serial core stuffs.
+ *
+ */
+static void sn_transmit_chars(struct sn_cons_port *port, int raw)
+{
+	int xmit_count, tail, head, loops, ii;
+	int result;
+	char *start;
+	struct circ_buf *xmit;
+
+	if (!port)
+		return;
+
+	BUG_ON(!port->sc_is_asynch);
+
+	if (port->sc_port.state) {
+		/* We're initialized, using serial core infrastructure */
+		xmit = &port->sc_port.state->xmit;
+	} else {
+		/* Probably sn_sal_switch_to_asynch has been run but serial core isn't
+		 * initialized yet.  Just return.  Writes are going through
+		 * sn_sal_console_write (due to register_console) at this time.
+		 */
+		return;
+	}
+
+	if (uart_circ_empty(xmit) || uart_tx_stopped(&port->sc_port)) {
+		/* Nothing to do. */
+		ia64_sn_console_intr_disable(SAL_CONSOLE_INTR_XMIT);
+		return;
+	}
+
+	head = xmit->head;
+	tail = xmit->tail;
+	start = &xmit->buf[tail];
+
+	/* twice around gets the tail to the end of the buffer and
+	 * then to the head, if needed */
+	loops = (head < tail) ? 2 : 1;
+
+	for (ii = 0; ii < loops; ii++) {
+		xmit_count = (head < tail) ?
+		    (UART_XMIT_SIZE - tail) : (head - tail);
+
+		if (xmit_count > 0) {
+			if (raw == TRANSMIT_RAW)
+				result =
+				    port->sc_ops->sal_puts_raw(start,
+							       xmit_count);
+			else
+				result =
+				    port->sc_ops->sal_puts(start, xmit_count);
+#ifdef DEBUG
+			if (!result)
+				DPRINTF("`");
+#endif
+			if (result > 0) {
+				xmit_count -= result;
+				port->sc_port.icount.tx += result;
+				tail += result;
+				tail &= UART_XMIT_SIZE - 1;
+				xmit->tail = tail;
+				start = &xmit->buf[tail];
+			}
+		}
+	}
+
+	if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
+		uart_write_wakeup(&port->sc_port);
+
+	if (uart_circ_empty(xmit))
+		snp_stop_tx(&port->sc_port);	/* no-op for us */
+}
+
+/**
+ * sn_sal_interrupt - Handle console interrupts
+ * @irq: irq #, useful for debug statements
+ * @dev_id: our pointer to our port (sn_cons_port which contains the uart port)
+ *
+ */
+static irqreturn_t sn_sal_interrupt(int irq, void *dev_id)
+{
+	struct sn_cons_port *port = (struct sn_cons_port *)dev_id;
+	unsigned long flags;
+	int status = ia64_sn_console_intr_status();
+
+	if (!port)
+		return IRQ_NONE;
+
+	spin_lock_irqsave(&port->sc_port.lock, flags);
+	if (status & SAL_CONSOLE_INTR_RECV) {
+		sn_receive_chars(port, flags);
+	}
+	if (status & SAL_CONSOLE_INTR_XMIT) {
+		sn_transmit_chars(port, TRANSMIT_BUFFERED);
+	}
+	spin_unlock_irqrestore(&port->sc_port.lock, flags);
+	return IRQ_HANDLED;
+}
+
+/**
+ * sn_sal_timer_poll - this function handles polled console mode
+ * @data: A pointer to our sn_cons_port (which contains the uart port)
+ *
+ * data is the pointer that init_timer will store for us.  This function is
+ * associated with init_timer to see if there is any console traffic.
+ * Obviously not used in interrupt mode
+ *
+ */
+static void sn_sal_timer_poll(unsigned long data)
+{
+	struct sn_cons_port *port = (struct sn_cons_port *)data;
+	unsigned long flags;
+
+	if (!port)
+		return;
+
+	if (!port->sc_port.irq) {
+		spin_lock_irqsave(&port->sc_port.lock, flags);
+		if (sn_process_input)
+			sn_receive_chars(port, flags);
+		sn_transmit_chars(port, TRANSMIT_RAW);
+		spin_unlock_irqrestore(&port->sc_port.lock, flags);
+		mod_timer(&port->sc_timer,
+			  jiffies + port->sc_interrupt_timeout);
+	}
+}
+
+/*
+ * Boot-time initialization code
+ */
+
+/**
+ * sn_sal_switch_to_asynch - Switch to async mode (as opposed to synch)
+ * @port: Our sn_cons_port (which contains the uart port)
+ *
+ * So this is used by sn_sal_serial_console_init (early on, before we're
+ * registered with serial core).  It's also used by sn_sal_module_init
+ * right after we've registered with serial core.  The later only happens
+ * if we didn't already come through here via sn_sal_serial_console_init.
+ *
+ */
+static void __init sn_sal_switch_to_asynch(struct sn_cons_port *port)
+{
+	unsigned long flags;
+
+	if (!port)
+		return;
+
+	DPRINTF("sn_console: about to switch to asynchronous console\n");
+
+	/* without early_printk, we may be invoked late enough to race
+	 * with other cpus doing console IO at this point, however
+	 * console interrupts will never be enabled */
+	spin_lock_irqsave(&port->sc_port.lock, flags);
+
+	/* early_printk invocation may have done this for us */
+	if (!port->sc_ops)
+		port->sc_ops = &poll_ops;
+
+	/* we can't turn on the console interrupt (as request_irq
+	 * calls kmalloc, which isn't set up yet), so we rely on a
+	 * timer to poll for input and push data from the console
+	 * buffer.
+	 */
+	init_timer(&port->sc_timer);
+	port->sc_timer.function = sn_sal_timer_poll;
+	port->sc_timer.data = (unsigned long)port;
+
+	if (IS_RUNNING_ON_SIMULATOR())
+		port->sc_interrupt_timeout = 6;
+	else {
+		/* 960cps / 16 char FIFO = 60HZ
+		 * HZ / (SN_SAL_FIFO_SPEED_CPS / SN_SAL_FIFO_DEPTH) */
+		port->sc_interrupt_timeout =
+		    HZ * SN_SAL_UART_FIFO_DEPTH / SN_SAL_UART_FIFO_SPEED_CPS;
+	}
+	mod_timer(&port->sc_timer, jiffies + port->sc_interrupt_timeout);
+
+	port->sc_is_asynch = 1;
+	spin_unlock_irqrestore(&port->sc_port.lock, flags);
+}
+
+/**
+ * sn_sal_switch_to_interrupts - Switch to interrupt driven mode
+ * @port: Our sn_cons_port (which contains the uart port)
+ *
+ * In sn_sal_module_init, after we're registered with serial core and
+ * the port is added, this function is called to switch us to interrupt
+ * mode.  We were previously in asynch/polling mode (using init_timer).
+ *
+ * We attempt to switch to interrupt mode here by calling
+ * request_irq.  If that works out, we enable receive interrupts.
+ */
+static void __init sn_sal_switch_to_interrupts(struct sn_cons_port *port)
+{
+	unsigned long flags;
+
+	if (port) {
+		DPRINTF("sn_console: switching to interrupt driven console\n");
+
+		if (request_irq(SGI_UART_VECTOR, sn_sal_interrupt,
+				IRQF_SHARED,
+				"SAL console driver", port) >= 0) {
+			spin_lock_irqsave(&port->sc_port.lock, flags);
+			port->sc_port.irq = SGI_UART_VECTOR;
+			port->sc_ops = &intr_ops;
+			irq_set_handler(port->sc_port.irq, handle_level_irq);
+
+			/* turn on receive interrupts */
+			ia64_sn_console_intr_enable(SAL_CONSOLE_INTR_RECV);
+			spin_unlock_irqrestore(&port->sc_port.lock, flags);
+		}
+		else {
+			printk(KERN_INFO
+			    "sn_console: console proceeding in polled mode\n");
+		}
+	}
+}
+
+/*
+ * Kernel console definitions
+ */
+
+static void sn_sal_console_write(struct console *, const char *, unsigned);
+static int sn_sal_console_setup(struct console *, char *);
+static struct uart_driver sal_console_uart;
+extern struct tty_driver *uart_console_device(struct console *, int *);
+
+static struct console sal_console = {
+	.name = DEVICE_NAME,
+	.write = sn_sal_console_write,
+	.device = uart_console_device,
+	.setup = sn_sal_console_setup,
+	.index = -1,		/* unspecified */
+	.data = &sal_console_uart,
+};
+
+#define SAL_CONSOLE	&sal_console
+
+static struct uart_driver sal_console_uart = {
+	.owner = THIS_MODULE,
+	.driver_name = "sn_console",
+	.dev_name = DEVICE_NAME,
+	.major = 0,		/* major/minor set at registration time per USE_DYNAMIC_MINOR */
+	.minor = 0,
+	.nr = 1,		/* one port */
+	.cons = SAL_CONSOLE,
+};
+
+/**
+ * sn_sal_module_init - When the kernel loads us, get us rolling w/ serial core
+ *
+ * Before this is called, we've been printing kernel messages in a special
+ * early mode not making use of the serial core infrastructure.  When our
+ * driver is loaded for real, we register the driver and port with serial
+ * core and try to enable interrupt driven mode.
+ *
+ */
+static int __init sn_sal_module_init(void)
+{
+	int retval;
+
+	if (!ia64_platform_is("sn2"))
+		return 0;
+
+	printk(KERN_INFO "sn_console: Console driver init\n");
+
+	if (USE_DYNAMIC_MINOR == 1) {
+		misc.minor = MISC_DYNAMIC_MINOR;
+		misc.name = DEVICE_NAME_DYNAMIC;
+		retval = misc_register(&misc);
+		if (retval != 0) {
+			printk(KERN_WARNING "Failed to register console "
+			       "device using misc_register.\n");
+			return -ENODEV;
+		}
+		sal_console_uart.major = MISC_MAJOR;
+		sal_console_uart.minor = misc.minor;
+	} else {
+		sal_console_uart.major = DEVICE_MAJOR;
+		sal_console_uart.minor = DEVICE_MINOR;
+	}
+
+	/* We register the driver and the port before switching to interrupts
+	 * or async above so the proper uart structures are populated */
+
+	if (uart_register_driver(&sal_console_uart) < 0) {
+		printk
+		    ("ERROR sn_sal_module_init failed uart_register_driver, line %d\n",
+		     __LINE__);
+		return -ENODEV;
+	}
+
+	spin_lock_init(&sal_console_port.sc_port.lock);
+
+	/* Setup the port struct with the minimum needed */
+	sal_console_port.sc_port.membase = (char *)1;	/* just needs to be non-zero */
+	sal_console_port.sc_port.type = PORT_16550A;
+	sal_console_port.sc_port.fifosize = SN_SAL_MAX_CHARS;
+	sal_console_port.sc_port.ops = &sn_console_ops;
+	sal_console_port.sc_port.line = 0;
+
+	if (uart_add_one_port(&sal_console_uart, &sal_console_port.sc_port) < 0) {
+		/* error - not sure what I'd do - so I'll do nothing */
+		printk(KERN_ERR "%s: unable to add port\n", __func__);
+	}
+
+	/* when this driver is compiled in, the console initialization
+	 * will have already switched us into asynchronous operation
+	 * before we get here through the module initcalls */
+	if (!sal_console_port.sc_is_asynch) {
+		sn_sal_switch_to_asynch(&sal_console_port);
+	}
+
+	/* at this point (module_init) we can try to turn on interrupts */
+	if (!IS_RUNNING_ON_SIMULATOR()) {
+		sn_sal_switch_to_interrupts(&sal_console_port);
+	}
+	sn_process_input = 1;
+	return 0;
+}
+
+/**
+ * sn_sal_module_exit - When we're unloaded, remove the driver/port
+ *
+ */
+static void __exit sn_sal_module_exit(void)
+{
+	del_timer_sync(&sal_console_port.sc_timer);
+	uart_remove_one_port(&sal_console_uart, &sal_console_port.sc_port);
+	uart_unregister_driver(&sal_console_uart);
+	misc_deregister(&misc);
+}
+
+module_init(sn_sal_module_init);
+module_exit(sn_sal_module_exit);
+
+/**
+ * puts_raw_fixed - sn_sal_console_write helper for adding \r's as required
+ * @puts_raw : puts function to do the writing
+ * @s: input string
+ * @count: length
+ *
+ * We need a \r ahead of every \n for direct writes through
+ * ia64_sn_console_putb (what sal_puts_raw below actually does).
+ *
+ */
+
+static void puts_raw_fixed(int (*puts_raw) (const char *s, int len),
+			   const char *s, int count)
+{
+	const char *s1;
+
+	/* Output '\r' before each '\n' */
+	while ((s1 = memchr(s, '\n', count)) != NULL) {
+		puts_raw(s, s1 - s);
+		puts_raw("\r\n", 2);
+		count -= s1 + 1 - s;
+		s = s1 + 1;
+	}
+	puts_raw(s, count);
+}
+
+/**
+ * sn_sal_console_write - Print statements before serial core available
+ * @console: Console to operate on - we ignore since we have just one
+ * @s: String to send
+ * @count: length
+ *
+ * This is referenced in the console struct.  It is used for early
+ * console printing before we register with serial core and for things
+ * such as kdb.  The console_lock must be held when we get here.
+ *
+ * This function has some code for trying to print output even if the lock
+ * is held.  We try to cover the case where a lock holder could have died.
+ * We don't use this special case code if we're not registered with serial
+ * core yet.  After we're registered with serial core, the only time this
+ * function would be used is for high level kernel output like magic sys req,
+ * kdb, and printk's.
+ */
+static void
+sn_sal_console_write(struct console *co, const char *s, unsigned count)
+{
+	unsigned long flags = 0;
+	struct sn_cons_port *port = &sal_console_port;
+	static int stole_lock = 0;
+
+	BUG_ON(!port->sc_is_asynch);
+
+	/* We can't look at the xmit buffer if we're not registered with serial core
+	 *  yet.  So only do the fancy recovery after registering
+	 */
+	if (!port->sc_port.state) {
+		/* Not yet registered with serial core - simple case */
+		puts_raw_fixed(port->sc_ops->sal_puts_raw, s, count);
+		return;
+	}
+
+	/* somebody really wants this output, might be an
+	 * oops, kdb, panic, etc.  make sure they get it. */
+	if (spin_is_locked(&port->sc_port.lock)) {
+		int lhead = port->sc_port.state->xmit.head;
+		int ltail = port->sc_port.state->xmit.tail;
+		int counter, got_lock = 0;
+
+		/*
+		 * We attempt to determine if someone has died with the
+		 * lock. We wait ~20 secs after the head and tail ptrs
+		 * stop moving and assume the lock holder is not functional
+		 * and plow ahead. If the lock is freed within the time out
+		 * period we re-get the lock and go ahead normally. We also
+		 * remember if we have plowed ahead so that we don't have
+		 * to wait out the time out period again - the asumption
+		 * is that we will time out again.
+		 */
+
+		for (counter = 0; counter < 150; mdelay(125), counter++) {
+			if (!spin_is_locked(&port->sc_port.lock)
+			    || stole_lock) {
+				if (!stole_lock) {
+					spin_lock_irqsave(&port->sc_port.lock,
+							  flags);
+					got_lock = 1;
+				}
+				break;
+			} else {
+				/* still locked */
+				if ((lhead != port->sc_port.state->xmit.head)
+				    || (ltail !=
+					port->sc_port.state->xmit.tail)) {
+					lhead =
+						port->sc_port.state->xmit.head;
+					ltail =
+						port->sc_port.state->xmit.tail;
+					counter = 0;
+				}
+			}
+		}
+		/* flush anything in the serial core xmit buffer, raw */
+		sn_transmit_chars(port, 1);
+		if (got_lock) {
+			spin_unlock_irqrestore(&port->sc_port.lock, flags);
+			stole_lock = 0;
+		} else {
+			/* fell thru */
+			stole_lock = 1;
+		}
+		puts_raw_fixed(port->sc_ops->sal_puts_raw, s, count);
+	} else {
+		stole_lock = 0;
+		spin_lock_irqsave(&port->sc_port.lock, flags);
+		sn_transmit_chars(port, 1);
+		spin_unlock_irqrestore(&port->sc_port.lock, flags);
+
+		puts_raw_fixed(port->sc_ops->sal_puts_raw, s, count);
+	}
+}
+
+
+/**
+ * sn_sal_console_setup - Set up console for early printing
+ * @co: Console to work with
+ * @options: Options to set
+ *
+ * Altix console doesn't do anything with baud rates, etc, anyway.
+ *
+ * This isn't required since not providing the setup function in the
+ * console struct is ok.  However, other patches like KDB plop something
+ * here so providing it is easier.
+ *
+ */
+static int sn_sal_console_setup(struct console *co, char *options)
+{
+	return 0;
+}
+
+/**
+ * sn_sal_console_write_early - simple early output routine
+ * @co - console struct
+ * @s - string to print
+ * @count - count
+ *
+ * Simple function to provide early output, before even
+ * sn_sal_serial_console_init is called.  Referenced in the
+ * console struct registerd in sn_serial_console_early_setup.
+ *
+ */
+static void __init
+sn_sal_console_write_early(struct console *co, const char *s, unsigned count)
+{
+	puts_raw_fixed(sal_console_port.sc_ops->sal_puts_raw, s, count);
+}
+
+/* Used for very early console printing - again, before
+ * sn_sal_serial_console_init is run */
+static struct console sal_console_early __initdata = {
+	.name = "sn_sal",
+	.write = sn_sal_console_write_early,
+	.flags = CON_PRINTBUFFER,
+	.index = -1,
+};
+
+/**
+ * sn_serial_console_early_setup - Sets up early console output support
+ *
+ * Register a console early on...  This is for output before even
+ * sn_sal_serial_cosnole_init is called.  This function is called from
+ * setup.c.  This allows us to do really early polled writes. When
+ * sn_sal_serial_console_init is called, this console is unregistered
+ * and a new one registered.
+ */
+int __init sn_serial_console_early_setup(void)
+{
+	if (!ia64_platform_is("sn2"))
+		return -1;
+
+	sal_console_port.sc_ops = &poll_ops;
+	spin_lock_init(&sal_console_port.sc_port.lock);
+	early_sn_setup();	/* Find SAL entry points */
+	register_console(&sal_console_early);
+
+	return 0;
+}
+
+/**
+ * sn_sal_serial_console_init - Early console output - set up for register
+ *
+ * This function is called when regular console init happens.  Because we
+ * support even earlier console output with sn_serial_console_early_setup
+ * (called from setup.c directly), this function unregisters the really
+ * early console.
+ *
+ * Note: Even if setup.c doesn't register sal_console_early, unregistering
+ * it here doesn't hurt anything.
+ *
+ */
+static int __init sn_sal_serial_console_init(void)
+{
+	if (ia64_platform_is("sn2")) {
+		sn_sal_switch_to_asynch(&sal_console_port);
+		DPRINTF("sn_sal_serial_console_init : register console\n");
+		register_console(&sal_console);
+		unregister_console(&sal_console_early);
+	}
+	return 0;
+}
+
+console_initcall(sn_sal_serial_console_init);
diff --git a/ap/os/linux/linux-3.4.x/drivers/tty/serial/suncore.c b/ap/os/linux/linux-3.4.x/drivers/tty/serial/suncore.c
new file mode 100644
index 0000000..6e4ac8d
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/tty/serial/suncore.c
@@ -0,0 +1,247 @@
+/* suncore.c
+ *
+ * Common SUN serial routines.  Based entirely
+ * upon drivers/sbus/char/sunserial.c which is:
+ *
+ * Copyright (C) 1997  Eddie C. Dost  (ecd@skynet.be)
+ *
+ * Adaptation to new UART layer is:
+ *
+ * Copyright (C) 2002 David S. Miller (davem@redhat.com)
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/console.h>
+#include <linux/tty.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/serial_core.h>
+#include <linux/sunserialcore.h>
+#include <linux/init.h>
+
+#include <asm/prom.h>
+
+
+static int sunserial_current_minor = 64;
+
+int sunserial_register_minors(struct uart_driver *drv, int count)
+{
+	int err = 0;
+
+	drv->minor = sunserial_current_minor;
+	drv->nr += count;
+	/* Register the driver on the first call */
+	if (drv->nr == count)
+		err = uart_register_driver(drv);
+	if (err == 0) {
+		sunserial_current_minor += count;
+		drv->tty_driver->name_base = drv->minor - 64;
+	}
+	return err;
+}
+EXPORT_SYMBOL(sunserial_register_minors);
+
+void sunserial_unregister_minors(struct uart_driver *drv, int count)
+{
+	drv->nr -= count;
+	sunserial_current_minor -= count;
+
+	if (drv->nr == 0)
+		uart_unregister_driver(drv);
+}
+EXPORT_SYMBOL(sunserial_unregister_minors);
+
+int sunserial_console_match(struct console *con, struct device_node *dp,
+			    struct uart_driver *drv, int line, bool ignore_line)
+{
+	if (!con)
+		return 0;
+
+	drv->cons = con;
+
+	if (of_console_device != dp)
+		return 0;
+
+	if (!ignore_line) {
+		int off = 0;
+
+		if (of_console_options &&
+		    *of_console_options == 'b')
+			off = 1;
+
+		if ((line & 1) != off)
+			return 0;
+	}
+
+	if (!console_set_on_cmdline) {
+		con->index = line;
+		add_preferred_console(con->name, line, NULL);
+	}
+	return 1;
+}
+EXPORT_SYMBOL(sunserial_console_match);
+
+void sunserial_console_termios(struct console *con, struct device_node *uart_dp)
+{
+	const char *mode, *s;
+	char mode_prop[] = "ttyX-mode";
+	int baud, bits, stop, cflag;
+	char parity;
+
+	if (!strcmp(uart_dp->name, "rsc") ||
+	    !strcmp(uart_dp->name, "rsc-console") ||
+	    !strcmp(uart_dp->name, "rsc-control")) {
+		mode = of_get_property(uart_dp,
+				       "ssp-console-modes", NULL);
+		if (!mode)
+			mode = "115200,8,n,1,-";
+	} else if (!strcmp(uart_dp->name, "lom-console")) {
+		mode = "9600,8,n,1,-";
+	} else {
+		struct device_node *dp;
+		char c;
+
+		c = 'a';
+		if (of_console_options)
+			c = *of_console_options;
+
+		mode_prop[3] = c;
+
+		dp = of_find_node_by_path("/options");
+		mode = of_get_property(dp, mode_prop, NULL);
+		if (!mode)
+			mode = "9600,8,n,1,-";
+	}
+
+	cflag = CREAD | HUPCL | CLOCAL;
+
+	s = mode;
+	baud = simple_strtoul(s, NULL, 0);
+	s = strchr(s, ',');
+	bits = simple_strtoul(++s, NULL, 0);
+	s = strchr(s, ',');
+	parity = *(++s);
+	s = strchr(s, ',');
+	stop = simple_strtoul(++s, NULL, 0);
+	s = strchr(s, ',');
+	/* XXX handshake is not handled here. */
+
+	switch (baud) {
+		case 150: cflag |= B150; break;
+		case 300: cflag |= B300; break;
+		case 600: cflag |= B600; break;
+		case 1200: cflag |= B1200; break;
+		case 2400: cflag |= B2400; break;
+		case 4800: cflag |= B4800; break;
+		case 9600: cflag |= B9600; break;
+		case 19200: cflag |= B19200; break;
+		case 38400: cflag |= B38400; break;
+		case 57600: cflag |= B57600; break;
+		case 115200: cflag |= B115200; break;
+		case 230400: cflag |= B230400; break;
+		case 460800: cflag |= B460800; break;
+		default: baud = 9600; cflag |= B9600; break;
+	}
+
+	switch (bits) {
+		case 5: cflag |= CS5; break;
+		case 6: cflag |= CS6; break;
+		case 7: cflag |= CS7; break;
+		case 8: cflag |= CS8; break;
+		default: cflag |= CS8; break;
+	}
+
+	switch (parity) {
+		case 'o': cflag |= (PARENB | PARODD); break;
+		case 'e': cflag |= PARENB; break;
+		case 'n': default: break;
+	}
+
+	switch (stop) {
+		case 2: cflag |= CSTOPB; break;
+		case 1: default: break;
+	}
+
+	con->cflag = cflag;
+}
+
+/* Sun serial MOUSE auto baud rate detection.  */
+static struct mouse_baud_cflag {
+	int baud;
+	unsigned int cflag;
+} mouse_baud_table[] = {
+	{ 1200, B1200 },
+	{ 2400, B2400 },
+	{ 4800, B4800 },
+	{ 9600, B9600 },
+	{ -1, ~0 },
+	{ -1, ~0 },
+};
+
+unsigned int suncore_mouse_baud_cflag_next(unsigned int cflag, int *new_baud)
+{
+	int i;
+
+	for (i = 0; mouse_baud_table[i].baud != -1; i++)
+		if (mouse_baud_table[i].cflag == (cflag & CBAUD))
+			break;
+
+	i += 1;
+	if (mouse_baud_table[i].baud == -1)
+		i = 0;
+
+	*new_baud = mouse_baud_table[i].baud;
+	return mouse_baud_table[i].cflag;
+}
+
+EXPORT_SYMBOL(suncore_mouse_baud_cflag_next);
+
+/* Basically, when the baud rate is wrong the mouse spits out
+ * breaks to us.
+ */
+int suncore_mouse_baud_detection(unsigned char ch, int is_break)
+{
+	static int mouse_got_break = 0;
+	static int ctr = 0;
+
+	if (is_break) {
+		/* Let a few normal bytes go by before we jump the gun
+		 * and say we need to try another baud rate.
+		 */
+		if (mouse_got_break && ctr < 8)
+			return 1;
+
+		/* Ok, we need to try another baud. */
+		ctr = 0;
+		mouse_got_break = 1;
+		return 2;
+	}
+	if (mouse_got_break) {
+		ctr++;
+		if (ch == 0x87) {
+			/* Correct baud rate determined. */
+			mouse_got_break = 0;
+		}
+		return 1;
+	}
+	return 0;
+}
+
+EXPORT_SYMBOL(suncore_mouse_baud_detection);
+
+static int __init suncore_init(void)
+{
+	return 0;
+}
+
+static void __exit suncore_exit(void)
+{
+}
+
+module_init(suncore_init);
+module_exit(suncore_exit);
+
+MODULE_AUTHOR("Eddie C. Dost, David S. Miller");
+MODULE_DESCRIPTION("Sun serial common layer");
+MODULE_LICENSE("GPL");
diff --git a/ap/os/linux/linux-3.4.x/drivers/tty/serial/sunhv.c b/ap/os/linux/linux-3.4.x/drivers/tty/serial/sunhv.c
new file mode 100644
index 0000000..505961c
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/tty/serial/sunhv.c
@@ -0,0 +1,661 @@
+/* sunhv.c: Serial driver for SUN4V hypervisor console.
+ *
+ * Copyright (C) 2006, 2007 David S. Miller (davem@davemloft.net)
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
+#include <linux/major.h>
+#include <linux/circ_buf.h>
+#include <linux/serial.h>
+#include <linux/sysrq.h>
+#include <linux/console.h>
+#include <linux/spinlock.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/of_device.h>
+
+#include <asm/hypervisor.h>
+#include <asm/spitfire.h>
+#include <asm/prom.h>
+#include <asm/irq.h>
+#include <asm/setup.h>
+
+#if defined(CONFIG_MAGIC_SYSRQ)
+#define SUPPORT_SYSRQ
+#endif
+
+#include <linux/serial_core.h>
+#include <linux/sunserialcore.h>
+
+#define CON_BREAK	((long)-1)
+#define CON_HUP		((long)-2)
+
+#define IGNORE_BREAK	0x1
+#define IGNORE_ALL	0x2
+
+static char *con_write_page;
+static char *con_read_page;
+
+static int hung_up = 0;
+
+static void transmit_chars_putchar(struct uart_port *port, struct circ_buf *xmit)
+{
+	while (!uart_circ_empty(xmit)) {
+		long status = sun4v_con_putchar(xmit->buf[xmit->tail]);
+
+		if (status != HV_EOK)
+			break;
+
+		xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
+		port->icount.tx++;
+	}
+}
+
+static void transmit_chars_write(struct uart_port *port, struct circ_buf *xmit)
+{
+	while (!uart_circ_empty(xmit)) {
+		unsigned long ra = __pa(xmit->buf + xmit->tail);
+		unsigned long len, status, sent;
+
+		len = CIRC_CNT_TO_END(xmit->head, xmit->tail,
+				      UART_XMIT_SIZE);
+		status = sun4v_con_write(ra, len, &sent);
+		if (status != HV_EOK)
+			break;
+		xmit->tail = (xmit->tail + sent) & (UART_XMIT_SIZE - 1);
+		port->icount.tx += sent;
+	}
+}
+
+static int receive_chars_getchar(struct uart_port *port, struct tty_struct *tty)
+{
+	int saw_console_brk = 0;
+	int limit = 10000;
+
+	while (limit-- > 0) {
+		long status;
+		long c = sun4v_con_getchar(&status);
+
+		if (status == HV_EWOULDBLOCK)
+			break;
+
+		if (c == CON_BREAK) {
+			if (uart_handle_break(port))
+				continue;
+			saw_console_brk = 1;
+			c = 0;
+		}
+
+		if (c == CON_HUP) {
+			hung_up = 1;
+			uart_handle_dcd_change(port, 0);
+		} else if (hung_up) {
+			hung_up = 0;
+			uart_handle_dcd_change(port, 1);
+		}
+
+		if (tty == NULL) {
+			uart_handle_sysrq_char(port, c);
+			continue;
+		}
+
+		port->icount.rx++;
+
+		if (uart_handle_sysrq_char(port, c))
+			continue;
+
+		tty_insert_flip_char(tty, c, TTY_NORMAL);
+	}
+
+	return saw_console_brk;
+}
+
+static int receive_chars_read(struct uart_port *port, struct tty_struct *tty)
+{
+	int saw_console_brk = 0;
+	int limit = 10000;
+
+	while (limit-- > 0) {
+		unsigned long ra = __pa(con_read_page);
+		unsigned long bytes_read, i;
+		long stat = sun4v_con_read(ra, PAGE_SIZE, &bytes_read);
+
+		if (stat != HV_EOK) {
+			bytes_read = 0;
+
+			if (stat == CON_BREAK) {
+				if (uart_handle_break(port))
+					continue;
+				saw_console_brk = 1;
+				*con_read_page = 0;
+				bytes_read = 1;
+			} else if (stat == CON_HUP) {
+				hung_up = 1;
+				uart_handle_dcd_change(port, 0);
+				continue;
+			} else {
+				/* HV_EWOULDBLOCK, etc.  */
+				break;
+			}
+		}
+
+		if (hung_up) {
+			hung_up = 0;
+			uart_handle_dcd_change(port, 1);
+		}
+
+		for (i = 0; i < bytes_read; i++)
+			uart_handle_sysrq_char(port, con_read_page[i]);
+
+		if (tty == NULL)
+			continue;
+
+		port->icount.rx += bytes_read;
+
+		tty_insert_flip_string(tty, con_read_page, bytes_read);
+	}
+
+	return saw_console_brk;
+}
+
+struct sunhv_ops {
+	void (*transmit_chars)(struct uart_port *port, struct circ_buf *xmit);
+	int (*receive_chars)(struct uart_port *port, struct tty_struct *tty);
+};
+
+static struct sunhv_ops bychar_ops = {
+	.transmit_chars = transmit_chars_putchar,
+	.receive_chars = receive_chars_getchar,
+};
+
+static struct sunhv_ops bywrite_ops = {
+	.transmit_chars = transmit_chars_write,
+	.receive_chars = receive_chars_read,
+};
+
+static struct sunhv_ops *sunhv_ops = &bychar_ops;
+
+static struct tty_struct *receive_chars(struct uart_port *port)
+{
+	struct tty_struct *tty = NULL;
+
+	if (port->state != NULL)		/* Unopened serial console */
+		tty = port->state->port.tty;
+
+	if (sunhv_ops->receive_chars(port, tty))
+		sun_do_break();
+
+	return tty;
+}
+
+static void transmit_chars(struct uart_port *port)
+{
+	struct circ_buf *xmit;
+
+	if (!port->state)
+		return;
+
+	xmit = &port->state->xmit;
+	if (uart_circ_empty(xmit) || uart_tx_stopped(port))
+		return;
+
+	sunhv_ops->transmit_chars(port, xmit);
+
+	if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
+		uart_write_wakeup(port);
+}
+
+static irqreturn_t sunhv_interrupt(int irq, void *dev_id)
+{
+	struct uart_port *port = dev_id;
+	struct tty_struct *tty;
+	unsigned long flags;
+
+	spin_lock_irqsave(&port->lock, flags);
+	tty = receive_chars(port);
+	transmit_chars(port);
+	spin_unlock_irqrestore(&port->lock, flags);
+
+	if (tty)
+		tty_flip_buffer_push(tty);
+
+	return IRQ_HANDLED;
+}
+
+/* port->lock is not held.  */
+static unsigned int sunhv_tx_empty(struct uart_port *port)
+{
+	/* Transmitter is always empty for us.  If the circ buffer
+	 * is non-empty or there is an x_char pending, our caller
+	 * will do the right thing and ignore what we return here.
+	 */
+	return TIOCSER_TEMT;
+}
+
+/* port->lock held by caller.  */
+static void sunhv_set_mctrl(struct uart_port *port, unsigned int mctrl)
+{
+	return;
+}
+
+/* port->lock is held by caller and interrupts are disabled.  */
+static unsigned int sunhv_get_mctrl(struct uart_port *port)
+{
+	return TIOCM_DSR | TIOCM_CAR | TIOCM_CTS;
+}
+
+/* port->lock held by caller.  */
+static void sunhv_stop_tx(struct uart_port *port)
+{
+	return;
+}
+
+/* port->lock held by caller.  */
+static void sunhv_start_tx(struct uart_port *port)
+{
+	transmit_chars(port);
+}
+
+/* port->lock is not held.  */
+static void sunhv_send_xchar(struct uart_port *port, char ch)
+{
+	unsigned long flags;
+	int limit = 10000;
+
+	spin_lock_irqsave(&port->lock, flags);
+
+	while (limit-- > 0) {
+		long status = sun4v_con_putchar(ch);
+		if (status == HV_EOK)
+			break;
+		udelay(1);
+	}
+
+	spin_unlock_irqrestore(&port->lock, flags);
+}
+
+/* port->lock held by caller.  */
+static void sunhv_stop_rx(struct uart_port *port)
+{
+}
+
+/* port->lock held by caller.  */
+static void sunhv_enable_ms(struct uart_port *port)
+{
+}
+
+/* port->lock is not held.  */
+static void sunhv_break_ctl(struct uart_port *port, int break_state)
+{
+	if (break_state) {
+		unsigned long flags;
+		int limit = 10000;
+
+		spin_lock_irqsave(&port->lock, flags);
+
+		while (limit-- > 0) {
+			long status = sun4v_con_putchar(CON_BREAK);
+			if (status == HV_EOK)
+				break;
+			udelay(1);
+		}
+
+		spin_unlock_irqrestore(&port->lock, flags);
+	}
+}
+
+/* port->lock is not held.  */
+static int sunhv_startup(struct uart_port *port)
+{
+	return 0;
+}
+
+/* port->lock is not held.  */
+static void sunhv_shutdown(struct uart_port *port)
+{
+}
+
+/* port->lock is not held.  */
+static void sunhv_set_termios(struct uart_port *port, struct ktermios *termios,
+			      struct ktermios *old)
+{
+	unsigned int baud = uart_get_baud_rate(port, termios, old, 0, 4000000);
+	unsigned int quot = uart_get_divisor(port, baud);
+	unsigned int iflag, cflag;
+	unsigned long flags;
+
+	spin_lock_irqsave(&port->lock, flags);
+
+	iflag = termios->c_iflag;
+	cflag = termios->c_cflag;
+
+	port->ignore_status_mask = 0;
+	if (iflag & IGNBRK)
+		port->ignore_status_mask |= IGNORE_BREAK;
+	if ((cflag & CREAD) == 0)
+		port->ignore_status_mask |= IGNORE_ALL;
+
+	/* XXX */
+	uart_update_timeout(port, cflag,
+			    (port->uartclk / (16 * quot)));
+
+	spin_unlock_irqrestore(&port->lock, flags);
+}
+
+static const char *sunhv_type(struct uart_port *port)
+{
+	return "SUN4V HCONS";
+}
+
+static void sunhv_release_port(struct uart_port *port)
+{
+}
+
+static int sunhv_request_port(struct uart_port *port)
+{
+	return 0;
+}
+
+static void sunhv_config_port(struct uart_port *port, int flags)
+{
+}
+
+static int sunhv_verify_port(struct uart_port *port, struct serial_struct *ser)
+{
+	return -EINVAL;
+}
+
+static struct uart_ops sunhv_pops = {
+	.tx_empty	= sunhv_tx_empty,
+	.set_mctrl	= sunhv_set_mctrl,
+	.get_mctrl	= sunhv_get_mctrl,
+	.stop_tx	= sunhv_stop_tx,
+	.start_tx	= sunhv_start_tx,
+	.send_xchar	= sunhv_send_xchar,
+	.stop_rx	= sunhv_stop_rx,
+	.enable_ms	= sunhv_enable_ms,
+	.break_ctl	= sunhv_break_ctl,
+	.startup	= sunhv_startup,
+	.shutdown	= sunhv_shutdown,
+	.set_termios	= sunhv_set_termios,
+	.type		= sunhv_type,
+	.release_port	= sunhv_release_port,
+	.request_port	= sunhv_request_port,
+	.config_port	= sunhv_config_port,
+	.verify_port	= sunhv_verify_port,
+};
+
+static struct uart_driver sunhv_reg = {
+	.owner			= THIS_MODULE,
+	.driver_name		= "sunhv",
+	.dev_name		= "ttyS",
+	.major			= TTY_MAJOR,
+};
+
+static struct uart_port *sunhv_port;
+
+/* Copy 's' into the con_write_page, decoding "\n" into
+ * "\r\n" along the way.  We have to return two lengths
+ * because the caller needs to know how much to advance
+ * 's' and also how many bytes to output via con_write_page.
+ */
+static int fill_con_write_page(const char *s, unsigned int n,
+			       unsigned long *page_bytes)
+{
+	const char *orig_s = s;
+	char *p = con_write_page;
+	int left = PAGE_SIZE;
+
+	while (n--) {
+		if (*s == '\n') {
+			if (left < 2)
+				break;
+			*p++ = '\r';
+			left--;
+		} else if (left < 1)
+			break;
+		*p++ = *s++;
+		left--;
+	}
+	*page_bytes = p - con_write_page;
+	return s - orig_s;
+}
+
+static void sunhv_console_write_paged(struct console *con, const char *s, unsigned n)
+{
+	struct uart_port *port = sunhv_port;
+	unsigned long flags;
+	int locked = 1;
+
+	local_irq_save(flags);
+	if (port->sysrq) {
+		locked = 0;
+	} else if (oops_in_progress) {
+		locked = spin_trylock(&port->lock);
+	} else
+		spin_lock(&port->lock);
+
+	while (n > 0) {
+		unsigned long ra = __pa(con_write_page);
+		unsigned long page_bytes;
+		unsigned int cpy = fill_con_write_page(s, n,
+						       &page_bytes);
+
+		n -= cpy;
+		s += cpy;
+		while (page_bytes > 0) {
+			unsigned long written;
+			int limit = 1000000;
+
+			while (limit--) {
+				unsigned long stat;
+
+				stat = sun4v_con_write(ra, page_bytes,
+						       &written);
+				if (stat == HV_EOK)
+					break;
+				udelay(1);
+			}
+			if (limit < 0)
+				break;
+			page_bytes -= written;
+			ra += written;
+		}
+	}
+
+	if (locked)
+		spin_unlock(&port->lock);
+	local_irq_restore(flags);
+}
+
+static inline void sunhv_console_putchar(struct uart_port *port, char c)
+{
+	int limit = 1000000;
+
+	while (limit-- > 0) {
+		long status = sun4v_con_putchar(c);
+		if (status == HV_EOK)
+			break;
+		udelay(1);
+	}
+}
+
+static void sunhv_console_write_bychar(struct console *con, const char *s, unsigned n)
+{
+	struct uart_port *port = sunhv_port;
+	unsigned long flags;
+	int i, locked = 1;
+
+	local_irq_save(flags);
+	if (port->sysrq) {
+		locked = 0;
+	} else if (oops_in_progress) {
+		locked = spin_trylock(&port->lock);
+	} else
+		spin_lock(&port->lock);
+
+	for (i = 0; i < n; i++) {
+		if (*s == '\n')
+			sunhv_console_putchar(port, '\r');
+		sunhv_console_putchar(port, *s++);
+	}
+
+	if (locked)
+		spin_unlock(&port->lock);
+	local_irq_restore(flags);
+}
+
+static struct console sunhv_console = {
+	.name	=	"ttyHV",
+	.write	=	sunhv_console_write_bychar,
+	.device	=	uart_console_device,
+	.flags	=	CON_PRINTBUFFER,
+	.index	=	-1,
+	.data	=	&sunhv_reg,
+};
+
+static int __devinit hv_probe(struct platform_device *op)
+{
+	struct uart_port *port;
+	unsigned long minor;
+	int err;
+
+	if (op->archdata.irqs[0] == 0xffffffff)
+		return -ENODEV;
+
+	port = kzalloc(sizeof(struct uart_port), GFP_KERNEL);
+	if (unlikely(!port))
+		return -ENOMEM;
+
+	minor = 1;
+	if (sun4v_hvapi_register(HV_GRP_CORE, 1, &minor) == 0 &&
+	    minor >= 1) {
+		err = -ENOMEM;
+		con_write_page = kzalloc(PAGE_SIZE, GFP_KERNEL);
+		if (!con_write_page)
+			goto out_free_port;
+
+		con_read_page = kzalloc(PAGE_SIZE, GFP_KERNEL);
+		if (!con_read_page)
+			goto out_free_con_write_page;
+
+		sunhv_console.write = sunhv_console_write_paged;
+		sunhv_ops = &bywrite_ops;
+	}
+
+	sunhv_port = port;
+
+	port->line = 0;
+	port->ops = &sunhv_pops;
+	port->type = PORT_SUNHV;
+	port->uartclk = ( 29491200 / 16 ); /* arbitrary */
+
+	port->membase = (unsigned char __iomem *) __pa(port);
+
+	port->irq = op->archdata.irqs[0];
+
+	port->dev = &op->dev;
+
+	err = sunserial_register_minors(&sunhv_reg, 1);
+	if (err)
+		goto out_free_con_read_page;
+
+	sunserial_console_match(&sunhv_console, op->dev.of_node,
+				&sunhv_reg, port->line, false);
+
+	err = uart_add_one_port(&sunhv_reg, port);
+	if (err)
+		goto out_unregister_driver;
+
+	err = request_irq(port->irq, sunhv_interrupt, 0, "hvcons", port);
+	if (err)
+		goto out_remove_port;
+
+	dev_set_drvdata(&op->dev, port);
+
+	return 0;
+
+out_remove_port:
+	uart_remove_one_port(&sunhv_reg, port);
+
+out_unregister_driver:
+	sunserial_unregister_minors(&sunhv_reg, 1);
+
+out_free_con_read_page:
+	kfree(con_read_page);
+
+out_free_con_write_page:
+	kfree(con_write_page);
+
+out_free_port:
+	kfree(port);
+	sunhv_port = NULL;
+	return err;
+}
+
+static int __devexit hv_remove(struct platform_device *dev)
+{
+	struct uart_port *port = dev_get_drvdata(&dev->dev);
+
+	free_irq(port->irq, port);
+
+	uart_remove_one_port(&sunhv_reg, port);
+
+	sunserial_unregister_minors(&sunhv_reg, 1);
+
+	kfree(port);
+	sunhv_port = NULL;
+
+	dev_set_drvdata(&dev->dev, NULL);
+
+	return 0;
+}
+
+static const struct of_device_id hv_match[] = {
+	{
+		.name = "console",
+		.compatible = "qcn",
+	},
+	{
+		.name = "console",
+		.compatible = "SUNW,sun4v-console",
+	},
+	{},
+};
+MODULE_DEVICE_TABLE(of, hv_match);
+
+static struct platform_driver hv_driver = {
+	.driver = {
+		.name = "hv",
+		.owner = THIS_MODULE,
+		.of_match_table = hv_match,
+	},
+	.probe		= hv_probe,
+	.remove		= __devexit_p(hv_remove),
+};
+
+static int __init sunhv_init(void)
+{
+	if (tlb_type != hypervisor)
+		return -ENODEV;
+
+	return platform_driver_register(&hv_driver);
+}
+
+static void __exit sunhv_exit(void)
+{
+	platform_driver_unregister(&hv_driver);
+}
+
+module_init(sunhv_init);
+module_exit(sunhv_exit);
+
+MODULE_AUTHOR("David S. Miller");
+MODULE_DESCRIPTION("SUN4V Hypervisor console driver");
+MODULE_VERSION("2.0");
+MODULE_LICENSE("GPL");
diff --git a/ap/os/linux/linux-3.4.x/drivers/tty/serial/sunsab.c b/ap/os/linux/linux-3.4.x/drivers/tty/serial/sunsab.c
new file mode 100644
index 0000000..c9549aa
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/tty/serial/sunsab.c
@@ -0,0 +1,1162 @@
+/* sunsab.c: ASYNC Driver for the SIEMENS SAB82532 DUSCC.
+ *
+ * Copyright (C) 1997  Eddie C. Dost  (ecd@skynet.be)
+ * Copyright (C) 2002, 2006  David S. Miller (davem@davemloft.net)
+ *
+ * Rewrote buffer handling to use CIRC(Circular Buffer) macros.
+ *   Maxim Krasnyanskiy <maxk@qualcomm.com>
+ *
+ * Fixed to use tty_get_baud_rate, and to allow for arbitrary baud
+ * rates to be programmed into the UART.  Also eliminated a lot of
+ * duplicated code in the console setup.
+ *   Theodore Ts'o <tytso@mit.edu>, 2001-Oct-12
+ *
+ * Ported to new 2.5.x UART layer.
+ *   David S. Miller <davem@davemloft.net>
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
+#include <linux/major.h>
+#include <linux/string.h>
+#include <linux/ptrace.h>
+#include <linux/ioport.h>
+#include <linux/circ_buf.h>
+#include <linux/serial.h>
+#include <linux/sysrq.h>
+#include <linux/console.h>
+#include <linux/spinlock.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/of_device.h>
+
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/prom.h>
+#include <asm/setup.h>
+
+#if defined(CONFIG_SERIAL_SUNSAB_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ)
+#define SUPPORT_SYSRQ
+#endif
+
+#include <linux/serial_core.h>
+#include <linux/sunserialcore.h>
+
+#include "sunsab.h"
+
+struct uart_sunsab_port {
+	struct uart_port		port;		/* Generic UART port	*/
+	union sab82532_async_regs	__iomem *regs;	/* Chip registers	*/
+	unsigned long			irqflags;	/* IRQ state flags	*/
+	int				dsr;		/* Current DSR state	*/
+	unsigned int			cec_timeout;	/* Chip poll timeout... */
+	unsigned int			tec_timeout;	/* likewise		*/
+	unsigned char			interrupt_mask0;/* ISR0 masking		*/
+	unsigned char			interrupt_mask1;/* ISR1 masking		*/
+	unsigned char			pvr_dtr_bit;	/* Which PVR bit is DTR */
+	unsigned char			pvr_dsr_bit;	/* Which PVR bit is DSR */
+	unsigned int			gis_shift;
+	int				type;		/* SAB82532 version	*/
+
+	/* Setting configuration bits while the transmitter is active
+	 * can cause garbage characters to get emitted by the chip.
+	 * Therefore, we cache such writes here and do the real register
+	 * write the next time the transmitter becomes idle.
+	 */
+	unsigned int			cached_ebrg;
+	unsigned char			cached_mode;
+	unsigned char			cached_pvr;
+	unsigned char			cached_dafo;
+};
+
+/*
+ * This assumes you have a 29.4912 MHz clock for your UART.
+ */
+#define SAB_BASE_BAUD ( 29491200 / 16 )
+
+static char *sab82532_version[16] = {
+	"V1.0", "V2.0", "V3.2", "V(0x03)",
+	"V(0x04)", "V(0x05)", "V(0x06)", "V(0x07)",
+	"V(0x08)", "V(0x09)", "V(0x0a)", "V(0x0b)",
+	"V(0x0c)", "V(0x0d)", "V(0x0e)", "V(0x0f)"
+};
+
+#define SAB82532_MAX_TEC_TIMEOUT 200000	/* 1 character time (at 50 baud) */
+#define SAB82532_MAX_CEC_TIMEOUT  50000	/* 2.5 TX CLKs (at 50 baud) */
+
+#define SAB82532_RECV_FIFO_SIZE	32      /* Standard async fifo sizes */
+#define SAB82532_XMIT_FIFO_SIZE	32
+
+static __inline__ void sunsab_tec_wait(struct uart_sunsab_port *up)
+{
+	int timeout = up->tec_timeout;
+
+	while ((readb(&up->regs->r.star) & SAB82532_STAR_TEC) && --timeout)
+		udelay(1);
+}
+
+static __inline__ void sunsab_cec_wait(struct uart_sunsab_port *up)
+{
+	int timeout = up->cec_timeout;
+
+	while ((readb(&up->regs->r.star) & SAB82532_STAR_CEC) && --timeout)
+		udelay(1);
+}
+
+static struct tty_struct *
+receive_chars(struct uart_sunsab_port *up,
+	      union sab82532_irq_status *stat)
+{
+	struct tty_struct *tty = NULL;
+	unsigned char buf[32];
+	int saw_console_brk = 0;
+	int free_fifo = 0;
+	int count = 0;
+	int i;
+
+	if (up->port.state != NULL)		/* Unopened serial console */
+		tty = up->port.state->port.tty;
+
+	/* Read number of BYTES (Character + Status) available. */
+	if (stat->sreg.isr0 & SAB82532_ISR0_RPF) {
+		count = SAB82532_RECV_FIFO_SIZE;
+		free_fifo++;
+	}
+
+	if (stat->sreg.isr0 & SAB82532_ISR0_TCD) {
+		count = readb(&up->regs->r.rbcl) & (SAB82532_RECV_FIFO_SIZE - 1);
+		free_fifo++;
+	}
+
+	/* Issue a FIFO read command in case we where idle. */
+	if (stat->sreg.isr0 & SAB82532_ISR0_TIME) {
+		sunsab_cec_wait(up);
+		writeb(SAB82532_CMDR_RFRD, &up->regs->w.cmdr);
+		return tty;
+	}
+
+	if (stat->sreg.isr0 & SAB82532_ISR0_RFO)
+		free_fifo++;
+
+	/* Read the FIFO. */
+	for (i = 0; i < count; i++)
+		buf[i] = readb(&up->regs->r.rfifo[i]);
+
+	/* Issue Receive Message Complete command. */
+	if (free_fifo) {
+		sunsab_cec_wait(up);
+		writeb(SAB82532_CMDR_RMC, &up->regs->w.cmdr);
+	}
+
+	/* Count may be zero for BRK, so we check for it here */
+	if ((stat->sreg.isr1 & SAB82532_ISR1_BRK) &&
+	    (up->port.line == up->port.cons->index))
+		saw_console_brk = 1;
+
+	if (count == 0) {
+		if (unlikely(stat->sreg.isr1 & SAB82532_ISR1_BRK)) {
+			stat->sreg.isr0 &= ~(SAB82532_ISR0_PERR |
+					     SAB82532_ISR0_FERR);
+			up->port.icount.brk++;
+			uart_handle_break(&up->port);
+		}
+	}
+
+	for (i = 0; i < count; i++) {
+		unsigned char ch = buf[i], flag;
+
+		if (tty == NULL) {
+			uart_handle_sysrq_char(&up->port, ch);
+			continue;
+		}
+
+		flag = TTY_NORMAL;
+		up->port.icount.rx++;
+
+		if (unlikely(stat->sreg.isr0 & (SAB82532_ISR0_PERR |
+						SAB82532_ISR0_FERR |
+						SAB82532_ISR0_RFO)) ||
+		    unlikely(stat->sreg.isr1 & SAB82532_ISR1_BRK)) {
+			/*
+			 * For statistics only
+			 */
+			if (stat->sreg.isr1 & SAB82532_ISR1_BRK) {
+				stat->sreg.isr0 &= ~(SAB82532_ISR0_PERR |
+						     SAB82532_ISR0_FERR);
+				up->port.icount.brk++;
+				/*
+				 * We do the SysRQ and SAK checking
+				 * here because otherwise the break
+				 * may get masked by ignore_status_mask
+				 * or read_status_mask.
+				 */
+				if (uart_handle_break(&up->port))
+					continue;
+			} else if (stat->sreg.isr0 & SAB82532_ISR0_PERR)
+				up->port.icount.parity++;
+			else if (stat->sreg.isr0 & SAB82532_ISR0_FERR)
+				up->port.icount.frame++;
+			if (stat->sreg.isr0 & SAB82532_ISR0_RFO)
+				up->port.icount.overrun++;
+
+			/*
+			 * Mask off conditions which should be ingored.
+			 */
+			stat->sreg.isr0 &= (up->port.read_status_mask & 0xff);
+			stat->sreg.isr1 &= ((up->port.read_status_mask >> 8) & 0xff);
+
+			if (stat->sreg.isr1 & SAB82532_ISR1_BRK) {
+				flag = TTY_BREAK;
+			} else if (stat->sreg.isr0 & SAB82532_ISR0_PERR)
+				flag = TTY_PARITY;
+			else if (stat->sreg.isr0 & SAB82532_ISR0_FERR)
+				flag = TTY_FRAME;
+		}
+
+		if (uart_handle_sysrq_char(&up->port, ch))
+			continue;
+
+		if ((stat->sreg.isr0 & (up->port.ignore_status_mask & 0xff)) == 0 &&
+		    (stat->sreg.isr1 & ((up->port.ignore_status_mask >> 8) & 0xff)) == 0)
+			tty_insert_flip_char(tty, ch, flag);
+		if (stat->sreg.isr0 & SAB82532_ISR0_RFO)
+			tty_insert_flip_char(tty, 0, TTY_OVERRUN);
+	}
+
+	if (saw_console_brk)
+		sun_do_break();
+
+	return tty;
+}
+
+static void sunsab_stop_tx(struct uart_port *);
+static void sunsab_tx_idle(struct uart_sunsab_port *);
+
+static void transmit_chars(struct uart_sunsab_port *up,
+			   union sab82532_irq_status *stat)
+{
+	struct circ_buf *xmit = &up->port.state->xmit;
+	int i;
+
+	if (stat->sreg.isr1 & SAB82532_ISR1_ALLS) {
+		up->interrupt_mask1 |= SAB82532_IMR1_ALLS;
+		writeb(up->interrupt_mask1, &up->regs->w.imr1);
+		set_bit(SAB82532_ALLS, &up->irqflags);
+	}
+
+#if 0 /* bde@nwlink.com says this check causes problems */
+	if (!(stat->sreg.isr1 & SAB82532_ISR1_XPR))
+		return;
+#endif
+
+	if (!(readb(&up->regs->r.star) & SAB82532_STAR_XFW))
+		return;
+
+	set_bit(SAB82532_XPR, &up->irqflags);
+	sunsab_tx_idle(up);
+
+	if (uart_circ_empty(xmit) || uart_tx_stopped(&up->port)) {
+		up->interrupt_mask1 |= SAB82532_IMR1_XPR;
+		writeb(up->interrupt_mask1, &up->regs->w.imr1);
+		return;
+	}
+
+	up->interrupt_mask1 &= ~(SAB82532_IMR1_ALLS|SAB82532_IMR1_XPR);
+	writeb(up->interrupt_mask1, &up->regs->w.imr1);
+	clear_bit(SAB82532_ALLS, &up->irqflags);
+
+	/* Stuff 32 bytes into Transmit FIFO. */
+	clear_bit(SAB82532_XPR, &up->irqflags);
+	for (i = 0; i < up->port.fifosize; i++) {
+		writeb(xmit->buf[xmit->tail],
+		       &up->regs->w.xfifo[i]);
+		xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
+		up->port.icount.tx++;
+		if (uart_circ_empty(xmit))
+			break;
+	}
+
+	/* Issue a Transmit Frame command. */
+	sunsab_cec_wait(up);
+	writeb(SAB82532_CMDR_XF, &up->regs->w.cmdr);
+
+	if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
+		uart_write_wakeup(&up->port);
+
+	if (uart_circ_empty(xmit))
+		sunsab_stop_tx(&up->port);
+}
+
+static void check_status(struct uart_sunsab_port *up,
+			 union sab82532_irq_status *stat)
+{
+	if (stat->sreg.isr0 & SAB82532_ISR0_CDSC)
+		uart_handle_dcd_change(&up->port,
+				       !(readb(&up->regs->r.vstr) & SAB82532_VSTR_CD));
+
+	if (stat->sreg.isr1 & SAB82532_ISR1_CSC)
+		uart_handle_cts_change(&up->port,
+				       (readb(&up->regs->r.star) & SAB82532_STAR_CTS));
+
+	if ((readb(&up->regs->r.pvr) & up->pvr_dsr_bit) ^ up->dsr) {
+		up->dsr = (readb(&up->regs->r.pvr) & up->pvr_dsr_bit) ? 0 : 1;
+		up->port.icount.dsr++;
+	}
+
+	wake_up_interruptible(&up->port.state->port.delta_msr_wait);
+}
+
+static irqreturn_t sunsab_interrupt(int irq, void *dev_id)
+{
+	struct uart_sunsab_port *up = dev_id;
+	struct tty_struct *tty;
+	union sab82532_irq_status status;
+	unsigned long flags;
+	unsigned char gis;
+
+	spin_lock_irqsave(&up->port.lock, flags);
+
+	status.stat = 0;
+	gis = readb(&up->regs->r.gis) >> up->gis_shift;
+	if (gis & 1)
+		status.sreg.isr0 = readb(&up->regs->r.isr0);
+	if (gis & 2)
+		status.sreg.isr1 = readb(&up->regs->r.isr1);
+
+	tty = NULL;
+	if (status.stat) {
+		if ((status.sreg.isr0 & (SAB82532_ISR0_TCD | SAB82532_ISR0_TIME |
+					 SAB82532_ISR0_RFO | SAB82532_ISR0_RPF)) ||
+		    (status.sreg.isr1 & SAB82532_ISR1_BRK))
+			tty = receive_chars(up, &status);
+		if ((status.sreg.isr0 & SAB82532_ISR0_CDSC) ||
+		    (status.sreg.isr1 & SAB82532_ISR1_CSC))
+			check_status(up, &status);
+		if (status.sreg.isr1 & (SAB82532_ISR1_ALLS | SAB82532_ISR1_XPR))
+			transmit_chars(up, &status);
+	}
+
+	spin_unlock_irqrestore(&up->port.lock, flags);
+
+	if (tty)
+		tty_flip_buffer_push(tty);
+
+	return IRQ_HANDLED;
+}
+
+/* port->lock is not held.  */
+static unsigned int sunsab_tx_empty(struct uart_port *port)
+{
+	struct uart_sunsab_port *up = (struct uart_sunsab_port *) port;
+	int ret;
+
+	/* Do not need a lock for a state test like this.  */
+	if (test_bit(SAB82532_ALLS, &up->irqflags))
+		ret = TIOCSER_TEMT;
+	else
+		ret = 0;
+
+	return ret;
+}
+
+/* port->lock held by caller.  */
+static void sunsab_set_mctrl(struct uart_port *port, unsigned int mctrl)
+{
+	struct uart_sunsab_port *up = (struct uart_sunsab_port *) port;
+
+	if (mctrl & TIOCM_RTS) {
+		up->cached_mode &= ~SAB82532_MODE_FRTS;
+		up->cached_mode |= SAB82532_MODE_RTS;
+	} else {
+		up->cached_mode |= (SAB82532_MODE_FRTS |
+				    SAB82532_MODE_RTS);
+	}
+	if (mctrl & TIOCM_DTR) {
+		up->cached_pvr &= ~(up->pvr_dtr_bit);
+	} else {
+		up->cached_pvr |= up->pvr_dtr_bit;
+	}
+
+	set_bit(SAB82532_REGS_PENDING, &up->irqflags);
+	if (test_bit(SAB82532_XPR, &up->irqflags))
+		sunsab_tx_idle(up);
+}
+
+/* port->lock is held by caller and interrupts are disabled.  */
+static unsigned int sunsab_get_mctrl(struct uart_port *port)
+{
+	struct uart_sunsab_port *up = (struct uart_sunsab_port *) port;
+	unsigned char val;
+	unsigned int result;
+
+	result = 0;
+
+	val = readb(&up->regs->r.pvr);
+	result |= (val & up->pvr_dsr_bit) ? 0 : TIOCM_DSR;
+
+	val = readb(&up->regs->r.vstr);
+	result |= (val & SAB82532_VSTR_CD) ? 0 : TIOCM_CAR;
+
+	val = readb(&up->regs->r.star);
+	result |= (val & SAB82532_STAR_CTS) ? TIOCM_CTS : 0;
+
+	return result;
+}
+
+/* port->lock held by caller.  */
+static void sunsab_stop_tx(struct uart_port *port)
+{
+	struct uart_sunsab_port *up = (struct uart_sunsab_port *) port;
+
+	up->interrupt_mask1 |= SAB82532_IMR1_XPR;
+	writeb(up->interrupt_mask1, &up->regs->w.imr1);
+}
+
+/* port->lock held by caller.  */
+static void sunsab_tx_idle(struct uart_sunsab_port *up)
+{
+	if (test_bit(SAB82532_REGS_PENDING, &up->irqflags)) {
+		u8 tmp;
+
+		clear_bit(SAB82532_REGS_PENDING, &up->irqflags);
+		writeb(up->cached_mode, &up->regs->rw.mode);
+		writeb(up->cached_pvr, &up->regs->rw.pvr);
+		writeb(up->cached_dafo, &up->regs->w.dafo);
+
+		writeb(up->cached_ebrg & 0xff, &up->regs->w.bgr);
+		tmp = readb(&up->regs->rw.ccr2);
+		tmp &= ~0xc0;
+		tmp |= (up->cached_ebrg >> 2) & 0xc0;
+		writeb(tmp, &up->regs->rw.ccr2);
+	}
+}
+
+/* port->lock held by caller.  */
+static void sunsab_start_tx(struct uart_port *port)
+{
+	struct uart_sunsab_port *up = (struct uart_sunsab_port *) port;
+	struct circ_buf *xmit = &up->port.state->xmit;
+	int i;
+
+	up->interrupt_mask1 &= ~(SAB82532_IMR1_ALLS|SAB82532_IMR1_XPR);
+	writeb(up->interrupt_mask1, &up->regs->w.imr1);
+	
+	if (!test_bit(SAB82532_XPR, &up->irqflags))
+		return;
+
+	clear_bit(SAB82532_ALLS, &up->irqflags);
+	clear_bit(SAB82532_XPR, &up->irqflags);
+
+	for (i = 0; i < up->port.fifosize; i++) {
+		writeb(xmit->buf[xmit->tail],
+		       &up->regs->w.xfifo[i]);
+		xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
+		up->port.icount.tx++;
+		if (uart_circ_empty(xmit))
+			break;
+	}
+
+	/* Issue a Transmit Frame command.  */
+	sunsab_cec_wait(up);
+	writeb(SAB82532_CMDR_XF, &up->regs->w.cmdr);
+}
+
+/* port->lock is not held.  */
+static void sunsab_send_xchar(struct uart_port *port, char ch)
+{
+	struct uart_sunsab_port *up = (struct uart_sunsab_port *) port;
+	unsigned long flags;
+
+	spin_lock_irqsave(&up->port.lock, flags);
+
+	sunsab_tec_wait(up);
+	writeb(ch, &up->regs->w.tic);
+
+	spin_unlock_irqrestore(&up->port.lock, flags);
+}
+
+/* port->lock held by caller.  */
+static void sunsab_stop_rx(struct uart_port *port)
+{
+	struct uart_sunsab_port *up = (struct uart_sunsab_port *) port;
+
+	up->interrupt_mask0 |= SAB82532_IMR0_TCD;
+	writeb(up->interrupt_mask1, &up->regs->w.imr0);
+}
+
+/* port->lock held by caller.  */
+static void sunsab_enable_ms(struct uart_port *port)
+{
+	/* For now we always receive these interrupts.  */
+}
+
+/* port->lock is not held.  */
+static void sunsab_break_ctl(struct uart_port *port, int break_state)
+{
+	struct uart_sunsab_port *up = (struct uart_sunsab_port *) port;
+	unsigned long flags;
+	unsigned char val;
+
+	spin_lock_irqsave(&up->port.lock, flags);
+
+	val = up->cached_dafo;
+	if (break_state)
+		val |= SAB82532_DAFO_XBRK;
+	else
+		val &= ~SAB82532_DAFO_XBRK;
+	up->cached_dafo = val;
+
+	set_bit(SAB82532_REGS_PENDING, &up->irqflags);
+	if (test_bit(SAB82532_XPR, &up->irqflags))
+		sunsab_tx_idle(up);
+
+	spin_unlock_irqrestore(&up->port.lock, flags);
+}
+
+/* port->lock is not held.  */
+static int sunsab_startup(struct uart_port *port)
+{
+	struct uart_sunsab_port *up = (struct uart_sunsab_port *) port;
+	unsigned long flags;
+	unsigned char tmp;
+	int err = request_irq(up->port.irq, sunsab_interrupt,
+			      IRQF_SHARED, "sab", up);
+	if (err)
+		return err;
+
+	spin_lock_irqsave(&up->port.lock, flags);
+
+	/*
+	 * Wait for any commands or immediate characters
+	 */
+	sunsab_cec_wait(up);
+	sunsab_tec_wait(up);
+
+	/*
+	 * Clear the FIFO buffers.
+	 */
+	writeb(SAB82532_CMDR_RRES, &up->regs->w.cmdr);
+	sunsab_cec_wait(up);
+	writeb(SAB82532_CMDR_XRES, &up->regs->w.cmdr);
+
+	/*
+	 * Clear the interrupt registers.
+	 */
+	(void) readb(&up->regs->r.isr0);
+	(void) readb(&up->regs->r.isr1);
+
+	/*
+	 * Now, initialize the UART 
+	 */
+	writeb(0, &up->regs->w.ccr0);				/* power-down */
+	writeb(SAB82532_CCR0_MCE | SAB82532_CCR0_SC_NRZ |
+	       SAB82532_CCR0_SM_ASYNC, &up->regs->w.ccr0);
+	writeb(SAB82532_CCR1_ODS | SAB82532_CCR1_BCR | 7, &up->regs->w.ccr1);
+	writeb(SAB82532_CCR2_BDF | SAB82532_CCR2_SSEL |
+	       SAB82532_CCR2_TOE, &up->regs->w.ccr2);
+	writeb(0, &up->regs->w.ccr3);
+	writeb(SAB82532_CCR4_MCK4 | SAB82532_CCR4_EBRG, &up->regs->w.ccr4);
+	up->cached_mode = (SAB82532_MODE_RTS | SAB82532_MODE_FCTS |
+			   SAB82532_MODE_RAC);
+	writeb(up->cached_mode, &up->regs->w.mode);
+	writeb(SAB82532_RFC_DPS|SAB82532_RFC_RFTH_32, &up->regs->w.rfc);
+	
+	tmp = readb(&up->regs->rw.ccr0);
+	tmp |= SAB82532_CCR0_PU;	/* power-up */
+	writeb(tmp, &up->regs->rw.ccr0);
+
+	/*
+	 * Finally, enable interrupts
+	 */
+	up->interrupt_mask0 = (SAB82532_IMR0_PERR | SAB82532_IMR0_FERR |
+			       SAB82532_IMR0_PLLA);
+	writeb(up->interrupt_mask0, &up->regs->w.imr0);
+	up->interrupt_mask1 = (SAB82532_IMR1_BRKT | SAB82532_IMR1_ALLS |
+			       SAB82532_IMR1_XOFF | SAB82532_IMR1_TIN |
+			       SAB82532_IMR1_CSC | SAB82532_IMR1_XON |
+			       SAB82532_IMR1_XPR);
+	writeb(up->interrupt_mask1, &up->regs->w.imr1);
+	set_bit(SAB82532_ALLS, &up->irqflags);
+	set_bit(SAB82532_XPR, &up->irqflags);
+
+	spin_unlock_irqrestore(&up->port.lock, flags);
+
+	return 0;
+}
+
+/* port->lock is not held.  */
+static void sunsab_shutdown(struct uart_port *port)
+{
+	struct uart_sunsab_port *up = (struct uart_sunsab_port *) port;
+	unsigned long flags;
+
+	spin_lock_irqsave(&up->port.lock, flags);
+
+	/* Disable Interrupts */
+	up->interrupt_mask0 = 0xff;
+	writeb(up->interrupt_mask0, &up->regs->w.imr0);
+	up->interrupt_mask1 = 0xff;
+	writeb(up->interrupt_mask1, &up->regs->w.imr1);
+
+	/* Disable break condition */
+	up->cached_dafo = readb(&up->regs->rw.dafo);
+	up->cached_dafo &= ~SAB82532_DAFO_XBRK;
+	writeb(up->cached_dafo, &up->regs->rw.dafo);
+
+	/* Disable Receiver */	
+	up->cached_mode &= ~SAB82532_MODE_RAC;
+	writeb(up->cached_mode, &up->regs->rw.mode);
+
+	/*
+	 * XXX FIXME
+	 *
+	 * If the chip is powered down here the system hangs/crashes during
+	 * reboot or shutdown.  This needs to be investigated further,
+	 * similar behaviour occurs in 2.4 when the driver is configured
+	 * as a module only.  One hint may be that data is sometimes
+	 * transmitted at 9600 baud during shutdown (regardless of the
+	 * speed the chip was configured for when the port was open).
+	 */
+#if 0
+	/* Power Down */	
+	tmp = readb(&up->regs->rw.ccr0);
+	tmp &= ~SAB82532_CCR0_PU;
+	writeb(tmp, &up->regs->rw.ccr0);
+#endif
+
+	spin_unlock_irqrestore(&up->port.lock, flags);
+	free_irq(up->port.irq, up);
+}
+
+/*
+ * This is used to figure out the divisor speeds.
+ *
+ * The formula is:    Baud = SAB_BASE_BAUD / ((N + 1) * (1 << M)),
+ *
+ * with               0 <= N < 64 and 0 <= M < 16
+ */
+
+static void calc_ebrg(int baud, int *n_ret, int *m_ret)
+{
+	int	n, m;
+
+	if (baud == 0) {
+		*n_ret = 0;
+		*m_ret = 0;
+		return;
+	}
+     
+	/*
+	 * We scale numbers by 10 so that we get better accuracy
+	 * without having to use floating point.  Here we increment m
+	 * until n is within the valid range.
+	 */
+	n = (SAB_BASE_BAUD * 10) / baud;
+	m = 0;
+	while (n >= 640) {
+		n = n / 2;
+		m++;
+	}
+	n = (n+5) / 10;
+	/*
+	 * We try very hard to avoid speeds with M == 0 since they may
+	 * not work correctly for XTAL frequences above 10 MHz.
+	 */
+	if ((m == 0) && ((n & 1) == 0)) {
+		n = n / 2;
+		m++;
+	}
+	*n_ret = n - 1;
+	*m_ret = m;
+}
+
+/* Internal routine, port->lock is held and local interrupts are disabled.  */
+static void sunsab_convert_to_sab(struct uart_sunsab_port *up, unsigned int cflag,
+				  unsigned int iflag, unsigned int baud,
+				  unsigned int quot)
+{
+	unsigned char dafo;
+	int bits, n, m;
+
+	/* Byte size and parity */
+	switch (cflag & CSIZE) {
+	      case CS5: dafo = SAB82532_DAFO_CHL5; bits = 7; break;
+	      case CS6: dafo = SAB82532_DAFO_CHL6; bits = 8; break;
+	      case CS7: dafo = SAB82532_DAFO_CHL7; bits = 9; break;
+	      case CS8: dafo = SAB82532_DAFO_CHL8; bits = 10; break;
+	      /* Never happens, but GCC is too dumb to figure it out */
+	      default:  dafo = SAB82532_DAFO_CHL5; bits = 7; break;
+	}
+
+	if (cflag & CSTOPB) {
+		dafo |= SAB82532_DAFO_STOP;
+		bits++;
+	}
+
+	if (cflag & PARENB) {
+		dafo |= SAB82532_DAFO_PARE;
+		bits++;
+	}
+
+	if (cflag & PARODD) {
+		dafo |= SAB82532_DAFO_PAR_ODD;
+	} else {
+		dafo |= SAB82532_DAFO_PAR_EVEN;
+	}
+	up->cached_dafo = dafo;
+
+	calc_ebrg(baud, &n, &m);
+
+	up->cached_ebrg = n | (m << 6);
+
+	up->tec_timeout = (10 * 1000000) / baud;
+	up->cec_timeout = up->tec_timeout >> 2;
+
+	/* CTS flow control flags */
+	/* We encode read_status_mask and ignore_status_mask like so:
+	 *
+	 * ---------------------
+	 * | ... | ISR1 | ISR0 |
+	 * ---------------------
+	 *  ..    15   8 7    0
+	 */
+
+	up->port.read_status_mask = (SAB82532_ISR0_TCD | SAB82532_ISR0_TIME |
+				     SAB82532_ISR0_RFO | SAB82532_ISR0_RPF |
+				     SAB82532_ISR0_CDSC);
+	up->port.read_status_mask |= (SAB82532_ISR1_CSC |
+				      SAB82532_ISR1_ALLS |
+				      SAB82532_ISR1_XPR) << 8;
+	if (iflag & INPCK)
+		up->port.read_status_mask |= (SAB82532_ISR0_PERR |
+					      SAB82532_ISR0_FERR);
+	if (iflag & (BRKINT | PARMRK))
+		up->port.read_status_mask |= (SAB82532_ISR1_BRK << 8);
+
+	/*
+	 * Characteres to ignore
+	 */
+	up->port.ignore_status_mask = 0;
+	if (iflag & IGNPAR)
+		up->port.ignore_status_mask |= (SAB82532_ISR0_PERR |
+						SAB82532_ISR0_FERR);
+	if (iflag & IGNBRK) {
+		up->port.ignore_status_mask |= (SAB82532_ISR1_BRK << 8);
+		/*
+		 * If we're ignoring parity and break indicators,
+		 * ignore overruns too (for real raw support).
+		 */
+		if (iflag & IGNPAR)
+			up->port.ignore_status_mask |= SAB82532_ISR0_RFO;
+	}
+
+	/*
+	 * ignore all characters if CREAD is not set
+	 */
+	if ((cflag & CREAD) == 0)
+		up->port.ignore_status_mask |= (SAB82532_ISR0_RPF |
+						SAB82532_ISR0_TCD);
+
+	uart_update_timeout(&up->port, cflag,
+			    (up->port.uartclk / (16 * quot)));
+
+	/* Now schedule a register update when the chip's
+	 * transmitter is idle.
+	 */
+	up->cached_mode |= SAB82532_MODE_RAC;
+	set_bit(SAB82532_REGS_PENDING, &up->irqflags);
+	if (test_bit(SAB82532_XPR, &up->irqflags))
+		sunsab_tx_idle(up);
+}
+
+/* port->lock is not held.  */
+static void sunsab_set_termios(struct uart_port *port, struct ktermios *termios,
+			       struct ktermios *old)
+{
+	struct uart_sunsab_port *up = (struct uart_sunsab_port *) port;
+	unsigned long flags;
+	unsigned int baud = uart_get_baud_rate(port, termios, old, 0, 4000000);
+	unsigned int quot = uart_get_divisor(port, baud);
+
+	spin_lock_irqsave(&up->port.lock, flags);
+	sunsab_convert_to_sab(up, termios->c_cflag, termios->c_iflag, baud, quot);
+	spin_unlock_irqrestore(&up->port.lock, flags);
+}
+
+static const char *sunsab_type(struct uart_port *port)
+{
+	struct uart_sunsab_port *up = (void *)port;
+	static char buf[36];
+	
+	sprintf(buf, "SAB82532 %s", sab82532_version[up->type]);
+	return buf;
+}
+
+static void sunsab_release_port(struct uart_port *port)
+{
+}
+
+static int sunsab_request_port(struct uart_port *port)
+{
+	return 0;
+}
+
+static void sunsab_config_port(struct uart_port *port, int flags)
+{
+}
+
+static int sunsab_verify_port(struct uart_port *port, struct serial_struct *ser)
+{
+	return -EINVAL;
+}
+
+static struct uart_ops sunsab_pops = {
+	.tx_empty	= sunsab_tx_empty,
+	.set_mctrl	= sunsab_set_mctrl,
+	.get_mctrl	= sunsab_get_mctrl,
+	.stop_tx	= sunsab_stop_tx,
+	.start_tx	= sunsab_start_tx,
+	.send_xchar	= sunsab_send_xchar,
+	.stop_rx	= sunsab_stop_rx,
+	.enable_ms	= sunsab_enable_ms,
+	.break_ctl	= sunsab_break_ctl,
+	.startup	= sunsab_startup,
+	.shutdown	= sunsab_shutdown,
+	.set_termios	= sunsab_set_termios,
+	.type		= sunsab_type,
+	.release_port	= sunsab_release_port,
+	.request_port	= sunsab_request_port,
+	.config_port	= sunsab_config_port,
+	.verify_port	= sunsab_verify_port,
+};
+
+static struct uart_driver sunsab_reg = {
+	.owner			= THIS_MODULE,
+	.driver_name		= "sunsab",
+	.dev_name		= "ttyS",
+	.major			= TTY_MAJOR,
+};
+
+static struct uart_sunsab_port *sunsab_ports;
+
+#ifdef CONFIG_SERIAL_SUNSAB_CONSOLE
+
+static void sunsab_console_putchar(struct uart_port *port, int c)
+{
+	struct uart_sunsab_port *up = (struct uart_sunsab_port *)port;
+
+	sunsab_tec_wait(up);
+	writeb(c, &up->regs->w.tic);
+}
+
+static void sunsab_console_write(struct console *con, const char *s, unsigned n)
+{
+	struct uart_sunsab_port *up = &sunsab_ports[con->index];
+	unsigned long flags;
+	int locked = 1;
+
+	local_irq_save(flags);
+	if (up->port.sysrq) {
+		locked = 0;
+	} else if (oops_in_progress) {
+		locked = spin_trylock(&up->port.lock);
+	} else
+		spin_lock(&up->port.lock);
+
+	uart_console_write(&up->port, s, n, sunsab_console_putchar);
+	sunsab_tec_wait(up);
+
+	if (locked)
+		spin_unlock(&up->port.lock);
+	local_irq_restore(flags);
+}
+
+static int sunsab_console_setup(struct console *con, char *options)
+{
+	struct uart_sunsab_port *up = &sunsab_ports[con->index];
+	unsigned long flags;
+	unsigned int baud, quot;
+
+	/*
+	 * The console framework calls us for each and every port
+	 * registered. Defer the console setup until the requested
+	 * port has been properly discovered. A bit of a hack,
+	 * though...
+	 */
+	if (up->port.type != PORT_SUNSAB)
+		return -1;
+
+	printk("Console: ttyS%d (SAB82532)\n",
+	       (sunsab_reg.minor - 64) + con->index);
+
+	sunserial_console_termios(con, up->port.dev->of_node);
+
+	switch (con->cflag & CBAUD) {
+	case B150: baud = 150; break;
+	case B300: baud = 300; break;
+	case B600: baud = 600; break;
+	case B1200: baud = 1200; break;
+	case B2400: baud = 2400; break;
+	case B4800: baud = 4800; break;
+	default: case B9600: baud = 9600; break;
+	case B19200: baud = 19200; break;
+	case B38400: baud = 38400; break;
+	case B57600: baud = 57600; break;
+	case B115200: baud = 115200; break;
+	case B230400: baud = 230400; break;
+	case B460800: baud = 460800; break;
+	};
+
+	/*
+	 * Temporary fix.
+	 */
+	spin_lock_init(&up->port.lock);
+
+	/*
+	 * Initialize the hardware
+	 */
+	sunsab_startup(&up->port);
+
+	spin_lock_irqsave(&up->port.lock, flags);
+
+	/*
+	 * Finally, enable interrupts
+	 */
+	up->interrupt_mask0 = SAB82532_IMR0_PERR | SAB82532_IMR0_FERR |
+				SAB82532_IMR0_PLLA | SAB82532_IMR0_CDSC;
+	writeb(up->interrupt_mask0, &up->regs->w.imr0);
+	up->interrupt_mask1 = SAB82532_IMR1_BRKT | SAB82532_IMR1_ALLS |
+				SAB82532_IMR1_XOFF | SAB82532_IMR1_TIN |
+				SAB82532_IMR1_CSC | SAB82532_IMR1_XON |
+				SAB82532_IMR1_XPR;
+	writeb(up->interrupt_mask1, &up->regs->w.imr1);
+
+	quot = uart_get_divisor(&up->port, baud);
+	sunsab_convert_to_sab(up, con->cflag, 0, baud, quot);
+	sunsab_set_mctrl(&up->port, TIOCM_DTR | TIOCM_RTS);
+
+	spin_unlock_irqrestore(&up->port.lock, flags);
+	
+	return 0;
+}
+
+static struct console sunsab_console = {
+	.name	=	"ttyS",
+	.write	=	sunsab_console_write,
+	.device	=	uart_console_device,
+	.setup	=	sunsab_console_setup,
+	.flags	=	CON_PRINTBUFFER,
+	.index	=	-1,
+	.data	=	&sunsab_reg,
+};
+
+static inline struct console *SUNSAB_CONSOLE(void)
+{
+	return &sunsab_console;
+}
+#else
+#define SUNSAB_CONSOLE()	(NULL)
+#define sunsab_console_init()	do { } while (0)
+#endif
+
+static int __devinit sunsab_init_one(struct uart_sunsab_port *up,
+				     struct platform_device *op,
+				     unsigned long offset,
+				     int line)
+{
+	up->port.line = line;
+	up->port.dev = &op->dev;
+
+	up->port.mapbase = op->resource[0].start + offset;
+	up->port.membase = of_ioremap(&op->resource[0], offset,
+				      sizeof(union sab82532_async_regs),
+				      "sab");
+	if (!up->port.membase)
+		return -ENOMEM;
+	up->regs = (union sab82532_async_regs __iomem *) up->port.membase;
+
+	up->port.irq = op->archdata.irqs[0];
+
+	up->port.fifosize = SAB82532_XMIT_FIFO_SIZE;
+	up->port.iotype = UPIO_MEM;
+
+	writeb(SAB82532_IPC_IC_ACT_LOW, &up->regs->w.ipc);
+
+	up->port.ops = &sunsab_pops;
+	up->port.type = PORT_SUNSAB;
+	up->port.uartclk = SAB_BASE_BAUD;
+
+	up->type = readb(&up->regs->r.vstr) & 0x0f;
+	writeb(~((1 << 1) | (1 << 2) | (1 << 4)), &up->regs->w.pcr);
+	writeb(0xff, &up->regs->w.pim);
+	if ((up->port.line & 0x1) == 0) {
+		up->pvr_dsr_bit = (1 << 0);
+		up->pvr_dtr_bit = (1 << 1);
+		up->gis_shift = 2;
+	} else {
+		up->pvr_dsr_bit = (1 << 3);
+		up->pvr_dtr_bit = (1 << 2);
+		up->gis_shift = 0;
+	}
+	up->cached_pvr = (1 << 1) | (1 << 2) | (1 << 4);
+	writeb(up->cached_pvr, &up->regs->w.pvr);
+	up->cached_mode = readb(&up->regs->rw.mode);
+	up->cached_mode |= SAB82532_MODE_FRTS;
+	writeb(up->cached_mode, &up->regs->rw.mode);
+	up->cached_mode |= SAB82532_MODE_RTS;
+	writeb(up->cached_mode, &up->regs->rw.mode);
+
+	up->tec_timeout = SAB82532_MAX_TEC_TIMEOUT;
+	up->cec_timeout = SAB82532_MAX_CEC_TIMEOUT;
+
+	return 0;
+}
+
+static int __devinit sab_probe(struct platform_device *op)
+{
+	static int inst;
+	struct uart_sunsab_port *up;
+	int err;
+
+	up = &sunsab_ports[inst * 2];
+
+	err = sunsab_init_one(&up[0], op,
+			      0,
+			      (inst * 2) + 0);
+	if (err)
+		goto out;
+
+	err = sunsab_init_one(&up[1], op,
+			      sizeof(union sab82532_async_regs),
+			      (inst * 2) + 1);
+	if (err)
+		goto out1;
+
+	sunserial_console_match(SUNSAB_CONSOLE(), op->dev.of_node,
+				&sunsab_reg, up[0].port.line,
+				false);
+
+	sunserial_console_match(SUNSAB_CONSOLE(), op->dev.of_node,
+				&sunsab_reg, up[1].port.line,
+				false);
+
+	err = uart_add_one_port(&sunsab_reg, &up[0].port);
+	if (err)
+		goto out2;
+
+	err = uart_add_one_port(&sunsab_reg, &up[1].port);
+	if (err)
+		goto out3;
+
+	dev_set_drvdata(&op->dev, &up[0]);
+
+	inst++;
+
+	return 0;
+
+out3:
+	uart_remove_one_port(&sunsab_reg, &up[0].port);
+out2:
+	of_iounmap(&op->resource[0],
+		   up[1].port.membase,
+		   sizeof(union sab82532_async_regs));
+out1:
+	of_iounmap(&op->resource[0],
+		   up[0].port.membase,
+		   sizeof(union sab82532_async_regs));
+out:
+	return err;
+}
+
+static int __devexit sab_remove(struct platform_device *op)
+{
+	struct uart_sunsab_port *up = dev_get_drvdata(&op->dev);
+
+	uart_remove_one_port(&sunsab_reg, &up[1].port);
+	uart_remove_one_port(&sunsab_reg, &up[0].port);
+	of_iounmap(&op->resource[0],
+		   up[1].port.membase,
+		   sizeof(union sab82532_async_regs));
+	of_iounmap(&op->resource[0],
+		   up[0].port.membase,
+		   sizeof(union sab82532_async_regs));
+
+	dev_set_drvdata(&op->dev, NULL);
+
+	return 0;
+}
+
+static const struct of_device_id sab_match[] = {
+	{
+		.name = "se",
+	},
+	{
+		.name = "serial",
+		.compatible = "sab82532",
+	},
+	{},
+};
+MODULE_DEVICE_TABLE(of, sab_match);
+
+static struct platform_driver sab_driver = {
+	.driver = {
+		.name = "sab",
+		.owner = THIS_MODULE,
+		.of_match_table = sab_match,
+	},
+	.probe		= sab_probe,
+	.remove		= __devexit_p(sab_remove),
+};
+
+static int __init sunsab_init(void)
+{
+	struct device_node *dp;
+	int err;
+	int num_channels = 0;
+
+	for_each_node_by_name(dp, "se")
+		num_channels += 2;
+	for_each_node_by_name(dp, "serial") {
+		if (of_device_is_compatible(dp, "sab82532"))
+			num_channels += 2;
+	}
+
+	if (num_channels) {
+		sunsab_ports = kzalloc(sizeof(struct uart_sunsab_port) *
+				       num_channels, GFP_KERNEL);
+		if (!sunsab_ports)
+			return -ENOMEM;
+
+		err = sunserial_register_minors(&sunsab_reg, num_channels);
+		if (err) {
+			kfree(sunsab_ports);
+			sunsab_ports = NULL;
+
+			return err;
+		}
+	}
+
+	return platform_driver_register(&sab_driver);
+}
+
+static void __exit sunsab_exit(void)
+{
+	platform_driver_unregister(&sab_driver);
+	if (sunsab_reg.nr) {
+		sunserial_unregister_minors(&sunsab_reg, sunsab_reg.nr);
+	}
+
+	kfree(sunsab_ports);
+	sunsab_ports = NULL;
+}
+
+module_init(sunsab_init);
+module_exit(sunsab_exit);
+
+MODULE_AUTHOR("Eddie C. Dost and David S. Miller");
+MODULE_DESCRIPTION("Sun SAB82532 serial port driver");
+MODULE_LICENSE("GPL");
diff --git a/ap/os/linux/linux-3.4.x/drivers/tty/serial/sunsab.h b/ap/os/linux/linux-3.4.x/drivers/tty/serial/sunsab.h
new file mode 100644
index 0000000..b78e1f7
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/tty/serial/sunsab.h
@@ -0,0 +1,322 @@
+/* sunsab.h: Register Definitions for the Siemens SAB82532 DUSCC
+ *
+ * Copyright (C) 1997  Eddie C. Dost  (ecd@skynet.be)
+ */
+
+#ifndef _SUNSAB_H
+#define _SUNSAB_H
+
+struct sab82532_async_rd_regs {
+	u8	rfifo[0x20];	/* Receive FIFO				*/
+	u8	star;		/* Status Register			*/
+	u8	__pad1;
+	u8	mode;		/* Mode Register			*/
+	u8	timr;		/* Timer Register			*/
+	u8	xon;		/* XON Character			*/
+	u8	xoff;		/* XOFF Character			*/
+	u8	tcr;		/* Termination Character Register	*/
+	u8	dafo;		/* Data Format				*/
+	u8	rfc;		/* RFIFO Control Register		*/
+	u8	__pad2;
+	u8	rbcl;		/* Receive Byte Count Low		*/
+	u8	rbch;		/* Receive Byte Count High		*/
+	u8	ccr0;		/* Channel Configuration Register 0	*/
+	u8	ccr1;		/* Channel Configuration Register 1	*/
+	u8	ccr2;		/* Channel Configuration Register 2	*/
+	u8	ccr3;		/* Channel Configuration Register 3	*/
+	u8	__pad3[4];
+	u8	vstr;		/* Version Status Register		*/
+	u8	__pad4[3];
+	u8	gis;		/* Global Interrupt Status		*/
+	u8	ipc;		/* Interrupt Port Configuration		*/
+	u8	isr0;		/* Interrupt Status 0			*/
+	u8	isr1;		/* Interrupt Status 1			*/
+	u8	pvr;		/* Port Value Register			*/
+	u8	pis;		/* Port Interrupt Status		*/
+	u8	pcr;		/* Port Configuration Register		*/
+	u8	ccr4;		/* Channel Configuration Register 4	*/
+};
+
+struct sab82532_async_wr_regs {
+	u8	xfifo[0x20];	/* Transmit FIFO			*/
+	u8	cmdr;		/* Command Register			*/
+	u8	__pad1;
+	u8	mode;
+	u8	timr;
+	u8	xon;
+	u8	xoff;
+	u8	tcr;
+	u8	dafo;
+	u8	rfc;
+	u8	__pad2;
+	u8	xbcl;		/* Transmit Byte Count Low		*/
+	u8	xbch;		/* Transmit Byte Count High		*/
+	u8	ccr0;
+	u8	ccr1;
+	u8	ccr2;
+	u8	ccr3;
+	u8	tsax;		/* Time-Slot Assignment Reg. Transmit	*/
+	u8	tsar;		/* Time-Slot Assignment Reg. Receive	*/
+	u8	xccr;		/* Transmit Channel Capacity Register	*/
+	u8	rccr;		/* Receive Channel Capacity Register	*/
+	u8	bgr;		/* Baud Rate Generator Register		*/
+	u8	tic;		/* Transmit Immediate Character		*/
+	u8	mxn;		/* Mask XON Character			*/
+	u8	mxf;		/* Mask XOFF Character			*/
+	u8	iva;		/* Interrupt Vector Address		*/
+	u8	ipc;
+	u8	imr0;		/* Interrupt Mask Register 0		*/
+	u8	imr1;		/* Interrupt Mask Register 1		*/
+	u8	pvr;
+	u8	pim;		/* Port Interrupt Mask			*/
+	u8	pcr;
+	u8	ccr4;
+};
+
+struct sab82532_async_rw_regs {	/* Read/Write registers			*/
+	u8	__pad1[0x20];
+	u8	__pad2;
+	u8	__pad3;
+	u8	mode;
+	u8	timr;
+	u8	xon;
+	u8	xoff;
+	u8	tcr;
+	u8	dafo;
+	u8	rfc;
+	u8	__pad4;
+	u8	__pad5;
+	u8	__pad6;
+	u8	ccr0;
+	u8	ccr1;
+	u8	ccr2;
+	u8	ccr3;
+	u8	__pad7;
+	u8	__pad8;
+	u8	__pad9;
+	u8	__pad10;
+	u8	__pad11;
+	u8	__pad12;
+	u8	__pad13;
+	u8	__pad14;
+	u8	__pad15;
+	u8	ipc;
+	u8	__pad16;
+	u8	__pad17;
+	u8	pvr;
+	u8	__pad18;
+	u8	pcr;
+	u8	ccr4;
+};
+
+union sab82532_async_regs {
+	__volatile__ struct sab82532_async_rd_regs	r;
+	__volatile__ struct sab82532_async_wr_regs	w;
+	__volatile__ struct sab82532_async_rw_regs	rw;
+};
+
+union sab82532_irq_status {
+	unsigned short			 stat;
+	struct {
+		unsigned char		 isr0;
+		unsigned char		 isr1;
+	} sreg;
+};
+
+/* irqflags bits */
+#define SAB82532_ALLS			0x00000001
+#define SAB82532_XPR			0x00000002
+#define SAB82532_REGS_PENDING		0x00000004
+
+/* RFIFO Status Byte */
+#define SAB82532_RSTAT_PE		0x80
+#define SAB82532_RSTAT_FE		0x40
+#define SAB82532_RSTAT_PARITY		0x01
+
+/* Status Register (STAR) */
+#define SAB82532_STAR_XDOV		0x80
+#define SAB82532_STAR_XFW		0x40
+#define SAB82532_STAR_RFNE		0x20
+#define SAB82532_STAR_FCS		0x10
+#define SAB82532_STAR_TEC		0x08
+#define SAB82532_STAR_CEC		0x04
+#define SAB82532_STAR_CTS		0x02
+
+/* Command Register (CMDR) */
+#define SAB82532_CMDR_RMC		0x80
+#define SAB82532_CMDR_RRES		0x40
+#define SAB82532_CMDR_RFRD		0x20
+#define SAB82532_CMDR_STI		0x10
+#define SAB82532_CMDR_XF		0x08
+#define SAB82532_CMDR_XRES		0x01
+
+/* Mode Register (MODE) */
+#define SAB82532_MODE_FRTS		0x40
+#define SAB82532_MODE_FCTS		0x20
+#define SAB82532_MODE_FLON		0x10
+#define SAB82532_MODE_RAC		0x08
+#define SAB82532_MODE_RTS		0x04
+#define SAB82532_MODE_TRS		0x02
+#define SAB82532_MODE_TLP		0x01
+
+/* Timer Register (TIMR) */
+#define SAB82532_TIMR_CNT_MASK		0xe0
+#define SAB82532_TIMR_VALUE_MASK	0x1f
+
+/* Data Format (DAFO) */
+#define SAB82532_DAFO_XBRK		0x40
+#define SAB82532_DAFO_STOP		0x20
+#define SAB82532_DAFO_PAR_SPACE		0x00
+#define SAB82532_DAFO_PAR_ODD		0x08
+#define SAB82532_DAFO_PAR_EVEN		0x10
+#define SAB82532_DAFO_PAR_MARK		0x18
+#define SAB82532_DAFO_PARE		0x04
+#define SAB82532_DAFO_CHL8		0x00
+#define SAB82532_DAFO_CHL7		0x01
+#define SAB82532_DAFO_CHL6		0x02
+#define SAB82532_DAFO_CHL5		0x03
+
+/* RFIFO Control Register (RFC) */
+#define SAB82532_RFC_DPS		0x40
+#define SAB82532_RFC_DXS		0x20
+#define SAB82532_RFC_RFDF		0x10
+#define SAB82532_RFC_RFTH_1		0x00
+#define SAB82532_RFC_RFTH_4		0x04
+#define SAB82532_RFC_RFTH_16		0x08
+#define SAB82532_RFC_RFTH_32		0x0c
+#define SAB82532_RFC_TCDE		0x01
+
+/* Received Byte Count High (RBCH) */
+#define SAB82532_RBCH_DMA		0x80
+#define SAB82532_RBCH_CAS		0x20
+
+/* Transmit Byte Count High (XBCH) */
+#define SAB82532_XBCH_DMA		0x80
+#define SAB82532_XBCH_CAS		0x20
+#define SAB82532_XBCH_XC		0x10
+
+/* Channel Configuration Register 0 (CCR0) */
+#define SAB82532_CCR0_PU		0x80
+#define SAB82532_CCR0_MCE		0x40
+#define SAB82532_CCR0_SC_NRZ		0x00
+#define SAB82532_CCR0_SC_NRZI		0x08
+#define SAB82532_CCR0_SC_FM0		0x10
+#define SAB82532_CCR0_SC_FM1		0x14
+#define SAB82532_CCR0_SC_MANCH		0x18
+#define SAB82532_CCR0_SM_HDLC		0x00
+#define SAB82532_CCR0_SM_SDLC_LOOP	0x01
+#define SAB82532_CCR0_SM_BISYNC		0x02
+#define SAB82532_CCR0_SM_ASYNC		0x03
+
+/* Channel Configuration Register 1 (CCR1) */
+#define SAB82532_CCR1_ODS		0x10
+#define SAB82532_CCR1_BCR		0x08
+#define SAB82532_CCR1_CM_MASK		0x07
+
+/* Channel Configuration Register 2 (CCR2) */
+#define SAB82532_CCR2_SOC1		0x80
+#define SAB82532_CCR2_SOC0		0x40
+#define SAB82532_CCR2_BR9		0x80
+#define SAB82532_CCR2_BR8		0x40
+#define SAB82532_CCR2_BDF		0x20
+#define SAB82532_CCR2_SSEL		0x10
+#define SAB82532_CCR2_XCS0		0x20
+#define SAB82532_CCR2_RCS0		0x10
+#define SAB82532_CCR2_TOE		0x08
+#define SAB82532_CCR2_RWX		0x04
+#define SAB82532_CCR2_DIV		0x01
+
+/* Channel Configuration Register 3 (CCR3) */
+#define SAB82532_CCR3_PSD		0x01
+
+/* Time Slot Assignment Register Transmit (TSAX) */
+#define SAB82532_TSAX_TSNX_MASK		0xfc
+#define SAB82532_TSAX_XCS2		0x02	/* see also CCR2 */
+#define SAB82532_TSAX_XCS1		0x01
+
+/* Time Slot Assignment Register Receive (TSAR) */
+#define SAB82532_TSAR_TSNR_MASK		0xfc
+#define SAB82532_TSAR_RCS2		0x02	/* see also CCR2 */
+#define SAB82532_TSAR_RCS1		0x01
+
+/* Version Status Register (VSTR) */
+#define SAB82532_VSTR_CD		0x80
+#define SAB82532_VSTR_DPLA		0x40
+#define SAB82532_VSTR_VN_MASK		0x0f
+#define SAB82532_VSTR_VN_1		0x00
+#define SAB82532_VSTR_VN_2		0x01
+#define SAB82532_VSTR_VN_3_2		0x02
+
+/* Global Interrupt Status Register (GIS) */
+#define SAB82532_GIS_PI			0x80
+#define SAB82532_GIS_ISA1		0x08
+#define SAB82532_GIS_ISA0		0x04
+#define SAB82532_GIS_ISB1		0x02
+#define SAB82532_GIS_ISB0		0x01
+
+/* Interrupt Vector Address (IVA) */
+#define SAB82532_IVA_MASK		0xf1
+
+/* Interrupt Port Configuration (IPC) */
+#define SAB82532_IPC_VIS		0x80
+#define SAB82532_IPC_SLA1		0x10
+#define SAB82532_IPC_SLA0		0x08
+#define SAB82532_IPC_CASM		0x04
+#define SAB82532_IPC_IC_OPEN_DRAIN	0x00
+#define SAB82532_IPC_IC_ACT_LOW		0x01
+#define SAB82532_IPC_IC_ACT_HIGH	0x03
+
+/* Interrupt Status Register 0 (ISR0) */
+#define SAB82532_ISR0_TCD		0x80
+#define SAB82532_ISR0_TIME		0x40
+#define SAB82532_ISR0_PERR		0x20
+#define SAB82532_ISR0_FERR		0x10
+#define SAB82532_ISR0_PLLA		0x08
+#define SAB82532_ISR0_CDSC		0x04
+#define SAB82532_ISR0_RFO		0x02
+#define SAB82532_ISR0_RPF		0x01
+
+/* Interrupt Status Register 1 (ISR1) */
+#define SAB82532_ISR1_BRK		0x80
+#define SAB82532_ISR1_BRKT		0x40
+#define SAB82532_ISR1_ALLS		0x20
+#define SAB82532_ISR1_XOFF		0x10
+#define SAB82532_ISR1_TIN		0x08
+#define SAB82532_ISR1_CSC		0x04
+#define SAB82532_ISR1_XON		0x02
+#define SAB82532_ISR1_XPR		0x01
+
+/* Interrupt Mask Register 0 (IMR0) */
+#define SAB82532_IMR0_TCD		0x80
+#define SAB82532_IMR0_TIME		0x40
+#define SAB82532_IMR0_PERR		0x20
+#define SAB82532_IMR0_FERR		0x10
+#define SAB82532_IMR0_PLLA		0x08
+#define SAB82532_IMR0_CDSC		0x04
+#define SAB82532_IMR0_RFO		0x02
+#define SAB82532_IMR0_RPF		0x01
+
+/* Interrupt Mask Register 1 (IMR1) */
+#define SAB82532_IMR1_BRK		0x80
+#define SAB82532_IMR1_BRKT		0x40
+#define SAB82532_IMR1_ALLS		0x20
+#define SAB82532_IMR1_XOFF		0x10
+#define SAB82532_IMR1_TIN		0x08
+#define SAB82532_IMR1_CSC		0x04
+#define SAB82532_IMR1_XON		0x02
+#define SAB82532_IMR1_XPR		0x01
+
+/* Port Interrupt Status Register (PIS) */
+#define SAB82532_PIS_SYNC_B		0x08
+#define SAB82532_PIS_DTR_B		0x04
+#define SAB82532_PIS_DTR_A		0x02
+#define SAB82532_PIS_SYNC_A		0x01
+
+/* Channel Configuration Register 4 (CCR4) */
+#define SAB82532_CCR4_MCK4		0x80
+#define SAB82532_CCR4_EBRG		0x40
+#define SAB82532_CCR4_TST1		0x20
+#define SAB82532_CCR4_ICD		0x10
+
+
+#endif /* !(_SUNSAB_H) */
diff --git a/ap/os/linux/linux-3.4.x/drivers/tty/serial/sunsu.c b/ap/os/linux/linux-3.4.x/drivers/tty/serial/sunsu.c
new file mode 100644
index 0000000..76fa1fa
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/tty/serial/sunsu.c
@@ -0,0 +1,1605 @@
+/*
+ * su.c: Small serial driver for keyboard/mouse interface on sparc32/PCI
+ *
+ * Copyright (C) 1997  Eddie C. Dost  (ecd@skynet.be)
+ * Copyright (C) 1998-1999  Pete Zaitcev   (zaitcev@yahoo.com)
+ *
+ * This is mainly a variation of 8250.c, credits go to authors mentioned
+ * therein.  In fact this driver should be merged into the generic 8250.c
+ * infrastructure perhaps using a 8250_sparc.c module.
+ *
+ * Fixed to use tty_get_baud_rate().
+ *   Theodore Ts'o <tytso@mit.edu>, 2001-Oct-12
+ *
+ * Converted to new 2.5.x UART layer.
+ *   David S. Miller (davem@davemloft.net), 2002-Jul-29
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/spinlock.h>
+#include <linux/errno.h>
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
+#include <linux/major.h>
+#include <linux/string.h>
+#include <linux/ptrace.h>
+#include <linux/ioport.h>
+#include <linux/circ_buf.h>
+#include <linux/serial.h>
+#include <linux/sysrq.h>
+#include <linux/console.h>
+#include <linux/slab.h>
+#ifdef CONFIG_SERIO
+#include <linux/serio.h>
+#endif
+#include <linux/serial_reg.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/of_device.h>
+
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/prom.h>
+#include <asm/setup.h>
+
+#if defined(CONFIG_SERIAL_SUNSU_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ)
+#define SUPPORT_SYSRQ
+#endif
+
+#include <linux/serial_core.h>
+#include <linux/sunserialcore.h>
+
+/* We are on a NS PC87303 clocked with 24.0 MHz, which results
+ * in a UART clock of 1.8462 MHz.
+ */
+#define SU_BASE_BAUD	(1846200 / 16)
+
+enum su_type { SU_PORT_NONE, SU_PORT_MS, SU_PORT_KBD, SU_PORT_PORT };
+static char *su_typev[] = { "su(???)", "su(mouse)", "su(kbd)", "su(serial)" };
+
+/*
+ * Here we define the default xmit fifo size used for each type of UART.
+ */
+static const struct serial_uart_config uart_config[PORT_MAX_8250+1] = {
+	{ "unknown",	1,	0 },
+	{ "8250",	1,	0 },
+	{ "16450",	1,	0 },
+	{ "16550",	1,	0 },
+	{ "16550A",	16,	UART_CLEAR_FIFO | UART_USE_FIFO },
+	{ "Cirrus",	1, 	0 },
+	{ "ST16650",	1,	UART_CLEAR_FIFO | UART_STARTECH },
+	{ "ST16650V2",	32,	UART_CLEAR_FIFO | UART_USE_FIFO | UART_STARTECH },
+	{ "TI16750",	64,	UART_CLEAR_FIFO | UART_USE_FIFO },
+	{ "Startech",	1,	0 },
+	{ "16C950/954",	128,	UART_CLEAR_FIFO | UART_USE_FIFO },
+	{ "ST16654",	64,	UART_CLEAR_FIFO | UART_USE_FIFO | UART_STARTECH },
+	{ "XR16850",	128,	UART_CLEAR_FIFO | UART_USE_FIFO | UART_STARTECH },
+	{ "RSA",	2048,	UART_CLEAR_FIFO | UART_USE_FIFO }
+};
+
+struct uart_sunsu_port {
+	struct uart_port	port;
+	unsigned char		acr;
+	unsigned char		ier;
+	unsigned short		rev;
+	unsigned char		lcr;
+	unsigned int		lsr_break_flag;
+	unsigned int		cflag;
+
+	/* Probing information.  */
+	enum su_type		su_type;
+	unsigned int		type_probed;	/* XXX Stupid */
+	unsigned long		reg_size;
+
+#ifdef CONFIG_SERIO
+	struct serio		serio;
+	int			serio_open;
+#endif
+};
+
+static unsigned int serial_in(struct uart_sunsu_port *up, int offset)
+{
+	offset <<= up->port.regshift;
+
+	switch (up->port.iotype) {
+	case UPIO_HUB6:
+		outb(up->port.hub6 - 1 + offset, up->port.iobase);
+		return inb(up->port.iobase + 1);
+
+	case UPIO_MEM:
+		return readb(up->port.membase + offset);
+
+	default:
+		return inb(up->port.iobase + offset);
+	}
+}
+
+static void serial_out(struct uart_sunsu_port *up, int offset, int value)
+{
+#ifndef CONFIG_SPARC64
+	/*
+	 * MrCoffee has weird schematics: IRQ4 & P10(?) pins of SuperIO are
+	 * connected with a gate then go to SlavIO. When IRQ4 goes tristated
+	 * gate outputs a logical one. Since we use level triggered interrupts
+	 * we have lockup and watchdog reset. We cannot mask IRQ because
+	 * keyboard shares IRQ with us (Word has it as Bob Smelik's design).
+	 * This problem is similar to what Alpha people suffer, see serial.c.
+	 */
+	if (offset == UART_MCR)
+		value |= UART_MCR_OUT2;
+#endif
+	offset <<= up->port.regshift;
+
+	switch (up->port.iotype) {
+	case UPIO_HUB6:
+		outb(up->port.hub6 - 1 + offset, up->port.iobase);
+		outb(value, up->port.iobase + 1);
+		break;
+
+	case UPIO_MEM:
+		writeb(value, up->port.membase + offset);
+		break;
+
+	default:
+		outb(value, up->port.iobase + 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)		serial_in(up, offset)
+#define serial_outp(up, offset, value)	serial_out(up, offset, value)
+
+
+/*
+ * For the 16C950
+ */
+static void serial_icr_write(struct uart_sunsu_port *up, int offset, int value)
+{
+	serial_out(up, UART_SCR, offset);
+	serial_out(up, UART_ICR, value);
+}
+
+#if 0 /* Unused currently */
+static unsigned int serial_icr_read(struct uart_sunsu_port *up, int offset)
+{
+	unsigned int value;
+
+	serial_icr_write(up, UART_ACR, up->acr | UART_ACR_ICRRD);
+	serial_out(up, UART_SCR, offset);
+	value = serial_in(up, UART_ICR);
+	serial_icr_write(up, UART_ACR, up->acr);
+
+	return value;
+}
+#endif
+
+#ifdef CONFIG_SERIAL_8250_RSA
+/*
+ * Attempts to turn on the RSA FIFO.  Returns zero on failure.
+ * We set the port uart clock rate if we succeed.
+ */
+static int __enable_rsa(struct uart_sunsu_port *up)
+{
+	unsigned char mode;
+	int result;
+
+	mode = serial_inp(up, UART_RSA_MSR);
+	result = mode & UART_RSA_MSR_FIFO;
+
+	if (!result) {
+		serial_outp(up, UART_RSA_MSR, mode | UART_RSA_MSR_FIFO);
+		mode = serial_inp(up, UART_RSA_MSR);
+		result = mode & UART_RSA_MSR_FIFO;
+	}
+
+	if (result)
+		up->port.uartclk = SERIAL_RSA_BAUD_BASE * 16;
+
+	return result;
+}
+
+static void enable_rsa(struct uart_sunsu_port *up)
+{
+	if (up->port.type == PORT_RSA) {
+		if (up->port.uartclk != SERIAL_RSA_BAUD_BASE * 16) {
+			spin_lock_irq(&up->port.lock);
+			__enable_rsa(up);
+			spin_unlock_irq(&up->port.lock);
+		}
+		if (up->port.uartclk == SERIAL_RSA_BAUD_BASE * 16)
+			serial_outp(up, UART_RSA_FRR, 0);
+	}
+}
+
+/*
+ * Attempts to turn off the RSA FIFO.  Returns zero on failure.
+ * It is unknown why interrupts were disabled in here.  However,
+ * the caller is expected to preserve this behaviour by grabbing
+ * the spinlock before calling this function.
+ */
+static void disable_rsa(struct uart_sunsu_port *up)
+{
+	unsigned char mode;
+	int result;
+
+	if (up->port.type == PORT_RSA &&
+	    up->port.uartclk == SERIAL_RSA_BAUD_BASE * 16) {
+		spin_lock_irq(&up->port.lock);
+
+		mode = serial_inp(up, UART_RSA_MSR);
+		result = !(mode & UART_RSA_MSR_FIFO);
+
+		if (!result) {
+			serial_outp(up, UART_RSA_MSR, mode & ~UART_RSA_MSR_FIFO);
+			mode = serial_inp(up, UART_RSA_MSR);
+			result = !(mode & UART_RSA_MSR_FIFO);
+		}
+
+		if (result)
+			up->port.uartclk = SERIAL_RSA_BAUD_BASE_LO * 16;
+		spin_unlock_irq(&up->port.lock);
+	}
+}
+#endif /* CONFIG_SERIAL_8250_RSA */
+
+static inline void __stop_tx(struct uart_sunsu_port *p)
+{
+	if (p->ier & UART_IER_THRI) {
+		p->ier &= ~UART_IER_THRI;
+		serial_out(p, UART_IER, p->ier);
+	}
+}
+
+static void sunsu_stop_tx(struct uart_port *port)
+{
+	struct uart_sunsu_port *up = (struct uart_sunsu_port *) port;
+
+	__stop_tx(up);
+
+	/*
+	 * We really want to stop the transmitter from sending.
+	 */
+	if (up->port.type == PORT_16C950) {
+		up->acr |= UART_ACR_TXDIS;
+		serial_icr_write(up, UART_ACR, up->acr);
+	}
+}
+
+static void sunsu_start_tx(struct uart_port *port)
+{
+	struct uart_sunsu_port *up = (struct uart_sunsu_port *) port;
+
+	if (!(up->ier & UART_IER_THRI)) {
+		up->ier |= UART_IER_THRI;
+		serial_out(up, UART_IER, up->ier);
+	}
+
+	/*
+	 * Re-enable the transmitter if we disabled it.
+	 */
+	if (up->port.type == PORT_16C950 && up->acr & UART_ACR_TXDIS) {
+		up->acr &= ~UART_ACR_TXDIS;
+		serial_icr_write(up, UART_ACR, up->acr);
+	}
+}
+
+static void sunsu_stop_rx(struct uart_port *port)
+{
+	struct uart_sunsu_port *up = (struct uart_sunsu_port *) port;
+
+	up->ier &= ~UART_IER_RLSI;
+	up->port.read_status_mask &= ~UART_LSR_DR;
+	serial_out(up, UART_IER, up->ier);
+}
+
+static void sunsu_enable_ms(struct uart_port *port)
+{
+	struct uart_sunsu_port *up = (struct uart_sunsu_port *) port;
+	unsigned long flags;
+
+	spin_lock_irqsave(&up->port.lock, flags);
+	up->ier |= UART_IER_MSI;
+	serial_out(up, UART_IER, up->ier);
+	spin_unlock_irqrestore(&up->port.lock, flags);
+}
+
+static struct tty_struct *
+receive_chars(struct uart_sunsu_port *up, unsigned char *status)
+{
+	struct tty_struct *tty = up->port.state->port.tty;
+	unsigned char ch, flag;
+	int max_count = 256;
+	int saw_console_brk = 0;
+
+	do {
+		ch = serial_inp(up, UART_RX);
+		flag = TTY_NORMAL;
+		up->port.icount.rx++;
+
+		if (unlikely(*status & (UART_LSR_BI | UART_LSR_PE |
+				       UART_LSR_FE | UART_LSR_OE))) {
+			/*
+			 * For statistics only
+			 */
+			if (*status & UART_LSR_BI) {
+				*status &= ~(UART_LSR_FE | UART_LSR_PE);
+				up->port.icount.brk++;
+				if (up->port.cons != NULL &&
+				    up->port.line == up->port.cons->index)
+					saw_console_brk = 1;
+				/*
+				 * We do the SysRQ and SAK checking
+				 * here because otherwise the break
+				 * may get masked by ignore_status_mask
+				 * or read_status_mask.
+				 */
+				if (uart_handle_break(&up->port))
+					goto ignore_char;
+			} else if (*status & UART_LSR_PE)
+				up->port.icount.parity++;
+			else if (*status & UART_LSR_FE)
+				up->port.icount.frame++;
+			if (*status & UART_LSR_OE)
+				up->port.icount.overrun++;
+
+			/*
+			 * Mask off conditions which should be ingored.
+			 */
+			*status &= up->port.read_status_mask;
+
+			if (up->port.cons != NULL &&
+			    up->port.line == up->port.cons->index) {
+				/* Recover the break flag from console xmit */
+				*status |= up->lsr_break_flag;
+				up->lsr_break_flag = 0;
+			}
+
+			if (*status & UART_LSR_BI) {
+				flag = TTY_BREAK;
+			} else if (*status & UART_LSR_PE)
+				flag = TTY_PARITY;
+			else if (*status & UART_LSR_FE)
+				flag = TTY_FRAME;
+		}
+		if (uart_handle_sysrq_char(&up->port, ch))
+			goto ignore_char;
+		if ((*status & up->port.ignore_status_mask) == 0)
+			tty_insert_flip_char(tty, ch, flag);
+		if (*status & UART_LSR_OE)
+			/*
+			 * Overrun is special, since it's reported
+			 * immediately, and doesn't affect the current
+			 * character.
+			 */
+			 tty_insert_flip_char(tty, 0, TTY_OVERRUN);
+	ignore_char:
+		*status = serial_inp(up, UART_LSR);
+	} while ((*status & UART_LSR_DR) && (max_count-- > 0));
+
+	if (saw_console_brk)
+		sun_do_break();
+
+	return tty;
+}
+
+static void transmit_chars(struct uart_sunsu_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)) {
+		sunsu_stop_tx(&up->port);
+		return;
+	}
+	if (uart_circ_empty(xmit)) {
+		__stop_tx(up);
+		return;
+	}
+
+	count = up->port.fifosize;
+	do {
+		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 void check_modem_status(struct uart_sunsu_port *up)
+{
+	int status;
+
+	status = serial_in(up, UART_MSR);
+
+	if ((status & UART_MSR_ANY_DELTA) == 0)
+		return;
+
+	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);
+}
+
+static irqreturn_t sunsu_serial_interrupt(int irq, void *dev_id)
+{
+	struct uart_sunsu_port *up = dev_id;
+	unsigned long flags;
+	unsigned char status;
+
+	spin_lock_irqsave(&up->port.lock, flags);
+
+	do {
+		struct tty_struct *tty;
+
+		status = serial_inp(up, UART_LSR);
+		tty = NULL;
+		if (status & UART_LSR_DR)
+			tty = receive_chars(up, &status);
+		check_modem_status(up);
+		if (status & UART_LSR_THRE)
+			transmit_chars(up);
+
+		spin_unlock_irqrestore(&up->port.lock, flags);
+
+		if (tty)
+			tty_flip_buffer_push(tty);
+
+		spin_lock_irqsave(&up->port.lock, flags);
+
+	} while (!(serial_in(up, UART_IIR) & UART_IIR_NO_INT));
+
+	spin_unlock_irqrestore(&up->port.lock, flags);
+
+	return IRQ_HANDLED;
+}
+
+/* Separate interrupt handling path for keyboard/mouse ports.  */
+
+static void
+sunsu_change_speed(struct uart_port *port, unsigned int cflag,
+		   unsigned int iflag, unsigned int quot);
+
+static void sunsu_change_mouse_baud(struct uart_sunsu_port *up)
+{
+	unsigned int cur_cflag = up->cflag;
+	int quot, new_baud;
+
+	up->cflag &= ~CBAUD;
+	up->cflag |= suncore_mouse_baud_cflag_next(cur_cflag, &new_baud);
+
+	quot = up->port.uartclk / (16 * new_baud);
+
+	sunsu_change_speed(&up->port, up->cflag, 0, quot);
+}
+
+static void receive_kbd_ms_chars(struct uart_sunsu_port *up, int is_break)
+{
+	do {
+		unsigned char ch = serial_inp(up, UART_RX);
+
+		/* Stop-A is handled by drivers/char/keyboard.c now. */
+		if (up->su_type == SU_PORT_KBD) {
+#ifdef CONFIG_SERIO
+			serio_interrupt(&up->serio, ch, 0);
+#endif
+		} else if (up->su_type == SU_PORT_MS) {
+			int ret = suncore_mouse_baud_detection(ch, is_break);
+
+			switch (ret) {
+			case 2:
+				sunsu_change_mouse_baud(up);
+				/* fallthru */
+			case 1:
+				break;
+
+			case 0:
+#ifdef CONFIG_SERIO
+				serio_interrupt(&up->serio, ch, 0);
+#endif
+				break;
+			};
+		}
+	} while (serial_in(up, UART_LSR) & UART_LSR_DR);
+}
+
+static irqreturn_t sunsu_kbd_ms_interrupt(int irq, void *dev_id)
+{
+	struct uart_sunsu_port *up = dev_id;
+
+	if (!(serial_in(up, UART_IIR) & UART_IIR_NO_INT)) {
+		unsigned char status = serial_inp(up, UART_LSR);
+
+		if ((status & UART_LSR_DR) || (status & UART_LSR_BI))
+			receive_kbd_ms_chars(up, (status & UART_LSR_BI) != 0);
+	}
+
+	return IRQ_HANDLED;
+}
+
+static unsigned int sunsu_tx_empty(struct uart_port *port)
+{
+	struct uart_sunsu_port *up = (struct uart_sunsu_port *) port;
+	unsigned long flags;
+	unsigned int ret;
+
+	spin_lock_irqsave(&up->port.lock, flags);
+	ret = serial_in(up, UART_LSR) & UART_LSR_TEMT ? TIOCSER_TEMT : 0;
+	spin_unlock_irqrestore(&up->port.lock, flags);
+
+	return ret;
+}
+
+static unsigned int sunsu_get_mctrl(struct uart_port *port)
+{
+	struct uart_sunsu_port *up = (struct uart_sunsu_port *) port;
+	unsigned char status;
+	unsigned int ret;
+
+	status = serial_in(up, UART_MSR);
+
+	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 sunsu_set_mctrl(struct uart_port *port, unsigned int mctrl)
+{
+	struct uart_sunsu_port *up = (struct uart_sunsu_port *) port;
+	unsigned char mcr = 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;
+
+	serial_out(up, UART_MCR, mcr);
+}
+
+static void sunsu_break_ctl(struct uart_port *port, int break_state)
+{
+	struct uart_sunsu_port *up = (struct uart_sunsu_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;
+	serial_out(up, UART_LCR, up->lcr);
+	spin_unlock_irqrestore(&up->port.lock, flags);
+}
+
+static int sunsu_startup(struct uart_port *port)
+{
+	struct uart_sunsu_port *up = (struct uart_sunsu_port *) port;
+	unsigned long flags;
+	int retval;
+
+	if (up->port.type == PORT_16C950) {
+		/* Wake up and initialize UART */
+		up->acr = 0;
+		serial_outp(up, UART_LCR, 0xBF);
+		serial_outp(up, UART_EFR, UART_EFR_ECB);
+		serial_outp(up, UART_IER, 0);
+		serial_outp(up, UART_LCR, 0);
+		serial_icr_write(up, UART_CSR, 0); /* Reset the UART */
+		serial_outp(up, UART_LCR, 0xBF);
+		serial_outp(up, UART_EFR, UART_EFR_ECB);
+		serial_outp(up, UART_LCR, 0);
+	}
+
+#ifdef CONFIG_SERIAL_8250_RSA
+	/*
+	 * If this is an RSA port, see if we can kick it up to the
+	 * higher speed clock.
+	 */
+	enable_rsa(up);
+#endif
+
+	/*
+	 * Clear the FIFO buffers and disable them.
+	 * (they will be reenabled in set_termios())
+	 */
+	if (uart_config[up->port.type].flags & UART_CLEAR_FIFO) {
+		serial_outp(up, UART_FCR, UART_FCR_ENABLE_FIFO);
+		serial_outp(up, UART_FCR, UART_FCR_ENABLE_FIFO |
+				UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT);
+		serial_outp(up, UART_FCR, 0);
+	}
+
+	/*
+	 * 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);
+
+	/*
+	 * At this point, there's no way the LSR could still be 0xff;
+	 * if it is, then bail out, because there's likely no UART
+	 * here.
+	 */
+	if (!(up->port.flags & UPF_BUGGY_UART) &&
+	    (serial_inp(up, UART_LSR) == 0xff)) {
+		printk("ttyS%d: LSR safety check engaged!\n", up->port.line);
+		return -ENODEV;
+	}
+
+	if (up->su_type != SU_PORT_PORT) {
+		retval = request_irq(up->port.irq, sunsu_kbd_ms_interrupt,
+				     IRQF_SHARED, su_typev[up->su_type], up);
+	} else {
+		retval = request_irq(up->port.irq, sunsu_serial_interrupt,
+				     IRQF_SHARED, su_typev[up->su_type], up);
+	}
+	if (retval) {
+		printk("su: Cannot register IRQ %d\n", up->port.irq);
+		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;
+
+	sunsu_set_mctrl(&up->port, up->port.mctrl);
+	spin_unlock_irqrestore(&up->port.lock, flags);
+
+	/*
+	 * Finally, enable interrupts.  Note: Modem status interrupts
+	 * are set via set_termios(), which will be occurring imminently
+	 * anyway, so we don't enable them here.
+	 */
+	up->ier = UART_IER_RLSI | UART_IER_RDI;
+	serial_outp(up, UART_IER, up->ier);
+
+	if (up->port.flags & UPF_FOURPORT) {
+		unsigned int icp;
+		/*
+		 * Enable interrupts on the AST Fourport board
+		 */
+		icp = (up->port.iobase & 0xfe0) | 0x01f;
+		outb_p(0x80, icp);
+		(void) inb_p(icp);
+	}
+
+	/*
+	 * And clear the interrupt registers again for luck.
+	 */
+	(void) serial_inp(up, UART_LSR);
+	(void) serial_inp(up, UART_RX);
+	(void) serial_inp(up, UART_IIR);
+	(void) serial_inp(up, UART_MSR);
+
+	return 0;
+}
+
+static void sunsu_shutdown(struct uart_port *port)
+{
+	struct uart_sunsu_port *up = (struct uart_sunsu_port *) port;
+	unsigned long flags;
+
+	/*
+	 * Disable interrupts from this port
+	 */
+	up->ier = 0;
+	serial_outp(up, UART_IER, 0);
+
+	spin_lock_irqsave(&up->port.lock, flags);
+	if (up->port.flags & UPF_FOURPORT) {
+		/* reset interrupts on the AST Fourport board */
+		inb((up->port.iobase & 0xfe0) | 0x1f);
+		up->port.mctrl |= TIOCM_OUT1;
+	} else
+		up->port.mctrl &= ~TIOCM_OUT2;
+
+	sunsu_set_mctrl(&up->port, up->port.mctrl);
+	spin_unlock_irqrestore(&up->port.lock, flags);
+
+	/*
+	 * Disable break condition and FIFOs
+	 */
+	serial_out(up, UART_LCR, serial_inp(up, UART_LCR) & ~UART_LCR_SBC);
+	serial_outp(up, UART_FCR, UART_FCR_ENABLE_FIFO |
+				  UART_FCR_CLEAR_RCVR |
+				  UART_FCR_CLEAR_XMIT);
+	serial_outp(up, UART_FCR, 0);
+
+#ifdef CONFIG_SERIAL_8250_RSA
+	/*
+	 * Reset the RSA board back to 115kbps compat mode.
+	 */
+	disable_rsa(up);
+#endif
+
+	/*
+	 * Read data port to reset things.
+	 */
+	(void) serial_in(up, UART_RX);
+
+	free_irq(up->port.irq, up);
+}
+
+static void
+sunsu_change_speed(struct uart_port *port, unsigned int cflag,
+		   unsigned int iflag, unsigned int quot)
+{
+	struct uart_sunsu_port *up = (struct uart_sunsu_port *) port;
+	unsigned char cval, fcr = 0;
+	unsigned long flags;
+
+	switch (cflag & CSIZE) {
+	case CS5:
+		cval = 0x00;
+		break;
+	case CS6:
+		cval = 0x01;
+		break;
+	case CS7:
+		cval = 0x02;
+		break;
+	default:
+	case CS8:
+		cval = 0x03;
+		break;
+	}
+
+	if (cflag & CSTOPB)
+		cval |= 0x04;
+	if (cflag & PARENB)
+		cval |= UART_LCR_PARITY;
+	if (!(cflag & PARODD))
+		cval |= UART_LCR_EPAR;
+#ifdef CMSPAR
+	if (cflag & CMSPAR)
+		cval |= UART_LCR_SPAR;
+#endif
+
+	/*
+	 * Work around a bug in the Oxford Semiconductor 952 rev B
+	 * chip which causes it to seriously miscalculate baud rates
+	 * when DLL is 0.
+	 */
+	if ((quot & 0xff) == 0 && up->port.type == PORT_16C950 &&
+	    up->rev == 0x5201)
+		quot ++;
+
+	if (uart_config[up->port.type].flags & UART_USE_FIFO) {
+		if ((up->port.uartclk / quot) < (2400 * 16))
+			fcr = UART_FCR_ENABLE_FIFO | UART_FCR_TRIGGER_1;
+#ifdef CONFIG_SERIAL_8250_RSA
+		else if (up->port.type == PORT_RSA)
+			fcr = UART_FCR_ENABLE_FIFO | UART_FCR_TRIGGER_14;
+#endif
+		else
+			fcr = UART_FCR_ENABLE_FIFO | UART_FCR_TRIGGER_8;
+	}
+	if (up->port.type == PORT_16750)
+		fcr |= UART_FCR7_64BYTE;
+
+	/*
+	 * 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, cflag, (port->uartclk / (16 * quot)));
+
+	up->port.read_status_mask = UART_LSR_OE | UART_LSR_THRE | UART_LSR_DR;
+	if (iflag & INPCK)
+		up->port.read_status_mask |= UART_LSR_FE | UART_LSR_PE;
+	if (iflag & (BRKINT | PARMRK))
+		up->port.read_status_mask |= UART_LSR_BI;
+
+	/*
+	 * Characteres to ignore
+	 */
+	up->port.ignore_status_mask = 0;
+	if (iflag & IGNPAR)
+		up->port.ignore_status_mask |= UART_LSR_PE | UART_LSR_FE;
+	if (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 (iflag & IGNPAR)
+			up->port.ignore_status_mask |= UART_LSR_OE;
+	}
+
+	/*
+	 * ignore all characters if CREAD is not set
+	 */
+	if ((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, cflag))
+		up->ier |= UART_IER_MSI;
+
+	serial_out(up, UART_IER, up->ier);
+
+	if (uart_config[up->port.type].flags & UART_STARTECH) {
+		serial_outp(up, UART_LCR, 0xBF);
+		serial_outp(up, UART_EFR, cflag & CRTSCTS ? UART_EFR_CTS :0);
+	}
+	serial_outp(up, UART_LCR, cval | UART_LCR_DLAB);/* set DLAB */
+	serial_outp(up, UART_DLL, quot & 0xff);		/* LS of divisor */
+	serial_outp(up, UART_DLM, quot >> 8);		/* MS of divisor */
+	if (up->port.type == PORT_16750)
+		serial_outp(up, UART_FCR, fcr);		/* set fcr */
+	serial_outp(up, UART_LCR, cval);		/* reset DLAB */
+	up->lcr = cval;					/* Save LCR */
+	if (up->port.type != PORT_16750) {
+		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 */
+	}
+
+	up->cflag = cflag;
+
+	spin_unlock_irqrestore(&up->port.lock, flags);
+}
+
+static void
+sunsu_set_termios(struct uart_port *port, struct ktermios *termios,
+		  struct ktermios *old)
+{
+	unsigned int baud, quot;
+
+	/*
+	 * Ask the core to calculate the divisor for us.
+	 */
+	baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk/16); 
+	quot = uart_get_divisor(port, baud);
+
+	sunsu_change_speed(port, termios->c_cflag, termios->c_iflag, quot);
+}
+
+static void sunsu_release_port(struct uart_port *port)
+{
+}
+
+static int sunsu_request_port(struct uart_port *port)
+{
+	return 0;
+}
+
+static void sunsu_config_port(struct uart_port *port, int flags)
+{
+	struct uart_sunsu_port *up = (struct uart_sunsu_port *) port;
+
+	if (flags & UART_CONFIG_TYPE) {
+		/*
+		 * We are supposed to call autoconfig here, but this requires
+		 * splitting all the OBP probing crap from the UART probing.
+		 * We'll do it when we kill sunsu.c altogether.
+		 */
+		port->type = up->type_probed;	/* XXX */
+	}
+}
+
+static int
+sunsu_verify_port(struct uart_port *port, struct serial_struct *ser)
+{
+	return -EINVAL;
+}
+
+static const char *
+sunsu_type(struct uart_port *port)
+{
+	int type = port->type;
+
+	if (type >= ARRAY_SIZE(uart_config))
+		type = 0;
+	return uart_config[type].name;
+}
+
+static struct uart_ops sunsu_pops = {
+	.tx_empty	= sunsu_tx_empty,
+	.set_mctrl	= sunsu_set_mctrl,
+	.get_mctrl	= sunsu_get_mctrl,
+	.stop_tx	= sunsu_stop_tx,
+	.start_tx	= sunsu_start_tx,
+	.stop_rx	= sunsu_stop_rx,
+	.enable_ms	= sunsu_enable_ms,
+	.break_ctl	= sunsu_break_ctl,
+	.startup	= sunsu_startup,
+	.shutdown	= sunsu_shutdown,
+	.set_termios	= sunsu_set_termios,
+	.type		= sunsu_type,
+	.release_port	= sunsu_release_port,
+	.request_port	= sunsu_request_port,
+	.config_port	= sunsu_config_port,
+	.verify_port	= sunsu_verify_port,
+};
+
+#define UART_NR	4
+
+static struct uart_sunsu_port sunsu_ports[UART_NR];
+static int nr_inst; /* Number of already registered ports */
+
+#ifdef CONFIG_SERIO
+
+static DEFINE_SPINLOCK(sunsu_serio_lock);
+
+static int sunsu_serio_write(struct serio *serio, unsigned char ch)
+{
+	struct uart_sunsu_port *up = serio->port_data;
+	unsigned long flags;
+	int lsr;
+
+	spin_lock_irqsave(&sunsu_serio_lock, flags);
+
+	do {
+		lsr = serial_in(up, UART_LSR);
+	} while (!(lsr & UART_LSR_THRE));
+
+	/* Send the character out. */
+	serial_out(up, UART_TX, ch);
+
+	spin_unlock_irqrestore(&sunsu_serio_lock, flags);
+
+	return 0;
+}
+
+static int sunsu_serio_open(struct serio *serio)
+{
+	struct uart_sunsu_port *up = serio->port_data;
+	unsigned long flags;
+	int ret;
+
+	spin_lock_irqsave(&sunsu_serio_lock, flags);
+	if (!up->serio_open) {
+		up->serio_open = 1;
+		ret = 0;
+	} else
+		ret = -EBUSY;
+	spin_unlock_irqrestore(&sunsu_serio_lock, flags);
+
+	return ret;
+}
+
+static void sunsu_serio_close(struct serio *serio)
+{
+	struct uart_sunsu_port *up = serio->port_data;
+	unsigned long flags;
+
+	spin_lock_irqsave(&sunsu_serio_lock, flags);
+	up->serio_open = 0;
+	spin_unlock_irqrestore(&sunsu_serio_lock, flags);
+}
+
+#endif /* CONFIG_SERIO */
+
+static void sunsu_autoconfig(struct uart_sunsu_port *up)
+{
+	unsigned char status1, status2, scratch, scratch2, scratch3;
+	unsigned char save_lcr, save_mcr;
+	unsigned long flags;
+
+	if (up->su_type == SU_PORT_NONE)
+		return;
+
+	up->type_probed = PORT_UNKNOWN;
+	up->port.iotype = UPIO_MEM;
+
+	spin_lock_irqsave(&up->port.lock, flags);
+
+	if (!(up->port.flags & UPF_BUGGY_UART)) {
+		/*
+		 * Do a simple existence test first; if we fail this, there's
+		 * no point trying anything else.
+		 *
+		 * 0x80 is used as a nonsense port to prevent against false
+		 * positives due to ISA bus float.  The assumption is that
+		 * 0x80 is a non-existent port; which should be safe since
+		 * include/asm/io.h also makes this assumption.
+		 */
+		scratch = serial_inp(up, UART_IER);
+		serial_outp(up, UART_IER, 0);
+#ifdef __i386__
+		outb(0xff, 0x080);
+#endif
+		scratch2 = serial_inp(up, UART_IER);
+		serial_outp(up, UART_IER, 0x0f);
+#ifdef __i386__
+		outb(0, 0x080);
+#endif
+		scratch3 = serial_inp(up, UART_IER);
+		serial_outp(up, UART_IER, scratch);
+		if (scratch2 != 0 || scratch3 != 0x0F)
+			goto out;	/* We failed; there's nothing here */
+	}
+
+	save_mcr = serial_in(up, UART_MCR);
+	save_lcr = serial_in(up, UART_LCR);
+
+	/* 
+	 * Check to see if a UART is really there.  Certain broken
+	 * internal modems based on the Rockwell chipset fail this
+	 * test, because they apparently don't implement the loopback
+	 * test mode.  So this test is skipped on the COM 1 through
+	 * COM 4 ports.  This *should* be safe, since no board
+	 * manufacturer would be stupid enough to design a board
+	 * that conflicts with COM 1-4 --- we hope!
+	 */
+	if (!(up->port.flags & UPF_SKIP_TEST)) {
+		serial_outp(up, UART_MCR, UART_MCR_LOOP | 0x0A);
+		status1 = serial_inp(up, UART_MSR) & 0xF0;
+		serial_outp(up, UART_MCR, save_mcr);
+		if (status1 != 0x90)
+			goto out;	/* We failed loopback test */
+	}
+	serial_outp(up, UART_LCR, 0xBF);	/* set up for StarTech test */
+	serial_outp(up, UART_EFR, 0);		/* EFR is the same as FCR */
+	serial_outp(up, UART_LCR, 0);
+	serial_outp(up, UART_FCR, UART_FCR_ENABLE_FIFO);
+	scratch = serial_in(up, UART_IIR) >> 6;
+	switch (scratch) {
+		case 0:
+			up->port.type = PORT_16450;
+			break;
+		case 1:
+			up->port.type = PORT_UNKNOWN;
+			break;
+		case 2:
+			up->port.type = PORT_16550;
+			break;
+		case 3:
+			up->port.type = PORT_16550A;
+			break;
+	}
+	if (up->port.type == PORT_16550A) {
+		/* Check for Startech UART's */
+		serial_outp(up, UART_LCR, UART_LCR_DLAB);
+		if (serial_in(up, UART_EFR) == 0) {
+			up->port.type = PORT_16650;
+		} else {
+			serial_outp(up, UART_LCR, 0xBF);
+			if (serial_in(up, UART_EFR) == 0)
+				up->port.type = PORT_16650V2;
+		}
+	}
+	if (up->port.type == PORT_16550A) {
+		/* Check for TI 16750 */
+		serial_outp(up, UART_LCR, save_lcr | UART_LCR_DLAB);
+		serial_outp(up, UART_FCR,
+			    UART_FCR_ENABLE_FIFO | UART_FCR7_64BYTE);
+		scratch = serial_in(up, UART_IIR) >> 5;
+		if (scratch == 7) {
+			/*
+			 * If this is a 16750, and not a cheap UART
+			 * clone, then it should only go into 64 byte
+			 * mode if the UART_FCR7_64BYTE bit was set
+			 * while UART_LCR_DLAB was latched.
+			 */
+ 			serial_outp(up, UART_FCR, UART_FCR_ENABLE_FIFO);
+			serial_outp(up, UART_LCR, 0);
+			serial_outp(up, UART_FCR,
+				    UART_FCR_ENABLE_FIFO | UART_FCR7_64BYTE);
+			scratch = serial_in(up, UART_IIR) >> 5;
+			if (scratch == 6)
+				up->port.type = PORT_16750;
+		}
+		serial_outp(up, UART_FCR, UART_FCR_ENABLE_FIFO);
+	}
+	serial_outp(up, UART_LCR, save_lcr);
+	if (up->port.type == PORT_16450) {
+		scratch = serial_in(up, UART_SCR);
+		serial_outp(up, UART_SCR, 0xa5);
+		status1 = serial_in(up, UART_SCR);
+		serial_outp(up, UART_SCR, 0x5a);
+		status2 = serial_in(up, UART_SCR);
+		serial_outp(up, UART_SCR, scratch);
+
+		if ((status1 != 0xa5) || (status2 != 0x5a))
+			up->port.type = PORT_8250;
+	}
+
+	up->port.fifosize = uart_config[up->port.type].dfl_xmit_fifo_size;
+
+	if (up->port.type == PORT_UNKNOWN)
+		goto out;
+	up->type_probed = up->port.type;	/* XXX */
+
+	/*
+	 * Reset the UART.
+	 */
+#ifdef CONFIG_SERIAL_8250_RSA
+	if (up->port.type == PORT_RSA)
+		serial_outp(up, UART_RSA_FRR, 0);
+#endif
+	serial_outp(up, UART_MCR, save_mcr);
+	serial_outp(up, UART_FCR, (UART_FCR_ENABLE_FIFO |
+				     UART_FCR_CLEAR_RCVR |
+				     UART_FCR_CLEAR_XMIT));
+	serial_outp(up, UART_FCR, 0);
+	(void)serial_in(up, UART_RX);
+	serial_outp(up, UART_IER, 0);
+
+out:
+	spin_unlock_irqrestore(&up->port.lock, flags);
+}
+
+static struct uart_driver sunsu_reg = {
+	.owner			= THIS_MODULE,
+	.driver_name		= "sunsu",
+	.dev_name		= "ttyS",
+	.major			= TTY_MAJOR,
+};
+
+static int __devinit sunsu_kbd_ms_init(struct uart_sunsu_port *up)
+{
+	int quot, baud;
+#ifdef CONFIG_SERIO
+	struct serio *serio;
+#endif
+
+	if (up->su_type == SU_PORT_KBD) {
+		up->cflag = B1200 | CS8 | CLOCAL | CREAD;
+		baud = 1200;
+	} else {
+		up->cflag = B4800 | CS8 | CLOCAL | CREAD;
+		baud = 4800;
+	}
+	quot = up->port.uartclk / (16 * baud);
+
+	sunsu_autoconfig(up);
+	if (up->port.type == PORT_UNKNOWN)
+		return -ENODEV;
+
+	printk("%s: %s port at %llx, irq %u\n",
+	       up->port.dev->of_node->full_name,
+	       (up->su_type == SU_PORT_KBD) ? "Keyboard" : "Mouse",
+	       (unsigned long long) up->port.mapbase,
+	       up->port.irq);
+
+#ifdef CONFIG_SERIO
+	serio = &up->serio;
+	serio->port_data = up;
+
+	serio->id.type = SERIO_RS232;
+	if (up->su_type == SU_PORT_KBD) {
+		serio->id.proto = SERIO_SUNKBD;
+		strlcpy(serio->name, "sukbd", sizeof(serio->name));
+	} else {
+		serio->id.proto = SERIO_SUN;
+		serio->id.extra = 1;
+		strlcpy(serio->name, "sums", sizeof(serio->name));
+	}
+	strlcpy(serio->phys,
+		(!(up->port.line & 1) ? "su/serio0" : "su/serio1"),
+		sizeof(serio->phys));
+
+	serio->write = sunsu_serio_write;
+	serio->open = sunsu_serio_open;
+	serio->close = sunsu_serio_close;
+	serio->dev.parent = up->port.dev;
+
+	serio_register_port(serio);
+#endif
+
+	sunsu_change_speed(&up->port, up->cflag, 0, quot);
+
+	sunsu_startup(&up->port);
+	return 0;
+}
+
+/*
+ * ------------------------------------------------------------
+ * Serial console driver
+ * ------------------------------------------------------------
+ */
+
+#ifdef CONFIG_SERIAL_SUNSU_CONSOLE
+
+#define BOTH_EMPTY (UART_LSR_TEMT | UART_LSR_THRE)
+
+/*
+ *	Wait for transmitter & holding register to empty
+ */
+static __inline__ void wait_for_xmitr(struct uart_sunsu_port *up)
+{
+	unsigned int status, tmout = 10000;
+
+	/* Wait up to 10ms for the character(s) to be sent. */
+	do {
+		status = serial_in(up, UART_LSR);
+
+		if (status & UART_LSR_BI)
+			up->lsr_break_flag = UART_LSR_BI;
+
+		if (--tmout == 0)
+			break;
+		udelay(1);
+	} while ((status & BOTH_EMPTY) != BOTH_EMPTY);
+
+	/* Wait up to 1s for flow control if necessary */
+	if (up->port.flags & UPF_CONS_FLOW) {
+		tmout = 1000000;
+		while (--tmout &&
+		       ((serial_in(up, UART_MSR) & UART_MSR_CTS) == 0))
+			udelay(1);
+	}
+}
+
+static void sunsu_console_putchar(struct uart_port *port, int ch)
+{
+	struct uart_sunsu_port *up = (struct uart_sunsu_port *)port;
+
+	wait_for_xmitr(up);
+	serial_out(up, UART_TX, ch);
+}
+
+/*
+ *	Print a string to the serial port trying not to disturb
+ *	any possible real use of the port...
+ */
+static void sunsu_console_write(struct console *co, const char *s,
+				unsigned int count)
+{
+	struct uart_sunsu_port *up = &sunsu_ports[co->index];
+	unsigned long flags;
+	unsigned int ier;
+	int locked = 1;
+
+	local_irq_save(flags);
+	if (up->port.sysrq) {
+		locked = 0;
+	} else if (oops_in_progress) {
+		locked = spin_trylock(&up->port.lock);
+	} else
+		spin_lock(&up->port.lock);
+
+	/*
+	 *	First save the UER then disable the interrupts
+	 */
+	ier = serial_in(up, UART_IER);
+	serial_out(up, UART_IER, 0);
+
+	uart_console_write(&up->port, s, count, sunsu_console_putchar);
+
+	/*
+	 *	Finally, wait for transmitter to become empty
+	 *	and restore the IER
+	 */
+	wait_for_xmitr(up);
+	serial_out(up, UART_IER, ier);
+
+	if (locked)
+		spin_unlock(&up->port.lock);
+	local_irq_restore(flags);
+}
+
+/*
+ *	Setup initial baud/bits/parity. We do two things here:
+ *	- construct a cflag setting for the first su_open()
+ *	- initialize the serial port
+ *	Return non-zero if we didn't find a serial port.
+ */
+static int __init sunsu_console_setup(struct console *co, char *options)
+{
+	static struct ktermios dummy;
+	struct ktermios termios;
+	struct uart_port *port;
+
+	printk("Console: ttyS%d (SU)\n",
+	       (sunsu_reg.minor - 64) + co->index);
+
+	if (co->index > nr_inst)
+		return -ENODEV;
+	port = &sunsu_ports[co->index].port;
+
+	/*
+	 * Temporary fix.
+	 */
+	spin_lock_init(&port->lock);
+
+	/* Get firmware console settings.  */
+	sunserial_console_termios(co, port->dev->of_node);
+
+	memset(&termios, 0, sizeof(struct ktermios));
+	termios.c_cflag = co->cflag;
+	port->mctrl |= TIOCM_DTR;
+	port->ops->set_termios(port, &termios, &dummy);
+
+	return 0;
+}
+
+static struct console sunsu_console = {
+	.name	=	"ttyS",
+	.write	=	sunsu_console_write,
+	.device	=	uart_console_device,
+	.setup	=	sunsu_console_setup,
+	.flags	=	CON_PRINTBUFFER,
+	.index	=	-1,
+	.data	=	&sunsu_reg,
+};
+
+/*
+ *	Register console.
+ */
+
+static inline struct console *SUNSU_CONSOLE(void)
+{
+	return &sunsu_console;
+}
+#else
+#define SUNSU_CONSOLE()			(NULL)
+#define sunsu_serial_console_init()	do { } while (0)
+#endif
+
+static enum su_type __devinit su_get_type(struct device_node *dp)
+{
+	struct device_node *ap = of_find_node_by_path("/aliases");
+
+	if (ap) {
+		const char *keyb = of_get_property(ap, "keyboard", NULL);
+		const char *ms = of_get_property(ap, "mouse", NULL);
+
+		if (keyb) {
+			if (dp == of_find_node_by_path(keyb))
+				return SU_PORT_KBD;
+		}
+		if (ms) {
+			if (dp == of_find_node_by_path(ms))
+				return SU_PORT_MS;
+		}
+	}
+
+	return SU_PORT_PORT;
+}
+
+static int __devinit su_probe(struct platform_device *op)
+{
+	struct device_node *dp = op->dev.of_node;
+	struct uart_sunsu_port *up;
+	struct resource *rp;
+	enum su_type type;
+	bool ignore_line;
+	int err;
+
+	type = su_get_type(dp);
+	if (type == SU_PORT_PORT) {
+		if (nr_inst >= UART_NR)
+			return -EINVAL;
+		up = &sunsu_ports[nr_inst];
+	} else {
+		up = kzalloc(sizeof(*up), GFP_KERNEL);
+		if (!up)
+			return -ENOMEM;
+	}
+
+	up->port.line = nr_inst;
+
+	spin_lock_init(&up->port.lock);
+
+	up->su_type = type;
+
+	rp = &op->resource[0];
+	up->port.mapbase = rp->start;
+	up->reg_size = resource_size(rp);
+	up->port.membase = of_ioremap(rp, 0, up->reg_size, "su");
+	if (!up->port.membase) {
+		if (type != SU_PORT_PORT)
+			kfree(up);
+		return -ENOMEM;
+	}
+
+	up->port.irq = op->archdata.irqs[0];
+
+	up->port.dev = &op->dev;
+
+	up->port.type = PORT_UNKNOWN;
+	up->port.uartclk = (SU_BASE_BAUD * 16);
+
+	err = 0;
+	if (up->su_type == SU_PORT_KBD || up->su_type == SU_PORT_MS) {
+		err = sunsu_kbd_ms_init(up);
+		if (err) {
+			of_iounmap(&op->resource[0],
+				   up->port.membase, up->reg_size);
+			kfree(up);
+			return err;
+		}
+		dev_set_drvdata(&op->dev, up);
+
+		nr_inst++;
+
+		return 0;
+	}
+
+	up->port.flags |= UPF_BOOT_AUTOCONF;
+
+	sunsu_autoconfig(up);
+
+	err = -ENODEV;
+	if (up->port.type == PORT_UNKNOWN)
+		goto out_unmap;
+
+	up->port.ops = &sunsu_pops;
+
+	ignore_line = false;
+	if (!strcmp(dp->name, "rsc-console") ||
+	    !strcmp(dp->name, "lom-console"))
+		ignore_line = true;
+
+	sunserial_console_match(SUNSU_CONSOLE(), dp,
+				&sunsu_reg, up->port.line,
+				ignore_line);
+	err = uart_add_one_port(&sunsu_reg, &up->port);
+	if (err)
+		goto out_unmap;
+
+	dev_set_drvdata(&op->dev, up);
+
+	nr_inst++;
+
+	return 0;
+
+out_unmap:
+	of_iounmap(&op->resource[0], up->port.membase, up->reg_size);
+	return err;
+}
+
+static int __devexit su_remove(struct platform_device *op)
+{
+	struct uart_sunsu_port *up = dev_get_drvdata(&op->dev);
+	bool kbdms = false;
+
+	if (up->su_type == SU_PORT_MS ||
+	    up->su_type == SU_PORT_KBD)
+		kbdms = true;
+
+	if (kbdms) {
+#ifdef CONFIG_SERIO
+		serio_unregister_port(&up->serio);
+#endif
+	} else if (up->port.type != PORT_UNKNOWN)
+		uart_remove_one_port(&sunsu_reg, &up->port);
+
+	if (up->port.membase)
+		of_iounmap(&op->resource[0], up->port.membase, up->reg_size);
+
+	if (kbdms)
+		kfree(up);
+
+	dev_set_drvdata(&op->dev, NULL);
+
+	return 0;
+}
+
+static const struct of_device_id su_match[] = {
+	{
+		.name = "su",
+	},
+	{
+		.name = "su_pnp",
+	},
+	{
+		.name = "serial",
+		.compatible = "su",
+	},
+	{
+		.type = "serial",
+		.compatible = "su",
+	},
+	{},
+};
+MODULE_DEVICE_TABLE(of, su_match);
+
+static struct platform_driver su_driver = {
+	.driver = {
+		.name = "su",
+		.owner = THIS_MODULE,
+		.of_match_table = su_match,
+	},
+	.probe		= su_probe,
+	.remove		= __devexit_p(su_remove),
+};
+
+static int __init sunsu_init(void)
+{
+	struct device_node *dp;
+	int err;
+	int num_uart = 0;
+
+	for_each_node_by_name(dp, "su") {
+		if (su_get_type(dp) == SU_PORT_PORT)
+			num_uart++;
+	}
+	for_each_node_by_name(dp, "su_pnp") {
+		if (su_get_type(dp) == SU_PORT_PORT)
+			num_uart++;
+	}
+	for_each_node_by_name(dp, "serial") {
+		if (of_device_is_compatible(dp, "su")) {
+			if (su_get_type(dp) == SU_PORT_PORT)
+				num_uart++;
+		}
+	}
+	for_each_node_by_type(dp, "serial") {
+		if (of_device_is_compatible(dp, "su")) {
+			if (su_get_type(dp) == SU_PORT_PORT)
+				num_uart++;
+		}
+	}
+
+	if (num_uart) {
+		err = sunserial_register_minors(&sunsu_reg, num_uart);
+		if (err)
+			return err;
+	}
+
+	err = platform_driver_register(&su_driver);
+	if (err && num_uart)
+		sunserial_unregister_minors(&sunsu_reg, num_uart);
+
+	return err;
+}
+
+static void __exit sunsu_exit(void)
+{
+	if (sunsu_reg.nr)
+		sunserial_unregister_minors(&sunsu_reg, sunsu_reg.nr);
+}
+
+module_init(sunsu_init);
+module_exit(sunsu_exit);
+
+MODULE_AUTHOR("Eddie C. Dost, Peter Zaitcev, and David S. Miller");
+MODULE_DESCRIPTION("Sun SU serial port driver");
+MODULE_VERSION("2.0");
+MODULE_LICENSE("GPL");
diff --git a/ap/os/linux/linux-3.4.x/drivers/tty/serial/sunzilog.c b/ap/os/linux/linux-3.4.x/drivers/tty/serial/sunzilog.c
new file mode 100644
index 0000000..babd947
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/tty/serial/sunzilog.c
@@ -0,0 +1,1656 @@
+/* sunzilog.c: Zilog serial driver for Sparc systems.
+ *
+ * Driver for Zilog serial chips found on Sun workstations and
+ * servers.  This driver could actually be made more generic.
+ *
+ * This is based on the old drivers/sbus/char/zs.c code.  A lot
+ * of code has been simply moved over directly from there but
+ * much has been rewritten.  Credits therefore go out to Eddie
+ * C. Dost, Pete Zaitcev, Ted Ts'o and Alex Buell for their
+ * work there.
+ *
+ * Copyright (C) 2002, 2006, 2007 David S. Miller (davem@davemloft.net)
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/delay.h>
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
+#include <linux/major.h>
+#include <linux/string.h>
+#include <linux/ptrace.h>
+#include <linux/ioport.h>
+#include <linux/slab.h>
+#include <linux/circ_buf.h>
+#include <linux/serial.h>
+#include <linux/sysrq.h>
+#include <linux/console.h>
+#include <linux/spinlock.h>
+#ifdef CONFIG_SERIO
+#include <linux/serio.h>
+#endif
+#include <linux/init.h>
+#include <linux/of_device.h>
+
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/prom.h>
+#include <asm/setup.h>
+
+#if defined(CONFIG_SERIAL_SUNZILOG_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ)
+#define SUPPORT_SYSRQ
+#endif
+
+#include <linux/serial_core.h>
+#include <linux/sunserialcore.h>
+
+#include "sunzilog.h"
+
+/* On 32-bit sparcs we need to delay after register accesses
+ * to accommodate sun4 systems, but we do not need to flush writes.
+ * On 64-bit sparc we only need to flush single writes to ensure
+ * completion.
+ */
+#ifndef CONFIG_SPARC64
+#define ZSDELAY()		udelay(5)
+#define ZSDELAY_LONG()		udelay(20)
+#define ZS_WSYNC(channel)	do { } while (0)
+#else
+#define ZSDELAY()
+#define ZSDELAY_LONG()
+#define ZS_WSYNC(__channel) \
+	readb(&((__channel)->control))
+#endif
+
+#define ZS_CLOCK		4915200 /* Zilog input clock rate. */
+#define ZS_CLOCK_DIVISOR	16      /* Divisor this driver uses. */
+
+/*
+ * We wrap our port structure around the generic uart_port.
+ */
+struct uart_sunzilog_port {
+	struct uart_port		port;
+
+	/* IRQ servicing chain.  */
+	struct uart_sunzilog_port	*next;
+
+	/* Current values of Zilog write registers.  */
+	unsigned char			curregs[NUM_ZSREGS];
+
+	unsigned int			flags;
+#define SUNZILOG_FLAG_CONS_KEYB		0x00000001
+#define SUNZILOG_FLAG_CONS_MOUSE	0x00000002
+#define SUNZILOG_FLAG_IS_CONS		0x00000004
+#define SUNZILOG_FLAG_IS_KGDB		0x00000008
+#define SUNZILOG_FLAG_MODEM_STATUS	0x00000010
+#define SUNZILOG_FLAG_IS_CHANNEL_A	0x00000020
+#define SUNZILOG_FLAG_REGS_HELD		0x00000040
+#define SUNZILOG_FLAG_TX_STOPPED	0x00000080
+#define SUNZILOG_FLAG_TX_ACTIVE		0x00000100
+#define SUNZILOG_FLAG_ESCC		0x00000200
+#define SUNZILOG_FLAG_ISR_HANDLER	0x00000400
+
+	unsigned int cflag;
+
+	unsigned char			parity_mask;
+	unsigned char			prev_status;
+
+#ifdef CONFIG_SERIO
+	struct serio			serio;
+	int				serio_open;
+#endif
+};
+
+static void sunzilog_putchar(struct uart_port *port, int ch);
+
+#define ZILOG_CHANNEL_FROM_PORT(PORT)	((struct zilog_channel __iomem *)((PORT)->membase))
+#define UART_ZILOG(PORT)		((struct uart_sunzilog_port *)(PORT))
+
+#define ZS_IS_KEYB(UP)	((UP)->flags & SUNZILOG_FLAG_CONS_KEYB)
+#define ZS_IS_MOUSE(UP)	((UP)->flags & SUNZILOG_FLAG_CONS_MOUSE)
+#define ZS_IS_CONS(UP)	((UP)->flags & SUNZILOG_FLAG_IS_CONS)
+#define ZS_IS_KGDB(UP)	((UP)->flags & SUNZILOG_FLAG_IS_KGDB)
+#define ZS_WANTS_MODEM_STATUS(UP)	((UP)->flags & SUNZILOG_FLAG_MODEM_STATUS)
+#define ZS_IS_CHANNEL_A(UP)	((UP)->flags & SUNZILOG_FLAG_IS_CHANNEL_A)
+#define ZS_REGS_HELD(UP)	((UP)->flags & SUNZILOG_FLAG_REGS_HELD)
+#define ZS_TX_STOPPED(UP)	((UP)->flags & SUNZILOG_FLAG_TX_STOPPED)
+#define ZS_TX_ACTIVE(UP)	((UP)->flags & SUNZILOG_FLAG_TX_ACTIVE)
+
+/* Reading and writing Zilog8530 registers.  The delays are to make this
+ * driver work on the Sun4 which needs a settling delay after each chip
+ * register access, other machines handle this in hardware via auxiliary
+ * flip-flops which implement the settle time we do in software.
+ *
+ * The port lock must be held and local IRQs must be disabled
+ * when {read,write}_zsreg is invoked.
+ */
+static unsigned char read_zsreg(struct zilog_channel __iomem *channel,
+				unsigned char reg)
+{
+	unsigned char retval;
+
+	writeb(reg, &channel->control);
+	ZSDELAY();
+	retval = readb(&channel->control);
+	ZSDELAY();
+
+	return retval;
+}
+
+static void write_zsreg(struct zilog_channel __iomem *channel,
+			unsigned char reg, unsigned char value)
+{
+	writeb(reg, &channel->control);
+	ZSDELAY();
+	writeb(value, &channel->control);
+	ZSDELAY();
+}
+
+static void sunzilog_clear_fifo(struct zilog_channel __iomem *channel)
+{
+	int i;
+
+	for (i = 0; i < 32; i++) {
+		unsigned char regval;
+
+		regval = readb(&channel->control);
+		ZSDELAY();
+		if (regval & Rx_CH_AV)
+			break;
+
+		regval = read_zsreg(channel, R1);
+		readb(&channel->data);
+		ZSDELAY();
+
+		if (regval & (PAR_ERR | Rx_OVR | CRC_ERR)) {
+			writeb(ERR_RES, &channel->control);
+			ZSDELAY();
+			ZS_WSYNC(channel);
+		}
+	}
+}
+
+/* This function must only be called when the TX is not busy.  The UART
+ * port lock must be held and local interrupts disabled.
+ */
+static int __load_zsregs(struct zilog_channel __iomem *channel, unsigned char *regs)
+{
+	int i;
+	int escc;
+	unsigned char r15;
+
+	/* Let pending transmits finish.  */
+	for (i = 0; i < 1000; i++) {
+		unsigned char stat = read_zsreg(channel, R1);
+		if (stat & ALL_SNT)
+			break;
+		udelay(100);
+	}
+
+	writeb(ERR_RES, &channel->control);
+	ZSDELAY();
+	ZS_WSYNC(channel);
+
+	sunzilog_clear_fifo(channel);
+
+	/* Disable all interrupts.  */
+	write_zsreg(channel, R1,
+		    regs[R1] & ~(RxINT_MASK | TxINT_ENAB | EXT_INT_ENAB));
+
+	/* Set parity, sync config, stop bits, and clock divisor.  */
+	write_zsreg(channel, R4, regs[R4]);
+
+	/* Set misc. TX/RX control bits.  */
+	write_zsreg(channel, R10, regs[R10]);
+
+	/* Set TX/RX controls sans the enable bits.  */
+	write_zsreg(channel, R3, regs[R3] & ~RxENAB);
+	write_zsreg(channel, R5, regs[R5] & ~TxENAB);
+
+	/* Synchronous mode config.  */
+	write_zsreg(channel, R6, regs[R6]);
+	write_zsreg(channel, R7, regs[R7]);
+
+	/* Don't mess with the interrupt vector (R2, unused by us) and
+	 * master interrupt control (R9).  We make sure this is setup
+	 * properly at probe time then never touch it again.
+	 */
+
+	/* Disable baud generator.  */
+	write_zsreg(channel, R14, regs[R14] & ~BRENAB);
+
+	/* Clock mode control.  */
+	write_zsreg(channel, R11, regs[R11]);
+
+	/* Lower and upper byte of baud rate generator divisor.  */
+	write_zsreg(channel, R12, regs[R12]);
+	write_zsreg(channel, R13, regs[R13]);
+	
+	/* Now rewrite R14, with BRENAB (if set).  */
+	write_zsreg(channel, R14, regs[R14]);
+
+	/* External status interrupt control.  */
+	write_zsreg(channel, R15, (regs[R15] | WR7pEN) & ~FIFOEN);
+
+	/* ESCC Extension Register */
+	r15 = read_zsreg(channel, R15);
+	if (r15 & 0x01)	{
+		write_zsreg(channel, R7,  regs[R7p]);
+
+		/* External status interrupt and FIFO control.  */
+		write_zsreg(channel, R15, regs[R15] & ~WR7pEN);
+		escc = 1;
+	} else {
+		 /* Clear FIFO bit case it is an issue */
+		regs[R15] &= ~FIFOEN;
+		escc = 0;
+	}
+
+	/* Reset external status interrupts.  */
+	write_zsreg(channel, R0, RES_EXT_INT); /* First Latch  */
+	write_zsreg(channel, R0, RES_EXT_INT); /* Second Latch */
+
+	/* Rewrite R3/R5, this time without enables masked.  */
+	write_zsreg(channel, R3, regs[R3]);
+	write_zsreg(channel, R5, regs[R5]);
+
+	/* Rewrite R1, this time without IRQ enabled masked.  */
+	write_zsreg(channel, R1, regs[R1]);
+
+	return escc;
+}
+
+/* Reprogram the Zilog channel HW registers with the copies found in the
+ * software state struct.  If the transmitter is busy, we defer this update
+ * until the next TX complete interrupt.  Else, we do it right now.
+ *
+ * The UART port lock must be held and local interrupts disabled.
+ */
+static void sunzilog_maybe_update_regs(struct uart_sunzilog_port *up,
+				       struct zilog_channel __iomem *channel)
+{
+	if (!ZS_REGS_HELD(up)) {
+		if (ZS_TX_ACTIVE(up)) {
+			up->flags |= SUNZILOG_FLAG_REGS_HELD;
+		} else {
+			__load_zsregs(channel, up->curregs);
+		}
+	}
+}
+
+static void sunzilog_change_mouse_baud(struct uart_sunzilog_port *up)
+{
+	unsigned int cur_cflag = up->cflag;
+	int brg, new_baud;
+
+	up->cflag &= ~CBAUD;
+	up->cflag |= suncore_mouse_baud_cflag_next(cur_cflag, &new_baud);
+
+	brg = BPS_TO_BRG(new_baud, ZS_CLOCK / ZS_CLOCK_DIVISOR);
+	up->curregs[R12] = (brg & 0xff);
+	up->curregs[R13] = (brg >> 8) & 0xff;
+	sunzilog_maybe_update_regs(up, ZILOG_CHANNEL_FROM_PORT(&up->port));
+}
+
+static void sunzilog_kbdms_receive_chars(struct uart_sunzilog_port *up,
+					 unsigned char ch, int is_break)
+{
+	if (ZS_IS_KEYB(up)) {
+		/* Stop-A is handled by drivers/char/keyboard.c now. */
+#ifdef CONFIG_SERIO
+		if (up->serio_open)
+			serio_interrupt(&up->serio, ch, 0);
+#endif
+	} else if (ZS_IS_MOUSE(up)) {
+		int ret = suncore_mouse_baud_detection(ch, is_break);
+
+		switch (ret) {
+		case 2:
+			sunzilog_change_mouse_baud(up);
+			/* fallthru */
+		case 1:
+			break;
+
+		case 0:
+#ifdef CONFIG_SERIO
+			if (up->serio_open)
+				serio_interrupt(&up->serio, ch, 0);
+#endif
+			break;
+		};
+	}
+}
+
+static struct tty_struct *
+sunzilog_receive_chars(struct uart_sunzilog_port *up,
+		       struct zilog_channel __iomem *channel)
+{
+	struct tty_struct *tty;
+	unsigned char ch, r1, flag;
+
+	tty = NULL;
+	if (up->port.state != NULL &&		/* Unopened serial console */
+	    up->port.state->port.tty != NULL)	/* Keyboard || mouse */
+		tty = up->port.state->port.tty;
+
+	for (;;) {
+
+		r1 = read_zsreg(channel, R1);
+		if (r1 & (PAR_ERR | Rx_OVR | CRC_ERR)) {
+			writeb(ERR_RES, &channel->control);
+			ZSDELAY();
+			ZS_WSYNC(channel);
+		}
+
+		ch = readb(&channel->control);
+		ZSDELAY();
+
+		/* This funny hack depends upon BRK_ABRT not interfering
+		 * with the other bits we care about in R1.
+		 */
+		if (ch & BRK_ABRT)
+			r1 |= BRK_ABRT;
+
+		if (!(ch & Rx_CH_AV))
+			break;
+
+		ch = readb(&channel->data);
+		ZSDELAY();
+
+		ch &= up->parity_mask;
+
+		if (unlikely(ZS_IS_KEYB(up)) || unlikely(ZS_IS_MOUSE(up))) {
+			sunzilog_kbdms_receive_chars(up, ch, 0);
+			continue;
+		}
+
+		if (tty == NULL) {
+			uart_handle_sysrq_char(&up->port, ch);
+			continue;
+		}
+
+		/* A real serial line, record the character and status.  */
+		flag = TTY_NORMAL;
+		up->port.icount.rx++;
+		if (r1 & (BRK_ABRT | PAR_ERR | Rx_OVR | CRC_ERR)) {
+			if (r1 & BRK_ABRT) {
+				r1 &= ~(PAR_ERR | CRC_ERR);
+				up->port.icount.brk++;
+				if (uart_handle_break(&up->port))
+					continue;
+			}
+			else if (r1 & PAR_ERR)
+				up->port.icount.parity++;
+			else if (r1 & CRC_ERR)
+				up->port.icount.frame++;
+			if (r1 & Rx_OVR)
+				up->port.icount.overrun++;
+			r1 &= up->port.read_status_mask;
+			if (r1 & BRK_ABRT)
+				flag = TTY_BREAK;
+			else if (r1 & PAR_ERR)
+				flag = TTY_PARITY;
+			else if (r1 & CRC_ERR)
+				flag = TTY_FRAME;
+		}
+		if (uart_handle_sysrq_char(&up->port, ch))
+			continue;
+
+		if (up->port.ignore_status_mask == 0xff ||
+		    (r1 & up->port.ignore_status_mask) == 0) {
+		    	tty_insert_flip_char(tty, ch, flag);
+		}
+		if (r1 & Rx_OVR)
+			tty_insert_flip_char(tty, 0, TTY_OVERRUN);
+	}
+
+	return tty;
+}
+
+static void sunzilog_status_handle(struct uart_sunzilog_port *up,
+				   struct zilog_channel __iomem *channel)
+{
+	unsigned char status;
+
+	status = readb(&channel->control);
+	ZSDELAY();
+
+	writeb(RES_EXT_INT, &channel->control);
+	ZSDELAY();
+	ZS_WSYNC(channel);
+
+	if (status & BRK_ABRT) {
+		if (ZS_IS_MOUSE(up))
+			sunzilog_kbdms_receive_chars(up, 0, 1);
+		if (ZS_IS_CONS(up)) {
+			/* Wait for BREAK to deassert to avoid potentially
+			 * confusing the PROM.
+			 */
+			while (1) {
+				status = readb(&channel->control);
+				ZSDELAY();
+				if (!(status & BRK_ABRT))
+					break;
+			}
+			sun_do_break();
+			return;
+		}
+	}
+
+	if (ZS_WANTS_MODEM_STATUS(up)) {
+		if (status & SYNC)
+			up->port.icount.dsr++;
+
+		/* The Zilog just gives us an interrupt when DCD/CTS/etc. change.
+		 * But it does not tell us which bit has changed, we have to keep
+		 * track of this ourselves.
+		 */
+		if ((status ^ up->prev_status) ^ DCD)
+			uart_handle_dcd_change(&up->port,
+					       (status & DCD));
+		if ((status ^ up->prev_status) ^ CTS)
+			uart_handle_cts_change(&up->port,
+					       (status & CTS));
+
+		wake_up_interruptible(&up->port.state->port.delta_msr_wait);
+	}
+
+	up->prev_status = status;
+}
+
+static void sunzilog_transmit_chars(struct uart_sunzilog_port *up,
+				    struct zilog_channel __iomem *channel)
+{
+	struct circ_buf *xmit;
+
+	if (ZS_IS_CONS(up)) {
+		unsigned char status = readb(&channel->control);
+		ZSDELAY();
+
+		/* TX still busy?  Just wait for the next TX done interrupt.
+		 *
+		 * It can occur because of how we do serial console writes.  It would
+		 * be nice to transmit console writes just like we normally would for
+		 * a TTY line. (ie. buffered and TX interrupt driven).  That is not
+		 * easy because console writes cannot sleep.  One solution might be
+		 * to poll on enough port->xmit space becoming free.  -DaveM
+		 */
+		if (!(status & Tx_BUF_EMP))
+			return;
+	}
+
+	up->flags &= ~SUNZILOG_FLAG_TX_ACTIVE;
+
+	if (ZS_REGS_HELD(up)) {
+		__load_zsregs(channel, up->curregs);
+		up->flags &= ~SUNZILOG_FLAG_REGS_HELD;
+	}
+
+	if (ZS_TX_STOPPED(up)) {
+		up->flags &= ~SUNZILOG_FLAG_TX_STOPPED;
+		goto ack_tx_int;
+	}
+
+	if (up->port.x_char) {
+		up->flags |= SUNZILOG_FLAG_TX_ACTIVE;
+		writeb(up->port.x_char, &channel->data);
+		ZSDELAY();
+		ZS_WSYNC(channel);
+
+		up->port.icount.tx++;
+		up->port.x_char = 0;
+		return;
+	}
+
+	if (up->port.state == NULL)
+		goto ack_tx_int;
+	xmit = &up->port.state->xmit;
+	if (uart_circ_empty(xmit))
+		goto ack_tx_int;
+
+	if (uart_tx_stopped(&up->port))
+		goto ack_tx_int;
+
+	up->flags |= SUNZILOG_FLAG_TX_ACTIVE;
+	writeb(xmit->buf[xmit->tail], &channel->data);
+	ZSDELAY();
+	ZS_WSYNC(channel);
+
+	xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
+	up->port.icount.tx++;
+
+	if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
+		uart_write_wakeup(&up->port);
+
+	return;
+
+ack_tx_int:
+	writeb(RES_Tx_P, &channel->control);
+	ZSDELAY();
+	ZS_WSYNC(channel);
+}
+
+static irqreturn_t sunzilog_interrupt(int irq, void *dev_id)
+{
+	struct uart_sunzilog_port *up = dev_id;
+
+	while (up) {
+		struct zilog_channel __iomem *channel
+			= ZILOG_CHANNEL_FROM_PORT(&up->port);
+		struct tty_struct *tty;
+		unsigned char r3;
+
+		spin_lock(&up->port.lock);
+		r3 = read_zsreg(channel, R3);
+
+		/* Channel A */
+		tty = NULL;
+		if (r3 & (CHAEXT | CHATxIP | CHARxIP)) {
+			writeb(RES_H_IUS, &channel->control);
+			ZSDELAY();
+			ZS_WSYNC(channel);
+
+			if (r3 & CHARxIP)
+				tty = sunzilog_receive_chars(up, channel);
+			if (r3 & CHAEXT)
+				sunzilog_status_handle(up, channel);
+			if (r3 & CHATxIP)
+				sunzilog_transmit_chars(up, channel);
+		}
+		spin_unlock(&up->port.lock);
+
+		if (tty)
+			tty_flip_buffer_push(tty);
+
+		/* Channel B */
+		up = up->next;
+		channel = ZILOG_CHANNEL_FROM_PORT(&up->port);
+
+		spin_lock(&up->port.lock);
+		tty = NULL;
+		if (r3 & (CHBEXT | CHBTxIP | CHBRxIP)) {
+			writeb(RES_H_IUS, &channel->control);
+			ZSDELAY();
+			ZS_WSYNC(channel);
+
+			if (r3 & CHBRxIP)
+				tty = sunzilog_receive_chars(up, channel);
+			if (r3 & CHBEXT)
+				sunzilog_status_handle(up, channel);
+			if (r3 & CHBTxIP)
+				sunzilog_transmit_chars(up, channel);
+		}
+		spin_unlock(&up->port.lock);
+
+		if (tty)
+			tty_flip_buffer_push(tty);
+
+		up = up->next;
+	}
+
+	return IRQ_HANDLED;
+}
+
+/* A convenient way to quickly get R0 status.  The caller must _not_ hold the
+ * port lock, it is acquired here.
+ */
+static __inline__ unsigned char sunzilog_read_channel_status(struct uart_port *port)
+{
+	struct zilog_channel __iomem *channel;
+	unsigned char status;
+
+	channel = ZILOG_CHANNEL_FROM_PORT(port);
+	status = readb(&channel->control);
+	ZSDELAY();
+
+	return status;
+}
+
+/* The port lock is not held.  */
+static unsigned int sunzilog_tx_empty(struct uart_port *port)
+{
+	unsigned long flags;
+	unsigned char status;
+	unsigned int ret;
+
+	spin_lock_irqsave(&port->lock, flags);
+
+	status = sunzilog_read_channel_status(port);
+
+	spin_unlock_irqrestore(&port->lock, flags);
+
+	if (status & Tx_BUF_EMP)
+		ret = TIOCSER_TEMT;
+	else
+		ret = 0;
+
+	return ret;
+}
+
+/* The port lock is held and interrupts are disabled.  */
+static unsigned int sunzilog_get_mctrl(struct uart_port *port)
+{
+	unsigned char status;
+	unsigned int ret;
+
+	status = sunzilog_read_channel_status(port);
+
+	ret = 0;
+	if (status & DCD)
+		ret |= TIOCM_CAR;
+	if (status & SYNC)
+		ret |= TIOCM_DSR;
+	if (status & CTS)
+		ret |= TIOCM_CTS;
+
+	return ret;
+}
+
+/* The port lock is held and interrupts are disabled.  */
+static void sunzilog_set_mctrl(struct uart_port *port, unsigned int mctrl)
+{
+	struct uart_sunzilog_port *up = (struct uart_sunzilog_port *) port;
+	struct zilog_channel __iomem *channel = ZILOG_CHANNEL_FROM_PORT(port);
+	unsigned char set_bits, clear_bits;
+
+	set_bits = clear_bits = 0;
+
+	if (mctrl & TIOCM_RTS)
+		set_bits |= RTS;
+	else
+		clear_bits |= RTS;
+	if (mctrl & TIOCM_DTR)
+		set_bits |= DTR;
+	else
+		clear_bits |= DTR;
+
+	/* NOTE: Not subject to 'transmitter active' rule.  */ 
+	up->curregs[R5] |= set_bits;
+	up->curregs[R5] &= ~clear_bits;
+	write_zsreg(channel, R5, up->curregs[R5]);
+}
+
+/* The port lock is held and interrupts are disabled.  */
+static void sunzilog_stop_tx(struct uart_port *port)
+{
+	struct uart_sunzilog_port *up = (struct uart_sunzilog_port *) port;
+
+	up->flags |= SUNZILOG_FLAG_TX_STOPPED;
+}
+
+/* The port lock is held and interrupts are disabled.  */
+static void sunzilog_start_tx(struct uart_port *port)
+{
+	struct uart_sunzilog_port *up = (struct uart_sunzilog_port *) port;
+	struct zilog_channel __iomem *channel = ZILOG_CHANNEL_FROM_PORT(port);
+	unsigned char status;
+
+	up->flags |= SUNZILOG_FLAG_TX_ACTIVE;
+	up->flags &= ~SUNZILOG_FLAG_TX_STOPPED;
+
+	status = readb(&channel->control);
+	ZSDELAY();
+
+	/* TX busy?  Just wait for the TX done interrupt.  */
+	if (!(status & Tx_BUF_EMP))
+		return;
+
+	/* Send the first character to jump-start the TX done
+	 * IRQ sending engine.
+	 */
+	if (port->x_char) {
+		writeb(port->x_char, &channel->data);
+		ZSDELAY();
+		ZS_WSYNC(channel);
+
+		port->icount.tx++;
+		port->x_char = 0;
+	} else {
+		struct circ_buf *xmit = &port->state->xmit;
+
+		writeb(xmit->buf[xmit->tail], &channel->data);
+		ZSDELAY();
+		ZS_WSYNC(channel);
+
+		xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
+		port->icount.tx++;
+
+		if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
+			uart_write_wakeup(&up->port);
+	}
+}
+
+/* The port lock is held.  */
+static void sunzilog_stop_rx(struct uart_port *port)
+{
+	struct uart_sunzilog_port *up = UART_ZILOG(port);
+	struct zilog_channel __iomem *channel;
+
+	if (ZS_IS_CONS(up))
+		return;
+
+	channel = ZILOG_CHANNEL_FROM_PORT(port);
+
+	/* Disable all RX interrupts.  */
+	up->curregs[R1] &= ~RxINT_MASK;
+	sunzilog_maybe_update_regs(up, channel);
+}
+
+/* The port lock is held.  */
+static void sunzilog_enable_ms(struct uart_port *port)
+{
+	struct uart_sunzilog_port *up = (struct uart_sunzilog_port *) port;
+	struct zilog_channel __iomem *channel = ZILOG_CHANNEL_FROM_PORT(port);
+	unsigned char new_reg;
+
+	new_reg = up->curregs[R15] | (DCDIE | SYNCIE | CTSIE);
+	if (new_reg != up->curregs[R15]) {
+		up->curregs[R15] = new_reg;
+
+		/* NOTE: Not subject to 'transmitter active' rule.  */ 
+		write_zsreg(channel, R15, up->curregs[R15] & ~WR7pEN);
+	}
+}
+
+/* The port lock is not held.  */
+static void sunzilog_break_ctl(struct uart_port *port, int break_state)
+{
+	struct uart_sunzilog_port *up = (struct uart_sunzilog_port *) port;
+	struct zilog_channel __iomem *channel = ZILOG_CHANNEL_FROM_PORT(port);
+	unsigned char set_bits, clear_bits, new_reg;
+	unsigned long flags;
+
+	set_bits = clear_bits = 0;
+
+	if (break_state)
+		set_bits |= SND_BRK;
+	else
+		clear_bits |= SND_BRK;
+
+	spin_lock_irqsave(&port->lock, flags);
+
+	new_reg = (up->curregs[R5] | set_bits) & ~clear_bits;
+	if (new_reg != up->curregs[R5]) {
+		up->curregs[R5] = new_reg;
+
+		/* NOTE: Not subject to 'transmitter active' rule.  */ 
+		write_zsreg(channel, R5, up->curregs[R5]);
+	}
+
+	spin_unlock_irqrestore(&port->lock, flags);
+}
+
+static void __sunzilog_startup(struct uart_sunzilog_port *up)
+{
+	struct zilog_channel __iomem *channel;
+
+	channel = ZILOG_CHANNEL_FROM_PORT(&up->port);
+	up->prev_status = readb(&channel->control);
+
+	/* Enable receiver and transmitter.  */
+	up->curregs[R3] |= RxENAB;
+	up->curregs[R5] |= TxENAB;
+
+	up->curregs[R1] |= EXT_INT_ENAB | INT_ALL_Rx | TxINT_ENAB;
+	sunzilog_maybe_update_regs(up, channel);
+}
+
+static int sunzilog_startup(struct uart_port *port)
+{
+	struct uart_sunzilog_port *up = UART_ZILOG(port);
+	unsigned long flags;
+
+	if (ZS_IS_CONS(up))
+		return 0;
+
+	spin_lock_irqsave(&port->lock, flags);
+	__sunzilog_startup(up);
+	spin_unlock_irqrestore(&port->lock, flags);
+	return 0;
+}
+
+/*
+ * The test for ZS_IS_CONS is explained by the following e-mail:
+ *****
+ * From: Russell King <rmk@arm.linux.org.uk>
+ * Date: Sun, 8 Dec 2002 10:18:38 +0000
+ *
+ * On Sun, Dec 08, 2002 at 02:43:36AM -0500, Pete Zaitcev wrote:
+ * > I boot my 2.5 boxes using "console=ttyS0,9600" argument,
+ * > and I noticed that something is not right with reference
+ * > counting in this case. It seems that when the console
+ * > is open by kernel initially, this is not accounted
+ * > as an open, and uart_startup is not called.
+ *
+ * That is correct.  We are unable to call uart_startup when the serial
+ * console is initialised because it may need to allocate memory (as
+ * request_irq does) and the memory allocators may not have been
+ * initialised.
+ *
+ * 1. initialise the port into a state where it can send characters in the
+ *    console write method.
+ *
+ * 2. don't do the actual hardware shutdown in your shutdown() method (but
+ *    do the normal software shutdown - ie, free irqs etc)
+ *****
+ */
+static void sunzilog_shutdown(struct uart_port *port)
+{
+	struct uart_sunzilog_port *up = UART_ZILOG(port);
+	struct zilog_channel __iomem *channel;
+	unsigned long flags;
+
+	if (ZS_IS_CONS(up))
+		return;
+
+	spin_lock_irqsave(&port->lock, flags);
+
+	channel = ZILOG_CHANNEL_FROM_PORT(port);
+
+	/* Disable receiver and transmitter.  */
+	up->curregs[R3] &= ~RxENAB;
+	up->curregs[R5] &= ~TxENAB;
+
+	/* Disable all interrupts and BRK assertion.  */
+	up->curregs[R1] &= ~(EXT_INT_ENAB | TxINT_ENAB | RxINT_MASK);
+	up->curregs[R5] &= ~SND_BRK;
+	sunzilog_maybe_update_regs(up, channel);
+
+	spin_unlock_irqrestore(&port->lock, flags);
+}
+
+/* Shared by TTY driver and serial console setup.  The port lock is held
+ * and local interrupts are disabled.
+ */
+static void
+sunzilog_convert_to_zs(struct uart_sunzilog_port *up, unsigned int cflag,
+		       unsigned int iflag, int brg)
+{
+
+	up->curregs[R10] = NRZ;
+	up->curregs[R11] = TCBR | RCBR;
+
+	/* Program BAUD and clock source. */
+	up->curregs[R4] &= ~XCLK_MASK;
+	up->curregs[R4] |= X16CLK;
+	up->curregs[R12] = brg & 0xff;
+	up->curregs[R13] = (brg >> 8) & 0xff;
+	up->curregs[R14] = BRSRC | BRENAB;
+
+	/* Character size, stop bits, and parity. */
+	up->curregs[R3] &= ~RxN_MASK;
+	up->curregs[R5] &= ~TxN_MASK;
+	switch (cflag & CSIZE) {
+	case CS5:
+		up->curregs[R3] |= Rx5;
+		up->curregs[R5] |= Tx5;
+		up->parity_mask = 0x1f;
+		break;
+	case CS6:
+		up->curregs[R3] |= Rx6;
+		up->curregs[R5] |= Tx6;
+		up->parity_mask = 0x3f;
+		break;
+	case CS7:
+		up->curregs[R3] |= Rx7;
+		up->curregs[R5] |= Tx7;
+		up->parity_mask = 0x7f;
+		break;
+	case CS8:
+	default:
+		up->curregs[R3] |= Rx8;
+		up->curregs[R5] |= Tx8;
+		up->parity_mask = 0xff;
+		break;
+	};
+	up->curregs[R4] &= ~0x0c;
+	if (cflag & CSTOPB)
+		up->curregs[R4] |= SB2;
+	else
+		up->curregs[R4] |= SB1;
+	if (cflag & PARENB)
+		up->curregs[R4] |= PAR_ENAB;
+	else
+		up->curregs[R4] &= ~PAR_ENAB;
+	if (!(cflag & PARODD))
+		up->curregs[R4] |= PAR_EVEN;
+	else
+		up->curregs[R4] &= ~PAR_EVEN;
+
+	up->port.read_status_mask = Rx_OVR;
+	if (iflag & INPCK)
+		up->port.read_status_mask |= CRC_ERR | PAR_ERR;
+	if (iflag & (BRKINT | PARMRK))
+		up->port.read_status_mask |= BRK_ABRT;
+
+	up->port.ignore_status_mask = 0;
+	if (iflag & IGNPAR)
+		up->port.ignore_status_mask |= CRC_ERR | PAR_ERR;
+	if (iflag & IGNBRK) {
+		up->port.ignore_status_mask |= BRK_ABRT;
+		if (iflag & IGNPAR)
+			up->port.ignore_status_mask |= Rx_OVR;
+	}
+
+	if ((cflag & CREAD) == 0)
+		up->port.ignore_status_mask = 0xff;
+}
+
+/* The port lock is not held.  */
+static void
+sunzilog_set_termios(struct uart_port *port, struct ktermios *termios,
+		     struct ktermios *old)
+{
+	struct uart_sunzilog_port *up = (struct uart_sunzilog_port *) port;
+	unsigned long flags;
+	int baud, brg;
+
+	baud = uart_get_baud_rate(port, termios, old, 1200, 76800);
+
+	spin_lock_irqsave(&up->port.lock, flags);
+
+	brg = BPS_TO_BRG(baud, ZS_CLOCK / ZS_CLOCK_DIVISOR);
+
+	sunzilog_convert_to_zs(up, termios->c_cflag, termios->c_iflag, brg);
+
+	if (UART_ENABLE_MS(&up->port, termios->c_cflag))
+		up->flags |= SUNZILOG_FLAG_MODEM_STATUS;
+	else
+		up->flags &= ~SUNZILOG_FLAG_MODEM_STATUS;
+
+	up->cflag = termios->c_cflag;
+
+	sunzilog_maybe_update_regs(up, ZILOG_CHANNEL_FROM_PORT(port));
+
+	uart_update_timeout(port, termios->c_cflag, baud);
+
+	spin_unlock_irqrestore(&up->port.lock, flags);
+}
+
+static const char *sunzilog_type(struct uart_port *port)
+{
+	struct uart_sunzilog_port *up = UART_ZILOG(port);
+
+	return (up->flags & SUNZILOG_FLAG_ESCC) ? "zs (ESCC)" : "zs";
+}
+
+/* We do not request/release mappings of the registers here, this
+ * happens at early serial probe time.
+ */
+static void sunzilog_release_port(struct uart_port *port)
+{
+}
+
+static int sunzilog_request_port(struct uart_port *port)
+{
+	return 0;
+}
+
+/* These do not need to do anything interesting either.  */
+static void sunzilog_config_port(struct uart_port *port, int flags)
+{
+}
+
+/* We do not support letting the user mess with the divisor, IRQ, etc. */
+static int sunzilog_verify_port(struct uart_port *port, struct serial_struct *ser)
+{
+	return -EINVAL;
+}
+
+#ifdef CONFIG_CONSOLE_POLL
+static int sunzilog_get_poll_char(struct uart_port *port)
+{
+	unsigned char ch, r1;
+	struct uart_sunzilog_port *up = (struct uart_sunzilog_port *) port;
+	struct zilog_channel __iomem *channel
+		= ZILOG_CHANNEL_FROM_PORT(&up->port);
+
+
+	r1 = read_zsreg(channel, R1);
+	if (r1 & (PAR_ERR | Rx_OVR | CRC_ERR)) {
+		writeb(ERR_RES, &channel->control);
+		ZSDELAY();
+		ZS_WSYNC(channel);
+	}
+
+	ch = readb(&channel->control);
+	ZSDELAY();
+
+	/* This funny hack depends upon BRK_ABRT not interfering
+	 * with the other bits we care about in R1.
+	 */
+	if (ch & BRK_ABRT)
+		r1 |= BRK_ABRT;
+
+	if (!(ch & Rx_CH_AV))
+		return NO_POLL_CHAR;
+
+	ch = readb(&channel->data);
+	ZSDELAY();
+
+	ch &= up->parity_mask;
+	return ch;
+}
+
+static void sunzilog_put_poll_char(struct uart_port *port,
+			unsigned char ch)
+{
+	struct uart_sunzilog_port *up = (struct uart_sunzilog_port *)port;
+
+	sunzilog_putchar(&up->port, ch);
+}
+#endif /* CONFIG_CONSOLE_POLL */
+
+static struct uart_ops sunzilog_pops = {
+	.tx_empty	=	sunzilog_tx_empty,
+	.set_mctrl	=	sunzilog_set_mctrl,
+	.get_mctrl	=	sunzilog_get_mctrl,
+	.stop_tx	=	sunzilog_stop_tx,
+	.start_tx	=	sunzilog_start_tx,
+	.stop_rx	=	sunzilog_stop_rx,
+	.enable_ms	=	sunzilog_enable_ms,
+	.break_ctl	=	sunzilog_break_ctl,
+	.startup	=	sunzilog_startup,
+	.shutdown	=	sunzilog_shutdown,
+	.set_termios	=	sunzilog_set_termios,
+	.type		=	sunzilog_type,
+	.release_port	=	sunzilog_release_port,
+	.request_port	=	sunzilog_request_port,
+	.config_port	=	sunzilog_config_port,
+	.verify_port	=	sunzilog_verify_port,
+#ifdef CONFIG_CONSOLE_POLL
+	.poll_get_char	=	sunzilog_get_poll_char,
+	.poll_put_char	=	sunzilog_put_poll_char,
+#endif
+};
+
+static int uart_chip_count;
+static struct uart_sunzilog_port *sunzilog_port_table;
+static struct zilog_layout __iomem **sunzilog_chip_regs;
+
+static struct uart_sunzilog_port *sunzilog_irq_chain;
+
+static struct uart_driver sunzilog_reg = {
+	.owner		=	THIS_MODULE,
+	.driver_name	=	"sunzilog",
+	.dev_name	=	"ttyS",
+	.major		=	TTY_MAJOR,
+};
+
+static int __init sunzilog_alloc_tables(int num_sunzilog)
+{
+	struct uart_sunzilog_port *up;
+	unsigned long size;
+	int num_channels = num_sunzilog * 2;
+	int i;
+
+	size = num_channels * sizeof(struct uart_sunzilog_port);
+	sunzilog_port_table = kzalloc(size, GFP_KERNEL);
+	if (!sunzilog_port_table)
+		return -ENOMEM;
+
+	for (i = 0; i < num_channels; i++) {
+		up = &sunzilog_port_table[i];
+
+		spin_lock_init(&up->port.lock);
+
+		if (i == 0)
+			sunzilog_irq_chain = up;
+
+		if (i < num_channels - 1)
+			up->next = up + 1;
+		else
+			up->next = NULL;
+	}
+
+	size = num_sunzilog * sizeof(struct zilog_layout __iomem *);
+	sunzilog_chip_regs = kzalloc(size, GFP_KERNEL);
+	if (!sunzilog_chip_regs) {
+		kfree(sunzilog_port_table);
+		sunzilog_irq_chain = NULL;
+		return -ENOMEM;
+	}
+
+	return 0;
+}
+
+static void sunzilog_free_tables(void)
+{
+	kfree(sunzilog_port_table);
+	sunzilog_irq_chain = NULL;
+	kfree(sunzilog_chip_regs);
+}
+
+#define ZS_PUT_CHAR_MAX_DELAY	2000	/* 10 ms */
+
+static void sunzilog_putchar(struct uart_port *port, int ch)
+{
+	struct zilog_channel __iomem *channel = ZILOG_CHANNEL_FROM_PORT(port);
+	int loops = ZS_PUT_CHAR_MAX_DELAY;
+
+	/* This is a timed polling loop so do not switch the explicit
+	 * udelay with ZSDELAY as that is a NOP on some platforms.  -DaveM
+	 */
+	do {
+		unsigned char val = readb(&channel->control);
+		if (val & Tx_BUF_EMP) {
+			ZSDELAY();
+			break;
+		}
+		udelay(5);
+	} while (--loops);
+
+	writeb(ch, &channel->data);
+	ZSDELAY();
+	ZS_WSYNC(channel);
+}
+
+#ifdef CONFIG_SERIO
+
+static DEFINE_SPINLOCK(sunzilog_serio_lock);
+
+static int sunzilog_serio_write(struct serio *serio, unsigned char ch)
+{
+	struct uart_sunzilog_port *up = serio->port_data;
+	unsigned long flags;
+
+	spin_lock_irqsave(&sunzilog_serio_lock, flags);
+
+	sunzilog_putchar(&up->port, ch);
+
+	spin_unlock_irqrestore(&sunzilog_serio_lock, flags);
+
+	return 0;
+}
+
+static int sunzilog_serio_open(struct serio *serio)
+{
+	struct uart_sunzilog_port *up = serio->port_data;
+	unsigned long flags;
+	int ret;
+
+	spin_lock_irqsave(&sunzilog_serio_lock, flags);
+	if (!up->serio_open) {
+		up->serio_open = 1;
+		ret = 0;
+	} else
+		ret = -EBUSY;
+	spin_unlock_irqrestore(&sunzilog_serio_lock, flags);
+
+	return ret;
+}
+
+static void sunzilog_serio_close(struct serio *serio)
+{
+	struct uart_sunzilog_port *up = serio->port_data;
+	unsigned long flags;
+
+	spin_lock_irqsave(&sunzilog_serio_lock, flags);
+	up->serio_open = 0;
+	spin_unlock_irqrestore(&sunzilog_serio_lock, flags);
+}
+
+#endif /* CONFIG_SERIO */
+
+#ifdef CONFIG_SERIAL_SUNZILOG_CONSOLE
+static void
+sunzilog_console_write(struct console *con, const char *s, unsigned int count)
+{
+	struct uart_sunzilog_port *up = &sunzilog_port_table[con->index];
+	unsigned long flags;
+	int locked = 1;
+
+	local_irq_save(flags);
+	if (up->port.sysrq) {
+		locked = 0;
+	} else if (oops_in_progress) {
+		locked = spin_trylock(&up->port.lock);
+	} else
+		spin_lock(&up->port.lock);
+
+	uart_console_write(&up->port, s, count, sunzilog_putchar);
+	udelay(2);
+
+	if (locked)
+		spin_unlock(&up->port.lock);
+	local_irq_restore(flags);
+}
+
+static int __init sunzilog_console_setup(struct console *con, char *options)
+{
+	struct uart_sunzilog_port *up = &sunzilog_port_table[con->index];
+	unsigned long flags;
+	int baud, brg;
+
+	if (up->port.type != PORT_SUNZILOG)
+		return -1;
+
+	printk(KERN_INFO "Console: ttyS%d (SunZilog zs%d)\n",
+	       (sunzilog_reg.minor - 64) + con->index, con->index);
+
+	/* Get firmware console settings.  */
+	sunserial_console_termios(con, up->port.dev->of_node);
+
+	/* Firmware console speed is limited to 150-->38400 baud so
+	 * this hackish cflag thing is OK.
+	 */
+	switch (con->cflag & CBAUD) {
+	case B150: baud = 150; break;
+	case B300: baud = 300; break;
+	case B600: baud = 600; break;
+	case B1200: baud = 1200; break;
+	case B2400: baud = 2400; break;
+	case B4800: baud = 4800; break;
+	default: case B9600: baud = 9600; break;
+	case B19200: baud = 19200; break;
+	case B38400: baud = 38400; break;
+	};
+
+	brg = BPS_TO_BRG(baud, ZS_CLOCK / ZS_CLOCK_DIVISOR);
+
+	spin_lock_irqsave(&up->port.lock, flags);
+
+	up->curregs[R15] |= BRKIE;
+	sunzilog_convert_to_zs(up, con->cflag, 0, brg);
+
+	sunzilog_set_mctrl(&up->port, TIOCM_DTR | TIOCM_RTS);
+	__sunzilog_startup(up);
+
+	spin_unlock_irqrestore(&up->port.lock, flags);
+
+	return 0;
+}
+
+static struct console sunzilog_console_ops = {
+	.name	=	"ttyS",
+	.write	=	sunzilog_console_write,
+	.device	=	uart_console_device,
+	.setup	=	sunzilog_console_setup,
+	.flags	=	CON_PRINTBUFFER,
+	.index	=	-1,
+	.data   =	&sunzilog_reg,
+};
+
+static inline struct console *SUNZILOG_CONSOLE(void)
+{
+	return &sunzilog_console_ops;
+}
+
+#else
+#define SUNZILOG_CONSOLE()	(NULL)
+#endif
+
+static void __devinit sunzilog_init_kbdms(struct uart_sunzilog_port *up)
+{
+	int baud, brg;
+
+	if (up->flags & SUNZILOG_FLAG_CONS_KEYB) {
+		up->cflag = B1200 | CS8 | CLOCAL | CREAD;
+		baud = 1200;
+	} else {
+		up->cflag = B4800 | CS8 | CLOCAL | CREAD;
+		baud = 4800;
+	}
+
+	up->curregs[R15] |= BRKIE;
+	brg = BPS_TO_BRG(baud, ZS_CLOCK / ZS_CLOCK_DIVISOR);
+	sunzilog_convert_to_zs(up, up->cflag, 0, brg);
+	sunzilog_set_mctrl(&up->port, TIOCM_DTR | TIOCM_RTS);
+	__sunzilog_startup(up);
+}
+
+#ifdef CONFIG_SERIO
+static void __devinit sunzilog_register_serio(struct uart_sunzilog_port *up)
+{
+	struct serio *serio = &up->serio;
+
+	serio->port_data = up;
+
+	serio->id.type = SERIO_RS232;
+	if (up->flags & SUNZILOG_FLAG_CONS_KEYB) {
+		serio->id.proto = SERIO_SUNKBD;
+		strlcpy(serio->name, "zskbd", sizeof(serio->name));
+	} else {
+		serio->id.proto = SERIO_SUN;
+		serio->id.extra = 1;
+		strlcpy(serio->name, "zsms", sizeof(serio->name));
+	}
+	strlcpy(serio->phys,
+		((up->flags & SUNZILOG_FLAG_CONS_KEYB) ?
+		 "zs/serio0" : "zs/serio1"),
+		sizeof(serio->phys));
+
+	serio->write = sunzilog_serio_write;
+	serio->open = sunzilog_serio_open;
+	serio->close = sunzilog_serio_close;
+	serio->dev.parent = up->port.dev;
+
+	serio_register_port(serio);
+}
+#endif
+
+static void __devinit sunzilog_init_hw(struct uart_sunzilog_port *up)
+{
+	struct zilog_channel __iomem *channel;
+	unsigned long flags;
+	int baud, brg;
+
+	channel = ZILOG_CHANNEL_FROM_PORT(&up->port);
+
+	spin_lock_irqsave(&up->port.lock, flags);
+	if (ZS_IS_CHANNEL_A(up)) {
+		write_zsreg(channel, R9, FHWRES);
+		ZSDELAY_LONG();
+		(void) read_zsreg(channel, R0);
+	}
+
+	if (up->flags & (SUNZILOG_FLAG_CONS_KEYB |
+			 SUNZILOG_FLAG_CONS_MOUSE)) {
+		up->curregs[R1] = EXT_INT_ENAB | INT_ALL_Rx | TxINT_ENAB;
+		up->curregs[R4] = PAR_EVEN | X16CLK | SB1;
+		up->curregs[R3] = RxENAB | Rx8;
+		up->curregs[R5] = TxENAB | Tx8;
+		up->curregs[R6] = 0x00; /* SDLC Address */
+		up->curregs[R7] = 0x7E; /* SDLC Flag    */
+		up->curregs[R9] = NV;
+		up->curregs[R7p] = 0x00;
+		sunzilog_init_kbdms(up);
+		/* Only enable interrupts if an ISR handler available */
+		if (up->flags & SUNZILOG_FLAG_ISR_HANDLER)
+			up->curregs[R9] |= MIE;
+		write_zsreg(channel, R9, up->curregs[R9]);
+	} else {
+		/* Normal serial TTY. */
+		up->parity_mask = 0xff;
+		up->curregs[R1] = EXT_INT_ENAB | INT_ALL_Rx | TxINT_ENAB;
+		up->curregs[R4] = PAR_EVEN | X16CLK | SB1;
+		up->curregs[R3] = RxENAB | Rx8;
+		up->curregs[R5] = TxENAB | Tx8;
+		up->curregs[R6] = 0x00; /* SDLC Address */
+		up->curregs[R7] = 0x7E; /* SDLC Flag    */
+		up->curregs[R9] = NV;
+		up->curregs[R10] = NRZ;
+		up->curregs[R11] = TCBR | RCBR;
+		baud = 9600;
+		brg = BPS_TO_BRG(baud, ZS_CLOCK / ZS_CLOCK_DIVISOR);
+		up->curregs[R12] = (brg & 0xff);
+		up->curregs[R13] = (brg >> 8) & 0xff;
+		up->curregs[R14] = BRSRC | BRENAB;
+		up->curregs[R15] = FIFOEN; /* Use FIFO if on ESCC */
+		up->curregs[R7p] = TxFIFO_LVL | RxFIFO_LVL;
+		if (__load_zsregs(channel, up->curregs)) {
+			up->flags |= SUNZILOG_FLAG_ESCC;
+		}
+		/* Only enable interrupts if an ISR handler available */
+		if (up->flags & SUNZILOG_FLAG_ISR_HANDLER)
+			up->curregs[R9] |= MIE;
+		write_zsreg(channel, R9, up->curregs[R9]);
+	}
+
+	spin_unlock_irqrestore(&up->port.lock, flags);
+
+#ifdef CONFIG_SERIO
+	if (up->flags & (SUNZILOG_FLAG_CONS_KEYB |
+			 SUNZILOG_FLAG_CONS_MOUSE))
+		sunzilog_register_serio(up);
+#endif
+}
+
+static int zilog_irq;
+
+static int __devinit zs_probe(struct platform_device *op)
+{
+	static int kbm_inst, uart_inst;
+	int inst;
+	struct uart_sunzilog_port *up;
+	struct zilog_layout __iomem *rp;
+	int keyboard_mouse = 0;
+	int err;
+
+	if (of_find_property(op->dev.of_node, "keyboard", NULL))
+		keyboard_mouse = 1;
+
+	/* uarts must come before keyboards/mice */
+	if (keyboard_mouse)
+		inst = uart_chip_count + kbm_inst;
+	else
+		inst = uart_inst;
+
+	sunzilog_chip_regs[inst] = of_ioremap(&op->resource[0], 0,
+					      sizeof(struct zilog_layout),
+					      "zs");
+	if (!sunzilog_chip_regs[inst])
+		return -ENOMEM;
+
+	rp = sunzilog_chip_regs[inst];
+
+	if (!zilog_irq)
+		zilog_irq = op->archdata.irqs[0];
+
+	up = &sunzilog_port_table[inst * 2];
+
+	/* Channel A */
+	up[0].port.mapbase = op->resource[0].start + 0x00;
+	up[0].port.membase = (void __iomem *) &rp->channelA;
+	up[0].port.iotype = UPIO_MEM;
+	up[0].port.irq = op->archdata.irqs[0];
+	up[0].port.uartclk = ZS_CLOCK;
+	up[0].port.fifosize = 1;
+	up[0].port.ops = &sunzilog_pops;
+	up[0].port.type = PORT_SUNZILOG;
+	up[0].port.flags = 0;
+	up[0].port.line = (inst * 2) + 0;
+	up[0].port.dev = &op->dev;
+	up[0].flags |= SUNZILOG_FLAG_IS_CHANNEL_A;
+	if (keyboard_mouse)
+		up[0].flags |= SUNZILOG_FLAG_CONS_KEYB;
+	sunzilog_init_hw(&up[0]);
+
+	/* Channel B */
+	up[1].port.mapbase = op->resource[0].start + 0x04;
+	up[1].port.membase = (void __iomem *) &rp->channelB;
+	up[1].port.iotype = UPIO_MEM;
+	up[1].port.irq = op->archdata.irqs[0];
+	up[1].port.uartclk = ZS_CLOCK;
+	up[1].port.fifosize = 1;
+	up[1].port.ops = &sunzilog_pops;
+	up[1].port.type = PORT_SUNZILOG;
+	up[1].port.flags = 0;
+	up[1].port.line = (inst * 2) + 1;
+	up[1].port.dev = &op->dev;
+	up[1].flags |= 0;
+	if (keyboard_mouse)
+		up[1].flags |= SUNZILOG_FLAG_CONS_MOUSE;
+	sunzilog_init_hw(&up[1]);
+
+	if (!keyboard_mouse) {
+		if (sunserial_console_match(SUNZILOG_CONSOLE(), op->dev.of_node,
+					    &sunzilog_reg, up[0].port.line,
+					    false))
+			up->flags |= SUNZILOG_FLAG_IS_CONS;
+		err = uart_add_one_port(&sunzilog_reg, &up[0].port);
+		if (err) {
+			of_iounmap(&op->resource[0],
+				   rp, sizeof(struct zilog_layout));
+			return err;
+		}
+		if (sunserial_console_match(SUNZILOG_CONSOLE(), op->dev.of_node,
+					    &sunzilog_reg, up[1].port.line,
+					    false))
+			up->flags |= SUNZILOG_FLAG_IS_CONS;
+		err = uart_add_one_port(&sunzilog_reg, &up[1].port);
+		if (err) {
+			uart_remove_one_port(&sunzilog_reg, &up[0].port);
+			of_iounmap(&op->resource[0],
+				   rp, sizeof(struct zilog_layout));
+			return err;
+		}
+		uart_inst++;
+	} else {
+		printk(KERN_INFO "%s: Keyboard at MMIO 0x%llx (irq = %d) "
+		       "is a %s\n",
+		       dev_name(&op->dev),
+		       (unsigned long long) up[0].port.mapbase,
+		       op->archdata.irqs[0], sunzilog_type(&up[0].port));
+		printk(KERN_INFO "%s: Mouse at MMIO 0x%llx (irq = %d) "
+		       "is a %s\n",
+		       dev_name(&op->dev),
+		       (unsigned long long) up[1].port.mapbase,
+		       op->archdata.irqs[0], sunzilog_type(&up[1].port));
+		kbm_inst++;
+	}
+
+	dev_set_drvdata(&op->dev, &up[0]);
+
+	return 0;
+}
+
+static void __devexit zs_remove_one(struct uart_sunzilog_port *up)
+{
+	if (ZS_IS_KEYB(up) || ZS_IS_MOUSE(up)) {
+#ifdef CONFIG_SERIO
+		serio_unregister_port(&up->serio);
+#endif
+	} else
+		uart_remove_one_port(&sunzilog_reg, &up->port);
+}
+
+static int __devexit zs_remove(struct platform_device *op)
+{
+	struct uart_sunzilog_port *up = dev_get_drvdata(&op->dev);
+	struct zilog_layout __iomem *regs;
+
+	zs_remove_one(&up[0]);
+	zs_remove_one(&up[1]);
+
+	regs = sunzilog_chip_regs[up[0].port.line / 2];
+	of_iounmap(&op->resource[0], regs, sizeof(struct zilog_layout));
+
+	dev_set_drvdata(&op->dev, NULL);
+
+	return 0;
+}
+
+static const struct of_device_id zs_match[] = {
+	{
+		.name = "zs",
+	},
+	{},
+};
+MODULE_DEVICE_TABLE(of, zs_match);
+
+static struct platform_driver zs_driver = {
+	.driver = {
+		.name = "zs",
+		.owner = THIS_MODULE,
+		.of_match_table = zs_match,
+	},
+	.probe		= zs_probe,
+	.remove		= __devexit_p(zs_remove),
+};
+
+static int __init sunzilog_init(void)
+{
+	struct device_node *dp;
+	int err;
+	int num_keybms = 0;
+	int num_sunzilog = 0;
+
+	for_each_node_by_name(dp, "zs") {
+		num_sunzilog++;
+		if (of_find_property(dp, "keyboard", NULL))
+			num_keybms++;
+	}
+
+	if (num_sunzilog) {
+		err = sunzilog_alloc_tables(num_sunzilog);
+		if (err)
+			goto out;
+
+		uart_chip_count = num_sunzilog - num_keybms;
+
+		err = sunserial_register_minors(&sunzilog_reg,
+						uart_chip_count * 2);
+		if (err)
+			goto out_free_tables;
+	}
+
+	err = platform_driver_register(&zs_driver);
+	if (err)
+		goto out_unregister_uart;
+
+	if (zilog_irq) {
+		struct uart_sunzilog_port *up = sunzilog_irq_chain;
+		err = request_irq(zilog_irq, sunzilog_interrupt, IRQF_SHARED,
+				  "zs", sunzilog_irq_chain);
+		if (err)
+			goto out_unregister_driver;
+
+		/* Enable Interrupts */
+		while (up) {
+			struct zilog_channel __iomem *channel;
+
+			/* printk (KERN_INFO "Enable IRQ for ZILOG Hardware %p\n", up); */
+			channel          = ZILOG_CHANNEL_FROM_PORT(&up->port);
+			up->flags       |= SUNZILOG_FLAG_ISR_HANDLER;
+			up->curregs[R9] |= MIE;
+			write_zsreg(channel, R9, up->curregs[R9]);
+			up = up->next;
+		}
+	}
+
+out:
+	return err;
+
+out_unregister_driver:
+	platform_driver_unregister(&zs_driver);
+
+out_unregister_uart:
+	if (num_sunzilog) {
+		sunserial_unregister_minors(&sunzilog_reg, num_sunzilog);
+		sunzilog_reg.cons = NULL;
+	}
+
+out_free_tables:
+	sunzilog_free_tables();
+	goto out;
+}
+
+static void __exit sunzilog_exit(void)
+{
+	platform_driver_unregister(&zs_driver);
+
+	if (zilog_irq) {
+		struct uart_sunzilog_port *up = sunzilog_irq_chain;
+
+		/* Disable Interrupts */
+		while (up) {
+			struct zilog_channel __iomem *channel;
+
+			/* printk (KERN_INFO "Disable IRQ for ZILOG Hardware %p\n", up); */
+			channel          = ZILOG_CHANNEL_FROM_PORT(&up->port);
+			up->flags       &= ~SUNZILOG_FLAG_ISR_HANDLER;
+			up->curregs[R9] &= ~MIE;
+			write_zsreg(channel, R9, up->curregs[R9]);
+			up = up->next;
+		}
+
+		free_irq(zilog_irq, sunzilog_irq_chain);
+		zilog_irq = 0;
+	}
+
+	if (sunzilog_reg.nr) {
+		sunserial_unregister_minors(&sunzilog_reg, sunzilog_reg.nr);
+		sunzilog_free_tables();
+	}
+}
+
+module_init(sunzilog_init);
+module_exit(sunzilog_exit);
+
+MODULE_AUTHOR("David S. Miller");
+MODULE_DESCRIPTION("Sun Zilog serial port driver");
+MODULE_VERSION("2.0");
+MODULE_LICENSE("GPL");
diff --git a/ap/os/linux/linux-3.4.x/drivers/tty/serial/sunzilog.h b/ap/os/linux/linux-3.4.x/drivers/tty/serial/sunzilog.h
new file mode 100644
index 0000000..5dec7b4
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/tty/serial/sunzilog.h
@@ -0,0 +1,289 @@
+#ifndef _SUNZILOG_H
+#define _SUNZILOG_H
+
+struct zilog_channel {
+	volatile unsigned char control;
+	volatile unsigned char __pad1;
+	volatile unsigned char data;
+	volatile unsigned char __pad2;
+};
+
+struct zilog_layout {
+	struct zilog_channel channelB;
+	struct zilog_channel channelA;
+};
+
+#define	NUM_ZSREGS	17
+#define	R7p		16 /* Written as R7 with P15 bit 0 set */
+
+/* Conversion routines to/from brg time constants from/to bits
+ * per second.
+ */
+#define BRG_TO_BPS(brg, freq) ((freq) / 2 / ((brg) + 2))
+#define BPS_TO_BRG(bps, freq) ((((freq) + (bps)) / (2 * (bps))) - 2)
+
+/* The Zilog register set */
+
+#define	FLAG	0x7e
+
+/* Write Register 0 */
+#define	R0	0		/* Register selects */
+#define	R1	1
+#define	R2	2
+#define	R3	3
+#define	R4	4
+#define	R5	5
+#define	R6	6
+#define	R7	7
+#define	R8	8
+#define	R9	9
+#define	R10	10
+#define	R11	11
+#define	R12	12
+#define	R13	13
+#define	R14	14
+#define	R15	15
+
+#define	NULLCODE	0	/* Null Code */
+#define	POINT_HIGH	0x8	/* Select upper half of registers */
+#define	RES_EXT_INT	0x10	/* Reset Ext. Status Interrupts */
+#define	SEND_ABORT	0x18	/* HDLC Abort */
+#define	RES_RxINT_FC	0x20	/* Reset RxINT on First Character */
+#define	RES_Tx_P	0x28	/* Reset TxINT Pending */
+#define	ERR_RES		0x30	/* Error Reset */
+#define	RES_H_IUS	0x38	/* Reset highest IUS */
+
+#define	RES_Rx_CRC	0x40	/* Reset Rx CRC Checker */
+#define	RES_Tx_CRC	0x80	/* Reset Tx CRC Checker */
+#define	RES_EOM_L	0xC0	/* Reset EOM latch */
+
+/* Write Register 1 */
+
+#define	EXT_INT_ENAB	0x1	/* Ext Int Enable */
+#define	TxINT_ENAB	0x2	/* Tx Int Enable */
+#define	PAR_SPEC	0x4	/* Parity is special condition */
+
+#define	RxINT_DISAB	0	/* Rx Int Disable */
+#define	RxINT_FCERR	0x8	/* Rx Int on First Character Only or Error */
+#define	INT_ALL_Rx	0x10	/* Int on all Rx Characters or error */
+#define	INT_ERR_Rx	0x18	/* Int on error only */
+#define RxINT_MASK	0x18
+
+#define	WT_RDY_RT	0x20	/* Wait/Ready on R/T */
+#define	WT_FN_RDYFN	0x40	/* Wait/FN/Ready FN */
+#define	WT_RDY_ENAB	0x80	/* Wait/Ready Enable */
+
+/* Write Register #2 (Interrupt Vector) */
+
+/* Write Register 3 */
+
+#define	RxENAB  	0x1	/* Rx Enable */
+#define	SYNC_L_INH	0x2	/* Sync Character Load Inhibit */
+#define	ADD_SM		0x4	/* Address Search Mode (SDLC) */
+#define	RxCRC_ENAB	0x8	/* Rx CRC Enable */
+#define	ENT_HM		0x10	/* Enter Hunt Mode */
+#define	AUTO_ENAB	0x20	/* Auto Enables */
+#define	Rx5		0x0	/* Rx 5 Bits/Character */
+#define	Rx7		0x40	/* Rx 7 Bits/Character */
+#define	Rx6		0x80	/* Rx 6 Bits/Character */
+#define	Rx8		0xc0	/* Rx 8 Bits/Character */
+#define RxN_MASK	0xc0
+
+/* Write Register 4 */
+
+#define	PAR_ENAB	0x1	/* Parity Enable */
+#define	PAR_EVEN	0x2	/* Parity Even/Odd* */
+
+#define	SYNC_ENAB	0	/* Sync Modes Enable */
+#define	SB1		0x4	/* 1 stop bit/char */
+#define	SB15		0x8	/* 1.5 stop bits/char */
+#define	SB2		0xc	/* 2 stop bits/char */
+
+#define	MONSYNC		0	/* 8 Bit Sync character */
+#define	BISYNC		0x10	/* 16 bit sync character */
+#define	SDLC		0x20	/* SDLC Mode (01111110 Sync Flag) */
+#define	EXTSYNC		0x30	/* External Sync Mode */
+
+#define	X1CLK		0x0	/* x1 clock mode */
+#define	X16CLK		0x40	/* x16 clock mode */
+#define	X32CLK		0x80	/* x32 clock mode */
+#define	X64CLK		0xC0	/* x64 clock mode */
+#define XCLK_MASK	0xC0
+
+/* Write Register 5 */
+
+#define	TxCRC_ENAB	0x1	/* Tx CRC Enable */
+#define	RTS		0x2	/* RTS */
+#define	SDLC_CRC	0x4	/* SDLC/CRC-16 */
+#define	TxENAB		0x8	/* Tx Enable */
+#define	SND_BRK		0x10	/* Send Break */
+#define	Tx5		0x0	/* Tx 5 bits (or less)/character */
+#define	Tx7		0x20	/* Tx 7 bits/character */
+#define	Tx6		0x40	/* Tx 6 bits/character */
+#define	Tx8		0x60	/* Tx 8 bits/character */
+#define TxN_MASK	0x60
+#define	DTR		0x80	/* DTR */
+
+/* Write Register 6 (Sync bits 0-7/SDLC Address Field) */
+
+/* Write Register 7 (Sync bits 8-15/SDLC 01111110) */
+
+/* Write Register 7' (ESCC Only) */
+#define	AUTO_TxFLAG	1	/* Automatic Tx SDLC Flag */
+#define	AUTO_EOM_RST	2	/* Automatic EOM Reset */
+#define	AUTOnRTS	4	/* Automatic /RTS pin deactivation */
+#define	RxFIFO_LVL	8	/* Receive FIFO interrupt level */
+#define	nDTRnREQ	0x10	/* /DTR/REQ timing */
+#define	TxFIFO_LVL	0x20	/* Transmit FIFO interrupt level */
+#define	EXT_RD_EN	0x40	/* Extended read register enable */
+
+/* Write Register 8 (transmit buffer) */
+
+/* Write Register 9 (Master interrupt control) */
+#define	VIS	1	/* Vector Includes Status */
+#define	NV	2	/* No Vector */
+#define	DLC	4	/* Disable Lower Chain */
+#define	MIE	8	/* Master Interrupt Enable */
+#define	STATHI	0x10	/* Status high */
+#define	SWIACK  0x20    /* Software Interrupt Ack (not on NMOS) */
+#define	NORESET	0	/* No reset on write to R9 */
+#define	CHRB	0x40	/* Reset channel B */
+#define	CHRA	0x80	/* Reset channel A */
+#define	FHWRES	0xc0	/* Force hardware reset */
+
+/* Write Register 10 (misc control bits) */
+#define	BIT6	1	/* 6 bit/8bit sync */
+#define	LOOPMODE 2	/* SDLC Loop mode */
+#define	ABUNDER	4	/* Abort/flag on SDLC xmit underrun */
+#define	MARKIDLE 8	/* Mark/flag on idle */
+#define	GAOP	0x10	/* Go active on poll */
+#define	NRZ	0	/* NRZ mode */
+#define	NRZI	0x20	/* NRZI mode */
+#define	FM1	0x40	/* FM1 (transition = 1) */
+#define	FM0	0x60	/* FM0 (transition = 0) */
+#define	CRCPS	0x80	/* CRC Preset I/O */
+
+/* Write Register 11 (Clock Mode control) */
+#define	TRxCXT	0	/* TRxC = Xtal output */
+#define	TRxCTC	1	/* TRxC = Transmit clock */
+#define	TRxCBR	2	/* TRxC = BR Generator Output */
+#define	TRxCDP	3	/* TRxC = DPLL output */
+#define	TRxCOI	4	/* TRxC O/I */
+#define	TCRTxCP	0	/* Transmit clock = RTxC pin */
+#define	TCTRxCP	8	/* Transmit clock = TRxC pin */
+#define	TCBR	0x10	/* Transmit clock = BR Generator output */
+#define	TCDPLL	0x18	/* Transmit clock = DPLL output */
+#define	RCRTxCP	0	/* Receive clock = RTxC pin */
+#define	RCTRxCP	0x20	/* Receive clock = TRxC pin */
+#define	RCBR	0x40	/* Receive clock = BR Generator output */
+#define	RCDPLL	0x60	/* Receive clock = DPLL output */
+#define	RTxCX	0x80	/* RTxC Xtal/No Xtal */
+
+/* Write Register 12 (lower byte of baud rate generator time constant) */
+
+/* Write Register 13 (upper byte of baud rate generator time constant) */
+
+/* Write Register 14 (Misc control bits) */
+#define	BRENAB 	1	/* Baud rate generator enable */
+#define	BRSRC	2	/* Baud rate generator source */
+#define	DTRREQ	4	/* DTR/Request function */
+#define	AUTOECHO 8	/* Auto Echo */
+#define	LOOPBAK	0x10	/* Local loopback */
+#define	SEARCH	0x20	/* Enter search mode */
+#define	RMC	0x40	/* Reset missing clock */
+#define	DISDPLL	0x60	/* Disable DPLL */
+#define	SSBR	0x80	/* Set DPLL source = BR generator */
+#define	SSRTxC	0xa0	/* Set DPLL source = RTxC */
+#define	SFMM	0xc0	/* Set FM mode */
+#define	SNRZI	0xe0	/* Set NRZI mode */
+
+/* Write Register 15 (external/status interrupt control) */
+#define	WR7pEN	1	/* WR7' Enable (ESCC only) */
+#define	ZCIE	2	/* Zero count IE */
+#define	FIFOEN	4	/* FIFO Enable (ESCC only) */
+#define	DCDIE	8	/* DCD IE */
+#define	SYNCIE	0x10	/* Sync/hunt IE */
+#define	CTSIE	0x20	/* CTS IE */
+#define	TxUIE	0x40	/* Tx Underrun/EOM IE */
+#define	BRKIE	0x80	/* Break/Abort IE */
+
+
+/* Read Register 0 */
+#define	Rx_CH_AV	0x1	/* Rx Character Available */
+#define	ZCOUNT		0x2	/* Zero count */
+#define	Tx_BUF_EMP	0x4	/* Tx Buffer empty */
+#define	DCD		0x8	/* DCD */
+#define	SYNC		0x10	/* Sync/hunt */
+#define	CTS		0x20	/* CTS */
+#define	TxEOM		0x40	/* Tx underrun */
+#define	BRK_ABRT	0x80	/* Break/Abort */
+
+/* Read Register 1 */
+#define	ALL_SNT		0x1	/* All sent */
+/* Residue Data for 8 Rx bits/char programmed */
+#define	RES3		0x8	/* 0/3 */
+#define	RES4		0x4	/* 0/4 */
+#define	RES5		0xc	/* 0/5 */
+#define	RES6		0x2	/* 0/6 */
+#define	RES7		0xa	/* 0/7 */
+#define	RES8		0x6	/* 0/8 */
+#define	RES18		0xe	/* 1/8 */
+#define	RES28		0x0	/* 2/8 */
+/* Special Rx Condition Interrupts */
+#define	PAR_ERR		0x10	/* Parity error */
+#define	Rx_OVR		0x20	/* Rx Overrun Error */
+#define	CRC_ERR		0x40	/* CRC/Framing Error */
+#define	END_FR		0x80	/* End of Frame (SDLC) */
+
+/* Read Register 2 (channel b only) - Interrupt vector */
+#define CHB_Tx_EMPTY	0x00
+#define CHB_EXT_STAT	0x02
+#define CHB_Rx_AVAIL	0x04
+#define CHB_SPECIAL	0x06
+#define CHA_Tx_EMPTY	0x08
+#define CHA_EXT_STAT	0x0a
+#define CHA_Rx_AVAIL	0x0c
+#define CHA_SPECIAL	0x0e
+#define STATUS_MASK	0x0e
+
+/* Read Register 3 (interrupt pending register) ch a only */
+#define	CHBEXT	0x1		/* Channel B Ext/Stat IP */
+#define	CHBTxIP	0x2		/* Channel B Tx IP */
+#define	CHBRxIP	0x4		/* Channel B Rx IP */
+#define	CHAEXT	0x8		/* Channel A Ext/Stat IP */
+#define	CHATxIP	0x10		/* Channel A Tx IP */
+#define	CHARxIP	0x20		/* Channel A Rx IP */
+
+/* Read Register 6 (LSB frame byte count [Not on NMOS]) */
+
+/* Read Register 7 (MSB frame byte count and FIFO status [Not on NMOS]) */
+
+/* Read Register 8 (receive data register) */
+
+/* Read Register 10  (misc status bits) */
+#define	ONLOOP	2		/* On loop */
+#define	LOOPSEND 0x10		/* Loop sending */
+#define	CLK2MIS	0x40		/* Two clocks missing */
+#define	CLK1MIS	0x80		/* One clock missing */
+
+/* Read Register 12 (lower byte of baud rate generator constant) */
+
+/* Read Register 13 (upper byte of baud rate generator constant) */
+
+/* Read Register 15 (value of WR 15) */
+
+/* Misc macros */
+#define ZS_CLEARERR(channel)    do { sbus_writeb(ERR_RES, &channel->control); \
+				     udelay(5); } while(0)
+
+#define ZS_CLEARSTAT(channel)   do { sbus_writeb(RES_EXT_INT, &channel->control); \
+				     udelay(5); } while(0)
+
+#define ZS_CLEARFIFO(channel)   do { sbus_readb(&channel->data); \
+				     udelay(2); \
+				     sbus_readb(&channel->data); \
+				     udelay(2); \
+				     sbus_readb(&channel->data); \
+				     udelay(2); } while(0)
+
+#endif /* _SUNZILOG_H */
diff --git a/ap/os/linux/linux-3.4.x/drivers/tty/serial/timbuart.c b/ap/os/linux/linux-3.4.x/drivers/tty/serial/timbuart.c
new file mode 100644
index 0000000..70f9749
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/tty/serial/timbuart.c
@@ -0,0 +1,521 @@
+/*
+ * timbuart.c timberdale FPGA UART driver
+ * Copyright (c) 2009 Intel Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/* Supports:
+ * Timberdale FPGA UART
+ */
+
+#include <linux/pci.h>
+#include <linux/interrupt.h>
+#include <linux/serial_core.h>
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
+#include <linux/kernel.h>
+#include <linux/platform_device.h>
+#include <linux/ioport.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+
+#include "timbuart.h"
+
+struct timbuart_port {
+	struct uart_port	port;
+	struct tasklet_struct	tasklet;
+	int			usedma;
+	u32			last_ier;
+	struct platform_device  *dev;
+};
+
+static int baudrates[] = {9600, 19200, 38400, 57600, 115200, 230400, 460800,
+	921600, 1843200, 3250000};
+
+static void timbuart_mctrl_check(struct uart_port *port, u32 isr, u32 *ier);
+
+static irqreturn_t timbuart_handleinterrupt(int irq, void *devid);
+
+static void timbuart_stop_rx(struct uart_port *port)
+{
+	/* spin lock held by upper layer, disable all RX interrupts */
+	u32 ier = ioread32(port->membase + TIMBUART_IER) & ~RXFLAGS;
+	iowrite32(ier, port->membase + TIMBUART_IER);
+}
+
+static void timbuart_stop_tx(struct uart_port *port)
+{
+	/* spinlock held by upper layer, disable TX interrupt */
+	u32 ier = ioread32(port->membase + TIMBUART_IER) & ~TXBAE;
+	iowrite32(ier, port->membase + TIMBUART_IER);
+}
+
+static void timbuart_start_tx(struct uart_port *port)
+{
+	struct timbuart_port *uart =
+		container_of(port, struct timbuart_port, port);
+
+	/* do not transfer anything here -> fire off the tasklet */
+	tasklet_schedule(&uart->tasklet);
+}
+
+static unsigned int timbuart_tx_empty(struct uart_port *port)
+{
+	u32 isr = ioread32(port->membase + TIMBUART_ISR);
+
+	return (isr & TXBE) ? TIOCSER_TEMT : 0;
+}
+
+static void timbuart_flush_buffer(struct uart_port *port)
+{
+	if (!timbuart_tx_empty(port)) {
+		u8 ctl = ioread8(port->membase + TIMBUART_CTRL) |
+			TIMBUART_CTRL_FLSHTX;
+
+		iowrite8(ctl, port->membase + TIMBUART_CTRL);
+		iowrite32(TXBF, port->membase + TIMBUART_ISR);
+	}
+}
+
+static void timbuart_rx_chars(struct uart_port *port)
+{
+	struct tty_struct *tty = port->state->port.tty;
+
+	while (ioread32(port->membase + TIMBUART_ISR) & RXDP) {
+		u8 ch = ioread8(port->membase + TIMBUART_RXFIFO);
+		port->icount.rx++;
+		tty_insert_flip_char(tty, ch, TTY_NORMAL);
+	}
+
+	spin_unlock(&port->lock);
+	tty_flip_buffer_push(port->state->port.tty);
+	spin_lock(&port->lock);
+
+	dev_dbg(port->dev, "%s - total read %d bytes\n",
+		__func__, port->icount.rx);
+}
+
+static void timbuart_tx_chars(struct uart_port *port)
+{
+	struct circ_buf *xmit = &port->state->xmit;
+
+	while (!(ioread32(port->membase + TIMBUART_ISR) & TXBF) &&
+		!uart_circ_empty(xmit)) {
+		iowrite8(xmit->buf[xmit->tail],
+			port->membase + TIMBUART_TXFIFO);
+		xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
+		port->icount.tx++;
+	}
+
+	dev_dbg(port->dev,
+		"%s - total written %d bytes, CTL: %x, RTS: %x, baud: %x\n",
+		 __func__,
+		port->icount.tx,
+		ioread8(port->membase + TIMBUART_CTRL),
+		port->mctrl & TIOCM_RTS,
+		ioread8(port->membase + TIMBUART_BAUDRATE));
+}
+
+static void timbuart_handle_tx_port(struct uart_port *port, u32 isr, u32 *ier)
+{
+	struct timbuart_port *uart =
+		container_of(port, struct timbuart_port, port);
+	struct circ_buf *xmit = &port->state->xmit;
+
+	if (uart_circ_empty(xmit) || uart_tx_stopped(port))
+		return;
+
+	if (port->x_char)
+		return;
+
+	if (isr & TXFLAGS) {
+		timbuart_tx_chars(port);
+		/* clear all TX interrupts */
+		iowrite32(TXFLAGS, port->membase + TIMBUART_ISR);
+
+		if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
+			uart_write_wakeup(port);
+	} else
+		/* Re-enable any tx interrupt */
+		*ier |= uart->last_ier & TXFLAGS;
+
+	/* enable interrupts if there are chars in the transmit buffer,
+	 * Or if we delivered some bytes and want the almost empty interrupt
+	 * we wake up the upper layer later when we got the interrupt
+	 * to give it some time to go out...
+	 */
+	if (!uart_circ_empty(xmit))
+		*ier |= TXBAE;
+
+	dev_dbg(port->dev, "%s - leaving\n", __func__);
+}
+
+void timbuart_handle_rx_port(struct uart_port *port, u32 isr, u32 *ier)
+{
+	if (isr & RXFLAGS) {
+		/* Some RX status is set */
+		if (isr & RXBF) {
+			u8 ctl = ioread8(port->membase + TIMBUART_CTRL) |
+				TIMBUART_CTRL_FLSHRX;
+			iowrite8(ctl, port->membase + TIMBUART_CTRL);
+			port->icount.overrun++;
+		} else if (isr & (RXDP))
+			timbuart_rx_chars(port);
+
+		/* ack all RX interrupts */
+		iowrite32(RXFLAGS, port->membase + TIMBUART_ISR);
+	}
+
+	/* always have the RX interrupts enabled */
+	*ier |= RXBAF | RXBF | RXTT;
+
+	dev_dbg(port->dev, "%s - leaving\n", __func__);
+}
+
+void timbuart_tasklet(unsigned long arg)
+{
+	struct timbuart_port *uart = (struct timbuart_port *)arg;
+	u32 isr, ier = 0;
+
+	spin_lock(&uart->port.lock);
+
+	isr = ioread32(uart->port.membase + TIMBUART_ISR);
+	dev_dbg(uart->port.dev, "%s ISR: %x\n", __func__, isr);
+
+	if (!uart->usedma)
+		timbuart_handle_tx_port(&uart->port, isr, &ier);
+
+	timbuart_mctrl_check(&uart->port, isr, &ier);
+
+	if (!uart->usedma)
+		timbuart_handle_rx_port(&uart->port, isr, &ier);
+
+	iowrite32(ier, uart->port.membase + TIMBUART_IER);
+
+	spin_unlock(&uart->port.lock);
+	dev_dbg(uart->port.dev, "%s leaving\n", __func__);
+}
+
+static unsigned int timbuart_get_mctrl(struct uart_port *port)
+{
+	u8 cts = ioread8(port->membase + TIMBUART_CTRL);
+	dev_dbg(port->dev, "%s - cts %x\n", __func__, cts);
+
+	if (cts & TIMBUART_CTRL_CTS)
+		return TIOCM_CTS | TIOCM_DSR | TIOCM_CAR;
+	else
+		return TIOCM_DSR | TIOCM_CAR;
+}
+
+static void timbuart_set_mctrl(struct uart_port *port, unsigned int mctrl)
+{
+	dev_dbg(port->dev, "%s - %x\n", __func__, mctrl);
+
+	if (mctrl & TIOCM_RTS)
+		iowrite8(TIMBUART_CTRL_RTS, port->membase + TIMBUART_CTRL);
+	else
+		iowrite8(0, port->membase + TIMBUART_CTRL);
+}
+
+static void timbuart_mctrl_check(struct uart_port *port, u32 isr, u32 *ier)
+{
+	unsigned int cts;
+
+	if (isr & CTS_DELTA) {
+		/* ack */
+		iowrite32(CTS_DELTA, port->membase + TIMBUART_ISR);
+		cts = timbuart_get_mctrl(port);
+		uart_handle_cts_change(port, cts & TIOCM_CTS);
+		wake_up_interruptible(&port->state->port.delta_msr_wait);
+	}
+
+	*ier |= CTS_DELTA;
+}
+
+static void timbuart_enable_ms(struct uart_port *port)
+{
+	/* N/A */
+}
+
+static void timbuart_break_ctl(struct uart_port *port, int ctl)
+{
+	/* N/A */
+}
+
+static int timbuart_startup(struct uart_port *port)
+{
+	struct timbuart_port *uart =
+		container_of(port, struct timbuart_port, port);
+
+	dev_dbg(port->dev, "%s\n", __func__);
+
+	iowrite8(TIMBUART_CTRL_FLSHRX, port->membase + TIMBUART_CTRL);
+	iowrite32(0x1ff, port->membase + TIMBUART_ISR);
+	/* Enable all but TX interrupts */
+	iowrite32(RXBAF | RXBF | RXTT | CTS_DELTA,
+		port->membase + TIMBUART_IER);
+
+	return request_irq(port->irq, timbuart_handleinterrupt, IRQF_SHARED,
+		"timb-uart", uart);
+}
+
+static void timbuart_shutdown(struct uart_port *port)
+{
+	struct timbuart_port *uart =
+		container_of(port, struct timbuart_port, port);
+	dev_dbg(port->dev, "%s\n", __func__);
+	free_irq(port->irq, uart);
+	iowrite32(0, port->membase + TIMBUART_IER);
+}
+
+static int get_bindex(int baud)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(baudrates); i++)
+		if (baud <= baudrates[i])
+			return i;
+
+	return -1;
+}
+
+static void timbuart_set_termios(struct uart_port *port,
+	struct ktermios *termios,
+	struct ktermios *old)
+{
+	unsigned int baud;
+	short bindex;
+	unsigned long flags;
+
+	baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk / 16);
+	bindex = get_bindex(baud);
+	dev_dbg(port->dev, "%s - bindex %d\n", __func__, bindex);
+
+	if (bindex < 0)
+		bindex = 0;
+	baud = baudrates[bindex];
+
+	/* The serial layer calls into this once with old = NULL when setting
+	   up initially */
+	if (old)
+		tty_termios_copy_hw(termios, old);
+	tty_termios_encode_baud_rate(termios, baud, baud);
+
+	spin_lock_irqsave(&port->lock, flags);
+	iowrite8((u8)bindex, port->membase + TIMBUART_BAUDRATE);
+	uart_update_timeout(port, termios->c_cflag, baud);
+	spin_unlock_irqrestore(&port->lock, flags);
+}
+
+static const char *timbuart_type(struct uart_port *port)
+{
+	return port->type == PORT_UNKNOWN ? "timbuart" : NULL;
+}
+
+/* We do not request/release mappings of the registers here,
+ * currently it's done in the proble function.
+ */
+static void timbuart_release_port(struct uart_port *port)
+{
+	struct platform_device *pdev = to_platform_device(port->dev);
+	int size =
+		resource_size(platform_get_resource(pdev, IORESOURCE_MEM, 0));
+
+	if (port->flags & UPF_IOREMAP) {
+		iounmap(port->membase);
+		port->membase = NULL;
+	}
+
+	release_mem_region(port->mapbase, size);
+}
+
+static int timbuart_request_port(struct uart_port *port)
+{
+	struct platform_device *pdev = to_platform_device(port->dev);
+	int size =
+		resource_size(platform_get_resource(pdev, IORESOURCE_MEM, 0));
+
+	if (!request_mem_region(port->mapbase, size, "timb-uart"))
+		return -EBUSY;
+
+	if (port->flags & UPF_IOREMAP) {
+		port->membase = ioremap(port->mapbase, size);
+		if (port->membase == NULL) {
+			release_mem_region(port->mapbase, size);
+			return -ENOMEM;
+		}
+	}
+
+	return 0;
+}
+
+static irqreturn_t timbuart_handleinterrupt(int irq, void *devid)
+{
+	struct timbuart_port *uart = (struct timbuart_port *)devid;
+
+	if (ioread8(uart->port.membase + TIMBUART_IPR)) {
+		uart->last_ier = ioread32(uart->port.membase + TIMBUART_IER);
+
+		/* disable interrupts, the tasklet enables them again */
+		iowrite32(0, uart->port.membase + TIMBUART_IER);
+
+		/* fire off bottom half */
+		tasklet_schedule(&uart->tasklet);
+
+		return IRQ_HANDLED;
+	} else
+		return IRQ_NONE;
+}
+
+/*
+ * Configure/autoconfigure the port.
+ */
+static void timbuart_config_port(struct uart_port *port, int flags)
+{
+	if (flags & UART_CONFIG_TYPE) {
+		port->type = PORT_TIMBUART;
+		timbuart_request_port(port);
+	}
+}
+
+static int timbuart_verify_port(struct uart_port *port,
+	struct serial_struct *ser)
+{
+	/* we don't want the core code to modify any port params */
+	return -EINVAL;
+}
+
+static struct uart_ops timbuart_ops = {
+	.tx_empty = timbuart_tx_empty,
+	.set_mctrl = timbuart_set_mctrl,
+	.get_mctrl = timbuart_get_mctrl,
+	.stop_tx = timbuart_stop_tx,
+	.start_tx = timbuart_start_tx,
+	.flush_buffer = timbuart_flush_buffer,
+	.stop_rx = timbuart_stop_rx,
+	.enable_ms = timbuart_enable_ms,
+	.break_ctl = timbuart_break_ctl,
+	.startup = timbuart_startup,
+	.shutdown = timbuart_shutdown,
+	.set_termios = timbuart_set_termios,
+	.type = timbuart_type,
+	.release_port = timbuart_release_port,
+	.request_port = timbuart_request_port,
+	.config_port = timbuart_config_port,
+	.verify_port = timbuart_verify_port
+};
+
+static struct uart_driver timbuart_driver = {
+	.owner = THIS_MODULE,
+	.driver_name = "timberdale_uart",
+	.dev_name = "ttyTU",
+	.major = TIMBUART_MAJOR,
+	.minor = TIMBUART_MINOR,
+	.nr = 1
+};
+
+static int __devinit timbuart_probe(struct platform_device *dev)
+{
+	int err, irq;
+	struct timbuart_port *uart;
+	struct resource *iomem;
+
+	dev_dbg(&dev->dev, "%s\n", __func__);
+
+	uart = kzalloc(sizeof(*uart), GFP_KERNEL);
+	if (!uart) {
+		err = -EINVAL;
+		goto err_mem;
+	}
+
+	uart->usedma = 0;
+
+	uart->port.uartclk = 3250000 * 16;
+	uart->port.fifosize  = TIMBUART_FIFO_SIZE;
+	uart->port.regshift  = 2;
+	uart->port.iotype  = UPIO_MEM;
+	uart->port.ops = &timbuart_ops;
+	uart->port.irq = 0;
+	uart->port.flags = UPF_BOOT_AUTOCONF | UPF_IOREMAP;
+	uart->port.line  = 0;
+	uart->port.dev	= &dev->dev;
+
+	iomem = platform_get_resource(dev, IORESOURCE_MEM, 0);
+	if (!iomem) {
+		err = -ENOMEM;
+		goto err_register;
+	}
+	uart->port.mapbase = iomem->start;
+	uart->port.membase = NULL;
+
+	irq = platform_get_irq(dev, 0);
+	if (irq < 0) {
+		err = -EINVAL;
+		goto err_register;
+	}
+	uart->port.irq = irq;
+
+	tasklet_init(&uart->tasklet, timbuart_tasklet, (unsigned long)uart);
+
+	err = uart_register_driver(&timbuart_driver);
+	if (err)
+		goto err_register;
+
+	err = uart_add_one_port(&timbuart_driver, &uart->port);
+	if (err)
+		goto err_add_port;
+
+	platform_set_drvdata(dev, uart);
+
+	return 0;
+
+err_add_port:
+	uart_unregister_driver(&timbuart_driver);
+err_register:
+	kfree(uart);
+err_mem:
+	printk(KERN_ERR "timberdale: Failed to register Timberdale UART: %d\n",
+		err);
+
+	return err;
+}
+
+static int __devexit timbuart_remove(struct platform_device *dev)
+{
+	struct timbuart_port *uart = platform_get_drvdata(dev);
+
+	tasklet_kill(&uart->tasklet);
+	uart_remove_one_port(&timbuart_driver, &uart->port);
+	uart_unregister_driver(&timbuart_driver);
+	kfree(uart);
+
+	return 0;
+}
+
+static struct platform_driver timbuart_platform_driver = {
+	.driver = {
+		.name	= "timb-uart",
+		.owner	= THIS_MODULE,
+	},
+	.probe		= timbuart_probe,
+	.remove		= __devexit_p(timbuart_remove),
+};
+
+module_platform_driver(timbuart_platform_driver);
+
+MODULE_DESCRIPTION("Timberdale UART driver");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:timb-uart");
+
diff --git a/ap/os/linux/linux-3.4.x/drivers/tty/serial/timbuart.h b/ap/os/linux/linux-3.4.x/drivers/tty/serial/timbuart.h
new file mode 100644
index 0000000..7e56676
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/tty/serial/timbuart.h
@@ -0,0 +1,58 @@
+/*
+ * timbuart.c timberdale FPGA GPIO driver
+ * Copyright (c) 2009 Intel Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/* Supports:
+ * Timberdale FPGA UART
+ */
+
+#ifndef _TIMBUART_H
+#define _TIMBUART_H
+
+#define TIMBUART_FIFO_SIZE	2048
+
+#define TIMBUART_RXFIFO		0x08
+#define TIMBUART_TXFIFO		0x0c
+#define TIMBUART_IER		0x10
+#define TIMBUART_IPR		0x14
+#define TIMBUART_ISR		0x18
+#define TIMBUART_CTRL		0x1c
+#define TIMBUART_BAUDRATE	0x20
+
+#define TIMBUART_CTRL_RTS	0x01
+#define TIMBUART_CTRL_CTS	0x02
+#define TIMBUART_CTRL_FLSHTX	0x40
+#define TIMBUART_CTRL_FLSHRX	0x80
+
+#define TXBF		0x01
+#define TXBAE		0x02
+#define CTS_DELTA	0x04
+#define RXDP		0x08
+#define RXBAF		0x10
+#define RXBF		0x20
+#define RXTT		0x40
+#define RXBNAE		0x80
+#define TXBE		0x100
+
+#define RXFLAGS (RXDP | RXBAF | RXBF | RXTT | RXBNAE)
+#define TXFLAGS (TXBF | TXBAE)
+
+#define TIMBUART_MAJOR 204
+#define TIMBUART_MINOR 192
+
+#endif /* _TIMBUART_H */
+
diff --git a/ap/os/linux/linux-3.4.x/drivers/tty/serial/uartlite.c b/ap/os/linux/linux-3.4.x/drivers/tty/serial/uartlite.c
new file mode 100644
index 0000000..d9706e7
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/tty/serial/uartlite.c
@@ -0,0 +1,654 @@
+/*
+ * uartlite.c: Serial driver for Xilinx uartlite serial controller
+ *
+ * Copyright (C) 2006 Peter Korsgaard <jacmet@sunsite.dk>
+ * Copyright (C) 2007 Secret Lab Technologies Ltd.
+ *
+ * This file is licensed under the terms of the GNU General Public License
+ * version 2.  This program is licensed "as is" without any warranty of any
+ * kind, whether express or implied.
+ */
+
+#include <linux/platform_device.h>
+#include <linux/module.h>
+#include <linux/console.h>
+#include <linux/serial.h>
+#include <linux/serial_core.h>
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/init.h>
+#include <asm/io.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_device.h>
+#include <linux/of_platform.h>
+
+#define ULITE_NAME		"ttyUL"
+#define ULITE_MAJOR		204
+#define ULITE_MINOR		187
+#define ULITE_NR_UARTS		4
+
+/* ---------------------------------------------------------------------
+ * Register definitions
+ *
+ * For register details see datasheet:
+ * http://www.xilinx.com/support/documentation/ip_documentation/opb_uartlite.pdf 
+ */
+
+#define ULITE_RX		0x00
+#define ULITE_TX		0x04
+#define ULITE_STATUS		0x08
+#define ULITE_CONTROL		0x0c
+
+#define ULITE_REGION		16
+
+#define ULITE_STATUS_RXVALID	0x01
+#define ULITE_STATUS_RXFULL	0x02
+#define ULITE_STATUS_TXEMPTY	0x04
+#define ULITE_STATUS_TXFULL	0x08
+#define ULITE_STATUS_IE		0x10
+#define ULITE_STATUS_OVERRUN	0x20
+#define ULITE_STATUS_FRAME	0x40
+#define ULITE_STATUS_PARITY	0x80
+
+#define ULITE_CONTROL_RST_TX	0x01
+#define ULITE_CONTROL_RST_RX	0x02
+#define ULITE_CONTROL_IE	0x10
+
+
+static struct uart_port ulite_ports[ULITE_NR_UARTS];
+
+/* ---------------------------------------------------------------------
+ * Core UART driver operations
+ */
+
+static int ulite_receive(struct uart_port *port, int stat)
+{
+	struct tty_struct *tty = port->state->port.tty;
+	unsigned char ch = 0;
+	char flag = TTY_NORMAL;
+
+	if ((stat & (ULITE_STATUS_RXVALID | ULITE_STATUS_OVERRUN
+		     | ULITE_STATUS_FRAME)) == 0)
+		return 0;
+
+	/* stats */
+	if (stat & ULITE_STATUS_RXVALID) {
+		port->icount.rx++;
+		ch = ioread32be(port->membase + ULITE_RX);
+
+		if (stat & ULITE_STATUS_PARITY)
+			port->icount.parity++;
+	}
+
+	if (stat & ULITE_STATUS_OVERRUN)
+		port->icount.overrun++;
+
+	if (stat & ULITE_STATUS_FRAME)
+		port->icount.frame++;
+
+
+	/* drop byte with parity error if IGNPAR specificed */
+	if (stat & port->ignore_status_mask & ULITE_STATUS_PARITY)
+		stat &= ~ULITE_STATUS_RXVALID;
+
+	stat &= port->read_status_mask;
+
+	if (stat & ULITE_STATUS_PARITY)
+		flag = TTY_PARITY;
+
+
+	stat &= ~port->ignore_status_mask;
+
+	if (stat & ULITE_STATUS_RXVALID)
+		tty_insert_flip_char(tty, ch, flag);
+
+	if (stat & ULITE_STATUS_FRAME)
+		tty_insert_flip_char(tty, 0, TTY_FRAME);
+
+	if (stat & ULITE_STATUS_OVERRUN)
+		tty_insert_flip_char(tty, 0, TTY_OVERRUN);
+
+	return 1;
+}
+
+static int ulite_transmit(struct uart_port *port, int stat)
+{
+	struct circ_buf *xmit  = &port->state->xmit;
+
+	if (stat & ULITE_STATUS_TXFULL)
+		return 0;
+
+	if (port->x_char) {
+		iowrite32be(port->x_char, port->membase + ULITE_TX);
+		port->x_char = 0;
+		port->icount.tx++;
+		return 1;
+	}
+
+	if (uart_circ_empty(xmit) || uart_tx_stopped(port))
+		return 0;
+
+	iowrite32be(xmit->buf[xmit->tail], port->membase + ULITE_TX);
+	xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE-1);
+	port->icount.tx++;
+
+	/* wake up */
+	if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
+		uart_write_wakeup(port);
+
+	return 1;
+}
+
+static irqreturn_t ulite_isr(int irq, void *dev_id)
+{
+	struct uart_port *port = dev_id;
+	int busy, n = 0;
+
+	do {
+		int stat = ioread32be(port->membase + ULITE_STATUS);
+		busy  = ulite_receive(port, stat);
+		busy |= ulite_transmit(port, stat);
+		n++;
+	} while (busy);
+
+	/* work done? */
+	if (n > 1) {
+		tty_flip_buffer_push(port->state->port.tty);
+		return IRQ_HANDLED;
+	} else {
+		return IRQ_NONE;
+	}
+}
+
+static unsigned int ulite_tx_empty(struct uart_port *port)
+{
+	unsigned long flags;
+	unsigned int ret;
+
+	spin_lock_irqsave(&port->lock, flags);
+	ret = ioread32be(port->membase + ULITE_STATUS);
+	spin_unlock_irqrestore(&port->lock, flags);
+
+	return ret & ULITE_STATUS_TXEMPTY ? TIOCSER_TEMT : 0;
+}
+
+static unsigned int ulite_get_mctrl(struct uart_port *port)
+{
+	return TIOCM_CTS | TIOCM_DSR | TIOCM_CAR;
+}
+
+static void ulite_set_mctrl(struct uart_port *port, unsigned int mctrl)
+{
+	/* N/A */
+}
+
+static void ulite_stop_tx(struct uart_port *port)
+{
+	/* N/A */
+}
+
+static void ulite_start_tx(struct uart_port *port)
+{
+	ulite_transmit(port, ioread32be(port->membase + ULITE_STATUS));
+}
+
+static void ulite_stop_rx(struct uart_port *port)
+{
+	/* don't forward any more data (like !CREAD) */
+	port->ignore_status_mask = ULITE_STATUS_RXVALID | ULITE_STATUS_PARITY
+		| ULITE_STATUS_FRAME | ULITE_STATUS_OVERRUN;
+}
+
+static void ulite_enable_ms(struct uart_port *port)
+{
+	/* N/A */
+}
+
+static void ulite_break_ctl(struct uart_port *port, int ctl)
+{
+	/* N/A */
+}
+
+static int ulite_startup(struct uart_port *port)
+{
+	int ret;
+
+	ret = request_irq(port->irq, ulite_isr,
+			  IRQF_SHARED | IRQF_SAMPLE_RANDOM, "uartlite", port);
+	if (ret)
+		return ret;
+
+	iowrite32be(ULITE_CONTROL_RST_RX | ULITE_CONTROL_RST_TX,
+	       port->membase + ULITE_CONTROL);
+	iowrite32be(ULITE_CONTROL_IE, port->membase + ULITE_CONTROL);
+
+	return 0;
+}
+
+static void ulite_shutdown(struct uart_port *port)
+{
+	iowrite32be(0, port->membase + ULITE_CONTROL);
+	ioread32be(port->membase + ULITE_CONTROL); /* dummy */
+	free_irq(port->irq, port);
+}
+
+static void ulite_set_termios(struct uart_port *port, struct ktermios *termios,
+			      struct ktermios *old)
+{
+	unsigned long flags;
+	unsigned int baud;
+
+	spin_lock_irqsave(&port->lock, flags);
+
+	port->read_status_mask = ULITE_STATUS_RXVALID | ULITE_STATUS_OVERRUN
+		| ULITE_STATUS_TXFULL;
+
+	if (termios->c_iflag & INPCK)
+		port->read_status_mask |=
+			ULITE_STATUS_PARITY | ULITE_STATUS_FRAME;
+
+	port->ignore_status_mask = 0;
+	if (termios->c_iflag & IGNPAR)
+		port->ignore_status_mask |= ULITE_STATUS_PARITY
+			| ULITE_STATUS_FRAME | ULITE_STATUS_OVERRUN;
+
+	/* ignore all characters if CREAD is not set */
+	if ((termios->c_cflag & CREAD) == 0)
+		port->ignore_status_mask |=
+			ULITE_STATUS_RXVALID | ULITE_STATUS_PARITY
+			| ULITE_STATUS_FRAME | ULITE_STATUS_OVERRUN;
+
+	/* update timeout */
+	baud = uart_get_baud_rate(port, termios, old, 0, 460800);
+	uart_update_timeout(port, termios->c_cflag, baud);
+
+	spin_unlock_irqrestore(&port->lock, flags);
+}
+
+static const char *ulite_type(struct uart_port *port)
+{
+	return port->type == PORT_UARTLITE ? "uartlite" : NULL;
+}
+
+static void ulite_release_port(struct uart_port *port)
+{
+	release_mem_region(port->mapbase, ULITE_REGION);
+	iounmap(port->membase);
+	port->membase = NULL;
+}
+
+static int ulite_request_port(struct uart_port *port)
+{
+	pr_debug("ulite console: port=%p; port->mapbase=%llx\n",
+		 port, (unsigned long long) port->mapbase);
+
+	if (!request_mem_region(port->mapbase, ULITE_REGION, "uartlite")) {
+		dev_err(port->dev, "Memory region busy\n");
+		return -EBUSY;
+	}
+
+	port->membase = ioremap(port->mapbase, ULITE_REGION);
+	if (!port->membase) {
+		dev_err(port->dev, "Unable to map registers\n");
+		release_mem_region(port->mapbase, ULITE_REGION);
+		return -EBUSY;
+	}
+
+	return 0;
+}
+
+static void ulite_config_port(struct uart_port *port, int flags)
+{
+	if (!ulite_request_port(port))
+		port->type = PORT_UARTLITE;
+}
+
+static int ulite_verify_port(struct uart_port *port, struct serial_struct *ser)
+{
+	/* we don't want the core code to modify any port params */
+	return -EINVAL;
+}
+
+#ifdef CONFIG_CONSOLE_POLL
+static int ulite_get_poll_char(struct uart_port *port)
+{
+	if (!(ioread32be(port->membase + ULITE_STATUS)
+						& ULITE_STATUS_RXVALID))
+		return NO_POLL_CHAR;
+
+	return ioread32be(port->membase + ULITE_RX);
+}
+
+static void ulite_put_poll_char(struct uart_port *port, unsigned char ch)
+{
+	while (ioread32be(port->membase + ULITE_STATUS) & ULITE_STATUS_TXFULL)
+		cpu_relax();
+
+	/* write char to device */
+	iowrite32be(ch, port->membase + ULITE_TX);
+}
+#endif
+
+static struct uart_ops ulite_ops = {
+	.tx_empty	= ulite_tx_empty,
+	.set_mctrl	= ulite_set_mctrl,
+	.get_mctrl	= ulite_get_mctrl,
+	.stop_tx	= ulite_stop_tx,
+	.start_tx	= ulite_start_tx,
+	.stop_rx	= ulite_stop_rx,
+	.enable_ms	= ulite_enable_ms,
+	.break_ctl	= ulite_break_ctl,
+	.startup	= ulite_startup,
+	.shutdown	= ulite_shutdown,
+	.set_termios	= ulite_set_termios,
+	.type		= ulite_type,
+	.release_port	= ulite_release_port,
+	.request_port	= ulite_request_port,
+	.config_port	= ulite_config_port,
+	.verify_port	= ulite_verify_port,
+#ifdef CONFIG_CONSOLE_POLL
+	.poll_get_char	= ulite_get_poll_char,
+	.poll_put_char	= ulite_put_poll_char,
+#endif
+};
+
+/* ---------------------------------------------------------------------
+ * Console driver operations
+ */
+
+#ifdef CONFIG_SERIAL_UARTLITE_CONSOLE
+static void ulite_console_wait_tx(struct uart_port *port)
+{
+	int i;
+	u8 val;
+
+	/* Spin waiting for TX fifo to have space available */
+	for (i = 0; i < 100000; i++) {
+		val = ioread32be(port->membase + ULITE_STATUS);
+		if ((val & ULITE_STATUS_TXFULL) == 0)
+			break;
+		cpu_relax();
+	}
+}
+
+static void ulite_console_putchar(struct uart_port *port, int ch)
+{
+	ulite_console_wait_tx(port);
+	iowrite32be(ch, port->membase + ULITE_TX);
+}
+
+static void ulite_console_write(struct console *co, const char *s,
+				unsigned int count)
+{
+	struct uart_port *port = &ulite_ports[co->index];
+	unsigned long flags;
+	unsigned int ier;
+	int locked = 1;
+
+	if (oops_in_progress) {
+		locked = spin_trylock_irqsave(&port->lock, flags);
+	} else
+		spin_lock_irqsave(&port->lock, flags);
+
+	/* save and disable interrupt */
+	ier = ioread32be(port->membase + ULITE_STATUS) & ULITE_STATUS_IE;
+	iowrite32be(0, port->membase + ULITE_CONTROL);
+
+	uart_console_write(port, s, count, ulite_console_putchar);
+
+	ulite_console_wait_tx(port);
+
+	/* restore interrupt state */
+	if (ier)
+		iowrite32be(ULITE_CONTROL_IE, port->membase + ULITE_CONTROL);
+
+	if (locked)
+		spin_unlock_irqrestore(&port->lock, flags);
+}
+
+static int __devinit ulite_console_setup(struct console *co, char *options)
+{
+	struct uart_port *port;
+	int baud = 9600;
+	int bits = 8;
+	int parity = 'n';
+	int flow = 'n';
+
+	if (co->index < 0 || co->index >= ULITE_NR_UARTS)
+		return -EINVAL;
+
+	port = &ulite_ports[co->index];
+
+	/* Has the device been initialized yet? */
+	if (!port->mapbase) {
+		pr_debug("console on ttyUL%i not present\n", co->index);
+		return -ENODEV;
+	}
+
+	/* not initialized yet? */
+	if (!port->membase) {
+		if (ulite_request_port(port))
+			return -ENODEV;
+	}
+
+	if (options)
+		uart_parse_options(options, &baud, &parity, &bits, &flow);
+
+	return uart_set_options(port, co, baud, parity, bits, flow);
+}
+
+static struct uart_driver ulite_uart_driver;
+
+static struct console ulite_console = {
+	.name	= ULITE_NAME,
+	.write	= ulite_console_write,
+	.device	= uart_console_device,
+	.setup	= ulite_console_setup,
+	.flags	= CON_PRINTBUFFER,
+	.index	= -1, /* Specified on the cmdline (e.g. console=ttyUL0 ) */
+	.data	= &ulite_uart_driver,
+};
+
+static int __init ulite_console_init(void)
+{
+	register_console(&ulite_console);
+	return 0;
+}
+
+console_initcall(ulite_console_init);
+
+#endif /* CONFIG_SERIAL_UARTLITE_CONSOLE */
+
+static struct uart_driver ulite_uart_driver = {
+	.owner		= THIS_MODULE,
+	.driver_name	= "uartlite",
+	.dev_name	= ULITE_NAME,
+	.major		= ULITE_MAJOR,
+	.minor		= ULITE_MINOR,
+	.nr		= ULITE_NR_UARTS,
+#ifdef CONFIG_SERIAL_UARTLITE_CONSOLE
+	.cons		= &ulite_console,
+#endif
+};
+
+/* ---------------------------------------------------------------------
+ * Port assignment functions (mapping devices to uart_port structures)
+ */
+
+/** ulite_assign: register a uartlite device with the driver
+ *
+ * @dev: pointer to device structure
+ * @id: requested id number.  Pass -1 for automatic port assignment
+ * @base: base address of uartlite registers
+ * @irq: irq number for uartlite
+ *
+ * Returns: 0 on success, <0 otherwise
+ */
+static int __devinit ulite_assign(struct device *dev, int id, u32 base, int irq)
+{
+	struct uart_port *port;
+	int rc;
+
+	/* if id = -1; then scan for a free id and use that */
+	if (id < 0) {
+		for (id = 0; id < ULITE_NR_UARTS; id++)
+			if (ulite_ports[id].mapbase == 0)
+				break;
+	}
+	if (id < 0 || id >= ULITE_NR_UARTS) {
+		dev_err(dev, "%s%i too large\n", ULITE_NAME, id);
+		return -EINVAL;
+	}
+
+	if ((ulite_ports[id].mapbase) && (ulite_ports[id].mapbase != base)) {
+		dev_err(dev, "cannot assign to %s%i; it is already in use\n",
+			ULITE_NAME, id);
+		return -EBUSY;
+	}
+
+	port = &ulite_ports[id];
+
+	spin_lock_init(&port->lock);
+	port->fifosize = 16;
+	port->regshift = 2;
+	port->iotype = UPIO_MEM;
+	port->iobase = 1; /* mark port in use */
+	port->mapbase = base;
+	port->membase = NULL;
+	port->ops = &ulite_ops;
+	port->irq = irq;
+	port->flags = UPF_BOOT_AUTOCONF;
+	port->dev = dev;
+	port->type = PORT_UNKNOWN;
+	port->line = id;
+
+	dev_set_drvdata(dev, port);
+
+	/* Register the port */
+	rc = uart_add_one_port(&ulite_uart_driver, port);
+	if (rc) {
+		dev_err(dev, "uart_add_one_port() failed; err=%i\n", rc);
+		port->mapbase = 0;
+		dev_set_drvdata(dev, NULL);
+		return rc;
+	}
+
+	return 0;
+}
+
+/** ulite_release: register a uartlite device with the driver
+ *
+ * @dev: pointer to device structure
+ */
+static int __devexit ulite_release(struct device *dev)
+{
+	struct uart_port *port = dev_get_drvdata(dev);
+	int rc = 0;
+
+	if (port) {
+		rc = uart_remove_one_port(&ulite_uart_driver, port);
+		dev_set_drvdata(dev, NULL);
+		port->mapbase = 0;
+	}
+
+	return rc;
+}
+
+/* ---------------------------------------------------------------------
+ * Platform bus binding
+ */
+
+#if defined(CONFIG_OF)
+/* Match table for of_platform binding */
+static struct of_device_id ulite_of_match[] __devinitdata = {
+	{ .compatible = "xlnx,opb-uartlite-1.00.b", },
+	{ .compatible = "xlnx,xps-uartlite-1.00.a", },
+	{}
+};
+MODULE_DEVICE_TABLE(of, ulite_of_match);
+#endif /* CONFIG_OF */
+
+static int __devinit ulite_probe(struct platform_device *pdev)
+{
+	struct resource *res;
+	int irq;
+	int id = pdev->id;
+#ifdef CONFIG_OF
+	const __be32 *prop;
+
+	prop = of_get_property(pdev->dev.of_node, "port-number", NULL);
+	if (prop)
+		id = be32_to_cpup(prop);
+#endif
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!res)
+		return -ENODEV;
+
+	irq = platform_get_irq(pdev, 0);
+	if (irq <= 0)
+		return -ENXIO;
+
+	return ulite_assign(&pdev->dev, id, res->start, irq);
+}
+
+static int __devexit ulite_remove(struct platform_device *pdev)
+{
+	return ulite_release(&pdev->dev);
+}
+
+/* work with hotplug and coldplug */
+MODULE_ALIAS("platform:uartlite");
+
+static struct platform_driver ulite_platform_driver = {
+	.probe = ulite_probe,
+	.remove = __devexit_p(ulite_remove),
+	.driver = {
+		.owner = THIS_MODULE,
+		.name  = "uartlite",
+		.of_match_table = of_match_ptr(ulite_of_match),
+	},
+};
+
+/* ---------------------------------------------------------------------
+ * Module setup/teardown
+ */
+
+int __init ulite_init(void)
+{
+	int ret;
+
+	pr_debug("uartlite: calling uart_register_driver()\n");
+	ret = uart_register_driver(&ulite_uart_driver);
+	if (ret)
+		goto err_uart;
+
+	pr_debug("uartlite: calling platform_driver_register()\n");
+	ret = platform_driver_register(&ulite_platform_driver);
+	if (ret)
+		goto err_plat;
+
+	return 0;
+
+err_plat:
+	uart_unregister_driver(&ulite_uart_driver);
+err_uart:
+	printk(KERN_ERR "registering uartlite driver failed: err=%i", ret);
+	return ret;
+}
+
+void __exit ulite_exit(void)
+{
+	platform_driver_unregister(&ulite_platform_driver);
+	uart_unregister_driver(&ulite_uart_driver);
+}
+
+module_init(ulite_init);
+module_exit(ulite_exit);
+
+MODULE_AUTHOR("Peter Korsgaard <jacmet@sunsite.dk>");
+MODULE_DESCRIPTION("Xilinx uartlite serial driver");
+MODULE_LICENSE("GPL");
diff --git a/ap/os/linux/linux-3.4.x/drivers/tty/serial/ucc_uart.c b/ap/os/linux/linux-3.4.x/drivers/tty/serial/ucc_uart.c
new file mode 100644
index 0000000..f99b0c9
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/tty/serial/ucc_uart.c
@@ -0,0 +1,1544 @@
+/*
+ * Freescale QUICC Engine UART device driver
+ *
+ * Author: Timur Tabi <timur@freescale.com>
+ *
+ * Copyright 2007 Freescale Semiconductor, Inc.  This file is licensed under
+ * the terms of the GNU General Public License version 2.  This program
+ * is licensed "as is" without any warranty of any kind, whether express
+ * or implied.
+ *
+ * This driver adds support for UART devices via Freescale's QUICC Engine
+ * found on some Freescale SOCs.
+ *
+ * If Soft-UART support is needed but not already present, then this driver
+ * will request and upload the "Soft-UART" microcode upon probe.  The
+ * filename of the microcode should be fsl_qe_ucode_uart_X_YZ.bin, where "X"
+ * is the name of the SOC (e.g. 8323), and YZ is the revision of the SOC,
+ * (e.g. "11" for 1.1).
+ */
+
+#include <linux/module.h>
+#include <linux/serial.h>
+#include <linux/serial_core.h>
+#include <linux/slab.h>
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
+#include <linux/io.h>
+#include <linux/of_platform.h>
+#include <linux/dma-mapping.h>
+
+#include <linux/fs_uart_pd.h>
+#include <asm/ucc_slow.h>
+
+#include <linux/firmware.h>
+#include <asm/reg.h>
+
+/*
+ * The GUMR flag for Soft UART.  This would normally be defined in qe.h,
+ * but Soft-UART is a hack and we want to keep everything related to it in
+ * this file.
+ */
+#define UCC_SLOW_GUMR_H_SUART   	0x00004000      /* Soft-UART */
+
+/*
+ * soft_uart is 1 if we need to use Soft-UART mode
+ */
+static int soft_uart;
+/*
+ * firmware_loaded is 1 if the firmware has been loaded, 0 otherwise.
+ */
+static int firmware_loaded;
+
+/* Enable this macro to configure all serial ports in internal loopback
+   mode */
+/* #define LOOPBACK */
+
+/* The major and minor device numbers are defined in
+ * http://www.lanana.org/docs/device-list/devices-2.6+.txt.  For the QE
+ * UART, we have major number 204 and minor numbers 46 - 49, which are the
+ * same as for the CPM2.  This decision was made because no Freescale part
+ * has both a CPM and a QE.
+ */
+#define SERIAL_QE_MAJOR 204
+#define SERIAL_QE_MINOR 46
+
+/* Since we only have minor numbers 46 - 49, there is a hard limit of 4 ports */
+#define UCC_MAX_UART    4
+
+/* The number of buffer descriptors for receiving characters. */
+#define RX_NUM_FIFO     4
+
+/* The number of buffer descriptors for transmitting characters. */
+#define TX_NUM_FIFO     4
+
+/* The maximum size of the character buffer for a single RX BD. */
+#define RX_BUF_SIZE     32
+
+/* The maximum size of the character buffer for a single TX BD. */
+#define TX_BUF_SIZE     32
+
+/*
+ * The number of jiffies to wait after receiving a close command before the
+ * device is actually closed.  This allows the last few characters to be
+ * sent over the wire.
+ */
+#define UCC_WAIT_CLOSING 100
+
+struct ucc_uart_pram {
+	struct ucc_slow_pram common;
+	u8 res1[8];     	/* reserved */
+	__be16 maxidl;  	/* Maximum idle chars */
+	__be16 idlc;    	/* temp idle counter */
+	__be16 brkcr;   	/* Break count register */
+	__be16 parec;   	/* receive parity error counter */
+	__be16 frmec;   	/* receive framing error counter */
+	__be16 nosec;   	/* receive noise counter */
+	__be16 brkec;   	/* receive break condition counter */
+	__be16 brkln;   	/* last received break length */
+	__be16 uaddr[2];	/* UART address character 1 & 2 */
+	__be16 rtemp;   	/* Temp storage */
+	__be16 toseq;   	/* Transmit out of sequence char */
+	__be16 cchars[8];       /* control characters 1-8 */
+	__be16 rccm;    	/* receive control character mask */
+	__be16 rccr;    	/* receive control character register */
+	__be16 rlbc;    	/* receive last break character */
+	__be16 res2;    	/* reserved */
+	__be32 res3;    	/* reserved, should be cleared */
+	u8 res4;		/* reserved, should be cleared */
+	u8 res5[3];     	/* reserved, should be cleared */
+	__be32 res6;    	/* reserved, should be cleared */
+	__be32 res7;    	/* reserved, should be cleared */
+	__be32 res8;    	/* reserved, should be cleared */
+	__be32 res9;    	/* reserved, should be cleared */
+	__be32 res10;   	/* reserved, should be cleared */
+	__be32 res11;   	/* reserved, should be cleared */
+	__be32 res12;   	/* reserved, should be cleared */
+	__be32 res13;   	/* reserved, should be cleared */
+/* The rest is for Soft-UART only */
+	__be16 supsmr;  	/* 0x90, Shadow UPSMR */
+	__be16 res92;   	/* 0x92, reserved, initialize to 0 */
+	__be32 rx_state;	/* 0x94, RX state, initialize to 0 */
+	__be32 rx_cnt;  	/* 0x98, RX count, initialize to 0 */
+	u8 rx_length;   	/* 0x9C, Char length, set to 1+CL+PEN+1+SL */
+	u8 rx_bitmark;  	/* 0x9D, reserved, initialize to 0 */
+	u8 rx_temp_dlst_qe;     /* 0x9E, reserved, initialize to 0 */
+	u8 res14[0xBC - 0x9F];  /* reserved */
+	__be32 dump_ptr;	/* 0xBC, Dump pointer */
+	__be32 rx_frame_rem;    /* 0xC0, reserved, initialize to 0 */
+	u8 rx_frame_rem_size;   /* 0xC4, reserved, initialize to 0 */
+	u8 tx_mode;     	/* 0xC5, mode, 0=AHDLC, 1=UART */
+	__be16 tx_state;	/* 0xC6, TX state */
+	u8 res15[0xD0 - 0xC8];  /* reserved */
+	__be32 resD0;   	/* 0xD0, reserved, initialize to 0 */
+	u8 resD4;       	/* 0xD4, reserved, initialize to 0 */
+	__be16 resD5;   	/* 0xD5, reserved, initialize to 0 */
+} __attribute__ ((packed));
+
+/* SUPSMR definitions, for Soft-UART only */
+#define UCC_UART_SUPSMR_SL      	0x8000
+#define UCC_UART_SUPSMR_RPM_MASK	0x6000
+#define UCC_UART_SUPSMR_RPM_ODD 	0x0000
+#define UCC_UART_SUPSMR_RPM_LOW 	0x2000
+#define UCC_UART_SUPSMR_RPM_EVEN	0x4000
+#define UCC_UART_SUPSMR_RPM_HIGH	0x6000
+#define UCC_UART_SUPSMR_PEN     	0x1000
+#define UCC_UART_SUPSMR_TPM_MASK	0x0C00
+#define UCC_UART_SUPSMR_TPM_ODD 	0x0000
+#define UCC_UART_SUPSMR_TPM_LOW 	0x0400
+#define UCC_UART_SUPSMR_TPM_EVEN	0x0800
+#define UCC_UART_SUPSMR_TPM_HIGH	0x0C00
+#define UCC_UART_SUPSMR_FRZ     	0x0100
+#define UCC_UART_SUPSMR_UM_MASK 	0x00c0
+#define UCC_UART_SUPSMR_UM_NORMAL       0x0000
+#define UCC_UART_SUPSMR_UM_MAN_MULTI    0x0040
+#define UCC_UART_SUPSMR_UM_AUTO_MULTI   0x00c0
+#define UCC_UART_SUPSMR_CL_MASK 	0x0030
+#define UCC_UART_SUPSMR_CL_8    	0x0030
+#define UCC_UART_SUPSMR_CL_7    	0x0020
+#define UCC_UART_SUPSMR_CL_6    	0x0010
+#define UCC_UART_SUPSMR_CL_5    	0x0000
+
+#define UCC_UART_TX_STATE_AHDLC 	0x00
+#define UCC_UART_TX_STATE_UART  	0x01
+#define UCC_UART_TX_STATE_X1    	0x00
+#define UCC_UART_TX_STATE_X16   	0x80
+
+#define UCC_UART_PRAM_ALIGNMENT 0x100
+
+#define UCC_UART_SIZE_OF_BD     UCC_SLOW_SIZE_OF_BD
+#define NUM_CONTROL_CHARS       8
+
+/* Private per-port data structure */
+struct uart_qe_port {
+	struct uart_port port;
+	struct ucc_slow __iomem *uccp;
+	struct ucc_uart_pram __iomem *uccup;
+	struct ucc_slow_info us_info;
+	struct ucc_slow_private *us_private;
+	struct device_node *np;
+	unsigned int ucc_num;   /* First ucc is 0, not 1 */
+
+	u16 rx_nrfifos;
+	u16 rx_fifosize;
+	u16 tx_nrfifos;
+	u16 tx_fifosize;
+	int wait_closing;
+	u32 flags;
+	struct qe_bd *rx_bd_base;
+	struct qe_bd *rx_cur;
+	struct qe_bd *tx_bd_base;
+	struct qe_bd *tx_cur;
+	unsigned char *tx_buf;
+	unsigned char *rx_buf;
+	void *bd_virt;  	/* virtual address of the BD buffers */
+	dma_addr_t bd_dma_addr; /* bus address of the BD buffers */
+	unsigned int bd_size;   /* size of BD buffer space */
+};
+
+static struct uart_driver ucc_uart_driver = {
+	.owner  	= THIS_MODULE,
+	.driver_name    = "ucc_uart",
+	.dev_name       = "ttyQE",
+	.major  	= SERIAL_QE_MAJOR,
+	.minor  	= SERIAL_QE_MINOR,
+	.nr     	= UCC_MAX_UART,
+};
+
+/*
+ * Virtual to physical address translation.
+ *
+ * Given the virtual address for a character buffer, this function returns
+ * the physical (DMA) equivalent.
+ */
+static inline dma_addr_t cpu2qe_addr(void *addr, struct uart_qe_port *qe_port)
+{
+	if (likely((addr >= qe_port->bd_virt)) &&
+	    (addr < (qe_port->bd_virt + qe_port->bd_size)))
+		return qe_port->bd_dma_addr + (addr - qe_port->bd_virt);
+
+	/* something nasty happened */
+	printk(KERN_ERR "%s: addr=%p\n", __func__, addr);
+	BUG();
+	return 0;
+}
+
+/*
+ * Physical to virtual address translation.
+ *
+ * Given the physical (DMA) address for a character buffer, this function
+ * returns the virtual equivalent.
+ */
+static inline void *qe2cpu_addr(dma_addr_t addr, struct uart_qe_port *qe_port)
+{
+	/* sanity check */
+	if (likely((addr >= qe_port->bd_dma_addr) &&
+		   (addr < (qe_port->bd_dma_addr + qe_port->bd_size))))
+		return qe_port->bd_virt + (addr - qe_port->bd_dma_addr);
+
+	/* something nasty happened */
+	printk(KERN_ERR "%s: addr=%llx\n", __func__, (u64)addr);
+	BUG();
+	return NULL;
+}
+
+/*
+ * Return 1 if the QE is done transmitting all buffers for this port
+ *
+ * This function scans each BD in sequence.  If we find a BD that is not
+ * ready (READY=1), then we return 0 indicating that the QE is still sending
+ * data.  If we reach the last BD (WRAP=1), then we know we've scanned
+ * the entire list, and all BDs are done.
+ */
+static unsigned int qe_uart_tx_empty(struct uart_port *port)
+{
+	struct uart_qe_port *qe_port =
+		container_of(port, struct uart_qe_port, port);
+	struct qe_bd *bdp = qe_port->tx_bd_base;
+
+	while (1) {
+		if (in_be16(&bdp->status) & BD_SC_READY)
+			/* This BD is not done, so return "not done" */
+			return 0;
+
+		if (in_be16(&bdp->status) & BD_SC_WRAP)
+			/*
+			 * This BD is done and it's the last one, so return
+			 * "done"
+			 */
+			return 1;
+
+		bdp++;
+	};
+}
+
+/*
+ * Set the modem control lines
+ *
+ * Although the QE can control the modem control lines (e.g. CTS), we
+ * don't need that support. This function must exist, however, otherwise
+ * the kernel will panic.
+ */
+void qe_uart_set_mctrl(struct uart_port *port, unsigned int mctrl)
+{
+}
+
+/*
+ * Get the current modem control line status
+ *
+ * Although the QE can control the modem control lines (e.g. CTS), this
+ * driver currently doesn't support that, so we always return Carrier
+ * Detect, Data Set Ready, and Clear To Send.
+ */
+static unsigned int qe_uart_get_mctrl(struct uart_port *port)
+{
+	return TIOCM_CAR | TIOCM_DSR | TIOCM_CTS;
+}
+
+/*
+ * Disable the transmit interrupt.
+ *
+ * Although this function is called "stop_tx", it does not actually stop
+ * transmission of data.  Instead, it tells the QE to not generate an
+ * interrupt when the UCC is finished sending characters.
+ */
+static void qe_uart_stop_tx(struct uart_port *port)
+{
+	struct uart_qe_port *qe_port =
+		container_of(port, struct uart_qe_port, port);
+
+	clrbits16(&qe_port->uccp->uccm, UCC_UART_UCCE_TX);
+}
+
+/*
+ * Transmit as many characters to the HW as possible.
+ *
+ * This function will attempt to stuff of all the characters from the
+ * kernel's transmit buffer into TX BDs.
+ *
+ * A return value of non-zero indicates that it successfully stuffed all
+ * characters from the kernel buffer.
+ *
+ * A return value of zero indicates that there are still characters in the
+ * kernel's buffer that have not been transmitted, but there are no more BDs
+ * available.  This function should be called again after a BD has been made
+ * available.
+ */
+static int qe_uart_tx_pump(struct uart_qe_port *qe_port)
+{
+	struct qe_bd *bdp;
+	unsigned char *p;
+	unsigned int count;
+	struct uart_port *port = &qe_port->port;
+	struct circ_buf *xmit = &port->state->xmit;
+
+	bdp = qe_port->rx_cur;
+
+	/* Handle xon/xoff */
+	if (port->x_char) {
+		/* Pick next descriptor and fill from buffer */
+		bdp = qe_port->tx_cur;
+
+		p = qe2cpu_addr(bdp->buf, qe_port);
+
+		*p++ = port->x_char;
+		out_be16(&bdp->length, 1);
+		setbits16(&bdp->status, BD_SC_READY);
+		/* Get next BD. */
+		if (in_be16(&bdp->status) & BD_SC_WRAP)
+			bdp = qe_port->tx_bd_base;
+		else
+			bdp++;
+		qe_port->tx_cur = bdp;
+
+		port->icount.tx++;
+		port->x_char = 0;
+		return 1;
+	}
+
+	if (uart_circ_empty(xmit) || uart_tx_stopped(port)) {
+		qe_uart_stop_tx(port);
+		return 0;
+	}
+
+	/* Pick next descriptor and fill from buffer */
+	bdp = qe_port->tx_cur;
+
+	while (!(in_be16(&bdp->status) & BD_SC_READY) &&
+	       (xmit->tail != xmit->head)) {
+		count = 0;
+		p = qe2cpu_addr(bdp->buf, qe_port);
+		while (count < qe_port->tx_fifosize) {
+			*p++ = xmit->buf[xmit->tail];
+			xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
+			port->icount.tx++;
+			count++;
+			if (xmit->head == xmit->tail)
+				break;
+		}
+
+		out_be16(&bdp->length, count);
+		setbits16(&bdp->status, BD_SC_READY);
+
+		/* Get next BD. */
+		if (in_be16(&bdp->status) & BD_SC_WRAP)
+			bdp = qe_port->tx_bd_base;
+		else
+			bdp++;
+	}
+	qe_port->tx_cur = bdp;
+
+	if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
+		uart_write_wakeup(port);
+
+	if (uart_circ_empty(xmit)) {
+		/* The kernel buffer is empty, so turn off TX interrupts.  We
+		   don't need to be told when the QE is finished transmitting
+		   the data. */
+		qe_uart_stop_tx(port);
+		return 0;
+	}
+
+	return 1;
+}
+
+/*
+ * Start transmitting data
+ *
+ * This function will start transmitting any available data, if the port
+ * isn't already transmitting data.
+ */
+static void qe_uart_start_tx(struct uart_port *port)
+{
+	struct uart_qe_port *qe_port =
+		container_of(port, struct uart_qe_port, port);
+
+	/* If we currently are transmitting, then just return */
+	if (in_be16(&qe_port->uccp->uccm) & UCC_UART_UCCE_TX)
+		return;
+
+	/* Otherwise, pump the port and start transmission */
+	if (qe_uart_tx_pump(qe_port))
+		setbits16(&qe_port->uccp->uccm, UCC_UART_UCCE_TX);
+}
+
+/*
+ * Stop transmitting data
+ */
+static void qe_uart_stop_rx(struct uart_port *port)
+{
+	struct uart_qe_port *qe_port =
+		container_of(port, struct uart_qe_port, port);
+
+	clrbits16(&qe_port->uccp->uccm, UCC_UART_UCCE_RX);
+}
+
+/*
+ * Enable status change interrupts
+ *
+ * We don't support status change interrupts, but we need to define this
+ * function otherwise the kernel will panic.
+ */
+static void qe_uart_enable_ms(struct uart_port *port)
+{
+}
+
+/* Start or stop sending  break signal
+ *
+ * This function controls the sending of a break signal.  If break_state=1,
+ * then we start sending a break signal.  If break_state=0, then we stop
+ * sending the break signal.
+ */
+static void qe_uart_break_ctl(struct uart_port *port, int break_state)
+{
+	struct uart_qe_port *qe_port =
+		container_of(port, struct uart_qe_port, port);
+
+	if (break_state)
+		ucc_slow_stop_tx(qe_port->us_private);
+	else
+		ucc_slow_restart_tx(qe_port->us_private);
+}
+
+/* ISR helper function for receiving character.
+ *
+ * This function is called by the ISR to handling receiving characters
+ */
+static void qe_uart_int_rx(struct uart_qe_port *qe_port)
+{
+	int i;
+	unsigned char ch, *cp;
+	struct uart_port *port = &qe_port->port;
+	struct tty_struct *tty = port->state->port.tty;
+	struct qe_bd *bdp;
+	u16 status;
+	unsigned int flg;
+
+	/* Just loop through the closed BDs and copy the characters into
+	 * the buffer.
+	 */
+	bdp = qe_port->rx_cur;
+	while (1) {
+		status = in_be16(&bdp->status);
+
+		/* If this one is empty, then we assume we've read them all */
+		if (status & BD_SC_EMPTY)
+			break;
+
+		/* get number of characters, and check space in RX buffer */
+		i = in_be16(&bdp->length);
+
+		/* If we don't have enough room in RX buffer for the entire BD,
+		 * then we try later, which will be the next RX interrupt.
+		 */
+		if (tty_buffer_request_room(tty, i) < i) {
+			dev_dbg(port->dev, "ucc-uart: no room in RX buffer\n");
+			return;
+		}
+
+		/* get pointer */
+		cp = qe2cpu_addr(bdp->buf, qe_port);
+
+		/* loop through the buffer */
+		while (i-- > 0) {
+			ch = *cp++;
+			port->icount.rx++;
+			flg = TTY_NORMAL;
+
+			if (!i && status &
+			    (BD_SC_BR | BD_SC_FR | BD_SC_PR | BD_SC_OV))
+				goto handle_error;
+			if (uart_handle_sysrq_char(port, ch))
+				continue;
+
+error_return:
+			tty_insert_flip_char(tty, ch, flg);
+
+		}
+
+		/* This BD is ready to be used again. Clear status. get next */
+		clrsetbits_be16(&bdp->status, BD_SC_BR | BD_SC_FR | BD_SC_PR |
+			BD_SC_OV | BD_SC_ID, BD_SC_EMPTY);
+		if (in_be16(&bdp->status) & BD_SC_WRAP)
+			bdp = qe_port->rx_bd_base;
+		else
+			bdp++;
+
+	}
+
+	/* Write back buffer pointer */
+	qe_port->rx_cur = bdp;
+
+	/* Activate BH processing */
+	tty_flip_buffer_push(tty);
+
+	return;
+
+	/* Error processing */
+
+handle_error:
+	/* Statistics */
+	if (status & BD_SC_BR)
+		port->icount.brk++;
+	if (status & BD_SC_PR)
+		port->icount.parity++;
+	if (status & BD_SC_FR)
+		port->icount.frame++;
+	if (status & BD_SC_OV)
+		port->icount.overrun++;
+
+	/* Mask out ignored conditions */
+	status &= port->read_status_mask;
+
+	/* Handle the remaining ones */
+	if (status & BD_SC_BR)
+		flg = TTY_BREAK;
+	else if (status & BD_SC_PR)
+		flg = TTY_PARITY;
+	else if (status & BD_SC_FR)
+		flg = TTY_FRAME;
+
+	/* Overrun does not affect the current character ! */
+	if (status & BD_SC_OV)
+		tty_insert_flip_char(tty, 0, TTY_OVERRUN);
+#ifdef SUPPORT_SYSRQ
+	port->sysrq = 0;
+#endif
+	goto error_return;
+}
+
+/* Interrupt handler
+ *
+ * This interrupt handler is called after a BD is processed.
+ */
+static irqreturn_t qe_uart_int(int irq, void *data)
+{
+	struct uart_qe_port *qe_port = (struct uart_qe_port *) data;
+	struct ucc_slow __iomem *uccp = qe_port->uccp;
+	u16 events;
+
+	/* Clear the interrupts */
+	events = in_be16(&uccp->ucce);
+	out_be16(&uccp->ucce, events);
+
+	if (events & UCC_UART_UCCE_BRKE)
+		uart_handle_break(&qe_port->port);
+
+	if (events & UCC_UART_UCCE_RX)
+		qe_uart_int_rx(qe_port);
+
+	if (events & UCC_UART_UCCE_TX)
+		qe_uart_tx_pump(qe_port);
+
+	return events ? IRQ_HANDLED : IRQ_NONE;
+}
+
+/* Initialize buffer descriptors
+ *
+ * This function initializes all of the RX and TX buffer descriptors.
+ */
+static void qe_uart_initbd(struct uart_qe_port *qe_port)
+{
+	int i;
+	void *bd_virt;
+	struct qe_bd *bdp;
+
+	/* Set the physical address of the host memory buffers in the buffer
+	 * descriptors, and the virtual address for us to work with.
+	 */
+	bd_virt = qe_port->bd_virt;
+	bdp = qe_port->rx_bd_base;
+	qe_port->rx_cur = qe_port->rx_bd_base;
+	for (i = 0; i < (qe_port->rx_nrfifos - 1); i++) {
+		out_be16(&bdp->status, BD_SC_EMPTY | BD_SC_INTRPT);
+		out_be32(&bdp->buf, cpu2qe_addr(bd_virt, qe_port));
+		out_be16(&bdp->length, 0);
+		bd_virt += qe_port->rx_fifosize;
+		bdp++;
+	}
+
+	/* */
+	out_be16(&bdp->status, BD_SC_WRAP | BD_SC_EMPTY | BD_SC_INTRPT);
+	out_be32(&bdp->buf, cpu2qe_addr(bd_virt, qe_port));
+	out_be16(&bdp->length, 0);
+
+	/* Set the physical address of the host memory
+	 * buffers in the buffer descriptors, and the
+	 * virtual address for us to work with.
+	 */
+	bd_virt = qe_port->bd_virt +
+		L1_CACHE_ALIGN(qe_port->rx_nrfifos * qe_port->rx_fifosize);
+	qe_port->tx_cur = qe_port->tx_bd_base;
+	bdp = qe_port->tx_bd_base;
+	for (i = 0; i < (qe_port->tx_nrfifos - 1); i++) {
+		out_be16(&bdp->status, BD_SC_INTRPT);
+		out_be32(&bdp->buf, cpu2qe_addr(bd_virt, qe_port));
+		out_be16(&bdp->length, 0);
+		bd_virt += qe_port->tx_fifosize;
+		bdp++;
+	}
+
+	/* Loopback requires the preamble bit to be set on the first TX BD */
+#ifdef LOOPBACK
+	setbits16(&qe_port->tx_cur->status, BD_SC_P);
+#endif
+
+	out_be16(&bdp->status, BD_SC_WRAP | BD_SC_INTRPT);
+	out_be32(&bdp->buf, cpu2qe_addr(bd_virt, qe_port));
+	out_be16(&bdp->length, 0);
+}
+
+/*
+ * Initialize a UCC for UART.
+ *
+ * This function configures a given UCC to be used as a UART device. Basic
+ * UCC initialization is handled in qe_uart_request_port().  This function
+ * does all the UART-specific stuff.
+ */
+static void qe_uart_init_ucc(struct uart_qe_port *qe_port)
+{
+	u32 cecr_subblock;
+	struct ucc_slow __iomem *uccp = qe_port->uccp;
+	struct ucc_uart_pram *uccup = qe_port->uccup;
+
+	unsigned int i;
+
+	/* First, disable TX and RX in the UCC */
+	ucc_slow_disable(qe_port->us_private, COMM_DIR_RX_AND_TX);
+
+	/* Program the UCC UART parameter RAM */
+	out_8(&uccup->common.rbmr, UCC_BMR_GBL | UCC_BMR_BO_BE);
+	out_8(&uccup->common.tbmr, UCC_BMR_GBL | UCC_BMR_BO_BE);
+	out_be16(&uccup->common.mrblr, qe_port->rx_fifosize);
+	out_be16(&uccup->maxidl, 0x10);
+	out_be16(&uccup->brkcr, 1);
+	out_be16(&uccup->parec, 0);
+	out_be16(&uccup->frmec, 0);
+	out_be16(&uccup->nosec, 0);
+	out_be16(&uccup->brkec, 0);
+	out_be16(&uccup->uaddr[0], 0);
+	out_be16(&uccup->uaddr[1], 0);
+	out_be16(&uccup->toseq, 0);
+	for (i = 0; i < 8; i++)
+		out_be16(&uccup->cchars[i], 0xC000);
+	out_be16(&uccup->rccm, 0xc0ff);
+
+	/* Configure the GUMR registers for UART */
+	if (soft_uart) {
+		/* Soft-UART requires a 1X multiplier for TX */
+		clrsetbits_be32(&uccp->gumr_l,
+			UCC_SLOW_GUMR_L_MODE_MASK | UCC_SLOW_GUMR_L_TDCR_MASK |
+			UCC_SLOW_GUMR_L_RDCR_MASK,
+			UCC_SLOW_GUMR_L_MODE_UART | UCC_SLOW_GUMR_L_TDCR_1 |
+			UCC_SLOW_GUMR_L_RDCR_16);
+
+		clrsetbits_be32(&uccp->gumr_h, UCC_SLOW_GUMR_H_RFW,
+			UCC_SLOW_GUMR_H_TRX | UCC_SLOW_GUMR_H_TTX);
+	} else {
+		clrsetbits_be32(&uccp->gumr_l,
+			UCC_SLOW_GUMR_L_MODE_MASK | UCC_SLOW_GUMR_L_TDCR_MASK |
+			UCC_SLOW_GUMR_L_RDCR_MASK,
+			UCC_SLOW_GUMR_L_MODE_UART | UCC_SLOW_GUMR_L_TDCR_16 |
+			UCC_SLOW_GUMR_L_RDCR_16);
+
+		clrsetbits_be32(&uccp->gumr_h,
+			UCC_SLOW_GUMR_H_TRX | UCC_SLOW_GUMR_H_TTX,
+			UCC_SLOW_GUMR_H_RFW);
+	}
+
+#ifdef LOOPBACK
+	clrsetbits_be32(&uccp->gumr_l, UCC_SLOW_GUMR_L_DIAG_MASK,
+		UCC_SLOW_GUMR_L_DIAG_LOOP);
+	clrsetbits_be32(&uccp->gumr_h,
+		UCC_SLOW_GUMR_H_CTSP | UCC_SLOW_GUMR_H_RSYN,
+		UCC_SLOW_GUMR_H_CDS);
+#endif
+
+	/* Disable rx interrupts  and clear all pending events.  */
+	out_be16(&uccp->uccm, 0);
+	out_be16(&uccp->ucce, 0xffff);
+	out_be16(&uccp->udsr, 0x7e7e);
+
+	/* Initialize UPSMR */
+	out_be16(&uccp->upsmr, 0);
+
+	if (soft_uart) {
+		out_be16(&uccup->supsmr, 0x30);
+		out_be16(&uccup->res92, 0);
+		out_be32(&uccup->rx_state, 0);
+		out_be32(&uccup->rx_cnt, 0);
+		out_8(&uccup->rx_bitmark, 0);
+		out_8(&uccup->rx_length, 10);
+		out_be32(&uccup->dump_ptr, 0x4000);
+		out_8(&uccup->rx_temp_dlst_qe, 0);
+		out_be32(&uccup->rx_frame_rem, 0);
+		out_8(&uccup->rx_frame_rem_size, 0);
+		/* Soft-UART requires TX to be 1X */
+		out_8(&uccup->tx_mode,
+			UCC_UART_TX_STATE_UART | UCC_UART_TX_STATE_X1);
+		out_be16(&uccup->tx_state, 0);
+		out_8(&uccup->resD4, 0);
+		out_be16(&uccup->resD5, 0);
+
+		/* Set UART mode.
+		 * Enable receive and transmit.
+		 */
+
+		/* From the microcode errata:
+		 * 1.GUMR_L register, set mode=0010 (QMC).
+		 * 2.Set GUMR_H[17] bit. (UART/AHDLC mode).
+		 * 3.Set GUMR_H[19:20] (Transparent mode)
+		 * 4.Clear GUMR_H[26] (RFW)
+		 * ...
+		 * 6.Receiver must use 16x over sampling
+		 */
+		clrsetbits_be32(&uccp->gumr_l,
+			UCC_SLOW_GUMR_L_MODE_MASK | UCC_SLOW_GUMR_L_TDCR_MASK |
+			UCC_SLOW_GUMR_L_RDCR_MASK,
+			UCC_SLOW_GUMR_L_MODE_QMC | UCC_SLOW_GUMR_L_TDCR_16 |
+			UCC_SLOW_GUMR_L_RDCR_16);
+
+		clrsetbits_be32(&uccp->gumr_h,
+			UCC_SLOW_GUMR_H_RFW | UCC_SLOW_GUMR_H_RSYN,
+			UCC_SLOW_GUMR_H_SUART | UCC_SLOW_GUMR_H_TRX |
+			UCC_SLOW_GUMR_H_TTX | UCC_SLOW_GUMR_H_TFL);
+
+#ifdef LOOPBACK
+		clrsetbits_be32(&uccp->gumr_l, UCC_SLOW_GUMR_L_DIAG_MASK,
+				UCC_SLOW_GUMR_L_DIAG_LOOP);
+		clrbits32(&uccp->gumr_h, UCC_SLOW_GUMR_H_CTSP |
+			  UCC_SLOW_GUMR_H_CDS);
+#endif
+
+		cecr_subblock = ucc_slow_get_qe_cr_subblock(qe_port->ucc_num);
+		qe_issue_cmd(QE_INIT_TX_RX, cecr_subblock,
+			QE_CR_PROTOCOL_UNSPECIFIED, 0);
+	} else {
+		cecr_subblock = ucc_slow_get_qe_cr_subblock(qe_port->ucc_num);
+		qe_issue_cmd(QE_INIT_TX_RX, cecr_subblock,
+			QE_CR_PROTOCOL_UART, 0);
+	}
+}
+
+/*
+ * Initialize the port.
+ */
+static int qe_uart_startup(struct uart_port *port)
+{
+	struct uart_qe_port *qe_port =
+		container_of(port, struct uart_qe_port, port);
+	int ret;
+
+	/*
+	 * If we're using Soft-UART mode, then we need to make sure the
+	 * firmware has been uploaded first.
+	 */
+	if (soft_uart && !firmware_loaded) {
+		dev_err(port->dev, "Soft-UART firmware not uploaded\n");
+		return -ENODEV;
+	}
+
+	qe_uart_initbd(qe_port);
+	qe_uart_init_ucc(qe_port);
+
+	/* Install interrupt handler. */
+	ret = request_irq(port->irq, qe_uart_int, IRQF_SHARED, "ucc-uart",
+		qe_port);
+	if (ret) {
+		dev_err(port->dev, "could not claim IRQ %u\n", port->irq);
+		return ret;
+	}
+
+	/* Startup rx-int */
+	setbits16(&qe_port->uccp->uccm, UCC_UART_UCCE_RX);
+	ucc_slow_enable(qe_port->us_private, COMM_DIR_RX_AND_TX);
+
+	return 0;
+}
+
+/*
+ * Shutdown the port.
+ */
+static void qe_uart_shutdown(struct uart_port *port)
+{
+	struct uart_qe_port *qe_port =
+		container_of(port, struct uart_qe_port, port);
+	struct ucc_slow __iomem *uccp = qe_port->uccp;
+	unsigned int timeout = 20;
+
+	/* Disable RX and TX */
+
+	/* Wait for all the BDs marked sent */
+	while (!qe_uart_tx_empty(port)) {
+		if (!--timeout) {
+			dev_warn(port->dev, "shutdown timeout\n");
+			break;
+		}
+		set_current_state(TASK_UNINTERRUPTIBLE);
+		schedule_timeout(2);
+	}
+
+	if (qe_port->wait_closing) {
+		/* Wait a bit longer */
+		set_current_state(TASK_UNINTERRUPTIBLE);
+		schedule_timeout(qe_port->wait_closing);
+	}
+
+	/* Stop uarts */
+	ucc_slow_disable(qe_port->us_private, COMM_DIR_RX_AND_TX);
+	clrbits16(&uccp->uccm, UCC_UART_UCCE_TX | UCC_UART_UCCE_RX);
+
+	/* Shut them really down and reinit buffer descriptors */
+	ucc_slow_graceful_stop_tx(qe_port->us_private);
+	qe_uart_initbd(qe_port);
+
+	free_irq(port->irq, qe_port);
+}
+
+/*
+ * Set the serial port parameters.
+ */
+static void qe_uart_set_termios(struct uart_port *port,
+				struct ktermios *termios, struct ktermios *old)
+{
+	struct uart_qe_port *qe_port =
+		container_of(port, struct uart_qe_port, port);
+	struct ucc_slow __iomem *uccp = qe_port->uccp;
+	unsigned int baud;
+	unsigned long flags;
+	u16 upsmr = in_be16(&uccp->upsmr);
+	struct ucc_uart_pram __iomem *uccup = qe_port->uccup;
+	u16 supsmr = in_be16(&uccup->supsmr);
+	u8 char_length = 2; /* 1 + CL + PEN + 1 + SL */
+
+	/* Character length programmed into the mode register is the
+	 * sum of: 1 start bit, number of data bits, 0 or 1 parity bit,
+	 * 1 or 2 stop bits, minus 1.
+	 * The value 'bits' counts this for us.
+	 */
+
+	/* byte size */
+	upsmr &= UCC_UART_UPSMR_CL_MASK;
+	supsmr &= UCC_UART_SUPSMR_CL_MASK;
+
+	switch (termios->c_cflag & CSIZE) {
+	case CS5:
+		upsmr |= UCC_UART_UPSMR_CL_5;
+		supsmr |= UCC_UART_SUPSMR_CL_5;
+		char_length += 5;
+		break;
+	case CS6:
+		upsmr |= UCC_UART_UPSMR_CL_6;
+		supsmr |= UCC_UART_SUPSMR_CL_6;
+		char_length += 6;
+		break;
+	case CS7:
+		upsmr |= UCC_UART_UPSMR_CL_7;
+		supsmr |= UCC_UART_SUPSMR_CL_7;
+		char_length += 7;
+		break;
+	default:	/* case CS8 */
+		upsmr |= UCC_UART_UPSMR_CL_8;
+		supsmr |= UCC_UART_SUPSMR_CL_8;
+		char_length += 8;
+		break;
+	}
+
+	/* If CSTOPB is set, we want two stop bits */
+	if (termios->c_cflag & CSTOPB) {
+		upsmr |= UCC_UART_UPSMR_SL;
+		supsmr |= UCC_UART_SUPSMR_SL;
+		char_length++;  /* + SL */
+	}
+
+	if (termios->c_cflag & PARENB) {
+		upsmr |= UCC_UART_UPSMR_PEN;
+		supsmr |= UCC_UART_SUPSMR_PEN;
+		char_length++;  /* + PEN */
+
+		if (!(termios->c_cflag & PARODD)) {
+			upsmr &= ~(UCC_UART_UPSMR_RPM_MASK |
+				   UCC_UART_UPSMR_TPM_MASK);
+			upsmr |= UCC_UART_UPSMR_RPM_EVEN |
+				UCC_UART_UPSMR_TPM_EVEN;
+			supsmr &= ~(UCC_UART_SUPSMR_RPM_MASK |
+				    UCC_UART_SUPSMR_TPM_MASK);
+			supsmr |= UCC_UART_SUPSMR_RPM_EVEN |
+				UCC_UART_SUPSMR_TPM_EVEN;
+		}
+	}
+
+	/*
+	 * Set up parity check flag
+	 */
+	port->read_status_mask = BD_SC_EMPTY | BD_SC_OV;
+	if (termios->c_iflag & INPCK)
+		port->read_status_mask |= BD_SC_FR | BD_SC_PR;
+	if (termios->c_iflag & (BRKINT | PARMRK))
+		port->read_status_mask |= BD_SC_BR;
+
+	/*
+	 * Characters to ignore
+	 */
+	port->ignore_status_mask = 0;
+	if (termios->c_iflag & IGNPAR)
+		port->ignore_status_mask |= BD_SC_PR | BD_SC_FR;
+	if (termios->c_iflag & IGNBRK) {
+		port->ignore_status_mask |= BD_SC_BR;
+		/*
+		 * If we're ignore parity and break indicators, ignore
+		 * overruns too.  (For real raw support).
+		 */
+		if (termios->c_iflag & IGNPAR)
+			port->ignore_status_mask |= BD_SC_OV;
+	}
+	/*
+	 * !!! ignore all characters if CREAD is not set
+	 */
+	if ((termios->c_cflag & CREAD) == 0)
+		port->read_status_mask &= ~BD_SC_EMPTY;
+
+	baud = uart_get_baud_rate(port, termios, old, 0, 115200);
+
+	/* Do we really need a spinlock here? */
+	spin_lock_irqsave(&port->lock, flags);
+
+	/* Update the per-port timeout. */
+	uart_update_timeout(port, termios->c_cflag, baud);
+
+	out_be16(&uccp->upsmr, upsmr);
+	if (soft_uart) {
+		out_be16(&uccup->supsmr, supsmr);
+		out_8(&uccup->rx_length, char_length);
+
+		/* Soft-UART requires a 1X multiplier for TX */
+		qe_setbrg(qe_port->us_info.rx_clock, baud, 16);
+		qe_setbrg(qe_port->us_info.tx_clock, baud, 1);
+	} else {
+		qe_setbrg(qe_port->us_info.rx_clock, baud, 16);
+		qe_setbrg(qe_port->us_info.tx_clock, baud, 16);
+	}
+
+	spin_unlock_irqrestore(&port->lock, flags);
+}
+
+/*
+ * Return a pointer to a string that describes what kind of port this is.
+ */
+static const char *qe_uart_type(struct uart_port *port)
+{
+	return "QE";
+}
+
+/*
+ * Allocate any memory and I/O resources required by the port.
+ */
+static int qe_uart_request_port(struct uart_port *port)
+{
+	int ret;
+	struct uart_qe_port *qe_port =
+		container_of(port, struct uart_qe_port, port);
+	struct ucc_slow_info *us_info = &qe_port->us_info;
+	struct ucc_slow_private *uccs;
+	unsigned int rx_size, tx_size;
+	void *bd_virt;
+	dma_addr_t bd_dma_addr = 0;
+
+	ret = ucc_slow_init(us_info, &uccs);
+	if (ret) {
+		dev_err(port->dev, "could not initialize UCC%u\n",
+		       qe_port->ucc_num);
+		return ret;
+	}
+
+	qe_port->us_private = uccs;
+	qe_port->uccp = uccs->us_regs;
+	qe_port->uccup = (struct ucc_uart_pram *) uccs->us_pram;
+	qe_port->rx_bd_base = uccs->rx_bd;
+	qe_port->tx_bd_base = uccs->tx_bd;
+
+	/*
+	 * Allocate the transmit and receive data buffers.
+	 */
+
+	rx_size = L1_CACHE_ALIGN(qe_port->rx_nrfifos * qe_port->rx_fifosize);
+	tx_size = L1_CACHE_ALIGN(qe_port->tx_nrfifos * qe_port->tx_fifosize);
+
+	bd_virt = dma_alloc_coherent(port->dev, rx_size + tx_size, &bd_dma_addr,
+		GFP_KERNEL);
+	if (!bd_virt) {
+		dev_err(port->dev, "could not allocate buffer descriptors\n");
+		return -ENOMEM;
+	}
+
+	qe_port->bd_virt = bd_virt;
+	qe_port->bd_dma_addr = bd_dma_addr;
+	qe_port->bd_size = rx_size + tx_size;
+
+	qe_port->rx_buf = bd_virt;
+	qe_port->tx_buf = qe_port->rx_buf + rx_size;
+
+	return 0;
+}
+
+/*
+ * Configure the port.
+ *
+ * We say we're a CPM-type port because that's mostly true.  Once the device
+ * is configured, this driver operates almost identically to the CPM serial
+ * driver.
+ */
+static void qe_uart_config_port(struct uart_port *port, int flags)
+{
+	if (flags & UART_CONFIG_TYPE) {
+		port->type = PORT_CPM;
+		qe_uart_request_port(port);
+	}
+}
+
+/*
+ * Release any memory and I/O resources that were allocated in
+ * qe_uart_request_port().
+ */
+static void qe_uart_release_port(struct uart_port *port)
+{
+	struct uart_qe_port *qe_port =
+		container_of(port, struct uart_qe_port, port);
+	struct ucc_slow_private *uccs = qe_port->us_private;
+
+	dma_free_coherent(port->dev, qe_port->bd_size, qe_port->bd_virt,
+			  qe_port->bd_dma_addr);
+
+	ucc_slow_free(uccs);
+}
+
+/*
+ * Verify that the data in serial_struct is suitable for this device.
+ */
+static int qe_uart_verify_port(struct uart_port *port,
+			       struct serial_struct *ser)
+{
+	if (ser->type != PORT_UNKNOWN && ser->type != PORT_CPM)
+		return -EINVAL;
+
+	if (ser->irq < 0 || ser->irq >= nr_irqs)
+		return -EINVAL;
+
+	if (ser->baud_base < 9600)
+		return -EINVAL;
+
+	return 0;
+}
+/* UART operations
+ *
+ * Details on these functions can be found in Documentation/serial/driver
+ */
+static struct uart_ops qe_uart_pops = {
+	.tx_empty       = qe_uart_tx_empty,
+	.set_mctrl      = qe_uart_set_mctrl,
+	.get_mctrl      = qe_uart_get_mctrl,
+	.stop_tx	= qe_uart_stop_tx,
+	.start_tx       = qe_uart_start_tx,
+	.stop_rx	= qe_uart_stop_rx,
+	.enable_ms      = qe_uart_enable_ms,
+	.break_ctl      = qe_uart_break_ctl,
+	.startup	= qe_uart_startup,
+	.shutdown       = qe_uart_shutdown,
+	.set_termios    = qe_uart_set_termios,
+	.type   	= qe_uart_type,
+	.release_port   = qe_uart_release_port,
+	.request_port   = qe_uart_request_port,
+	.config_port    = qe_uart_config_port,
+	.verify_port    = qe_uart_verify_port,
+};
+
+/*
+ * Obtain the SOC model number and revision level
+ *
+ * This function parses the device tree to obtain the SOC model.  It then
+ * reads the SVR register to the revision.
+ *
+ * The device tree stores the SOC model two different ways.
+ *
+ * The new way is:
+ *
+ *      	cpu@0 {
+ *      		compatible = "PowerPC,8323";
+ *      		device_type = "cpu";
+ *      		...
+ *
+ *
+ * The old way is:
+ *      	 PowerPC,8323@0 {
+ *      		device_type = "cpu";
+ *      		...
+ *
+ * This code first checks the new way, and then the old way.
+ */
+static unsigned int soc_info(unsigned int *rev_h, unsigned int *rev_l)
+{
+	struct device_node *np;
+	const char *soc_string;
+	unsigned int svr;
+	unsigned int soc;
+
+	/* Find the CPU node */
+	np = of_find_node_by_type(NULL, "cpu");
+	if (!np)
+		return 0;
+	/* Find the compatible property */
+	soc_string = of_get_property(np, "compatible", NULL);
+	if (!soc_string)
+		/* No compatible property, so try the name. */
+		soc_string = np->name;
+
+	/* Extract the SOC number from the "PowerPC," string */
+	if ((sscanf(soc_string, "PowerPC,%u", &soc) != 1) || !soc)
+		return 0;
+
+	/* Get the revision from the SVR */
+	svr = mfspr(SPRN_SVR);
+	*rev_h = (svr >> 4) & 0xf;
+	*rev_l = svr & 0xf;
+
+	return soc;
+}
+
+/*
+ * requst_firmware_nowait() callback function
+ *
+ * This function is called by the kernel when a firmware is made available,
+ * or if it times out waiting for the firmware.
+ */
+static void uart_firmware_cont(const struct firmware *fw, void *context)
+{
+	struct qe_firmware *firmware;
+	struct device *dev = context;
+	int ret;
+
+	if (!fw) {
+		dev_err(dev, "firmware not found\n");
+		return;
+	}
+
+	firmware = (struct qe_firmware *) fw->data;
+
+	if (firmware->header.length != fw->size) {
+		dev_err(dev, "invalid firmware\n");
+		goto out;
+	}
+
+	ret = qe_upload_firmware(firmware);
+	if (ret) {
+		dev_err(dev, "could not load firmware\n");
+		goto out;
+	}
+
+	firmware_loaded = 1;
+ out:
+	release_firmware(fw);
+}
+
+static int ucc_uart_probe(struct platform_device *ofdev)
+{
+	struct device_node *np = ofdev->dev.of_node;
+	const unsigned int *iprop;      /* Integer OF properties */
+	const char *sprop;      /* String OF properties */
+	struct uart_qe_port *qe_port = NULL;
+	struct resource res;
+	int ret;
+
+	/*
+	 * Determine if we need Soft-UART mode
+	 */
+	if (of_find_property(np, "soft-uart", NULL)) {
+		dev_dbg(&ofdev->dev, "using Soft-UART mode\n");
+		soft_uart = 1;
+	}
+
+	/*
+	 * If we are using Soft-UART, determine if we need to upload the
+	 * firmware, too.
+	 */
+	if (soft_uart) {
+		struct qe_firmware_info *qe_fw_info;
+
+		qe_fw_info = qe_get_firmware_info();
+
+		/* Check if the firmware has been uploaded. */
+		if (qe_fw_info && strstr(qe_fw_info->id, "Soft-UART")) {
+			firmware_loaded = 1;
+		} else {
+			char filename[32];
+			unsigned int soc;
+			unsigned int rev_h;
+			unsigned int rev_l;
+
+			soc = soc_info(&rev_h, &rev_l);
+			if (!soc) {
+				dev_err(&ofdev->dev, "unknown CPU model\n");
+				return -ENXIO;
+			}
+			sprintf(filename, "fsl_qe_ucode_uart_%u_%u%u.bin",
+				soc, rev_h, rev_l);
+
+			dev_info(&ofdev->dev, "waiting for firmware %s\n",
+				filename);
+
+			/*
+			 * We call request_firmware_nowait instead of
+			 * request_firmware so that the driver can load and
+			 * initialize the ports without holding up the rest of
+			 * the kernel.  If hotplug support is enabled in the
+			 * kernel, then we use it.
+			 */
+			ret = request_firmware_nowait(THIS_MODULE,
+				FW_ACTION_HOTPLUG, filename, &ofdev->dev,
+				GFP_KERNEL, &ofdev->dev, uart_firmware_cont);
+			if (ret) {
+				dev_err(&ofdev->dev,
+					"could not load firmware %s\n",
+					filename);
+				return ret;
+			}
+		}
+	}
+
+	qe_port = kzalloc(sizeof(struct uart_qe_port), GFP_KERNEL);
+	if (!qe_port) {
+		dev_err(&ofdev->dev, "can't allocate QE port structure\n");
+		return -ENOMEM;
+	}
+
+	/* Search for IRQ and mapbase */
+	ret = of_address_to_resource(np, 0, &res);
+	if (ret) {
+		dev_err(&ofdev->dev, "missing 'reg' property in device tree\n");
+		goto out_free;
+	}
+	if (!res.start) {
+		dev_err(&ofdev->dev, "invalid 'reg' property in device tree\n");
+		ret = -EINVAL;
+		goto out_free;
+	}
+	qe_port->port.mapbase = res.start;
+
+	/* Get the UCC number (device ID) */
+	/* UCCs are numbered 1-7 */
+	iprop = of_get_property(np, "cell-index", NULL);
+	if (!iprop) {
+		iprop = of_get_property(np, "device-id", NULL);
+		if (!iprop) {
+			dev_err(&ofdev->dev, "UCC is unspecified in "
+				"device tree\n");
+			ret = -EINVAL;
+			goto out_free;
+		}
+	}
+
+	if ((*iprop < 1) || (*iprop > UCC_MAX_NUM)) {
+		dev_err(&ofdev->dev, "no support for UCC%u\n", *iprop);
+		ret = -ENODEV;
+		goto out_free;
+	}
+	qe_port->ucc_num = *iprop - 1;
+
+	/*
+	 * In the future, we should not require the BRG to be specified in the
+	 * device tree.  If no clock-source is specified, then just pick a BRG
+	 * to use.  This requires a new QE library function that manages BRG
+	 * assignments.
+	 */
+
+	sprop = of_get_property(np, "rx-clock-name", NULL);
+	if (!sprop) {
+		dev_err(&ofdev->dev, "missing rx-clock-name in device tree\n");
+		ret = -ENODEV;
+		goto out_free;
+	}
+
+	qe_port->us_info.rx_clock = qe_clock_source(sprop);
+	if ((qe_port->us_info.rx_clock < QE_BRG1) ||
+	    (qe_port->us_info.rx_clock > QE_BRG16)) {
+		dev_err(&ofdev->dev, "rx-clock-name must be a BRG for UART\n");
+		ret = -ENODEV;
+		goto out_free;
+	}
+
+#ifdef LOOPBACK
+	/* In internal loopback mode, TX and RX must use the same clock */
+	qe_port->us_info.tx_clock = qe_port->us_info.rx_clock;
+#else
+	sprop = of_get_property(np, "tx-clock-name", NULL);
+	if (!sprop) {
+		dev_err(&ofdev->dev, "missing tx-clock-name in device tree\n");
+		ret = -ENODEV;
+		goto out_free;
+	}
+	qe_port->us_info.tx_clock = qe_clock_source(sprop);
+#endif
+	if ((qe_port->us_info.tx_clock < QE_BRG1) ||
+	    (qe_port->us_info.tx_clock > QE_BRG16)) {
+		dev_err(&ofdev->dev, "tx-clock-name must be a BRG for UART\n");
+		ret = -ENODEV;
+		goto out_free;
+	}
+
+	/* Get the port number, numbered 0-3 */
+	iprop = of_get_property(np, "port-number", NULL);
+	if (!iprop) {
+		dev_err(&ofdev->dev, "missing port-number in device tree\n");
+		ret = -EINVAL;
+		goto out_free;
+	}
+	qe_port->port.line = *iprop;
+	if (qe_port->port.line >= UCC_MAX_UART) {
+		dev_err(&ofdev->dev, "port-number must be 0-%u\n",
+			UCC_MAX_UART - 1);
+		ret = -EINVAL;
+		goto out_free;
+	}
+
+	qe_port->port.irq = irq_of_parse_and_map(np, 0);
+	if (qe_port->port.irq == 0) {
+		dev_err(&ofdev->dev, "could not map IRQ for UCC%u\n",
+		       qe_port->ucc_num + 1);
+		ret = -EINVAL;
+		goto out_free;
+	}
+
+	/*
+	 * Newer device trees have an "fsl,qe" compatible property for the QE
+	 * node, but we still need to support older device trees.
+	 */
+	np = of_find_compatible_node(NULL, NULL, "fsl,qe");
+	if (!np) {
+		np = of_find_node_by_type(NULL, "qe");
+		if (!np) {
+			dev_err(&ofdev->dev, "could not find 'qe' node\n");
+			ret = -EINVAL;
+			goto out_free;
+		}
+	}
+
+	iprop = of_get_property(np, "brg-frequency", NULL);
+	if (!iprop) {
+		dev_err(&ofdev->dev,
+		       "missing brg-frequency in device tree\n");
+		ret = -EINVAL;
+		goto out_np;
+	}
+
+	if (*iprop)
+		qe_port->port.uartclk = *iprop;
+	else {
+		/*
+		 * Older versions of U-Boot do not initialize the brg-frequency
+		 * property, so in this case we assume the BRG frequency is
+		 * half the QE bus frequency.
+		 */
+		iprop = of_get_property(np, "bus-frequency", NULL);
+		if (!iprop) {
+			dev_err(&ofdev->dev,
+				"missing QE bus-frequency in device tree\n");
+			ret = -EINVAL;
+			goto out_np;
+		}
+		if (*iprop)
+			qe_port->port.uartclk = *iprop / 2;
+		else {
+			dev_err(&ofdev->dev,
+				"invalid QE bus-frequency in device tree\n");
+			ret = -EINVAL;
+			goto out_np;
+		}
+	}
+
+	spin_lock_init(&qe_port->port.lock);
+	qe_port->np = np;
+	qe_port->port.dev = &ofdev->dev;
+	qe_port->port.ops = &qe_uart_pops;
+	qe_port->port.iotype = UPIO_MEM;
+
+	qe_port->tx_nrfifos = TX_NUM_FIFO;
+	qe_port->tx_fifosize = TX_BUF_SIZE;
+	qe_port->rx_nrfifos = RX_NUM_FIFO;
+	qe_port->rx_fifosize = RX_BUF_SIZE;
+
+	qe_port->wait_closing = UCC_WAIT_CLOSING;
+	qe_port->port.fifosize = 512;
+	qe_port->port.flags = UPF_BOOT_AUTOCONF | UPF_IOREMAP;
+
+	qe_port->us_info.ucc_num = qe_port->ucc_num;
+	qe_port->us_info.regs = (phys_addr_t) res.start;
+	qe_port->us_info.irq = qe_port->port.irq;
+
+	qe_port->us_info.rx_bd_ring_len = qe_port->rx_nrfifos;
+	qe_port->us_info.tx_bd_ring_len = qe_port->tx_nrfifos;
+
+	/* Make sure ucc_slow_init() initializes both TX and RX */
+	qe_port->us_info.init_tx = 1;
+	qe_port->us_info.init_rx = 1;
+
+	/* Add the port to the uart sub-system.  This will cause
+	 * qe_uart_config_port() to be called, so the us_info structure must
+	 * be initialized.
+	 */
+	ret = uart_add_one_port(&ucc_uart_driver, &qe_port->port);
+	if (ret) {
+		dev_err(&ofdev->dev, "could not add /dev/ttyQE%u\n",
+		       qe_port->port.line);
+		goto out_np;
+	}
+
+	dev_set_drvdata(&ofdev->dev, qe_port);
+
+	dev_info(&ofdev->dev, "UCC%u assigned to /dev/ttyQE%u\n",
+		qe_port->ucc_num + 1, qe_port->port.line);
+
+	/* Display the mknod command for this device */
+	dev_dbg(&ofdev->dev, "mknod command is 'mknod /dev/ttyQE%u c %u %u'\n",
+	       qe_port->port.line, SERIAL_QE_MAJOR,
+	       SERIAL_QE_MINOR + qe_port->port.line);
+
+	return 0;
+out_np:
+	of_node_put(np);
+out_free:
+	kfree(qe_port);
+	return ret;
+}
+
+static int ucc_uart_remove(struct platform_device *ofdev)
+{
+	struct uart_qe_port *qe_port = dev_get_drvdata(&ofdev->dev);
+
+	dev_info(&ofdev->dev, "removing /dev/ttyQE%u\n", qe_port->port.line);
+
+	uart_remove_one_port(&ucc_uart_driver, &qe_port->port);
+
+	dev_set_drvdata(&ofdev->dev, NULL);
+	kfree(qe_port);
+
+	return 0;
+}
+
+static struct of_device_id ucc_uart_match[] = {
+	{
+		.type = "serial",
+		.compatible = "ucc_uart",
+	},
+	{},
+};
+MODULE_DEVICE_TABLE(of, ucc_uart_match);
+
+static struct platform_driver ucc_uart_of_driver = {
+	.driver = {
+		.name = "ucc_uart",
+		.owner = THIS_MODULE,
+		.of_match_table    = ucc_uart_match,
+	},
+	.probe  	= ucc_uart_probe,
+	.remove 	= ucc_uart_remove,
+};
+
+static int __init ucc_uart_init(void)
+{
+	int ret;
+
+	printk(KERN_INFO "Freescale QUICC Engine UART device driver\n");
+#ifdef LOOPBACK
+	printk(KERN_INFO "ucc-uart: Using loopback mode\n");
+#endif
+
+	ret = uart_register_driver(&ucc_uart_driver);
+	if (ret) {
+		printk(KERN_ERR "ucc-uart: could not register UART driver\n");
+		return ret;
+	}
+
+	ret = platform_driver_register(&ucc_uart_of_driver);
+	if (ret)
+		printk(KERN_ERR
+		       "ucc-uart: could not register platform driver\n");
+
+	return ret;
+}
+
+static void __exit ucc_uart_exit(void)
+{
+	printk(KERN_INFO
+	       "Freescale QUICC Engine UART device driver unloading\n");
+
+	platform_driver_unregister(&ucc_uart_of_driver);
+	uart_unregister_driver(&ucc_uart_driver);
+}
+
+module_init(ucc_uart_init);
+module_exit(ucc_uart_exit);
+
+MODULE_DESCRIPTION("Freescale QUICC Engine (QE) UART");
+MODULE_AUTHOR("Timur Tabi <timur@freescale.com>");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS_CHARDEV_MAJOR(SERIAL_QE_MAJOR);
+
diff --git a/ap/os/linux/linux-3.4.x/drivers/tty/serial/vr41xx_siu.c b/ap/os/linux/linux-3.4.x/drivers/tty/serial/vr41xx_siu.c
new file mode 100644
index 0000000..cf0d948
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/tty/serial/vr41xx_siu.c
@@ -0,0 +1,967 @@
+/*
+ *  Driver for NEC VR4100 series Serial Interface Unit.
+ *
+ *  Copyright (C) 2004-2008  Yoichi Yuasa <yuasa@linux-mips.org>
+ *
+ *  Based on drivers/serial/8250.c, by Russell King.
+ *
+ *  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, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#if defined(CONFIG_SERIAL_VR41XX_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ)
+#define SUPPORT_SYSRQ
+#endif
+
+#include <linux/console.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/ioport.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/serial.h>
+#include <linux/serial_core.h>
+#include <linux/serial_reg.h>
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
+
+#include <asm/io.h>
+#include <asm/vr41xx/siu.h>
+#include <asm/vr41xx/vr41xx.h>
+
+#define SIU_BAUD_BASE	1152000
+#define SIU_MAJOR	204
+#define SIU_MINOR_BASE	82
+
+#define RX_MAX_COUNT	256
+#define TX_MAX_COUNT	15
+
+#define SIUIRSEL	0x08
+ #define TMICMODE	0x20
+ #define TMICTX		0x10
+ #define IRMSEL		0x0c
+ #define IRMSEL_HP	0x08
+ #define IRMSEL_TEMIC	0x04
+ #define IRMSEL_SHARP	0x00
+ #define IRUSESEL	0x02
+ #define SIRSEL		0x01
+
+static struct uart_port siu_uart_ports[SIU_PORTS_MAX] = {
+	[0 ... SIU_PORTS_MAX-1] = {
+		.lock	= __SPIN_LOCK_UNLOCKED(siu_uart_ports->lock),
+		.irq	= 0,
+	},
+};
+
+#ifdef CONFIG_SERIAL_VR41XX_CONSOLE
+static uint8_t lsr_break_flag[SIU_PORTS_MAX];
+#endif
+
+#define siu_read(port, offset)		readb((port)->membase + (offset))
+#define siu_write(port, offset, value)	writeb((value), (port)->membase + (offset))
+
+void vr41xx_select_siu_interface(siu_interface_t interface)
+{
+	struct uart_port *port;
+	unsigned long flags;
+	uint8_t irsel;
+
+	port = &siu_uart_ports[0];
+
+	spin_lock_irqsave(&port->lock, flags);
+
+	irsel = siu_read(port, SIUIRSEL);
+	if (interface == SIU_INTERFACE_IRDA)
+		irsel |= SIRSEL;
+	else
+		irsel &= ~SIRSEL;
+	siu_write(port, SIUIRSEL, irsel);
+
+	spin_unlock_irqrestore(&port->lock, flags);
+}
+EXPORT_SYMBOL_GPL(vr41xx_select_siu_interface);
+
+void vr41xx_use_irda(irda_use_t use)
+{
+	struct uart_port *port;
+	unsigned long flags;
+	uint8_t irsel;
+
+	port = &siu_uart_ports[0];
+
+	spin_lock_irqsave(&port->lock, flags);
+
+	irsel = siu_read(port, SIUIRSEL);
+	if (use == FIR_USE_IRDA)
+		irsel |= IRUSESEL;
+	else
+		irsel &= ~IRUSESEL;
+	siu_write(port, SIUIRSEL, irsel);
+
+	spin_unlock_irqrestore(&port->lock, flags);
+}
+EXPORT_SYMBOL_GPL(vr41xx_use_irda);
+
+void vr41xx_select_irda_module(irda_module_t module, irda_speed_t speed)
+{
+	struct uart_port *port;
+	unsigned long flags;
+	uint8_t irsel;
+
+	port = &siu_uart_ports[0];
+
+	spin_lock_irqsave(&port->lock, flags);
+
+	irsel = siu_read(port, SIUIRSEL);
+	irsel &= ~(IRMSEL | TMICTX | TMICMODE);
+	switch (module) {
+	case SHARP_IRDA:
+		irsel |= IRMSEL_SHARP;
+		break;
+	case TEMIC_IRDA:
+		irsel |= IRMSEL_TEMIC | TMICMODE;
+		if (speed == IRDA_TX_4MBPS)
+			irsel |= TMICTX;
+		break;
+	case HP_IRDA:
+		irsel |= IRMSEL_HP;
+		break;
+	default:
+		break;
+	}
+	siu_write(port, SIUIRSEL, irsel);
+
+	spin_unlock_irqrestore(&port->lock, flags);
+}
+EXPORT_SYMBOL_GPL(vr41xx_select_irda_module);
+
+static inline void siu_clear_fifo(struct uart_port *port)
+{
+	siu_write(port, UART_FCR, UART_FCR_ENABLE_FIFO);
+	siu_write(port, UART_FCR, UART_FCR_ENABLE_FIFO | UART_FCR_CLEAR_RCVR |
+	                          UART_FCR_CLEAR_XMIT);
+	siu_write(port, UART_FCR, 0);
+}
+
+static inline unsigned long siu_port_size(struct uart_port *port)
+{
+	switch (port->type) {
+	case PORT_VR41XX_SIU:
+		return 11UL;
+	case PORT_VR41XX_DSIU:
+		return 8UL;
+	}
+
+	return 0;
+}
+
+static inline unsigned int siu_check_type(struct uart_port *port)
+{
+	if (port->line == 0)
+		return PORT_VR41XX_SIU;
+	if (port->line == 1 && port->irq)
+		return PORT_VR41XX_DSIU;
+
+	return PORT_UNKNOWN;
+}
+
+static inline const char *siu_type_name(struct uart_port *port)
+{
+	switch (port->type) {
+	case PORT_VR41XX_SIU:
+		return "SIU";
+	case PORT_VR41XX_DSIU:
+		return "DSIU";
+	}
+
+	return NULL;
+}
+
+static unsigned int siu_tx_empty(struct uart_port *port)
+{
+	uint8_t lsr;
+
+	lsr = siu_read(port, UART_LSR);
+	if (lsr & UART_LSR_TEMT)
+		return TIOCSER_TEMT;
+
+	return 0;
+}
+
+static void siu_set_mctrl(struct uart_port *port, unsigned int mctrl)
+{
+	uint8_t mcr = 0;
+
+	if (mctrl & TIOCM_DTR)
+		mcr |= UART_MCR_DTR;
+	if (mctrl & TIOCM_RTS)
+		mcr |= UART_MCR_RTS;
+	if (mctrl & TIOCM_OUT1)
+		mcr |= UART_MCR_OUT1;
+	if (mctrl & TIOCM_OUT2)
+		mcr |= UART_MCR_OUT2;
+	if (mctrl & TIOCM_LOOP)
+		mcr |= UART_MCR_LOOP;
+
+	siu_write(port, UART_MCR, mcr);
+}
+
+static unsigned int siu_get_mctrl(struct uart_port *port)
+{
+	uint8_t msr;
+	unsigned int mctrl = 0;
+
+	msr = siu_read(port, UART_MSR);
+	if (msr & UART_MSR_DCD)
+		mctrl |= TIOCM_CAR;
+	if (msr & UART_MSR_RI)
+		mctrl |= TIOCM_RNG;
+	if (msr & UART_MSR_DSR)
+		mctrl |= TIOCM_DSR;
+	if (msr & UART_MSR_CTS)
+		mctrl |= TIOCM_CTS;
+
+	return mctrl;
+}
+
+static void siu_stop_tx(struct uart_port *port)
+{
+	unsigned long flags;
+	uint8_t ier;
+
+	spin_lock_irqsave(&port->lock, flags);
+
+	ier = siu_read(port, UART_IER);
+	ier &= ~UART_IER_THRI;
+	siu_write(port, UART_IER, ier);
+
+	spin_unlock_irqrestore(&port->lock, flags);
+}
+
+static void siu_start_tx(struct uart_port *port)
+{
+	unsigned long flags;
+	uint8_t ier;
+
+	spin_lock_irqsave(&port->lock, flags);
+
+	ier = siu_read(port, UART_IER);
+	ier |= UART_IER_THRI;
+	siu_write(port, UART_IER, ier);
+
+	spin_unlock_irqrestore(&port->lock, flags);
+}
+
+static void siu_stop_rx(struct uart_port *port)
+{
+	unsigned long flags;
+	uint8_t ier;
+
+	spin_lock_irqsave(&port->lock, flags);
+
+	ier = siu_read(port, UART_IER);
+	ier &= ~UART_IER_RLSI;
+	siu_write(port, UART_IER, ier);
+
+	port->read_status_mask &= ~UART_LSR_DR;
+
+	spin_unlock_irqrestore(&port->lock, flags);
+}
+
+static void siu_enable_ms(struct uart_port *port)
+{
+	unsigned long flags;
+	uint8_t ier;
+
+	spin_lock_irqsave(&port->lock, flags);
+
+	ier = siu_read(port, UART_IER);
+	ier |= UART_IER_MSI;
+	siu_write(port, UART_IER, ier);
+
+	spin_unlock_irqrestore(&port->lock, flags);
+}
+
+static void siu_break_ctl(struct uart_port *port, int ctl)
+{
+	unsigned long flags;
+	uint8_t lcr;
+
+	spin_lock_irqsave(&port->lock, flags);
+
+	lcr = siu_read(port, UART_LCR);
+	if (ctl == -1)
+		lcr |= UART_LCR_SBC;
+	else
+		lcr &= ~UART_LCR_SBC;
+	siu_write(port, UART_LCR, lcr);
+
+	spin_unlock_irqrestore(&port->lock, flags);
+}
+
+static inline void receive_chars(struct uart_port *port, uint8_t *status)
+{
+	struct tty_struct *tty;
+	uint8_t lsr, ch;
+	char flag;
+	int max_count = RX_MAX_COUNT;
+
+	tty = port->state->port.tty;
+	lsr = *status;
+
+	do {
+		ch = siu_read(port, UART_RX);
+		port->icount.rx++;
+		flag = TTY_NORMAL;
+
+#ifdef CONFIG_SERIAL_VR41XX_CONSOLE
+		lsr |= lsr_break_flag[port->line];
+		lsr_break_flag[port->line] = 0;
+#endif
+		if (unlikely(lsr & (UART_LSR_BI | UART_LSR_FE |
+		                    UART_LSR_PE | UART_LSR_OE))) {
+			if (lsr & UART_LSR_BI) {
+				lsr &= ~(UART_LSR_FE | UART_LSR_PE);
+				port->icount.brk++;
+
+				if (uart_handle_break(port))
+					goto ignore_char;
+			}
+
+			if (lsr & UART_LSR_FE)
+				port->icount.frame++;
+			if (lsr & UART_LSR_PE)
+				port->icount.parity++;
+			if (lsr & UART_LSR_OE)
+				port->icount.overrun++;
+
+			lsr &= port->read_status_mask;
+			if (lsr & UART_LSR_BI)
+				flag = TTY_BREAK;
+			if (lsr & UART_LSR_FE)
+				flag = TTY_FRAME;
+			if (lsr & UART_LSR_PE)
+				flag = TTY_PARITY;
+		}
+
+		if (uart_handle_sysrq_char(port, ch))
+			goto ignore_char;
+
+		uart_insert_char(port, lsr, UART_LSR_OE, ch, flag);
+
+	ignore_char:
+		lsr = siu_read(port, UART_LSR);
+	} while ((lsr & UART_LSR_DR) && (max_count-- > 0));
+
+	tty_flip_buffer_push(tty);
+
+	*status = lsr;
+}
+
+static inline void check_modem_status(struct uart_port *port)
+{
+	uint8_t msr;
+
+	msr = siu_read(port, UART_MSR);
+	if ((msr & UART_MSR_ANY_DELTA) == 0)
+		return;
+	if (msr & UART_MSR_DDCD)
+		uart_handle_dcd_change(port, msr & UART_MSR_DCD);
+	if (msr & UART_MSR_TERI)
+		port->icount.rng++;
+	if (msr & UART_MSR_DDSR)
+		port->icount.dsr++;
+	if (msr & UART_MSR_DCTS)
+		uart_handle_cts_change(port, msr & UART_MSR_CTS);
+
+	wake_up_interruptible(&port->state->port.delta_msr_wait);
+}
+
+static inline void transmit_chars(struct uart_port *port)
+{
+	struct circ_buf *xmit;
+	int max_count = TX_MAX_COUNT;
+
+	xmit = &port->state->xmit;
+
+	if (port->x_char) {
+		siu_write(port, UART_TX, port->x_char);
+		port->icount.tx++;
+		port->x_char = 0;
+		return;
+	}
+
+	if (uart_circ_empty(xmit) || uart_tx_stopped(port)) {
+		siu_stop_tx(port);
+		return;
+	}
+
+	do {
+		siu_write(port, UART_TX, xmit->buf[xmit->tail]);
+		xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
+		port->icount.tx++;
+		if (uart_circ_empty(xmit))
+			break;
+	} while (max_count-- > 0);
+
+	if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
+		uart_write_wakeup(port);
+
+	if (uart_circ_empty(xmit))
+		siu_stop_tx(port);
+}
+
+static irqreturn_t siu_interrupt(int irq, void *dev_id)
+{
+	struct uart_port *port;
+	uint8_t iir, lsr;
+
+	port = (struct uart_port *)dev_id;
+
+	iir = siu_read(port, UART_IIR);
+	if (iir & UART_IIR_NO_INT)
+		return IRQ_NONE;
+
+	lsr = siu_read(port, UART_LSR);
+	if (lsr & UART_LSR_DR)
+		receive_chars(port, &lsr);
+
+	check_modem_status(port);
+
+	if (lsr & UART_LSR_THRE)
+		transmit_chars(port);
+
+	return IRQ_HANDLED;
+}
+
+static int siu_startup(struct uart_port *port)
+{
+	int retval;
+
+	if (port->membase == NULL)
+		return -ENODEV;
+
+	siu_clear_fifo(port);
+
+	(void)siu_read(port, UART_LSR);
+	(void)siu_read(port, UART_RX);
+	(void)siu_read(port, UART_IIR);
+	(void)siu_read(port, UART_MSR);
+
+	if (siu_read(port, UART_LSR) == 0xff)
+		return -ENODEV;
+
+	retval = request_irq(port->irq, siu_interrupt, 0, siu_type_name(port), port);
+	if (retval)
+		return retval;
+
+	if (port->type == PORT_VR41XX_DSIU)
+		vr41xx_enable_dsiuint(DSIUINT_ALL);
+
+	siu_write(port, UART_LCR, UART_LCR_WLEN8);
+
+	spin_lock_irq(&port->lock);
+	siu_set_mctrl(port, port->mctrl);
+	spin_unlock_irq(&port->lock);
+
+	siu_write(port, UART_IER, UART_IER_RLSI | UART_IER_RDI);
+
+	(void)siu_read(port, UART_LSR);
+	(void)siu_read(port, UART_RX);
+	(void)siu_read(port, UART_IIR);
+	(void)siu_read(port, UART_MSR);
+
+	return 0;
+}
+
+static void siu_shutdown(struct uart_port *port)
+{
+	unsigned long flags;
+	uint8_t lcr;
+
+	siu_write(port, UART_IER, 0);
+
+	spin_lock_irqsave(&port->lock, flags);
+
+	port->mctrl &= ~TIOCM_OUT2;
+	siu_set_mctrl(port, port->mctrl);
+
+	spin_unlock_irqrestore(&port->lock, flags);
+
+	lcr = siu_read(port, UART_LCR);
+	lcr &= ~UART_LCR_SBC;
+	siu_write(port, UART_LCR, lcr);
+
+	siu_clear_fifo(port);
+
+	(void)siu_read(port, UART_RX);
+
+	if (port->type == PORT_VR41XX_DSIU)
+		vr41xx_disable_dsiuint(DSIUINT_ALL);
+
+	free_irq(port->irq, port);
+}
+
+static void siu_set_termios(struct uart_port *port, struct ktermios *new,
+                            struct ktermios *old)
+{
+	tcflag_t c_cflag, c_iflag;
+	uint8_t lcr, fcr, ier;
+	unsigned int baud, quot;
+	unsigned long flags;
+
+	c_cflag = new->c_cflag;
+	switch (c_cflag & CSIZE) {
+	case CS5:
+		lcr = UART_LCR_WLEN5;
+		break;
+	case CS6:
+		lcr = UART_LCR_WLEN6;
+		break;
+	case CS7:
+		lcr = UART_LCR_WLEN7;
+		break;
+	default:
+		lcr = UART_LCR_WLEN8;
+		break;
+	}
+
+	if (c_cflag & CSTOPB)
+		lcr |= UART_LCR_STOP;
+	if (c_cflag & PARENB)
+		lcr |= UART_LCR_PARITY;
+	if ((c_cflag & PARODD) != PARODD)
+		lcr |= UART_LCR_EPAR;
+	if (c_cflag & CMSPAR)
+		lcr |= UART_LCR_SPAR;
+
+	baud = uart_get_baud_rate(port, new, old, 0, port->uartclk/16);
+	quot = uart_get_divisor(port, baud);
+
+	fcr = UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_10;
+
+	spin_lock_irqsave(&port->lock, flags);
+
+	uart_update_timeout(port, c_cflag, baud);
+
+	c_iflag = new->c_iflag;
+
+	port->read_status_mask = UART_LSR_THRE | UART_LSR_OE | UART_LSR_DR;
+	if (c_iflag & INPCK)
+		port->read_status_mask |= UART_LSR_FE | UART_LSR_PE;
+	if (c_iflag & (BRKINT | PARMRK))
+		port->read_status_mask |= UART_LSR_BI;
+
+	port->ignore_status_mask = 0;
+	if (c_iflag & IGNPAR)
+		port->ignore_status_mask |= UART_LSR_FE | UART_LSR_PE;
+	if (c_iflag & IGNBRK) {
+		port->ignore_status_mask |= UART_LSR_BI;
+		if (c_iflag & IGNPAR)
+			port->ignore_status_mask |= UART_LSR_OE;
+	}
+
+	if ((c_cflag & CREAD) == 0)
+		port->ignore_status_mask |= UART_LSR_DR;
+
+	ier = siu_read(port, UART_IER);
+	ier &= ~UART_IER_MSI;
+	if (UART_ENABLE_MS(port, c_cflag))
+		ier |= UART_IER_MSI;
+	siu_write(port, UART_IER, ier);
+
+	siu_write(port, UART_LCR, lcr | UART_LCR_DLAB);
+
+	siu_write(port, UART_DLL, (uint8_t)quot);
+	siu_write(port, UART_DLM, (uint8_t)(quot >> 8));
+
+	siu_write(port, UART_LCR, lcr);
+
+	siu_write(port, UART_FCR, fcr);
+
+	siu_set_mctrl(port, port->mctrl);
+
+	spin_unlock_irqrestore(&port->lock, flags);
+}
+
+static void siu_pm(struct uart_port *port, unsigned int state, unsigned int oldstate)
+{
+	switch (state) {
+	case 0:
+		switch (port->type) {
+		case PORT_VR41XX_SIU:
+			vr41xx_supply_clock(SIU_CLOCK);
+			break;
+		case PORT_VR41XX_DSIU:
+			vr41xx_supply_clock(DSIU_CLOCK);
+			break;
+		}
+		break;
+	case 3:
+		switch (port->type) {
+		case PORT_VR41XX_SIU:
+			vr41xx_mask_clock(SIU_CLOCK);
+			break;
+		case PORT_VR41XX_DSIU:
+			vr41xx_mask_clock(DSIU_CLOCK);
+			break;
+		}
+		break;
+	}
+}
+
+static const char *siu_type(struct uart_port *port)
+{
+	return siu_type_name(port);
+}
+
+static void siu_release_port(struct uart_port *port)
+{
+	unsigned long size;
+
+	if (port->flags	& UPF_IOREMAP) {
+		iounmap(port->membase);
+		port->membase = NULL;
+	}
+
+	size = siu_port_size(port);
+	release_mem_region(port->mapbase, size);
+}
+
+static int siu_request_port(struct uart_port *port)
+{
+	unsigned long size;
+	struct resource *res;
+
+	size = siu_port_size(port);
+	res = request_mem_region(port->mapbase, size, siu_type_name(port));
+	if (res == NULL)
+		return -EBUSY;
+
+	if (port->flags & UPF_IOREMAP) {
+		port->membase = ioremap(port->mapbase, size);
+		if (port->membase == NULL) {
+			release_resource(res);
+			return -ENOMEM;
+		}
+	}
+
+	return 0;
+}
+
+static void siu_config_port(struct uart_port *port, int flags)
+{
+	if (flags & UART_CONFIG_TYPE) {
+		port->type = siu_check_type(port);
+		(void)siu_request_port(port);
+	}
+}
+
+static int siu_verify_port(struct uart_port *port, struct serial_struct *serial)
+{
+	if (port->type != PORT_VR41XX_SIU && port->type != PORT_VR41XX_DSIU)
+		return -EINVAL;
+	if (port->irq != serial->irq)
+		return -EINVAL;
+	if (port->iotype != serial->io_type)
+		return -EINVAL;
+	if (port->mapbase != (unsigned long)serial->iomem_base)
+		return -EINVAL;
+
+	return 0;
+}
+
+static struct uart_ops siu_uart_ops = {
+	.tx_empty	= siu_tx_empty,
+	.set_mctrl	= siu_set_mctrl,
+	.get_mctrl	= siu_get_mctrl,
+	.stop_tx	= siu_stop_tx,
+	.start_tx	= siu_start_tx,
+	.stop_rx	= siu_stop_rx,
+	.enable_ms	= siu_enable_ms,
+	.break_ctl	= siu_break_ctl,
+	.startup	= siu_startup,
+	.shutdown	= siu_shutdown,
+	.set_termios	= siu_set_termios,
+	.pm		= siu_pm,
+	.type		= siu_type,
+	.release_port	= siu_release_port,
+	.request_port	= siu_request_port,
+	.config_port	= siu_config_port,
+	.verify_port	= siu_verify_port,
+};
+
+static int siu_init_ports(struct platform_device *pdev)
+{
+	struct uart_port *port;
+	struct resource *res;
+	int *type = pdev->dev.platform_data;
+	int i;
+
+	if (!type)
+		return 0;
+
+	port = siu_uart_ports;
+	for (i = 0; i < SIU_PORTS_MAX; i++) {
+		port->type = type[i];
+		if (port->type == PORT_UNKNOWN)
+			continue;
+		port->irq = platform_get_irq(pdev, i);
+		port->uartclk = SIU_BAUD_BASE * 16;
+		port->fifosize = 16;
+		port->regshift = 0;
+		port->iotype = UPIO_MEM;
+		port->flags = UPF_IOREMAP | UPF_BOOT_AUTOCONF;
+		port->line = i;
+		res = platform_get_resource(pdev, IORESOURCE_MEM, i);
+		port->mapbase = res->start;
+		port++;
+	}
+
+	return i;
+}
+
+#ifdef CONFIG_SERIAL_VR41XX_CONSOLE
+
+#define BOTH_EMPTY	(UART_LSR_TEMT | UART_LSR_THRE)
+
+static void wait_for_xmitr(struct uart_port *port)
+{
+	int timeout = 10000;
+	uint8_t lsr, msr;
+
+	do {
+		lsr = siu_read(port, UART_LSR);
+		if (lsr & UART_LSR_BI)
+			lsr_break_flag[port->line] = UART_LSR_BI;
+
+		if ((lsr & BOTH_EMPTY) == BOTH_EMPTY)
+			break;
+	} while (timeout-- > 0);
+
+	if (port->flags & UPF_CONS_FLOW) {
+		timeout = 1000000;
+
+		do {
+			msr = siu_read(port, UART_MSR);
+			if ((msr & UART_MSR_CTS) != 0)
+				break;
+		} while (timeout-- > 0);
+	}
+}
+
+static void siu_console_putchar(struct uart_port *port, int ch)
+{
+	wait_for_xmitr(port);
+	siu_write(port, UART_TX, ch);
+}
+
+static void siu_console_write(struct console *con, const char *s, unsigned count)
+{
+	struct uart_port *port;
+	uint8_t ier;
+
+	port = &siu_uart_ports[con->index];
+
+	ier = siu_read(port, UART_IER);
+	siu_write(port, UART_IER, 0);
+
+	uart_console_write(port, s, count, siu_console_putchar);
+
+	wait_for_xmitr(port);
+	siu_write(port, UART_IER, ier);
+}
+
+static int __init siu_console_setup(struct console *con, char *options)
+{
+	struct uart_port *port;
+	int baud = 9600;
+	int parity = 'n';
+	int bits = 8;
+	int flow = 'n';
+
+	if (con->index >= SIU_PORTS_MAX)
+		con->index = 0;
+
+	port = &siu_uart_ports[con->index];
+	if (port->membase == NULL) {
+		if (port->mapbase == 0)
+			return -ENODEV;
+		port->membase = ioremap(port->mapbase, siu_port_size(port));
+	}
+
+	if (port->type == PORT_VR41XX_SIU)
+		vr41xx_select_siu_interface(SIU_INTERFACE_RS232C);
+
+	if (options != NULL)
+		uart_parse_options(options, &baud, &parity, &bits, &flow);
+
+	return uart_set_options(port, con, baud, parity, bits, flow);
+}
+
+static struct uart_driver siu_uart_driver;
+
+static struct console siu_console = {
+	.name	= "ttyVR",
+	.write	= siu_console_write,
+	.device	= uart_console_device,
+	.setup	= siu_console_setup,
+	.flags	= CON_PRINTBUFFER,
+	.index	= -1,
+	.data	= &siu_uart_driver,
+};
+
+static int __devinit siu_console_init(void)
+{
+	struct uart_port *port;
+	int i;
+
+	for (i = 0; i < SIU_PORTS_MAX; i++) {
+		port = &siu_uart_ports[i];
+		port->ops = &siu_uart_ops;
+	}
+
+	register_console(&siu_console);
+
+	return 0;
+}
+
+console_initcall(siu_console_init);
+
+void __init vr41xx_siu_early_setup(struct uart_port *port)
+{
+	if (port->type == PORT_UNKNOWN)
+		return;
+
+	siu_uart_ports[port->line].line = port->line;
+	siu_uart_ports[port->line].type = port->type;
+	siu_uart_ports[port->line].uartclk = SIU_BAUD_BASE * 16;
+	siu_uart_ports[port->line].mapbase = port->mapbase;
+	siu_uart_ports[port->line].mapbase = port->mapbase;
+	siu_uart_ports[port->line].ops = &siu_uart_ops;
+}
+
+#define SERIAL_VR41XX_CONSOLE	&siu_console
+#else
+#define SERIAL_VR41XX_CONSOLE	NULL
+#endif
+
+static struct uart_driver siu_uart_driver = {
+	.owner		= THIS_MODULE,
+	.driver_name	= "SIU",
+	.dev_name	= "ttyVR",
+	.major		= SIU_MAJOR,
+	.minor		= SIU_MINOR_BASE,
+	.cons		= SERIAL_VR41XX_CONSOLE,
+};
+
+static int __devinit siu_probe(struct platform_device *dev)
+{
+	struct uart_port *port;
+	int num, i, retval;
+
+	num = siu_init_ports(dev);
+	if (num <= 0)
+		return -ENODEV;
+
+	siu_uart_driver.nr = num;
+	retval = uart_register_driver(&siu_uart_driver);
+	if (retval)
+		return retval;
+
+	for (i = 0; i < num; i++) {
+		port = &siu_uart_ports[i];
+		port->ops = &siu_uart_ops;
+		port->dev = &dev->dev;
+
+		retval = uart_add_one_port(&siu_uart_driver, port);
+		if (retval < 0) {
+			port->dev = NULL;
+			break;
+		}
+	}
+
+	if (i == 0 && retval < 0) {
+		uart_unregister_driver(&siu_uart_driver);
+		return retval;
+	}
+
+	return 0;
+}
+
+static int __devexit siu_remove(struct platform_device *dev)
+{
+	struct uart_port *port;
+	int i;
+
+	for (i = 0; i < siu_uart_driver.nr; i++) {
+		port = &siu_uart_ports[i];
+		if (port->dev == &dev->dev) {
+			uart_remove_one_port(&siu_uart_driver, port);
+			port->dev = NULL;
+		}
+	}
+
+	uart_unregister_driver(&siu_uart_driver);
+
+	return 0;
+}
+
+static int siu_suspend(struct platform_device *dev, pm_message_t state)
+{
+	struct uart_port *port;
+	int i;
+
+	for (i = 0; i < siu_uart_driver.nr; i++) {
+		port = &siu_uart_ports[i];
+		if ((port->type == PORT_VR41XX_SIU ||
+		     port->type == PORT_VR41XX_DSIU) && port->dev == &dev->dev)
+			uart_suspend_port(&siu_uart_driver, port);
+
+	}
+
+	return 0;
+}
+
+static int siu_resume(struct platform_device *dev)
+{
+	struct uart_port *port;
+	int i;
+
+	for (i = 0; i < siu_uart_driver.nr; i++) {
+		port = &siu_uart_ports[i];
+		if ((port->type == PORT_VR41XX_SIU ||
+		     port->type == PORT_VR41XX_DSIU) && port->dev == &dev->dev)
+			uart_resume_port(&siu_uart_driver, port);
+	}
+
+	return 0;
+}
+
+static struct platform_driver siu_device_driver = {
+	.probe		= siu_probe,
+	.remove		= __devexit_p(siu_remove),
+	.suspend	= siu_suspend,
+	.resume		= siu_resume,
+	.driver		= {
+		.name	= "SIU",
+		.owner	= THIS_MODULE,
+	},
+};
+
+module_platform_driver(siu_device_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:SIU");
diff --git a/ap/os/linux/linux-3.4.x/drivers/tty/serial/vt8500_serial.c b/ap/os/linux/linux-3.4.x/drivers/tty/serial/vt8500_serial.c
new file mode 100644
index 0000000..2be006f
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/tty/serial/vt8500_serial.c
@@ -0,0 +1,645 @@
+/*
+ * Copyright (C) 2010 Alexey Charkov <alchark@gmail.com>
+ *
+ * Based on msm_serial.c, which is:
+ * Copyright (C) 2007 Google, Inc.
+ * Author: Robert Love <rlove@google.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ */
+
+#if defined(CONFIG_SERIAL_VT8500_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ)
+# define SUPPORT_SYSRQ
+#endif
+
+#include <linux/hrtimer.h>
+#include <linux/delay.h>
+#include <linux/module.h>
+#include <linux/io.h>
+#include <linux/ioport.h>
+#include <linux/irq.h>
+#include <linux/init.h>
+#include <linux/console.h>
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
+#include <linux/serial_core.h>
+#include <linux/serial.h>
+#include <linux/slab.h>
+#include <linux/clk.h>
+#include <linux/platform_device.h>
+
+/*
+ * UART Register offsets
+ */
+
+#define VT8500_URTDR		0x0000	/* Transmit data */
+#define VT8500_URRDR		0x0004	/* Receive data */
+#define VT8500_URDIV		0x0008	/* Clock/Baud rate divisor */
+#define VT8500_URLCR		0x000C	/* Line control */
+#define VT8500_URICR		0x0010	/* IrDA control */
+#define VT8500_URIER		0x0014	/* Interrupt enable */
+#define VT8500_URISR		0x0018	/* Interrupt status */
+#define VT8500_URUSR		0x001c	/* UART status */
+#define VT8500_URFCR		0x0020	/* FIFO control */
+#define VT8500_URFIDX		0x0024	/* FIFO index */
+#define VT8500_URBKR		0x0028	/* Break signal count */
+#define VT8500_URTOD		0x002c	/* Time out divisor */
+#define VT8500_TXFIFO		0x1000	/* Transmit FIFO (16x8) */
+#define VT8500_RXFIFO		0x1020	/* Receive FIFO (16x10) */
+
+/*
+ * Interrupt enable and status bits
+ */
+
+#define TXDE	(1 << 0)	/* Tx Data empty */
+#define RXDF	(1 << 1)	/* Rx Data full */
+#define TXFAE	(1 << 2)	/* Tx FIFO almost empty */
+#define TXFE	(1 << 3)	/* Tx FIFO empty */
+#define RXFAF	(1 << 4)	/* Rx FIFO almost full */
+#define RXFF	(1 << 5)	/* Rx FIFO full */
+#define TXUDR	(1 << 6)	/* Tx underrun */
+#define RXOVER	(1 << 7)	/* Rx overrun */
+#define PER	(1 << 8)	/* Parity error */
+#define FER	(1 << 9)	/* Frame error */
+#define TCTS	(1 << 10)	/* Toggle of CTS */
+#define RXTOUT	(1 << 11)	/* Rx timeout */
+#define BKDONE	(1 << 12)	/* Break signal done */
+#define ERR	(1 << 13)	/* AHB error response */
+
+#define RX_FIFO_INTS	(RXFAF | RXFF | RXOVER | PER | FER | RXTOUT)
+#define TX_FIFO_INTS	(TXFAE | TXFE | TXUDR)
+
+struct vt8500_port {
+	struct uart_port	uart;
+	char			name[16];
+	struct clk		*clk;
+	unsigned int		ier;
+};
+
+static inline void vt8500_write(struct uart_port *port, unsigned int val,
+			     unsigned int off)
+{
+	writel(val, port->membase + off);
+}
+
+static inline unsigned int vt8500_read(struct uart_port *port, unsigned int off)
+{
+	return readl(port->membase + off);
+}
+
+static void vt8500_stop_tx(struct uart_port *port)
+{
+	struct vt8500_port *vt8500_port = container_of(port,
+						       struct vt8500_port,
+						       uart);
+
+	vt8500_port->ier &= ~TX_FIFO_INTS;
+	vt8500_write(port, vt8500_port->ier, VT8500_URIER);
+}
+
+static void vt8500_stop_rx(struct uart_port *port)
+{
+	struct vt8500_port *vt8500_port = container_of(port,
+						       struct vt8500_port,
+						       uart);
+
+	vt8500_port->ier &= ~RX_FIFO_INTS;
+	vt8500_write(port, vt8500_port->ier, VT8500_URIER);
+}
+
+static void vt8500_enable_ms(struct uart_port *port)
+{
+	struct vt8500_port *vt8500_port = container_of(port,
+						       struct vt8500_port,
+						       uart);
+
+	vt8500_port->ier |= TCTS;
+	vt8500_write(port, vt8500_port->ier, VT8500_URIER);
+}
+
+static void handle_rx(struct uart_port *port)
+{
+	struct tty_struct *tty = tty_port_tty_get(&port->state->port);
+	if (!tty) {
+		/* Discard data: no tty available */
+		int count = (vt8500_read(port, VT8500_URFIDX) & 0x1f00) >> 8;
+		u16 ch;
+		while (count--)
+			ch = readw(port->membase + VT8500_RXFIFO);
+		return;
+	}
+
+	/*
+	 * Handle overrun
+	 */
+	if ((vt8500_read(port, VT8500_URISR) & RXOVER)) {
+		port->icount.overrun++;
+		tty_insert_flip_char(tty, 0, TTY_OVERRUN);
+	}
+
+	/* and now the main RX loop */
+	while (vt8500_read(port, VT8500_URFIDX) & 0x1f00) {
+		unsigned int c;
+		char flag = TTY_NORMAL;
+
+		c = readw(port->membase + VT8500_RXFIFO) & 0x3ff;
+
+		/* Mask conditions we're ignorning. */
+		c &= ~port->read_status_mask;
+
+		if (c & FER) {
+			port->icount.frame++;
+			flag = TTY_FRAME;
+		} else if (c & PER) {
+			port->icount.parity++;
+			flag = TTY_PARITY;
+		}
+		port->icount.rx++;
+
+		if (!uart_handle_sysrq_char(port, c))
+			tty_insert_flip_char(tty, c, flag);
+	}
+
+	tty_flip_buffer_push(tty);
+	tty_kref_put(tty);
+}
+
+static void handle_tx(struct uart_port *port)
+{
+	struct circ_buf *xmit = &port->state->xmit;
+
+	if (port->x_char) {
+		writeb(port->x_char, port->membase + VT8500_TXFIFO);
+		port->icount.tx++;
+		port->x_char = 0;
+	}
+	if (uart_circ_empty(xmit) || uart_tx_stopped(port)) {
+		vt8500_stop_tx(port);
+		return;
+	}
+
+	while ((vt8500_read(port, VT8500_URFIDX) & 0x1f) < 16) {
+		if (uart_circ_empty(xmit))
+			break;
+
+		writeb(xmit->buf[xmit->tail], port->membase + VT8500_TXFIFO);
+
+		xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
+		port->icount.tx++;
+	}
+
+	if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
+		uart_write_wakeup(port);
+
+	if (uart_circ_empty(xmit))
+		vt8500_stop_tx(port);
+}
+
+static void vt8500_start_tx(struct uart_port *port)
+{
+	struct vt8500_port *vt8500_port = container_of(port,
+						       struct vt8500_port,
+						       uart);
+
+	vt8500_port->ier &= ~TX_FIFO_INTS;
+	vt8500_write(port, vt8500_port->ier, VT8500_URIER);
+	handle_tx(port);
+	vt8500_port->ier |= TX_FIFO_INTS;
+	vt8500_write(port, vt8500_port->ier, VT8500_URIER);
+}
+
+static void handle_delta_cts(struct uart_port *port)
+{
+	port->icount.cts++;
+	wake_up_interruptible(&port->state->port.delta_msr_wait);
+}
+
+static irqreturn_t vt8500_irq(int irq, void *dev_id)
+{
+	struct uart_port *port = dev_id;
+	unsigned long isr;
+
+	spin_lock(&port->lock);
+	isr = vt8500_read(port, VT8500_URISR);
+
+	/* Acknowledge active status bits */
+	vt8500_write(port, isr, VT8500_URISR);
+
+	if (isr & RX_FIFO_INTS)
+		handle_rx(port);
+	if (isr & TX_FIFO_INTS)
+		handle_tx(port);
+	if (isr & TCTS)
+		handle_delta_cts(port);
+
+	spin_unlock(&port->lock);
+
+	return IRQ_HANDLED;
+}
+
+static unsigned int vt8500_tx_empty(struct uart_port *port)
+{
+	return (vt8500_read(port, VT8500_URFIDX) & 0x1f) < 16 ?
+						TIOCSER_TEMT : 0;
+}
+
+static unsigned int vt8500_get_mctrl(struct uart_port *port)
+{
+	unsigned int usr;
+
+	usr = vt8500_read(port, VT8500_URUSR);
+	if (usr & (1 << 4))
+		return TIOCM_CTS;
+	else
+		return 0;
+}
+
+static void vt8500_set_mctrl(struct uart_port *port, unsigned int mctrl)
+{
+}
+
+static void vt8500_break_ctl(struct uart_port *port, int break_ctl)
+{
+	if (break_ctl)
+		vt8500_write(port, vt8500_read(port, VT8500_URLCR) | (1 << 9),
+			     VT8500_URLCR);
+}
+
+static int vt8500_set_baud_rate(struct uart_port *port, unsigned int baud)
+{
+	unsigned long div;
+	unsigned int loops = 1000;
+
+	div = vt8500_read(port, VT8500_URDIV) & ~(0x3ff);
+
+	if (unlikely((baud < 900) || (baud > 921600)))
+		div |= 7;
+	else
+		div |= (921600 / baud) - 1;
+
+	while ((vt8500_read(port, VT8500_URUSR) & (1 << 5)) && --loops)
+		cpu_relax();
+	vt8500_write(port, div, VT8500_URDIV);
+
+	return baud;
+}
+
+static int vt8500_startup(struct uart_port *port)
+{
+	struct vt8500_port *vt8500_port =
+			container_of(port, struct vt8500_port, uart);
+	int ret;
+
+	snprintf(vt8500_port->name, sizeof(vt8500_port->name),
+		 "vt8500_serial%d", port->line);
+
+	ret = request_irq(port->irq, vt8500_irq, IRQF_TRIGGER_HIGH,
+			  vt8500_port->name, port);
+	if (unlikely(ret))
+		return ret;
+
+	vt8500_write(port, 0x03, VT8500_URLCR);	/* enable TX & RX */
+
+	return 0;
+}
+
+static void vt8500_shutdown(struct uart_port *port)
+{
+	struct vt8500_port *vt8500_port =
+			container_of(port, struct vt8500_port, uart);
+
+	vt8500_port->ier = 0;
+
+	/* disable interrupts and FIFOs */
+	vt8500_write(&vt8500_port->uart, 0, VT8500_URIER);
+	vt8500_write(&vt8500_port->uart, 0x880, VT8500_URFCR);
+	free_irq(port->irq, port);
+}
+
+static void vt8500_set_termios(struct uart_port *port,
+			       struct ktermios *termios,
+			       struct ktermios *old)
+{
+	struct vt8500_port *vt8500_port =
+			container_of(port, struct vt8500_port, uart);
+	unsigned long flags;
+	unsigned int baud, lcr;
+	unsigned int loops = 1000;
+
+	spin_lock_irqsave(&port->lock, flags);
+
+	/* calculate and set baud rate */
+	baud = uart_get_baud_rate(port, termios, old, 900, 921600);
+	baud = vt8500_set_baud_rate(port, baud);
+	if (tty_termios_baud_rate(termios))
+		tty_termios_encode_baud_rate(termios, baud, baud);
+
+	/* calculate parity */
+	lcr = vt8500_read(&vt8500_port->uart, VT8500_URLCR);
+	lcr &= ~((1 << 5) | (1 << 4));
+	if (termios->c_cflag & PARENB) {
+		lcr |= (1 << 4);
+		termios->c_cflag &= ~CMSPAR;
+		if (termios->c_cflag & PARODD)
+			lcr |= (1 << 5);
+	}
+
+	/* calculate bits per char */
+	lcr &= ~(1 << 2);
+	switch (termios->c_cflag & CSIZE) {
+	case CS7:
+		break;
+	case CS8:
+	default:
+		lcr |= (1 << 2);
+		termios->c_cflag &= ~CSIZE;
+		termios->c_cflag |= CS8;
+		break;
+	}
+
+	/* calculate stop bits */
+	lcr &= ~(1 << 3);
+	if (termios->c_cflag & CSTOPB)
+		lcr |= (1 << 3);
+
+	/* set parity, bits per char, and stop bit */
+	vt8500_write(&vt8500_port->uart, lcr, VT8500_URLCR);
+
+	/* Configure status bits to ignore based on termio flags. */
+	port->read_status_mask = 0;
+	if (termios->c_iflag & IGNPAR)
+		port->read_status_mask = FER | PER;
+
+	uart_update_timeout(port, termios->c_cflag, baud);
+
+	/* Reset FIFOs */
+	vt8500_write(&vt8500_port->uart, 0x88c, VT8500_URFCR);
+	while ((vt8500_read(&vt8500_port->uart, VT8500_URFCR) & 0xc)
+							&& --loops)
+		cpu_relax();
+
+	/* Every possible FIFO-related interrupt */
+	vt8500_port->ier = RX_FIFO_INTS | TX_FIFO_INTS;
+
+	/*
+	 * CTS flow control
+	 */
+	if (UART_ENABLE_MS(&vt8500_port->uart, termios->c_cflag))
+		vt8500_port->ier |= TCTS;
+
+	vt8500_write(&vt8500_port->uart, 0x881, VT8500_URFCR);
+	vt8500_write(&vt8500_port->uart, vt8500_port->ier, VT8500_URIER);
+
+	spin_unlock_irqrestore(&port->lock, flags);
+}
+
+static const char *vt8500_type(struct uart_port *port)
+{
+	struct vt8500_port *vt8500_port =
+			container_of(port, struct vt8500_port, uart);
+	return vt8500_port->name;
+}
+
+static void vt8500_release_port(struct uart_port *port)
+{
+}
+
+static int vt8500_request_port(struct uart_port *port)
+{
+	return 0;
+}
+
+static void vt8500_config_port(struct uart_port *port, int flags)
+{
+	port->type = PORT_VT8500;
+}
+
+static int vt8500_verify_port(struct uart_port *port,
+			      struct serial_struct *ser)
+{
+	if (unlikely(ser->type != PORT_UNKNOWN && ser->type != PORT_VT8500))
+		return -EINVAL;
+	if (unlikely(port->irq != ser->irq))
+		return -EINVAL;
+	return 0;
+}
+
+static struct vt8500_port *vt8500_uart_ports[4];
+static struct uart_driver vt8500_uart_driver;
+
+#ifdef CONFIG_SERIAL_VT8500_CONSOLE
+
+static inline void wait_for_xmitr(struct uart_port *port)
+{
+	unsigned int status, tmout = 10000;
+
+	/* Wait up to 10ms for the character(s) to be sent. */
+	do {
+		status = vt8500_read(port, VT8500_URFIDX);
+
+		if (--tmout == 0)
+			break;
+		udelay(1);
+	} while (status & 0x10);
+}
+
+static void vt8500_console_putchar(struct uart_port *port, int c)
+{
+	wait_for_xmitr(port);
+	writeb(c, port->membase + VT8500_TXFIFO);
+}
+
+static void vt8500_console_write(struct console *co, const char *s,
+			      unsigned int count)
+{
+	struct vt8500_port *vt8500_port = vt8500_uart_ports[co->index];
+	unsigned long ier;
+
+	BUG_ON(co->index < 0 || co->index >= vt8500_uart_driver.nr);
+
+	ier = vt8500_read(&vt8500_port->uart, VT8500_URIER);
+	vt8500_write(&vt8500_port->uart, VT8500_URIER, 0);
+
+	uart_console_write(&vt8500_port->uart, s, count,
+			   vt8500_console_putchar);
+
+	/*
+	 *	Finally, wait for transmitter to become empty
+	 *	and switch back to FIFO
+	 */
+	wait_for_xmitr(&vt8500_port->uart);
+	vt8500_write(&vt8500_port->uart, VT8500_URIER, ier);
+}
+
+static int __init vt8500_console_setup(struct console *co, char *options)
+{
+	struct vt8500_port *vt8500_port;
+	int baud = 9600;
+	int bits = 8;
+	int parity = 'n';
+	int flow = 'n';
+
+	if (unlikely(co->index >= vt8500_uart_driver.nr || co->index < 0))
+		return -ENXIO;
+
+	vt8500_port = vt8500_uart_ports[co->index];
+
+	if (!vt8500_port)
+		return -ENODEV;
+
+	if (options)
+		uart_parse_options(options, &baud, &parity, &bits, &flow);
+
+	return uart_set_options(&vt8500_port->uart,
+				 co, baud, parity, bits, flow);
+}
+
+static struct console vt8500_console = {
+	.name = "ttyWMT",
+	.write = vt8500_console_write,
+	.device = uart_console_device,
+	.setup = vt8500_console_setup,
+	.flags = CON_PRINTBUFFER,
+	.index = -1,
+	.data = &vt8500_uart_driver,
+};
+
+#define VT8500_CONSOLE	(&vt8500_console)
+
+#else
+#define VT8500_CONSOLE	NULL
+#endif
+
+static struct uart_ops vt8500_uart_pops = {
+	.tx_empty	= vt8500_tx_empty,
+	.set_mctrl	= vt8500_set_mctrl,
+	.get_mctrl	= vt8500_get_mctrl,
+	.stop_tx	= vt8500_stop_tx,
+	.start_tx	= vt8500_start_tx,
+	.stop_rx	= vt8500_stop_rx,
+	.enable_ms	= vt8500_enable_ms,
+	.break_ctl	= vt8500_break_ctl,
+	.startup	= vt8500_startup,
+	.shutdown	= vt8500_shutdown,
+	.set_termios	= vt8500_set_termios,
+	.type		= vt8500_type,
+	.release_port	= vt8500_release_port,
+	.request_port	= vt8500_request_port,
+	.config_port	= vt8500_config_port,
+	.verify_port	= vt8500_verify_port,
+};
+
+static struct uart_driver vt8500_uart_driver = {
+	.owner		= THIS_MODULE,
+	.driver_name	= "vt8500_serial",
+	.dev_name	= "ttyWMT",
+	.nr		= 6,
+	.cons		= VT8500_CONSOLE,
+};
+
+static int __devinit vt8500_serial_probe(struct platform_device *pdev)
+{
+	struct vt8500_port *vt8500_port;
+	struct resource *mmres, *irqres;
+	int ret;
+
+	mmres = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	irqres = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+	if (!mmres || !irqres)
+		return -ENODEV;
+
+	vt8500_port = kzalloc(sizeof(struct vt8500_port), GFP_KERNEL);
+	if (!vt8500_port)
+		return -ENOMEM;
+
+	vt8500_port->uart.type = PORT_VT8500;
+	vt8500_port->uart.iotype = UPIO_MEM;
+	vt8500_port->uart.mapbase = mmres->start;
+	vt8500_port->uart.irq = irqres->start;
+	vt8500_port->uart.fifosize = 16;
+	vt8500_port->uart.ops = &vt8500_uart_pops;
+	vt8500_port->uart.line = pdev->id;
+	vt8500_port->uart.dev = &pdev->dev;
+	vt8500_port->uart.flags = UPF_IOREMAP | UPF_BOOT_AUTOCONF;
+	vt8500_port->uart.uartclk = 24000000;
+
+	snprintf(vt8500_port->name, sizeof(vt8500_port->name),
+		 "VT8500 UART%d", pdev->id);
+
+	vt8500_port->uart.membase = ioremap(mmres->start, resource_size(mmres));
+	if (!vt8500_port->uart.membase) {
+		ret = -ENOMEM;
+		goto err;
+	}
+
+	vt8500_uart_ports[pdev->id] = vt8500_port;
+
+	uart_add_one_port(&vt8500_uart_driver, &vt8500_port->uart);
+
+	platform_set_drvdata(pdev, vt8500_port);
+
+	return 0;
+
+err:
+	kfree(vt8500_port);
+	return ret;
+}
+
+static int __devexit vt8500_serial_remove(struct platform_device *pdev)
+{
+	struct vt8500_port *vt8500_port = platform_get_drvdata(pdev);
+
+	platform_set_drvdata(pdev, NULL);
+	uart_remove_one_port(&vt8500_uart_driver, &vt8500_port->uart);
+	kfree(vt8500_port);
+
+	return 0;
+}
+
+static struct platform_driver vt8500_platform_driver = {
+	.probe  = vt8500_serial_probe,
+	.remove = __devexit_p(vt8500_serial_remove),
+	.driver = {
+		.name = "vt8500_serial",
+		.owner = THIS_MODULE,
+	},
+};
+
+static int __init vt8500_serial_init(void)
+{
+	int ret;
+
+	ret = uart_register_driver(&vt8500_uart_driver);
+	if (unlikely(ret))
+		return ret;
+
+	ret = platform_driver_register(&vt8500_platform_driver);
+
+	if (unlikely(ret))
+		uart_unregister_driver(&vt8500_uart_driver);
+
+	return ret;
+}
+
+static void __exit vt8500_serial_exit(void)
+{
+#ifdef CONFIG_SERIAL_VT8500_CONSOLE
+	unregister_console(&vt8500_console);
+#endif
+	platform_driver_unregister(&vt8500_platform_driver);
+	uart_unregister_driver(&vt8500_uart_driver);
+}
+
+module_init(vt8500_serial_init);
+module_exit(vt8500_serial_exit);
+
+MODULE_AUTHOR("Alexey Charkov <alchark@gmail.com>");
+MODULE_DESCRIPTION("Driver for vt8500 serial device");
+MODULE_LICENSE("GPL");
diff --git a/ap/os/linux/linux-3.4.x/drivers/tty/serial/xilinx_uartps.c b/ap/os/linux/linux-3.4.x/drivers/tty/serial/xilinx_uartps.c
new file mode 100644
index 0000000..778c39a
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/tty/serial/xilinx_uartps.c
@@ -0,0 +1,1116 @@
+/*
+ * Xilinx PS UART driver
+ *
+ * 2011 (c) Xilinx 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.
+ *
+ */
+
+#include <linux/platform_device.h>
+#include <linux/serial.h>
+#include <linux/serial_core.h>
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
+#include <linux/console.h>
+#include <linux/irq.h>
+#include <linux/io.h>
+#include <linux/of.h>
+#include <linux/module.h>
+
+#define XUARTPS_TTY_NAME	"ttyPS"
+#define XUARTPS_NAME		"xuartps"
+#define XUARTPS_MAJOR		0	/* use dynamic node allocation */
+#define XUARTPS_MINOR		0	/* works best with devtmpfs */
+#define XUARTPS_NR_PORTS	2
+#define XUARTPS_FIFO_SIZE	16	/* FIFO size */
+#define XUARTPS_REGISTER_SPACE	0xFFF
+
+#define xuartps_readl(offset)		ioread32(port->membase + offset)
+#define xuartps_writel(val, offset)	iowrite32(val, port->membase + offset)
+
+/********************************Register Map********************************/
+/** UART
+ *
+ * Register offsets for the UART.
+ *
+ */
+#define XUARTPS_CR_OFFSET	0x00  /* Control Register [8:0] */
+#define XUARTPS_MR_OFFSET	0x04  /* Mode Register [10:0] */
+#define XUARTPS_IER_OFFSET	0x08  /* Interrupt Enable [10:0] */
+#define XUARTPS_IDR_OFFSET	0x0C  /* Interrupt Disable [10:0] */
+#define XUARTPS_IMR_OFFSET	0x10  /* Interrupt Mask [10:0] */
+#define XUARTPS_ISR_OFFSET	0x14  /* Interrupt Status [10:0]*/
+#define XUARTPS_BAUDGEN_OFFSET	0x18  /* Baud Rate Generator [15:0] */
+#define XUARTPS_RXTOUT_OFFSET	0x1C  /* RX Timeout [7:0] */
+#define XUARTPS_RXWM_OFFSET	0x20  /* RX FIFO Trigger Level [5:0] */
+#define XUARTPS_MODEMCR_OFFSET	0x24  /* Modem Control [5:0] */
+#define XUARTPS_MODEMSR_OFFSET	0x28  /* Modem Status [8:0] */
+#define XUARTPS_SR_OFFSET	0x2C  /* Channel Status [11:0] */
+#define XUARTPS_FIFO_OFFSET	0x30  /* FIFO [15:0] or [7:0] */
+#define XUARTPS_BAUDDIV_OFFSET	0x34  /* Baud Rate Divider [7:0] */
+#define XUARTPS_FLOWDEL_OFFSET	0x38  /* Flow Delay [15:0] */
+#define XUARTPS_IRRX_PWIDTH_OFFSET 0x3C /* IR Minimum Received Pulse
+						Width [15:0] */
+#define XUARTPS_IRTX_PWIDTH_OFFSET 0x40 /* IR Transmitted pulse
+						Width [7:0] */
+#define XUARTPS_TXWM_OFFSET	0x44  /* TX FIFO Trigger Level [5:0] */
+
+/** Control Register
+ *
+ * The Control register (CR) controls the major functions of the device.
+ *
+ * Control Register Bit Definitions
+ */
+#define XUARTPS_CR_STOPBRK	0x00000100  /* Stop TX break */
+#define XUARTPS_CR_STARTBRK	0x00000080  /* Set TX break */
+#define XUARTPS_CR_TX_DIS	0x00000020  /* TX disabled. */
+#define XUARTPS_CR_TX_EN	0x00000010  /* TX enabled */
+#define XUARTPS_CR_RX_DIS	0x00000008  /* RX disabled. */
+#define XUARTPS_CR_RX_EN	0x00000004  /* RX enabled */
+#define XUARTPS_CR_TXRST	0x00000002  /* TX logic reset */
+#define XUARTPS_CR_RXRST	0x00000001  /* RX logic reset */
+#define XUARTPS_CR_RST_TO	0x00000040  /* Restart Timeout Counter */
+
+/** Mode Register
+ *
+ * The mode register (MR) defines the mode of transfer as well as the data
+ * format. If this register is modified during transmission or reception,
+ * data validity cannot be guaranteed.
+ *
+ * Mode Register Bit Definitions
+ *
+ */
+#define XUARTPS_MR_CLKSEL		0x00000001  /* Pre-scalar selection */
+#define XUARTPS_MR_CHMODE_L_LOOP	0x00000200  /* Local loop back mode */
+#define XUARTPS_MR_CHMODE_NORM		0x00000000  /* Normal mode */
+
+#define XUARTPS_MR_STOPMODE_2_BIT	0x00000080  /* 2 stop bits */
+#define XUARTPS_MR_STOPMODE_1_BIT	0x00000000  /* 1 stop bit */
+
+#define XUARTPS_MR_PARITY_NONE		0x00000020  /* No parity mode */
+#define XUARTPS_MR_PARITY_MARK		0x00000018  /* Mark parity mode */
+#define XUARTPS_MR_PARITY_SPACE		0x00000010  /* Space parity mode */
+#define XUARTPS_MR_PARITY_ODD		0x00000008  /* Odd parity mode */
+#define XUARTPS_MR_PARITY_EVEN		0x00000000  /* Even parity mode */
+
+#define XUARTPS_MR_CHARLEN_6_BIT	0x00000006  /* 6 bits data */
+#define XUARTPS_MR_CHARLEN_7_BIT	0x00000004  /* 7 bits data */
+#define XUARTPS_MR_CHARLEN_8_BIT	0x00000000  /* 8 bits data */
+
+/** Interrupt Registers
+ *
+ * Interrupt control logic uses the interrupt enable register (IER) and the
+ * interrupt disable register (IDR) to set the value of the bits in the
+ * interrupt mask register (IMR). The IMR determines whether to pass an
+ * interrupt to the interrupt status register (ISR).
+ * Writing a 1 to IER Enables an interrupt, writing a 1 to IDR disables an
+ * interrupt. IMR and ISR are read only, and IER and IDR are write only.
+ * Reading either IER or IDR returns 0x00.
+ *
+ * All four registers have the same bit definitions.
+ */
+#define XUARTPS_IXR_TOUT	0x00000100 /* RX Timeout error interrupt */
+#define XUARTPS_IXR_PARITY	0x00000080 /* Parity error interrupt */
+#define XUARTPS_IXR_FRAMING	0x00000040 /* Framing error interrupt */
+#define XUARTPS_IXR_OVERRUN	0x00000020 /* Overrun error interrupt */
+#define XUARTPS_IXR_TXFULL	0x00000010 /* TX FIFO Full interrupt */
+#define XUARTPS_IXR_TXEMPTY	0x00000008 /* TX FIFO empty interrupt */
+#define XUARTPS_ISR_RXEMPTY	0x00000002 /* RX FIFO empty interrupt */
+#define XUARTPS_IXR_RXTRIG	0x00000001 /* RX FIFO trigger interrupt */
+#define XUARTPS_IXR_RXFULL	0x00000004 /* RX FIFO full interrupt. */
+#define XUARTPS_IXR_RXEMPTY	0x00000002 /* RX FIFO empty interrupt. */
+#define XUARTPS_IXR_MASK	0x00001FFF /* Valid bit mask */
+
+/** Channel Status Register
+ *
+ * The channel status register (CSR) is provided to enable the control logic
+ * to monitor the status of bits in the channel interrupt status register,
+ * even if these are masked out by the interrupt mask register.
+ */
+#define XUARTPS_SR_RXEMPTY	0x00000002 /* RX FIFO empty */
+#define XUARTPS_SR_TXEMPTY	0x00000008 /* TX FIFO empty */
+#define XUARTPS_SR_TXFULL	0x00000010 /* TX FIFO full */
+#define XUARTPS_SR_RXTRIG	0x00000001 /* Rx Trigger */
+
+/**
+ * xuartps_isr - Interrupt handler
+ * @irq: Irq number
+ * @dev_id: Id of the port
+ *
+ * Returns IRQHANDLED
+ **/
+static irqreturn_t xuartps_isr(int irq, void *dev_id)
+{
+	struct uart_port *port = (struct uart_port *)dev_id;
+	struct tty_struct *tty;
+	unsigned long flags;
+	unsigned int isrstatus, numbytes;
+	unsigned int data;
+	char status = TTY_NORMAL;
+
+	/* Get the tty which could be NULL so don't assume it's valid */
+	tty = tty_port_tty_get(&port->state->port);
+
+	spin_lock_irqsave(&port->lock, flags);
+
+	/* Read the interrupt status register to determine which
+	 * interrupt(s) is/are active.
+	 */
+	isrstatus = xuartps_readl(XUARTPS_ISR_OFFSET);
+
+	/* drop byte with parity error if IGNPAR specified */
+	if (isrstatus & port->ignore_status_mask & XUARTPS_IXR_PARITY)
+		isrstatus &= ~(XUARTPS_IXR_RXTRIG | XUARTPS_IXR_TOUT);
+
+	isrstatus &= port->read_status_mask;
+	isrstatus &= ~port->ignore_status_mask;
+
+	if ((isrstatus & XUARTPS_IXR_TOUT) ||
+		(isrstatus & XUARTPS_IXR_RXTRIG)) {
+		/* Receive Timeout Interrupt */
+		while ((xuartps_readl(XUARTPS_SR_OFFSET) &
+			XUARTPS_SR_RXEMPTY) != XUARTPS_SR_RXEMPTY) {
+			data = xuartps_readl(XUARTPS_FIFO_OFFSET);
+			port->icount.rx++;
+
+			if (isrstatus & XUARTPS_IXR_PARITY) {
+				port->icount.parity++;
+				status = TTY_PARITY;
+			} else if (isrstatus & XUARTPS_IXR_FRAMING) {
+				port->icount.frame++;
+				status = TTY_FRAME;
+			} else if (isrstatus & XUARTPS_IXR_OVERRUN)
+				port->icount.overrun++;
+
+			if (tty)
+				uart_insert_char(port, isrstatus,
+						XUARTPS_IXR_OVERRUN, data,
+						status);
+		}
+		spin_unlock(&port->lock);
+		if (tty)
+			tty_flip_buffer_push(tty);
+		spin_lock(&port->lock);
+	}
+
+	/* Dispatch an appropriate handler */
+	if ((isrstatus & XUARTPS_IXR_TXEMPTY) == XUARTPS_IXR_TXEMPTY) {
+		if (uart_circ_empty(&port->state->xmit)) {
+			xuartps_writel(XUARTPS_IXR_TXEMPTY,
+						XUARTPS_IDR_OFFSET);
+		} else {
+			numbytes = port->fifosize;
+			/* Break if no more data available in the UART buffer */
+			while (numbytes--) {
+				if (uart_circ_empty(&port->state->xmit))
+					break;
+				/* Get the data from the UART circular buffer
+				 * and write it to the xuartps's TX_FIFO
+				 * register.
+				 */
+				xuartps_writel(
+					port->state->xmit.buf[port->state->xmit.
+					tail], XUARTPS_FIFO_OFFSET);
+
+				port->icount.tx++;
+
+				/* Adjust the tail of the UART buffer and wrap
+				 * the buffer if it reaches limit.
+				 */
+				port->state->xmit.tail =
+					(port->state->xmit.tail + 1) & \
+						(UART_XMIT_SIZE - 1);
+			}
+
+			if (uart_circ_chars_pending(
+					&port->state->xmit) < WAKEUP_CHARS)
+				uart_write_wakeup(port);
+		}
+	}
+
+	xuartps_writel(isrstatus, XUARTPS_ISR_OFFSET);
+
+	/* be sure to release the lock and tty before leaving */
+	spin_unlock_irqrestore(&port->lock, flags);
+	tty_kref_put(tty);
+
+	return IRQ_HANDLED;
+}
+
+/**
+ * xuartps_set_baud_rate - Calculate and set the baud rate
+ * @port: Handle to the uart port structure
+ * @baud: Baud rate to set
+ *
+ * Returns baud rate, requested baud when possible, or actual baud when there
+ *	was too much error
+ **/
+static unsigned int xuartps_set_baud_rate(struct uart_port *port,
+						unsigned int baud)
+{
+	unsigned int sel_clk;
+	unsigned int calc_baud = 0;
+	unsigned int brgr_val, brdiv_val;
+	unsigned int bauderror;
+
+	/* Formula to obtain baud rate is
+	 *	baud_tx/rx rate = sel_clk/CD * (BDIV + 1)
+	 *	input_clk = (Uart User Defined Clock or Apb Clock)
+	 *		depends on UCLKEN in MR Reg
+	 *	sel_clk = input_clk or input_clk/8;
+	 *		depends on CLKS in MR reg
+	 *	CD and BDIV depends on values in
+	 *			baud rate generate register
+	 *			baud rate clock divisor register
+	 */
+	sel_clk = port->uartclk;
+	if (xuartps_readl(XUARTPS_MR_OFFSET) & XUARTPS_MR_CLKSEL)
+		sel_clk = sel_clk / 8;
+
+	/* Find the best values for baud generation */
+	for (brdiv_val = 4; brdiv_val < 255; brdiv_val++) {
+
+		brgr_val = sel_clk / (baud * (brdiv_val + 1));
+		if (brgr_val < 2 || brgr_val > 65535)
+			continue;
+
+		calc_baud = sel_clk / (brgr_val * (brdiv_val + 1));
+
+		if (baud > calc_baud)
+			bauderror = baud - calc_baud;
+		else
+			bauderror = calc_baud - baud;
+
+		/* use the values when percent error is acceptable */
+		if (((bauderror * 100) / baud) < 3) {
+			calc_baud = baud;
+			break;
+		}
+	}
+
+	/* Set the values for the new baud rate */
+	xuartps_writel(brgr_val, XUARTPS_BAUDGEN_OFFSET);
+	xuartps_writel(brdiv_val, XUARTPS_BAUDDIV_OFFSET);
+
+	return calc_baud;
+}
+
+/*----------------------Uart Operations---------------------------*/
+
+/**
+ * xuartps_start_tx -  Start transmitting bytes
+ * @port: Handle to the uart port structure
+ *
+ **/
+static void xuartps_start_tx(struct uart_port *port)
+{
+	unsigned int status, numbytes = port->fifosize;
+
+	if (uart_circ_empty(&port->state->xmit) || uart_tx_stopped(port))
+		return;
+
+	status = xuartps_readl(XUARTPS_CR_OFFSET);
+	/* Set the TX enable bit and clear the TX disable bit to enable the
+	 * transmitter.
+	 */
+	xuartps_writel((status & ~XUARTPS_CR_TX_DIS) | XUARTPS_CR_TX_EN,
+		XUARTPS_CR_OFFSET);
+
+	while (numbytes-- && ((xuartps_readl(XUARTPS_SR_OFFSET)
+		& XUARTPS_SR_TXFULL)) != XUARTPS_SR_TXFULL) {
+
+		/* Break if no more data available in the UART buffer */
+		if (uart_circ_empty(&port->state->xmit))
+			break;
+
+		/* Get the data from the UART circular buffer and
+		 * write it to the xuartps's TX_FIFO register.
+		 */
+		xuartps_writel(
+			port->state->xmit.buf[port->state->xmit.tail],
+			XUARTPS_FIFO_OFFSET);
+		port->icount.tx++;
+
+		/* Adjust the tail of the UART buffer and wrap
+		 * the buffer if it reaches limit.
+		 */
+		port->state->xmit.tail = (port->state->xmit.tail + 1) &
+					(UART_XMIT_SIZE - 1);
+	}
+
+	/* Enable the TX Empty interrupt */
+	xuartps_writel(XUARTPS_IXR_TXEMPTY, XUARTPS_IER_OFFSET);
+
+	if (uart_circ_chars_pending(&port->state->xmit) < WAKEUP_CHARS)
+		uart_write_wakeup(port);
+}
+
+/**
+ * xuartps_stop_tx - Stop TX
+ * @port: Handle to the uart port structure
+ *
+ **/
+static void xuartps_stop_tx(struct uart_port *port)
+{
+	unsigned int regval;
+
+	regval = xuartps_readl(XUARTPS_CR_OFFSET);
+	regval |= XUARTPS_CR_TX_DIS;
+	/* Disable the transmitter */
+	xuartps_writel(regval, XUARTPS_CR_OFFSET);
+}
+
+/**
+ * xuartps_stop_rx - Stop RX
+ * @port: Handle to the uart port structure
+ *
+ **/
+static void xuartps_stop_rx(struct uart_port *port)
+{
+	unsigned int regval;
+
+	regval = xuartps_readl(XUARTPS_CR_OFFSET);
+	regval |= XUARTPS_CR_RX_DIS;
+	/* Disable the receiver */
+	xuartps_writel(regval, XUARTPS_CR_OFFSET);
+}
+
+/**
+ * xuartps_tx_empty -  Check whether TX is empty
+ * @port: Handle to the uart port structure
+ *
+ * Returns TIOCSER_TEMT on success, 0 otherwise
+ **/
+static unsigned int xuartps_tx_empty(struct uart_port *port)
+{
+	unsigned int status;
+
+	status = xuartps_readl(XUARTPS_ISR_OFFSET) & XUARTPS_IXR_TXEMPTY;
+	return status ? TIOCSER_TEMT : 0;
+}
+
+/**
+ * xuartps_break_ctl - Based on the input ctl we have to start or stop
+ *			transmitting char breaks
+ * @port: Handle to the uart port structure
+ * @ctl: Value based on which start or stop decision is taken
+ *
+ **/
+static void xuartps_break_ctl(struct uart_port *port, int ctl)
+{
+	unsigned int status;
+	unsigned long flags;
+
+	spin_lock_irqsave(&port->lock, flags);
+
+	status = xuartps_readl(XUARTPS_CR_OFFSET);
+
+	if (ctl == -1)
+		xuartps_writel(XUARTPS_CR_STARTBRK | status,
+					XUARTPS_CR_OFFSET);
+	else {
+		if ((status & XUARTPS_CR_STOPBRK) == 0)
+			xuartps_writel(XUARTPS_CR_STOPBRK | status,
+					 XUARTPS_CR_OFFSET);
+	}
+	spin_unlock_irqrestore(&port->lock, flags);
+}
+
+/**
+ * xuartps_set_termios - termios operations, handling data length, parity,
+ *				stop bits, flow control, baud rate
+ * @port: Handle to the uart port structure
+ * @termios: Handle to the input termios structure
+ * @old: Values of the previously saved termios structure
+ *
+ **/
+static void xuartps_set_termios(struct uart_port *port,
+				struct ktermios *termios, struct ktermios *old)
+{
+	unsigned int cval = 0;
+	unsigned int baud;
+	unsigned long flags;
+	unsigned int ctrl_reg, mode_reg;
+
+	spin_lock_irqsave(&port->lock, flags);
+
+	/* Empty the receive FIFO 1st before making changes */
+	while ((xuartps_readl(XUARTPS_SR_OFFSET) &
+		 XUARTPS_SR_RXEMPTY) != XUARTPS_SR_RXEMPTY) {
+		xuartps_readl(XUARTPS_FIFO_OFFSET);
+	}
+
+	/* Disable the TX and RX to set baud rate */
+	xuartps_writel(xuartps_readl(XUARTPS_CR_OFFSET) |
+			(XUARTPS_CR_TX_DIS | XUARTPS_CR_RX_DIS),
+			XUARTPS_CR_OFFSET);
+
+	/* Min baud rate = 6bps and Max Baud Rate is 10Mbps for 100Mhz clk */
+	baud = uart_get_baud_rate(port, termios, old, 0, 10000000);
+	baud = xuartps_set_baud_rate(port, baud);
+	if (tty_termios_baud_rate(termios))
+		tty_termios_encode_baud_rate(termios, baud, baud);
+
+	/*
+	 * Update the per-port timeout.
+	 */
+	uart_update_timeout(port, termios->c_cflag, baud);
+
+	/* Set TX/RX Reset */
+	xuartps_writel(xuartps_readl(XUARTPS_CR_OFFSET) |
+			(XUARTPS_CR_TXRST | XUARTPS_CR_RXRST),
+			XUARTPS_CR_OFFSET);
+
+	ctrl_reg = xuartps_readl(XUARTPS_CR_OFFSET);
+
+	/* Clear the RX disable and TX disable bits and then set the TX enable
+	 * bit and RX enable bit to enable the transmitter and receiver.
+	 */
+	xuartps_writel(
+		(ctrl_reg & ~(XUARTPS_CR_TX_DIS | XUARTPS_CR_RX_DIS))
+			| (XUARTPS_CR_TX_EN | XUARTPS_CR_RX_EN),
+			XUARTPS_CR_OFFSET);
+
+	xuartps_writel(10, XUARTPS_RXTOUT_OFFSET);
+
+	port->read_status_mask = XUARTPS_IXR_TXEMPTY | XUARTPS_IXR_RXTRIG |
+			XUARTPS_IXR_OVERRUN | XUARTPS_IXR_TOUT;
+	port->ignore_status_mask = 0;
+
+	if (termios->c_iflag & INPCK)
+		port->read_status_mask |= XUARTPS_IXR_PARITY |
+		XUARTPS_IXR_FRAMING;
+
+	if (termios->c_iflag & IGNPAR)
+		port->ignore_status_mask |= XUARTPS_IXR_PARITY |
+			XUARTPS_IXR_FRAMING | XUARTPS_IXR_OVERRUN;
+
+	/* ignore all characters if CREAD is not set */
+	if ((termios->c_cflag & CREAD) == 0)
+		port->ignore_status_mask |= XUARTPS_IXR_RXTRIG |
+			XUARTPS_IXR_TOUT | XUARTPS_IXR_PARITY |
+			XUARTPS_IXR_FRAMING | XUARTPS_IXR_OVERRUN;
+
+	mode_reg = xuartps_readl(XUARTPS_MR_OFFSET);
+
+	/* Handling Data Size */
+	switch (termios->c_cflag & CSIZE) {
+	case CS6:
+		cval |= XUARTPS_MR_CHARLEN_6_BIT;
+		break;
+	case CS7:
+		cval |= XUARTPS_MR_CHARLEN_7_BIT;
+		break;
+	default:
+	case CS8:
+		cval |= XUARTPS_MR_CHARLEN_8_BIT;
+		termios->c_cflag &= ~CSIZE;
+		termios->c_cflag |= CS8;
+		break;
+	}
+
+	/* Handling Parity and Stop Bits length */
+	if (termios->c_cflag & CSTOPB)
+		cval |= XUARTPS_MR_STOPMODE_2_BIT; /* 2 STOP bits */
+	else
+		cval |= XUARTPS_MR_STOPMODE_1_BIT; /* 1 STOP bit */
+
+	if (termios->c_cflag & PARENB) {
+		/* Mark or Space parity */
+		if (termios->c_cflag & CMSPAR) {
+			if (termios->c_cflag & PARODD)
+				cval |= XUARTPS_MR_PARITY_MARK;
+			else
+				cval |= XUARTPS_MR_PARITY_SPACE;
+		} else if (termios->c_cflag & PARODD)
+				cval |= XUARTPS_MR_PARITY_ODD;
+			else
+				cval |= XUARTPS_MR_PARITY_EVEN;
+	} else
+		cval |= XUARTPS_MR_PARITY_NONE;
+	xuartps_writel(cval , XUARTPS_MR_OFFSET);
+
+	spin_unlock_irqrestore(&port->lock, flags);
+}
+
+/**
+ * xuartps_startup - Called when an application opens a xuartps port
+ * @port: Handle to the uart port structure
+ *
+ * Returns 0 on success, negative error otherwise
+ **/
+static int xuartps_startup(struct uart_port *port)
+{
+	unsigned int retval = 0, status = 0;
+
+	retval = request_irq(port->irq, xuartps_isr, 0, XUARTPS_NAME,
+								(void *)port);
+	if (retval)
+		return retval;
+
+	/* Disable the TX and RX */
+	xuartps_writel(XUARTPS_CR_TX_DIS | XUARTPS_CR_RX_DIS,
+						XUARTPS_CR_OFFSET);
+
+	/* Set the Control Register with TX/RX Enable, TX/RX Reset,
+	 * no break chars.
+	 */
+	xuartps_writel(XUARTPS_CR_TXRST | XUARTPS_CR_RXRST,
+				XUARTPS_CR_OFFSET);
+
+	status = xuartps_readl(XUARTPS_CR_OFFSET);
+
+	/* Clear the RX disable and TX disable bits and then set the TX enable
+	 * bit and RX enable bit to enable the transmitter and receiver.
+	 */
+	xuartps_writel((status & ~(XUARTPS_CR_TX_DIS | XUARTPS_CR_RX_DIS))
+			| (XUARTPS_CR_TX_EN | XUARTPS_CR_RX_EN |
+			XUARTPS_CR_STOPBRK), XUARTPS_CR_OFFSET);
+
+	/* Set the Mode Register with normal mode,8 data bits,1 stop bit,
+	 * no parity.
+	 */
+	xuartps_writel(XUARTPS_MR_CHMODE_NORM | XUARTPS_MR_STOPMODE_1_BIT
+		| XUARTPS_MR_PARITY_NONE | XUARTPS_MR_CHARLEN_8_BIT,
+		 XUARTPS_MR_OFFSET);
+
+	/* Set the RX FIFO Trigger level to 14 assuming FIFO size as 16 */
+	xuartps_writel(14, XUARTPS_RXWM_OFFSET);
+
+	/* Receive Timeout register is enabled with value of 10 */
+	xuartps_writel(10, XUARTPS_RXTOUT_OFFSET);
+
+
+	/* Set the Interrupt Registers with desired interrupts */
+	xuartps_writel(XUARTPS_IXR_TXEMPTY | XUARTPS_IXR_PARITY |
+		XUARTPS_IXR_FRAMING | XUARTPS_IXR_OVERRUN |
+		XUARTPS_IXR_RXTRIG | XUARTPS_IXR_TOUT, XUARTPS_IER_OFFSET);
+	xuartps_writel(~(XUARTPS_IXR_TXEMPTY | XUARTPS_IXR_PARITY |
+		XUARTPS_IXR_FRAMING | XUARTPS_IXR_OVERRUN |
+		XUARTPS_IXR_RXTRIG | XUARTPS_IXR_TOUT), XUARTPS_IDR_OFFSET);
+
+	return retval;
+}
+
+/**
+ * xuartps_shutdown - Called when an application closes a xuartps port
+ * @port: Handle to the uart port structure
+ *
+ **/
+static void xuartps_shutdown(struct uart_port *port)
+{
+	int status;
+
+	/* Disable interrupts */
+	status = xuartps_readl(XUARTPS_IMR_OFFSET);
+	xuartps_writel(status, XUARTPS_IDR_OFFSET);
+
+	/* Disable the TX and RX */
+	xuartps_writel(XUARTPS_CR_TX_DIS | XUARTPS_CR_RX_DIS,
+				 XUARTPS_CR_OFFSET);
+	free_irq(port->irq, port);
+}
+
+/**
+ * xuartps_type - Set UART type to xuartps port
+ * @port: Handle to the uart port structure
+ *
+ * Returns string on success, NULL otherwise
+ **/
+static const char *xuartps_type(struct uart_port *port)
+{
+	return port->type == PORT_XUARTPS ? XUARTPS_NAME : NULL;
+}
+
+/**
+ * xuartps_verify_port - Verify the port params
+ * @port: Handle to the uart port structure
+ * @ser: Handle to the structure whose members are compared
+ *
+ * Returns 0 if success otherwise -EINVAL
+ **/
+static int xuartps_verify_port(struct uart_port *port,
+					struct serial_struct *ser)
+{
+	if (ser->type != PORT_UNKNOWN && ser->type != PORT_XUARTPS)
+		return -EINVAL;
+	if (port->irq != ser->irq)
+		return -EINVAL;
+	if (ser->io_type != UPIO_MEM)
+		return -EINVAL;
+	if (port->iobase != ser->port)
+		return -EINVAL;
+	if (ser->hub6 != 0)
+		return -EINVAL;
+	return 0;
+}
+
+/**
+ * xuartps_request_port - Claim the memory region attached to xuartps port,
+ *				called when the driver adds a xuartps port via
+ *				uart_add_one_port()
+ * @port: Handle to the uart port structure
+ *
+ * Returns 0, -ENOMEM if request fails
+ **/
+static int xuartps_request_port(struct uart_port *port)
+{
+	if (!request_mem_region(port->mapbase, XUARTPS_REGISTER_SPACE,
+					 XUARTPS_NAME)) {
+		return -ENOMEM;
+	}
+
+	port->membase = ioremap(port->mapbase, XUARTPS_REGISTER_SPACE);
+	if (!port->membase) {
+		dev_err(port->dev, "Unable to map registers\n");
+		release_mem_region(port->mapbase, XUARTPS_REGISTER_SPACE);
+		return -ENOMEM;
+	}
+	return 0;
+}
+
+/**
+ * xuartps_release_port - Release the memory region attached to a xuartps
+ *				port, called when the driver removes a xuartps
+ *				port via uart_remove_one_port().
+ * @port: Handle to the uart port structure
+ *
+ **/
+static void xuartps_release_port(struct uart_port *port)
+{
+	release_mem_region(port->mapbase, XUARTPS_REGISTER_SPACE);
+	iounmap(port->membase);
+	port->membase = NULL;
+}
+
+/**
+ * xuartps_config_port - Configure xuartps, called when the driver adds a
+ *				xuartps port
+ * @port: Handle to the uart port structure
+ * @flags: If any
+ *
+ **/
+static void xuartps_config_port(struct uart_port *port, int flags)
+{
+	if (flags & UART_CONFIG_TYPE && xuartps_request_port(port) == 0)
+		port->type = PORT_XUARTPS;
+}
+
+/**
+ * xuartps_get_mctrl - Get the modem control state
+ *
+ * @port: Handle to the uart port structure
+ *
+ * Returns the modem control state
+ *
+ **/
+static unsigned int xuartps_get_mctrl(struct uart_port *port)
+{
+	return TIOCM_CTS | TIOCM_DSR | TIOCM_CAR;
+}
+
+static void xuartps_set_mctrl(struct uart_port *port, unsigned int mctrl)
+{
+	/* N/A */
+}
+
+static void xuartps_enable_ms(struct uart_port *port)
+{
+	/* N/A */
+}
+
+/** The UART operations structure
+ */
+static struct uart_ops xuartps_ops = {
+	.set_mctrl	= xuartps_set_mctrl,
+	.get_mctrl	= xuartps_get_mctrl,
+	.enable_ms	= xuartps_enable_ms,
+
+	.start_tx	= xuartps_start_tx,	/* Start transmitting */
+	.stop_tx	= xuartps_stop_tx,	/* Stop transmission */
+	.stop_rx	= xuartps_stop_rx,	/* Stop reception */
+	.tx_empty	= xuartps_tx_empty,	/* Transmitter busy? */
+	.break_ctl	= xuartps_break_ctl,	/* Start/stop
+						 * transmitting break
+						 */
+	.set_termios	= xuartps_set_termios,	/* Set termios */
+	.startup	= xuartps_startup,	/* App opens xuartps */
+	.shutdown	= xuartps_shutdown,	/* App closes xuartps */
+	.type		= xuartps_type,		/* Set UART type */
+	.verify_port	= xuartps_verify_port,	/* Verification of port
+						 * params
+						 */
+	.request_port	= xuartps_request_port,	/* Claim resources
+						 * associated with a
+						 * xuartps port
+						 */
+	.release_port	= xuartps_release_port,	/* Release resources
+						 * associated with a
+						 * xuartps port
+						 */
+	.config_port	= xuartps_config_port,	/* Configure when driver
+						 * adds a xuartps port
+						 */
+};
+
+static struct uart_port xuartps_port[2];
+
+/**
+ * xuartps_get_port - Configure the port from the platform device resource
+ *			info
+ *
+ * Returns a pointer to a uart_port or NULL for failure
+ **/
+static struct uart_port *xuartps_get_port(void)
+{
+	struct uart_port *port;
+	int id;
+
+	/* Find the next unused port */
+	for (id = 0; id < XUARTPS_NR_PORTS; id++)
+		if (xuartps_port[id].mapbase == 0)
+			break;
+
+	if (id >= XUARTPS_NR_PORTS)
+		return NULL;
+
+	port = &xuartps_port[id];
+
+	/* At this point, we've got an empty uart_port struct, initialize it */
+	spin_lock_init(&port->lock);
+	port->membase	= NULL;
+	port->iobase	= 1; /* mark port in use */
+	port->irq	= 0;
+	port->type	= PORT_UNKNOWN;
+	port->iotype	= UPIO_MEM32;
+	port->flags	= UPF_BOOT_AUTOCONF;
+	port->ops	= &xuartps_ops;
+	port->fifosize	= XUARTPS_FIFO_SIZE;
+	port->line	= id;
+	port->dev	= NULL;
+	return port;
+}
+
+/*-----------------------Console driver operations--------------------------*/
+
+#ifdef CONFIG_SERIAL_XILINX_PS_UART_CONSOLE
+/**
+ * xuartps_console_wait_tx - Wait for the TX to be full
+ * @port: Handle to the uart port structure
+ *
+ **/
+static void xuartps_console_wait_tx(struct uart_port *port)
+{
+	while ((xuartps_readl(XUARTPS_SR_OFFSET) & XUARTPS_SR_TXEMPTY)
+				!= XUARTPS_SR_TXEMPTY)
+		barrier();
+}
+
+/**
+ * xuartps_console_putchar - write the character to the FIFO buffer
+ * @port: Handle to the uart port structure
+ * @ch: Character to be written
+ *
+ **/
+static void xuartps_console_putchar(struct uart_port *port, int ch)
+{
+	xuartps_console_wait_tx(port);
+	xuartps_writel(ch, XUARTPS_FIFO_OFFSET);
+}
+
+/**
+ * xuartps_console_write - perform write operation
+ * @port: Handle to the uart port structure
+ * @s: Pointer to character array
+ * @count: No of characters
+ **/
+static void xuartps_console_write(struct console *co, const char *s,
+				unsigned int count)
+{
+	struct uart_port *port = &xuartps_port[co->index];
+	unsigned long flags;
+	unsigned int imr;
+	int locked = 1;
+
+	if (oops_in_progress)
+		locked = spin_trylock_irqsave(&port->lock, flags);
+	else
+		spin_lock_irqsave(&port->lock, flags);
+
+	/* save and disable interrupt */
+	imr = xuartps_readl(XUARTPS_IMR_OFFSET);
+	xuartps_writel(imr, XUARTPS_IDR_OFFSET);
+
+	uart_console_write(port, s, count, xuartps_console_putchar);
+	xuartps_console_wait_tx(port);
+
+	/* restore interrupt state, it seems like there may be a h/w bug
+	 * in that the interrupt enable register should not need to be
+	 * written based on the data sheet
+	 */
+	xuartps_writel(~imr, XUARTPS_IDR_OFFSET);
+	xuartps_writel(imr, XUARTPS_IER_OFFSET);
+
+	if (locked)
+		spin_unlock_irqrestore(&port->lock, flags);
+}
+
+/**
+ * xuartps_console_setup - Initialize the uart to default config
+ * @co: Console handle
+ * @options: Initial settings of uart
+ *
+ * Returns 0, -ENODEV if no device
+ **/
+static int __init xuartps_console_setup(struct console *co, char *options)
+{
+	struct uart_port *port = &xuartps_port[co->index];
+	int baud = 9600;
+	int bits = 8;
+	int parity = 'n';
+	int flow = 'n';
+
+	if (co->index < 0 || co->index >= XUARTPS_NR_PORTS)
+		return -EINVAL;
+
+	if (!port->mapbase) {
+		pr_debug("console on ttyPS%i not present\n", co->index);
+		return -ENODEV;
+	}
+
+	if (options)
+		uart_parse_options(options, &baud, &parity, &bits, &flow);
+
+	return uart_set_options(port, co, baud, parity, bits, flow);
+}
+
+static struct uart_driver xuartps_uart_driver;
+
+static struct console xuartps_console = {
+	.name	= XUARTPS_TTY_NAME,
+	.write	= xuartps_console_write,
+	.device	= uart_console_device,
+	.setup	= xuartps_console_setup,
+	.flags	= CON_PRINTBUFFER,
+	.index	= -1, /* Specified on the cmdline (e.g. console=ttyPS ) */
+	.data	= &xuartps_uart_driver,
+};
+
+/**
+ * xuartps_console_init - Initialization call
+ *
+ * Returns 0 on success, negative error otherwise
+ **/
+static int __init xuartps_console_init(void)
+{
+	register_console(&xuartps_console);
+	return 0;
+}
+
+console_initcall(xuartps_console_init);
+
+#endif /* CONFIG_SERIAL_XILINX_PS_UART_CONSOLE */
+
+/** Structure Definitions
+ */
+static struct uart_driver xuartps_uart_driver = {
+	.owner		= THIS_MODULE,		/* Owner */
+	.driver_name	= XUARTPS_NAME,		/* Driver name */
+	.dev_name	= XUARTPS_TTY_NAME,	/* Node name */
+	.major		= XUARTPS_MAJOR,	/* Major number */
+	.minor		= XUARTPS_MINOR,	/* Minor number */
+	.nr		= XUARTPS_NR_PORTS,	/* Number of UART ports */
+#ifdef CONFIG_SERIAL_XILINX_PS_UART_CONSOLE
+	.cons		= &xuartps_console,	/* Console */
+#endif
+};
+
+/* ---------------------------------------------------------------------
+ * Platform bus binding
+ */
+/**
+ * xuartps_probe - Platform driver probe
+ * @pdev: Pointer to the platform device structure
+ *
+ * Returns 0 on success, negative error otherwise
+ **/
+static int __devinit xuartps_probe(struct platform_device *pdev)
+{
+	int rc, irq;
+	struct uart_port *port;
+	struct resource *res;
+	int clk = 0;
+
+#ifdef CONFIG_OF
+	const unsigned int *prop;
+
+	prop = of_get_property(pdev->dev.of_node, "clock", NULL);
+	if (prop)
+		clk = be32_to_cpup(prop);
+#else
+	clk = *((unsigned int *)(pdev->dev.platform_data));
+#endif
+	if (!clk) {
+		dev_err(&pdev->dev, "no clock specified\n");
+		return -ENODEV;
+	}
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!res)
+		return -ENODEV;
+
+	irq = platform_get_irq(pdev, 0);
+	if (irq <= 0)
+		return -ENXIO;
+
+	/* Initialize the port structure */
+	port = xuartps_get_port();
+
+	if (!port) {
+		dev_err(&pdev->dev, "Cannot get uart_port structure\n");
+		return -ENODEV;
+	} else {
+		/* Register the port.
+		 * This function also registers this device with the tty layer
+		 * and triggers invocation of the config_port() entry point.
+		 */
+		port->mapbase = res->start;
+		port->irq = irq;
+		port->dev = &pdev->dev;
+		port->uartclk = clk;
+		dev_set_drvdata(&pdev->dev, port);
+		rc = uart_add_one_port(&xuartps_uart_driver, port);
+		if (rc) {
+			dev_err(&pdev->dev,
+				"uart_add_one_port() failed; err=%i\n", rc);
+			dev_set_drvdata(&pdev->dev, NULL);
+			return rc;
+		}
+		return 0;
+	}
+}
+
+/**
+ * xuartps_remove - called when the platform driver is unregistered
+ * @pdev: Pointer to the platform device structure
+ *
+ * Returns 0 on success, negative error otherwise
+ **/
+static int __devexit xuartps_remove(struct platform_device *pdev)
+{
+	struct uart_port *port = dev_get_drvdata(&pdev->dev);
+	int rc = 0;
+
+	/* Remove the xuartps port from the serial core */
+	if (port) {
+		rc = uart_remove_one_port(&xuartps_uart_driver, port);
+		dev_set_drvdata(&pdev->dev, NULL);
+		port->mapbase = 0;
+	}
+	return rc;
+}
+
+/**
+ * xuartps_suspend - suspend event
+ * @pdev: Pointer to the platform device structure
+ * @state: State of the device
+ *
+ * Returns 0
+ **/
+static int xuartps_suspend(struct platform_device *pdev, pm_message_t state)
+{
+	/* Call the API provided in serial_core.c file which handles
+	 * the suspend.
+	 */
+	uart_suspend_port(&xuartps_uart_driver, &xuartps_port[pdev->id]);
+	return 0;
+}
+
+/**
+ * xuartps_resume - Resume after a previous suspend
+ * @pdev: Pointer to the platform device structure
+ *
+ * Returns 0
+ **/
+static int xuartps_resume(struct platform_device *pdev)
+{
+	uart_resume_port(&xuartps_uart_driver, &xuartps_port[pdev->id]);
+	return 0;
+}
+
+/* Match table for of_platform binding */
+
+#ifdef CONFIG_OF
+static struct of_device_id xuartps_of_match[] __devinitdata = {
+	{ .compatible = "xlnx,xuartps", },
+	{}
+};
+MODULE_DEVICE_TABLE(of, xuartps_of_match);
+#else
+#define xuartps_of_match NULL
+#endif
+
+static struct platform_driver xuartps_platform_driver = {
+	.probe   = xuartps_probe,		/* Probe method */
+	.remove  = __exit_p(xuartps_remove),	/* Detach method */
+	.suspend = xuartps_suspend,		/* Suspend */
+	.resume  = xuartps_resume,		/* Resume after a suspend */
+	.driver  = {
+		.owner = THIS_MODULE,
+		.name = XUARTPS_NAME,		/* Driver name */
+		.of_match_table = xuartps_of_match,
+		},
+};
+
+/* ---------------------------------------------------------------------
+ * Module Init and Exit
+ */
+/**
+ * xuartps_init - Initial driver registration call
+ *
+ * Returns whether the registration was successful or not
+ **/
+static int __init xuartps_init(void)
+{
+	int retval = 0;
+
+	/* Register the xuartps driver with the serial core */
+	retval = uart_register_driver(&xuartps_uart_driver);
+	if (retval)
+		return retval;
+
+	/* Register the platform driver */
+	retval = platform_driver_register(&xuartps_platform_driver);
+	if (retval)
+		uart_unregister_driver(&xuartps_uart_driver);
+
+	return retval;
+}
+
+/**
+ * xuartps_exit - Driver unregistration call
+ **/
+static void __exit xuartps_exit(void)
+{
+	/* The order of unregistration is important. Unregister the
+	 * UART driver before the platform driver crashes the system.
+	 */
+
+	/* Unregister the platform driver */
+	platform_driver_unregister(&xuartps_platform_driver);
+
+	/* Unregister the xuartps driver */
+	uart_unregister_driver(&xuartps_uart_driver);
+}
+
+module_init(xuartps_init);
+module_exit(xuartps_exit);
+
+MODULE_DESCRIPTION("Driver for PS UART");
+MODULE_AUTHOR("Xilinx Inc.");
+MODULE_LICENSE("GPL");
diff --git a/ap/os/linux/linux-3.4.x/drivers/tty/serial/zs.c b/ap/os/linux/linux-3.4.x/drivers/tty/serial/zs.c
new file mode 100644
index 0000000..4001eee
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/tty/serial/zs.c
@@ -0,0 +1,1304 @@
+/*
+ * zs.c: Serial port driver for IOASIC DECstations.
+ *
+ * Derived from drivers/sbus/char/sunserial.c by Paul Mackerras.
+ * Derived from drivers/macintosh/macserial.c by Harald Koerfgen.
+ *
+ * DECstation changes
+ * Copyright (C) 1998-2000 Harald Koerfgen
+ * Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2007  Maciej W. Rozycki
+ *
+ * For the rest of the code the original Copyright applies:
+ * Copyright (C) 1996 Paul Mackerras (Paul.Mackerras@cs.anu.edu.au)
+ * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
+ *
+ *
+ * Note: for IOASIC systems the wiring is as follows:
+ *
+ * mouse/keyboard:
+ * DIN-7 MJ-4  signal        SCC
+ * 2     1     TxD       <-  A.TxD
+ * 3     4     RxD       ->  A.RxD
+ *
+ * EIA-232/EIA-423:
+ * DB-25 MMJ-6 signal        SCC
+ * 2     2     TxD       <-  B.TxD
+ * 3     5     RxD       ->  B.RxD
+ * 4           RTS       <- ~A.RTS
+ * 5           CTS       -> ~B.CTS
+ * 6     6     DSR       -> ~A.SYNC
+ * 8           CD        -> ~B.DCD
+ * 12          DSRS(DCE) -> ~A.CTS  (*)
+ * 15          TxC       ->  B.TxC
+ * 17          RxC       ->  B.RxC
+ * 20    1     DTR       <- ~A.DTR
+ * 22          RI        -> ~A.DCD
+ * 23          DSRS(DTE) <- ~B.RTS
+ *
+ * (*) EIA-232 defines the signal at this pin to be SCD, while DSRS(DCE)
+ *     is shared with DSRS(DTE) at pin 23.
+ *
+ * As you can immediately notice the wiring of the RTS, DTR and DSR signals
+ * is a bit odd.  This makes the handling of port B unnecessarily
+ * complicated and prevents the use of some automatic modes of operation.
+ */
+
+#if defined(CONFIG_SERIAL_ZS_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ)
+#define SUPPORT_SYSRQ
+#endif
+
+#include <linux/bug.h>
+#include <linux/console.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/ioport.h>
+#include <linux/irqflags.h>
+#include <linux/kernel.h>
+#include <linux/major.h>
+#include <linux/serial.h>
+#include <linux/serial_core.h>
+#include <linux/spinlock.h>
+#include <linux/sysrq.h>
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
+#include <linux/types.h>
+
+#include <linux/atomic.h>
+
+#include <asm/dec/interrupts.h>
+#include <asm/dec/ioasic_addrs.h>
+#include <asm/dec/system.h>
+
+#include "zs.h"
+
+
+MODULE_AUTHOR("Maciej W. Rozycki <macro@linux-mips.org>");
+MODULE_DESCRIPTION("DECstation Z85C30 serial driver");
+MODULE_LICENSE("GPL");
+
+
+static char zs_name[] __initdata = "DECstation Z85C30 serial driver version ";
+static char zs_version[] __initdata = "0.10";
+
+/*
+ * It would be nice to dynamically allocate everything that
+ * depends on ZS_NUM_SCCS, so we could support any number of
+ * Z85C30s, but for now...
+ */
+#define ZS_NUM_SCCS	2		/* Max # of ZS chips supported.  */
+#define ZS_NUM_CHAN	2		/* 2 channels per chip.  */
+#define ZS_CHAN_A	0		/* Index of the channel A.  */
+#define ZS_CHAN_B	1		/* Index of the channel B.  */
+#define ZS_CHAN_IO_SIZE 8		/* IOMEM space size.  */
+#define ZS_CHAN_IO_STRIDE 4		/* Register alignment.  */
+#define ZS_CHAN_IO_OFFSET 1		/* The SCC resides on the high byte
+					   of the 16-bit IOBUS.  */
+#define ZS_CLOCK        7372800 	/* Z85C30 PCLK input clock rate.  */
+
+#define to_zport(uport) container_of(uport, struct zs_port, port)
+
+struct zs_parms {
+	resource_size_t scc[ZS_NUM_SCCS];
+	int irq[ZS_NUM_SCCS];
+};
+
+static struct zs_scc zs_sccs[ZS_NUM_SCCS];
+
+static u8 zs_init_regs[ZS_NUM_REGS] __initdata = {
+	0,				/* write 0 */
+	PAR_SPEC,			/* write 1 */
+	0,				/* write 2 */
+	0,				/* write 3 */
+	X16CLK | SB1,			/* write 4 */
+	0,				/* write 5 */
+	0, 0, 0,			/* write 6, 7, 8 */
+	MIE | DLC | NV,			/* write 9 */
+	NRZ,				/* write 10 */
+	TCBR | RCBR,			/* write 11 */
+	0, 0,				/* BRG time constant, write 12 + 13 */
+	BRSRC | BRENABL,		/* write 14 */
+	0,				/* write 15 */
+};
+
+/*
+ * Debugging.
+ */
+#undef ZS_DEBUG_REGS
+
+
+/*
+ * Reading and writing Z85C30 registers.
+ */
+static void recovery_delay(void)
+{
+	udelay(2);
+}
+
+static u8 read_zsreg(struct zs_port *zport, int reg)
+{
+	void __iomem *control = zport->port.membase + ZS_CHAN_IO_OFFSET;
+	u8 retval;
+
+	if (reg != 0) {
+		writeb(reg & 0xf, control);
+		fast_iob();
+		recovery_delay();
+	}
+	retval = readb(control);
+	recovery_delay();
+	return retval;
+}
+
+static void write_zsreg(struct zs_port *zport, int reg, u8 value)
+{
+	void __iomem *control = zport->port.membase + ZS_CHAN_IO_OFFSET;
+
+	if (reg != 0) {
+		writeb(reg & 0xf, control);
+		fast_iob(); recovery_delay();
+	}
+	writeb(value, control);
+	fast_iob();
+	recovery_delay();
+	return;
+}
+
+static u8 read_zsdata(struct zs_port *zport)
+{
+	void __iomem *data = zport->port.membase +
+			     ZS_CHAN_IO_STRIDE + ZS_CHAN_IO_OFFSET;
+	u8 retval;
+
+	retval = readb(data);
+	recovery_delay();
+	return retval;
+}
+
+static void write_zsdata(struct zs_port *zport, u8 value)
+{
+	void __iomem *data = zport->port.membase +
+			     ZS_CHAN_IO_STRIDE + ZS_CHAN_IO_OFFSET;
+
+	writeb(value, data);
+	fast_iob();
+	recovery_delay();
+	return;
+}
+
+#ifdef ZS_DEBUG_REGS
+void zs_dump(void)
+{
+	struct zs_port *zport;
+	int i, j;
+
+	for (i = 0; i < ZS_NUM_SCCS * ZS_NUM_CHAN; i++) {
+		zport = &zs_sccs[i / ZS_NUM_CHAN].zport[i % ZS_NUM_CHAN];
+
+		if (!zport->scc)
+			continue;
+
+		for (j = 0; j < 16; j++)
+			printk("W%-2d = 0x%02x\t", j, zport->regs[j]);
+		printk("\n");
+		for (j = 0; j < 16; j++)
+			printk("R%-2d = 0x%02x\t", j, read_zsreg(zport, j));
+		printk("\n\n");
+	}
+}
+#endif
+
+
+static void zs_spin_lock_cond_irq(spinlock_t *lock, int irq)
+{
+	if (irq)
+		spin_lock_irq(lock);
+	else
+		spin_lock(lock);
+}
+
+static void zs_spin_unlock_cond_irq(spinlock_t *lock, int irq)
+{
+	if (irq)
+		spin_unlock_irq(lock);
+	else
+		spin_unlock(lock);
+}
+
+static int zs_receive_drain(struct zs_port *zport)
+{
+	int loops = 10000;
+
+	while ((read_zsreg(zport, R0) & Rx_CH_AV) && --loops)
+		read_zsdata(zport);
+	return loops;
+}
+
+static int zs_transmit_drain(struct zs_port *zport, int irq)
+{
+	struct zs_scc *scc = zport->scc;
+	int loops = 10000;
+
+	while (!(read_zsreg(zport, R0) & Tx_BUF_EMP) && --loops) {
+		zs_spin_unlock_cond_irq(&scc->zlock, irq);
+		udelay(2);
+		zs_spin_lock_cond_irq(&scc->zlock, irq);
+	}
+	return loops;
+}
+
+static int zs_line_drain(struct zs_port *zport, int irq)
+{
+	struct zs_scc *scc = zport->scc;
+	int loops = 10000;
+
+	while (!(read_zsreg(zport, R1) & ALL_SNT) && --loops) {
+		zs_spin_unlock_cond_irq(&scc->zlock, irq);
+		udelay(2);
+		zs_spin_lock_cond_irq(&scc->zlock, irq);
+	}
+	return loops;
+}
+
+
+static void load_zsregs(struct zs_port *zport, u8 *regs, int irq)
+{
+	/* Let the current transmission finish.  */
+	zs_line_drain(zport, irq);
+	/* Load 'em up.  */
+	write_zsreg(zport, R3, regs[3] & ~RxENABLE);
+	write_zsreg(zport, R5, regs[5] & ~TxENAB);
+	write_zsreg(zport, R4, regs[4]);
+	write_zsreg(zport, R9, regs[9]);
+	write_zsreg(zport, R1, regs[1]);
+	write_zsreg(zport, R2, regs[2]);
+	write_zsreg(zport, R10, regs[10]);
+	write_zsreg(zport, R14, regs[14] & ~BRENABL);
+	write_zsreg(zport, R11, regs[11]);
+	write_zsreg(zport, R12, regs[12]);
+	write_zsreg(zport, R13, regs[13]);
+	write_zsreg(zport, R14, regs[14]);
+	write_zsreg(zport, R15, regs[15]);
+	if (regs[3] & RxENABLE)
+		write_zsreg(zport, R3, regs[3]);
+	if (regs[5] & TxENAB)
+		write_zsreg(zport, R5, regs[5]);
+	return;
+}
+
+
+/*
+ * Status handling routines.
+ */
+
+/*
+ * zs_tx_empty() -- get the transmitter empty status
+ *
+ * Purpose: Let user call ioctl() to get info when the UART physically
+ * 	    is emptied.  On bus types like RS485, the transmitter must
+ * 	    release the bus after transmitting.  This must be done when
+ * 	    the transmit shift register is empty, not be done when the
+ * 	    transmit holding register is empty.  This functionality
+ * 	    allows an RS485 driver to be written in user space.
+ */
+static unsigned int zs_tx_empty(struct uart_port *uport)
+{
+	struct zs_port *zport = to_zport(uport);
+	struct zs_scc *scc = zport->scc;
+	unsigned long flags;
+	u8 status;
+
+	spin_lock_irqsave(&scc->zlock, flags);
+	status = read_zsreg(zport, R1);
+	spin_unlock_irqrestore(&scc->zlock, flags);
+
+	return status & ALL_SNT ? TIOCSER_TEMT : 0;
+}
+
+static unsigned int zs_raw_get_ab_mctrl(struct zs_port *zport_a,
+					struct zs_port *zport_b)
+{
+	u8 status_a, status_b;
+	unsigned int mctrl;
+
+	status_a = read_zsreg(zport_a, R0);
+	status_b = read_zsreg(zport_b, R0);
+
+	mctrl = ((status_b & CTS) ? TIOCM_CTS : 0) |
+		((status_b & DCD) ? TIOCM_CAR : 0) |
+		((status_a & DCD) ? TIOCM_RNG : 0) |
+		((status_a & SYNC_HUNT) ? TIOCM_DSR : 0);
+
+	return mctrl;
+}
+
+static unsigned int zs_raw_get_mctrl(struct zs_port *zport)
+{
+	struct zs_port *zport_a = &zport->scc->zport[ZS_CHAN_A];
+
+	return zport != zport_a ? zs_raw_get_ab_mctrl(zport_a, zport) : 0;
+}
+
+static unsigned int zs_raw_xor_mctrl(struct zs_port *zport)
+{
+	struct zs_port *zport_a = &zport->scc->zport[ZS_CHAN_A];
+	unsigned int mmask, mctrl, delta;
+	u8 mask_a, mask_b;
+
+	if (zport == zport_a)
+		return 0;
+
+	mask_a = zport_a->regs[15];
+	mask_b = zport->regs[15];
+
+	mmask = ((mask_b & CTSIE) ? TIOCM_CTS : 0) |
+		((mask_b & DCDIE) ? TIOCM_CAR : 0) |
+		((mask_a & DCDIE) ? TIOCM_RNG : 0) |
+		((mask_a & SYNCIE) ? TIOCM_DSR : 0);
+
+	mctrl = zport->mctrl;
+	if (mmask) {
+		mctrl &= ~mmask;
+		mctrl |= zs_raw_get_ab_mctrl(zport_a, zport) & mmask;
+	}
+
+	delta = mctrl ^ zport->mctrl;
+	if (delta)
+		zport->mctrl = mctrl;
+
+	return delta;
+}
+
+static unsigned int zs_get_mctrl(struct uart_port *uport)
+{
+	struct zs_port *zport = to_zport(uport);
+	struct zs_scc *scc = zport->scc;
+	unsigned int mctrl;
+
+	spin_lock(&scc->zlock);
+	mctrl = zs_raw_get_mctrl(zport);
+	spin_unlock(&scc->zlock);
+
+	return mctrl;
+}
+
+static void zs_set_mctrl(struct uart_port *uport, unsigned int mctrl)
+{
+	struct zs_port *zport = to_zport(uport);
+	struct zs_scc *scc = zport->scc;
+	struct zs_port *zport_a = &scc->zport[ZS_CHAN_A];
+	u8 oldloop, newloop;
+
+	spin_lock(&scc->zlock);
+	if (zport != zport_a) {
+		if (mctrl & TIOCM_DTR)
+			zport_a->regs[5] |= DTR;
+		else
+			zport_a->regs[5] &= ~DTR;
+		if (mctrl & TIOCM_RTS)
+			zport_a->regs[5] |= RTS;
+		else
+			zport_a->regs[5] &= ~RTS;
+		write_zsreg(zport_a, R5, zport_a->regs[5]);
+	}
+
+	/* Rarely modified, so don't poke at hardware unless necessary. */
+	oldloop = zport->regs[14];
+	newloop = oldloop;
+	if (mctrl & TIOCM_LOOP)
+		newloop |= LOOPBAK;
+	else
+		newloop &= ~LOOPBAK;
+	if (newloop != oldloop) {
+		zport->regs[14] = newloop;
+		write_zsreg(zport, R14, zport->regs[14]);
+	}
+	spin_unlock(&scc->zlock);
+}
+
+static void zs_raw_stop_tx(struct zs_port *zport)
+{
+	write_zsreg(zport, R0, RES_Tx_P);
+	zport->tx_stopped = 1;
+}
+
+static void zs_stop_tx(struct uart_port *uport)
+{
+	struct zs_port *zport = to_zport(uport);
+	struct zs_scc *scc = zport->scc;
+
+	spin_lock(&scc->zlock);
+	zs_raw_stop_tx(zport);
+	spin_unlock(&scc->zlock);
+}
+
+static void zs_raw_transmit_chars(struct zs_port *);
+
+static void zs_start_tx(struct uart_port *uport)
+{
+	struct zs_port *zport = to_zport(uport);
+	struct zs_scc *scc = zport->scc;
+
+	spin_lock(&scc->zlock);
+	if (zport->tx_stopped) {
+		zs_transmit_drain(zport, 0);
+		zport->tx_stopped = 0;
+		zs_raw_transmit_chars(zport);
+	}
+	spin_unlock(&scc->zlock);
+}
+
+static void zs_stop_rx(struct uart_port *uport)
+{
+	struct zs_port *zport = to_zport(uport);
+	struct zs_scc *scc = zport->scc;
+	struct zs_port *zport_a = &scc->zport[ZS_CHAN_A];
+
+	spin_lock(&scc->zlock);
+	zport->regs[15] &= ~BRKIE;
+	zport->regs[1] &= ~(RxINT_MASK | TxINT_ENAB);
+	zport->regs[1] |= RxINT_DISAB;
+
+	if (zport != zport_a) {
+		/* A-side DCD tracks RI and SYNC tracks DSR.  */
+		zport_a->regs[15] &= ~(DCDIE | SYNCIE);
+		write_zsreg(zport_a, R15, zport_a->regs[15]);
+		if (!(zport_a->regs[15] & BRKIE)) {
+			zport_a->regs[1] &= ~EXT_INT_ENAB;
+			write_zsreg(zport_a, R1, zport_a->regs[1]);
+		}
+
+		/* This-side DCD tracks DCD and CTS tracks CTS.  */
+		zport->regs[15] &= ~(DCDIE | CTSIE);
+		zport->regs[1] &= ~EXT_INT_ENAB;
+	} else {
+		/* DCD tracks RI and SYNC tracks DSR for the B side.  */
+		if (!(zport->regs[15] & (DCDIE | SYNCIE)))
+			zport->regs[1] &= ~EXT_INT_ENAB;
+	}
+
+	write_zsreg(zport, R15, zport->regs[15]);
+	write_zsreg(zport, R1, zport->regs[1]);
+	spin_unlock(&scc->zlock);
+}
+
+static void zs_enable_ms(struct uart_port *uport)
+{
+	struct zs_port *zport = to_zport(uport);
+	struct zs_scc *scc = zport->scc;
+	struct zs_port *zport_a = &scc->zport[ZS_CHAN_A];
+
+	if (zport == zport_a)
+		return;
+
+	spin_lock(&scc->zlock);
+
+	/* Clear Ext interrupts if not being handled already.  */
+	if (!(zport_a->regs[1] & EXT_INT_ENAB))
+		write_zsreg(zport_a, R0, RES_EXT_INT);
+
+	/* A-side DCD tracks RI and SYNC tracks DSR.  */
+	zport_a->regs[1] |= EXT_INT_ENAB;
+	zport_a->regs[15] |= DCDIE | SYNCIE;
+
+	/* This-side DCD tracks DCD and CTS tracks CTS.  */
+	zport->regs[15] |= DCDIE | CTSIE;
+
+	zs_raw_xor_mctrl(zport);
+
+	write_zsreg(zport_a, R1, zport_a->regs[1]);
+	write_zsreg(zport_a, R15, zport_a->regs[15]);
+	write_zsreg(zport, R15, zport->regs[15]);
+	spin_unlock(&scc->zlock);
+}
+
+static void zs_break_ctl(struct uart_port *uport, int break_state)
+{
+	struct zs_port *zport = to_zport(uport);
+	struct zs_scc *scc = zport->scc;
+	unsigned long flags;
+
+	spin_lock_irqsave(&scc->zlock, flags);
+	if (break_state == -1)
+		zport->regs[5] |= SND_BRK;
+	else
+		zport->regs[5] &= ~SND_BRK;
+	write_zsreg(zport, R5, zport->regs[5]);
+	spin_unlock_irqrestore(&scc->zlock, flags);
+}
+
+
+/*
+ * Interrupt handling routines.
+ */
+#define Rx_BRK 0x0100			/* BREAK event software flag.  */
+#define Rx_SYS 0x0200			/* SysRq event software flag.  */
+
+static void zs_receive_chars(struct zs_port *zport)
+{
+	struct uart_port *uport = &zport->port;
+	struct zs_scc *scc = zport->scc;
+	struct uart_icount *icount;
+	unsigned int avail, status, ch, flag;
+	int count;
+
+	for (count = 16; count; count--) {
+		spin_lock(&scc->zlock);
+		avail = read_zsreg(zport, R0) & Rx_CH_AV;
+		spin_unlock(&scc->zlock);
+		if (!avail)
+			break;
+
+		spin_lock(&scc->zlock);
+		status = read_zsreg(zport, R1) & (Rx_OVR | FRM_ERR | PAR_ERR);
+		ch = read_zsdata(zport);
+		spin_unlock(&scc->zlock);
+
+		flag = TTY_NORMAL;
+
+		icount = &uport->icount;
+		icount->rx++;
+
+		/* Handle the null char got when BREAK is removed.  */
+		if (!ch)
+			status |= zport->tty_break;
+		if (unlikely(status &
+			     (Rx_OVR | FRM_ERR | PAR_ERR | Rx_SYS | Rx_BRK))) {
+			zport->tty_break = 0;
+
+			/* Reset the error indication.  */
+			if (status & (Rx_OVR | FRM_ERR | PAR_ERR)) {
+				spin_lock(&scc->zlock);
+				write_zsreg(zport, R0, ERR_RES);
+				spin_unlock(&scc->zlock);
+			}
+
+			if (status & (Rx_SYS | Rx_BRK)) {
+				icount->brk++;
+				/* SysRq discards the null char.  */
+				if (status & Rx_SYS)
+					continue;
+			} else if (status & FRM_ERR)
+				icount->frame++;
+			else if (status & PAR_ERR)
+				icount->parity++;
+			if (status & Rx_OVR)
+				icount->overrun++;
+
+			status &= uport->read_status_mask;
+			if (status & Rx_BRK)
+				flag = TTY_BREAK;
+			else if (status & FRM_ERR)
+				flag = TTY_FRAME;
+			else if (status & PAR_ERR)
+				flag = TTY_PARITY;
+		}
+
+		if (uart_handle_sysrq_char(uport, ch))
+			continue;
+
+		uart_insert_char(uport, status, Rx_OVR, ch, flag);
+	}
+
+	tty_flip_buffer_push(uport->state->port.tty);
+}
+
+static void zs_raw_transmit_chars(struct zs_port *zport)
+{
+	struct circ_buf *xmit = &zport->port.state->xmit;
+
+	/* XON/XOFF chars.  */
+	if (zport->port.x_char) {
+		write_zsdata(zport, zport->port.x_char);
+		zport->port.icount.tx++;
+		zport->port.x_char = 0;
+		return;
+	}
+
+	/* If nothing to do or stopped or hardware stopped.  */
+	if (uart_circ_empty(xmit) || uart_tx_stopped(&zport->port)) {
+		zs_raw_stop_tx(zport);
+		return;
+	}
+
+	/* Send char.  */
+	write_zsdata(zport, xmit->buf[xmit->tail]);
+	xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
+	zport->port.icount.tx++;
+
+	if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
+		uart_write_wakeup(&zport->port);
+
+	/* Are we are done?  */
+	if (uart_circ_empty(xmit))
+		zs_raw_stop_tx(zport);
+}
+
+static void zs_transmit_chars(struct zs_port *zport)
+{
+	struct zs_scc *scc = zport->scc;
+
+	spin_lock(&scc->zlock);
+	zs_raw_transmit_chars(zport);
+	spin_unlock(&scc->zlock);
+}
+
+static void zs_status_handle(struct zs_port *zport, struct zs_port *zport_a)
+{
+	struct uart_port *uport = &zport->port;
+	struct zs_scc *scc = zport->scc;
+	unsigned int delta;
+	u8 status, brk;
+
+	spin_lock(&scc->zlock);
+
+	/* Get status from Read Register 0.  */
+	status = read_zsreg(zport, R0);
+
+	if (zport->regs[15] & BRKIE) {
+		brk = status & BRK_ABRT;
+		if (brk && !zport->brk) {
+			spin_unlock(&scc->zlock);
+			if (uart_handle_break(uport))
+				zport->tty_break = Rx_SYS;
+			else
+				zport->tty_break = Rx_BRK;
+			spin_lock(&scc->zlock);
+		}
+		zport->brk = brk;
+	}
+
+	if (zport != zport_a) {
+		delta = zs_raw_xor_mctrl(zport);
+		spin_unlock(&scc->zlock);
+
+		if (delta & TIOCM_CTS)
+			uart_handle_cts_change(uport,
+					       zport->mctrl & TIOCM_CTS);
+		if (delta & TIOCM_CAR)
+			uart_handle_dcd_change(uport,
+					       zport->mctrl & TIOCM_CAR);
+		if (delta & TIOCM_RNG)
+			uport->icount.dsr++;
+		if (delta & TIOCM_DSR)
+			uport->icount.rng++;
+
+		if (delta)
+			wake_up_interruptible(&uport->state->port.delta_msr_wait);
+
+		spin_lock(&scc->zlock);
+	}
+
+	/* Clear the status condition...  */
+	write_zsreg(zport, R0, RES_EXT_INT);
+
+	spin_unlock(&scc->zlock);
+}
+
+/*
+ * This is the Z85C30 driver's generic interrupt routine.
+ */
+static irqreturn_t zs_interrupt(int irq, void *dev_id)
+{
+	struct zs_scc *scc = dev_id;
+	struct zs_port *zport_a = &scc->zport[ZS_CHAN_A];
+	struct zs_port *zport_b = &scc->zport[ZS_CHAN_B];
+	irqreturn_t status = IRQ_NONE;
+	u8 zs_intreg;
+	int count;
+
+	/*
+	 * NOTE: The read register 3, which holds the irq status,
+	 *       does so for both channels on each chip.  Although
+	 *       the status value itself must be read from the A
+	 *       channel and is only valid when read from channel A.
+	 *       Yes... broken hardware...
+	 */
+	for (count = 16; count; count--) {
+		spin_lock(&scc->zlock);
+		zs_intreg = read_zsreg(zport_a, R3);
+		spin_unlock(&scc->zlock);
+		if (!zs_intreg)
+			break;
+
+		/*
+		 * We do not like losing characters, so we prioritise
+		 * interrupt sources a little bit differently than
+		 * the SCC would, was it allowed to.
+		 */
+		if (zs_intreg & CHBRxIP)
+			zs_receive_chars(zport_b);
+		if (zs_intreg & CHARxIP)
+			zs_receive_chars(zport_a);
+		if (zs_intreg & CHBEXT)
+			zs_status_handle(zport_b, zport_a);
+		if (zs_intreg & CHAEXT)
+			zs_status_handle(zport_a, zport_a);
+		if (zs_intreg & CHBTxIP)
+			zs_transmit_chars(zport_b);
+		if (zs_intreg & CHATxIP)
+			zs_transmit_chars(zport_a);
+
+		status = IRQ_HANDLED;
+	}
+
+	return status;
+}
+
+
+/*
+ * Finally, routines used to initialize the serial port.
+ */
+static int zs_startup(struct uart_port *uport)
+{
+	struct zs_port *zport = to_zport(uport);
+	struct zs_scc *scc = zport->scc;
+	unsigned long flags;
+	int irq_guard;
+	int ret;
+
+	irq_guard = atomic_add_return(1, &scc->irq_guard);
+	if (irq_guard == 1) {
+		ret = request_irq(zport->port.irq, zs_interrupt,
+				  IRQF_SHARED, "scc", scc);
+		if (ret) {
+			atomic_add(-1, &scc->irq_guard);
+			printk(KERN_ERR "zs: can't get irq %d\n",
+			       zport->port.irq);
+			return ret;
+		}
+	}
+
+	spin_lock_irqsave(&scc->zlock, flags);
+
+	/* Clear the receive FIFO.  */
+	zs_receive_drain(zport);
+
+	/* Clear the interrupt registers.  */
+	write_zsreg(zport, R0, ERR_RES);
+	write_zsreg(zport, R0, RES_Tx_P);
+	/* But Ext only if not being handled already.  */
+	if (!(zport->regs[1] & EXT_INT_ENAB))
+		write_zsreg(zport, R0, RES_EXT_INT);
+
+	/* Finally, enable sequencing and interrupts.  */
+	zport->regs[1] &= ~RxINT_MASK;
+	zport->regs[1] |= RxINT_ALL | TxINT_ENAB | EXT_INT_ENAB;
+	zport->regs[3] |= RxENABLE;
+	zport->regs[15] |= BRKIE;
+	write_zsreg(zport, R1, zport->regs[1]);
+	write_zsreg(zport, R3, zport->regs[3]);
+	write_zsreg(zport, R5, zport->regs[5]);
+	write_zsreg(zport, R15, zport->regs[15]);
+
+	/* Record the current state of RR0.  */
+	zport->mctrl = zs_raw_get_mctrl(zport);
+	zport->brk = read_zsreg(zport, R0) & BRK_ABRT;
+
+	zport->tx_stopped = 1;
+
+	spin_unlock_irqrestore(&scc->zlock, flags);
+
+	return 0;
+}
+
+static void zs_shutdown(struct uart_port *uport)
+{
+	struct zs_port *zport = to_zport(uport);
+	struct zs_scc *scc = zport->scc;
+	unsigned long flags;
+	int irq_guard;
+
+	spin_lock_irqsave(&scc->zlock, flags);
+
+	zport->regs[3] &= ~RxENABLE;
+	write_zsreg(zport, R5, zport->regs[5]);
+	write_zsreg(zport, R3, zport->regs[3]);
+
+	spin_unlock_irqrestore(&scc->zlock, flags);
+
+	irq_guard = atomic_add_return(-1, &scc->irq_guard);
+	if (!irq_guard)
+		free_irq(zport->port.irq, scc);
+}
+
+
+static void zs_reset(struct zs_port *zport)
+{
+	struct zs_scc *scc = zport->scc;
+	int irq;
+	unsigned long flags;
+
+	spin_lock_irqsave(&scc->zlock, flags);
+	irq = !irqs_disabled_flags(flags);
+	if (!scc->initialised) {
+		/* Reset the pointer first, just in case...  */
+		read_zsreg(zport, R0);
+		/* And let the current transmission finish.  */
+		zs_line_drain(zport, irq);
+		write_zsreg(zport, R9, FHWRES);
+		udelay(10);
+		write_zsreg(zport, R9, 0);
+		scc->initialised = 1;
+	}
+	load_zsregs(zport, zport->regs, irq);
+	spin_unlock_irqrestore(&scc->zlock, flags);
+}
+
+static void zs_set_termios(struct uart_port *uport, struct ktermios *termios,
+			   struct ktermios *old_termios)
+{
+	struct zs_port *zport = to_zport(uport);
+	struct zs_scc *scc = zport->scc;
+	struct zs_port *zport_a = &scc->zport[ZS_CHAN_A];
+	int irq;
+	unsigned int baud, brg;
+	unsigned long flags;
+
+	spin_lock_irqsave(&scc->zlock, flags);
+	irq = !irqs_disabled_flags(flags);
+
+	/* Byte size.  */
+	zport->regs[3] &= ~RxNBITS_MASK;
+	zport->regs[5] &= ~TxNBITS_MASK;
+	switch (termios->c_cflag & CSIZE) {
+	case CS5:
+		zport->regs[3] |= Rx5;
+		zport->regs[5] |= Tx5;
+		break;
+	case CS6:
+		zport->regs[3] |= Rx6;
+		zport->regs[5] |= Tx6;
+		break;
+	case CS7:
+		zport->regs[3] |= Rx7;
+		zport->regs[5] |= Tx7;
+		break;
+	case CS8:
+	default:
+		zport->regs[3] |= Rx8;
+		zport->regs[5] |= Tx8;
+		break;
+	}
+
+	/* Parity and stop bits.  */
+	zport->regs[4] &= ~(XCLK_MASK | SB_MASK | PAR_ENA | PAR_EVEN);
+	if (termios->c_cflag & CSTOPB)
+		zport->regs[4] |= SB2;
+	else
+		zport->regs[4] |= SB1;
+	if (termios->c_cflag & PARENB)
+		zport->regs[4] |= PAR_ENA;
+	if (!(termios->c_cflag & PARODD))
+		zport->regs[4] |= PAR_EVEN;
+	switch (zport->clk_mode) {
+	case 64:
+		zport->regs[4] |= X64CLK;
+		break;
+	case 32:
+		zport->regs[4] |= X32CLK;
+		break;
+	case 16:
+		zport->regs[4] |= X16CLK;
+		break;
+	case 1:
+		zport->regs[4] |= X1CLK;
+		break;
+	default:
+		BUG();
+	}
+
+	baud = uart_get_baud_rate(uport, termios, old_termios, 0,
+				  uport->uartclk / zport->clk_mode / 4);
+
+	brg = ZS_BPS_TO_BRG(baud, uport->uartclk / zport->clk_mode);
+	zport->regs[12] = brg & 0xff;
+	zport->regs[13] = (brg >> 8) & 0xff;
+
+	uart_update_timeout(uport, termios->c_cflag, baud);
+
+	uport->read_status_mask = Rx_OVR;
+	if (termios->c_iflag & INPCK)
+		uport->read_status_mask |= FRM_ERR | PAR_ERR;
+	if (termios->c_iflag & (BRKINT | PARMRK))
+		uport->read_status_mask |= Rx_BRK;
+
+	uport->ignore_status_mask = 0;
+	if (termios->c_iflag & IGNPAR)
+		uport->ignore_status_mask |= FRM_ERR | PAR_ERR;
+	if (termios->c_iflag & IGNBRK) {
+		uport->ignore_status_mask |= Rx_BRK;
+		if (termios->c_iflag & IGNPAR)
+			uport->ignore_status_mask |= Rx_OVR;
+	}
+
+	if (termios->c_cflag & CREAD)
+		zport->regs[3] |= RxENABLE;
+	else
+		zport->regs[3] &= ~RxENABLE;
+
+	if (zport != zport_a) {
+		if (!(termios->c_cflag & CLOCAL)) {
+			zport->regs[15] |= DCDIE;
+		} else
+			zport->regs[15] &= ~DCDIE;
+		if (termios->c_cflag & CRTSCTS) {
+			zport->regs[15] |= CTSIE;
+		} else
+			zport->regs[15] &= ~CTSIE;
+		zs_raw_xor_mctrl(zport);
+	}
+
+	/* Load up the new values.  */
+	load_zsregs(zport, zport->regs, irq);
+
+	spin_unlock_irqrestore(&scc->zlock, flags);
+}
+
+/*
+ * Hack alert!
+ * Required solely so that the initial PROM-based console
+ * works undisturbed in parallel with this one.
+ */
+static void zs_pm(struct uart_port *uport, unsigned int state,
+		  unsigned int oldstate)
+{
+	struct zs_port *zport = to_zport(uport);
+
+	if (state < 3)
+		zport->regs[5] |= TxENAB;
+	else
+		zport->regs[5] &= ~TxENAB;
+	write_zsreg(zport, R5, zport->regs[5]);
+}
+
+
+static const char *zs_type(struct uart_port *uport)
+{
+	return "Z85C30 SCC";
+}
+
+static void zs_release_port(struct uart_port *uport)
+{
+	iounmap(uport->membase);
+	uport->membase = 0;
+	release_mem_region(uport->mapbase, ZS_CHAN_IO_SIZE);
+}
+
+static int zs_map_port(struct uart_port *uport)
+{
+	if (!uport->membase)
+		uport->membase = ioremap_nocache(uport->mapbase,
+						 ZS_CHAN_IO_SIZE);
+	if (!uport->membase) {
+		printk(KERN_ERR "zs: Cannot map MMIO\n");
+		return -ENOMEM;
+	}
+	return 0;
+}
+
+static int zs_request_port(struct uart_port *uport)
+{
+	int ret;
+
+	if (!request_mem_region(uport->mapbase, ZS_CHAN_IO_SIZE, "scc")) {
+		printk(KERN_ERR "zs: Unable to reserve MMIO resource\n");
+		return -EBUSY;
+	}
+	ret = zs_map_port(uport);
+	if (ret) {
+		release_mem_region(uport->mapbase, ZS_CHAN_IO_SIZE);
+		return ret;
+	}
+	return 0;
+}
+
+static void zs_config_port(struct uart_port *uport, int flags)
+{
+	struct zs_port *zport = to_zport(uport);
+
+	if (flags & UART_CONFIG_TYPE) {
+		if (zs_request_port(uport))
+			return;
+
+		uport->type = PORT_ZS;
+
+		zs_reset(zport);
+	}
+}
+
+static int zs_verify_port(struct uart_port *uport, struct serial_struct *ser)
+{
+	struct zs_port *zport = to_zport(uport);
+	int ret = 0;
+
+	if (ser->type != PORT_UNKNOWN && ser->type != PORT_ZS)
+		ret = -EINVAL;
+	if (ser->irq != uport->irq)
+		ret = -EINVAL;
+	if (ser->baud_base != uport->uartclk / zport->clk_mode / 4)
+		ret = -EINVAL;
+	return ret;
+}
+
+
+static struct uart_ops zs_ops = {
+	.tx_empty	= zs_tx_empty,
+	.set_mctrl	= zs_set_mctrl,
+	.get_mctrl	= zs_get_mctrl,
+	.stop_tx	= zs_stop_tx,
+	.start_tx	= zs_start_tx,
+	.stop_rx	= zs_stop_rx,
+	.enable_ms	= zs_enable_ms,
+	.break_ctl	= zs_break_ctl,
+	.startup	= zs_startup,
+	.shutdown	= zs_shutdown,
+	.set_termios	= zs_set_termios,
+	.pm		= zs_pm,
+	.type		= zs_type,
+	.release_port	= zs_release_port,
+	.request_port	= zs_request_port,
+	.config_port	= zs_config_port,
+	.verify_port	= zs_verify_port,
+};
+
+/*
+ * Initialize Z85C30 port structures.
+ */
+static int __init zs_probe_sccs(void)
+{
+	static int probed;
+	struct zs_parms zs_parms;
+	int chip, side, irq;
+	int n_chips = 0;
+	int i;
+
+	if (probed)
+		return 0;
+
+	irq = dec_interrupt[DEC_IRQ_SCC0];
+	if (irq >= 0) {
+		zs_parms.scc[n_chips] = IOASIC_SCC0;
+		zs_parms.irq[n_chips] = dec_interrupt[DEC_IRQ_SCC0];
+		n_chips++;
+	}
+	irq = dec_interrupt[DEC_IRQ_SCC1];
+	if (irq >= 0) {
+		zs_parms.scc[n_chips] = IOASIC_SCC1;
+		zs_parms.irq[n_chips] = dec_interrupt[DEC_IRQ_SCC1];
+		n_chips++;
+	}
+	if (!n_chips)
+		return -ENXIO;
+
+	probed = 1;
+
+	for (chip = 0; chip < n_chips; chip++) {
+		spin_lock_init(&zs_sccs[chip].zlock);
+		for (side = 0; side < ZS_NUM_CHAN; side++) {
+			struct zs_port *zport = &zs_sccs[chip].zport[side];
+			struct uart_port *uport = &zport->port;
+
+			zport->scc	= &zs_sccs[chip];
+			zport->clk_mode	= 16;
+
+			uport->irq	= zs_parms.irq[chip];
+			uport->uartclk	= ZS_CLOCK;
+			uport->fifosize	= 1;
+			uport->iotype	= UPIO_MEM;
+			uport->flags	= UPF_BOOT_AUTOCONF;
+			uport->ops	= &zs_ops;
+			uport->line	= chip * ZS_NUM_CHAN + side;
+			uport->mapbase	= dec_kn_slot_base +
+					  zs_parms.scc[chip] +
+					  (side ^ ZS_CHAN_B) * ZS_CHAN_IO_SIZE;
+
+			for (i = 0; i < ZS_NUM_REGS; i++)
+				zport->regs[i] = zs_init_regs[i];
+		}
+	}
+
+	return 0;
+}
+
+
+#ifdef CONFIG_SERIAL_ZS_CONSOLE
+static void zs_console_putchar(struct uart_port *uport, int ch)
+{
+	struct zs_port *zport = to_zport(uport);
+	struct zs_scc *scc = zport->scc;
+	int irq;
+	unsigned long flags;
+
+	spin_lock_irqsave(&scc->zlock, flags);
+	irq = !irqs_disabled_flags(flags);
+	if (zs_transmit_drain(zport, irq))
+		write_zsdata(zport, ch);
+	spin_unlock_irqrestore(&scc->zlock, flags);
+}
+
+/*
+ * Print a string to the serial port trying not to disturb
+ * any possible real use of the port...
+ */
+static void zs_console_write(struct console *co, const char *s,
+			     unsigned int count)
+{
+	int chip = co->index / ZS_NUM_CHAN, side = co->index % ZS_NUM_CHAN;
+	struct zs_port *zport = &zs_sccs[chip].zport[side];
+	struct zs_scc *scc = zport->scc;
+	unsigned long flags;
+	u8 txint, txenb;
+	int irq;
+
+	/* Disable transmit interrupts and enable the transmitter. */
+	spin_lock_irqsave(&scc->zlock, flags);
+	txint = zport->regs[1];
+	txenb = zport->regs[5];
+	if (txint & TxINT_ENAB) {
+		zport->regs[1] = txint & ~TxINT_ENAB;
+		write_zsreg(zport, R1, zport->regs[1]);
+	}
+	if (!(txenb & TxENAB)) {
+		zport->regs[5] = txenb | TxENAB;
+		write_zsreg(zport, R5, zport->regs[5]);
+	}
+	spin_unlock_irqrestore(&scc->zlock, flags);
+
+	uart_console_write(&zport->port, s, count, zs_console_putchar);
+
+	/* Restore transmit interrupts and the transmitter enable. */
+	spin_lock_irqsave(&scc->zlock, flags);
+	irq = !irqs_disabled_flags(flags);
+	zs_line_drain(zport, irq);
+	if (!(txenb & TxENAB)) {
+		zport->regs[5] &= ~TxENAB;
+		write_zsreg(zport, R5, zport->regs[5]);
+	}
+	if (txint & TxINT_ENAB) {
+		zport->regs[1] |= TxINT_ENAB;
+		write_zsreg(zport, R1, zport->regs[1]);
+	}
+	spin_unlock_irqrestore(&scc->zlock, flags);
+}
+
+/*
+ * Setup serial console baud/bits/parity.  We do two things here:
+ * - construct a cflag setting for the first uart_open()
+ * - initialise the serial port
+ * Return non-zero if we didn't find a serial port.
+ */
+static int __init zs_console_setup(struct console *co, char *options)
+{
+	int chip = co->index / ZS_NUM_CHAN, side = co->index % ZS_NUM_CHAN;
+	struct zs_port *zport = &zs_sccs[chip].zport[side];
+	struct uart_port *uport = &zport->port;
+	int baud = 9600;
+	int bits = 8;
+	int parity = 'n';
+	int flow = 'n';
+	int ret;
+
+	ret = zs_map_port(uport);
+	if (ret)
+		return ret;
+
+	zs_reset(zport);
+	zs_pm(uport, 0, -1);
+
+	if (options)
+		uart_parse_options(options, &baud, &parity, &bits, &flow);
+	return uart_set_options(uport, co, baud, parity, bits, flow);
+}
+
+static struct uart_driver zs_reg;
+static struct console zs_console = {
+	.name	= "ttyS",
+	.write	= zs_console_write,
+	.device	= uart_console_device,
+	.setup	= zs_console_setup,
+	.flags	= CON_PRINTBUFFER,
+	.index	= -1,
+	.data	= &zs_reg,
+};
+
+/*
+ *	Register console.
+ */
+static int __init zs_serial_console_init(void)
+{
+	int ret;
+
+	ret = zs_probe_sccs();
+	if (ret)
+		return ret;
+	register_console(&zs_console);
+
+	return 0;
+}
+
+console_initcall(zs_serial_console_init);
+
+#define SERIAL_ZS_CONSOLE	&zs_console
+#else
+#define SERIAL_ZS_CONSOLE	NULL
+#endif /* CONFIG_SERIAL_ZS_CONSOLE */
+
+static struct uart_driver zs_reg = {
+	.owner			= THIS_MODULE,
+	.driver_name		= "serial",
+	.dev_name		= "ttyS",
+	.major			= TTY_MAJOR,
+	.minor			= 64,
+	.nr			= ZS_NUM_SCCS * ZS_NUM_CHAN,
+	.cons			= SERIAL_ZS_CONSOLE,
+};
+
+/* zs_init inits the driver. */
+static int __init zs_init(void)
+{
+	int i, ret;
+
+	pr_info("%s%s\n", zs_name, zs_version);
+
+	/* Find out how many Z85C30 SCCs we have.  */
+	ret = zs_probe_sccs();
+	if (ret)
+		return ret;
+
+	ret = uart_register_driver(&zs_reg);
+	if (ret)
+		return ret;
+
+	for (i = 0; i < ZS_NUM_SCCS * ZS_NUM_CHAN; i++) {
+		struct zs_scc *scc = &zs_sccs[i / ZS_NUM_CHAN];
+		struct zs_port *zport = &scc->zport[i % ZS_NUM_CHAN];
+		struct uart_port *uport = &zport->port;
+
+		if (zport->scc)
+			uart_add_one_port(&zs_reg, uport);
+	}
+
+	return 0;
+}
+
+static void __exit zs_exit(void)
+{
+	int i;
+
+	for (i = ZS_NUM_SCCS * ZS_NUM_CHAN - 1; i >= 0; i--) {
+		struct zs_scc *scc = &zs_sccs[i / ZS_NUM_CHAN];
+		struct zs_port *zport = &scc->zport[i % ZS_NUM_CHAN];
+		struct uart_port *uport = &zport->port;
+
+		if (zport->scc)
+			uart_remove_one_port(&zs_reg, uport);
+	}
+
+	uart_unregister_driver(&zs_reg);
+}
+
+module_init(zs_init);
+module_exit(zs_exit);
diff --git a/ap/os/linux/linux-3.4.x/drivers/tty/serial/zs.h b/ap/os/linux/linux-3.4.x/drivers/tty/serial/zs.h
new file mode 100644
index 0000000..aa921b5
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/tty/serial/zs.h
@@ -0,0 +1,284 @@
+/*
+ * zs.h: Definitions for the DECstation Z85C30 serial driver.
+ *
+ * Adapted from drivers/sbus/char/sunserial.h by Paul Mackerras.
+ * Adapted from drivers/macintosh/macserial.h by Harald Koerfgen.
+ *
+ * Copyright (C) 1996 Paul Mackerras (Paul.Mackerras@cs.anu.edu.au)
+ * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
+ * Copyright (C) 2004, 2005, 2007  Maciej W. Rozycki
+ */
+#ifndef _SERIAL_ZS_H
+#define _SERIAL_ZS_H
+
+#ifdef __KERNEL__
+
+#define ZS_NUM_REGS 16
+
+/*
+ * This is our internal structure for each serial port's state.
+ */
+struct zs_port {
+	struct zs_scc	*scc;			/* Containing SCC.  */
+	struct uart_port port;			/* Underlying UART.  */
+
+	int		clk_mode;		/* May be 1, 16, 32, or 64.  */
+
+	unsigned int	tty_break;		/* Set on BREAK condition.  */
+	int		tx_stopped;		/* Output is suspended.  */
+
+	unsigned int	mctrl;			/* State of modem lines.  */
+	u8		brk;			/* BREAK state from RR0.  */
+
+	u8		regs[ZS_NUM_REGS];	/* Channel write registers.  */
+};
+
+/*
+ * Per-SCC state for locking and the interrupt handler.
+ */
+struct zs_scc {
+	struct zs_port	zport[2];
+	spinlock_t	zlock;
+	atomic_t	irq_guard;
+	int		initialised;
+};
+
+#endif /* __KERNEL__ */
+
+/*
+ * Conversion routines to/from brg time constants from/to bits per second.
+ */
+#define ZS_BRG_TO_BPS(brg, freq) ((freq) / 2 / ((brg) + 2))
+#define ZS_BPS_TO_BRG(bps, freq) ((((freq) + (bps)) / (2 * (bps))) - 2)
+
+/*
+ * The Zilog register set.
+ */
+
+/* Write Register 0 (Command) */
+#define R0		0	/* Register selects */
+#define R1		1
+#define R2		2
+#define R3		3
+#define R4		4
+#define R5		5
+#define R6		6
+#define R7		7
+#define R8		8
+#define R9		9
+#define R10		10
+#define R11		11
+#define R12		12
+#define R13		13
+#define R14		14
+#define R15		15
+
+#define NULLCODE	0	/* Null Code */
+#define POINT_HIGH	0x8	/* Select upper half of registers */
+#define RES_EXT_INT	0x10	/* Reset Ext. Status Interrupts */
+#define SEND_ABORT	0x18	/* HDLC Abort */
+#define RES_RxINT_FC	0x20	/* Reset RxINT on First Character */
+#define RES_Tx_P	0x28	/* Reset TxINT Pending */
+#define ERR_RES		0x30	/* Error Reset */
+#define RES_H_IUS	0x38	/* Reset highest IUS */
+
+#define RES_Rx_CRC	0x40	/* Reset Rx CRC Checker */
+#define RES_Tx_CRC	0x80	/* Reset Tx CRC Checker */
+#define RES_EOM_L	0xC0	/* Reset EOM latch */
+
+/* Write Register 1 (Tx/Rx/Ext Int Enable and WAIT/DMA Commands) */
+#define EXT_INT_ENAB	0x1	/* Ext Int Enable */
+#define TxINT_ENAB	0x2	/* Tx Int Enable */
+#define PAR_SPEC	0x4	/* Parity is special condition */
+
+#define RxINT_DISAB	0	/* Rx Int Disable */
+#define RxINT_FCERR	0x8	/* Rx Int on First Character Only or Error */
+#define RxINT_ALL	0x10	/* Int on all Rx Characters or error */
+#define RxINT_ERR	0x18	/* Int on error only */
+#define RxINT_MASK	0x18
+
+#define WT_RDY_RT	0x20	/* Wait/Ready on R/T */
+#define WT_FN_RDYFN	0x40	/* Wait/FN/Ready FN */
+#define WT_RDY_ENAB	0x80	/* Wait/Ready Enable */
+
+/* Write Register 2 (Interrupt Vector) */
+
+/* Write Register 3 (Receive Parameters and Control) */
+#define RxENABLE	0x1	/* Rx Enable */
+#define SYNC_L_INH	0x2	/* Sync Character Load Inhibit */
+#define ADD_SM		0x4	/* Address Search Mode (SDLC) */
+#define RxCRC_ENAB	0x8	/* Rx CRC Enable */
+#define ENT_HM		0x10	/* Enter Hunt Mode */
+#define AUTO_ENAB	0x20	/* Auto Enables */
+#define Rx5		0x0	/* Rx 5 Bits/Character */
+#define Rx7		0x40	/* Rx 7 Bits/Character */
+#define Rx6		0x80	/* Rx 6 Bits/Character */
+#define Rx8		0xc0	/* Rx 8 Bits/Character */
+#define RxNBITS_MASK	0xc0
+
+/* Write Register 4 (Transmit/Receive Miscellaneous Parameters and Modes) */
+#define PAR_ENA		0x1	/* Parity Enable */
+#define PAR_EVEN	0x2	/* Parity Even/Odd* */
+
+#define SYNC_ENAB	0	/* Sync Modes Enable */
+#define SB1		0x4	/* 1 stop bit/char */
+#define SB15		0x8	/* 1.5 stop bits/char */
+#define SB2		0xc	/* 2 stop bits/char */
+#define SB_MASK		0xc
+
+#define MONSYNC		0	/* 8 Bit Sync character */
+#define BISYNC		0x10	/* 16 bit sync character */
+#define SDLC		0x20	/* SDLC Mode (01111110 Sync Flag) */
+#define EXTSYNC		0x30	/* External Sync Mode */
+
+#define X1CLK		0x0	/* x1 clock mode */
+#define X16CLK		0x40	/* x16 clock mode */
+#define X32CLK		0x80	/* x32 clock mode */
+#define X64CLK		0xc0	/* x64 clock mode */
+#define XCLK_MASK	0xc0
+
+/* Write Register 5 (Transmit Parameters and Controls) */
+#define TxCRC_ENAB	0x1	/* Tx CRC Enable */
+#define RTS		0x2	/* RTS */
+#define SDLC_CRC	0x4	/* SDLC/CRC-16 */
+#define TxENAB		0x8	/* Tx Enable */
+#define SND_BRK		0x10	/* Send Break */
+#define Tx5		0x0	/* Tx 5 bits (or less)/character */
+#define Tx7		0x20	/* Tx 7 bits/character */
+#define Tx6		0x40	/* Tx 6 bits/character */
+#define Tx8		0x60	/* Tx 8 bits/character */
+#define TxNBITS_MASK	0x60
+#define DTR		0x80	/* DTR */
+
+/* Write Register 6 (Sync bits 0-7/SDLC Address Field) */
+
+/* Write Register 7 (Sync bits 8-15/SDLC 01111110) */
+
+/* Write Register 8 (Transmit Buffer) */
+
+/* Write Register 9 (Master Interrupt Control) */
+#define VIS		1	/* Vector Includes Status */
+#define NV		2	/* No Vector */
+#define DLC		4	/* Disable Lower Chain */
+#define MIE		8	/* Master Interrupt Enable */
+#define STATHI		0x10	/* Status high */
+#define SOFTACK		0x20	/* Software Interrupt Acknowledge */
+#define NORESET		0	/* No reset on write to R9 */
+#define CHRB		0x40	/* Reset channel B */
+#define CHRA		0x80	/* Reset channel A */
+#define FHWRES		0xc0	/* Force hardware reset */
+
+/* Write Register 10 (Miscellaneous Transmitter/Receiver Control Bits) */
+#define BIT6		1	/* 6 bit/8bit sync */
+#define LOOPMODE	2	/* SDLC Loop mode */
+#define ABUNDER		4	/* Abort/flag on SDLC xmit underrun */
+#define MARKIDLE	8	/* Mark/flag on idle */
+#define GAOP		0x10	/* Go active on poll */
+#define NRZ		0	/* NRZ mode */
+#define NRZI		0x20	/* NRZI mode */
+#define FM1		0x40	/* FM1 (transition = 1) */
+#define FM0		0x60	/* FM0 (transition = 0) */
+#define CRCPS		0x80	/* CRC Preset I/O */
+
+/* Write Register 11 (Clock Mode Control) */
+#define TRxCXT		0	/* TRxC = Xtal output */
+#define TRxCTC		1	/* TRxC = Transmit clock */
+#define TRxCBR		2	/* TRxC = BR Generator Output */
+#define TRxCDP		3	/* TRxC = DPLL output */
+#define TRxCOI		4	/* TRxC O/I */
+#define TCRTxCP		0	/* Transmit clock = RTxC pin */
+#define TCTRxCP		8	/* Transmit clock = TRxC pin */
+#define TCBR		0x10	/* Transmit clock = BR Generator output */
+#define TCDPLL		0x18	/* Transmit clock = DPLL output */
+#define RCRTxCP		0	/* Receive clock = RTxC pin */
+#define RCTRxCP		0x20	/* Receive clock = TRxC pin */
+#define RCBR		0x40	/* Receive clock = BR Generator output */
+#define RCDPLL		0x60	/* Receive clock = DPLL output */
+#define RTxCX		0x80	/* RTxC Xtal/No Xtal */
+
+/* Write Register 12 (Lower Byte of Baud Rate Generator Time Constant) */
+
+/* Write Register 13 (Upper Byte of Baud Rate Generator Time Constant) */
+
+/* Write Register 14 (Miscellaneous Control Bits) */
+#define BRENABL		1	/* Baud rate generator enable */
+#define BRSRC		2	/* Baud rate generator source */
+#define DTRREQ		4	/* DTR/Request function */
+#define AUTOECHO	8	/* Auto Echo */
+#define LOOPBAK		0x10	/* Local loopback */
+#define SEARCH		0x20	/* Enter search mode */
+#define RMC		0x40	/* Reset missing clock */
+#define DISDPLL		0x60	/* Disable DPLL */
+#define SSBR		0x80	/* Set DPLL source = BR generator */
+#define SSRTxC		0xa0	/* Set DPLL source = RTxC */
+#define SFMM		0xc0	/* Set FM mode */
+#define SNRZI		0xe0	/* Set NRZI mode */
+
+/* Write Register 15 (External/Status Interrupt Control) */
+#define WR7P_EN		1	/* WR7 Prime SDLC Feature Enable */
+#define ZCIE		2	/* Zero count IE */
+#define DCDIE		8	/* DCD IE */
+#define SYNCIE		0x10	/* Sync/hunt IE */
+#define CTSIE		0x20	/* CTS IE */
+#define TxUIE		0x40	/* Tx Underrun/EOM IE */
+#define BRKIE		0x80	/* Break/Abort IE */
+
+
+/* Read Register 0 (Transmit/Receive Buffer Status and External Status) */
+#define Rx_CH_AV	0x1	/* Rx Character Available */
+#define ZCOUNT		0x2	/* Zero count */
+#define Tx_BUF_EMP	0x4	/* Tx Buffer empty */
+#define DCD		0x8	/* DCD */
+#define SYNC_HUNT	0x10	/* Sync/hunt */
+#define CTS		0x20	/* CTS */
+#define TxEOM		0x40	/* Tx underrun */
+#define BRK_ABRT	0x80	/* Break/Abort */
+
+/* Read Register 1 (Special Receive Condition Status) */
+#define ALL_SNT		0x1	/* All sent */
+/* Residue Data for 8 Rx bits/char programmed */
+#define RES3		0x8	/* 0/3 */
+#define RES4		0x4	/* 0/4 */
+#define RES5		0xc	/* 0/5 */
+#define RES6		0x2	/* 0/6 */
+#define RES7		0xa	/* 0/7 */
+#define RES8		0x6	/* 0/8 */
+#define RES18		0xe	/* 1/8 */
+#define RES28		0x0	/* 2/8 */
+/* Special Rx Condition Interrupts */
+#define PAR_ERR		0x10	/* Parity Error */
+#define Rx_OVR		0x20	/* Rx Overrun Error */
+#define FRM_ERR		0x40	/* CRC/Framing Error */
+#define END_FR		0x80	/* End of Frame (SDLC) */
+
+/* Read Register 2 (Interrupt Vector (WR2) -- channel A).  */
+
+/* Read Register 2 (Modified Interrupt Vector -- channel B).  */
+
+/* Read Register 3 (Interrupt Pending Bits -- channel A only).  */
+#define CHBEXT		0x1	/* Channel B Ext/Stat IP */
+#define CHBTxIP		0x2	/* Channel B Tx IP */
+#define CHBRxIP		0x4	/* Channel B Rx IP */
+#define CHAEXT		0x8	/* Channel A Ext/Stat IP */
+#define CHATxIP		0x10	/* Channel A Tx IP */
+#define CHARxIP		0x20	/* Channel A Rx IP */
+
+/* Read Register 6 (SDLC FIFO Status and Byte Count LSB) */
+
+/* Read Register 7 (SDLC FIFO Status and Byte Count MSB) */
+
+/* Read Register 8 (Receive Data) */
+
+/* Read Register 10 (Miscellaneous Status Bits) */
+#define ONLOOP		2	/* On loop */
+#define LOOPSEND	0x10	/* Loop sending */
+#define CLK2MIS		0x40	/* Two clocks missing */
+#define CLK1MIS		0x80	/* One clock missing */
+
+/* Read Register 12 (Lower Byte of Baud Rate Generator Constant (WR12)) */
+
+/* Read Register 13 (Upper Byte of Baud Rate Generator Constant (WR13) */
+
+/* Read Register 15 (External/Status Interrupt Control (WR15)) */
+
+#endif /* _SERIAL_ZS_H */
diff --git a/ap/os/linux/linux-3.4.x/drivers/tty/serial/zx297502_uart.h b/ap/os/linux/linux-3.4.x/drivers/tty/serial/zx297502_uart.h
new file mode 100755
index 0000000..c94966b
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/tty/serial/zx297502_uart.h
@@ -0,0 +1,24 @@
+/*
+ * zx297502_uart.h -- sanchips zx297502 UART driver defines.
+ */
+
+#ifndef	__ZX297502_UART_H
+#define	__ZX297502_UART_H
+
+#include <linux/init.h>
+#include <linux/serial_core.h>
+#include <linux/platform_device.h>
+
+/*
+ * This is the platform device platform_data structure
+ */
+struct zx297502_platform_uart {
+	void __iomem	*membase;	/* Virtual address if mapped */
+	unsigned long	mapbase;	/* resource base */
+	unsigned int	irq;		/* interrupt number */
+	unsigned int	uartclk;	/* UART clock rate */
+	void (*init) (void);
+	void (*exit) (void);
+};
+
+#endif /* __ZX297502_UART_H */
diff --git a/ap/os/linux/linux-3.4.x/drivers/tty/serial/zx297510_uart.c b/ap/os/linux/linux-3.4.x/drivers/tty/serial/zx297510_uart.c
new file mode 100755
index 0000000..6833edd
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/tty/serial/zx297510_uart.c
@@ -0,0 +1,883 @@
+/****************************************************************************/
+/*
+ *    zx297510_uart.c   sanchips  zx297510
+ *
+ *	(C) Copyright 2003-2007, gaowei
+ *	(C) Copyright 2003-2007, sanchips
+ * 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.
+ */
+
+/****************************************************************************/
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/timer.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/console.h>
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
+#include <linux/serial.h>
+#include <linux/serial_core.h>
+#include <linux/platform_device.h>
+#include <linux/of.h>
+#include <linux/io.h>
+#include <linux/printk.h>
+#include <linux/clk.h>
+#include <linux/gpio.h>
+
+#include <mach/irqs.h>
+#include <mach/clock.h>
+#include <mach/gpio.h>
+#include <mach/debug.h>
+#include <mach/board.h>
+
+#include "zx297510_uart.h"
+
+//#define DEBUG_UART
+
+#ifdef DEBUG_UART
+#pragma GCC optimize("O0")
+#endif
+
+
+#define UART_NUM 1
+
+#define UART0 0
+#define UART1 1
+#define UART2 2
+
+/* 
+ *Use device name ttyS, major 4, minor 64-68.  This is the usual serial port
+ * name, but it is legally reserved for the 8250 driver. 
+ */
+#define SERIAL_zx297510_MAJOR	TTY_MAJOR
+#define SERIAL_MINOR_START		64
+
+
+/* UART registers.  hence no GET macro */
+#define UART_PUT_IFLS(port, v)  __raw_writel(v, (port)->membase + zx297510_UART_IFLS)
+
+#define UART_PUT_CHAR(port, v)	__raw_writel(v, (port)->membase + zx297510_UART_DR)
+#define UART_GET_CHAR(port)	    __raw_readl((port)->membase + zx297510_UART_DR)
+
+#define UART_GET_FR(port)	    __raw_readl((port)->membase + zx297510_UART_FR)
+    
+#define UART_PUT_IBRD(port, v)	__raw_writel(v, (port)->membase + zx297510_UART_IBRD)
+#define UART_GET_IBRD(port)		__raw_readl((port)->membase + zx297510_UART_IBRD)
+
+#define UART_PUT_FBRD(port, v)	__raw_writel(v, (port)->membase + zx297510_UART_FBRD)
+#define UART_GET_FBRD(port)		__raw_readl((port)->membase + zx297510_UART_FBRD)
+
+#define UART_PUT_LCRH(port, v)	__raw_writel(v, (port)->membase + zx297510_UART_LCRH)
+#define UART_GET_LCRH(port)	    __raw_readl((port)->membase + zx297510_UART_LCRH)
+  
+#define UART_PUT_CR(port, v)	__raw_writel(v, (port)->membase + zx297510_UART_CR)
+#define UART_GET_CR(port)	    __raw_readl((port)->membase + zx297510_UART_CR)
+
+#define UART_PUT_ICR(port, v)	__raw_writel(v, (port)->membase + zx297510_UART_ICR)
+
+#define UART_PUT_IMSC(port, v)  __raw_writel(v, (port)->membase + zx297510_UART_IMSC)
+
+#define UART_PUT_MIS(port, v)   __raw_writel(v, (port)->membase + zx297510_UART_MIS)
+#define UART_GET_MIS(port)      __raw_readl((port)->membase + zx297510_UART_MIS)
+
+
+
+/*
+ *	Local per-uart structure.
+ */
+struct zx297510_uart_port {
+	struct uart_port	port;
+	unsigned int		sigs;		/* Local copy of line sigs */
+	unsigned int		old_status;
+	unsigned char		imr;		/* Local interrupt mask reg mirror */
+	bool				rts_state;
+	bool				autorts;    /* hardware flow control */
+	struct clk          *wclk;      /* uart work clock */
+	struct clk          *busclk;      /* uart apb clock */
+};
+
+
+static unsigned int zx297510_uart_tx_empty(struct uart_port *port)
+{
+	return (UART_GET_FR(port)&(UART_FR_TXBUSY|UART_FR_TXFF)) ? 0 : TIOCSER_TEMT;
+}
+
+static void zx297510_uart_set_mctrl(struct uart_port *port, unsigned int mctrl)
+{
+	unsigned int control = 0;
+	struct zx297510_uart_port *zup = container_of(port, struct zx297510_uart_port, port);
+
+	zup->sigs = mctrl;
+	control = UART_GET_CR(&zup->port);
+	if(mctrl & TIOCM_DTR)
+		control |= UART_CR_DTR;
+	else
+		control &= ~ UART_CR_DTR;
+	
+	if(mctrl & TIOCM_RTS)
+		control |= UART_CR_RTS;
+	else
+		control &= ~UART_CR_RTS;
+	
+	if(mctrl & TIOCM_LOOP)
+		control |= UART_CR_LBE;
+	else
+		control &= ~UART_CR_LBE;
+	
+	/* We need to disable auto-RTS if we want to turn RTS off */	
+	if (zup->autorts) {
+		if (mctrl & TIOCM_RTS)		
+			control |= UART_CR_RTSEN;		
+		else				
+			control &= ~UART_CR_RTSEN;
+	}
+	UART_PUT_CR(port, control);
+}
+
+static unsigned int zx297510_uart_get_mctrl(struct uart_port *port)
+{
+	struct zx297510_uart_port *zup = container_of(port, struct zx297510_uart_port, port);
+	unsigned int mctrl = 0;
+	unsigned int uart_flag = 0;
+	
+	uart_flag = UART_GET_FR(port);
+
+	mctrl = (uart_flag&UART_FR_CTS) ?TIOCM_CTS : 0;
+	mctrl |= (zup->sigs & TIOCM_RTS);
+	mctrl |= (uart_flag&UART_FR_DCD) ? TIOCM_CD : 0;
+	mctrl |= (uart_flag&UART_FR_DSR) ? TIOCM_DSR : 0;
+	mctrl |= (uart_flag&UART_FR_RI) ? TIOCM_RI : 0;
+
+	return mctrl;
+}
+
+static void zx297510_uart_start_tx(struct uart_port *port)
+{
+	struct zx297510_uart_port *zup = container_of(port, struct zx297510_uart_port, port);
+	zup->imr |= UART_TXIM;
+	UART_PUT_IMSC(port, zup->imr);
+}
+
+static void zx297510_uart_stop_tx(struct uart_port *port)
+{
+	struct zx297510_uart_port *zup = container_of(port, struct zx297510_uart_port, port);
+	zup->imr &= ~UART_TXIM;
+	UART_PUT_IMSC(port, zup->imr);
+}
+
+
+static void zx297510_uart_stop_rx(struct uart_port *port)
+{
+	struct zx297510_uart_port *zup = container_of(port, struct zx297510_uart_port, port);
+
+	zup->imr &= ~(UART_RXIM|UART_RTIM|UART_FEIM|UART_PEIM|UART_BEIM|UART_OEIM);
+	UART_PUT_IMSC(port, zup->imr);
+}
+
+static void zx297510_uart_break_ctl(struct uart_port *port, int break_state)
+{
+	struct zx297510_uart_port *zup = container_of(port, struct zx297510_uart_port, port);
+	unsigned long flags;
+	unsigned int lcr_h;
+	spin_lock_irqsave(&zup->port.lock, flags);
+	lcr_h = UART_GET_LCRH(port);
+	if (break_state == -1)
+		lcr_h |= UART_LCRH_BRK;
+	else
+		lcr_h &= ~UART_LCRH_BRK;
+	UART_PUT_LCRH(port, lcr_h);
+	spin_unlock_irqrestore(&zup->port.lock, flags);
+}
+
+static void zx297510_uart_enable_ms(struct uart_port *port)
+{
+	struct zx297510_uart_port *zup = container_of(port, struct zx297510_uart_port, port);
+	zup->imr |= UART_RIMIM|UART_CTSMIM|UART_DCDMIM|UART_DSRMIM;
+	UART_PUT_IMSC(port, zup->imr);
+}
+
+
+/*
+ * Reads up to 256 characters from the FIFO or until it's empty and
+ * inserts them into the TTY layer. Returns the number of characters
+ * read from the FIFO.
+ */
+static int zx297510_uart_fifo_to_tty(struct zx297510_uart_port *zup)
+{
+	struct uart_port *port = &zup->port;
+	u32 status, ch;
+	unsigned int flag, max_count = 256;
+	int fifotaken = 0;
+
+	while (max_count--) {
+		status = UART_GET_FR(port);
+		if (status & UART_FR_RXFE)
+			break;
+
+		/* Take chars from the FIFO and update status */
+		ch = UART_GET_CHAR(port) |	UART_DUMMY_DR_RX;
+		flag = TTY_NORMAL;
+		zup->port.icount.rx++;
+		fifotaken++;
+
+		if (unlikely(ch & UART_DR_ERROR)) {
+			if (ch & UART_DR_BE) {
+				ch &= ~(UART_DR_FE | UART_DR_PE);
+				zup->port.icount.brk++;
+				if (uart_handle_break(&zup->port))
+					continue;
+			} else if (ch & UART_DR_PE)
+				zup->port.icount.parity++;
+			else if (ch & UART_DR_FE)
+				zup->port.icount.frame++;
+			if (ch & UART_DR_OE)
+				zup->port.icount.overrun++;
+
+			ch &= zup->port.read_status_mask;
+
+			if (ch & UART_DR_BE)
+				flag = TTY_BREAK;
+			else if (ch & UART_DR_PE)
+				flag = TTY_PARITY;
+			else if (ch & UART_DR_FE)
+				flag = TTY_FRAME;
+		}
+
+		if (uart_handle_sysrq_char(&zup->port, ch & 255))
+			continue;
+
+		uart_insert_char(&zup->port, ch, UART_DR_OE, ch, flag);
+	}
+
+	return fifotaken;
+}
+
+
+static void zx297510_uart_rx_chars(struct zx297510_uart_port *zup)
+{
+	struct tty_struct *tty = zup->port.state->port.tty;
+
+	zx297510_uart_fifo_to_tty(zup);
+
+	spin_unlock(&zup->port.lock);
+	tty_flip_buffer_push(tty);
+	spin_lock(&zup->port.lock);
+
+}
+
+
+static void zx297510_uart_tx_chars(struct zx297510_uart_port *zup)
+{
+	struct circ_buf *xmit = &zup->port.state->xmit;
+	int count;
+
+	if (zup->port.x_char) {
+		UART_PUT_CHAR(&zup->port, zup->port.x_char);
+		zup->port.icount.tx++;
+		zup->port.x_char = 0;
+		return;
+	}
+	if (uart_circ_empty(xmit) || uart_tx_stopped(&zup->port)) {
+		zx297510_uart_stop_tx(&zup->port);
+		return;
+	}
+	count = zup->port.fifosize >> 1;
+	do {
+		UART_PUT_CHAR(&zup->port, xmit->buf[xmit->tail]);
+		xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
+		zup->port.icount.tx++;
+		if (uart_circ_empty(xmit))
+			break;
+	} while (--count > 0);
+
+	if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
+		uart_write_wakeup(&zup->port);
+
+	if (uart_circ_empty(xmit))
+		zx297510_uart_stop_tx(&zup->port);
+}
+
+
+static void zx297510_uart_modem_status(struct zx297510_uart_port *zup)
+{
+	unsigned int status, delta;
+
+	status = UART_GET_FR(&zup->port)& UART_FR_MODEM_ANY;
+
+	delta = status ^ zup->old_status;
+	zup->old_status = status;
+
+	if (!delta)
+		return; 
+
+	if (delta & UART_FR_DCD)
+		uart_handle_dcd_change(&zup->port, status & UART_FR_DCD);
+
+	if (delta & UART_FR_DSR)
+		zup->port.icount.dsr++;
+
+	if (delta & UART_FR_CTS)
+		uart_handle_cts_change(&zup->port, status & UART_FR_CTS);
+
+	wake_up_interruptible(&zup->port.state->port.delta_msr_wait);
+}
+
+
+static irqreturn_t zx297510_uart_interrupt(int irq, void *dev_id)
+{
+	struct uart_port *port = dev_id;
+	struct zx297510_uart_port *zup = container_of(port, struct zx297510_uart_port, port);
+	unsigned long flags;
+	unsigned int status, pass_counter = 256;
+	int handled = 0;
+
+	spin_lock_irqsave(&zup->port.lock, flags);
+	status = UART_GET_MIS(port) & zup->imr;
+	if (status) {
+		do {
+			UART_PUT_ICR(port,(status & ~(UART_TXIS|UART_RTIS|UART_RXIS)));
+			if (status & (UART_RTIS|UART_RXIS))
+				zx297510_uart_rx_chars(zup);
+			
+			if (status & (UART_DSRMIS|UART_DCDMIS|UART_CTSMIS|UART_RIMIS))
+				zx297510_uart_modem_status(zup);
+			
+			if (status & UART_TXIS)
+				zx297510_uart_tx_chars(zup);
+
+			if (pass_counter-- == 0)
+				break;
+
+			status = UART_GET_MIS(port);
+		} while (status != 0);
+		handled = IRQ_HANDLED;
+	}
+	spin_unlock_irqrestore(&zup->port.lock, flags);
+	
+	return IRQ_RETVAL(handled);
+}
+
+
+static int zx297510_uart_startup(struct uart_port *port)
+{
+	struct zx297510_uart_port *zup = container_of(port, struct zx297510_uart_port, port);
+	unsigned long flags = 0;
+	unsigned long control = 0;
+	int retval = 0;
+	struct platform_device *pdev=port->private_data;
+	
+    /*  config uart apb_clk   */
+	clk_enable(zup->busclk);
+	/* enable uart work clock */
+	clk_enable(zup->wclk);
+	
+    /*configure gpio pin to UART*/
+	retval=gpio_request(UART_PIN_NUM_BASE+(port->line)*2,pdev->dev.kobj.name);
+	if(retval)
+		BUG();
+		
+	retval=gpio_request(UART_PIN_NUM_BASE+(port->line)*2+1,pdev->dev.kobj.name);
+	if(retval)
+		BUG();
+	
+    zx29_gpio_config(UART_PIN_NUM_BASE+(pdev->id)*2,1);    /*uart rxd*/
+	zx29_gpio_config(UART_PIN_NUM_BASE+(pdev->id)*2+1,1);  /*uart txd*/
+	
+	/* Clear all pending error and receive interrupts */
+	UART_PUT_ICR(port, 0xfff);
+	
+	/*  Allocate the IRQ	 */
+	retval = request_irq(port->irq, zx297510_uart_interrupt, 0, "uart-zx297510", zup);
+	if (retval){
+		pr_err("zx297510 uart: unable to attach zx297510 UART %d "
+		       "interrupt vector=%d\n", port->line, port->irq);
+		return retval;
+	}
+	
+	/*	set interrupt fifo level RX:1/2 Full, TX:1/2 Full */
+	UART_PUT_IFLS(port, UART_IFLS_RX4_8|UART_IFLS_TX4_8);
+	
+	/* Provoke TX FIFO interrupt into asserting. */
+	control = UART_CR_UARTEN | UART_CR_TXE | UART_CR_LBE;
+	UART_PUT_CR(port, control);
+	UART_PUT_FBRD(port, 0);
+	UART_PUT_IBRD(port, 1);	
+	UART_PUT_LCRH(port, 0);
+	UART_PUT_CHAR(port, 0);
+	while (UART_GET_FR(port) & UART_FR_TXBUSY)
+		barrier();
+
+	control = UART_CR_UARTEN | UART_CR_RXE | UART_CR_TXE;
+	UART_PUT_CR(port, control);
+
+	/*
+	 * Finally, enable interrupts, only timeouts when using DMA
+	 * if initial RX DMA job failed, start in interrupt mode
+	 * as well.
+	 */
+	spin_lock_irqsave(&zup->port.lock, flags);
+	/* Clear out any spuriously appearing RX interrupts */
+	UART_PUT_ICR(port, (UART_RTIS | UART_RXIS));
+	
+	zup->imr = UART_RTIM | UART_RXIM;
+	UART_PUT_IMSC(port, zup->imr);
+	spin_unlock_irqrestore(&zup->port.lock, flags);
+
+	return 0;
+}
+
+
+static void zx297510_uart_shutdown(struct uart_port *port)
+{
+	struct zx297510_uart_port *zup = container_of(port, struct zx297510_uart_port, port);
+	unsigned long flags;
+	uint32_t val;
+	
+	spin_lock_irqsave(&port->lock, flags);
+	/* Disable and clear all interrupts now */
+	zup->imr = 0;
+	UART_PUT_IMSC(port, zup->imr);
+	UART_PUT_ICR(port, 0xFFFF);
+	
+	spin_unlock_irqrestore(&port->lock, flags);
+
+	/* Free the interrupt */
+	free_irq(zup->port.irq, zup);
+
+	/* Disable UART transmitter and receiver */
+	zup->autorts = false;
+	val = UART_GET_CR(port);
+	if (val & UART_CR_RTS) {
+		zup->rts_state = true;
+		val = UART_CR_RTS;
+	} else
+		zup->rts_state = false;
+	val = UART_CR_UARTEN | UART_CR_TXE;
+	UART_PUT_CR(port, val);
+
+	/* disable break condition and fifos */
+	val = UART_GET_LCRH(port);
+	val &= ~(UART_LCRH_BRK | UART_LCRH_FEN);
+	UART_PUT_LCRH(port, val);
+
+	/* Shutdown uart clock */
+}
+
+
+static void zx297510_uart_set_termios(struct uart_port *port, struct ktermios *termios,
+	struct ktermios *old)
+{
+	struct zx297510_uart_port *zup = container_of(port, struct zx297510_uart_port, port);
+	unsigned int lcr_h, old_cr;
+	unsigned long flags;
+	unsigned int baud, ibrd, fbrd;
+ 
+    /* Set baud rate */
+	/* Ask the core to calculate the divisor for us. */
+	baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk/16);
+	ibrd = port->uartclk  / (baud<<4);
+	fbrd = ((port->uartclk % (baud<<4) )*8 + baud)/(2*baud);		
+	UART_PUT_FBRD(port, fbrd);
+	UART_PUT_IBRD(port, ibrd);
+	
+
+	switch (termios->c_cflag & CSIZE) {
+	case CS5:
+		lcr_h = UART_LCRH_WLEN_5;
+		break;
+	case CS6:
+		lcr_h = UART_LCRH_WLEN_6;
+		break;
+	case CS7:
+		lcr_h = UART_LCRH_WLEN_7;
+		break;
+	default: // CS8
+		lcr_h = UART_LCRH_WLEN_8;
+		break;
+	}
+	if (termios->c_cflag & CSTOPB)
+		lcr_h |= UART_LCRH_STP2;
+	if (termios->c_cflag & PARENB) {
+		lcr_h |= UART_LCRH_PEN;
+		if (!(termios->c_cflag & PARODD))
+			lcr_h |= UART_LCRH_EPS;
+	}
+	if (port->fifosize > 1)
+		lcr_h |= UART_LCRH_FEN;
+
+	spin_lock_irqsave(&port->lock, flags);
+
+	/*
+	 * Update the per-port timeout.
+	 */
+	uart_update_timeout(port, termios->c_cflag, baud);
+
+	port->read_status_mask = UART_DR_OE | 255;
+	if (termios->c_iflag & INPCK)
+		port->read_status_mask |= UART_DR_FE | UART_DR_PE;
+	if (termios->c_iflag & (BRKINT | PARMRK))
+		port->read_status_mask |= UART_DR_BE;
+
+	/*
+	 * Characters to ignore
+	 */
+	port->ignore_status_mask = 0;
+	if (termios->c_iflag & IGNPAR)
+		port->ignore_status_mask |= UART_DR_FE | UART_DR_PE;
+	if (termios->c_iflag & IGNBRK) {
+		port->ignore_status_mask |= UART_DR_BE;
+		/*
+		 * If we're ignoring parity and break indicators,
+		 * ignore overruns too (for real raw support).
+		 */
+		if (termios->c_iflag & IGNPAR)
+			port->ignore_status_mask |= UART_DR_OE;
+	}
+
+	/*
+	 * Ignore all characters if CREAD is not set.
+	 */
+	if ((termios->c_cflag & CREAD) == 0)
+		port->ignore_status_mask |= UART_DUMMY_DR_RX;
+
+	if (UART_ENABLE_MS(port, termios->c_cflag))
+		zx297510_uart_enable_ms(port);
+
+	/* first, disable everything */
+	old_cr = UART_GET_CR(port);
+	UART_PUT_CR(port, 0);
+	
+	if (termios->c_cflag & CRTSCTS) {
+		if (old_cr & UART_CR_RTS)
+			old_cr |= UART_CR_RTSEN;
+
+		old_cr |= UART_CR_CTSEN;
+		zup->autorts = true;
+	} else {
+		old_cr &= ~(UART_CR_CTSEN | UART_CR_RTSEN);
+		zup->autorts = false;
+	}
+
+	/*
+	 * ----------v----------v----------v----------v-----
+	 * NOTE: MUST BE WRITTEN AFTER UARTLCR_M & UARTLCR_L
+	 * ----------^----------^----------^----------^-----
+	 */
+	UART_PUT_LCRH(port, lcr_h);
+	UART_PUT_CR(port, old_cr);
+
+	spin_unlock_irqrestore(&port->lock, flags);
+}
+
+
+static const char *zx297510_uart_type(struct uart_port *port)
+{
+	return (port->type == PORT_ZX297510) ? "zx297510_UART" : NULL;
+}
+
+
+static int zx297510_uart_request_port(struct uart_port *port)
+{
+	/* UARTs always present */
+//	return request_mem_region(port->mapbase, SZ_4K, "uart-zx297510")!= NULL ? 0 : -EBUSY;
+	return 0;
+}
+
+
+static void zx297510_uart_config_port(struct uart_port *port, int flags)
+{
+	if (flags & UART_CONFIG_TYPE) {
+		port->type = PORT_ZX297510;
+		zx297510_uart_request_port(port);
+	}
+}
+
+
+static void zx297510_uart_release_port(struct uart_port *port)
+{
+//	release_mem_region(port->mapbase, SZ_4K);
+}
+
+
+static int zx297510_uart_verify_port(struct uart_port *port, struct serial_struct *ser)
+{
+	if ((ser->type != PORT_UNKNOWN) && (ser->type != PORT_ZX297510))
+		return -EINVAL;
+	return 0;
+}
+
+#ifdef CONFIG_CONSOLE_POLL
+static int zx297510_get_poll_char(struct uart_port *port)
+{
+	if (UART_GET_FR(port) & UART_FR_RXFE)
+		return NO_POLL_CHAR;
+
+	return UART_PUT_CHAR(port);
+}
+
+
+static void zx297510_put_poll_char(struct uart_port *port, unsigned char ch)
+{
+	while (UART_GET_FR(port) & UART_FR_TXFF)
+		barrier();
+	UART_PUT_CHAR(port, ch);
+}
+#endif /* CONFIG_CONSOLE_POLL */
+
+
+/*
+ *	Define the basic serial functions we support.
+ */
+static const struct uart_ops zx297510_uart_ops = {
+	.tx_empty	= zx297510_uart_tx_empty,
+	.set_mctrl	= zx297510_uart_set_mctrl,
+	.get_mctrl	= zx297510_uart_get_mctrl,
+	.start_tx	= zx297510_uart_start_tx,
+	.stop_tx	= zx297510_uart_stop_tx,
+	.stop_rx	= zx297510_uart_stop_rx,
+	.enable_ms	= zx297510_uart_enable_ms,
+	.break_ctl	= zx297510_uart_break_ctl,
+	.startup	= zx297510_uart_startup,
+	.shutdown	= zx297510_uart_shutdown,
+	.set_termios	= zx297510_uart_set_termios,
+	.type		= zx297510_uart_type,
+	.request_port	= zx297510_uart_request_port,
+	.release_port	= zx297510_uart_release_port,
+	.config_port	= zx297510_uart_config_port,
+	.verify_port	= zx297510_uart_verify_port,
+#ifdef CONFIG_CONSOLE_POLL
+	.poll_get_char = zx297510_get_poll_char,
+	.poll_put_char = zx297510_put_poll_char,
+#endif
+
+};
+
+
+static struct zx297510_uart_port zx297510_uart_ports[UART_NUM];	
+
+#define	zx297510_MAXPORTS	ARRAY_SIZE(zx297510_uart_ports)
+
+static void __devinit zx297510_init_ports(struct zx297510_uart_port *zx297510_port,	
+                                           struct platform_device *pdev)
+{
+	struct uart_port *port=&zx297510_port->port;
+	unsigned int offset=(unsigned int)(pdev->id);
+	struct clk *pclk=NULL;
+
+    /*  config uart apb_clk   */
+	pclk=clk_get(&pdev->dev, "apb_clk");
+	if (IS_ERR(pclk))
+		ZDRV_ASSERT(0);
+	zx297510_port->busclk=pclk;   /*get apb clock*/ 
+	
+
+	/*  config uart work_clk   */
+	pclk=clk_get(&pdev->dev, "work_clk");
+	if (IS_ERR(pclk))
+		ZDRV_ASSERT(0);
+	zx297510_port->wclk=pclk;   /*get work clock*/ 
+
+	port->line = offset;
+	port->type = PORT_ZX297510;
+	port->fifosize = UART_TXFIFO_SIZE;
+	port->iotype = UPIO_MEM;	
+	port->irq = pdev->resource[1].start;
+	port->mapbase = pdev->resource[0].start;
+	port->membase = ZX29_UART0_VA+0x10000*offset;
+	port->flags = UPF_BOOT_AUTOCONF;
+	port->ops = &zx297510_uart_ops;
+	port->uartclk = clk_get_rate(pclk);
+    port->private_data=pdev;
+
+}
+
+
+#ifdef CONFIG_SERIAL_ZX297510_UART_CONSOLE
+
+static void zx297510_uart_console_putc(struct uart_port *port, const char c)
+{
+	while (UART_GET_FR(port) & UART_FR_TXFF)
+		barrier();
+	UART_PUT_CHAR(port, c);
+}
+
+static void zx297510_uart_console_write(struct console *co, const char *s, unsigned int count)
+{
+	struct uart_port *port = &zx297510_uart_ports[co->index].port;
+
+	for (; (count); count--, s++) {
+		zx297510_uart_console_putc(port, *s);
+		if (*s == '\n')
+			zx297510_uart_console_putc(port, '\r');
+	}
+}
+
+/*
+ * If the port was already initialized (eg, by a boot loader),
+ * try to determine the current setup.
+ */
+static void __init zx297510_console_get_options(struct uart_port *port, int *baud,
+						 int *parity, int *bits)
+{
+	if (UART_GET_CR(port) & UART_CR_UARTEN) {
+		unsigned int lcr_h, ibrd, fbrd;
+
+		lcr_h = UART_GET_LCRH(port);
+		*parity = 'n';
+		if (lcr_h & UART_LCRH_PEN) {
+			if (lcr_h & UART_LCRH_EPS)
+				*parity = 'e';
+			else
+				*parity = 'o';
+		}
+		if ((lcr_h & 0x60) == UART_LCRH_WLEN_7)
+			*bits = 7;
+		else
+			*bits = 8;
+
+		ibrd = UART_GET_IBRD(port);
+		fbrd = UART_GET_FBRD(port);
+
+		*baud = port->uartclk * 4 / (64 * ibrd + fbrd);
+	}
+}
+
+static int __init zx297510_uart_console_setup(struct console *co, char *options)
+{
+	struct uart_port *port;
+	int baud = 115200;
+	int bits = 8;
+	int parity = 'n';
+	int flow = 'n';
+
+	if ((co->index < 0) || (co->index >= zx297510_MAXPORTS))
+		co->index = 0;
+	port = &zx297510_uart_ports[co->index].port;
+	if (port->membase == NULL)
+		return -ENODEV;
+
+	if (options)
+		uart_parse_options(options, &baud, &parity, &bits, &flow);
+	else
+		zx297510_console_get_options(port, &baud, &parity, &bits);
+
+	return uart_set_options(port, co, baud, parity, bits, flow);
+}
+
+static struct uart_driver zx297510_uart_driver;
+
+static struct console zx297510_uart_console = {
+	.name		= "ttyS",
+	.write		= zx297510_uart_console_write,
+	.device		= uart_console_device,
+	.setup		= zx297510_uart_console_setup,
+	.flags		= CON_PRINTBUFFER,
+	.index		= -1,
+	.data		= &zx297510_uart_driver,
+};
+
+#define	zx297510_UART_CONSOLE	(&zx297510_uart_console)
+#else
+#define	zx297510_UART_CONSOLE	NULL
+#endif /* CONFIG_zx297510_UART_CONSOLE */
+
+/*
+ *	Define the zx297510 UART driver structure.
+ */
+static struct uart_driver zx297510_uart_driver = {
+	.owner		    = THIS_MODULE,
+	.driver_name	= "zx297510_uart",
+	.dev_name	    = "ttyS",
+	.major		    = SERIAL_zx297510_MAJOR,
+	.minor		    = SERIAL_MINOR_START,
+	.nr		        = zx297510_MAXPORTS,
+	.cons		    = zx297510_UART_CONSOLE,
+};
+
+
+static int __devinit zx297510_uart_probe(struct platform_device *pdev)
+{
+	/*struct zx297510_platform_uart *pdata = pdev->dev.platform_data;*/
+	struct zx297510_uart_port *port = &zx297510_uart_ports[pdev->id];
+	int ret=0;
+
+ 	zx297510_init_ports(port,pdev);	
+    ret=uart_add_one_port(&zx297510_uart_driver, &port->port);
+	if(ret)
+		return ret;
+	 
+	/*platform_set_drvdata(pdev, port);*/
+	
+    printk(KERN_INFO "TSP zx297510 UART_%d probe OK\n",pdev->id);
+	return 0;
+}
+
+
+static int __devexit zx297510_uart_remove(struct platform_device *pdev)
+{
+	struct uart_port *port;
+	int i;
+
+	for (i = 0; (i < zx297510_MAXPORTS); i++) {
+		port = &zx297510_uart_ports[i].port;
+		if (port)
+			uart_remove_one_port(&zx297510_uart_driver, port);
+	}
+
+	return 0;
+}
+
+
+static struct platform_driver zx297510_uart_platform_driver = {
+	.probe		= zx297510_uart_probe,
+	.remove		= zx297510_uart_remove,
+	.driver		= {
+		.name	= "zx297510_uart",
+		.owner	= THIS_MODULE,
+	},
+};
+
+
+static int __init zx297510_uart_init(void)
+{
+	int rc;
+
+	rc = uart_register_driver(&zx297510_uart_driver);
+	if (rc)
+		return rc;
+	rc = platform_driver_register(&zx297510_uart_platform_driver);
+	if (rc){
+		uart_unregister_driver(&zx297510_uart_driver);
+		return rc;
+	}
+		
+	printk(KERN_INFO "TSP zx297510 UART driver registered\n");
+	
+	return 0;
+}
+
+
+static void __exit zx297510_uart_exit(void)
+{
+#ifdef CONFIG_SERIAL_ZX297510_UART_CONSOLE
+	unregister_console(&zx297510_uart_console);
+#endif
+	platform_driver_unregister(&zx297510_uart_platform_driver);
+	uart_unregister_driver(&zx297510_uart_driver);
+}
+
+/****************************************************************************/
+
+module_init(zx297510_uart_init);
+module_exit(zx297510_uart_exit);
+
+MODULE_AUTHOR("gaowei");
+MODULE_DESCRIPTION("sanchips zx297510 UART driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:zx297510_uart");
+
+/****************************************************************************/
diff --git a/ap/os/linux/linux-3.4.x/drivers/tty/serial/zx297510_uart.h b/ap/os/linux/linux-3.4.x/drivers/tty/serial/zx297510_uart.h
new file mode 100755
index 0000000..5c0f4c3
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/tty/serial/zx297510_uart.h
@@ -0,0 +1,148 @@
+/*
+ * zx297510_uart.h -- sanchips zx297510 UART driver defines.
+ */
+
+#ifndef	__ZX297510_UART_H
+#define	__ZX297510_UART_H
+
+/*uart0 base address*/
+#define ZX29_UART0_VA	   (ZX297510_UART0_BASE - ZX297510_A1_BASE + ZX29_A1_VA)
+
+/*offset address*/
+#define zx297510_UART_DR		0x04	/* Data read or written from the interface. */
+#define zx297510_UART_SC        0x08    /* Set special character to generate a interrupt*/
+#define zx297510_UART_RSR		0x10	/* Receive status register (Read). */
+#define zx297510_UART_ECR		0x10	/* Error clear register (Write). */
+#define zx297510_UART_FR		0x14	/* Flag register (Read only). */
+
+#define zx297510_UART_ILPR		0x20	/* IrDA low power counter register. */
+#define zx297510_UART_IBRD		0x24	/* Integer baud rate divisor register. */
+#define zx297510_UART_FBRD		0x28	/* Fractional baud rate divisor register. */
+#define zx297510_UART_LCRH		0x30	/* Line control register. */
+#define zx297510_UART_CR		0x34	/* Control register. */
+#define zx297510_UART_IFLS		0x38	/* Interrupt fifo level select. */
+#define zx297510_UART_IMSC		0x40	/* Interrupt mask. */
+#define zx297510_UART_RIS		0x44	/* Raw interrupt status. */
+#define zx297510_UART_MIS		0x48	/* Masked interrupt status. */
+#define zx297510_UART_ICR		0x4c	/* Interrupt clear register. */
+#define zx297510_UART_DMACR		0x50	/* DMA control register. */
+
+
+/*------ uart flag reg -----*/
+#define UART_FR_RXBUSY		(1<<9)
+#define UART_FR_TXBUSY		(1<<8)
+#define UART_FR_TXFE		(1<<7)
+#define UART_FR_RXFF		(1<<6)
+#define UART_FR_TXFF		(1<<5)
+#define UART_FR_RXFE		(1<<4)
+#define UART_FR_DSR 		(1<<3)
+#define UART_FR_DCD 		(1<<2)
+#define UART_FR_CTS 		(1<<1)
+#define UART_FR_RI			(1<<0)
+
+/*------ uart control reg -----*/
+#define UART_CR_CTSEN		(1<<15)	/* CTS hardware flow control */
+#define UART_CR_RTSEN		(1<<14)	/* RTS hardware flow control */
+#define UART_CR_OUT2		(1<<13)	/* OUT2 */
+#define UART_CR_OUT1		(1<<12)	/* OUT1 */
+#define UART_CR_RTS			(1<<11)	/* RTS */
+#define UART_CR_DTR			(1<<10)	/* DTR */
+#define UART_CR_RXE			(1<<9)	/* receive enable */
+#define UART_CR_TXE			(1<<8)	/* transmit enable */
+#define UART_CR_LBE			(1<<7)	/* loopback enable */
+#define UART_CR_SIRLP		(1<<2)	/* SIR low power mode */
+#define UART_CR_SIREN		(1<<1)	/* SIR enable */
+#define UART_CR_UARTEN		(1<<0)	/* UART enable */
+
+/*------ uart line control reg -----*/
+#define UART_LCRH_SPS		(1<<7)
+#define UART_LCRH_WLEN_8	(3<<5)
+#define UART_LCRH_WLEN_7	(2<<5)
+#define UART_LCRH_WLEN_6	(1<<5)
+#define UART_LCRH_WLEN_5	(0<<5)
+#define UART_LCRH_FEN		(1<<4)
+#define UART_LCRH_STP2		(1<<3)
+#define UART_LCRH_EPS		(1<<2)
+#define UART_LCRH_PEN		(1<<1)
+#define UART_LCRH_BRK		(1<<0)
+
+/*------ uart fifo level select reg -----*/
+#define UART_IFLS_RX1_8	(0 << 3)
+#define UART_IFLS_RX2_8	(1 << 3)
+#define UART_IFLS_RX4_8	(2 << 3)
+#define UART_IFLS_RX6_8	(3 << 3)
+#define UART_IFLS_RX7_8	(4 << 3)
+#define UART_IFLS_TX1_8	(0 << 0)
+#define UART_IFLS_TX2_8	(1 << 0)
+#define UART_IFLS_TX4_8	(2 << 0)
+#define UART_IFLS_TX6_8	(3 << 0)
+#define UART_IFLS_TX7_8	(4 << 0)
+
+/*------ uart interrupt mask reg -----*/
+#define UART_OEIM		(1 << 10)	/* overrun error interrupt mask */
+#define UART_BEIM		(1 << 9)	/* break error interrupt mask */
+#define UART_PEIM		(1 << 8)	/* parity error interrupt mask */
+#define UART_FEIM		(1 << 7)	/* framing error interrupt mask */
+#define UART_RTIM		(1 << 6)	/* receive timeout interrupt mask */
+#define UART_TXIM		(1 << 5)	/* transmit interrupt mask */
+#define UART_RXIM		(1 << 4)	/* receive interrupt mask */
+#define UART_DSRMIM		(1 << 3)	/* DSR interrupt mask */
+#define UART_DCDMIM		(1 << 2)	/* DCD interrupt mask */
+#define UART_CTSMIM		(1 << 1)	/* CTS interrupt mask */
+#define UART_RIMIM		(1 << 0)	/* RI interrupt mask */
+
+/*------ uart raw interrupt status reg -----*/
+#define UART_OEIS		(1 << 10)	/* overrun error interrupt status */
+#define UART_BEIS		(1 << 9)	/* break error interrupt status */
+#define UART_PEIS		(1 << 8)	/* parity error interrupt status */
+#define UART_FEIS		(1 << 7)	/* framing error interrupt status */
+#define UART_RTIS		(1 << 6)	/* receive timeout interrupt status */
+#define UART_TXIS		(1 << 5)	/* transmit interrupt status */
+#define UART_RXIS		(1 << 4)	/* receive interrupt status */
+#define UART_DSRMIS		(1 << 3)	/* DSR interrupt status */
+#define UART_DCDMIS		(1 << 2)	/* DCD interrupt status */
+#define UART_CTSMIS		(1 << 1)	/* CTS interrupt status */
+#define UART_RIMIS		(1 << 0)	/* RI interrupt status */
+
+/*------ uart interrupt clear reg -----*/
+#define UART_SCIC		(1 << 11)	/* special character interrupt clear */
+#define UART_OEIC		(1 << 10)	/* overrun error interrupt clear */
+#define UART_BEIC		(1 << 9)	/* break error interrupt clear */
+#define UART_PEIC		(1 << 8)	/* parity error interrupt clear */
+#define UART_FEIC		(1 << 7)	/* framing error interrupt clear */
+#define UART_RTIC		(1 << 6)	/* receive timeout interrupt clear */
+#define UART_TXIC		(1 << 5)	/* transmit interrupt clear */
+#define UART_RXIC		(1 << 4)	/* receive interrupt clear */
+#define UART_DSRMIC		(1 << 3)	/* DSR interrupt clear */
+#define UART_DCDMIC		(1 << 2)	/* DCD interrupt clear */
+#define UART_CTSMIC		(1 << 1)	/* CTS interrupt clear */
+#define UART_RIMIC		(1 << 0)	/* RI interrupt clear */
+
+/*------ uart DMA control reg -----*/
+#define UART_DMAONERR	(1 << 2)	/* disable dma on error */
+#define UART_TXDMAE		(1 << 1)	/* enable transmit dma */
+#define UART_RXDMAE		(1 << 0)	/* enable receive dma */
+
+/*------ uart receive data  error indicator in data reg -----*/
+#define UART_DR_OE		(1 << 11)
+#define UART_DR_BE		(1 << 10)
+#define UART_DR_PE		(1 << 9)
+#define UART_DR_FE		(1 << 8)
+
+/*------ uart RSR reg -----*/
+#define UART_RSR_OE 		0x08
+#define UART_RSR_BE 		0x04
+#define UART_RSR_PE 		0x02
+#define UART_RSR_FE 		0x01
+
+#define UART_DR_ERROR		(UART_DR_OE|UART_DR_BE|UART_DR_PE|UART_DR_FE)
+#define UART_DUMMY_DR_RX	(1 << 16)
+
+#define UART_RSR_ANY		(UART_RSR_OE|UART_RSR_BE|UART_RSR_PE|UART_RSR_FE)
+#define UART_FR_MODEM_ANY	(UART_FR_DCD|UART_FR_DSR|UART_FR_CTS)
+
+#define UART_TXFIFO_SIZE 16
+
+#define UART_PIN_NUM_BASE 48  /*the first uart pin number*/
+
+#endif /* __ZX297502_UART_H */
diff --git a/ap/os/linux/linux-3.4.x/drivers/tty/serial/zx29_uart.c b/ap/os/linux/linux-3.4.x/drivers/tty/serial/zx29_uart.c
new file mode 100755
index 0000000..7e52d2f
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/tty/serial/zx29_uart.c
@@ -0,0 +1,3239 @@
+/****************************************************************************/
+/*
+ *    zx29_uart.c   sanchips
+ *
+ *	(C) Copyright 2003-2007, gaowei
+ *	(C) Copyright 2003-2007, sanchips
+ * 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.
+ */
+
+/****************************************************************************/
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/timer.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/console.h>
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
+#include <linux/serial.h>
+#include <linux/serial_core.h>
+#include <linux/platform_device.h>
+#include <linux/of.h>
+#include <linux/io.h>
+#include <linux/printk.h>
+#include <linux/clk.h>
+#include <linux/gpio.h>
+#include <linux/delay.h>
+#include <linux/dmaengine.h>
+#include <linux/dma-mapping.h>
+#include <linux/slab.h>
+#include <linux/scatterlist.h>
+#include <linux/device.h>
+#include <linux/ioport.h>
+#include <mach/gpio_def.h>
+#include <mach/irqs.h>
+#include <mach/board.h>
+#include <mach/gpio.h>
+#include <mach/debug.h>
+#include <mach/iomap.h>
+#include <mach/dma.h>
+#include <mach/dma_cfg.h>
+#include <linux/wakelock.h>
+#include <linux/kthread.h>
+#include <linux/semaphore.h>
+
+#include "mach/zx29_uart_def.h"
+
+#include "zx29_uart.h"
+#include <linux/soc/zte/pm/drv_idle.h>
+#include <mach/pcu.h>
+//#define DEBUG_UART
+
+#ifdef DEBUG_UART
+#pragma GCC optimize("O0")
+#endif
+
+#define CONFIG_SERIAL_ZX29_DMA 1
+//when using app control sleep, set this value to 1
+#define _USE_APP_CTRL_LP 0
+
+char uart_names[4][12] = {
+	"zx29_uart.0",
+	"zx29_uart.1",
+	"zx29_uart.2",
+	"zx29_uart.3"
+};
+#if CONFIG_SERIAL_ZX29_DMA
+#define ZX29_DMA_BUFFER_SIZE PAGE_SIZE
+#define UART_DMA_RX_MAX_COUNT 2
+#define RX_DMA_TIMEOUT (HZ / 10)//60
+#define RX_DMA_WORK	1
+struct zx29_sgbuf {
+	struct scatterlist sg;
+	char *o_buf;
+	char *buf;
+};
+
+struct zx29_dmarx_data {
+	struct dma_chan 	*chan;
+	struct completion	complete;
+	dma_channel_def rx_def[UART_DMA_RX_MAX_COUNT];
+	u32 			rx_index;
+	bool			use_buf_b;
+	struct zx29_sgbuf	sgbuf_a;
+	struct zx29_sgbuf	sgbuf_b;
+	dma_cookie_t		cookie;
+	bool			running;
+	atomic_t		count;
+	bool 		used;
+};
+
+struct zx29_dmatx_data {
+	struct dma_chan 	*chan;
+	struct completion	complete;
+	dma_channel_def tx_def;
+	struct scatterlist	sg;
+	char			*buf;
+	bool			queued;
+	atomic_t		count;
+};
+#endif
+
+
+
+#define UART_NUM 3
+
+
+/****************************************************************************/
+
+/* Use device name ttyS, major 4, minor 64-68.  This is the usual serial port
+ * name, but it is legally reserved for the 8250 driver. */
+#define SERIAL_zx29_MAJOR	TTY_MAJOR
+#define SERIAL_MINOR_START		64
+
+#define UART_PORT_AUTOBAUD_ON       1
+#define UART_PORT_AUTOBAUD_OFF      0
+#define UART_PORT_AUTOBAUD_BYTE      2
+#define UART_AT_SENDOK_NUM  	6
+#define UART_AUTOBAUD_LEVEL 	5
+#define UART_AUTOBAUD_CHECKBYTE 4
+#define UART_AUTOBAUD_RATE     115200
+#define UART1_AUTOBAUD_RATE    921600
+
+
+unsigned char uart_port_autobaud_buffer[UART_PORT_AUTOBAUD_BYTE] = {0};
+unsigned char uart_port_autobaud_gtflag = 0 ;
+unsigned char uart_port_autobaud_suflag = 0 ;
+unsigned char g_console_open_flag = 1;
+
+
+unsigned char  UART_AT_send_ok[UART_AT_SENDOK_NUM] = 
+	{
+		0x0d,0x0a,0x4F,0x4B,0x0d,0x0a
+	};
+
+unsigned char UART_baud_check[UART_AUTOBAUD_LEVEL][UART_AUTOBAUD_CHECKBYTE]=
+	{
+		{0x61,0x74,0x41,0x54},{0x06,0x9e,0x06,0x98},{0x1c,0x80,0x1c,0x00},
+		{0xe0,0x00,0xe0,0x00},{0x00,0x00,0x00,0x00},
+	};
+unsigned int UART_baud[UART_AUTOBAUD_LEVEL] =
+	{
+		115200,57600,38400,19200,9600
+	};
+unsigned int UART_termios_cflag[UART_AUTOBAUD_LEVEL] =
+	{
+		B115200,B57600,B38400,B19200,B9600
+	};
+
+#ifdef CONFIG_SERIAL_CORE_CONSOLE
+#define uart_console(port)	((port)->cons && (port)->cons->index == (port)->line)
+#else
+#define uart_console(port)	(0)
+#endif
+
+/****************************************************************************/
+/*
+ *	Local per-uart structure.
+ */
+struct zx29_uart_port 
+{
+	struct uart_port	port;
+	unsigned int		sigs;		/* Local copy of line sigs */
+	unsigned int		old_status;
+	unsigned char		imr;		/* Local interrupt mask reg mirror */
+#if CONFIG_SERIAL_ZX29_DMA
+	unsigned char		dmacr;		/* DMA reg*/
+#endif
+	bool				rts_state;
+	bool				autorts;	/* hardware flow control */
+	struct clk			*wclk;		/* uart work clock */
+	struct clk			*busclk;	  /* uart apb clock */
+	bool				autobaud;
+	bool				autobaud_state;
+	unsigned int		baudrate;
+	bool				uartwake;
+
+	int irq;
+	int irq_state;
+	struct tasklet_struct write_wakeup;
+	bool  rxd_wakeup;
+	int 	rxd_int_depth;	
+#if CONFIG_SERIAL_ZX29_DMA
+	/* DMA stuff */
+	bool			using_tx_dma;
+	bool			using_rx_dma;
+	struct zx29_dmarx_data dmarx;
+	struct zx29_dmatx_data dmatx;
+	struct timer_list rx_dma_timer;
+	struct task_struct	*dma_compl_th;
+	struct	semaphore    sema;
+	bool port_close;
+	bool work_state;
+	size_t pre_pending;
+	struct zx29_sgbuf	*sg2tty;
+		size_t sg2tty_len;
+	struct zx29_sgbuf	*curr_sg;	
+#endif
+
+	//means application decide close and release DMA &wakelock
+	int app_ctrl;
+};
+
+
+
+static struct zx29_uart_port zx29_uart_ports[UART_NUM];
+
+#define	zx29_MAXPORTS	ARRAY_SIZE(zx29_uart_ports)
+
+
+
+void uart_mod_timer(struct zx29_uart_port *zup, unsigned long *flags)
+{
+	raw_spin_unlock_irqrestore(&zup->port.lock, *flags);
+	mod_timer(&(zup->rx_dma_timer), jiffies + RX_DMA_TIMEOUT);
+	
+	raw_spin_lock_irqsave(&zup->port.lock, *flags);
+}
+/**
+* Show the console_input attribute.
+*/
+static ssize_t console_input_show(struct device *_dev,
+				struct device_attribute *attr, char *buf)
+{
+	return sprintf(buf, "\n console_input = %d\n",g_console_open_flag);
+}
+
+/**
+ * Store the console_input attribure.
+ * 0:  disable console input function,only out put log 
+ * 1:  able console input, can input commands
+ */
+static ssize_t console_input_store(struct device *_dev,
+				 struct device_attribute *attr,
+				 const char *buf, size_t count)
+{
+    uint32_t  flag = 0;
+	flag = simple_strtoul(buf, NULL, 16);
+    g_console_open_flag = flag;	
+
+	return count;
+}
+
+DEVICE_ATTR(console_input, S_IRUGO | S_IWUSR, console_input_show,
+	    console_input_store);
+
+static ssize_t ctsrts_input_show(struct device *_dev,
+				struct device_attribute *attr, char *buf)
+{
+	struct platform_device *pdev = container_of(_dev, struct platform_device, dev);
+	struct zx29_uart_platdata *pdata = pdev->dev.platform_data;
+	printk(KERN_INFO"ctsrts_input_show\n");
+	return sprintf(buf, "\n ctsrts_input = %d\n",pdata->uart_ctsrtsuse);
+}
+
+static ssize_t ctsrts_input_store(struct device *_dev,
+				 struct device_attribute *attr,
+				 const char *buf, size_t count)
+{
+    uint32_t  flag = 0;
+	struct platform_device *pdev = container_of(_dev, struct platform_device, dev);
+	struct zx29_uart_platdata *pdata = pdev->dev.platform_data;
+	flag = simple_strtoul(buf, NULL, 16);
+    pdata->uart_ctsrtsuse = flag;	
+
+	return count;
+}
+
+DEVICE_ATTR(ctsrts_input, S_IRUGO | S_IWUSR, ctsrts_input_show,
+	    ctsrts_input_store);
+
+static ssize_t wakeup_enable_show(struct device *_dev,
+				struct device_attribute *attr, char *buf)
+{
+	struct platform_device *pdev = container_of(_dev, struct platform_device, dev);
+	struct zx29_uart_platdata *pdata = pdev->dev.platform_data;
+	
+	return sprintf(buf, "\n wakeup_enable = %d\n",pdata->uart_wakeup_enable);
+}
+
+static ssize_t wakeup_enable_store(struct device *_dev,
+				 struct device_attribute *attr,
+				 const char *buf, size_t count)
+{
+    uint32_t  flag = 0;
+	struct platform_device *pdev = container_of(_dev, struct platform_device, dev);
+	struct zx29_uart_platdata *pdata = pdev->dev.platform_data;
+	flag = simple_strtoul(buf, NULL, 16);
+    pdata->uart_wakeup_enable = flag;	
+
+	return count;
+}
+
+DEVICE_ATTR(wakeup_enable, S_IRUGO | S_IWUSR, wakeup_enable_show,
+	    wakeup_enable_store);
+int rxd_wake_cnt = 0;
+static ssize_t statics_show(struct device *_dev,
+				struct device_attribute *attr, char *buf)
+{
+	struct platform_device *pdev = container_of(_dev, struct platform_device, dev);
+	struct zx29_uart_platdata *pdata = pdev->dev.platform_data;
+	
+	return sprintf(buf, "\n RX:%u,TX:%u,OE:%u,brk:%u,FE:%u,PE:%u ,rxd_wake_cnt:%d\n",
+		zx29_uart_ports[pdev->id].port.icount.rx,
+		zx29_uart_ports[pdev->id].port.icount.tx,
+		zx29_uart_ports[pdev->id].port.icount.overrun,
+		zx29_uart_ports[pdev->id].port.icount.brk,
+		zx29_uart_ports[pdev->id].port.icount.frame,
+		zx29_uart_ports[pdev->id].port.icount.parity,
+		rxd_wake_cnt);
+}
+DEVICE_ATTR(statics, S_IRUGO | S_IWUSR, statics_show, NULL);
+
+
+static ssize_t app_ctrl_show(struct device *_dev,
+				struct device_attribute *attr, char *buf)
+{
+	struct platform_device *pdev = container_of(_dev, struct platform_device, dev);
+	//struct zx29_uart_platdata *pdata = pdev->dev.platform_data;
+	
+	return sprintf(buf, "%d\n",zx29_uart_ports[pdev->id].app_ctrl);
+}
+
+static ssize_t app_ctrl_store(struct device *_dev,
+				 struct device_attribute *attr,
+				 const char *buf, size_t count)
+{
+    uint32_t  flag = 0;
+	struct platform_device *pdev = container_of(_dev, struct platform_device, dev);
+	//struct zx29_uart_platdata *pdata = pdev->dev.platform_data;
+	flag = simple_strtoul(buf, NULL, 16);
+   // pdata->uart_wakeup_enable = flag;	
+	zx29_uart_ports[pdev->id].app_ctrl = (flag == 0) ? 0 : 1;
+   
+	return count;
+}
+DEVICE_ATTR(app_ctrl, S_IRUGO | S_IWUSR, app_ctrl_show,
+	    app_ctrl_store);
+
+#if CONFIG_SERIAL_ZX29_DMA
+static inline bool zx29_dma_tx_start(struct zx29_uart_port *zup);
+static inline void zx29_dma_tx_stop(struct zx29_uart_port *zup);
+static bool zx29_dma_tx_irq(struct zx29_uart_port *zup);
+static int zx29_uart_dma_tx_chars(struct zx29_uart_port *zup);
+void uart_dma_rx_callback(void *data);
+static void zx29_uart_dma_rx_chars(struct zx29_uart_port *zup,
+											//u32 pending, bool use_buf_b, 
+											u32 pending, struct zx29_sgbuf *sgbuf, 
+											bool readfifo, unsigned long *flags);
+static inline void zx29_dma_rx_stop(struct zx29_uart_port *zup);
+static inline bool zx29_dma_rx_available(struct zx29_uart_port *zup);
+static inline bool zx29_dma_rx_running(struct zx29_uart_port *zup);
+static int zx29_dma_rx_trigger_dma(struct zx29_uart_port *zup);
+
+static void zx29_uart_rx_dma_chars(struct zx29_uart_port *zup, unsigned long *flags);
+dma_peripheral_id uart_get_rx_dma_peripheral_id(struct zx29_uart_port *zup);
+
+#if RX_DMA_WORK	
+static void zx29_uart_rx_timeout_chars(struct zx29_uart_port *zup, unsigned long *flags);
+static inline bool zx29_dma_rx_work_scheduled(struct zx29_uart_port *zup);
+
+static void zx29_uart_rt_dma(struct zx29_uart_port *zup, unsigned long *flags);
+#endif
+
+
+//extern int (*pm_callback_fn)(void);
+#ifdef CONFIG_CPU_IDLE
+typedef int (*pm_callback_fn)(void);
+extern int zx_pm_register_callback(pm_callback_fn enter_cb, pm_callback_fn exit_cb);
+
+extern void disable_irq_nosync(unsigned int irq);
+extern void enable_irq(unsigned int irq);
+
+void uart_rxd_int_disable(struct uart_port *port)
+{
+	struct zx29_uart_port *zup = container_of(port, struct zx29_uart_port, port);
+	zup->rxd_int_depth++;
+}
+EXPORT_SYMBOL(uart_rxd_int_disable);
+
+int uart_0_pm_enter()
+{
+    struct zx29_uart_port *zup = &zx29_uart_ports[0];
+
+	//zDrvInt_UnmaskIrq(UART0_RXD_INT);
+	if(zup->irq_state == 0 || zup->imr== 0)
+		return 0;
+	
+	pcu_int_clear(PCU_UART0_RXD_INT);
+	if(!zup->rxd_int_depth){
+		enable_irq(UART0_RXD_INT);
+		zup->rxd_int_depth++;
+	}
+	return 0;
+}
+
+int uart_0_pm_exit()
+{
+	
+	return 0;
+}
+#endif
+/****************************************************************************/
+#define ALIGN_DMA(size,align) (((unsigned int)(size)+align-1)&(~(align-1)))
+
+
+static int zx29_sgbuf_init(struct dma_chan *chan, struct zx29_sgbuf *sg,
+	enum dma_data_direction dir)
+{
+  	sg->o_buf = kmalloc(ZX29_DMA_BUFFER_SIZE+32, GFP_KERNEL);
+	if (!sg->o_buf)
+		return -ENOMEM;
+	
+	char *tmp_buf = sg->o_buf;
+	sg->buf =  (char *)(ALIGN_DMA(tmp_buf, 32));
+	sg_init_one(&sg->sg, sg->buf, ZX29_DMA_BUFFER_SIZE);
+
+	if (dma_map_sg(chan->device->dev, &sg->sg, 1, dir) != 1) {
+		kfree(sg->o_buf);
+		return -EINVAL;
+	}
+	
+	return 0;	
+}
+
+static void zx29_sgbuf_free(struct dma_chan *chan, struct zx29_sgbuf *sg,
+	enum dma_data_direction dir)
+{
+	if (sg->o_buf) {
+			dma_unmap_sg(chan->device->dev, &sg->sg, 1, dir);
+			kfree(sg->o_buf);
+			sg->o_buf = NULL ;
+			sg->buf = NULL;
+		}
+}
+#endif
+
+
+
+
+/****************************************************************************/
+static unsigned int zx29_uart_tx_empty(struct uart_port *port)
+{
+	return (UART_GET_FR(port)&(UART_FR_TXBUSY|UART_FR_TXFF)) ? 0 : TIOCSER_TEMT;
+}
+
+/****************************************************************************/
+static void zx29_uart_set_mctrl(struct uart_port *port, unsigned int mctrl)
+{
+	unsigned int control = 0;
+	struct zx29_uart_port *zup = container_of(port, struct zx29_uart_port, port);
+
+	zup->sigs = mctrl;
+	control = UART_GET_CR(&zup->port);
+	if(mctrl & TIOCM_DTR)
+		control |= UART_CR_DTR;
+	else
+		control &= ~ UART_CR_DTR;
+	
+	if(mctrl & TIOCM_RTS)
+		control |= UART_CR_RTS;
+	else
+		control &= ~UART_CR_RTS;
+	
+	if(mctrl & TIOCM_LOOP)
+		control |= UART_CR_LBE;
+	else
+		control &= ~UART_CR_LBE;
+	
+	/* We need to disable auto-RTS if we want to turn RTS off */	
+	if (zup->autorts) {
+		if (mctrl & TIOCM_RTS)		
+			control |= UART_CR_RTSEN;		
+		else				
+			control &= ~UART_CR_RTSEN;
+	}
+	UART_PUT_CR(port, control);
+}
+
+/****************************************************************************/
+static unsigned int zx29_uart_get_mctrl(struct uart_port *port)
+{
+	struct zx29_uart_port *zup = container_of(port, struct zx29_uart_port, port);
+	unsigned int mctrl = 0;
+	unsigned int uart_flag = 0;
+	
+	uart_flag = UART_GET_FR(port);
+
+	mctrl = (uart_flag&UART_FR_CTS) ?TIOCM_CTS : 0;
+	mctrl |= (zup->sigs & TIOCM_RTS);
+	mctrl |= (uart_flag&UART_FR_DCD) ? TIOCM_CD : 0;
+	mctrl |= (uart_flag&UART_FR_DSR) ? TIOCM_DSR : 0;
+	mctrl |= (uart_flag&UART_FR_RI) ? TIOCM_RI : 0;
+
+	return mctrl;
+}
+
+/****************************************************************************/
+static void zx29_uart_start_tx(struct uart_port *port)
+{
+	struct zx29_uart_port *zup = container_of(port, struct zx29_uart_port, port);
+	unsigned int control = 0;
+	unsigned int reg_bak[10] = {0};
+	struct circ_buf *xmit = &zup->port.state->xmit;
+	int count = 0;	
+#if 0
+	if(uart_console(port) && !g_console_open_flag){
+		count = uart_circ_chars_pending(xmit);
+		xmit->tail = xmit->head;
+		zup->port.icount.tx += count;
+		return;
+	}
+#endif
+	if(!(UART_GET_RIS(port)&UART_TXIS) && (UART_GET_FR(port) & UART_FR_TXFE))
+	{
+		if(!(UART_GET_RIS(port)&UART_TXIS))	
+		{
+			count = uart_circ_chars_pending(xmit);
+			if(count >= zup->port.fifosize)
+				count = 15;//sent data more than TX ifls, TXIS will  coming soon
+
+			do {
+				UART_PUT_CHAR(&zup->port, xmit->buf[xmit->tail]);
+				xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
+				zup->port.icount.tx++;
+				if (uart_circ_empty(xmit) || (UART_GET_RIS(port)&UART_TXIS))
+					break;
+			} while (--count > 0);
+		}
+
+	}
+#if CONFIG_SERIAL_ZX29_DMA	 
+	if(!uart_console(port))
+	 { 
+		 if (!zx29_dma_tx_start(zup))
+		   	{
+				zup->imr |= UART_TXIM;
+				UART_PUT_IMSC(port, zup->imr);
+				if(!(UART_GET_RIS(port)&UART_TXIS)){
+					if((UART_GET_FR(port) & UART_FR_TXFF))
+						return;
+					count = uart_circ_chars_pending(xmit);
+					while (count > 0) {
+						UART_PUT_CHAR(&zup->port, xmit->buf[xmit->tail]);
+						xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
+						zup->port.icount.tx++;
+						if (uart_circ_empty(xmit) || (UART_GET_RIS(port)&UART_TXIS) ||
+							(UART_GET_FR(port) & UART_FR_TXFF))
+							break;
+					} 					
+				}
+		   	}
+	}
+	else
+	{
+		zup->imr |= UART_TXIM;
+		UART_PUT_IMSC(port, zup->imr);
+	}
+#else
+	zup->imr |= UART_TXIM;
+	UART_PUT_IMSC(port, zup->imr);
+#endif
+}
+
+static void uart_write_wakeup_task(unsigned long _port)
+{
+	struct uart_port	*port = (void *)_port;
+struct zx29_uart_port *zup = container_of(port, struct zx29_uart_port, port);	
+	//printk("wakeup_task,port:%d\n", port->line);
+	if(zup->rxd_wakeup){
+		wake_lock_timeout(&(zup->port.port_wakelock),msecs_to_jiffies(5*1000));
+		zup->rxd_wakeup = false;
+	} else {
+	uart_write_wakeup(port);
+	}
+}
+
+
+void dma_complete_thread(void *ptr)
+{
+	unsigned long flags;
+	struct zx29_uart_port *zup = (struct zx29_uart_port *)ptr;
+
+	size_t pending;
+	struct dma_tx_state state;
+	struct zx29_dmarx_data *dmarx = &zup->dmarx;
+	struct dma_chan *rxchan = dmarx->chan;
+	bool lastbuf;
+	int dma_count = 0;
+	struct zx29_sgbuf *sgbuf = NULL;	
+	struct device *dev = NULL;
+	struct tty_struct *tty = NULL;
+	dma_peripheral_id rx_id = uart_get_rx_dma_peripheral_id(zup);
+		tty = zup->port.state->port.tty;
+		dev = zup->dmarx.chan->device->dev;
+	
+	while(down_interruptible(&zup->sema) == 0)
+	{	
+		if(zup->port_close)
+			break;
+		raw_spin_lock_irqsave(&zup->port.lock, flags);
+	//	tty = zup->port.state->port.tty;
+		if(!zup->sg2tty)
+			panic("dma_complete_thread, buffer 2 tty is invalid\n");
+	//	dev = zup->dmarx.chan->device->dev;
+		pending = zup->sg2tty_len;
+		if(zx29_dma_rx_running(zup)){
+
+			
+			uart_mod_timer(zup, &flags);
+		}	
+		/* Pick everything from the DMA first */
+		if (pending) {
+			/* Sync in buffer */		
+			dma_sync_sg_for_cpu(dev, &zup->sg2tty->sg, 1, DMA_FROM_DEVICE);
+			//BUG();
+
+			raw_spin_unlock_irqrestore(&zup->port.lock, flags);		
+			dma_count = tty_insert_flip_string(tty,
+							   zup->sg2tty->buf, pending);
+
+			tty_flip_buffer_push(tty);
+
+			raw_spin_lock_irqsave(&zup->port.lock, flags);		
+			/* Return buffer to device */
+			dma_sync_sg_for_device(dev, &zup->sg2tty->sg, 1, DMA_FROM_DEVICE);
+
+		zup->sg2tty = NULL;
+		zup->sg2tty_len = 0;
+			zup->port.icount.rx += dma_count;
+
+			//if(zup->port.line == 0)
+		      //printk("yanming dma_complete_thread, dma2tty:%d\n", dma_count);			
+			if (dma_count < pending)
+				dev_info(zup->port.dev,
+					 "couldn't insert all characters (TTY is full?)\n");
+			
+
+		}
+#if 0		
+		zup->work_state = false;
+		zup->pre_pending = 0;	 
+		zup->imr |= UART_RXIM;
+		UART_PUT_IMSC(&zup->port, zup->imr);	
+#endif		
+
+		raw_spin_unlock_irqrestore(&zup->port.lock, flags);
+	}
+}
+
+/****************************************************************************/
+static void zx29_uart_stop_tx(struct uart_port *port)
+{
+	struct zx29_uart_port *zup = container_of(port, struct zx29_uart_port, port);
+	zup->imr &= ~UART_TXIM;
+	UART_PUT_IMSC(port, zup->imr);
+#ifdef CONFIG_SERIAL_ZX29_UART_CONSOLE
+	if((port->line == DEBUG_CONSOLE)  &&  uart_tx_stopped(port))
+	{
+		//uart_write_wakeup(port);
+		tasklet_schedule(&zup->write_wakeup);
+	}
+#endif
+
+#if CONFIG_SERIAL_ZX29_DMA
+	zx29_dma_tx_stop(zup);
+#endif
+
+	zx_cpuidle_set_free(IDLE_FLAG_UART);
+
+}
+
+/****************************************************************************/
+static void zx29_uart_stop_rx(struct uart_port *port)
+{
+	struct zx29_uart_port *zup = container_of(port, struct zx29_uart_port, port);
+
+	zup->imr &= ~(UART_RXIM|UART_RTIM|UART_FEIM|UART_PEIM|UART_BEIM|UART_OEIM);
+	UART_PUT_IMSC(port, zup->imr);
+#if CONFIG_SERIAL_ZX29_DMA
+	zx29_dma_rx_stop(zup);
+#endif
+}
+
+/****************************************************************************/
+static void zx29_uart_break_ctl(struct uart_port *port, int break_state)
+{
+	struct zx29_uart_port *zup = container_of(port, struct zx29_uart_port, port);
+	unsigned long flags;
+	unsigned int lcr_h;
+	raw_spin_lock_irqsave(&zup->port.lock, flags);
+	lcr_h = UART_GET_LCRH(port);
+	if (break_state == -1)
+		lcr_h |= UART_LCRH_BRK;
+	else
+		lcr_h &= ~UART_LCRH_BRK;
+	UART_PUT_LCRH(port, lcr_h);
+	raw_spin_unlock_irqrestore(&zup->port.lock, flags);
+}
+
+/****************************************************************************/
+static void zx29_uart_enable_ms(struct uart_port *port)
+{
+	struct zx29_uart_port *zup = container_of(port, struct zx29_uart_port, port);
+	zup->imr |= UART_RIMIM|UART_CTSMIM|UART_DCDMIM|UART_DSRMIM;
+	UART_PUT_IMSC(port, zup->imr);
+}
+
+/****************************************************************************/
+/*--------------------------------------------------------------------
+ * Reads up to 256 characters from the FIFO or until it's empty and
+ * inserts them into the TTY layer. Returns the number of characters
+ * read from the FIFO.
+ --------------------------------------------------------------------*/
+static int zx29_uart_fifo_to_tty(struct zx29_uart_port *zup)
+{
+	struct uart_port *port = &zup->port;
+	u32 status, ch, i = 0;
+	unsigned int flag, max_count = 256;
+	int fifotaken = 0;	
+	u32 uart_poll_char[6] ={0};
+
+	while (max_count--) {
+		status = UART_GET_FR(port);
+		if (status & UART_FR_RXFE)
+			break;
+
+		/* Take chars from the FIFO and update status */
+		ch = UART_GET_CHAR(port) |	UART_DUMMY_DR_RX;
+#if 0
+		if(g_console_open_flag == 0 && 
+			port->line == DEBUG_CONSOLE){
+			if((ch&0xff) == 't'){
+				memset(uart_poll_char, 0, sizeof(uart_poll_char));
+				uart_poll_char[0] = 't';
+				i = 0;
+				printk("ch = %c i = %d\n",ch,i);
+			}else if ((ch&0xff) == 'y' && (i == 1)){
+				uart_poll_char[1] = 'y';
+				printk("ch = %c i = %d\n",ch,i);
+			}else if ((ch&0xff) == 'o' && (i == 2)){
+				uart_poll_char[2] = 'o';
+				printk("ch = %c i = %d\n",ch,i);
+			}else if ((ch&0xff) == 'p' && (i == 3)){
+				uart_poll_char[3] = 'p';
+				printk("ch = %c i = %d\n",ch,i);
+
+			}else if ((ch&0xff) == 'e' && (i == 4)){
+				uart_poll_char[4] = 'e';
+				printk("ch = %c i = %d\n",ch,i);
+
+			}else if ((ch&0xff) == 'n' && (i == 5)){
+				uart_poll_char[5] = 'n';
+				printk("ch = %c i = %d\n",ch,i);
+				g_console_open_flag = 1;
+				printk("ch = %c i = %d,g_console_open_flag:%d\n",ch,i,g_console_open_flag);
+			}else { 
+				i = 10;
+			}
+			i++;
+		}
+#endif
+		flag = TTY_NORMAL;
+              if(zup->autobaud_state == UART_PORT_AUTOBAUD_ON)
+              {
+                    if(zup->port.icount.rx < UART_PORT_AUTOBAUD_BYTE)
+                    {
+                        uart_port_autobaud_buffer[zup->port.icount.rx] = ch;
+                    }
+                    else
+                    {
+                        uart_port_autobaud_gtflag = 1 ; 
+                    }
+              }
+		zup->port.icount.rx++;
+              if(zup->autobaud_state == UART_PORT_AUTOBAUD_OFF) 
+              {
+		fifotaken++;
+
+		if (unlikely(ch & UART_DR_ERROR)) {
+			if (ch & UART_DR_BE) {
+				ch &= ~(UART_DR_FE | UART_DR_PE);
+				zup->port.icount.brk++;
+				if (uart_handle_break(&zup->port))
+					continue;
+			} else if (ch & UART_DR_PE)
+				zup->port.icount.parity++;
+			else if (ch & UART_DR_FE)
+				zup->port.icount.frame++;
+			else if (ch & UART_DR_OE){
+				zup->port.icount.overrun++;
+				//if(!uart_console(&zup->port))
+				//	BUG_ON(1);
+			}
+			ch &= zup->port.read_status_mask;
+
+			if (ch & UART_DR_BE)
+				flag = TTY_BREAK;
+			else if (ch & UART_DR_PE)
+				flag = TTY_PARITY;
+			else if (ch & UART_DR_FE)
+				flag = TTY_FRAME;
+		}
+
+			if (uart_handle_sysrq_char(&zup->port, ch & 255))
+				continue;
+			if(g_console_open_flag || port->line != DEBUG_CONSOLE){
+				uart_insert_char(&zup->port, ch, UART_DR_OE, ch, flag);
+			}
+		}
+	}
+
+	return fifotaken;
+}
+
+/****************************************************************************/
+static void zx29_uart_rx_chars(struct zx29_uart_port *zup)
+{
+	unsigned long flags;
+
+	struct tty_struct *tty = zup->port.state->port.tty;
+
+	zx29_uart_fifo_to_tty(zup);
+	raw_spin_unlock(&zup->port.lock);
+
+	tty_flip_buffer_push(tty);
+
+#if CONFIG_SERIAL_ZX29_DMA
+	if(!uart_console(&zup->port)){//console doesn't use dma rcv data
+		if (zx29_dma_rx_available(zup)) {
+				if (zx29_dma_rx_trigger_dma(zup)) {
+				dev_dbg(zup->port.dev, "could not trigger RX DMA job "
+					"fall back to interrupt mode again\n");
+				zup->imr |= UART_RXIM;
+			} else{
+				zup->imr &= ~UART_RXIM;
+			}
+			UART_PUT_IMSC(&zup->port,zup->imr);
+		}
+    }
+#endif
+	raw_spin_lock(&zup->port.lock);
+
+}
+
+/****************************************************************************/
+static void zx29_uart_tx_chars(struct zx29_uart_port *zup)
+{
+	struct circ_buf *xmit = &zup->port.state->xmit;
+	unsigned long flags;
+	int count;
+
+	if (zup->port.x_char) {
+		UART_PUT_CHAR(&zup->port, zup->port.x_char);
+		zup->port.icount.tx++;
+		zup->port.x_char = 0;
+		return;
+	}
+	if (uart_circ_empty(xmit) || uart_tx_stopped(&zup->port)) {
+		zx29_uart_stop_tx(&zup->port);
+		return;
+	}
+#if CONFIG_SERIAL_ZX29_DMA
+	/* If we are using DMA mode, try to send some characters. */
+	if(!uart_console(&(zup->port)))
+	 { 
+		if (zx29_dma_tx_irq(zup))
+			return;
+	}
+#endif	
+	count = zup->port.fifosize >> 1;
+	do {
+		UART_PUT_CHAR(&zup->port, xmit->buf[xmit->tail]);
+		xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
+		zup->port.icount.tx++;
+		if (uart_circ_empty(xmit))
+			break;
+	} while (--count > 0);
+
+	if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
+	{
+		raw_spin_unlock(&zup->port.lock);
+		//uart_write_wakeup(&zup->port);
+		tasklet_schedule(&zup->write_wakeup);
+		raw_spin_lock(&zup->port.lock);
+	}
+
+	if (uart_circ_empty(xmit))
+		zx29_uart_stop_tx(&zup->port);
+}
+
+#if CONFIG_SERIAL_ZX29_DMA
+
+dma_peripheral_id uart_get_rx_dma_peripheral_id(struct zx29_uart_port *zup)
+{
+       struct uart_port *port = &zup->port;
+	if(port->line  == UART0){
+	    return DMA_CH_UART0_RX;
+	} else if(port->line   == UART1){
+	    return DMA_CH_UART1_RX;
+	}else if(port->line   == UART2){
+	    return DMA_CH_UART2_RX;
+	}	
+	return 0;
+}
+
+/*
+ * We received a transmit interrupt without a pending X-char but with
+ * pending characters.
+ * Locking: called with port lock held and IRQs disabled.
+ * Returns:
+ *   false if we want to use PIO to transmit
+ *   true if we queued a DMA buffer
+ */
+static bool zx29_dma_tx_irq(struct zx29_uart_port *zup)
+{
+	if (!zup->using_tx_dma)
+		return false;
+
+	/*
+	 * If we already have a TX buffer queued, but received a
+	 * TX interrupt, it will be because we've just sent an X-char.
+	 * Ensure the TX DMA is enabled and the TX IRQ is disabled.
+	 */
+	if (zup->dmatx.queued) {
+		zup->dmacr |= UART_TXDMAE;
+		UART_PUT_DMACR(&zup->port, zup->dmacr);
+		zup->imr &= ~UART_TXIM;
+		UART_PUT_IMSC(&zup->port,zup->imr);
+		return true;
+	}
+
+	/*
+	 * We don't have a TX buffer queued, so try to queue one.
+	 * If we successfully queued a buffer, mask the TX IRQ.
+	 */
+	if (zx29_uart_dma_tx_chars(zup) > 0) {
+		zup->imr &= ~UART_TXIM;
+		UART_PUT_IMSC(&zup->port,zup->imr);
+		return true;
+	}
+	return false;
+}
+
+
+/*
+ * Stop the DMA transmit (eg, due to received XOFF).
+ * Locking: called with port lock held and IRQs disabled.
+ */
+static inline void zx29_dma_tx_stop(struct zx29_uart_port *zup)
+{
+	if (zup->dmatx.queued) {
+		zup->dmacr &= ~UART_TXDMAE;
+		UART_PUT_DMACR(&zup->port, zup->dmacr);
+	}
+}
+
+
+/*
+ * Try to start a DMA transmit, or in the case of an XON/OFF
+ * character queued for send, try to get that character out ASAP.
+ * Locking: called with port lock held and IRQs disabled.
+ * Returns:
+ *   false if we want the TX IRQ to be enabled
+ *   true if we have a buffer queued
+ */
+static inline bool zx29_dma_tx_start(struct zx29_uart_port *zup)
+{
+	u16 dmacr;
+
+	if (!zup->using_tx_dma)
+		return false;
+
+	if (!zup->port.x_char) {
+		/* no X-char, try to push chars out in DMA mode */
+		bool ret = true;
+
+		if (!zup->dmatx.queued) {
+			if (zx29_uart_dma_tx_chars(zup) > 0) {
+				zup->imr &= ~UART_TXIM;
+				ret = true;
+			} else {
+				zup->imr |= UART_TXIM;
+				ret = false;
+			}
+			UART_PUT_IMSC(&zup->port,zup->imr);
+		} else if (!(zup->dmacr & UART_TXDMAE)) {
+			zup->dmacr |= UART_TXDMAE;
+			UART_PUT_DMACR(&zup->port, zup->dmacr);
+		}
+		return ret;
+	}
+
+	/*
+	 * We have an X-char to send.  Disable DMA to prevent it loading
+	 * the TX fifo, and then see if we can stuff it into the FIFO.
+	 */
+	dmacr = zup->dmacr;
+	zup->dmacr &= ~UART_TXDMAE;
+	UART_PUT_DMACR(&zup->port, zup->dmacr);
+
+	if (UART_GET_FR(&zup->port) & UART_FR_TXFF) {
+		/*
+		 * No space in the FIFO, so enable the transmit interrupt
+		 * so we know when there is space.  Note that once we've
+		 * loaded the character, we should just re-enable DMA.
+		 */
+		return false;
+	}
+
+	UART_PUT_CHAR(&zup->port, zup->port.x_char);
+	//writew(uap->port.x_char, uap->port.membase + UART01x_DR);
+	zup->port.icount.tx++;
+	zup->port.x_char = 0;
+
+	/* Success - restore the DMA state */
+	zup->dmacr = dmacr;
+	UART_PUT_DMACR(&zup->port, zup->dmacr);
+	//writew(dmacr, uap->port.membase + UART011_DMACR);
+
+	return true;
+}
+
+/****************************************************************************/
+
+//#if CONFIG_SERIAL_ZX29_DMA
+/*
+ * Flush the transmit buffer.
+ * Locking: called with port lock held and IRQs disabled.
+ */
+static void zx29_dma_flush_buffer(struct uart_port *port)
+{
+	struct zx29_uart_port *zup = (struct zx29_uart_port *)port;
+	if (!zup->using_tx_dma)
+		return;
+
+	/* Avoid deadlock with the DMA engine callback */
+	//dmaengine_terminate_all(zup->dmatx.chan);
+	if (zup->dmatx.queued) {
+		
+	//printk(KERN_INFO "zx29_dma_flush_buffer enter[%s][%d] Port[%d]\n",__func__,__LINE__,port->line);
+		dma_unmap_sg(zup->dmatx.chan->device->dev, &zup->dmatx.sg, 1,
+			     DMA_TO_DEVICE);
+		zup->dmatx.queued = false;
+		zup->dmacr &= ~UART_TXDMAE;
+		UART_PUT_DMACR(&zup->port, zup->dmacr);
+	}
+}
+
+static int zx29_dma_rx_trigger_dma(struct zx29_uart_port *zup)
+{
+	struct dma_chan *rxchan = zup->dmarx.chan;
+	struct zx29_dmarx_data *dmarx = &zup->dmarx;
+	struct dma_async_tx_descriptor *desc;
+	struct zx29_sgbuf *sgbuf;
+	if (!rxchan)
+	{
+	printk("[%s][%d]\n",__func__,__LINE__);
+	return -EIO;
+	}
+
+	/* Start the RX DMA job */
+
+	sgbuf = zup->dmarx.use_buf_b ?
+		&zup->dmarx.sgbuf_b : &zup->dmarx.sgbuf_a;
+	/*
+
+		sgbuf = zup->dmarx.use_buf_b ?
+			&zup->dmarx.sgbuf_a : &zup->dmarx.sgbuf_b;
+    */
+#if 0	
+	if(sgbuf == zup->curr_sg)
+		panic("commit the same sg\n");
+#endif	
+	zup->dmarx.rx_def[zup->dmarx.rx_index].link_addr=0;
+	zup->dmarx.rx_def[zup->dmarx.rx_index].dest_addr=(unsigned int)(sgbuf->sg.dma_address);
+	zup->dmarx.rx_def[zup->dmarx.rx_index].count=ZX29_DMA_BUFFER_SIZE;//fifo or max buffer?
+		wmb();
+	dmaengine_slave_config(zup->dmarx.chan, (struct dma_slave_config*)&zup->dmarx.rx_def[zup->dmarx.rx_index]);
+	desc = zup->dmarx.chan->device->device_prep_interleaved_dma(zup->dmarx.chan,NULL,0);
+	/*
+	 * If the DMA engine is busy and cannot prepare a
+	 * channel, no big deal, the driver will fall back
+	 * to interrupt mode as a result of this error code.
+	 */
+	if (!desc) {
+		printk(KERN_INFO "!!ERROR DESC !!![%s][%d]Port:[%d]\n",__func__,__LINE__,zup->port.line);
+		zup->dmarx.running = false;
+		//dmaengine_terminate_all(rxchan);
+		return -EBUSY;
+	}
+
+	/* Some data to go along to the callback */
+	desc->callback = uart_dma_rx_callback;
+	desc->callback_param = zup;
+	zup->curr_sg = sgbuf;
+	wmb();
+
+	dmarx->cookie = dmaengine_submit(desc);
+	dma_async_issue_pending(rxchan);
+	atomic_inc(&zup->dmarx.count);
+	zup->dmarx.rx_index = (zup->dmarx.rx_index +1)%UART_DMA_RX_MAX_COUNT;
+	zup->dmacr |= UART_RXDMAE;
+	UART_PUT_DMACR(&zup->port, zup->dmacr);
+	zup->dmarx.running = true;
+	zup->imr &= ~UART_RXIM;
+	UART_PUT_IMSC(&zup->port,zup->imr);
+	return 0;
+}
+
+void uart_dma_rx_callback(void *data)
+{
+	unsigned long flags;
+
+	struct zx29_uart_port *zup = (struct zx29_uart_port *)data;
+	struct zx29_dmarx_data *dmarx = &zup->dmarx;
+	struct dma_chan *rxchan = dmarx->chan;
+		struct device *dev = NULL;
+
+	bool lastbuf;
+	int dma_count = 0;
+
+#if 0	
+
+	struct zx29_sgbuf *sgbuf = dmarx->use_buf_b ?
+
+		&dmarx->sgbuf_b : &dmarx->sgbuf_a;
+
+#else
+
+	struct zx29_sgbuf *sgbuf = zup->curr_sg;
+
+#endif
+
+	size_t pending;
+
+	dma_peripheral_id rx_id = uart_get_rx_dma_peripheral_id(zup);
+		zx29_dma_stop(rx_id);
+
+		dev = zup->dmarx.chan->device->dev;
+		zup->dmacr &= ~UART_RXDMAE;
+		UART_PUT_DMACR(&zup->port,zup->dmacr);
+
+#if 1
+		raw_spin_lock_irqsave(&zup->port.lock, flags);
+
+
+
+		zup->sg2tty = sgbuf;
+
+		 
+
+		zup->sg2tty_len =  zup->sg2tty->sg.length - zx29_dma_get_transfer_num(rx_id);
+
+		dmarx->use_buf_b = ! dmarx->use_buf_b;
+
+			wmb();
+
+		//BUG_ON(pending > ZX29_DMA_BUFFER_SIZE);
+		/* Then we terminate the transfer - we now know our residue */
+		//dmaengine_terminate_all(rxchan);
+
+		zup->dmarx.running = false;
+		zup->dmarx.used = false;
+	
+		if (zx29_dma_rx_trigger_dma(zup)) {
+			printk("rx_dma_chars RXDMA start fail\n");
+			zup->imr |= UART_RXIM;
+			UART_PUT_IMSC(&zup->port,zup->imr);
+		}else{
+			zup->pre_pending = 0;
+			zup->dmarx.used = true;
+			zup->work_state = true;
+		}
+		raw_spin_unlock_irqrestore(&zup->port.lock, flags);
+
+#endif
+#if 1	
+	up(&zup->sema);
+#else
+	struct zx29_dmarx_data *dmarx = &zup->dmarx;
+	struct dma_chan *rxchan = dmarx->chan;
+	bool lastbuf = dmarx->use_buf_b;
+	struct zx29_sgbuf *sgbuf = dmarx->use_buf_b ?
+		&dmarx->sgbuf_b : &dmarx->sgbuf_a;
+	size_t pending;
+	struct dma_tx_state state;
+	int ret;
+	unsigned long flags;
+	/*
+	 * This completion interrupt occurs typically when the
+	 * RX buffer is totally stuffed but no timeout has yet
+	 * occurred. When that happens, we just want the RX
+	 * routine to flush out the secondary DMA buffer while
+	 * we immediately trigger the next DMA job.
+	 */
+	raw_spin_lock_irqsave(&zup->port.lock, flags);
+	/*
+	 * Rx data can be taken by the UART interrupts during
+	 * the DMA irq handler. So we check the residue here.
+	 */
+	rxchan->device->device_tx_status(rxchan, dmarx->cookie, &state);
+	
+
+	pending = sgbuf->sg.length - state.residue;
+	BUG_ON(pending > ZX29_DMA_BUFFER_SIZE);
+	/* Then we terminate the transfer - we now know our residue */
+	//dmaengine_terminate_all(rxchan);
+
+	zup->dmarx.running = false;
+	zup->dmarx.used = false;
+	dmarx->use_buf_b = !lastbuf;
+	//ret = zx29_dma_rx_trigger_dma(zup);
+
+	zx29_uart_dma_rx_chars(zup, pending, lastbuf, false, &flags);
+	zup->work_state = false;
+	zup->pre_pending = 0;	 
+	zup->imr |= UART_RXIM;
+	UART_PUT_IMSC(&zup->port, zup->imr);	
+	
+	raw_spin_unlock_irqrestore(&zup->port.lock, flags);
+#endif	
+	/*
+	 * Do this check after we picked the DMA chars so we don't
+	 * get some IRQ immediately from RX.
+	 */
+	 /*
+	if (ret) {
+		dev_dbg(zup->port.dev, "could not retrigger RX DMA job "
+			"fall back to interrupt mode\n");
+		zup->imr |= UART_RXIM;
+		UART_PUT_IMSC(&zup->port, zup->imr);
+		//writew(uap->im, uap->port.membase + UART011_IMSC);
+	}
+	*/
+}
+
+static inline void zx29_dma_rx_stop(struct zx29_uart_port *zup)
+{
+	dma_peripheral_id rx_id = uart_get_rx_dma_peripheral_id(zup);
+	/* FIXME.  Just disable the DMA enable */
+	zup->dmacr &= ~UART_RXDMAE;
+	UART_PUT_DMACR(&zup->port,zup->dmacr);
+	zx29_dma_stop(rx_id);
+	 zup->curr_sg = NULL;
+}
+
+static void zx29_dma_remove(struct zx29_uart_port *zup)
+{
+	/* TODO: remove the initcall if it has not yet executed */
+	if (zup->dmatx.chan)
+		dma_release_channel(zup->dmatx.chan);
+	if (zup->dmarx.chan)
+		dma_release_channel(zup->dmarx.chan);
+}
+
+
+static void zx29_dma_shutdown(struct zx29_uart_port *zup)
+{
+	unsigned long flags;
+	dma_peripheral_id rx_id = uart_get_rx_dma_peripheral_id(zup);
+	
+	if (!(zup->using_tx_dma || zup->using_rx_dma))
+		return;
+	/* Disable RX and TX DMA */
+	while(UART_GET_FR(&zup->port) & (UART_FR_TXBUSY | UART_FR_TXBUSY))
+		barrier();
+
+	raw_spin_lock_irqsave(&zup->port.lock, flags);
+	zup->dmacr &= ~(UART_DMAONERR | UART_RXDMAE | UART_TXDMAE);
+	UART_PUT_DMACR(&zup->port,zup->dmacr);
+	zx29_dma_stop(rx_id);
+	 zup->curr_sg = NULL;
+	raw_spin_unlock_irqrestore(&zup->port.lock, flags);
+	if (zup->using_tx_dma) {
+		/* In theory, this should already be done by zx29_dma_flush_buffer */
+		//dmaengine_terminate_all(zup->dmatx.chan);
+		if (zup->dmatx.queued) {
+			dma_unmap_sg(zup->dmatx.chan->device->dev, &zup->dmatx.sg, 1,
+				     DMA_TO_DEVICE);
+			zup->dmatx.queued = false;
+		}
+
+		kfree(zup->dmatx.buf);
+		zup->using_tx_dma = false;
+	}
+	if (zup->using_rx_dma) {
+		//dmaengine_terminate_all(zup->dmarx.chan);
+		/* Clean up the RX DMA */
+		zx29_sgbuf_free(zup->dmarx.chan, &zup->dmarx.sgbuf_a, DMA_FROM_DEVICE);
+		zx29_sgbuf_free(zup->dmarx.chan, &zup->dmarx.sgbuf_b, DMA_FROM_DEVICE);
+		zup->using_rx_dma = false;
+		zup->dmarx.used = false;
+		zup->dmarx.running = false;
+		zup->dmarx.use_buf_b = false;
+		zup->dmarx.rx_index = 0;
+	}
+	zup->pre_pending = 0;
+	zup->work_state = false;	
+
+}
+
+static void zx29_shutdown_channel(struct zx29_uart_port *zup,
+					unsigned int lcrh)
+{
+      unsigned long val;
+
+	  val = UART_GET_LCRH(&zup->port);
+	  val &= ~(UART_LCRH_BRK | UART_LCRH_FEN);
+	  UART_PUT_LCRH(&zup->port, val);
+}
+
+
+static inline bool zx29_dma_rx_available(struct zx29_uart_port *zup)
+{
+	return zup->using_rx_dma;
+}
+
+static inline bool zx29_dma_rx_running(struct zx29_uart_port *zup)
+{
+	return zup->using_rx_dma && zup->dmarx.running;
+}
+
+static inline bool zx29_dma_rx_used(struct zx29_uart_port *zup)
+{
+	return zup->using_rx_dma && zup->dmarx.used;
+}
+
+static inline bool zx29_dma_rx_work_scheduled(struct zx29_uart_port *zup)
+{
+	return zup->using_rx_dma && zup->work_state;
+}
+
+
+void uart_dma_tx_callback(void *data)
+{
+	struct zx29_uart_port *zup = data;
+	struct zx29_dmatx_data *dmatx = &zup->dmatx;
+	
+	unsigned long flags;
+	u16 dmacr;
+	raw_spin_lock_irqsave(&zup->port.lock, flags);
+	if (zup->dmatx.queued)
+		dma_unmap_sg(dmatx->chan->device->dev, &dmatx->sg, 1,
+			     DMA_TO_DEVICE);
+
+	dmacr = zup->dmacr;
+	zup->dmacr = dmacr & ~UART_TXDMAE;
+	UART_PUT_DMACR(&zup->port,zup->dmacr);
+
+	/*
+	 * If TX DMA was disabled, it means that we've stopped the DMA for
+	 * some reason (eg, XOFF received, or we want to send an X-char.)
+	 *
+	 * Note: we need to be careful here of a potential race between DMA
+	 * and the rest of the driver - if the driver disables TX DMA while
+	 * a TX buffer completing, we must update the tx queued status to
+	 * get further refills (hence we check dmacr).
+	 */
+	if (!(dmacr & UART_TXDMAE) || uart_tx_stopped(&zup->port) ||
+	    uart_circ_empty(&zup->port.state->xmit)) {
+		zup->dmatx.queued = false;
+		
+
+		zx_cpuidle_set_free(IDLE_FLAG_UART);
+
+		
+		raw_spin_unlock_irqrestore(&zup->port.lock, flags);
+		return;
+	}
+
+	if (zx29_uart_dma_tx_chars(zup) <= 0) {
+		/*
+		 * We didn't queue a DMA buffer for some reason, but we
+		 * have data pending to be sent.  Re-enable the TX IRQ.
+		 */
+		zup->imr |= UART_TXIM;
+		UART_PUT_IMSC(&zup->port, zup->imr);
+	}
+	raw_spin_unlock_irqrestore(&zup->port.lock, flags);
+}
+
+static int zx29_uart_dma_tx_chars(struct zx29_uart_port *zup)
+{
+	struct zx29_dmatx_data *dmatx = &zup->dmatx;
+	struct dma_chan *chan = dmatx->chan;
+	struct dma_device *dma_dev = chan->device;
+	struct dma_async_tx_descriptor *desc;
+	struct circ_buf *xmit = &zup->port.state->xmit;
+	unsigned int count;
+
+	/*
+	 * Try to avoid the overhead involved in using DMA if the
+	 * transaction fits in the first half of the FIFO, by using
+	 * the standard interrupt handling.  This ensures that we
+	 * issue a uart_write_wakeup() at the appropriate time.
+	 */
+ 
+	count = uart_circ_chars_pending(xmit);
+	if (count < (16 >> 1)) {
+		zup->dmatx.queued = false;
+		return 0;
+	}
+	
+	/*
+	if (count < (zup->port->fifosize >> 1)) {
+		zup->dmatx.queued = false;
+		return 0;
+	}
+*/
+	/*
+	 * Bodge: don't send the last character by DMA, as this
+	 * will prevent XON from notifying us to restart DMA.
+	 */
+	//count -= 1;
+
+	/* Else proceed to copy the TX chars to the DMA buffer and fire DMA */
+/*	
+	if (count > ZX29_DMA_BUFFER_SIZE)
+		count = ZX29_DMA_BUFFER_SIZE;
+*/
+	if (xmit->tail < xmit->head)
+		memcpy(&dmatx->buf[0], &xmit->buf[xmit->tail], count);
+	else {
+		size_t first = UART_XMIT_SIZE - xmit->tail;
+		size_t second = xmit->head;
+
+		memcpy(&dmatx->buf[0], &xmit->buf[xmit->tail], first);
+		if (second)
+			memcpy(&dmatx->buf[first], &xmit->buf[0], second);
+	}
+	dmatx->sg.length = count;
+
+	if (dma_map_sg(dma_dev->dev, &dmatx->sg, 1, DMA_TO_DEVICE) != 1) {
+		zup->dmatx.queued = false;
+		dev_dbg(zup->port.dev, "unable to map TX DMA\n");
+		return -EBUSY;
+	}
+
+	
+	zup->dmatx.tx_def.link_addr=0;
+	zup->dmatx.tx_def.src_addr=(unsigned int)(dmatx->sg.dma_address);
+	zup->dmatx.tx_def.count=count;
+	dmaengine_slave_config(zup->dmatx.chan, (struct dma_slave_config*)&zup->dmatx.tx_def);
+	desc = zup->dmatx.chan->device->device_prep_interleaved_dma(zup->dmatx.chan,NULL,0);
+	if (!desc) {		
+	printk(KERN_INFO "!!!!!ERROR TX DESC[%s][%d]\n\n\n\n\n\n\n\n\n",__func__,__LINE__);
+	dma_unmap_sg(dma_dev->dev, &dmatx->sg, 1, DMA_TO_DEVICE);
+	zup->dmatx.queued = false;
+	/*
+	 * If DMA cannot be used right now, we complete this
+	 * transaction via IRQ and let the TTY layer retry.
+	 */
+	dev_dbg(zup->port.dev, "TX DMA busy\n");
+	return -EBUSY;
+	}	
+	desc->callback = (dma_async_tx_callback)uart_dma_tx_callback;
+	desc->callback_param = (void *)zup;
+	dmaengine_submit(desc);
+	dma_async_issue_pending(zup->dmatx.chan);	
+	atomic_inc(&zup->dmatx.count);
+	zup->dmacr |= UART_TXDMAE;
+	UART_PUT_DMACR(&zup->port,zup->dmacr);
+	zup->dmatx.queued = true;
+
+	/*
+	 * Now we know that DMA will fire, so advance the ring buffer
+	 * with the stuff we just dispatched.
+	 */
+	xmit->tail = (xmit->tail + count) & (UART_XMIT_SIZE - 1);
+	zup->port.icount.tx += count;
+
+	if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
+		//uart_write_wakeup(&zup->port);
+		tasklet_schedule(&zup->write_wakeup);
+
+	return 1;
+}
+
+static void zx29_uart_dma_rx_chars(struct zx29_uart_port *zup,
+											//u32 pending, bool use_buf_b, 
+											u32 pending, struct zx29_sgbuf *sgbuf,
+											bool readfifo, unsigned long *flags)
+{
+	struct tty_struct *tty = zup->port.state->port.tty;
+#if 0	
+	struct zx29_sgbuf *sgbuf = use_buf_b ?
+		&zup->dmarx.sgbuf_b : &zup->dmarx.sgbuf_a;
+#endif
+	struct device *dev = zup->dmarx.chan->device->dev;
+	int dma_count = 0;
+	u32 fifotaken = 0; /* only used for vdbg() */
+	//unsigned long flags;
+
+	/* Pick everything from the DMA first */
+	if (pending) {
+		/* Sync in buffer */
+		
+		dma_sync_sg_for_cpu(dev, &sgbuf->sg, 1, DMA_FROM_DEVICE);
+
+		/*
+		 * First take all chars in the DMA pipe, then look in the FIFO.
+		 * Note that tty_insert_flip_buf() tries to take as many chars
+		 * as it can.
+		 */
+	
+	raw_spin_unlock_irqrestore(&zup->port.lock, *flags);
+	
+		dma_count = tty_insert_flip_string(zup->port.state->port.tty,
+						   sgbuf->buf, pending);
+	
+	raw_spin_lock_irqsave(&zup->port.lock, *flags);	 	
+		/* Return buffer to device */
+		dma_sync_sg_for_device(dev, &sgbuf->sg, 1, DMA_FROM_DEVICE);
+
+		zup->port.icount.rx += dma_count;
+
+			//if(zup->port.line == 0)
+		    //  printk("yanming uart_dma_rx_chars, dma2tty:%d\n", dma_count);			
+		if (dma_count < pending)
+			dev_info(zup->port.dev,
+				 "couldn't insert all characters (TTY is full?)\n");
+	}
+
+	/*
+	 * Only continue with trying to read the FIFO if all DMA chars have
+	 * been taken first.
+	 */
+	//if (dma_count == pending && readfifo) {
+	//if ((pending > 0) && readfifo) {	
+	if (readfifo) {		
+		/* Clear any error flags */
+		//UART_PUT_ICR(&zup->port,UART_OEIC | UART_BEIC | UART_PEIC | UART_FEIC);
+		/*
+		 * If we read all the DMA'd characters, and we had an
+		 * incomplete buffer, that could be due to an rx error, or
+		 * maybe we just timed out. Read any pending chars and check
+		 * the error status.
+		 *
+		 * Error conditions will only occur in the FIFO, these will
+		 * trigger an immediate interrupt and stop the DMA job, so we
+		 * will always find the error in the FIFO, never in the DMA
+		 * buffer.
+		 */
+		fifotaken = zx29_uart_fifo_to_tty(zup);
+			//if(zup->port.line == 0)
+		     // printk("yanming uart_dma_rx_chars, fifo2tty:%d\n", fifotaken);			
+	}
+	if((pending > 0) || (fifotaken > 0))	{
+		raw_spin_unlock(&zup->port.lock);
+		tty_flip_buffer_push(tty);
+		raw_spin_lock(&zup->port.lock);
+	}
+}
+
+static void zx29_dma_rx_irq(struct zx29_uart_port *zup, unsigned long *flags)
+{
+	struct zx29_dmarx_data *dmarx = &zup->dmarx;
+	struct dma_chan *rxchan = dmarx->chan;
+	struct zx29_sgbuf *sgbuf = dmarx->use_buf_b ?
+		&dmarx->sgbuf_b : &dmarx->sgbuf_a;
+	size_t pending;
+	struct dma_tx_state state;
+	enum dma_status dmastat;
+	dma_peripheral_id rx_id = uart_get_rx_dma_peripheral_id(zup);
+
+	uint32_t ris_status = UART_GET_RIS(&zup->port);
+	//printk("rx irq\n");
+	if(ris_status & (UART_OEIS | UART_BEIS | UART_PEIS | UART_FEIS)){
+		if(ris_status & UART_OEIS){		   
+			zup->port.icount.overrun++; 
+				//if(!uart_console(&zup->port))
+				//	BUG_ON(1);
+		}
+		if(ris_status & UART_BEIS)			 
+			zup->port.icount.brk++; 
+		if(ris_status & UART_PEIS)				  
+			zup->port.icount.parity++;			   
+		if(ris_status & UART_FEIS)			  
+			zup->port.icount.frame++;
+		
+		UART_PUT_ICR(&zup->port, (UART_OEIS | UART_BEIS | UART_PEIS | UART_FEIS));		
+	}
+
+	if(zx29_dma_rx_running(zup)){
+		/*
+		 * Pause the transfer so we can trust the current counter,
+		 * do this before we pause the block, else we may
+		 * overflow the FIFO.
+		 */
+	//	if(zx29_dma_stop(rx_id))
+		//	printk( "uart%d unable to pause DMA transfer\n", zup->port.line);
+		zup->dmacr &= ~UART_RXDMAE;
+		UART_PUT_DMACR(&zup->port,zup->dmacr);
+		zx29_dma_stop(rx_id);
+
+		//dmastat = zx29_dma_get_status();//Normally,this value is insignificance.
+
+		/* Disable RX DMA - incoming data will wait in the FIFO */
+		zup->dmarx.running = false;
+		zup->dmarx.used = false;
+		pending = sgbuf->sg.length - zx29_dma_get_transfer_num(rx_id);//state.residue;
+		BUG_ON(pending > ZX29_DMA_BUFFER_SIZE);
+		/* Then we terminate the transfer - we now know our residue */
+		//dmaengine_terminate_all(rxchan);
+	
+	dmarx->use_buf_b = !dmarx->use_buf_b;
+		/*
+		 * This will take the chars we have so far and insert
+		 * into the framework.
+		 */
+			zx29_uart_dma_rx_chars(zup, pending, sgbuf, false, flags);
+	}
+
+	/* Switch buffer & re-trigger DMA job */	
+	if (zx29_dma_rx_trigger_dma(zup)) {
+		printk("uart%d could not retrigger RX DMA job\n",zup->port.line);
+		zup->imr |= UART_RXIM;
+		UART_PUT_IMSC(&zup->port, zup->imr);
+	}
+#if RX_DMA_WORK		
+	//printk("add timer\n");
+	else{
+	//	mod_timer(&(zup->rx_dma_timer), jiffies + RX_DMA_TIMEOUT);
+		uart_mod_timer(zup, flags);
+		zup->pre_pending = 0;
+		zup->work_state = true;
+	}
+#endif	
+
+}
+/****************************************************************************/
+static void zx29_uart_rx_dma_chars(struct zx29_uart_port *zup, unsigned long *flags)
+{	
+
+	struct tty_struct *tty = zup->port.state->port.tty;
+	//zx29_uart_fifo_to_tty(zup);
+//	raw_spin_unlock(&zup->port.lock);
+		if (zx29_dma_rx_available(zup)) {
+			if (zx29_dma_rx_trigger_dma(zup)) {
+				printk("rx_dma_chars RXDMA start fail\n");
+				zup->imr |= UART_RXIM;
+				UART_PUT_IMSC(&zup->port,zup->imr);
+			}
+#if RX_DMA_WORK		
+//printk("add timer\n");
+			else{
+				//mod_timer(&(zup->rx_dma_timer), jiffies + RX_DMA_TIMEOUT);
+				uart_mod_timer(zup, flags);
+				zup->pre_pending = 0;
+				zup->work_state = true;
+			}
+#endif			
+		}
+		
+	//tty_flip_buffer_push(tty);
+	//raw_spin_lock(&zup->port.lock);
+}
+
+
+/****************************************************************************/
+static void zx29_uart_rx_timeout_chars(struct zx29_uart_port *zup, unsigned long *flags)
+{
+	int rt_cnt = 0;
+//	unsigned long flags;
+	
+	struct tty_struct *tty = zup->port.state->port.tty;
+	
+	int fr = UART_GET_FR(&zup->port);
+	//printk("rx_timeout_chars\n");
+#if 0	
+	if((fr & UART_FR_RXBUSY) || ((fr & UART_FR_RXFE)  == 0)){
+		zx29_uart_rx_dma_chars(zup, flags);
+		zup->dmarx.used = true;		
+		return;
+	}
+#endif	
+	rt_cnt = zx29_uart_fifo_to_tty(zup);
+#if 0
+	//if(rt_cnt >= zup->port.fifosize){
+		zx29_uart_rx_dma_chars(zup, flags);						
+		zup->dmarx.used = true;		
+	//}
+#endif	
+	if(rt_cnt){
+	if(g_console_open_flag == 1 || zup->port.line != DEBUG_CONSOLE){
+		raw_spin_unlock(&zup->port.lock);
+		tty_flip_buffer_push(tty);
+		raw_spin_lock(&zup->port.lock);
+    }	
+	}
+}
+
+static void zx29_uart_rt_dma(struct zx29_uart_port *zup, unsigned long *flags)
+{
+	struct zx29_dmarx_data *dmarx = &zup->dmarx;
+	struct dma_chan *rxchan = dmarx->chan;
+
+#if 0	
+
+	struct zx29_sgbuf *sgbuf = dmarx->use_buf_b ?
+		&dmarx->sgbuf_b : &dmarx->sgbuf_a;
+
+#else
+
+	struct zx29_sgbuf *sgbuf = zup->curr_sg;
+
+#endif
+
+	size_t pending;
+	struct dma_tx_state state;
+	enum dma_status dmastat;
+	dma_peripheral_id rx_id = uart_get_rx_dma_peripheral_id(zup);
+	uint32_t ris_status = UART_GET_RIS(&zup->port);
+	pending = sgbuf->sg.length - zx29_dma_get_transfer_num(rx_id);
+	if(ris_status & (UART_OEIS | UART_BEIS | UART_PEIS | UART_FEIS)){
+		if(ris_status & UART_OEIS){ 			   
+			zup->port.icount.overrun++;
+			//	if(!uart_console(&zup->port))
+			//BUG_ON(1);
+		}
+		if(ris_status & UART_BEIS) 			 
+			zup->port.icount.brk++; 
+		if(ris_status & UART_PEIS)				  
+			zup->port.icount.parity++;			   
+		if(ris_status & UART_FEIS)			  
+			zup->port.icount.frame++;	
+		
+		UART_PUT_ICR(&zup->port, (UART_OEIS | UART_BEIS | UART_PEIS | UART_FEIS));		
+	}
+
+	if(zx29_dma_rx_running(zup)){
+		/*
+		 * Pause the transfer so we can trust the current counter,
+		 * do this before we pause the block, else we may
+		 * overflow the FIFO.
+		 */
+		 
+		zup->dmacr &= ~UART_RXDMAE;
+		UART_PUT_DMACR(&zup->port,zup->dmacr);
+		zx29_dma_stop(rx_id);
+			//printk( "uart%d unable to pause DMA transfer\n", zup->port.line);
+		//dmastat = rxchan->device->device_tx_status(rxchan,
+		//					   dmarx->cookie, &state);
+	//	dmastat = zx29_dma_get_status();//Normally,this value is insignificance.
+
+
+		/* Disable RX DMA - incoming data will wait in the FIFO */
+		zup->dmarx.running = false;
+		zup->dmarx.used = false;
+		zup->curr_sg = zup->sg2tty = NULL;
+		zup->sg2tty_len = 0;
+		zup->imr |= (UART_RTIM|UART_RXIM);		
+		UART_PUT_IMSC(&zup->port, zup->imr);		
+		pending = sgbuf->sg.length - zx29_dma_get_transfer_num(rx_id);//state.residue;
+		BUG_ON(pending > ZX29_DMA_BUFFER_SIZE);
+		/* Then we terminate the transfer - we now know our residue */
+		//dmaengine_terminate_all(rxchan);
+
+	dmarx->use_buf_b = !dmarx->use_buf_b;	
+		wmb();
+		/*
+		* This will take the chars we have so far and insert
+		* into the framework.
+		*/
+		zx29_uart_dma_rx_chars(zup, pending, sgbuf, true, flags);
+	}
+
+#if 0
+//printk("rt dma\n");
+	/* Switch buffer & re-trigger DMA job */
+	if (zx29_dma_rx_trigger_dma(zup)) {
+		printk("zx29_dma_rx_trigger_dma fail,uart:%d\n", zup->port.line);
+		zup->imr |= UART_RXIM;
+		UART_PUT_IMSC(&zup->port, zup->imr);
+	}
+#if RX_DMA_WORK		
+	//printk("add timer\n");
+	else{
+		//mod_timer(&(zup->rx_dma_timer), jiffies + RX_DMA_TIMEOUT);
+		uart_mod_timer(zup, flags);
+		zup->pre_pending = 0;
+		zup->work_state = true;
+		zup->dmarx.used = true;
+	}
+#endif	
+
+#endif	
+
+}
+static void zx29_uart_rx_dma_timeout(struct zx29_uart_port *zup)
+{
+	struct zx29_dmarx_data *dmarx = &zup->dmarx;
+	static bool dma_timeout_flag = false;
+	size_t pending, tmp_len;
+	uint32_t ris_status = 0;
+	int cancel_timer = 0;
+	int sg_idx = (dmarx->use_buf_b ? 1 : 0);
+
+	unsigned long flags;
+	struct zx29_sgbuf *sgbuf = NULL;	
+	int read_fifo = 0;
+
+	if(!zx29_dma_rx_running(zup))
+		return;
+	raw_spin_lock_irqsave(&zup->port.lock, flags);	
+	if(zup->port_close || (zup->curr_sg == NULL)){
+		raw_spin_unlock_irqrestore(&zup->port.lock, flags);
+		return;
+	}
+
+	if(zup->sg2tty)	{//dma complete now, later check again
+		raw_spin_unlock_irqrestore(&zup->port.lock, flags);
+		//zup->pre_pending = pending;
+		//test_uart_static(NULL, 0, 14, zup->port.line);
+		mod_timer(&(zup->rx_dma_timer), jiffies + RX_DMA_TIMEOUT);	
+		return;
+	}		
+	sgbuf = zup->curr_sg;	
+	read_fifo = UART_GET_FR(&zup->port);
+	dma_peripheral_id rx_id = uart_get_rx_dma_peripheral_id(zup);
+	pending = sgbuf->sg.length - zx29_dma_get_transfer_num(rx_id);
+	if(pending == zup->pre_pending){	
+
+	int fr = UART_GET_FR(&zup->port);	
+	//if RXBUSY,means data come again	
+
+	if((fr  & UART_FR_RXBUSY)){
+
+		//printk("RXBUSY, data come again\n");
+
+		uart_mod_timer(zup, &flags);	
+
+		goto deal_end;
+
+	}	
+	zup->imr &= ~UART_RXIM;
+	UART_PUT_IMSC(&zup->port, zup->imr);
+
+	ris_status = UART_GET_RIS(&zup->port);
+	
+	if(ris_status & (UART_OEIS | UART_BEIS | UART_PEIS | UART_FEIS)){
+		if(ris_status & UART_OEIS){ 			   
+			zup->port.icount.overrun++;
+				//if(!uart_console(&zup->port))
+			//BUG_ON(1);
+		}
+		if(ris_status & UART_BEIS) 			 
+			zup->port.icount.brk++; 
+		if(ris_status & UART_PEIS)				  
+			zup->port.icount.parity++;			   
+		if(ris_status & UART_FEIS) 			  
+			zup->port.icount.frame++;
+		
+		UART_PUT_ICR(&zup->port, (UART_OEIS | UART_BEIS | UART_PEIS | UART_FEIS));
+	}		
+
+		//if(zx29_dma_stop(rx_id))
+		//	printk("unable to pause DMA transfer\n");
+		/* Disable RX DMA - incoming data will wait in the FIFO */
+		zup->dmacr &= ~UART_RXDMAE;
+		UART_PUT_DMACR(&zup->port,zup->dmacr);
+		zx29_dma_stop(rx_id);
+		zup->dmarx.running = false;
+		zup->dmarx.used = false;
+
+	if(zup->app_ctrl){
+		//int tmp_len = sgbuf->sg.length - zx29_dma_get_transfer_num(rx_id);
+		//zup->sg2tty = sgbuf;
+		tmp_len = sgbuf->sg.length - zx29_dma_get_transfer_num(rx_id);		
+		//if(zup->port.line == UART0){
+			if(tmp_len != pending){
+				//printk("\npending:%d, new len:%d, now update pending time:%llu\n", pending, zup->sg2tty_len, __UART_TIME_);
+				pending = tmp_len;
+			}else{
+				#if 0
+				//if(zup->sleep_state || !xp2xp_Ap2CpIsApWakeup()){
+				if(zup->sleep_state){
+					//now mcu is sleep, we can release dma
+					//cancel_timer = 1;
+				}
+				#endif
+			}
+		//}
+		
+			dmarx->use_buf_b = !dmarx->use_buf_b;
+			wmb();
+		//now don't start dma again
+		//if(cancel_timer == 0){
+			if (zx29_dma_rx_trigger_dma(zup)) {
+				printk("rx_dma_chars RXDMA start fail\n");
+				zup->imr |= UART_RXIM;
+				UART_PUT_IMSC(&zup->port,zup->imr);
+			}else{
+				uart_mod_timer(zup, &flags);
+				zup->pre_pending = 0;
+				zup->dmarx.used = true;
+				zup->work_state = true;
+			}		
+		//}	
+		
+			if(pending){
+				//test_uart_static(NULL, 0, 13, zup->port.line);
+				zx29_uart_dma_rx_chars(zup, zup->sg2tty_len, zup->sg2tty, !(read_fifo & UART_FR_RXFE), &flags);
+			}
+		}else{			
+			zup->imr |= (UART_RTIM|UART_RXIM);		
+			UART_PUT_IMSC(&zup->port, zup->imr);
+			pending = sgbuf->sg.length - zx29_dma_get_transfer_num(rx_id);
+			dmarx->use_buf_b = !dmarx->use_buf_b;
+			wmb();		
+			if(pending){
+				zx29_uart_dma_rx_chars(zup, pending, sgbuf, !(read_fifo & UART_FR_RXFE), &flags);
+			}
+			zup->pre_pending = 0;
+			zup->work_state = false;
+			if((UART_GET_RIS(&zup->port) & (UART_RXIS | UART_RTIS))  || 
+				(UART_GET_FR(&zup->port) & UART_FR_RXBUSY)){
+				zup->imr &= ~(UART_RXIM);		
+				UART_PUT_IMSC(&zup->port, zup->imr);
+			//	zx29_dma_rx_irq(zup, &flags);	
+			
+				if (zx29_dma_rx_trigger_dma(zup)) {
+					printk("uart%d could not retrigger RX DMA job\n",zup->port.line);
+					zup->imr |= UART_RXIM;
+					UART_PUT_IMSC(&zup->port, zup->imr);
+	#if RX_DMA_WORK		
+				}
+				else{
+				//	mod_timer(&(zup->rx_dma_timer), jiffies + RX_DMA_TIMEOUT);
+					uart_mod_timer(zup, &flags);
+					zup->pre_pending = 0;
+					zup->work_state = true;
+					zup->dmarx.used = true;
+					UART_PUT_ICR(&zup->port,(UART_RTIS|UART_RXIS));
+	#endif					
+				}
+
+			}
+		}
+		
+deal_end:		
+		
+	raw_spin_unlock_irqrestore(&zup->port.lock, flags);
+	}else{
+		raw_spin_unlock_irqrestore(&zup->port.lock, flags);
+		zup->pre_pending = pending;
+		mod_timer(&(zup->rx_dma_timer), jiffies + RX_DMA_TIMEOUT);
+		//uart_mod_timer(zup, &flags);
+	}
+			
+
+}
+#endif
+
+
+static void zx29_uart_modem_status(struct zx29_uart_port *zup)
+{
+	unsigned int status, delta;
+
+	status = UART_GET_FR(&zup->port)& UART_FR_MODEM_ANY;
+
+	delta = status ^ zup->old_status;
+	zup->old_status = status;
+
+	if (!delta)
+		return; 
+
+	if (delta & UART_FR_DCD)
+		uart_handle_dcd_change(&zup->port, status & UART_FR_DCD);
+
+	if (delta & UART_FR_DSR)
+		zup->port.icount.dsr++;
+
+	if (delta & UART_FR_CTS)
+		uart_handle_cts_change(&zup->port, status & UART_FR_CTS);
+
+	wake_up_interruptible(&zup->port.state->port.delta_msr_wait);
+}
+
+/****************************************************************************/
+static irqreturn_t zx29_uart_interrupt(int irq, void *dev_id)
+{
+	struct uart_port *port = dev_id;
+	struct zx29_uart_port *zup = container_of(port, struct zx29_uart_port, port);
+	unsigned long flags;
+	unsigned int status, pass_counter = 256;
+	int handled = 0;
+
+	raw_spin_lock_irqsave(&zup->port.lock, flags);
+	status = UART_GET_MIS(port) & zup->imr;
+	if (status) {
+		do {
+			UART_PUT_ICR(port,(status & ~(UART_TXIS|UART_RTIS|UART_RXIS)));
+			if(uart_console(&zup->port)){
+				if (status & (UART_RTIS|UART_RXIS))
+					zx29_uart_rx_chars(zup);
+			}else{
+#ifdef 	CONFIG_CPU_IDLE			
+			    zup->rxd_int_depth = 0;
+#endif
+				if (status & (UART_RXIS)){
+#if CONFIG_SERIAL_ZX29_DMA
+					if (zx29_dma_rx_used(zup)){
+						UART_PUT_ICR(port,UART_RXIS);
+						if(!(zup->imr & UART_RTIM)){
+							zup->imr |= UART_RTIM;
+							UART_PUT_IMSC(port,zup->imr);
+						}
+						uart_mod_timer(zup, &flags);
+
+					}else{
+						zx29_uart_rx_dma_chars(zup, &flags);
+						zup->dmarx.used = true;
+						//when RX&RT comes both, we trigger dma and add timer,so clear RT,waiting the timer
+						if(status & (UART_RTIS))
+							status &= ~UART_RTIS;
+					}
+#else
+					zx29_uart_rx_chars(zup);	
+#endif
+				}	
+				if (status & (UART_RTIS)){
+#if CONFIG_SERIAL_ZX29_DMA
+					if(!zx29_dma_rx_running(zup)){
+						zx29_uart_rx_timeout_chars(zup, &flags);
+					}else{
+						UART_PUT_ICR(port, UART_RTIS);	
+						zx29_uart_rt_dma(zup, &flags);
+					}
+#else
+					zx29_uart_rx_chars(zup);
+#endif
+				}
+			}
+
+			if (status & (UART_DSRMIS|UART_DCDMIS|UART_CTSMIS|UART_RIMIS))
+				zx29_uart_modem_status(zup);
+			
+			if (status & UART_TXIS)
+				zx29_uart_tx_chars(zup);
+			
+			if (pass_counter-- == 0)
+				break;
+
+			status = UART_GET_MIS(port);
+		} while (status != 0);
+
+		handled = IRQ_HANDLED;
+	}
+	raw_spin_unlock_irqrestore(&zup->port.lock, flags);
+	return IRQ_RETVAL(handled);
+}
+
+
+
+#if CONFIG_SERIAL_ZX29_DMA
+static void uart_dma_init(struct zx29_uart_port *zup)
+{
+	int i=0;
+	struct dma_chan *chan = NULL;
+	
+	atomic_set(&zup->dmarx.count, 1);
+	atomic_set(&zup->dmatx.count, 1);
+	
+	if(zup->port.line == UART0)
+	{
+	zup->dmatx.tx_def.dest_addr = (unsigned int)(ZX29_UART0_PHYS+zx29_UART_DR);
+	}
+	else if(zup->port.line == UART1)
+	{
+	zup->dmatx.tx_def.dest_addr = (unsigned int)(ZX29_UART1_PHYS+zx29_UART_DR);
+	}
+	else if(zup->port.line == UART2)
+	{
+	zup->dmatx.tx_def.dest_addr = (unsigned int)(ZX29_UART2_PHYS+zx29_UART_DR);
+	}
+
+	zup->dmatx.tx_def.dma_control.tran_mode = TRAN_MEM_TO_PERI;
+	zup->dmatx.tx_def.dma_control.irq_mode =  DMA_ALL_IRQ_ENABLE;
+	zup->dmatx.tx_def.dma_control.src_burst_size = DMA_BURST_SIZE_8BIT;
+	zup->dmatx.tx_def.dma_control.src_burst_len = DMA_BURST_LEN_4;
+	zup->dmatx.tx_def.dma_control.dest_burst_size = DMA_BURST_SIZE_8BIT;
+	zup->dmatx.tx_def.dma_control.dest_burst_len = DMA_BURST_LEN_4;
+
+	dma_cap_mask_t mask;	
+	dma_cap_zero(mask);
+	dma_cap_set(DMA_SLAVE, mask);
+
+	if(zup->port.line == UART0)
+	{
+	chan = dma_request_channel(mask, zx29_dma_filter_fn, (void*)DMA_CH_UART0_TX);
+	}
+	else if(zup->port.line == UART1)
+	{
+	chan = dma_request_channel(mask, zx29_dma_filter_fn, (void*)DMA_CH_UART1_TX);
+	}
+	else if(zup->port.line == UART2)
+	{
+	chan = dma_request_channel(mask, zx29_dma_filter_fn, (void*)DMA_CH_UART2_TX);
+	}
+	zup->dmatx.chan = chan;
+
+
+
+for(i=0;i<UART_DMA_RX_MAX_COUNT;i++)
+{
+	if(zup->port.line == UART0)
+	{
+	zup->dmarx.rx_def[i].src_addr = ZX29_UART0_PHYS+zx29_UART_DR;
+	}
+	else if(zup->port.line == UART1)
+	{
+	zup->dmarx.rx_def[i].src_addr = ZX29_UART1_PHYS+zx29_UART_DR;
+	}
+	else if(zup->port.line == UART2)
+	{
+	zup->dmarx.rx_def[i].src_addr = ZX29_UART2_PHYS+zx29_UART_DR;
+	}
+
+	zup->dmarx.rx_def[i].dma_control.tran_mode = TRAN_PERI_TO_MEM;
+	zup->dmarx.rx_def[i].dma_control.irq_mode =  DMA_ALL_IRQ_ENABLE;
+	zup->dmarx.rx_def[i].dma_control.src_burst_size = DMA_BURST_SIZE_8BIT;
+	zup->dmarx.rx_def[i].dma_control.src_burst_len = DMA_BURST_LEN_4;
+	zup->dmarx.rx_def[i].dma_control.dest_burst_size = DMA_BURST_SIZE_8BIT;
+	zup->dmarx.rx_def[i].dma_control.dest_burst_len = DMA_BURST_LEN_4;
+}
+
+	zup->dmarx.rx_index = 0;
+	chan = NULL;
+
+	if(zup->port.line == UART0)
+	{
+	chan = dma_request_channel(mask, zx29_dma_filter_fn, (void*)DMA_CH_UART0_RX);
+	}
+	else if(zup->port.line == UART1)
+	{
+	chan = dma_request_channel(mask, zx29_dma_filter_fn, (void*)DMA_CH_UART1_RX);
+	}
+	else if(zup->port.line == UART2)
+	{
+	chan = dma_request_channel(mask, zx29_dma_filter_fn, (void*)DMA_CH_UART2_RX);
+	}
+	zup->dmarx.chan = chan;
+
+}
+
+static void uart_dma_startup(struct zx29_uart_port *zup)
+{
+	int ret;
+	if (!zup->dmatx.chan)
+		{
+		printk(KERN_INFO "tx_chan is error[%s][%d]\n",__func__,__LINE__);
+		return;
+		}
+
+	zup->dmatx.buf = kmalloc(ZX29_DMA_BUFFER_SIZE, GFP_KERNEL | __GFP_DMA);
+	if (!zup->dmatx.buf) {
+		printk(KERN_INFO "tx_buf is error[%s][%d]\n",__func__,__LINE__);
+		return;
+	}
+	
+
+	sg_init_one(&zup->dmatx.sg, zup->dmatx.buf, ZX29_DMA_BUFFER_SIZE);
+
+	/* The DMA buffer is now the FIFO the TTY subsystem can use */
+	zup->port.fifosize = 16;//ZX29_DMA_BUFFER_SIZE;
+	zup->using_tx_dma = true;
+
+	if (!zup->dmarx.chan)
+		{
+		printk(KERN_INFO "rx_chan is error[%s][%d]\n",__func__,__LINE__);
+		goto skip_rx;
+		}
+
+	/* Allocate and map DMA RX buffers */
+	ret = zx29_sgbuf_init(zup->dmarx.chan, &zup->dmarx.sgbuf_a,
+				   DMA_FROM_DEVICE);
+	if (ret) {
+		printk(KERN_INFO "rx_buf_a is error[%s][%d]\n",__func__,__LINE__);
+		//dev_err(uap->port.dev, "failed to init DMA %s: %d\n",
+		//	"RX buffer A", ret);
+		goto skip_rx;
+	}
+
+	ret = zx29_sgbuf_init(zup->dmarx.chan, &zup->dmarx.sgbuf_b,
+				   DMA_FROM_DEVICE);
+	if (ret) {
+		dev_err(zup->port.dev, "failed to init DMA %s: %d\n",
+			"RX buffer B", ret);
+		zx29_sgbuf_free(zup->dmarx.chan, &zup->dmarx.sgbuf_a,
+				 DMA_FROM_DEVICE);
+		goto skip_rx;
+	}
+
+	zup->using_rx_dma = true;
+	zup->sg2tty = NULL;
+	zup->sg2tty_len = 0;	
+	zup->curr_sg = NULL;
+#if RX_DMA_WORK
+		init_timer(&(zup->rx_dma_timer));
+		zup->rx_dma_timer.data = (unsigned long)(zup);
+		zup->rx_dma_timer.function = (void *)zx29_uart_rx_dma_timeout;
+		zup->rx_dma_timer.expires = jiffies + RX_DMA_TIMEOUT;
+		zup->dmarx.running = false;
+		zup->dmarx.used = false;
+		zup->dmarx.use_buf_b = false;
+		zup->dmarx.rx_index = 0;
+		
+		zup->pre_pending = 0;
+		zup->work_state = false;
+		
+	zup->dma_compl_th = kthread_run(dma_complete_thread, zup, "uart_dma_compl");
+	BUG_ON(IS_ERR(zup->dma_compl_th));
+#endif
+
+skip_rx:
+	
+	/* Turn on DMA error (RX/TX will be enabled on demand) */
+
+	zup->dmacr &= ~UART_DMAONERR;
+	//zup->dmacr |= UART_DMAONERR;
+	UART_PUT_DMACR(&zup->port, zup->dmacr);
+
+
+	if(zup->app_ctrl){
+		if (zup->using_rx_dma) {		
+			//printk(KERN_INFO "[%s][%d]\n",__func__,__LINE__);
+			if (zx29_dma_rx_trigger_dma(zup)){
+				dev_dbg(zup->port.dev, "could not trigger initial "
+					"RX DMA job, fall back to interrupt mode\n");
+			}else{
+				mod_timer(&(zup->rx_dma_timer), jiffies + RX_DMA_TIMEOUT);
+				zup->pre_pending = 0;
+				zup->work_state = true;
+			}
+		}
+	}
+}
+
+
+#endif
+
+static irqreturn_t zx29_uart_rxd_irq(int irq, void *dev_id)
+{
+	struct zx29_uart_port *zup = (struct zx29_uart_port *)dev_id;
+#ifdef CONFIG_ARCH_ZX297520V3
+	pcu_int_clear(PCU_UART0_RXD_INT);
+#endif
+	rxd_wake_cnt++;
+#ifdef 	CONFIG_CPU_IDLE
+	disable_irq_nosync(UART0_RXD_INT);
+	zup->rxd_int_depth = 0;
+#endif	
+	zup->rxd_wakeup = true;
+	tasklet_schedule(&zup->write_wakeup);
+
+	return IRQ_HANDLED;//IRQ_RETVAL(retval);
+}
+
+/****************************************************************************/
+static int zx29_uart_startup(struct uart_port *port)
+{
+	struct zx29_uart_port *zup = container_of(port, struct zx29_uart_port, port);
+	unsigned long flags = 0;
+	unsigned long control = 0;
+	int retval = 0;
+	struct platform_device *pdev=port->private_data;
+	struct zx29_uart_platdata *pdata = pdev->dev.platform_data;
+	int i = 0,j = 0,iflag = 0;
+    	struct tty_struct  *tty = port->state->port.tty;
+    	unsigned int ibrd, fbrd,lcr_h, old_cr;
+	int ret=0;
+	
+	#ifdef CONFIG_ARCH_ZX297520V3_WATCH
+		
+		if(port->line == 0)
+		{
+			gpio_free(pdata->uart_txd.gpionum);
+			gpio_free(pdata->uart_rxd.gpionum);
+			//printk("gpio_free err err!\n");
+		}
+		
+		wmb();
+	
+	#endif
+	//when open, clear last statistic info 
+	port->icount.brk = port->icount.buf_overrun = port->icount.frame = 0;
+	port->icount.overrun = port->icount.parity = port->icount.rng = 0;
+	port->icount.rx = port->icount.tx = 0;
+	/* 
+	 *enable uart clock 
+	 *if uart is used for console, don't need do these, these was done before
+	 */
+	if (DEBUG_CONSOLE != pdev->id) {	
+	    /*  config uart apb_clk   */
+		clk_prepare_enable(zup->busclk);
+		/* enable uart work clock */
+		clk_prepare_enable(zup->wclk);
+	}
+	
+	/* Clear all pending error and receive interrupts */
+	UART_PUT_ICR(port, 0xfff);
+	
+	/*  Allocate the IRQ	 */
+	retval = request_irq(port->irq, zx29_uart_interrupt, 0, "uart-zx29", zup);
+	if (retval){
+		pr_err("[UART]unable to attach zx29 UART %d "
+		       "interrupt vector=%d\n", port->line, port->irq);
+		return retval;
+	}
+	
+	/*	set interrupt fifo level RX:1/2 Full, TX:1/2 Full */
+#if 0//CONFIG_SERIAL_ZX29_DMA
+	UART_PUT_IFLS(port, UART_IFLS_RX2_8|UART_IFLS_TX6_8);
+#else
+	UART_PUT_IFLS(port, UART_IFLS_RX2_8|UART_IFLS_TX4_8);
+#endif
+
+#if 0
+	/* Provoke TX FIFO interrupt into asserting. */
+	control = UART_CR_UARTEN | UART_CR_TXE | UART_CR_LBE;
+	UART_PUT_CR(port, control);
+	UART_PUT_FBRD(port, 0);
+	UART_PUT_IBRD(port, 1);	
+	UART_PUT_LCRH(port, 0);
+	UART_PUT_CHAR(port, 0);
+	while (UART_GET_FR(port) & UART_FR_TXBUSY)
+		barrier();
+#endif
+	control = UART_CR_UARTEN | UART_CR_RXE | UART_CR_TXE;
+
+	if (DEBUG_CONSOLE != pdev->id) {
+#if CONFIG_SERIAL_ZX29_DMA
+		//UART_PUT_DMACR(port, UART_TXDMAE | UART_RXDMAE);
+		uart_dma_startup(zup);
+#endif
+	}
+
+	tasklet_init(&zup->write_wakeup, uart_write_wakeup_task, (unsigned long) port);	
+	    /*configure gpio pin to UART*/
+	if((pdata->uart_use)/*&&(port->line == UART0 )*/)
+	{
+		retval=gpio_request(pdata->uart_rxd.gpionum,pdata->uart_rxd.gpioname);
+		if(retval)
+			BUG();		
+		retval=gpio_request(pdata->uart_txd.gpionum,pdata->uart_txd.gpioname);
+		if(retval)
+			BUG();
+  /*uart rxd*/  
+		zx29_gpio_config(pdata->uart_rxd.gpionum, pdata->uart_rxd.gpiofnc);  
+  		if(pdata->uart_rxd.gpionum == ZX29_GPIO_121 ) {      
+                //pull up gpio121				
+				*(volatile unsigned int  *)0xf843c82c |= 0xf0;
+  		}
+  /*uart txd*/ 
+		zx29_gpio_config(pdata->uart_txd.gpionum, pdata->uart_txd.gpiofnc);  
+#ifdef CONFIG_ARCH_ZX297520V3
+  		if((pdev->id != DEBUG_CONSOLE) && (pdata->uart_wakeup_enable == 1) && (zup->irq_state == 0)){
+			zup->irq = platform_get_irq_byname(pdev, "zx29_uart_rxd_wakeup");
+			printk(KERN_INFO"zx29_uart_startup,irq:%d,%s.%d\n",zup->irq,pdata->uart_cts.gpioname,zup->irq_state);
+			if(zup->irq >= 0){
+					
+				pcu_int_set_type(PCU_UART0_RXD_INT, IRQF_TRIGGER_FALLING);	
+				pcu_int_clear(PCU_UART0_RXD_INT); 
+				ret = request_irq(zup->irq, zx29_uart_rxd_irq,
+			      		IRQF_ONESHOT , "uart_rxd_irq",
+			    		 zup);
+				printk(KERN_INFO"zx29_uart_startup, retval:%d\n",ret);
+				irq_set_irq_wake(zup->irq,1);
+#ifdef CONFIG_CPU_IDLE	
+				zup->rxd_int_depth = rxd_wake_cnt = 0;	
+				zx_pm_register_callback(uart_0_pm_enter, uart_0_pm_exit);				
+				disable_irq_nosync(UART0_RXD_INT);
+#endif				
+				zup->irq_state = 1;
+			}else{
+				printk("uart_startup, request wake irq fail:%d\n",zup->irq);
+			}
+		}
+#endif
+		if(pdata->uart_ctsrtsuse)
+		{	
+			retval=gpio_request(pdata->uart_cts.gpionum,pdata->uart_cts.gpioname);
+			if(retval)
+				BUG();
+				
+			retval=gpio_request(pdata->uart_rts.gpionum,pdata->uart_rts.gpioname);
+			if(retval)
+				BUG();
+/*uart cts*/ 
+		    zx29_gpio_config(pdata->uart_cts.gpionum, pdata->uart_cts.gpiofnc);  
+/*uart rts*/ 
+			zx29_gpio_config(pdata->uart_rts.gpionum, pdata->uart_rts.gpiofnc); 
+
+			control |= (UART_CR_RTSEN |UART_CR_CTSEN );
+			control |= UART_CR_RTS;   //wl write1 for allow send
+		}
+              zup->autobaud = pdata->uart_abauduse ;	
+	}
+#if 0
+	if((pdata->uart_use)&&(port->line == UART1 ))
+	{
+		retval=gpio_request(pdata->uart_rx.gpionum,pdata->uart_rx.gpioname);
+		if(retval)
+			BUG();		
+		retval=gpio_request(pdata->uart_txd.gpionum,pdata->uart_tx.gpioname);
+		if(retval)
+			BUG();
+/*uart rxd*/  
+		zx29_gpio_config(pdata->uart_rxd.gpionum, pdata->uart_rxd.gpiofnc);  
+/*uart txd*/ 
+		zx29_gpio_config(pdata->uart_txdnum, pdata->uart_txdfnc);  
+
+		if(pdata->uart_ctsrtsuse)
+		{	
+			retval=gpio_request(pdata->uart_ctsnum,"uart1_cts");
+			if(retval)
+				BUG();		
+			retval=gpio_request(pdata->uart_rtsnum,"uart1_rts");
+			if(retval)
+				BUG();
+/*uart cts*/ 
+		    zx29_gpio_config(pdata->uart_ctsnum, pdata->uart_ctsfnc);  
+/*uart rts*/ 
+			zx29_gpio_config(pdata->uart_rtsnum, pdata->uart_rtsfnc); 
+		       
+			control |= (UART_CR_RTSEN |UART_CR_CTSEN );
+			control |= UART_CR_RTS;   //wl write1 for allow send
+		}
+              zup->autobaud = pdata->uart_abauduse;	
+	}
+	if((pdata->uart_use)&&(port->line == UART2 ))
+	{
+		retval=gpio_request(pdata->uart_rxdnum,"uart2_rxd");
+		if(retval)
+			BUG();		
+		retval=gpio_request(pdata->uart_txdnum,"uart2_txd");
+		if(retval)
+			BUG();
+		
+/*uart rxd*/  
+		zx29_gpio_config(pdata->uart_rxdnum, pdata->uart_rxdfnc);  
+		if(pdata->uart_rxdnum == ZX29_GPIO_121 ) {      
+                //pull up gpio121				
+				*(volatile unsigned int  *)0xf843c82c |= 0xf0;
+	       }
+/*uart txd*/ 
+		zx29_gpio_config(pdata->uart_txdnum, pdata->uart_txdfnc);  
+		
+		if(pdata->uart_ctsrtsuse)
+		{	
+			retval=gpio_request(pdata->uart_ctsnum,"uart2_cts");
+			if(retval)
+				BUG();
+				
+			retval=gpio_request(pdata->uart_rtsnum,"uart2_rts");
+			if(retval)
+				BUG();
+/*uart cts*/ 
+		    zx29_gpio_config(pdata->uart_ctsnum, pdata->uart_ctsfnc);  
+/*uart rts*/ 
+			zx29_gpio_config(pdata->uart_rtsnum, pdata->uart_rtsfnc); 
+
+			control |= (UART_CR_RTSEN |UART_CR_CTSEN );
+			control |= UART_CR_RTS;   //wl write1 for allow send
+		}
+        zup->autobaud = pdata->uart_abauduse ;	
+	}
+#endif    
+	zup->autobaud_state = UART_PORT_AUTOBAUD_OFF;
+	UART_PUT_CR(port, control);
+
+	/*
+	 * Finally, enable interrupts, only timeouts when using DMA
+	 * if initial RX DMA job failed, start in interrupt mode
+	 * as well.
+	 */
+	raw_spin_lock_irqsave(&zup->port.lock, flags);
+	/* Clear out any spuriously appearing RX interrupts */
+	UART_PUT_ICR(port, (UART_RTIS | UART_RXIS));
+	
+	zup->imr = UART_RTIM | UART_RXIM;
+	UART_PUT_IMSC(port, zup->imr);
+
+	zup->port_close = false;
+	raw_spin_unlock_irqrestore(&zup->port.lock, flags);	
+#if 0
+       if(zup->autobaud)
+       {  
+		//printk("enter autobaud \n");
+		zup->autobaud_state = UART_PORT_AUTOBAUD_ON;
+
+		if(pdev->id == UART1)
+			zup->baudrate = UART1_AUTOBAUD_RATE;
+		else
+			zup->baudrate = UART_AUTOBAUD_RATE;
+		
+		ibrd = port->uartclk  / (zup->baudrate <<4);
+		fbrd = ((port->uartclk % (zup->baudrate <<4) )*8 + zup->baudrate )/(2*zup->baudrate );		
+		UART_PUT_FBRD(port, fbrd);
+		UART_PUT_IBRD(port, ibrd);
+		lcr_h = UART_LCRH_WLEN_8 | UART_LCRH_FEN ;
+		old_cr = UART_GET_CR(port);
+		UART_PUT_CR(port, 0);
+		UART_PUT_LCRH(port, lcr_h);
+		UART_PUT_CR(port, old_cr);
+		msleep(1);
+	    
+		while(!uart_port_autobaud_suflag)
+		{
+			while(!uart_port_autobaud_gtflag)
+			{
+				msleep(2);
+			}
+
+			for(i=0;i<UART_AUTOBAUD_LEVEL;i++)
+			{
+				for(j=0;j<UART_AUTOBAUD_CHECKBYTE;j++)
+				{	
+					if((j ==0)&&(uart_port_autobaud_buffer[0] == UART_baud_check[i][j]))
+					{
+						j++;
+						if(uart_port_autobaud_buffer[1] == UART_baud_check[i][j])
+						{
+							zup->baudrate = UART_baud[i];
+							iflag = i;
+							uart_port_autobaud_suflag = 1;
+							break;
+						}
+					}
+					if((j ==2 )&&(uart_port_autobaud_buffer[0] == UART_baud_check[i][j]))
+					{
+						j++;
+						if(uart_port_autobaud_buffer[1] == UART_baud_check[i][j])
+						{
+							zup->baudrate = UART_baud[i];
+							iflag = i;
+							uart_port_autobaud_suflag = 1;
+							break;
+						}
+					}
+				}
+				if(uart_port_autobaud_suflag)
+				break;       
+			}
+
+			zup->port.icount.rx = 0; 
+			uart_port_autobaud_gtflag = 0;
+
+		}
+		uart_port_autobaud_suflag = 0;
+		tty->termios->c_cflag &= ~CBAUD;
+		tty->termios->c_cflag |= UART_termios_cflag[iflag];
+		tty->termios->c_ispeed  = zup->baudrate;
+		tty->termios->c_ospeed = zup->baudrate;
+	   }
+#endif	   
+	return 0;
+}
+
+/****************************************************************************/
+static void zx29_uart_shutdown(struct uart_port *port)
+{
+	struct zx29_uart_port *zup = container_of(port, struct zx29_uart_port, port);
+	unsigned long flags;
+	uint32_t val;
+	int retval = 0;
+	struct platform_device *pdev=port->private_data;
+	struct zx29_uart_platdata *pdata = pdev->dev.platform_data;
+	zup->port_close = true;
+	up(&zup->sema);
+	tasklet_kill(&zup->write_wakeup);
+	raw_spin_lock_irqsave(&port->lock, flags);
+#if RX_DMA_WORK
+	if(zx29_dma_rx_work_scheduled(zup)){
+		del_timer_sync(&(zup->rx_dma_timer));
+		zup->work_state = 0;
+	}
+#endif	
+	/* Disable and clear all interrupts now */
+	zup->imr = 0;
+	UART_PUT_IMSC(port, zup->imr);
+	UART_PUT_ICR(port, 0xFFFF);
+	
+	raw_spin_unlock_irqrestore(&port->lock, flags);
+#if CONFIG_SERIAL_ZX29_DMA
+	zx29_dma_shutdown(zup);
+#endif
+	/* Free the interrupt */
+	free_irq(zup->port.irq, zup);
+
+	/* Disable UART transmitter and receiver */
+	zup->autorts = false;
+	val = UART_GET_CR(port);
+	if (val & UART_CR_RTS) {
+		zup->rts_state = true;
+		val = UART_CR_RTS;
+	} else
+		zup->rts_state = false;
+	val = UART_CR_UARTEN | UART_CR_TXE;
+	UART_PUT_CR(port, val);
+
+	/* disable break condition and fifos */
+	val = UART_GET_LCRH(port);
+	val &= ~(UART_LCRH_BRK | UART_LCRH_FEN);
+	UART_PUT_LCRH(port, val);
+	if(pdata->uart_use)
+	{
+		if(pdata->uart_ctsrtsuse)
+		{
+			gpio_free(pdata->uart_cts.gpionum);
+			gpio_free(pdata->uart_rts.gpionum);
+		}
+#ifdef CONFIG_ARCH_ZX297520V3
+		if((pdev->id != DEBUG_CONSOLE) && (pdata->uart_wakeup_enable == 1) && (zup->irq_state == 1)){
+			printk(KERN_INFO"zx29_uart_shutdown,irq:%d,%s\n",zup->irq,pdata->uart_cts.gpioname);
+			if(zup->irq){
+				free_irq(zup->irq, zup);
+				pcu_int_clear(PCU_UART0_RXD_INT); 
+				irq_set_irq_wake(zup->irq, 0);
+				zup->irq_state = 0;
+				zup->rxd_int_depth = 0;
+			}
+		}
+#endif	
+		gpio_free(pdata->uart_rxd.gpionum);
+		gpio_free(pdata->uart_txd.gpionum);
+		
+#ifdef CONFIG_ARCH_ZX297520V3_WATCH
+		if(port->line == 0)
+		{
+			retval = gpio_request(pdata->uart_txd.gpionum, pdata->uart_txd.gpioname); 
+			if(retval)
+			{
+				BUG(); 
+			}
+			zx29_gpio_config(pdata->uart_txd.gpionum, GPIO30_GPIO30);
+			gpio_direction_input(pdata->uart_txd.gpionum);
+			
+			retval = gpio_request(pdata->uart_rxd.gpionum, pdata->uart_rxd.gpioname); 
+			if(retval)
+			{
+				BUG(); 
+			}
+			zx29_gpio_config(pdata->uart_rxd.gpionum, GPIO29_GPIO29);
+			gpio_direction_input(pdata->uart_rxd.gpionum);
+		}
+#endif		
+		
+	}
+	/* Shutdown uart clock */
+}
+
+/****************************************************************************/
+static void zx29_uart_set_termios(struct uart_port *port, struct ktermios *termios,
+	struct ktermios *old)
+{
+	struct zx29_uart_port *zup = container_of(port, struct zx29_uart_port, port);
+	unsigned int lcr_h, old_cr;
+	unsigned long flags;
+	unsigned int baud, ibrd, fbrd,j;
+ 
+    /* Set baud rate */
+	/* Ask the core to calculate the divisor for us. */
+	baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk/16);
+	//this should not hapend
+	if(baud == 0)
+		BUG_ON(1);
+	ibrd = port->uartclk  / (baud<<4);
+	fbrd = ((port->uartclk % (baud<<4) )*8 + baud)/(2*baud);		
+	UART_PUT_FBRD(port, fbrd);
+	UART_PUT_IBRD(port, ibrd);
+	
+
+	switch (termios->c_cflag & CSIZE) {
+	case CS5:
+		lcr_h = UART_LCRH_WLEN_5;
+		break;
+	case CS6:
+		lcr_h = UART_LCRH_WLEN_6;
+		break;
+	case CS7:
+		lcr_h = UART_LCRH_WLEN_7;
+		break;
+	default: // CS8
+		lcr_h = UART_LCRH_WLEN_8;
+		break;
+	}
+	if (termios->c_cflag & CSTOPB)
+		lcr_h |= UART_LCRH_STP2;
+	if (termios->c_cflag & PARENB) {
+		lcr_h |= UART_LCRH_PEN;
+		if (!(termios->c_cflag & PARODD))
+			lcr_h |= UART_LCRH_EPS;
+	}
+	if (port->fifosize > 1)
+		lcr_h |= UART_LCRH_FEN;
+
+	raw_spin_lock_irqsave(&port->lock, flags);
+
+	/*
+	 * Update the per-port timeout.
+	 */
+	uart_update_timeout(port, termios->c_cflag, baud);
+
+	port->read_status_mask = UART_DR_OE | 255;
+	if (termios->c_iflag & INPCK)
+		port->read_status_mask |= UART_DR_FE | UART_DR_PE;
+	if (termios->c_iflag & (BRKINT | PARMRK))
+		port->read_status_mask |= UART_DR_BE;
+
+	/*
+	 * Characters to ignore
+	 */
+	port->ignore_status_mask = 0;
+	if (termios->c_iflag & IGNPAR)
+		port->ignore_status_mask |= UART_DR_FE | UART_DR_PE;
+	if (termios->c_iflag & IGNBRK) {
+		port->ignore_status_mask |= UART_DR_BE;
+		/*
+		 * If we're ignoring parity and break indicators,
+		 * ignore overruns too (for real raw support).
+		 */
+		if (termios->c_iflag & IGNPAR)
+			port->ignore_status_mask |= UART_DR_OE;
+	}
+
+	/*
+	 * Ignore all characters if CREAD is not set.
+	 */
+	if ((termios->c_cflag & CREAD) == 0)
+		port->ignore_status_mask |= UART_DUMMY_DR_RX;
+
+	if (UART_ENABLE_MS(port, termios->c_cflag))
+		zx29_uart_enable_ms(port);
+
+	/* first, disable everything */
+	old_cr = UART_GET_CR(port);
+	UART_PUT_CR(port, 0);
+	
+	if (termios->c_cflag & CRTSCTS) {
+		if (old_cr & UART_CR_RTS)
+			old_cr |= UART_CR_RTSEN;
+
+		old_cr |= UART_CR_CTSEN;
+		zup->autorts = true;
+	} else {
+		old_cr &= ~(UART_CR_CTSEN | UART_CR_RTSEN);
+		zup->autorts = false;
+	}
+
+	/*
+	 * ----------v----------v----------v----------v-----
+	 * NOTE: MUST BE WRITTEN AFTER UARTLCR_M & UARTLCR_L
+	 * ----------^----------^----------^----------^-----
+	 */
+	UART_PUT_LCRH(port, lcr_h);
+	UART_PUT_CR(port, old_cr);
+
+	raw_spin_unlock_irqrestore(&port->lock, flags);
+	if( zup->autobaud_state == UART_PORT_AUTOBAUD_ON)
+	{
+		msleep(50);
+		zup->port.icount.rx = 0;
+
+		for( j = 0; j<UART_AT_SENDOK_NUM; j++)
+		{
+			while (UART_GET_FR(port) & UART_FR_TXFF)
+			barrier();
+			UART_PUT_CHAR(&zup->port, UART_AT_send_ok[j]);
+		}
+
+		zup->autobaud_state = UART_PORT_AUTOBAUD_OFF;
+	}
+}
+
+/****************************************************************************/
+static const char *zx29_uart_type(struct uart_port *port)
+{
+	return (port->type == PORT_ZX29) ? "zx29_UART" : NULL;
+}
+
+/****************************************************************************/
+
+static int zx29_uart_request_port(struct uart_port *port)
+{
+	/* UARTs always present */
+//	return request_mem_region(port->mapbase, SZ_4K, "uart-zx29")!= NULL ? 0 : -EBUSY;
+	return 0;
+}
+
+/****************************************************************************/
+static void zx29_uart_config_port(struct uart_port *port, int flags)
+{
+	if (flags & UART_CONFIG_TYPE) {
+		port->type = PORT_ZX29;
+		zx29_uart_request_port(port);
+	}
+}
+
+/****************************************************************************/
+
+static void zx29_uart_release_port(struct uart_port *port)
+{
+//	release_mem_region(port->mapbase, SZ_4K);
+}
+
+/****************************************************************************/
+
+static int zx29_uart_verify_port(struct uart_port *port, struct serial_struct *ser)
+{
+	if ((ser->type != PORT_UNKNOWN) && (ser->type != PORT_ZX29))
+		return -EINVAL;
+	return 0;
+}
+
+#ifdef CONFIG_CONSOLE_POLL
+/****************************************************************************/
+static int zx29_get_poll_char(struct uart_port *port)
+{
+	if (UART_GET_FR(port) & UART_FR_RXFE)
+		return NO_POLL_CHAR;
+
+	return UART_PUT_CHAR(port);
+}
+
+/****************************************************************************/
+static void zx29_put_poll_char(struct uart_port *port, unsigned char ch)
+{
+	while (UART_GET_FR(port) & UART_FR_TXFF)
+		barrier();
+	UART_PUT_CHAR(port, ch);
+}
+#endif /* CONFIG_CONSOLE_POLL */
+
+/****************************************************************************/
+/*
+ *	Define the basic serial functions we support.
+ */
+static const struct uart_ops zx29_uart_ops = {
+	.tx_empty	= zx29_uart_tx_empty,
+	.set_mctrl	= zx29_uart_set_mctrl,
+	.get_mctrl	= zx29_uart_get_mctrl,
+	.start_tx	= zx29_uart_start_tx,
+	.stop_tx	= zx29_uart_stop_tx,
+	.stop_rx	= zx29_uart_stop_rx,
+	.enable_ms	= zx29_uart_enable_ms,
+	.break_ctl	= zx29_uart_break_ctl,
+	.startup	= zx29_uart_startup,
+	.shutdown	= zx29_uart_shutdown,
+	.set_termios	= zx29_uart_set_termios,
+#if CONFIG_SERIAL_ZX29_DMA
+	.flush_buffer	= zx29_dma_flush_buffer,
+#endif
+	.type		= zx29_uart_type,
+	.request_port	= zx29_uart_request_port,
+	.release_port	= zx29_uart_release_port,
+	.config_port	= zx29_uart_config_port,
+	.verify_port	= zx29_uart_verify_port,
+#ifdef CONFIG_CONSOLE_POLL
+	.poll_get_char = zx29_get_poll_char,
+	.poll_put_char = zx29_put_poll_char,
+#endif
+
+};
+
+
+/****************************************************************************/
+static void __init zx29_init_ports(struct zx29_uart_port *zx29_port,	
+                                           struct platform_device *pdev)
+{
+	struct uart_port *port=&zx29_port->port;
+	unsigned int offset=(unsigned int)(pdev->id);
+	struct clk *pclk=NULL;
+	struct clk *wclk=NULL;
+#if 0	
+//following code just a demo,may be we should use NEW_LINUX_FRAME
+#ifdef CONSOLE_UART0
+    /*  config uart apb_clk   */
+	if(pdev->id == DEBUG_CONSOLE)
+	{
+#if NEW_LINUX_FRAME	
+		pclk = clk_get(CONSOLE_NAME, "uart0_pclk");
+              wclk = clk_get(CONSOLE_NAME, "uart0_wclk");
+#else
+		pclk = clk_get_sys(CONSOLE_NAME, "apb_clk");
+		wclk = clk_get_sys(CONSOLE_NAME, "work_clk");
+#endif	
+	}
+	else if(pdev->id == UART1)	
+	{
+		pclk=clk_get_sys(UART1_NAME, "apb_clk");
+		wclk=clk_get_sys(UART1_NAME, "work_clk");
+	}else if(pdev->id == UART2)
+	{
+		pclk = clk_get_sys(UART2_NAME, "apb_clk");
+		wclk = clk_get_sys(UART2_NAME, "work_clk");
+	}
+#endif 
+
+#else
+
+	pclk = clk_get_sys(uart_names[pdev->id], "apb_clk");
+	wclk = clk_get_sys(uart_names[pdev->id], "work_clk");
+
+#endif	
+	if (IS_ERR(pclk))
+		panic("failed to get uart_pclk.\n");
+	zx29_port->busclk=pclk;   /*get apb clock*/ 
+	if (IS_ERR(wclk))
+		panic("failed to get uart_wclk.\n");
+	zx29_port->wclk=wclk;   /*get work clock*/ 
+	
+	port->line = offset;
+	port->type = PORT_ZX29;
+	port->fifosize = UART_TXFIFO_SIZE;
+	port->iotype = UPIO_MEM;	
+	port->irq = pdev->resource[1].start;
+	port->mapbase = pdev->resource[0].start;
+	if(pdev->id == 0){
+		port->membase = ZX_UART0_BASE;}
+	else if(pdev->id == 1){
+		port->membase = ZX_UART1_BASE;}
+	else if(pdev->id == 2){
+		port->membase = ZX_UART2_BASE;}
+	else 
+		panic("uart id error.\n");
+	
+	port->flags = UPF_BOOT_AUTOCONF;
+	port->ops = &zx29_uart_ops;
+	port->uartclk = clk_get_rate(wclk);
+    	port->private_data=pdev;
+
+    /*
+        * just configure clock, 
+        * actually pin configuration is needed, but now gpio driver is not OK
+        * use bootloader default configuration
+	 */
+    if(DEBUG_CONSOLE == pdev->id){
+		/*  config uart apb_clk   */
+		clk_prepare_enable(zx29_port->busclk);
+
+		/* enable uart work clock */
+		clk_prepare_enable(zx29_port->wclk);
+   	}
+
+}
+
+
+#ifdef CONFIG_SERIAL_ZX29_UART_CONSOLE
+
+/****************************************************************************/
+static void zx29_uart_console_putc(struct uart_port *port, const char c)
+{
+#ifdef CONFIG_PREEMPT_RT_FULL
+    /*防止中间出现优先级反转*/
+    if(current->prio <= 62)
+        return;
+#endif
+	while (UART_GET_FR(port) & UART_FR_TXFF)
+		barrier();
+	UART_PUT_CHAR(port, c);
+}
+
+
+/****************************************************************************/
+static void zx29_uart_console_write(struct console *co, const char *s, unsigned int count)
+{
+	struct uart_port *port = &zx29_uart_ports[co->index].port;
+#if 0
+	if(!g_console_open_flag)
+		return;
+#endif	
+	//raw_spin_lock(&port->lock);
+	for (; (count); count--, s++) {
+		zx29_uart_console_putc(port, *s);
+		if (*s == '\n')
+			zx29_uart_console_putc(port, '\r');
+	}
+	
+	//raw_spin_unlock(&port->lock);
+}
+
+/***************************************************************************
+ * If the port was already initialised (eg, by a boot loader),
+ * try to determine the current setup.
+ ****************************************************************************/
+static void __init zx29_console_get_options(struct uart_port *port, int *baud,
+						 int *parity, int *bits)
+{
+	if (UART_GET_CR(port) & UART_CR_UARTEN) {
+		unsigned int lcr_h, ibrd, fbrd;
+
+		lcr_h = UART_GET_LCRH(port);
+		*parity = 'n';
+		if (lcr_h & UART_LCRH_PEN) {
+			if (lcr_h & UART_LCRH_EPS)
+				*parity = 'e';
+			else
+				*parity = 'o';
+		}
+		if ((lcr_h & 0x60) == UART_LCRH_WLEN_7)
+			*bits = 7;
+		else
+			*bits = 8;
+
+		ibrd = UART_GET_IBRD(port);
+		fbrd = UART_GET_FBRD(port);
+
+		*baud = port->uartclk * 8 / (16*8 * ibrd + 2*fbrd-1);
+	}
+}
+
+/****************************************************************************/
+static int __init zx29_uart_console_setup(struct console *co, char *options)
+{
+	struct uart_port *port;
+	int baud = 115200;
+	int bits = 8;
+	int parity = 'n';
+	int flow = 'n';
+	unsigned int uart_cr = 0;
+
+	if ((co->index < 0) || (co->index >= zx29_MAXPORTS))
+		co->index = CONFIG_UART_CONSOLE_ID;
+
+	port = &zx29_uart_ports[co->index].port;
+	if (port->membase == NULL)
+		return -ENODEV;
+	
+	uart_cr = UART_GET_CR(port);
+	uart_cr |= UART_CR_UARTEN | UART_CR_TXE;
+	UART_PUT_CR(port,uart_cr);
+	
+	if (options)
+		uart_parse_options(options, &baud, &parity, &bits, &flow);
+	else
+		zx29_console_get_options(port, &baud, &parity, &bits);
+
+	return uart_set_options(port, co, baud, parity, bits, flow);
+}
+
+/****************************************************************************/
+
+static struct uart_driver zx29_uart_driver;
+int zx29_get_console_index(void)
+{
+	int dev_cnt = zx29_device_table_num;
+	int idx = 0; 
+	struct platform_device *pdev = NULL;
+	for(idx = 0; idx < dev_cnt; idx++)
+	{
+		pdev = zx29_device_table[idx];
+		if(strcmp(pdev->name,"zx29_uart") == 0 && pdev->id == CONFIG_UART_CONSOLE_ID)
+			return idx;
+	}
+	return -1;
+}
+static struct console zx29_uart_console = {
+	.name		= "ttyS",
+	.write		= zx29_uart_console_write,
+	.device		= uart_console_device,
+	.setup		= zx29_uart_console_setup,
+	.flags		= CON_PRINTBUFFER,
+	.index		= -1,
+	.data		= &zx29_uart_driver,
+};
+static int __init zx29_uart_console_init(void)
+{
+	int console_dev_id = zx29_get_console_index();
+	if(console_dev_id < 0){
+		printk("console init fail, uart config fail, console_dev_id is: %d", console_dev_id);
+		return -1;
+	}
+	zx29_init_ports(&zx29_uart_ports[DEBUG_CONSOLE], zx29_device_table[console_dev_id]);
+	register_console(&zx29_uart_console);
+	pr_info("[UART]register_console: zx29 console registered!\n");
+
+	return 0;
+}
+
+console_initcall(zx29_uart_console_init);
+
+#define	zx29_UART_CONSOLE	(&zx29_uart_console)
+#else
+#define	zx29_UART_CONSOLE	NULL
+#endif /* CONFIG_zx29_UART_CONSOLE */
+
+/*
+ *	Define the zx29 UART driver structure.
+ */
+static struct uart_driver zx29_uart_driver = {
+	.owner		    = THIS_MODULE,
+	.driver_name	= "zx29_uart",
+	.dev_name	    = "ttyS",
+	.major		    = SERIAL_zx29_MAJOR,
+	.minor		    = SERIAL_MINOR_START,
+	.nr		        = zx29_MAXPORTS,
+	.cons		    = zx29_UART_CONSOLE,
+};
+
+unsigned char uart_wakelock_name[zx29_MAXPORTS][20]={{0}};
+
+/****************************************************************************/
+static int __init zx29_uart_probe(struct platform_device *pdev)
+{
+    struct zx29_uart_platdata *pdata = pdev->dev.platform_data;
+    struct zx29_uart_port *port = &zx29_uart_ports[pdev->id];
+    int ret=0;	
+    int error;
+     char    wakelock_name[20];
+     
+    if(!pdata->uart_use) {
+        return 0;
+    }
+#ifdef CONFIG_SERIAL_ZX29_UART_CONSOLE
+    if(DEBUG_CONSOLE != pdev->id){
+	   zx29_init_ports(port,pdev);
+    }
+#else 
+	   zx29_init_ports(port,pdev);
+#endif
+
+#if CONFIG_SERIAL_ZX29_DMA
+//	if(pdev->id == 0)
+//		{
+		uart_dma_init(port);
+		printk(KERN_INFO "[%s][%d]UART DMA is OPENED%d\n",__func__,__LINE__,pdev->id);
+//		}
+#endif
+
+    ret=uart_add_one_port(&zx29_uart_driver, &port->port);
+	if(ret)
+		{
+#if CONFIG_SERIAL_ZX29_DMA
+		zx29_dma_remove(port);
+#endif
+		return ret;
+		}
+	 sema_init(&port->sema, 0);
+	/*platform_set_drvdata(pdev, port);*/
+	
+	if(pdev->id == DEBUG_CONSOLE){
+		g_console_open_flag = pdata->uart_input_enable ? pdata->uart_input_enable : 0;
+		error = device_create_file(&pdev->dev, &dev_attr_console_input);
+	}
+	
+	if(pdev->id != DEBUG_CONSOLE){
+		error = device_create_file(&pdev->dev, &dev_attr_ctsrts_input);
+		error = device_create_file(&pdev->dev, &dev_attr_wakeup_enable);
+		error = device_create_file(&pdev->dev, &dev_attr_app_ctrl);
+	}
+		error = device_create_file(&pdev->dev, &dev_attr_statics);
+	
+	strcpy(wakelock_name, "uart_wakelock_x");
+	wakelock_name[14] = '0' + pdev->id;
+	strcpy(uart_wakelock_name[pdev->id], wakelock_name);
+	wake_lock_init(&(port->port.port_wakelock),WAKE_LOCK_SUSPEND,uart_wakelock_name[pdev->id]);
+	
+    printk(KERN_INFO "TSP zx29 UART_%d probe OK\n",pdev->id);
+	return 0;
+}
+
+/****************************************************************************/
+static int /*__devexit*/ zx29_uart_remove(struct platform_device *pdev)
+{
+	struct uart_port *port = NULL;
+#if CONFIG_SERIAL_ZX29_DMA
+	struct zx29_uart_port *zup = container_of(port, struct zx29_uart_port, port);
+#endif
+	int i;
+	if(pdev->id == DEBUG_CONSOLE){		
+		 device_remove_file(&pdev->dev, &dev_attr_console_input);
+	}
+	
+	if(pdev->id != DEBUG_CONSOLE){
+		device_remove_file(&pdev->dev, &dev_attr_ctsrts_input);
+		device_remove_file(&pdev->dev, &dev_attr_wakeup_enable);
+	}
+
+	for (i = 0; (i < zx29_MAXPORTS); i++) {
+		port = &zx29_uart_ports[i].port;
+		if (port){
+			uart_remove_one_port(&zx29_uart_driver, port);
+
+	
+#if CONFIG_SERIAL_ZX29_DMA
+            zup=container_of(port,struct zx29_uart_port,port);
+        	zx29_dma_remove(zup);
+
+#endif
+	    }
+	}
+	return 0;
+}
+
+static struct platform_driver zx29_uart_platform_driver = {
+	.probe		= zx29_uart_probe,
+	.remove		= __devexit_p(zx29_uart_remove),
+	.driver		= {
+		.name	= "zx29_uart",
+		.owner	= THIS_MODULE,
+	},
+};
+
+static int __init zx29_uart_init(void)
+{
+	int rc;
+
+	rc = uart_register_driver(&zx29_uart_driver);
+	if (rc)
+		return rc;
+	rc = platform_driver_register(&zx29_uart_platform_driver);
+	if (rc){
+		uart_unregister_driver(&zx29_uart_driver);
+		return rc;
+	}
+		
+	printk(KERN_INFO "TSP zx29 UART driver registered\n");
+	
+	return 0;
+}
+
+static void __exit zx29_uart_exit(void)
+{
+#ifdef CONFIG_SERIAL_ZX29_UART_CONSOLE
+	unregister_console(&zx29_uart_console);
+#endif
+	platform_driver_unregister(&zx29_uart_platform_driver);
+	uart_unregister_driver(&zx29_uart_driver);
+}
+
+module_init(zx29_uart_init);
+module_exit(zx29_uart_exit);
+
diff --git a/ap/os/linux/linux-3.4.x/drivers/tty/serial/zx29_uart.h b/ap/os/linux/linux-3.4.x/drivers/tty/serial/zx29_uart.h
new file mode 100644
index 0000000..c160ea8
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/tty/serial/zx29_uart.h
@@ -0,0 +1,192 @@
+/*
+ * zx29_uart.h -- sanchips zx29 UART driver defines.
+ */
+
+#ifndef	__ZX29_UART_H
+#define	__ZX29_UART_H
+
+#define SYM_TO_STR(str) #str
+#define UART_NAME_PREFIX zx29_uart.
+#define STR_JOINT(sym1, sym2)	sym1##sym2
+#define UART_NAME(id)	SYM_TO_STR(id)
+
+
+#if 1 //remove from zx29_uart.c, used by serial_core.c
+#define UART0 0
+#define UART1 1
+#define UART2 2
+
+#define DEBUG_CONSOLE	CONFIG_UART_CONSOLE_ID
+
+#endif
+/*uart0 base address*/
+#define ZX29_UART_BASE	   (ZX_UART0_BASE)
+
+/*offset address*/
+#define zx29_UART_DR		0x04	/* Data read or written from the interface. */
+#define zx29_UART_SC        0x08    /* Set special character to generate a interrupt*/
+#define zx29_UART_RSR		0x10	/* Receive status register (Read). */
+#define zx29_UART_ECR		0x10	/* Error clear register (Write). */
+#define zx29_UART_FR		0x14	/* Flag register (Read only). */
+
+#define zx29_UART_ILPR		0x20	/* IrDA low power counter register. */
+#define zx29_UART_IBRD		0x24	/* Integer baud rate divisor register. */
+#define zx29_UART_FBRD		0x28	/* Fractional baud rate divisor register. */
+#define zx29_UART_LCRH		0x30	/* Line control register. */
+#define zx29_UART_CR		0x34	/* Control register. */
+#define zx29_UART_IFLS		0x38	/* Interrupt fifo level select. */
+#define zx29_UART_IMSC		0x40	/* Interrupt mask. */
+#define zx29_UART_RIS		0x44	/* Raw interrupt status. */
+#define zx29_UART_MIS		0x48	/* Masked interrupt status. */
+#define zx29_UART_ICR		0x4c	/* Interrupt clear register. */
+#define zx29_UART_DMACR		0x50	/* DMA control register. */
+
+
+/*------ uart flag reg -----*/
+#define UART_FR_RXBUSY		(1<<9)
+#define UART_FR_TXBUSY		(1<<8)
+#define UART_FR_TXFE		(1<<7)
+#define UART_FR_RXFF		(1<<6)
+#define UART_FR_TXFF		(1<<5)
+#define UART_FR_RXFE		(1<<4)
+#define UART_FR_DSR 		(1<<3)
+#define UART_FR_DCD 		(1<<2)
+#define UART_FR_CTS 		(1<<1)
+#define UART_FR_RI			(1<<0)
+
+/*------ uart control reg -----*/
+#define UART_CR_CTSEN		(1<<15)	/* CTS hardware flow control */
+#define UART_CR_RTSEN		(1<<14)	/* RTS hardware flow control */
+#define UART_CR_OUT2		(1<<13)	/* OUT2 */
+#define UART_CR_OUT1		(1<<12)	/* OUT1 */
+#define UART_CR_RTS			(1<<11)	/* RTS */
+#define UART_CR_DTR			(1<<10)	/* DTR */
+#define UART_CR_RXE			(1<<9)	/* receive enable */
+#define UART_CR_TXE			(1<<8)	/* transmit enable */
+#define UART_CR_LBE			(1<<7)	/* loopback enable */
+#define UART_CR_SIRLP		(1<<2)	/* SIR low power mode */
+#define UART_CR_SIREN		(1<<1)	/* SIR enable */
+#define UART_CR_UARTEN		(1<<0)	/* UART enable */
+
+/*------ uart line control reg -----*/
+#define UART_LCRH_SPS		(1<<7)
+#define UART_LCRH_WLEN_8	(3<<5)
+#define UART_LCRH_WLEN_7	(2<<5)
+#define UART_LCRH_WLEN_6	(1<<5)
+#define UART_LCRH_WLEN_5	(0<<5)
+#define UART_LCRH_FEN		(1<<4)
+#define UART_LCRH_STP2		(1<<3)
+#define UART_LCRH_EPS		(1<<2)
+#define UART_LCRH_PEN		(1<<1)
+#define UART_LCRH_BRK		(1<<0)
+
+/*------ uart fifo level select reg -----*/
+#define UART_IFLS_RX1_8	(0 << 3)
+#define UART_IFLS_RX2_8	(1 << 3)
+#define UART_IFLS_RX4_8	(2 << 3)
+#define UART_IFLS_RX6_8	(3 << 3)
+#define UART_IFLS_RX7_8	(4 << 3)
+#define UART_IFLS_TX1_8	(0 << 0)
+#define UART_IFLS_TX2_8	(1 << 0)
+#define UART_IFLS_TX4_8	(2 << 0)
+#define UART_IFLS_TX6_8	(3 << 0)
+#define UART_IFLS_TX7_8	(4 << 0)
+
+/*------ uart interrupt mask reg -----*/
+#define UART_OEIM		(1 << 10)	/* overrun error interrupt mask */
+#define UART_BEIM		(1 << 9)	/* break error interrupt mask */
+#define UART_PEIM		(1 << 8)	/* parity error interrupt mask */
+#define UART_FEIM		(1 << 7)	/* framing error interrupt mask */
+#define UART_RTIM		(1 << 6)	/* receive timeout interrupt mask */
+#define UART_TXIM		(1 << 5)	/* transmit interrupt mask */
+#define UART_RXIM		(1 << 4)	/* receive interrupt mask */
+#define UART_DSRMIM		(1 << 3)	/* DSR interrupt mask */
+#define UART_DCDMIM		(1 << 2)	/* DCD interrupt mask */
+#define UART_CTSMIM		(1 << 1)	/* CTS interrupt mask */
+#define UART_RIMIM		(1 << 0)	/* RI interrupt mask */
+
+/*------ uart raw interrupt status reg -----*/
+#define UART_OEIS		(1 << 10)	/* overrun error interrupt status */
+#define UART_BEIS		(1 << 9)	/* break error interrupt status */
+#define UART_PEIS		(1 << 8)	/* parity error interrupt status */
+#define UART_FEIS		(1 << 7)	/* framing error interrupt status */
+#define UART_RTIS		(1 << 6)	/* receive timeout interrupt status */
+#define UART_TXIS		(1 << 5)	/* transmit interrupt status */
+#define UART_RXIS		(1 << 4)	/* receive interrupt status */
+#define UART_DSRMIS		(1 << 3)	/* DSR interrupt status */
+#define UART_DCDMIS		(1 << 2)	/* DCD interrupt status */
+#define UART_CTSMIS		(1 << 1)	/* CTS interrupt status */
+#define UART_RIMIS		(1 << 0)	/* RI interrupt status */
+
+/*------ uart interrupt clear reg -----*/
+#define UART_SCIC		(1 << 11)	/* special character interrupt clear */
+#define UART_OEIC		(1 << 10)	/* overrun error interrupt clear */
+#define UART_BEIC		(1 << 9)	/* break error interrupt clear */
+#define UART_PEIC		(1 << 8)	/* parity error interrupt clear */
+#define UART_FEIC		(1 << 7)	/* framing error interrupt clear */
+#define UART_RTIC		(1 << 6)	/* receive timeout interrupt clear */
+#define UART_TXIC		(1 << 5)	/* transmit interrupt clear */
+#define UART_RXIC		(1 << 4)	/* receive interrupt clear */
+#define UART_DSRMIC		(1 << 3)	/* DSR interrupt clear */
+#define UART_DCDMIC		(1 << 2)	/* DCD interrupt clear */
+#define UART_CTSMIC		(1 << 1)	/* CTS interrupt clear */
+#define UART_RIMIC		(1 << 0)	/* RI interrupt clear */
+
+/*------ uart DMA control reg -----*/
+#define UART_DMAONERR	(1 << 2)	/* disable dma on error */
+#define UART_TXDMAE		(1 << 1)	/* enable transmit dma */
+#define UART_RXDMAE		(1 << 0)	/* enable receive dma */
+
+/*------ uart receive data  error indicator in data reg -----*/
+#define UART_DR_OE		(1 << 11)
+#define UART_DR_BE		(1 << 10)
+#define UART_DR_PE		(1 << 9)
+#define UART_DR_FE		(1 << 8)
+
+/*------ uart RSR reg -----*/
+#define UART_RSR_OE 		0x08
+#define UART_RSR_BE 		0x04
+#define UART_RSR_PE 		0x02
+#define UART_RSR_FE 		0x01
+
+#define UART_DR_ERROR		(UART_DR_OE|UART_DR_BE|UART_DR_PE|UART_DR_FE)
+#define UART_DUMMY_DR_RX	(1 << 16)
+
+#define UART_RSR_ANY		(UART_RSR_OE|UART_RSR_BE|UART_RSR_PE|UART_RSR_FE)
+#define UART_FR_MODEM_ANY	(UART_FR_DCD|UART_FR_DSR|UART_FR_CTS)
+
+#define UART_TXFIFO_SIZE 16
+
+/* UART registers.  hence no GET macro */
+#define UART_PUT_IFLS(port, v)  __raw_writel(v, (port)->membase + zx29_UART_IFLS)
+#define UART_GET_IFLS(port)	    __raw_readl((port)->membase + zx29_UART_IFLS)
+
+#define UART_PUT_CHAR(port, v)	__raw_writel(v, (port)->membase + zx29_UART_DR)
+#define UART_GET_CHAR(port)	    __raw_readl((port)->membase + zx29_UART_DR)
+
+#define UART_GET_FR(port)	    __raw_readl((port)->membase + zx29_UART_FR)
+    
+#define UART_PUT_IBRD(port, v)	__raw_writel(v, (port)->membase + zx29_UART_IBRD)
+#define UART_GET_IBRD(port)		__raw_readl((port)->membase + zx29_UART_IBRD)
+
+#define UART_PUT_FBRD(port, v)	__raw_writel(v, (port)->membase + zx29_UART_FBRD)
+#define UART_GET_FBRD(port)		__raw_readl((port)->membase + zx29_UART_FBRD)
+
+#define UART_PUT_LCRH(port, v)	__raw_writel(v, (port)->membase + zx29_UART_LCRH)
+#define UART_GET_LCRH(port)	    __raw_readl((port)->membase + zx29_UART_LCRH)
+  
+#define UART_PUT_CR(port, v)	__raw_writel(v, (port)->membase + zx29_UART_CR)
+#define UART_GET_CR(port)	    __raw_readl((port)->membase + zx29_UART_CR)
+
+#define UART_PUT_ICR(port, v)	__raw_writel(v, (port)->membase + zx29_UART_ICR)
+
+#define UART_PUT_IMSC(port, v)  __raw_writel(v, (port)->membase + zx29_UART_IMSC)
+#define UART_GET_IMSC(port)      __raw_readl((port)->membase + zx29_UART_IMSC)
+
+#define UART_PUT_MIS(port, v)   __raw_writel(v, (port)->membase + zx29_UART_MIS)
+#define UART_GET_MIS(port)      __raw_readl((port)->membase + zx29_UART_MIS)
+
+#define UART_GET_RIS(port)      __raw_readl((port)->membase + zx29_UART_RIS)
+#define UART_PUT_DMACR(port, v)  __raw_writel(v, (port)->membase + zx29_UART_DMACR)
+#define UART_GET_DMACR(port)      __raw_readl((port)->membase + zx29_UART_DMACR)
+#endif /* __ZX29_UART_H */