Subject: [@num@/@total@] i.MX2 family: Add the common serial driver
From: Juergen Beisert <j.beisert@pengutronix.de>

This patch adds common UART driver for the whole i.MX/MXC processor
family.

TODO:

 - should it be a full blown driver for all possible architectures?
   Or better one main part with several adaptions to i.MX1/i.MX2/i.MX3?
 - get a sign from freescale for the patch

Signed-off-by: Juergen Beisert <j.beisert@pengutronix.de>

---

 drivers/serial/Kconfig      |   24 
 drivers/serial/Makefile     |    1 
 drivers/serial/mxc_uart.c   | 1584 ++++++++++++++++++++++++++++++++++++++++++++
 include/linux/serial_core.h |    3 
 4 files changed, 1612 insertions(+)

Index: drivers/serial/mxc_uart.c
===================================================================
--- /dev/null
+++ drivers/serial/mxc_uart.c
@@ -0,0 +1,1584 @@
+/*
+ * Copyright 2004-2007 Freescale Semiconductor, Inc. All Rights Reserved.
+ * Copyright 2008 Juergen Beisert, kernel@pengutronix.de
+ *
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/tty.h>
+#include <linux/string.h>
+#include <linux/ioport.h>
+#include <linux/init.h>
+#include <linux/serial.h>
+#include <linux/console.h>
+#include <linux/platform_device.h>
+#include <linux/sysrq.h>
+#include <linux/dma-mapping.h>
+#include <linux/clk.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/dma.h>
+#include <asm/arch/hardware.h>
+#include <asm/arch/imx_uart.h>
+
+#if defined(CONFIG_SERIAL_MXC_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ)
+#define SUPPORT_SYSRQ
+#endif
+#define SERIAL_MXC_MAJOR        207
+#define SERIAL_MXC_MINOR        16
+#define MXC_ISR_PASS_LIMIT      256
+#define UART_CREAD_BIT          256
+
+/* IRDA minimum pulse duration in micro seconds */
+#define MIN_PULSE_DUR           2
+/*
+ * Transmit DMA buffer size is set to 1024 bytes, this is limited
+ * by UART_XMIT_SIZE.
+ */
+#define TXDMA_BUFF_SIZE         UART_XMIT_SIZE
+/*
+ * Receive DMA sub-buffer size
+ */
+#define RXDMA_BUFF_SIZE         128
+
+/*
+ * This structure is used to store the information for DMA data transfer.
+ */
+typedef struct {
+	/*
+	 * Holds the read channel number.
+	 */
+	int rd_channel;
+	/*
+	 * Holds the write channel number.
+	 */
+	int wr_channel;
+	/*
+	 * UART Transmit Event ID
+	 */
+	int tx_event_id;
+	/*
+	 * UART Receive Event ID
+	 */
+	int rx_event_id;
+	/*
+	 * DMA Transmit tasklet
+	 */
+	struct tasklet_struct dma_tx_tasklet;
+	/*
+	 * Flag indicates if the channel is in use
+	 */
+	int dma_txchnl_inuse;
+} dma_info;
+
+/* Used to indicate if we want echo cancellation in the Irda mode */
+static int echo_cancel;
+/* Stores the port number of the UART used as a console. */
+static int console_index = 0;
+extern void config_uartdma_event(int port);
+
+static struct uart_mxc_port *mxc_ports[MXC_UART_NR];
+
+/*
+ * This array holds the DMA channel information for each MXC UART
+ */
+static dma_info dma_list[MXC_UART_NR];
+
+/* Called by the core driver to stop UART transmission */
+static void mxcuart_stop_tx(struct uart_port *port)
+{
+	struct uart_mxc_port *umxc = (struct uart_mxc_port *) port;
+	unsigned int cr1;
+
+	cr1 = readl(port->membase + MXC_UARTUCR1);
+	/* Disable Transmitter rdy interrupt */
+	if (umxc->dma_enabled == 1) {
+		cr1 &= ~MXC_UARTUCR1_TXDMAEN;
+	} else {
+		cr1 &= ~MXC_UARTUCR1_TRDYEN;
+	}
+	writel(cr1, port->membase + MXC_UARTUCR1);
+}
+
+/*
+ * DMA Transmit tasklet method is scheduled on completion of a DMA transmit
+ * to send out any more data that is available in the UART xmit buffer.
+ */
+static void dma_tx_do_tasklet(unsigned long arg)
+{
+	struct uart_mxc_port *umxc = (struct uart_mxc_port *) arg;
+	struct circ_buf *xmit = &umxc->port.info->xmit;
+	mxc_dma_requestbuf_t writechnl_request;
+	int tx_num;
+	unsigned long flags;
+
+	spin_lock_irqsave(&umxc->port.lock, flags);
+	tx_num = uart_circ_chars_pending(xmit);
+	if (tx_num > 0) {
+		if (xmit->tail > xmit->head) {
+			memcpy(umxc->tx_buf, xmit->buf + xmit->tail,
+			       UART_XMIT_SIZE - xmit->tail);
+			memcpy(umxc->tx_buf + (UART_XMIT_SIZE - xmit->tail),
+			       xmit->buf, xmit->head);
+		} else {
+			memcpy(umxc->tx_buf, xmit->buf + xmit->tail, tx_num);
+		}
+		umxc->tx_handle = dma_map_single(umxc->port.dev, umxc->tx_buf,
+						 TXDMA_BUFF_SIZE,
+						 DMA_TO_DEVICE);
+
+		writechnl_request.dst_addr = umxc->port.mapbase + MXC_UARTUTXD;
+		writechnl_request.src_addr = umxc->tx_handle;
+		writechnl_request.num_of_bytes = tx_num;
+
+		if ((mxc_dma_config(dma_list[umxc->port.line].wr_channel,
+				    &writechnl_request, 1,
+				    MXC_DMA_MODE_WRITE)) == 0) {
+			mxc_dma_enable(dma_list[umxc->port.line].wr_channel);
+		}
+	} else {
+		/* No more data available in the xmit queue, clear the flag */
+		dma_list[umxc->port.line].dma_txchnl_inuse = 0;
+	}
+	spin_unlock_irqrestore(&umxc->port.lock, flags);
+}
+
+/*
+ * DMA Write callback is called by the SDMA controller after it has sent out all
+ * the data from the user buffer. This function updates the xmit buffer pointers.
+ */
+static void mxcuart_dma_writecallback(void *arg, int error, unsigned int count)
+{
+	struct uart_mxc_port *umxc = arg;
+	struct circ_buf *xmit = &umxc->port.info->xmit;
+	int tx_num;
+
+	if (error != MXC_DMA_TRANSFER_ERROR) {
+		tx_num = count;
+		umxc->port.icount.tx += tx_num;
+		xmit->tail = (xmit->tail + tx_num) & (UART_XMIT_SIZE - 1);
+	}
+
+	dma_unmap_single(umxc->port.dev, umxc->tx_handle, TXDMA_BUFF_SIZE,
+			 DMA_TO_DEVICE);
+	tx_num = uart_circ_chars_pending(xmit);
+	/* Schedule a tasklet to send out the pending characters */
+	if (tx_num > 0) {
+		tasklet_schedule(&dma_list[umxc->port.line].dma_tx_tasklet);
+	} else {
+		dma_list[umxc->port.line].dma_txchnl_inuse = 0;
+	}
+	if (tx_num < WAKEUP_CHARS) {
+		uart_write_wakeup(&umxc->port);
+	}
+}
+
+/* Called by the core driver to start transmitting characters */
+static void mxcuart_start_tx(struct uart_port *port)
+{
+	struct uart_mxc_port *umxc = (struct uart_mxc_port *) port;
+	struct circ_buf *xmit = &umxc->port.info->xmit;
+	volatile unsigned int cr1;
+	mxc_dma_requestbuf_t writechnl_request;
+	int tx_num;
+
+	cr1 = readl(port->membase + MXC_UARTUCR1);
+	/* Enable Transmitter rdy interrupt */
+	if (umxc->dma_enabled == 1) {
+		/*
+		 * If the channel is in use then return immediately and use
+		 * the dma_tx tasklet to transfer queued data when current DMA
+		 * transfer is complete
+		 */
+		if (dma_list[umxc->port.line].dma_txchnl_inuse == 1) {
+			return;
+		}
+		tx_num = uart_circ_chars_pending(xmit);
+		if (tx_num > 0) {
+			dma_list[umxc->port.line].dma_txchnl_inuse = 1;
+			if (xmit->tail > xmit->head) {
+				memcpy(umxc->tx_buf, xmit->buf + xmit->tail,
+				       UART_XMIT_SIZE - xmit->tail);
+				memcpy(umxc->tx_buf +
+				       (UART_XMIT_SIZE - xmit->tail), xmit->buf,
+				       xmit->head);
+			} else {
+				memcpy(umxc->tx_buf, xmit->buf + xmit->tail,
+				       tx_num);
+			}
+			umxc->tx_handle =
+			    dma_map_single(umxc->port.dev, umxc->tx_buf,
+					   TXDMA_BUFF_SIZE, DMA_TO_DEVICE);
+
+			writechnl_request.dst_addr =
+			    umxc->port.mapbase + MXC_UARTUTXD;
+			writechnl_request.src_addr = umxc->tx_handle;
+			writechnl_request.num_of_bytes = tx_num;
+			if ((mxc_dma_config
+			     (dma_list[umxc->port.line].wr_channel,
+			      &writechnl_request, 1,
+			      MXC_DMA_MODE_WRITE)) == 0) {
+				mxc_dma_enable(dma_list[umxc->port.line].
+					       wr_channel);
+			}
+			cr1 |= MXC_UARTUCR1_TXDMAEN;
+		}
+	} else {
+		cr1 |= MXC_UARTUCR1_TRDYEN;
+	}
+	writel(cr1, port->membase + MXC_UARTUCR1);
+}
+
+/* Called by the core driver to stop receiving characters */
+static void mxcuart_stop_rx(struct uart_port *port)
+{
+	struct uart_mxc_port *umxc = (struct uart_mxc_port *) port;
+	unsigned int cr1;
+
+	cr1 = readl(port->membase + MXC_UARTUCR1);
+	if (umxc->dma_enabled == 1) {
+		cr1 &= ~MXC_UARTUCR1_RXDMAEN;
+	} else {
+		cr1 &= ~MXC_UARTUCR1_RRDYEN;
+	}
+	writel(cr1, port->membase + MXC_UARTUCR1);
+}
+
+/* Called by the core driver to enable the modem status interrupts */
+static void mxcuart_enable_ms(struct uart_port *port)
+{
+	struct uart_mxc_port *umxc = (struct uart_mxc_port *) port;
+	unsigned int cr1, cr3;
+
+	/*
+	 * RTS interrupt is enabled only if we are using interrupt-driven
+	 * software controlled hardware flow control
+	 */
+	if (umxc->hardware_flow == 0) {
+		cr1 = readl(umxc->port.membase + MXC_UARTUCR1);
+		cr1 |= MXC_UARTUCR1_RTSDEN;
+		writel(cr1, umxc->port.membase + MXC_UARTUCR1);
+	}
+	cr3 = readl(umxc->port.membase + MXC_UARTUCR3);
+	cr3 |= MXC_UARTUCR3_DTRDEN;
+	if (umxc->flags & MXC_UART_MODE_DTE) {
+		cr3 |= MXC_UARTUCR3_DCD | MXC_UARTUCR3_RI;
+	}
+	writel(cr3, umxc->port.membase + MXC_UARTUCR3);
+}
+
+/*
+ * Called from the interrupt service routine if the status bit
+ * indicates that the receive fifo data level is above the set threshold. The
+ * function reads the character and queues them into the TTY layers read
+ * buffer. The function also looks for break characters, parity and framing
+ * errors in the received character and sets the appropriate flag in the TTY
+ * receive buffer.
+ */
+static void mxcuart_rx_chars(struct uart_mxc_port * umxc)
+{
+	struct tty_struct *tty = umxc->port.info->tty;
+	unsigned int ch, sr2;
+	unsigned int status, flag, max_count = 256;
+
+	sr2 = readl(umxc->port.membase + MXC_UARTUSR2);
+	while (((sr2 & MXC_UARTUSR2_RDR) == 1) && (max_count-- > 0)) {
+		ch = readl(umxc->port.membase + MXC_UARTURXD);
+
+		flag = TTY_NORMAL;
+		status = ch | UART_CREAD_BIT;
+		ch &= 0xFF;	/* Clear the upper bits */
+		umxc->port.icount.rx++;
+
+		/*
+		 * Check to see if there is an  error in the received
+		 * character. Perform the appropriate actions based on the
+		 * error bit that was set.
+		 */
+		if (status & MXC_UARTURXD_ERR) {
+			if (status & MXC_UARTURXD_BRK) {
+				/*
+				 * Clear the frame and parity error bits
+				 * as these always get set on receiving a
+				 * break character
+				 */
+				status &= ~(MXC_UARTURXD_FRMERR |
+					    MXC_UARTURXD_PRERR);
+				umxc->port.icount.brk++;
+				if (uart_handle_break(&umxc->port)) {
+					goto ignore_char;
+				}
+			} else if (status & MXC_UARTURXD_FRMERR) {
+				umxc->port.icount.frame++;
+			} else if (status & MXC_UARTURXD_PRERR) {
+				umxc->port.icount.parity++;
+			}
+			if (status & MXC_UARTURXD_OVRRUN) {
+				umxc->port.icount.overrun++;
+			}
+
+			status &= umxc->port.read_status_mask;
+
+			if (status & MXC_UARTURXD_BRK) {
+				flag = TTY_BREAK;
+			} else if (status & MXC_UARTURXD_FRMERR) {
+				flag = TTY_PARITY;
+			} else if (status & MXC_UARTURXD_PRERR) {
+				flag = TTY_FRAME;
+			}
+		}
+
+		if (uart_handle_sysrq_char(&umxc->port, ch)) {
+			goto ignore_char;
+		}
+
+		uart_insert_char(&umxc->port, status, MXC_UARTURXD_OVRRUN, ch,
+				 flag);
+	      ignore_char:
+		sr2 = readl(umxc->port.membase + MXC_UARTUSR2);
+	}
+	tty_flip_buffer_push(tty);
+}
+
+/*
+ * This function is called from the interrupt service routine if the status bit
+ * indicates that the transmit fifo is emptied below its set threshold and
+ * requires data. The function pulls characters from the TTY layers write
+ * buffer and writes it out to the UART transmit fifo.
+ */
+static void mxcuart_tx_chars(struct uart_mxc_port * umxc)
+{
+	struct circ_buf *xmit = &umxc->port.info->xmit;
+	int count;
+
+	/*
+	 * Transmit the XON/XOFF character if required
+	 */
+	if (umxc->port.x_char) {
+		writel(umxc->port.x_char, umxc->port.membase + MXC_UARTUTXD);
+		umxc->port.icount.tx++;
+		umxc->port.x_char = 0;
+		return;
+	}
+
+	/*
+	 * Check to see if there is any data to be sent and that the
+	 * port has not been currently stopped by anything.
+	 */
+	if (uart_circ_empty(xmit) || uart_tx_stopped(&umxc->port)) {
+		mxcuart_stop_tx(&umxc->port);
+		return;
+	}
+
+	count = umxc->port.fifosize - umxc->tx_threshold;
+	do {
+		writel(xmit->buf[xmit->tail],
+		       umxc->port.membase + MXC_UARTUTXD);
+		xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
+		umxc->port.icount.tx++;
+		if (uart_circ_empty(xmit)) {
+			break;
+		}
+	} while (--count > 0);
+
+	/*
+	 * Check to see if we have flushed enough characters to ask for more
+	 * to be sent to us, if so, we notify the user space that we can
+	 * accept more data
+	 */
+	if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) {
+		uart_write_wakeup(&umxc->port);
+	}
+
+	if (uart_circ_empty(xmit)) {
+		mxcuart_stop_tx(&umxc->port);
+	}
+}
+
+/*
+ * This function is called from the interrupt service routine if there is a
+ * change in the modem signals. This function handles these signal changes and
+ * also clears the appropriate status register bits.
+ */
+static void mxcuart_modem_status(struct uart_mxc_port * umxc, unsigned int sr1,
+				 unsigned int sr2)
+{
+	if (umxc->flags & MXC_UART_MODE_DTE) {
+		if (sr2 & MXC_UARTUSR2_DCDDELT) {
+			uart_handle_dcd_change(&umxc->port,
+					       !(sr2 & MXC_UARTUSR2_DCDIN));
+		}
+		if (sr2 & MXC_UARTUSR2_RIDELT) {
+			umxc->port.icount.rng++;
+		}
+	}
+	if (sr1 & MXC_UARTUSR1_DTRD) {
+		umxc->port.icount.dsr++;
+	}
+	if ((umxc->hardware_flow == 0) && (sr1 & MXC_UARTUSR1_RTSD)) {
+		uart_handle_cts_change(&umxc->port, sr1 & MXC_UARTUSR1_RTSS);
+	}
+
+	wake_up_interruptible(&umxc->port.info->delta_msr_wait);
+}
+
+/*
+ * Interrupt service routine registered to handle the muxed ANDed interrupts.
+ * This routine is registered only in the case where the UART interrupts are
+ * muxed.
+ */
+static irqreturn_t mxcuart_int(int irq, void *dev_id)
+{
+	struct uart_mxc_port *umxc = dev_id;
+	unsigned int sr1, sr2, cr1, cr;
+	unsigned int pass_counter = MXC_ISR_PASS_LIMIT;
+	unsigned int term_cond = 0;
+	int handled = 0;
+
+	sr1 = readl(umxc->port.membase + MXC_UARTUSR1);
+	sr2 = readl(umxc->port.membase + MXC_UARTUSR2);
+	cr1 = readl(umxc->port.membase + MXC_UARTUCR1);
+
+	do {
+		/* Clear the bits that triggered the interrupt */
+		writel(sr1, umxc->port.membase + MXC_UARTUSR1);
+		writel(sr2, umxc->port.membase + MXC_UARTUSR2);
+		/*
+		 * Read if there is data available
+		 */
+		if (sr2 & MXC_UARTUSR2_RDR) {
+			mxcuart_rx_chars(umxc);
+		}
+
+		if ((sr1 & (MXC_UARTUSR1_RTSD | MXC_UARTUSR1_DTRD)) ||
+		    (sr2 & (MXC_UARTUSR2_DCDDELT | MXC_UARTUSR2_RIDELT))) {
+			mxcuart_modem_status(umxc, sr1, sr2);
+		}
+
+		/*
+		 * Send data if there is data to be sent
+		 */
+		if ((cr1 & MXC_UARTUCR1_TRDYEN) && (sr1 & MXC_UARTUSR1_TRDY)) {
+			/* Echo cancellation for IRDA Transmit chars */
+			if ((umxc->flags & MXC_UART_MODE_IRDA) && echo_cancel) {
+				/* Disable the receiver */
+				cr = readl(umxc->port.membase + MXC_UARTUCR2);
+				cr &= ~MXC_UARTUCR2_RXEN;
+				writel(cr, umxc->port.membase + MXC_UARTUCR2);
+				/* Enable Transmit complete intr to reenable RX */
+				cr = readl(umxc->port.membase + MXC_UARTUCR4);
+				cr |= MXC_UARTUCR4_TCEN;
+				writel(cr, umxc->port.membase + MXC_UARTUCR4);
+			}
+			mxcuart_tx_chars(umxc);
+		}
+
+		if (pass_counter-- == 0) {
+			break;
+		}
+
+		sr1 = readl(umxc->port.membase + MXC_UARTUSR1);
+		sr2 = readl(umxc->port.membase + MXC_UARTUSR2);
+
+		/* Is the transmit complete to reenable the receiver? */
+		if ((umxc->flags & MXC_UART_MODE_IRDA) && echo_cancel) {
+			if (sr2 & MXC_UARTUSR2_TXDC) {
+				cr = readl(umxc->port.membase + MXC_UARTUCR2);
+				cr |= MXC_UARTUCR2_RXEN;
+				writel(cr, umxc->port.membase + MXC_UARTUCR2);
+				/* Disable the Transmit complete interrupt bit */
+				cr = readl(umxc->port.membase + MXC_UARTUCR4);
+				cr &= ~MXC_UARTUCR4_TCEN;
+				writel(cr, umxc->port.membase + MXC_UARTUCR4);
+			}
+		}
+
+		/*
+		 * If there is no data to send or receive and if there is no
+		 * change in the modem status signals then quit the routine
+		 */
+		term_cond = sr1 & (MXC_UARTUSR1_RTSD | MXC_UARTUSR1_DTRD);
+		term_cond |= sr2 & (MXC_UARTUSR2_RDR | MXC_UARTUSR2_DCDDELT);
+		term_cond |= !(sr2 & MXC_UARTUSR2_TXFE);
+	} while (term_cond > 0);
+
+	handled = 1;
+	return IRQ_RETVAL(handled);
+}
+
+/*
+ * Interrupt service routine registered to handle the transmit interrupts. This
+ * routine is registered only in the case where the UART interrupts are not
+ * muxed.
+ */
+static irqreturn_t mxcuart_tx_int(int irq, void *dev_id)
+{
+	struct uart_mxc_port *umxc = dev_id;
+	int handled = 0;
+	unsigned int sr2, cr;
+
+	/* Echo cancellation for IRDA Transmit chars */
+	if ((umxc->flags & MXC_UART_MODE_IRDA) && echo_cancel) {
+		/* Disable the receiver */
+		cr = readl(umxc->port.membase + MXC_UARTUCR2);
+		cr &= ~MXC_UARTUCR2_RXEN;
+		writel(cr, umxc->port.membase + MXC_UARTUCR2);
+		/* Enable Transmit complete to reenable receiver */
+		cr = readl(umxc->port.membase + MXC_UARTUCR4);
+		cr |= MXC_UARTUCR4_TCEN;
+		writel(cr, umxc->port.membase + MXC_UARTUCR4);
+	}
+
+	mxcuart_tx_chars(umxc);
+
+	/* Is the transmit complete to reenable the receiver? */
+	if ((umxc->flags & MXC_UART_MODE_IRDA) && echo_cancel) {
+		sr2 = readl(umxc->port.membase + MXC_UARTUSR2);
+		if (sr2 & MXC_UARTUSR2_TXDC) {
+			cr = readl(umxc->port.membase + MXC_UARTUCR2);
+			cr |= MXC_UARTUCR2_RXEN;
+			writel(cr, umxc->port.membase + MXC_UARTUCR2);
+			/* Disable the Transmit complete interrupt bit */
+			cr = readl(umxc->port.membase + MXC_UARTUCR4);
+			cr &= ~MXC_UARTUCR4_TCEN;
+			writel(cr, umxc->port.membase + MXC_UARTUCR4);
+		}
+	}
+
+	handled = 1;
+
+	return IRQ_RETVAL(handled);
+}
+
+/*
+ * Interrupt service routine registered to handle the receive interrupts. This
+ * routine is registered only in the case where the UART interrupts are not
+ * muxed.
+ */
+static irqreturn_t mxcuart_rx_int(int irq, void *dev_id)
+{
+	struct uart_mxc_port *umxc = dev_id;
+	int handled = 0;
+
+	/* Clear the aging timer bit */
+	writel(MXC_UARTUSR1_AGTIM, umxc->port.membase + MXC_UARTUSR1);
+	mxcuart_rx_chars(umxc);
+	handled = 1;
+
+	return IRQ_RETVAL(handled);
+}
+
+/*
+ * Interrupt service routine registered to handle the master interrupts. This
+ * routine is registered only in the case where the UART interrupts are not
+ * muxed.
+ */
+static irqreturn_t mxcuart_mint_int(int irq, void *dev_id)
+{
+	struct uart_mxc_port *umxc = dev_id;
+	int handled = 0;
+	unsigned int sr1, sr2;
+
+	sr1 = readl(umxc->port.membase + MXC_UARTUSR1);
+	sr2 = readl(umxc->port.membase + MXC_UARTUSR2);
+	/* Clear the modem status interrupt bits */
+	writel(MXC_UARTUSR1_RTSD | MXC_UARTUSR1_DTRD,
+	       umxc->port.membase + MXC_UARTUSR1);
+	writel(MXC_UARTUSR2_DCDDELT | MXC_UARTUSR2_RIDELT,
+	       umxc->port.membase + MXC_UARTUSR2);
+	mxcuart_modem_status(umxc, sr1, sr2);
+	handled = 1;
+
+	return IRQ_RETVAL(handled);
+}
+
+/*
+ * Called by the core driver to test whether the transmitter
+ * fifo and shift register for the UART port are empty.
+ */
+static unsigned int mxcuart_tx_empty(struct uart_port *port)
+{
+	unsigned int sr2;
+	unsigned long flags;
+
+	spin_lock_irqsave(&port->lock, flags);
+	sr2 = readl(port->membase + MXC_UARTUSR2);
+	spin_unlock_irqrestore(&port->lock, flags);
+
+	return sr2 & MXC_UARTUSR2_TXDC ? TIOCSER_TEMT : 0;
+}
+
+/* Called to get the current status of the modem input signals */
+static unsigned int mxcuart_get_mctrl(struct uart_port *port)
+{
+	struct uart_mxc_port *umxc = (struct uart_mxc_port *) port;
+	unsigned int result = 0;
+	unsigned int sr1, sr2;
+
+	sr1 = readl(umxc->port.membase + MXC_UARTUSR1);
+	sr2 = readl(umxc->port.membase + MXC_UARTUSR2);
+
+	if (sr1 & MXC_UARTUSR1_RTSS) {
+		result |= TIOCM_CTS;
+	}
+	if (umxc->flags & MXC_UART_MODE_DTE) {
+		if (!(sr2 & MXC_UARTUSR2_DCDIN)) {
+			result |= TIOCM_CAR;
+		}
+		if (!(sr2 & MXC_UARTUSR2_RIIN)) {
+			result |= TIOCM_RI;
+		}
+	}
+	return result;
+}
+
+/* Called by the core driver to set the state of the modem control lines */
+static void mxcuart_set_mctrl(struct uart_port *port, unsigned int mctrl)
+{
+	struct uart_mxc_port *umxc = (struct uart_mxc_port *) port;
+	unsigned int cr2 = 0, cr3 = 0, uts = 0;
+
+	cr2 = readl(port->membase + MXC_UARTUCR2);
+	cr3 = readl(port->membase + MXC_UARTUCR3);
+	uts = readl(port->membase + MXC_UARTUTS);
+
+	if (mctrl & TIOCM_RTS) {
+		/*
+		 * Return to hardware-driven hardware flow control if the
+		 * option is enabled
+		 */
+		if (umxc->hardware_flow == 1) {
+			cr2 |= MXC_UARTUCR2_CTSC;
+		} else {
+			cr2 |= MXC_UARTUCR2_CTS;
+			cr2 &= ~MXC_UARTUCR2_CTSC;
+		}
+	} else {
+		cr2 &= ~(MXC_UARTUCR2_CTS | MXC_UARTUCR2_CTSC);
+	}
+	writel(cr2, port->membase + MXC_UARTUCR2);
+
+	if (mctrl & TIOCM_DTR) {
+		cr3 |= MXC_UARTUCR3_DSR;
+	} else {
+		cr3 &= ~MXC_UARTUCR3_DSR;
+	}
+	writel(cr3, port->membase + MXC_UARTUCR3);
+
+	if (mctrl & TIOCM_LOOP) {
+		if (umxc->flags & MXC_UART_MODE_IRDA) {
+			echo_cancel = 0;
+		} else {
+			uts |= MXC_UARTUTS_LOOP;
+		}
+	} else {
+		if (umxc->flags & MXC_UART_MODE_IRDA) {
+			echo_cancel = 1;
+		} else {
+			uts &= ~MXC_UARTUTS_LOOP;
+		}
+	}
+	writel(uts, port->membase + MXC_UARTUTS);
+}
+
+/* Called by the core driver to control the transmission of the break signal */
+static void mxcuart_break_ctl(struct uart_port *port, int break_state)
+{
+	unsigned int cr1;
+	unsigned long flags;
+
+	spin_lock_irqsave(&port->lock, flags);
+	cr1 = readl(port->membase + MXC_UARTUCR1);
+	if (break_state == -1) {
+		cr1 |= MXC_UARTUCR1_SNDBRK;
+	} else {
+		cr1 &= ~MXC_UARTUCR1_SNDBRK;
+	}
+	writel(cr1, port->membase + MXC_UARTUCR1);
+	spin_unlock_irqrestore(&port->lock, flags);
+}
+
+/* DMA callback, this method is called when the DMA buffer has received its data */
+static void mxcuart_dmaread_callback(void *arg, int error, unsigned int cnt)
+{
+	struct uart_mxc_port *umxc = arg;
+	struct tty_struct *tty = umxc->port.info->tty;
+	int buff_id, flip_cnt, num_bufs;
+	mxc_dma_requestbuf_t readchnl_request;
+	struct mxc_uart_rxdmamap *rx_buf_elem = NULL;
+
+	num_bufs = umxc->dma_rxbuf_size / RXDMA_BUFF_SIZE;
+	/* Clear the aging timer bit */
+	writel(MXC_UARTUSR1_AGTIM, umxc->port.membase + MXC_UARTUSR1);
+
+	buff_id = umxc->dma_rxbuf_id;
+
+	if ((umxc->dma_rxbuf_id += 1) >= num_bufs) {
+		umxc->dma_rxbuf_id = 0;
+	}
+
+	rx_buf_elem = (struct mxc_uart_rxdmamap *) (umxc->rx_dmamap + buff_id);
+
+	if (error == MXC_DMA_TRANSFER_ERROR) {
+		goto drop_data;
+	}
+
+	flip_cnt = tty_buffer_request_room(tty, cnt);
+	/* Check for space availability in the TTY Flip buffer */
+	if (flip_cnt <= 0) {
+		goto drop_data;
+	}
+
+	tty_insert_flip_string(tty, rx_buf_elem->rx_buf, flip_cnt);
+	tty_flip_buffer_push(tty);
+
+	umxc->port.info->tty->real_raw = 1;
+drop_data:
+	readchnl_request.src_addr = umxc->port.mapbase;
+	readchnl_request.dst_addr = rx_buf_elem->rx_handle;
+	readchnl_request.num_of_bytes = RXDMA_BUFF_SIZE;
+	mxc_dma_config(dma_list[umxc->port.line].rd_channel, &readchnl_request,
+			1, MXC_DMA_MODE_READ);
+	mxc_dma_enable(dma_list[umxc->port.line].rd_channel);
+}
+
+/* Allocates DMA read and write channels */
+static int mxcuart_initdma(dma_info * d_info, struct uart_mxc_port * umxc)
+{
+	int ret = 0, rxbufs, i, j;
+	mxc_dma_requestbuf_t *readchnl_reqelem;
+	struct mxc_uart_rxdmamap *rx_buf_elem;
+
+	/* Request for the read and write channels */
+	d_info->rd_channel = mxc_dma_request(umxc->dma_rx_id, "MXC UART Read");
+	if (d_info->rd_channel < 0) {
+		printk(KERN_ERR "MXC UART: Cannot allocate DMA read channel\n");
+		return -1;
+	} else {
+		d_info->wr_channel =
+		    mxc_dma_request(umxc->dma_tx_id, "MXC UART Write");
+		if (d_info->wr_channel < 0) {
+			mxc_dma_free(d_info->rd_channel);
+			printk(KERN_ERR
+			       "MXC UART: Cannot allocate DMA write channel\n");
+			return -1;
+		}
+	}
+
+	/* Allocate the DMA Transmit Buffer */
+	if ((umxc->tx_buf = kmalloc(TXDMA_BUFF_SIZE, GFP_KERNEL)) == NULL) {
+		ret = -1;
+		goto err_dma_tx_buff;
+	}
+	rxbufs = umxc->dma_rxbuf_size / RXDMA_BUFF_SIZE;
+	/* Allocate the DMA Virtual Receive Buffer */
+	if ((umxc->rx_dmamap = kmalloc(rxbufs * sizeof(struct mxc_uart_rxdmamap),
+				       GFP_KERNEL)) == NULL) {
+		ret = -1;
+		goto err_dma_rx_buff;
+	}
+
+	/* Allocate the DMA Receive Request structures */
+	if ((readchnl_reqelem =
+	     kmalloc(rxbufs * sizeof(mxc_dma_requestbuf_t),
+		     GFP_KERNEL)) == NULL) {
+		ret = -1;
+		goto err_request;
+	}
+
+	for (i = 0; i < rxbufs; i++) {
+		rx_buf_elem = (struct mxc_uart_rxdmamap *) (umxc->rx_dmamap + i);
+		rx_buf_elem->rx_buf =
+		    dma_alloc_coherent(NULL, RXDMA_BUFF_SIZE,
+				       &rx_buf_elem->rx_handle, GFP_DMA);
+		if (rx_buf_elem->rx_buf == NULL) {
+			for (j = 0; j < i; j++) {
+				rx_buf_elem =
+				    (struct mxc_uart_rxdmamap *) (umxc->rx_dmamap + j);
+				dma_free_coherent(NULL, RXDMA_BUFF_SIZE,
+						  rx_buf_elem->rx_buf,
+						  rx_buf_elem->rx_handle);
+			}
+			ret = -1;
+			goto cleanup;
+		}
+	}
+
+	umxc->dma_rxbuf_id = 0;
+	/* Setup the DMA read request structures */
+	for (i = 0; i < rxbufs; i++) {
+		rx_buf_elem = (struct mxc_uart_rxdmamap *) (umxc->rx_dmamap + i);
+		(readchnl_reqelem + i)->src_addr = umxc->port.mapbase;
+		(readchnl_reqelem + i)->dst_addr = rx_buf_elem->rx_handle;
+		(readchnl_reqelem + i)->num_of_bytes = RXDMA_BUFF_SIZE;
+	}
+	mxc_dma_config(d_info->rd_channel, readchnl_reqelem, rxbufs,
+		       MXC_DMA_MODE_READ);
+	mxc_dma_callback_set(d_info->rd_channel, mxcuart_dmaread_callback,
+			     umxc);
+	mxc_dma_callback_set(d_info->wr_channel, mxcuart_dma_writecallback,
+			     umxc);
+
+	/* Start the read channel */
+	mxc_dma_enable(d_info->rd_channel);
+	kfree(readchnl_reqelem);
+	tasklet_init(&d_info->dma_tx_tasklet, dma_tx_do_tasklet,
+		     (unsigned long)umxc);
+	d_info->dma_txchnl_inuse = 0;
+	return ret;
+      cleanup:
+	kfree(readchnl_reqelem);
+      err_request:
+	kfree(umxc->rx_dmamap);
+      err_dma_rx_buff:
+	kfree(umxc->tx_buf);
+      err_dma_tx_buff:
+	mxc_dma_free(d_info->rd_channel);
+	mxc_dma_free(d_info->wr_channel);
+
+	return ret;
+}
+
+/* Stops DMA and frees the DMA resources */
+static void mxcuart_freedma(dma_info * d_info, struct uart_mxc_port * umxc)
+{
+	int i, rxbufs;
+	struct mxc_uart_rxdmamap *rx_buf_elem;
+
+	rxbufs = umxc->dma_rxbuf_size / RXDMA_BUFF_SIZE;
+
+	for (i = 0; i < rxbufs; i++) {
+		rx_buf_elem = (struct mxc_uart_rxdmamap *) (umxc->rx_dmamap + i);
+		dma_free_coherent(NULL, RXDMA_BUFF_SIZE,
+				  rx_buf_elem->rx_buf, rx_buf_elem->rx_handle);
+	}
+	kfree(umxc->rx_dmamap);
+	kfree(umxc->tx_buf);
+	mxc_dma_free(d_info->rd_channel);
+	mxc_dma_free(d_info->wr_channel);
+}
+
+/* Free the interrupts */
+static void mxcuart_free_interrupts(struct uart_mxc_port * umxc)
+{
+	free_irq(umxc->port.irq, umxc);
+	if (umxc->ints_muxed == 0) {
+		free_irq(umxc->irqs[0], umxc);
+		free_irq(umxc->irqs[1], umxc);
+	}
+}
+
+/* Calculate the maximum baud rate supported */
+static void mxcuart_get_maxbaud(unsigned long per_clk, unsigned long *max_baud)
+{
+	unsigned long baud;
+
+	baud = per_clk / 16;
+	*max_baud = baud;
+}
+
+/* Calculate and set the UART port clock value */
+static void mxcuart_set_ref_freq(struct uart_mxc_port * umxc, unsigned long per_clk,
+				unsigned int req_baud, int *div)
+{
+	int d = 1;
+
+	d = per_clk / ((req_baud * 16) + 1000);
+	if (d > 6) {
+		d = 6;
+	}
+
+	umxc->port.uartclk = per_clk / d;
+	/*
+	 * Set the ONEMS register that is used by IR special case bit and
+	 * the Escape character detect logic
+	 */
+	writel(umxc->port.uartclk / 1000, umxc->port.membase + MXC_UARTONEMS);
+	*div = d;
+}
+
+/*
+ * Called by the core driver to initialize the low-level
+ * driver. The function grabs the interrupt resources and registers its
+ * interrupt service routines. It then initializes the IOMUX registers to
+ * configure the pins for UART signals and finally initializes the various
+ * UART registers and enables the port for reception.
+ */
+static int mxcuart_startup(struct uart_port *port)
+{
+	struct uart_mxc_port *umxc = (struct uart_mxc_port *) port;
+	int retval;
+	unsigned int cr, cr1 = 0, cr2 = 0, ufcr = 0;
+
+	/*
+	 * Some UARTs need separate registrations for the interrupts as
+	 * they do not take the muxed interrupt output to the ARM core
+	 */
+	if (umxc->ints_muxed == 1) {
+		retval = request_irq(umxc->port.irq, mxcuart_int, 0,
+				     "mxcintuart", umxc);
+		if (retval != 0) {
+			return retval;
+		}
+	} else {
+		retval = request_irq(umxc->port.irq, mxcuart_tx_int,
+				     0, "mxcintuart", umxc);
+		if (retval != 0) {
+			return retval;
+		} else {
+			retval = request_irq(umxc->irqs[0], mxcuart_rx_int,
+					     0, "mxcintuart", umxc);
+			if (retval != 0) {
+				free_irq(umxc->port.irq, umxc);
+				return retval;
+			} else {
+				retval =
+				    request_irq(umxc->irqs[1], mxcuart_mint_int,
+						0, "mxcintuart", umxc);
+				if (retval != 0) {
+					free_irq(umxc->port.irq, umxc);
+					free_irq(umxc->irqs[0], umxc);
+					return retval;
+				}
+			}
+		}
+	}
+
+	/* Initialize the DMA if we need SDMA data transfer */
+	if (umxc->dma_enabled == 1) {
+		retval = mxcuart_initdma(dma_list + umxc->port.line, umxc);
+		if (retval != 0) {
+			printk
+			    (KERN_ERR
+			     "MXC UART: Failed to initialize DMA for UART %d\n",
+			     umxc->port.line);
+			mxcuart_free_interrupts(umxc);
+			return retval;
+		}
+#if 0 /* FIXME */
+		/* Configure the GPR register to receive SDMA events */
+		config_uartdma_event(umxc->port.line);
+#endif
+	}
+
+	/*
+	 * Clear Status Registers 1 and 2
+	 */
+	writel(0xFFFF, umxc->port.membase + MXC_UARTUSR1);
+	writel(0xFFFF, umxc->port.membase + MXC_UARTUSR2);
+
+	/* Configure the IOMUX for the UART */
+	umxc->init(NULL); /* FIXME */
+
+	/*
+	 * Set the transceiver invert bits if required
+	 */
+	if (umxc->flags & MXC_UART_MODE_IRDA) {
+		echo_cancel = 1;
+		writel(((umxc->flags & MXC_UART_MODE_IRDA_RX_INV) ? MXC_UARTUCR4_INVR : 0) |
+			MXC_UARTUCR4_IRSC, umxc->port.membase
+		       + MXC_UARTUCR4);
+		writel(umxc->rxd_mux |
+			((umxc->flags & MXC_UART_MODE_IRDA_TX_INV) ? MXC_UARTUCR3_INVT : 0),
+		       umxc->port.membase + MXC_UARTUCR3);
+	} else {
+		writel(umxc->rxd_mux, umxc->port.membase + MXC_UARTUCR3);
+	}
+
+	/*
+	 * Initialize UCR1,2 and UFCR registers
+	 */
+	if (umxc->dma_enabled == 1) {
+		cr2 = (MXC_UARTUCR2_TXEN | MXC_UARTUCR2_RXEN);
+	} else {
+		cr2 =
+		    (MXC_UARTUCR2_ATEN | MXC_UARTUCR2_TXEN | MXC_UARTUCR2_RXEN);
+	}
+
+	writel(cr2, umxc->port.membase + MXC_UARTUCR2);
+	/* Wait till we are out of software reset */
+	do {
+		cr = readl(umxc->port.membase + MXC_UARTUCR2);
+	} while (!(cr & MXC_UARTUCR2_SRST));
+
+	if (umxc->flags & MXC_UART_MODE_DTE) {
+		ufcr |= ((umxc->tx_threshold << MXC_UARTUFCR_TXTL_OFFSET) |
+			 MXC_UARTUFCR_DCEDTE | MXC_UARTUFCR_RFDIV | umxc->
+			 rx_threshold);
+	} else {
+		ufcr |= ((umxc->tx_threshold << MXC_UARTUFCR_TXTL_OFFSET) |
+			 MXC_UARTUFCR_RFDIV | umxc->rx_threshold);
+	}
+	writel(ufcr, umxc->port.membase + MXC_UARTUFCR);
+
+	/*
+	 * Finally enable the UART and the Receive interrupts
+	 */
+	if (umxc->flags & MXC_UART_MODE_IRDA) {
+		cr1 |= MXC_UARTUCR1_IREN;
+	}
+	if (umxc->dma_enabled == 1) {
+		cr1 |= (MXC_UARTUCR1_RXDMAEN | MXC_UARTUCR1_ATDMAEN |
+			MXC_UARTUCR1_UARTEN);
+	} else {
+		cr1 |= (MXC_UARTUCR1_RRDYEN | MXC_UARTUCR1_UARTEN);
+	}
+	writel(cr1, umxc->port.membase + MXC_UARTUCR1);
+
+	return 0;
+}
+
+/* Called by the core driver for the low-level driver to free its resources */
+static void mxcuart_shutdown(struct uart_port *port)
+{
+	struct uart_mxc_port *umxc = (struct uart_mxc_port *) port;
+
+	/* Disable the IOMUX for the UART */
+	umxc->exit(NULL); /* FIXME */
+
+	mxcuart_free_interrupts(umxc);
+	/* Disable all interrupts, port and break condition */
+	writel(0, umxc->port.membase + MXC_UARTUCR1);
+	writel(0, umxc->port.membase + MXC_UARTUCR3);
+	if (umxc->dma_enabled == 1) {
+		mxcuart_freedma(dma_list + umxc->port.line, umxc);
+	}
+}
+
+/* Called by the core driver to change the UART parameters */
+static void mxcuart_set_termios(struct uart_port *port,
+				struct ktermios *termios, struct ktermios *old)
+{
+	struct uart_mxc_port *umxc = (struct uart_mxc_port *) port;
+	unsigned int cr4 = 0, cr2 = 0, sr1, ufcr;
+	u_int num, denom, baud;
+	u_int cr2_mask;		/* Used to add the changes to CR2 */
+	unsigned long flags, max_baud, per_clk;
+	int div;
+
+	cr2_mask = ~(MXC_UARTUCR2_IRTS | MXC_UARTUCR2_CTSC | MXC_UARTUCR2_PREN |
+		     MXC_UARTUCR2_PROE | MXC_UARTUCR2_STPB | MXC_UARTUCR2_WS);
+
+	per_clk = clk_get_rate(umxc->clk);
+
+	mxcuart_get_maxbaud(per_clk, &max_baud);
+	/*
+	 * Ask the core to get the baudrate, if requested baudrate is not
+	 * between max and min, then either use the baudrate in old termios
+	 * setting. If it's still invalid, we try 9600 baud.
+	 */
+	baud = uart_get_baud_rate(&umxc->port, termios, old, 0, max_baud);
+	/* Set the Reference frequency divider */
+	mxcuart_set_ref_freq(umxc, per_clk, baud, &div);
+
+	/* Byte size, default is 8-bit mode */
+	switch (termios->c_cflag & CSIZE) {
+	case CS7:
+		cr2 = 0;
+		break;
+	default:
+		cr2 = MXC_UARTUCR2_WS;
+		break;
+	}
+	/* Check to see if we need 2 Stop bits */
+	if (termios->c_cflag & CSTOPB) {
+		cr2 |= MXC_UARTUCR2_STPB;
+	}
+
+	/* Check to see if we need Parity checking */
+	if (termios->c_cflag & PARENB) {
+		cr2 |= MXC_UARTUCR2_PREN;
+		if (termios->c_cflag & PARODD) {
+			cr2 |= MXC_UARTUCR2_PROE;
+		}
+	}
+	spin_lock_irqsave(&umxc->port.lock, flags);
+
+	ufcr = readl(umxc->port.membase + MXC_UARTUFCR);
+	ufcr = (ufcr & (~MXC_UARTUFCR_RFDIV_MASK)) |
+	    ((6 - div) << MXC_UARTUFCR_RFDIV_OFFSET);
+	writel(ufcr, umxc->port.membase + MXC_UARTUFCR);
+
+	/*
+	 * Update the per-port timeout
+	 */
+	uart_update_timeout(&umxc->port, termios->c_cflag, baud);
+
+	umxc->port.read_status_mask = MXC_UARTURXD_OVRRUN;
+	/*
+	 * Enable appropriate events to be passed to the TTY layer
+	 */
+	if (termios->c_iflag & INPCK) {
+		umxc->port.read_status_mask |= MXC_UARTURXD_FRMERR |
+		    MXC_UARTURXD_PRERR;
+	}
+	if (termios->c_iflag & (BRKINT | PARMRK)) {
+		umxc->port.read_status_mask |= MXC_UARTURXD_BRK;
+	}
+
+	/*
+	 * Characters to ignore
+	 */
+	umxc->port.ignore_status_mask = 0;
+	if (termios->c_iflag & IGNPAR) {
+		umxc->port.ignore_status_mask |= MXC_UARTURXD_FRMERR |
+		    MXC_UARTURXD_PRERR;
+	}
+	if (termios->c_iflag & IGNBRK) {
+		umxc->port.ignore_status_mask |= MXC_UARTURXD_BRK;
+		/*
+		 * If we are ignoring parity and break indicators,
+		 * ignore overruns too (for real raw support)
+		 */
+		if (termios->c_iflag & IGNPAR) {
+			umxc->port.ignore_status_mask |= MXC_UARTURXD_OVRRUN;
+		}
+	}
+
+	/*
+	 * Ignore all characters if CREAD is not set, still receive characters
+	 * from the port, but throw them away.
+	 */
+	if ((termios->c_cflag & CREAD) == 0) {
+		umxc->port.ignore_status_mask |= UART_CREAD_BIT;
+	}
+
+	cr4 = readl(umxc->port.membase + MXC_UARTUCR4);
+	if (UART_ENABLE_MS(port, termios->c_cflag)) {
+		mxcuart_enable_ms(port);
+		if (umxc->hardware_flow == 1) {
+			cr4 = (cr4 & (~MXC_UARTUCR4_CTSTL_MASK)) |
+			    (umxc->cts_threshold << MXC_UARTUCR4_CTSTL_OFFSET);
+			cr2 |= MXC_UARTUCR2_CTSC;
+		} else {
+			cr2 |= MXC_UARTUCR2_IRTS;
+			sr1 = readl(umxc->port.membase + MXC_UARTUSR1);
+			/* RTS not active, do not transmit */
+			if ((sr1 & MXC_UARTUSR1_RTSS) == 0) {
+				umxc->port.info->tty->hw_stopped = 1;
+			}
+		}
+	} else {
+		cr2 |= MXC_UARTUCR2_IRTS;
+	}
+
+	/* Add Parity, character length and stop bits information */
+	cr2 |= (readl(umxc->port.membase + MXC_UARTUCR2) & cr2_mask);
+	writel(cr2, umxc->port.membase + MXC_UARTUCR2);
+	/*
+	   if (umxc->ir_mode == IRDA) {
+	   ret = mxcuart_setir_special(baud);
+	   if (ret == 0) {
+	   cr4 &= ~MXC_UARTUCR4_IRSC;
+	   } else {
+	   cr4 |= MXC_UARTUCR4_IRSC;
+	   }
+	   } */
+	writel(cr4, umxc->port.membase + MXC_UARTUCR4);
+	/* Set baud rate */
+	num = (baud / 100) - 1;
+	denom = (umxc->port.uartclk / 1600) - 1;
+	if ((denom < 65536) && (umxc->port.uartclk > 1600)) {
+		writel(num, umxc->port.membase + MXC_UARTUBIR);
+		writel(denom, umxc->port.membase + MXC_UARTUBMR);
+	}
+	spin_unlock_irqrestore(&umxc->port.lock, flags);
+}
+
+/*
+ * Called by the core driver to know the UART type.
+ */
+static const char *mxcuart_type(struct uart_port *port)
+{
+	return port->type == PORT_MXC ? "Freescale MXC" : NULL;
+}
+
+/* Called by the core driver to release the memory resources */
+static void mxcuart_release_port(struct uart_port *port)
+{
+}
+
+/* Called by the core driver to request memory resources */
+static int mxcuart_request_port(struct uart_port *port)
+{
+	return 0;
+}
+
+/* Called by the core driver to perform any autoconfiguration */
+static void mxcuart_config_port(struct uart_port *port, int flags)
+{
+	if ((flags & UART_CONFIG_TYPE) && (mxcuart_request_port(port) == 0)) {
+		port->type = PORT_MXC;
+	}
+}
+
+/* Called by the core driver to verify given settings for this port */
+static int mxcuart_verify_port(struct uart_port *port,
+			       struct serial_struct *ser)
+{
+	int ret = 0;
+	if (ser->type != PORT_UNKNOWN && ser->type != PORT_MXC) {
+		ret = -EINVAL;
+	}
+	return ret;
+}
+
+/* Send a high priority XON/XOFF character */
+static void mxcuart_send_xchar(struct uart_port *port, char ch)
+{
+	unsigned long flags;
+
+	port->x_char = ch;
+	if (port->info->tty->hw_stopped) {
+		return;
+	}
+
+	if (ch) {
+		spin_lock_irqsave(&port->lock, flags);
+		port->ops->start_tx(port);
+		spin_unlock_irqrestore(&port->lock, flags);
+	}
+}
+
+/* Enable/disable the MXC UART clocks */
+static void
+mxcuart_pm(struct uart_port *port, unsigned int state, unsigned int oldstate)
+{
+	struct uart_mxc_port *umxc = (struct uart_mxc_port *) port;
+
+	if (state)
+		clk_disable(umxc->clk);
+	else
+		clk_enable(umxc->clk);
+}
+
+/*
+ * This structure contains the pointers to the control functions that are
+ * invoked by the core serial driver to access the UART hardware. The
+ * structure is passed to serial_core.c file during registration.
+ */
+static struct uart_ops mxc_ops = {
+	.tx_empty = mxcuart_tx_empty,
+	.set_mctrl = mxcuart_set_mctrl,
+	.get_mctrl = mxcuart_get_mctrl,
+	.stop_tx = mxcuart_stop_tx,
+	.start_tx = mxcuart_start_tx,
+	.stop_rx = mxcuart_stop_rx,
+	.enable_ms = mxcuart_enable_ms,
+	.break_ctl = mxcuart_break_ctl,
+	.startup = mxcuart_startup,
+	.shutdown = mxcuart_shutdown,
+	.set_termios = mxcuart_set_termios,
+	.type = mxcuart_type,
+	.pm = mxcuart_pm,
+	.release_port = mxcuart_release_port,
+	.request_port = mxcuart_request_port,
+	.config_port = mxcuart_config_port,
+	.verify_port = mxcuart_verify_port,
+	.send_xchar = mxcuart_send_xchar,
+};
+
+#ifdef CONFIG_SERIAL_MXC_CONSOLE
+
+/* Write out a character once the UART is ready */
+static inline void mxcuart_console_write_char(struct uart_port *port, char ch)
+{
+	unsigned int status;
+
+	do {
+		status = readl(port->membase + MXC_UARTUSR1);
+	} while ((status & MXC_UARTUSR1_TRDY) == 0);
+	writel(ch, port->membase + MXC_UARTUTXD);
+}
+
+/* Write the console messages through the UART port */
+static void mxcuart_console_write(struct console *co, const char *s,
+				u_int count)
+{
+	struct uart_port *port = &mxc_ports[co->index]->port;
+	unsigned int status, oldcr1, oldcr2, oldcr3, cr2, cr3;
+	int i;
+
+	/*
+	 * First save the control registers and then disable the interrupts
+	 */
+	oldcr1 = readl(port->membase + MXC_UARTUCR1);
+	oldcr2 = readl(port->membase + MXC_UARTUCR2);
+	oldcr3 = readl(port->membase + MXC_UARTUCR3);
+	cr2 =
+	    oldcr2 & ~(MXC_UARTUCR2_ATEN | MXC_UARTUCR2_RTSEN |
+		       MXC_UARTUCR2_ESCI);
+	cr3 =
+	    oldcr3 & ~(MXC_UARTUCR3_DCD | MXC_UARTUCR3_RI |
+		       MXC_UARTUCR3_DTRDEN);
+	writel(MXC_UARTUCR1_UARTEN, port->membase + MXC_UARTUCR1);
+	writel(cr2, port->membase + MXC_UARTUCR2);
+	writel(cr3, port->membase + MXC_UARTUCR3);
+	/*
+	 * Do each character
+	 */
+	for (i = 0; i < count; i++) {
+		mxcuart_console_write_char(port, s[i]);
+		if (s[i] == '\n') {
+			mxcuart_console_write_char(port, '\r');
+		}
+	}
+	/*
+	 * Finally, wait for the transmitter to become empty
+	 */
+	do {
+		status = readl(port->membase + MXC_UARTUSR2);
+	} while (!(status & MXC_UARTUSR2_TXDC));
+
+	/*
+	 * Restore the control registers
+	 */
+	writel(oldcr1, port->membase + MXC_UARTUCR1);
+	writel(oldcr2, port->membase + MXC_UARTUCR2);
+	writel(oldcr3, port->membase + MXC_UARTUCR3);
+}
+
+/* Initializes the UART port to be used to print console message */
+static int __init mxcuart_console_setup(struct console *co, char *options)
+{
+	struct uart_mxc_port *umxc;
+	int baud = 115200;
+	int bits = 8;
+	int parity = 'n';
+	int flow = 'n';
+	unsigned int cr = 0;
+
+	/*
+	 * Check whether an invalid uart number had been specified, and if
+	 * so, search for the first available port that does have console
+	 * support
+	 */
+	if (co->index >= MXC_UART_NR) {
+		co->index = 0;
+	}
+	console_index = co->index;
+	umxc = mxc_ports[co->index];
+
+	if (umxc == NULL) {
+		return -ENODEV;
+	}
+
+	clk_enable(umxc->clk);
+
+	/* initialize port.lock else oops */
+	spin_lock_init(&umxc->port.lock);
+
+	/*
+	 * Initialize the UART registers
+	 */
+	writel(MXC_UARTUCR1_UARTEN, umxc->port.membase + MXC_UARTUCR1);
+	/* Enable the transmitter and do a software reset */
+	writel(MXC_UARTUCR2_TXEN, umxc->port.membase + MXC_UARTUCR2);
+	/* Wait till we are out of software reset */
+	do {
+		cr = readl(umxc->port.membase + MXC_UARTUCR2);
+	} while (!(cr & MXC_UARTUCR2_SRST));
+
+	writel(0x0, umxc->port.membase + MXC_UARTUCR3);
+	writel(0x0, umxc->port.membase + MXC_UARTUCR4);
+	/* Set TXTL to 2, RXTL to 1 and RFDIV to 2 */
+	cr = 0x0800 | MXC_UARTUFCR_RFDIV | 0x1;
+	if (umxc->flags & MXC_UART_MODE_DTE) {
+		cr |= MXC_UARTUFCR_DCEDTE;
+	}
+	writel(cr, umxc->port.membase + MXC_UARTUFCR);
+	writel(0xFFFF, umxc->port.membase + MXC_UARTUSR1);
+	writel(0xFFFF, umxc->port.membase + MXC_UARTUSR2);
+
+	if (options != NULL) {
+		uart_parse_options(options, &baud, &parity, &bits, &flow);
+	}
+	return uart_set_options(&umxc->port, co, baud, parity, bits, flow);
+}
+
+static struct uart_driver mxc_reg;
+
+/*
+ * This structure contains the pointers to the UART console functions. It is
+ * passed as an argument when registering the console.
+ */
+static struct console mxc_console = {
+	.name = "ttymxc",
+	.write = mxcuart_console_write,
+	.device = uart_console_device,
+	.setup = mxcuart_console_setup,
+	.flags = CON_PRINTBUFFER,
+	.index = -1,
+	.data = &mxc_reg,
+};
+
+static int __init mxcuart_console_init(void)
+{
+	register_console(&mxc_console);
+	return 0;
+}
+
+console_initcall(mxcuart_console_init);
+
+#define MXC_CONSOLE     &mxc_console
+#else
+#define MXC_CONSOLE     NULL
+#endif /* CONFIG_SERIAL_MXC_CONSOLE */
+
+/* Data to pass to the serial_core.c */
+static struct uart_driver mxc_reg = {
+	.owner = THIS_MODULE,
+	.driver_name = "ttymxc",
+	.dev_name = "ttymxc",
+	.major = SERIAL_MXC_MAJOR,
+	.minor = SERIAL_MXC_MINOR,
+	.nr = MXC_UART_NR,
+	.cons = MXC_CONSOLE,
+};
+
+/* Put the UART in a low power state */
+static int mxcuart_suspend(struct platform_device *pdev, pm_message_t state)
+{
+	struct uart_mxc_port *umxc = platform_get_drvdata(pdev);
+
+	if (umxc == NULL) {
+		return 0;	/* skip disabled ports */
+	}
+	uart_suspend_port(&mxc_reg, &umxc->port);
+	if (umxc->port.info && umxc->port.info->flags & UIF_INITIALIZED) {
+		umxc->port.info->tty->hw_stopped = 1;
+	}
+
+	if (device_may_wakeup(&pdev->dev)) {
+		/* UART RTS signal is used as wakeup source */
+		writel(MXC_UARTUCR1_RTSDEN, umxc->port.membase + MXC_UARTUCR1);
+		umxc->init(NULL); /* FIXME */
+	}
+
+	return 0;
+}
+
+/* Bring the UART back from a low power state */
+static int mxcuart_resume(struct platform_device *pdev)
+{
+	struct uart_mxc_port *umxc = platform_get_drvdata(pdev);
+
+	if (umxc == NULL) {
+		return 0;	/* skip disabled ports */
+	}
+	if (umxc->port.info && umxc->port.info->flags & UIF_INITIALIZED) {
+		umxc->port.info->tty->hw_stopped = 0;
+	}
+	if (device_may_wakeup(&pdev->dev)) {
+		writel(0, umxc->port.membase + MXC_UARTUCR1);
+		umxc->exit(NULL); /* FIXME */
+	}
+	uart_resume_port(&mxc_reg, &umxc->port);
+
+	return 0;
+}
+
+/* Platform device/driver probe */
+static int mxcuart_probe(struct platform_device *pdev)
+{
+	int id = pdev->id;
+	int err = 0;
+	struct resource *r;
+
+	r = request_mem_region(pdev->resource[0].start,
+				pdev->resource[0].end - pdev->resource[0].start + 1,
+				"serial_mxc");
+	if (!r) {
+		printk(KERN_ERR "Cannot claim UART area!\n");
+		return -EBUSY;
+	}
+
+	mxc_ports[id] = pdev->dev.platform_data;
+	mxc_ports[id]->port.ops = &mxc_ops;
+
+	mxc_ports[id]->port.dev = &pdev->dev;
+	spin_lock_init(&mxc_ports[id]->port.lock);
+	/* Enable the low latency flag for DMA UART ports */
+	if (mxc_ports[id]->dma_enabled == 1) {
+		mxc_ports[id]->port.flags |= UPF_LOW_LATENCY;
+	}
+
+	mxc_ports[id]->clk = clk_get(&pdev->dev, "uart_clk");
+	clk_enable(mxc_ports[id]->clk);
+	if (mxc_ports[id]->clk == NULL)
+		return -1;
+
+	uart_add_one_port(&mxc_reg, &mxc_ports[id]->port);
+	platform_set_drvdata(pdev, mxc_ports[id]);
+	pdev->dev.power.can_wakeup = 1;
+
+	return err;
+}
+
+/* Platform device/driver remove */
+static int mxcuart_remove(struct platform_device *pdev)
+{
+	struct uart_mxc_port *umxc = platform_get_drvdata(pdev);
+
+	release_region(pdev->resource[0].start,
+		       pdev->resource[0].end - pdev->resource[0].start + 1);
+
+	platform_set_drvdata(pdev, NULL);
+
+	if (umxc) {
+		uart_remove_one_port(&mxc_reg, &umxc->port);
+	}
+	return 0;
+}
+
+static struct platform_driver mxcuart_driver = {
+	.driver = {
+		   .name = "mxcintuart",
+		   },
+	.probe = mxcuart_probe,
+	.remove = mxcuart_remove,
+	.suspend = mxcuart_suspend,
+	.resume = mxcuart_resume,
+};
+
+static int __init mxcuart_init(void)
+{
+	int ret = 0;
+
+	printk(KERN_INFO "Serial: MXC Internal UART driver\n");
+	ret = uart_register_driver(&mxc_reg);
+	if (ret == 0) {
+		/* Register the device driver structure. */
+		ret = platform_driver_register(&mxcuart_driver);
+		if (ret != 0) {
+			uart_unregister_driver(&mxc_reg);
+		}
+	}
+	return ret;
+}
+
+static void __exit mxcuart_exit(void)
+{
+	platform_driver_unregister(&mxcuart_driver);
+	uart_unregister_driver(&mxc_reg);
+}
+
+module_init(mxcuart_init);
+module_exit(mxcuart_exit);
+
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_DESCRIPTION("MXC serial port driver");
+MODULE_LICENSE("GPL");
Index: drivers/serial/Kconfig
===================================================================
--- drivers/serial/Kconfig.orig
+++ drivers/serial/Kconfig
@@ -280,6 +280,30 @@ config SERIAL_8250_RM9K
 
 comment "Non-8250 serial port support"
 
+config SERIAL_MXC
+    tristate "MXC Internal serial port support"
+    depends on ARCH_MXC
+    select SERIAL_CORE
+    help
+      This selects the Freescale Semiconductor MXC Internal UART driver.
+      If unsure, say N.
+
+config SERIAL_MXC_CONSOLE
+    bool "Support for console on a MXC/MX27/MX21 Internal serial port"
+    depends on SERIAL_MXC=y
+    select SERIAL_CORE_CONSOLE
+    help
+	  Say Y here if you wish to use an MXC Internal 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=ttymxc". (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_PL010
 	tristate "ARM AMBA PL010 serial port support"
 	depends on ARM_AMBA && (BROKEN || !ARCH_VERSATILE)
Index: drivers/serial/Makefile
===================================================================
--- drivers/serial/Makefile.orig
+++ drivers/serial/Makefile
@@ -64,3 +64,4 @@ obj-$(CONFIG_SERIAL_UARTLITE) += uartlit
 obj-$(CONFIG_SERIAL_NETX) += netx-serial.o
 obj-$(CONFIG_SERIAL_OF_PLATFORM) += of_serial.o
 obj-$(CONFIG_SERIAL_KS8695) += serial_ks8695.o
+obj-$(CONFIG_SERIAL_MXC) += mxc_uart.o
Index: include/linux/serial_core.h
===================================================================
--- include/linux/serial_core.h.orig
+++ include/linux/serial_core.h
@@ -2,6 +2,7 @@
  *  linux/drivers/char/serial_core.h
  *
  *  Copyright (C) 2000 Deep Blue Solutions Ltd.
+ *  Copyright 2008 Juergen Beisert, kernel@pengutronix.de
  *
  * 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
@@ -149,6 +150,8 @@
 /* Freescale ColdFire */
 #define PORT_MCF	78
 
+/* Freescale Semiconductor MXC fmaily */
+#define PORT_MXC        79
 
 #ifdef __KERNEL__
 
