All of lore.kernel.org
 help / color / mirror / Atom feed
From: Jingchang Lu <b35083@freescale.com>
To: linux-serial@vger.kernel.org
Cc: Jingchang Lu <b35083@freescale.com>,
	shawn.guo@linaro.org, linux-arm-kernel@lists.infradead.org
Subject: [PATCH] tty/serial: add MVF uart driver support
Date: Fri, 12 Apr 2013 15:10:55 +0800	[thread overview]
Message-ID: <1365750655-3530-1-git-send-email-b35083@freescale.com> (raw)

It adds Freescale Vybrid Family uart driver support

Signed-off-by: Jingchang Lu <b35083@freescale.com>
---
 drivers/tty/serial/Kconfig       |   17 +
 drivers/tty/serial/Makefile      |    1 +
 drivers/tty/serial/mvf.c         | 1246 ++++++++++++++++++++++++++++++++++++++
 include/uapi/linux/serial_core.h |    3 +
 4 files changed, 1267 insertions(+)
 create mode 100644 drivers/tty/serial/mvf.c

diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig
index 7e7006f..ab6dc27 100644
--- a/drivers/tty/serial/Kconfig
+++ b/drivers/tty/serial/Kconfig
@@ -574,6 +574,23 @@ config SERIAL_IMX_CONSOLE
 	  your boot loader (lilo or loadlin) about how to pass options to the
 	  kernel at boot time.)
 
+config SERIAL_MVF
+	bool "Freescale Vybrid serial port support"
+	depends on ARCH_MVF
+	select SERIAL_CORE
+	select RATIONAL
+	help
+	  If you have a machine based on a Freescale Vybrid CPU you
+	  can enable its onboard serial port by enabling this option.
+
+config SERIAL_MVF_CONSOLE
+	bool "Console on Vybrid serial port"
+	depends on SERIAL_MVF
+	select SERIAL_CORE_CONSOLE
+	help
+	  If you have enabled the serial port on the Freescale Vybrid family
+	  CPU you can make it the console by answering Y to this option.
+
 config SERIAL_UARTLITE
 	tristate "Xilinx uartlite serial port support"
 	depends on PPC32 || MICROBLAZE || MFD_TIMBERDALE
diff --git a/drivers/tty/serial/Makefile b/drivers/tty/serial/Makefile
index eedfec4..be2ed68 100644
--- a/drivers/tty/serial/Makefile
+++ b/drivers/tty/serial/Makefile
@@ -41,6 +41,7 @@ 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_MVF) += mvf.o
 obj-$(CONFIG_SERIAL_MPC52xx) += mpc52xx_uart.o
 obj-$(CONFIG_SERIAL_ICOM) += icom.o
 obj-$(CONFIG_SERIAL_M32R_SIO) += m32r_sio.o
diff --git a/drivers/tty/serial/mvf.c b/drivers/tty/serial/mvf.c
new file mode 100644
index 0000000..b3b7ffd
--- /dev/null
+++ b/drivers/tty/serial/mvf.c
@@ -0,0 +1,1246 @@
+/*
+ *  Driver for Freescale Vybrid Family serial ports
+ *
+ *  Based on drivers/tty/serial/imx.c
+ *
+ *  Copyright 2012-2013 Freescale Semiconductor, Inc.
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#if defined(CONFIG_SERIAL_MVF_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/dma-mapping.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/pinctrl/consumer.h>
+#include <linux/io.h>
+#include <asm/irq.h>
+#include <linux/platform_data/serial-imx.h>
+#include <linux/platform_data/dma-imx.h>
+
+/* All uart module registers for MVF is 8-bit width */
+#define MXC_UARTBDH		0x00 /* Baud rate reg: high */
+#define MXC_UARTBDL		0x01 /* Baud rate reg: low */
+#define MXC_UARTCR1		0x02 /* Control reg 1 */
+#define MXC_UARTCR2		0x03 /* Control reg 2 */
+#define MXC_UARTSR1		0x04 /* Status reg 1 */
+#define MXC_UARTSR2		0x05 /* Status reg 2 */
+#define MXC_UARTCR3		0x06 /* Control reg 3 */
+#define MXC_UARTDR		0x07 /* Data reg */
+#define MXC_UARTMAR1		0x08 /* Match address reg 1 */
+#define MXC_UARTMAR2		0x09 /* Match address reg 2 */
+#define MXC_UARTCR4		0x0A /* Control reg 4 */
+#define MXC_UARTCR5		0x0B /* Control reg 5 */
+#define MXC_UARTEDR		0x0C /* Extended data reg */
+#define MXC_UARTMODEM		0x0D /* Modem reg */
+#define MXC_UARTIR		0x0E /* Infrared reg */
+#define MXC_UARTPFIFO		0x10 /* FIFO parameter reg */
+#define MXC_UARTCFIFO		0x11 /* FIFO control reg */
+#define MXC_UARTSFIFO		0x12 /* FIFO status reg */
+#define MXC_UARTTWFIFO		0x13 /* FIFO transmit watermark reg */
+#define MXC_UARTTCFIFO		0x14 /* FIFO transmit count reg */
+#define MXC_UARTRWFIFO		0x15 /* FIFO receive watermark reg */
+#define MXC_UARTRCFIFO		0x16 /* FIFO receive count reg */
+#define MXC_UARTC7816		0x18 /* 7816 control reg */
+#define MXC_UARTIE7816		0x19 /* 7816 interrupt enable reg */
+#define MXC_UARTIS7816		0x1A /* 7816 interrupt status reg */
+#define MXC_UARTWP7816T0	0x1B /* 7816 wait parameter reg */
+#define MXC_UARTWP7816T1	0x1B /* 7816 wait parameter reg */
+#define MXC_UARTWN7816		0x1C /* 7816 wait N reg */
+#define MXC_UARTWF7816		0x1D /* 7816 wait FD reg */
+#define MXC_UARTET7816		0x1E /* 7816 error threshold reg */
+#define MXC_UARTTL7816		0x1F /* 7816 transmit length reg */
+#define MXC_UARTCR6		0x21 /* CEA709.1-B contrl reg */
+#define MXC_UARTPCTH		0x22 /* CEA709.1-B packet cycle counter high */
+#define MXC_UARTPCTL		0x23 /* CEA709.1-B packet cycle counter low */
+#define MXC_UARTB1T		0x24 /* CEA709.1-B beta 1 time */
+#define MXC_UARTSDTH		0x25 /* CEA709.1-B secondary delay timer high */
+#define MXC_UARTSDTL		0x26 /* CEA709.1-B secondary delay timer low */
+#define MXC_UARTPRE		0x27 /* CEA709.1-B preamble */
+#define MXC_UARTTPL		0x28 /* CEA709.1-B transmit packet length */
+#define MXC_UARTIE		0x29 /* CEA709.1-B transmit interrupt enable */
+#define MXC_UARTSR3		0x2B /* CEA709.1-B status reg */
+#define MXC_UARTSR4		0x2C /* CEA709.1-B status reg */
+#define MXC_UARTRPL		0x2D /* CEA709.1-B received packet length */
+#define MXC_UARTRPREL		0x2E /* CEA709.1-B received preamble length */
+#define MXC_UARTCPW		0x2F /* CEA709.1-B collision pulse width */
+#define MXC_UARTRIDT		0x30 /* CEA709.1-B receive indeterminate time */
+#define MXC_UARTTIDT		0x31 /* CEA709.1-B transmit indeterminate time*/
+
+/* Bit definations of BDH */
+#define MXC_UARTBDH_LBKDIE	0x80 /* LIN break detect interrupt enable */
+#define MXC_UARTBDH_RXEDGIE	0x40 /* RxD input Active edge interrupt enable*/
+#define MXC_UARTBDH_SBR_MASK	0x1f /* Uart baud rate high 5-bits */
+/* Bit definations of CR1 */
+#define MXC_UARTCR1_LOOPS	0x80 /* Loop mode select */
+#define MXC_UARTCR1_RSRC	0x20 /* Receiver source select */
+#define MXC_UARTCR1_M		0x10 /* 9-bit 8-bit mode select */
+#define MXC_UARTCR1_WAKE	0x08 /* Receiver wakeup method */
+#define MXC_UARTCR1_ILT		0x04 /* Idle line type */
+#define MXC_UARTCR1_PE		0x02 /* Parity enable */
+#define MXC_UARTCR1_PT		0x01 /* Parity type */
+/* Bit definations of CR2 */
+#define MXC_UARTCR2_TIE		0x80 /* Tx interrupt or DMA request enable */
+#define MXC_UARTCR2_TCIE	0x40 /* Transmission complete int enable */
+#define MXC_UARTCR2_RIE		0x20 /* Rx full int or DMA request enable */
+#define MXC_UARTCR2_ILIE	0x10 /* Idle line interrupt enable */
+#define MXC_UARTCR2_TE		0x08 /* Transmitter enable */
+#define MXC_UARTCR2_RE		0x04 /* Receiver enable */
+#define MXC_UARTCR2_RWU		0x02 /* Receiver wakeup control */
+#define MXC_UARTCR2_SBK		0x01 /* Send break */
+/* Bit definations of SR1 */
+#define MXC_UARTSR1_TDRE	0x80 /* Tx data reg empty */
+#define MXC_UARTSR1_TC		0x40 /* Transmit complete */
+#define MXC_UARTSR1_RDRF	0x20 /* Rx data reg full */
+#define MXC_UARTSR1_IDLE	0x10 /* Idle line flag */
+#define MXC_UARTSR1_OR		0x08 /* Receiver overrun */
+#define MXC_UARTSR1_NF		0x04 /* Noise flag */
+#define MXC_UARTSR1_FE		0x02 /* Frame error */
+#define MXC_UARTSR1_PE		0x01 /* Parity error */
+/* Bit definations of SR2 */
+#define MXC_UARTSR2_LBKDIF	0x80 /* LIN brk detect interrupt flag */
+#define MXC_UARTSR2_RXEDGIF	0x40 /* RxD pin active edge interrupt flag */
+#define MXC_UARTSR2_MSBF	0x20 /* MSB first */
+#define MXC_UARTSR2_RXINV	0x10 /* Receive data inverted */
+#define MXC_UARTSR2_RWUID	0x08 /* Receive wakeup idle detect */
+#define MXC_UARTSR2_BRK13	0x04 /* Break transmit character length */
+#define MXC_UARTSR2_LBKDE	0x02 /* LIN break detection enable */
+#define MXC_UARTSR2_RAF		0x01 /* Receiver active flag */
+/* Bit definations of CR3 */
+#define MXC_UARTCR3_R8		0x80 /* Received bit8, for 9-bit data format */
+#define MXC_UARTCR3_T8		0x40 /* transmit bit8, for 9-bit data format */
+#define MXC_UARTCR3_TXDIR	0x20 /* Tx pin direction in single-wire mode */
+#define MXC_UARTCR3_TXINV	0x10 /* Transmit data inversion */
+#define MXC_UARTCR3_ORIE	0x08 /* Overrun error interrupt enable */
+#define MXC_UARTCR3_NEIE	0x04 /* Noise error interrupt enable */
+#define MXC_UARTCR3_FEIE	0x02 /* Framing error interrupt enable */
+#define MXC_UARTCR3_PEIE	0x01 /* Parity errror interrupt enable */
+/* Bit definations of CR4 */
+#define MXC_UARTCR4_MAEN1	0x80 /* Match address mode enable 1 */
+#define MXC_UARTCR4_MAEN2	0x40 /* Match address mode enable 2 */
+#define MXC_UARTCR4_M10		0x20 /* 10-bit mode select */
+#define MXC_UARTCR4_BRFA_MASK	0x1F /* Baud rate fine adjust */
+#define MXC_UARTCR4_BRFA_OFF	0
+/* Bit definations of CR5 */
+#define MXC_UARTCR5_TDMAS	0x80 /* Transmitter DMA select */
+#define MXC_UARTCR5_RDMAS	0x20 /* Receiver DMA select */
+/* Bit definations of Modem */
+#define MXC_UARTMODEM_RXRTSE	0x08 /* Enable receiver request-to-send */
+#define MXC_UARTMODEM_TXRTSPOL	0x04 /* Select transmitter RTS polarity */
+#define MXC_UARTMODEM_TXRTSE	0x02 /* Enable transmitter request-to-send */
+#define MXC_UARTMODEM_TXCTSE	0x01 /* Enable transmitter CTS clear-to-send */
+/* Bit definations of EDR */
+#define MXC_UARTEDR_NOISY	0x80 /* Current dataword received with noise */
+#define MXC_UARTEDR_PARITYE	0x40 /* Dataword received with parity error */
+/* Bit definations of Infrared reg(IR) */
+#define MXC_UARTIR_IREN		0x04 /* Infrared enable */
+#define MXC_UARTIR_TNP_MASK	0x03 /* Transmitter narrow pluse */
+#define MXC_UARTIR_TNP_OFF	0
+/* Bit definations of FIFO parameter reg */
+#define MXC_UARTPFIFO_TXFE	0x80 /* Transmit fifo enable */
+#define MXC_UARTPFIFO_TXFIFOSIZE_MASK	0x7
+#define MXC_UARTPFIFO_TXFIFOSIZE_OFF	4
+#define MXC_UARTPFIFO_RXFE	0x08 /* Receiver fifo enable */
+#define MXC_UARTPFIFO_RXFIFOSIZE_MASK	0x7
+#define MXC_UARTPFIFO_RXFIFOSIZE_OFF	0
+/* Bit definations of FIFO control reg */
+#define MXC_UARTCFIFO_TXFLUSH	0x80 /* Transmit FIFO/buffer flush */
+#define MXC_UARTCFIFO_RXFLUSH	0x40 /* Receive FIFO/buffer flush */
+#define MXC_UARTCFIFO_RXOFE	0x04 /* Receive fifo overflow INT enable */
+#define MXC_UARTCFIFO_TXOFE	0x02 /* Transmit fifo overflow INT enable */
+#define MXC_UARTCFIFO_RXUFE	0x01 /* Receive fifo underflow INT enable */
+/* Bit definations of FIFO status reg */
+#define MXC_UARTSFIFO_TXEMPT	0x80 /* Transmit fifo/buffer empty */
+#define MXC_UARTSFIFO_RXEMPT	0x40 /* Receive fifo/buffer empty */
+#define MXC_UARTSFIFO_RXOF	0x04 /* Rx buffer overflow flag */
+#define MXC_UARTSFIFO_TXOF	0x02 /* Tx buffer overflow flag */
+#define MXC_UARTSFIFO_RXUF	0x01 /* Rx buffer underflow flag */
+
+
+
+#undef CONFIG_MVF_SERIAL_DMA
+/* follow IMX dev node number */
+#define SERIAL_IMX_MAJOR	207
+#define MINOR_START		24
+#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 "MVF-uart"
+
+#define UART_NR 6
+
+struct mvf_port {
+	struct uart_port	port;
+	unsigned int		old_status;
+	int			txirq, rxirq, rtsirq;
+	unsigned int		have_rtscts:1;
+	unsigned int		use_dcedte:1;
+	unsigned int		use_irda:1;
+	unsigned int		irda_inv_rx:1;
+	unsigned int		irda_inv_tx:1;
+	unsigned int		fifo_en:1; /* enable FIFO mode */
+	unsigned int		mark_en:1; /* enable Mark address match */
+	unsigned int		format_9bits:1; /* 9bits data format */
+	unsigned short		trcv_delay; /* transceiver delay */
+	unsigned char		ma_addr; /* Match address */
+	struct clk		*clk;
+
+	unsigned int		tx_fifo_size, rx_fifo_size;
+
+	/* DMA fields */
+	int			enable_dma;
+	unsigned long		dma_tx_ch; /* configured eDMA channel */
+	struct imx_dma_data	dma_data;
+	struct dma_chan		*dma_chan_rx, *dma_chan_tx;
+	struct scatterlist	rx_sgl, tx_sgl;
+	void			*rx_buf;
+	unsigned char		*tx_buf;
+	unsigned int		rx_bytes, tx_bytes;
+	struct work_struct	tsk_dma_rx, tsk_dma_tx;
+	unsigned int		dma_tx_nents;
+	bool			dma_is_rxing, dma_is_txing;
+	wait_queue_head_t	dma_wait;
+};
+
+#ifdef CONFIG_IRDA
+#define USE_IRDA(sport)	((sport)->use_irda)
+#else
+#define USE_IRDA(sport)	(0)
+#endif
+enum mvf_uart_type {
+	MVF600_UART,
+};
+
+struct mvf_uart_data {
+	unsigned uts_reg;
+	enum mvf_uart_type devtype;
+};
+
+static struct mvf_uart_data mvf_uart_devdata[] = {
+	[MVF600_UART] = {
+		.devtype = MVF600_UART,
+	},
+};
+
+static struct platform_device_id mvf_uart_devtype[] = {
+	{
+		.name = "mvf600-uart",
+		.driver_data = (kernel_ulong_t) &mvf_uart_devdata[MVF600_UART],
+	}, {
+		/* sentinel */
+	}
+};
+MODULE_DEVICE_TABLE(platform, mvf_uart_devtype);
+
+static struct of_device_id mvf_uart_dt_ids[] = {
+	{
+		.compatible = "fsl,mvf-uart",
+		.data = &mvf_uart_devdata[MVF600_UART],
+	},
+	{ /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, mvf_uart_dt_ids);
+
+/*
+ * interrupts disabled on entry
+ */
+static void mvf_stop_tx(struct uart_port *port)
+{
+	struct mvf_port *sport = (struct mvf_port *)port;
+	unsigned char temp;
+
+	temp = readb(sport->port.membase + MXC_UARTCR2);
+	writeb(temp & ~(MXC_UARTCR2_TIE | MXC_UARTCR2_TCIE),
+			sport->port.membase + MXC_UARTCR2);
+}
+
+/*
+ * interrupts disabled on entry
+ */
+static void mvf_stop_rx(struct uart_port *port)
+{
+	struct mvf_port *sport = (struct mvf_port *)port;
+	unsigned char temp;
+
+	/* if the DMA RX thread is running, wait for it to finish. */
+	if (sport->enable_dma && sport->dma_is_rxing)
+		return;
+
+	temp = readb(sport->port.membase + MXC_UARTCR2);
+	writeb(temp & ~MXC_UARTCR2_RE, sport->port.membase + MXC_UARTCR2);
+}
+
+ /* modem status update function */
+static void mvf_enable_ms(struct uart_port *port)
+{
+}
+
+static inline void mvf_transmit_buffer(struct mvf_port *sport)
+{
+	struct circ_buf *xmit = &sport->port.state->xmit;
+
+	while (!uart_circ_empty(xmit) && (sport->fifo_en == 1 ?
+	(readb(sport->port.membase + MXC_UARTTCFIFO) < sport->tx_fifo_size) :
+		(readb(sport->port.membase + MXC_UARTSR1) & MXC_UARTSR1_TDRE)
+		)) {
+		/* send out xmit->buf[xmit->tail] */
+		writeb(xmit->buf[xmit->tail], sport->port.membase + MXC_UARTDR);
+		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))
+		mvf_stop_tx(&sport->port);
+}
+
+/*
+ * interrupts disabled on entry
+ */
+static void mvf_start_tx(struct uart_port *port)
+{
+	struct mvf_port *sport = (struct mvf_port *)port;
+	unsigned char temp;
+
+	if (!sport->enable_dma) {
+		temp = readb(sport->port.membase + MXC_UARTCR2);
+		writeb(temp | MXC_UARTCR2_TIE,
+				sport->port.membase + MXC_UARTCR2);
+	}
+
+	if (sport->enable_dma) {
+		schedule_work(&sport->tsk_dma_tx);
+		return;
+	}
+
+	if (readb(sport->port.membase + MXC_UARTSR1) & MXC_UARTSR1_TDRE)
+		mvf_transmit_buffer(sport);
+}
+
+
+static irqreturn_t mvf_txint(int irq, void *dev_id)
+{
+	struct mvf_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 */
+		writeb(sport->port.x_char, sport->port.membase + MXC_UARTDR);
+		goto out;
+	}
+
+	if (uart_circ_empty(xmit) || uart_tx_stopped(&sport->port)) {
+		mvf_stop_tx(&sport->port);
+		goto out;
+	}
+
+	mvf_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 mvf_rxint(int irq, void *dev_id)
+{
+	struct mvf_port *sport = dev_id;
+	unsigned int flg, ignored = 0;
+	struct tty_port *port = &sport->port.state->port;
+	unsigned long flags;
+	unsigned char r8, rx, sr;
+
+	spin_lock_irqsave(&sport->port.lock, flags);
+
+	while (!(readb(sport->port.membase + MXC_UARTSFIFO) &
+				MXC_UARTSFIFO_RXEMPT)) {
+		flg = TTY_NORMAL;
+		sport->port.icount.rx++;
+
+		/*  To clear the FE, OR, NF, FE, PE flags when set,
+		 *  read SR1 then read DR
+		 */
+		sr = readb(sport->port.membase + MXC_UARTSR1);
+
+		r8 = readb(sport->port.membase + MXC_UARTCR3) & MXC_UARTCR3_R8;
+		rx = readb(sport->port.membase + MXC_UARTDR);
+
+		if (sport->mark_en &&
+			(sport->format_9bits ? r8 : (rx == sport->ma_addr)))
+				continue;
+
+		if (uart_handle_sysrq_char(&sport->port, (unsigned char)rx))
+			continue;
+		if (sr & (MXC_UARTSR1_PE | MXC_UARTSR1_OR | MXC_UARTSR1_FE)) {
+			if (sr & MXC_UARTSR1_PE)
+				sport->port.icount.parity++;
+			else if (sr & MXC_UARTSR1_FE)
+				sport->port.icount.frame++;
+			if (sr & MXC_UARTSR1_OR)
+				sport->port.icount.overrun++;
+
+			if (sr & sport->port.ignore_status_mask) {
+				if (++ignored > 100)
+					goto out;
+				continue;
+			}
+
+			sr &= sport->port.read_status_mask;
+
+			if (sr & MXC_UARTSR1_PE)
+				flg = TTY_PARITY;
+			else if (sr & MXC_UARTSR1_FE)
+				flg = TTY_FRAME;
+			if (sr & MXC_UARTSR1_OR)
+				flg = TTY_OVERRUN;
+
+#ifdef SUPPORT_SYSRQ
+			sport->port.sysrq = 0;
+#endif
+		}
+
+		tty_insert_flip_char(port, rx, flg);
+	}
+
+out:
+	spin_unlock_irqrestore(&sport->port.lock, flags);
+
+	tty_flip_buffer_push(port);
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t mvf_int(int irq, void *dev_id)
+{
+	struct mvf_port *sport = dev_id;
+	unsigned int sts;
+
+	sts = readb(sport->port.membase + MXC_UARTSR1);
+
+	if (sts & MXC_UARTSR1_RDRF)
+			mvf_rxint(irq, dev_id);
+
+	if (sts & MXC_UARTSR1_TDRE &&
+		!(readb(sport->port.membase + MXC_UARTCR5) &
+			MXC_UARTCR5_TDMAS))
+		mvf_txint(irq, dev_id);
+
+	return IRQ_HANDLED;
+}
+
+/*
+ * Return TIOCSER_TEMT when transmitter is not busy.
+ */
+static unsigned int mvf_tx_empty(struct uart_port *port)
+{
+	struct mvf_port *sport = (struct mvf_port *)port;
+
+	return (readb(sport->port.membase + MXC_UARTSR1) & MXC_UARTSR1_TC) ?
+		TIOCSER_TEMT : 0;
+}
+
+static unsigned int mvf_get_mctrl(struct uart_port *port)
+{
+	struct mvf_port *sport = (struct mvf_port *)port;
+	unsigned int tmp = 0;
+	unsigned char reg;
+
+	reg = readb(sport->port.membase + MXC_UARTMODEM);
+	if (reg & MXC_UARTMODEM_TXCTSE)
+		tmp |= TIOCM_CTS;
+
+	if (reg & MXC_UARTMODEM_RXRTSE)
+		tmp |= TIOCM_RTS;
+
+	return tmp;
+}
+
+static void mvf_set_mctrl(struct uart_port *port, unsigned int mctrl)
+{
+	struct mvf_port *sport = (struct mvf_port *)port;
+	unsigned long temp;
+
+	temp = readb(sport->port.membase + MXC_UARTMODEM) &
+			~(MXC_UARTMODEM_RXRTSE | MXC_UARTMODEM_TXCTSE);
+
+	if (mctrl & TIOCM_RTS)
+		temp |= MXC_UARTMODEM_RXRTSE;
+	if (mctrl & TIOCM_CTS)
+		temp |= MXC_UARTMODEM_TXCTSE;
+
+	writeb(temp, sport->port.membase + MXC_UARTMODEM);
+}
+
+/*
+ * Interrupts always disabled.
+ */
+static void mvf_break_ctl(struct uart_port *port, int break_state)
+{
+	struct mvf_port *sport = (struct mvf_port *)port;
+	unsigned long flags;
+	unsigned char temp;
+
+	spin_lock_irqsave(&sport->port.lock, flags);
+
+	temp = readb(sport->port.membase + MXC_UARTCR2) & ~MXC_UARTCR2_SBK;
+
+	if (break_state != 0)
+		temp |= MXC_UARTCR2_SBK;
+
+	writeb(temp, sport->port.membase + MXC_UARTCR2);
+
+	spin_unlock_irqrestore(&sport->port.lock, flags);
+}
+
+#define TXTL 2
+#define RXTL 1
+
+static int mvf_setup_watermark(struct mvf_port *sport, unsigned int mode)
+{
+	unsigned char val, old_cr2, cr2;
+
+	/* set receiver/transmitter trigger level. */
+	old_cr2 = cr2 = readb(sport->port.membase + MXC_UARTCR2);
+	cr2 &= ~(MXC_UARTCR2_TIE | MXC_UARTCR2_TCIE | MXC_UARTCR2_TE |
+			MXC_UARTCR2_RIE | MXC_UARTCR2_RE);
+	writeb(cr2, sport->port.membase + MXC_UARTCR2);
+
+	val = TXTL;
+	writeb(val, sport->port.membase + MXC_UARTTWFIFO);
+	val = RXTL;
+	writeb(val, sport->port.membase + MXC_UARTRWFIFO);
+
+	/* Enable Tx and Rx FIFO */
+	val = readb(sport->port.membase + MXC_UARTPFIFO);
+	sport->tx_fifo_size = 0x1 << (((val >> MXC_UARTPFIFO_TXFIFOSIZE_OFF) &
+		MXC_UARTPFIFO_TXFIFOSIZE_MASK) + 1);
+	sport->rx_fifo_size = 0x1 << (((val >> MXC_UARTPFIFO_RXFIFOSIZE_OFF) &
+		MXC_UARTPFIFO_RXFIFOSIZE_MASK) + 1);
+	writeb(val | MXC_UARTPFIFO_TXFE | MXC_UARTPFIFO_RXFE,
+			sport->port.membase + MXC_UARTPFIFO);
+
+	/* Flush the Tx and Rx FIFO to a known state */
+	writeb(MXC_UARTCFIFO_TXFLUSH | MXC_UARTCFIFO_RXFLUSH,
+			sport->port.membase + MXC_UARTCFIFO);
+
+	/* restore CR2 */
+	writeb(old_cr2, sport->port.membase + MXC_UARTCR2);
+
+	return 0;
+}
+
+
+static int mvf_startup(struct uart_port *port)
+{
+	struct mvf_port *sport = (struct mvf_port *)port;
+	int retval;
+	unsigned long flags, temp;
+
+	if (sport->fifo_en)
+		mvf_setup_watermark(sport, 0);
+
+	temp = readb(sport->port.membase + MXC_UARTCR2);
+
+	writeb(temp & ~MXC_UARTCR2_RIE, sport->port.membase + MXC_UARTCR2);
+
+	/*
+	 * Allocate the IRQ(s)
+	 * Vybrid chips only have one interrupt.
+	 */
+	retval = request_irq(sport->port.irq, mvf_int, 0,
+				DRIVER_NAME, sport);
+	if (retval)
+		goto error_out1;
+
+	/* Enable the DMA ops for uart. */
+	if (sport->enable_dma) {
+		sport->dma_is_txing = 0;
+
+		/* enable DMA request generation */
+		temp = readb(sport->port.membase + MXC_UARTCR5);
+		temp |= MXC_UARTCR5_TDMAS;
+		writeb(temp, sport->port.membase + MXC_UARTCR5);
+
+		init_waitqueue_head(&sport->dma_wait);
+	}
+
+	spin_lock_irqsave(&sport->port.lock, flags);
+
+	/* Finally, clear and enable interrupts */
+	temp = readb(sport->port.membase + MXC_UARTCR2);
+	temp |= MXC_UARTCR2_RIE | MXC_UARTCR2_TIE;
+	writeb(temp, sport->port.membase + MXC_UARTCR2);
+
+	spin_unlock_irqrestore(&sport->port.lock, flags);
+
+	return 0;
+
+error_out1:
+	return retval;
+}
+
+static void mvf_shutdown(struct uart_port *port)
+{
+	struct mvf_port *sport = (struct mvf_port *)port;
+	unsigned char temp;
+	unsigned long flags;
+
+	spin_lock_irqsave(&sport->port.lock, flags);
+	temp = readb(sport->port.membase + MXC_UARTCR2);
+	temp &= ~(MXC_UARTCR2_TE | MXC_UARTCR2_RE);
+	writeb(temp, sport->port.membase + MXC_UARTCR2);
+	spin_unlock_irqrestore(&sport->port.lock, flags);
+
+	/*
+	 * 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 = readb(sport->port.membase + MXC_UARTCR2);
+	temp &= ~(MXC_UARTCR2_TIE | MXC_UARTCR2_TCIE | MXC_UARTCR2_RIE);
+	writeb(temp, sport->port.membase + MXC_UARTCR2);
+
+	spin_unlock_irqrestore(&sport->port.lock, flags);
+}
+
+static void
+mvf_set_termios(struct uart_port *port, struct ktermios *termios,
+		   struct ktermios *old)
+{
+	struct mvf_port *sport = (struct mvf_port *)port;
+	unsigned long flags;
+	unsigned char cr1, old_cr1, old_cr2, cr4, bdh, modem;
+	unsigned int  baud;
+	unsigned int old_csize = old ? old->c_cflag & CSIZE : CS8;
+	unsigned int sbr, brfa;
+
+	cr1 = old_cr1 = readb(sport->port.membase + MXC_UARTCR1);
+	old_cr2 = readb(sport->port.membase + MXC_UARTCR2);
+	cr4 = readb(sport->port.membase + MXC_UARTCR4);
+	bdh = readb(sport->port.membase + MXC_UARTBDH);
+	modem = readb(sport->port.membase + MXC_UARTMODEM);
+	/*
+	 * 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 CS8 and CS7,but CS7 must enable PE.
+	 * supported mode:
+	 *  - (7,e/o,1)
+	 *  - (8,n,1)
+	 *  - (8,m/s,1)
+	 *  - (8,e/o,1)
+	 */
+	while ((termios->c_cflag & CSIZE) != CS8 &&
+		(termios->c_cflag & CSIZE) != CS7) {
+		termios->c_cflag &= ~CSIZE;
+		termios->c_cflag |= old_csize;
+		old_csize = CS8;
+	}
+
+	if ((termios->c_cflag & CSIZE) == CS8 ||
+		(termios->c_cflag & CSIZE) == CS7)
+		cr1 = old_cr1 & ~MXC_UARTCR1_M;
+
+	if (termios->c_cflag & CMSPAR) {
+		if ((termios->c_cflag & CSIZE) != CS8) {
+			termios->c_cflag &= ~CSIZE;
+			termios->c_cflag |= CS8;
+		}
+		cr1 |= MXC_UARTCR1_M;
+	}
+
+	if (termios->c_cflag & CRTSCTS) {
+		if (sport->have_rtscts)
+			modem |= (MXC_UARTMODEM_RXRTSE | MXC_UARTMODEM_TXCTSE);
+
+	} else {
+		termios->c_cflag &= ~CRTSCTS;
+		modem &= ~(MXC_UARTMODEM_RXRTSE | MXC_UARTMODEM_TXCTSE);
+	}
+
+	if (termios->c_cflag & CSTOPB)
+		termios->c_cflag &= ~CSTOPB;
+
+	/* parity must enable when CS7 to match 8-bits format */
+	if ((termios->c_cflag & CSIZE) == CS7)
+		termios->c_cflag |= PARENB;
+
+	if ((termios->c_cflag & PARENB)) {
+		if (termios->c_cflag & CMSPAR) {
+			cr1 &= ~MXC_UARTCR1_PE;
+			cr1 |= MXC_UARTCR1_M;
+			sport->format_9bits = 1;
+		} else {
+			cr1 |= MXC_UARTCR1_PE;
+			if ((termios->c_cflag & CSIZE) == CS8)
+				cr1 |= MXC_UARTCR1_M;
+			if (termios->c_cflag & PARODD)
+				cr1 |= MXC_UARTCR1_PT;
+			else
+				cr1 &= ~MXC_UARTCR1_PT;
+		}
+	}
+
+	/*
+	 * Ask the core to calculate the divisor for us.
+	 */
+	baud = uart_get_baud_rate(port, termios, old, 50, port->uartclk / 16);
+
+	spin_lock_irqsave(&sport->port.lock, flags);
+
+	sport->port.read_status_mask = 0;
+	if (termios->c_iflag & INPCK)
+		sport->port.read_status_mask |=
+					(MXC_UARTSR1_FE | MXC_UARTSR1_PE);
+	if (termios->c_iflag & (BRKINT | PARMRK))
+		sport->port.read_status_mask |= MXC_UARTSR1_FE;
+
+	/*
+	 * Characters to ignore
+	 */
+	sport->port.ignore_status_mask = 0;
+	if (termios->c_iflag & IGNPAR)
+		sport->port.ignore_status_mask |= MXC_UARTSR1_PE;
+	if (termios->c_iflag & IGNBRK) {
+		sport->port.ignore_status_mask |= MXC_UARTSR1_FE;
+		/*
+		 * 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 |= MXC_UARTSR1_OR;
+	}
+
+	/*
+	 * Update the per-port timeout.
+	 */
+	uart_update_timeout(port, termios->c_cflag, baud);
+
+	/* wait transmit engin complete */
+	while (!(readb(sport->port.membase + MXC_UARTSR1) & MXC_UARTSR1_TC))
+		barrier();
+
+	writeb(old_cr2 & ~(MXC_UARTCR2_TE | MXC_UARTCR2_RE),
+			sport->port.membase + MXC_UARTCR2);
+
+	/* disable transmit and receive */
+	sbr = sport->port.uartclk / (16 * baud);
+	brfa = ((sport->port.uartclk - (16 * sbr * baud)) * 2)/baud;
+
+	bdh &= ~MXC_UARTBDH_SBR_MASK;
+	bdh |= (sbr >> 8) & 0x1F;
+
+	cr4 &= ~MXC_UARTCR4_BRFA_MASK;
+	brfa &= MXC_UARTCR4_BRFA_MASK;
+	writeb(cr4 | brfa, sport->port.membase + MXC_UARTCR4);
+	writeb(bdh, sport->port.membase + MXC_UARTBDH);
+	writeb(sbr & 0xFF, sport->port.membase + MXC_UARTBDL);
+	writeb(cr1, sport->port.membase + MXC_UARTCR1);
+	writeb(modem, sport->port.membase + MXC_UARTMODEM);
+
+	/* restore control register */
+	writeb(old_cr2, sport->port.membase + MXC_UARTCR2);
+
+	spin_unlock_irqrestore(&sport->port.lock, flags);
+}
+
+static const char *mvf_type(struct uart_port *port)
+{
+	struct mvf_port *sport = (struct mvf_port *)port;
+
+	return sport->port.type == PORT_MVF ? "MVF" : NULL;
+}
+
+/*
+ * Release the memory region(s) being used by 'port'.
+ */
+static void mvf_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, mmres->end - mmres->start + 1);
+}
+
+/*
+ * Request the memory region(s) being used by 'port'.
+ */
+static int mvf_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, mmres->end - mmres->start + 1,
+			"imx-uart");
+
+	return  ret ? 0 : -EBUSY;
+}
+
+/*
+ * Configure/autoconfigure the port.
+ */
+static void mvf_config_port(struct uart_port *port, int flags)
+{
+	struct mvf_port *sport = (struct mvf_port *)port;
+
+	if (flags & UART_CONFIG_TYPE &&
+	    mvf_request_port(&sport->port) == 0)
+		sport->port.type = PORT_MVF;
+}
+
+/*
+ * Verify the new serial_struct (for TIOCSSERIAL).
+ * The only change we allow are to the flags and type, and
+ * even then only between PORT_MVF and PORT_UNKNOWN
+ */
+static int
+mvf_verify_port(struct uart_port *port, struct serial_struct *ser)
+{
+	struct mvf_port *sport = (struct mvf_port *)port;
+	int ret = 0;
+
+	if (ser->type != PORT_UNKNOWN && ser->type != PORT_MVF)
+		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 (sport->port.iobase != ser->port)
+		ret = -EINVAL;
+	if (ser->hub6 != 0)
+		ret = -EINVAL;
+	return ret;
+}
+
+
+static struct uart_ops mvf_pops = {
+	.tx_empty	= mvf_tx_empty,
+	.set_mctrl	= mvf_set_mctrl,
+	.get_mctrl	= mvf_get_mctrl,
+	.stop_tx	= mvf_stop_tx,
+	.start_tx	= mvf_start_tx,
+	.stop_rx	= mvf_stop_rx,
+	.enable_ms	= mvf_enable_ms,
+	.break_ctl	= mvf_break_ctl,
+	.startup	= mvf_startup,
+	.shutdown	= mvf_shutdown,
+	.set_termios	= mvf_set_termios,
+	.type		= mvf_type,
+	.release_port	= mvf_release_port,
+	.request_port	= mvf_request_port,
+	.config_port	= mvf_config_port,
+	.verify_port	= mvf_verify_port,
+};
+
+static struct mvf_port *mvf_ports[UART_NR];
+
+#ifdef CONFIG_SERIAL_MVF_CONSOLE
+static void mvf_console_putchar(struct uart_port *port, int ch)
+{
+	struct mvf_port *sport = (struct mvf_port *)port;
+
+	while (!(readb(sport->port.membase + MXC_UARTSR1) & MXC_UARTSR1_TDRE))
+		barrier();
+
+	writeb(ch, sport->port.membase + MXC_UARTDR);
+}
+
+/*
+ * Interrupts are disabled on entering
+ */
+static void
+mvf_console_write(struct console *co, const char *s, unsigned int count)
+{
+	struct mvf_port *sport = mvf_ports[co->index];
+	unsigned int  old_cr2, cr2;
+	unsigned long flags;
+
+	spin_lock_irqsave(&sport->port.lock, flags);
+	/*
+	 * First, save UCR1/2 and then disable interrupts
+	 */
+	cr2 = old_cr2 = readb(sport->port.membase + MXC_UARTCR2);
+
+	cr2 |= (MXC_UARTCR2_TE |  MXC_UARTCR2_RE);
+	cr2 &= ~(MXC_UARTCR2_TIE | MXC_UARTCR2_TCIE | MXC_UARTCR2_RIE);
+
+	writeb(cr2, sport->port.membase + MXC_UARTCR2);
+
+	uart_console_write(&sport->port, s, count, mvf_console_putchar);
+
+	/*
+	 * wait for transmitter finish complete and restore CR2
+	 */
+	while (!(readb(sport->port.membase + MXC_UARTSR1) & MXC_UARTSR1_TC))
+		;
+
+	writeb(old_cr2, sport->port.membase + MXC_UARTCR2);
+	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
+mvf_console_get_options(struct mvf_port *sport, int *baud,
+			   int *parity, int *bits)
+{
+
+	if (readb(sport->port.membase + MXC_UARTCR2) &
+			(MXC_UARTCR2_TE | MXC_UARTCR2)) {
+		/* ok, the port was enabled */
+		unsigned char cr1, bdh, bdl, brfa;
+		unsigned int sbr, uartclk;
+		unsigned int baud_raw;
+
+		cr1 = readb(sport->port.membase + MXC_UARTCR1);
+
+		*parity = 'n';
+		if (cr1 & MXC_UARTCR1_PE) {
+			if (cr1 & MXC_UARTCR1_PT)
+				*parity = 'o';
+			else
+				*parity = 'e';
+		}
+
+		if (cr1 & MXC_UARTCR1_M)
+			*bits = 9;
+		else
+			*bits = 8;
+
+		bdh = readb(sport->port.membase + MXC_UARTBDH) &
+						MXC_UARTBDH_SBR_MASK;
+		bdl = readb(sport->port.membase + MXC_UARTBDL);
+		sbr = bdh;
+		sbr <<= 8;
+		sbr |= bdl;
+		brfa = readb(sport->port.membase + MXC_UARTCR4) &
+						MXC_UARTCR4_BRFA_MASK;
+		uartclk = clk_get_rate(sport->clk);
+		/*
+		 * Baud = mod_clk/(16*(sbr[13]+(brfa)/32)
+		 */
+		baud_raw = uartclk/(16 * (sbr + brfa/32));
+
+		if (*baud != baud_raw)
+			printk(KERN_INFO "Serial: Console IMX "
+					"rounded baud rate from %d to %d\n",
+				baud_raw, *baud);
+	}
+}
+
+static int __init
+mvf_console_setup(struct console *co, char *options)
+{
+	struct mvf_port *sport;
+	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.
+	 */
+	if (co->index == -1 || co->index >= ARRAY_SIZE(mvf_ports))
+		co->index = 0;
+	sport = mvf_ports[co->index];
+
+	if (sport == NULL)
+		return -ENODEV;
+
+	if (options)
+		uart_parse_options(options, &baud, &parity, &bits, &flow);
+	else
+		mvf_console_get_options(sport, &baud, &parity, &bits);
+
+	if (sport->fifo_en == 1)
+		mvf_setup_watermark(sport, 0);
+
+	return uart_set_options(&sport->port, co, baud, parity, bits, flow);
+}
+
+static struct uart_driver mvf_reg;
+static struct console mvf_console = {
+	.name		= DEV_NAME,
+	.write		= mvf_console_write,
+	.device		= uart_console_device,
+	.setup		= mvf_console_setup,
+	.flags		= CON_PRINTBUFFER,
+	.index		= -1,
+	.data		= &mvf_reg,
+};
+
+#define MVF_CONSOLE	(&mvf_console)
+#else
+#define MVF_CONSOLE	NULL
+#endif
+
+static struct uart_driver mvf_reg = {
+	.owner          = THIS_MODULE,
+	.driver_name    = DRIVER_NAME,
+	.dev_name       = DEV_NAME,
+	.major          = SERIAL_IMX_MAJOR,
+	.minor          = MINOR_START,
+	.nr             = ARRAY_SIZE(mvf_ports),
+	.cons           = MVF_CONSOLE,
+};
+
+static int serial_mvf_suspend(struct platform_device *dev, pm_message_t state)
+{
+	struct mvf_port *sport = platform_get_drvdata(dev);
+
+	/* Enable UART wakeup */
+
+	if (sport)
+		uart_suspend_port(&mvf_reg, &sport->port);
+
+	return 0;
+}
+
+static int serial_mvf_resume(struct platform_device *dev)
+{
+	struct mvf_port *sport = platform_get_drvdata(dev);
+
+	if (sport)
+		uart_resume_port(&mvf_reg, &sport->port);
+
+	/* Disable UART wakeup */
+
+	return 0;
+}
+
+static int serial_mvf_probe_dt(struct mvf_port *sport,
+		struct platform_device *pdev)
+{
+	struct device_node *np = pdev->dev.of_node;
+	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-fifo-mode", NULL))
+		sport->fifo_en = 1;
+
+	return 0;
+}
+
+static int serial_mvf_probe(struct platform_device *pdev)
+{
+	struct mvf_port *sport;
+	struct imxuart_platform_data *pdata;
+	void __iomem *base;
+	int ret = 0;
+	struct resource *res;
+
+	sport = devm_kzalloc(&pdev->dev, sizeof(*sport), GFP_KERNEL);
+	if (!sport)
+		return -ENOMEM;
+
+	pdev->dev.coherent_dma_mask = 0;
+
+	ret = serial_mvf_probe_dt(sport, pdev);
+	if (ret < 0)
+		return ret ;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!res)
+		return -ENODEV;
+
+	base = devm_ioremap(&pdev->dev, res->start, PAGE_SIZE);
+	if (!base)
+		return -ENOMEM;
+
+	sport->port.dev = &pdev->dev;
+	sport->port.mapbase = res->start;
+	sport->port.membase = base;
+	sport->port.type = PORT_MVF,
+	sport->port.iotype = UPIO_MEM;
+	sport->port.irq = platform_get_irq(pdev, 0);
+	sport->port.fifosize = 32;
+	sport->port.ops = &mvf_pops;
+	sport->port.flags = UPF_BOOT_AUTOCONF;
+	if (pdev->id >= 0)
+		sport->port.line = pdev->id;
+
+	sport->clk = devm_clk_get(&pdev->dev, "ipg");
+	if (IS_ERR(sport->clk)) {
+		ret = PTR_ERR(sport->clk);
+		dev_err(&pdev->dev, "failed to uart clk: %d\n", ret);
+		return ret;
+	}
+
+	clk_prepare_enable(sport->clk);
+
+	sport->port.uartclk = clk_get_rate(sport->clk);
+
+	mvf_ports[sport->port.line] = sport;
+
+	pdata = pdev->dev.platform_data;
+
+	/* all uarts support hardware RTS/CTS */
+	sport->have_rtscts = 1;
+
+	if (pdata && pdata->init) {
+		ret = pdata->init(pdev);
+		if (ret)
+			goto clkput;
+	}
+
+	ret = uart_add_one_port(&mvf_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);
+
+	return ret;
+}
+
+static int serial_mvf_remove(struct platform_device *pdev)
+{
+	struct imxuart_platform_data *pdata;
+	struct mvf_port *sport = platform_get_drvdata(pdev);
+
+	pdata = pdev->dev.platform_data;
+
+	platform_set_drvdata(pdev, NULL);
+
+	if (sport)
+		uart_remove_one_port(&mvf_reg, &sport->port);
+
+	clk_disable_unprepare(sport->clk);
+
+	if (pdata && pdata->exit)
+		pdata->exit(pdev);
+
+	return 0;
+}
+
+static struct platform_driver serial_mvf_driver = {
+	.probe		= serial_mvf_probe,
+	.remove		= serial_mvf_remove,
+
+	.suspend	= serial_mvf_suspend,
+	.resume		= serial_mvf_resume,
+	.id_table       = mvf_uart_devtype,
+	.driver		= {
+		.name	= "mvf-uart",
+		.owner	= THIS_MODULE,
+		.of_match_table = mvf_uart_dt_ids,
+	},
+};
+
+static int __init mvf_serial_init(void)
+{
+	int ret;
+
+	pr_info("Serial: Vybrid Family driver\n");
+
+	ret = uart_register_driver(&mvf_reg);
+	if (ret)
+		return ret;
+
+	ret = platform_driver_register(&serial_mvf_driver);
+	if (ret != 0)
+		uart_unregister_driver(&mvf_reg);
+
+	return 0;
+}
+
+static void __exit mvf_serial_exit(void)
+{
+	platform_driver_unregister(&serial_mvf_driver);
+	uart_unregister_driver(&mvf_reg);
+}
+
+module_init(mvf_serial_init);
+module_exit(mvf_serial_exit);
+
+MODULE_DESCRIPTION("Vybrid Family serial port driver");
+MODULE_LICENSE("GPL");
diff --git a/include/uapi/linux/serial_core.h b/include/uapi/linux/serial_core.h
index 74c2bf7..ffb7192 100644
--- a/include/uapi/linux/serial_core.h
+++ b/include/uapi/linux/serial_core.h
@@ -226,4 +226,7 @@
 /* Rocketport EXPRESS/INFINITY */
 #define PORT_RP2	102
 
+/* Freescale Vybrid uart */
+#define PORT_MVF	103
+
 #endif /* _UAPILINUX_SERIAL_CORE_H */
-- 
1.8.0

WARNING: multiple messages have this Message-ID (diff)
From: b35083@freescale.com (Jingchang Lu)
To: linux-arm-kernel@lists.infradead.org
Subject: [PATCH] tty/serial: add MVF uart driver support
Date: Fri, 12 Apr 2013 15:10:55 +0800	[thread overview]
Message-ID: <1365750655-3530-1-git-send-email-b35083@freescale.com> (raw)

It adds Freescale Vybrid Family uart driver support

Signed-off-by: Jingchang Lu <b35083@freescale.com>
---
 drivers/tty/serial/Kconfig       |   17 +
 drivers/tty/serial/Makefile      |    1 +
 drivers/tty/serial/mvf.c         | 1246 ++++++++++++++++++++++++++++++++++++++
 include/uapi/linux/serial_core.h |    3 +
 4 files changed, 1267 insertions(+)
 create mode 100644 drivers/tty/serial/mvf.c

diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig
index 7e7006f..ab6dc27 100644
--- a/drivers/tty/serial/Kconfig
+++ b/drivers/tty/serial/Kconfig
@@ -574,6 +574,23 @@ config SERIAL_IMX_CONSOLE
 	  your boot loader (lilo or loadlin) about how to pass options to the
 	  kernel at boot time.)
 
+config SERIAL_MVF
+	bool "Freescale Vybrid serial port support"
+	depends on ARCH_MVF
+	select SERIAL_CORE
+	select RATIONAL
+	help
+	  If you have a machine based on a Freescale Vybrid CPU you
+	  can enable its onboard serial port by enabling this option.
+
+config SERIAL_MVF_CONSOLE
+	bool "Console on Vybrid serial port"
+	depends on SERIAL_MVF
+	select SERIAL_CORE_CONSOLE
+	help
+	  If you have enabled the serial port on the Freescale Vybrid family
+	  CPU you can make it the console by answering Y to this option.
+
 config SERIAL_UARTLITE
 	tristate "Xilinx uartlite serial port support"
 	depends on PPC32 || MICROBLAZE || MFD_TIMBERDALE
diff --git a/drivers/tty/serial/Makefile b/drivers/tty/serial/Makefile
index eedfec4..be2ed68 100644
--- a/drivers/tty/serial/Makefile
+++ b/drivers/tty/serial/Makefile
@@ -41,6 +41,7 @@ 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_MVF) += mvf.o
 obj-$(CONFIG_SERIAL_MPC52xx) += mpc52xx_uart.o
 obj-$(CONFIG_SERIAL_ICOM) += icom.o
 obj-$(CONFIG_SERIAL_M32R_SIO) += m32r_sio.o
diff --git a/drivers/tty/serial/mvf.c b/drivers/tty/serial/mvf.c
new file mode 100644
index 0000000..b3b7ffd
--- /dev/null
+++ b/drivers/tty/serial/mvf.c
@@ -0,0 +1,1246 @@
+/*
+ *  Driver for Freescale Vybrid Family serial ports
+ *
+ *  Based on drivers/tty/serial/imx.c
+ *
+ *  Copyright 2012-2013 Freescale Semiconductor, Inc.
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#if defined(CONFIG_SERIAL_MVF_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/dma-mapping.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/pinctrl/consumer.h>
+#include <linux/io.h>
+#include <asm/irq.h>
+#include <linux/platform_data/serial-imx.h>
+#include <linux/platform_data/dma-imx.h>
+
+/* All uart module registers for MVF is 8-bit width */
+#define MXC_UARTBDH		0x00 /* Baud rate reg: high */
+#define MXC_UARTBDL		0x01 /* Baud rate reg: low */
+#define MXC_UARTCR1		0x02 /* Control reg 1 */
+#define MXC_UARTCR2		0x03 /* Control reg 2 */
+#define MXC_UARTSR1		0x04 /* Status reg 1 */
+#define MXC_UARTSR2		0x05 /* Status reg 2 */
+#define MXC_UARTCR3		0x06 /* Control reg 3 */
+#define MXC_UARTDR		0x07 /* Data reg */
+#define MXC_UARTMAR1		0x08 /* Match address reg 1 */
+#define MXC_UARTMAR2		0x09 /* Match address reg 2 */
+#define MXC_UARTCR4		0x0A /* Control reg 4 */
+#define MXC_UARTCR5		0x0B /* Control reg 5 */
+#define MXC_UARTEDR		0x0C /* Extended data reg */
+#define MXC_UARTMODEM		0x0D /* Modem reg */
+#define MXC_UARTIR		0x0E /* Infrared reg */
+#define MXC_UARTPFIFO		0x10 /* FIFO parameter reg */
+#define MXC_UARTCFIFO		0x11 /* FIFO control reg */
+#define MXC_UARTSFIFO		0x12 /* FIFO status reg */
+#define MXC_UARTTWFIFO		0x13 /* FIFO transmit watermark reg */
+#define MXC_UARTTCFIFO		0x14 /* FIFO transmit count reg */
+#define MXC_UARTRWFIFO		0x15 /* FIFO receive watermark reg */
+#define MXC_UARTRCFIFO		0x16 /* FIFO receive count reg */
+#define MXC_UARTC7816		0x18 /* 7816 control reg */
+#define MXC_UARTIE7816		0x19 /* 7816 interrupt enable reg */
+#define MXC_UARTIS7816		0x1A /* 7816 interrupt status reg */
+#define MXC_UARTWP7816T0	0x1B /* 7816 wait parameter reg */
+#define MXC_UARTWP7816T1	0x1B /* 7816 wait parameter reg */
+#define MXC_UARTWN7816		0x1C /* 7816 wait N reg */
+#define MXC_UARTWF7816		0x1D /* 7816 wait FD reg */
+#define MXC_UARTET7816		0x1E /* 7816 error threshold reg */
+#define MXC_UARTTL7816		0x1F /* 7816 transmit length reg */
+#define MXC_UARTCR6		0x21 /* CEA709.1-B contrl reg */
+#define MXC_UARTPCTH		0x22 /* CEA709.1-B packet cycle counter high */
+#define MXC_UARTPCTL		0x23 /* CEA709.1-B packet cycle counter low */
+#define MXC_UARTB1T		0x24 /* CEA709.1-B beta 1 time */
+#define MXC_UARTSDTH		0x25 /* CEA709.1-B secondary delay timer high */
+#define MXC_UARTSDTL		0x26 /* CEA709.1-B secondary delay timer low */
+#define MXC_UARTPRE		0x27 /* CEA709.1-B preamble */
+#define MXC_UARTTPL		0x28 /* CEA709.1-B transmit packet length */
+#define MXC_UARTIE		0x29 /* CEA709.1-B transmit interrupt enable */
+#define MXC_UARTSR3		0x2B /* CEA709.1-B status reg */
+#define MXC_UARTSR4		0x2C /* CEA709.1-B status reg */
+#define MXC_UARTRPL		0x2D /* CEA709.1-B received packet length */
+#define MXC_UARTRPREL		0x2E /* CEA709.1-B received preamble length */
+#define MXC_UARTCPW		0x2F /* CEA709.1-B collision pulse width */
+#define MXC_UARTRIDT		0x30 /* CEA709.1-B receive indeterminate time */
+#define MXC_UARTTIDT		0x31 /* CEA709.1-B transmit indeterminate time*/
+
+/* Bit definations of BDH */
+#define MXC_UARTBDH_LBKDIE	0x80 /* LIN break detect interrupt enable */
+#define MXC_UARTBDH_RXEDGIE	0x40 /* RxD input Active edge interrupt enable*/
+#define MXC_UARTBDH_SBR_MASK	0x1f /* Uart baud rate high 5-bits */
+/* Bit definations of CR1 */
+#define MXC_UARTCR1_LOOPS	0x80 /* Loop mode select */
+#define MXC_UARTCR1_RSRC	0x20 /* Receiver source select */
+#define MXC_UARTCR1_M		0x10 /* 9-bit 8-bit mode select */
+#define MXC_UARTCR1_WAKE	0x08 /* Receiver wakeup method */
+#define MXC_UARTCR1_ILT		0x04 /* Idle line type */
+#define MXC_UARTCR1_PE		0x02 /* Parity enable */
+#define MXC_UARTCR1_PT		0x01 /* Parity type */
+/* Bit definations of CR2 */
+#define MXC_UARTCR2_TIE		0x80 /* Tx interrupt or DMA request enable */
+#define MXC_UARTCR2_TCIE	0x40 /* Transmission complete int enable */
+#define MXC_UARTCR2_RIE		0x20 /* Rx full int or DMA request enable */
+#define MXC_UARTCR2_ILIE	0x10 /* Idle line interrupt enable */
+#define MXC_UARTCR2_TE		0x08 /* Transmitter enable */
+#define MXC_UARTCR2_RE		0x04 /* Receiver enable */
+#define MXC_UARTCR2_RWU		0x02 /* Receiver wakeup control */
+#define MXC_UARTCR2_SBK		0x01 /* Send break */
+/* Bit definations of SR1 */
+#define MXC_UARTSR1_TDRE	0x80 /* Tx data reg empty */
+#define MXC_UARTSR1_TC		0x40 /* Transmit complete */
+#define MXC_UARTSR1_RDRF	0x20 /* Rx data reg full */
+#define MXC_UARTSR1_IDLE	0x10 /* Idle line flag */
+#define MXC_UARTSR1_OR		0x08 /* Receiver overrun */
+#define MXC_UARTSR1_NF		0x04 /* Noise flag */
+#define MXC_UARTSR1_FE		0x02 /* Frame error */
+#define MXC_UARTSR1_PE		0x01 /* Parity error */
+/* Bit definations of SR2 */
+#define MXC_UARTSR2_LBKDIF	0x80 /* LIN brk detect interrupt flag */
+#define MXC_UARTSR2_RXEDGIF	0x40 /* RxD pin active edge interrupt flag */
+#define MXC_UARTSR2_MSBF	0x20 /* MSB first */
+#define MXC_UARTSR2_RXINV	0x10 /* Receive data inverted */
+#define MXC_UARTSR2_RWUID	0x08 /* Receive wakeup idle detect */
+#define MXC_UARTSR2_BRK13	0x04 /* Break transmit character length */
+#define MXC_UARTSR2_LBKDE	0x02 /* LIN break detection enable */
+#define MXC_UARTSR2_RAF		0x01 /* Receiver active flag */
+/* Bit definations of CR3 */
+#define MXC_UARTCR3_R8		0x80 /* Received bit8, for 9-bit data format */
+#define MXC_UARTCR3_T8		0x40 /* transmit bit8, for 9-bit data format */
+#define MXC_UARTCR3_TXDIR	0x20 /* Tx pin direction in single-wire mode */
+#define MXC_UARTCR3_TXINV	0x10 /* Transmit data inversion */
+#define MXC_UARTCR3_ORIE	0x08 /* Overrun error interrupt enable */
+#define MXC_UARTCR3_NEIE	0x04 /* Noise error interrupt enable */
+#define MXC_UARTCR3_FEIE	0x02 /* Framing error interrupt enable */
+#define MXC_UARTCR3_PEIE	0x01 /* Parity errror interrupt enable */
+/* Bit definations of CR4 */
+#define MXC_UARTCR4_MAEN1	0x80 /* Match address mode enable 1 */
+#define MXC_UARTCR4_MAEN2	0x40 /* Match address mode enable 2 */
+#define MXC_UARTCR4_M10		0x20 /* 10-bit mode select */
+#define MXC_UARTCR4_BRFA_MASK	0x1F /* Baud rate fine adjust */
+#define MXC_UARTCR4_BRFA_OFF	0
+/* Bit definations of CR5 */
+#define MXC_UARTCR5_TDMAS	0x80 /* Transmitter DMA select */
+#define MXC_UARTCR5_RDMAS	0x20 /* Receiver DMA select */
+/* Bit definations of Modem */
+#define MXC_UARTMODEM_RXRTSE	0x08 /* Enable receiver request-to-send */
+#define MXC_UARTMODEM_TXRTSPOL	0x04 /* Select transmitter RTS polarity */
+#define MXC_UARTMODEM_TXRTSE	0x02 /* Enable transmitter request-to-send */
+#define MXC_UARTMODEM_TXCTSE	0x01 /* Enable transmitter CTS clear-to-send */
+/* Bit definations of EDR */
+#define MXC_UARTEDR_NOISY	0x80 /* Current dataword received with noise */
+#define MXC_UARTEDR_PARITYE	0x40 /* Dataword received with parity error */
+/* Bit definations of Infrared reg(IR) */
+#define MXC_UARTIR_IREN		0x04 /* Infrared enable */
+#define MXC_UARTIR_TNP_MASK	0x03 /* Transmitter narrow pluse */
+#define MXC_UARTIR_TNP_OFF	0
+/* Bit definations of FIFO parameter reg */
+#define MXC_UARTPFIFO_TXFE	0x80 /* Transmit fifo enable */
+#define MXC_UARTPFIFO_TXFIFOSIZE_MASK	0x7
+#define MXC_UARTPFIFO_TXFIFOSIZE_OFF	4
+#define MXC_UARTPFIFO_RXFE	0x08 /* Receiver fifo enable */
+#define MXC_UARTPFIFO_RXFIFOSIZE_MASK	0x7
+#define MXC_UARTPFIFO_RXFIFOSIZE_OFF	0
+/* Bit definations of FIFO control reg */
+#define MXC_UARTCFIFO_TXFLUSH	0x80 /* Transmit FIFO/buffer flush */
+#define MXC_UARTCFIFO_RXFLUSH	0x40 /* Receive FIFO/buffer flush */
+#define MXC_UARTCFIFO_RXOFE	0x04 /* Receive fifo overflow INT enable */
+#define MXC_UARTCFIFO_TXOFE	0x02 /* Transmit fifo overflow INT enable */
+#define MXC_UARTCFIFO_RXUFE	0x01 /* Receive fifo underflow INT enable */
+/* Bit definations of FIFO status reg */
+#define MXC_UARTSFIFO_TXEMPT	0x80 /* Transmit fifo/buffer empty */
+#define MXC_UARTSFIFO_RXEMPT	0x40 /* Receive fifo/buffer empty */
+#define MXC_UARTSFIFO_RXOF	0x04 /* Rx buffer overflow flag */
+#define MXC_UARTSFIFO_TXOF	0x02 /* Tx buffer overflow flag */
+#define MXC_UARTSFIFO_RXUF	0x01 /* Rx buffer underflow flag */
+
+
+
+#undef CONFIG_MVF_SERIAL_DMA
+/* follow IMX dev node number */
+#define SERIAL_IMX_MAJOR	207
+#define MINOR_START		24
+#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 "MVF-uart"
+
+#define UART_NR 6
+
+struct mvf_port {
+	struct uart_port	port;
+	unsigned int		old_status;
+	int			txirq, rxirq, rtsirq;
+	unsigned int		have_rtscts:1;
+	unsigned int		use_dcedte:1;
+	unsigned int		use_irda:1;
+	unsigned int		irda_inv_rx:1;
+	unsigned int		irda_inv_tx:1;
+	unsigned int		fifo_en:1; /* enable FIFO mode */
+	unsigned int		mark_en:1; /* enable Mark address match */
+	unsigned int		format_9bits:1; /* 9bits data format */
+	unsigned short		trcv_delay; /* transceiver delay */
+	unsigned char		ma_addr; /* Match address */
+	struct clk		*clk;
+
+	unsigned int		tx_fifo_size, rx_fifo_size;
+
+	/* DMA fields */
+	int			enable_dma;
+	unsigned long		dma_tx_ch; /* configured eDMA channel */
+	struct imx_dma_data	dma_data;
+	struct dma_chan		*dma_chan_rx, *dma_chan_tx;
+	struct scatterlist	rx_sgl, tx_sgl;
+	void			*rx_buf;
+	unsigned char		*tx_buf;
+	unsigned int		rx_bytes, tx_bytes;
+	struct work_struct	tsk_dma_rx, tsk_dma_tx;
+	unsigned int		dma_tx_nents;
+	bool			dma_is_rxing, dma_is_txing;
+	wait_queue_head_t	dma_wait;
+};
+
+#ifdef CONFIG_IRDA
+#define USE_IRDA(sport)	((sport)->use_irda)
+#else
+#define USE_IRDA(sport)	(0)
+#endif
+enum mvf_uart_type {
+	MVF600_UART,
+};
+
+struct mvf_uart_data {
+	unsigned uts_reg;
+	enum mvf_uart_type devtype;
+};
+
+static struct mvf_uart_data mvf_uart_devdata[] = {
+	[MVF600_UART] = {
+		.devtype = MVF600_UART,
+	},
+};
+
+static struct platform_device_id mvf_uart_devtype[] = {
+	{
+		.name = "mvf600-uart",
+		.driver_data = (kernel_ulong_t) &mvf_uart_devdata[MVF600_UART],
+	}, {
+		/* sentinel */
+	}
+};
+MODULE_DEVICE_TABLE(platform, mvf_uart_devtype);
+
+static struct of_device_id mvf_uart_dt_ids[] = {
+	{
+		.compatible = "fsl,mvf-uart",
+		.data = &mvf_uart_devdata[MVF600_UART],
+	},
+	{ /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, mvf_uart_dt_ids);
+
+/*
+ * interrupts disabled on entry
+ */
+static void mvf_stop_tx(struct uart_port *port)
+{
+	struct mvf_port *sport = (struct mvf_port *)port;
+	unsigned char temp;
+
+	temp = readb(sport->port.membase + MXC_UARTCR2);
+	writeb(temp & ~(MXC_UARTCR2_TIE | MXC_UARTCR2_TCIE),
+			sport->port.membase + MXC_UARTCR2);
+}
+
+/*
+ * interrupts disabled on entry
+ */
+static void mvf_stop_rx(struct uart_port *port)
+{
+	struct mvf_port *sport = (struct mvf_port *)port;
+	unsigned char temp;
+
+	/* if the DMA RX thread is running, wait for it to finish. */
+	if (sport->enable_dma && sport->dma_is_rxing)
+		return;
+
+	temp = readb(sport->port.membase + MXC_UARTCR2);
+	writeb(temp & ~MXC_UARTCR2_RE, sport->port.membase + MXC_UARTCR2);
+}
+
+ /* modem status update function */
+static void mvf_enable_ms(struct uart_port *port)
+{
+}
+
+static inline void mvf_transmit_buffer(struct mvf_port *sport)
+{
+	struct circ_buf *xmit = &sport->port.state->xmit;
+
+	while (!uart_circ_empty(xmit) && (sport->fifo_en == 1 ?
+	(readb(sport->port.membase + MXC_UARTTCFIFO) < sport->tx_fifo_size) :
+		(readb(sport->port.membase + MXC_UARTSR1) & MXC_UARTSR1_TDRE)
+		)) {
+		/* send out xmit->buf[xmit->tail] */
+		writeb(xmit->buf[xmit->tail], sport->port.membase + MXC_UARTDR);
+		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))
+		mvf_stop_tx(&sport->port);
+}
+
+/*
+ * interrupts disabled on entry
+ */
+static void mvf_start_tx(struct uart_port *port)
+{
+	struct mvf_port *sport = (struct mvf_port *)port;
+	unsigned char temp;
+
+	if (!sport->enable_dma) {
+		temp = readb(sport->port.membase + MXC_UARTCR2);
+		writeb(temp | MXC_UARTCR2_TIE,
+				sport->port.membase + MXC_UARTCR2);
+	}
+
+	if (sport->enable_dma) {
+		schedule_work(&sport->tsk_dma_tx);
+		return;
+	}
+
+	if (readb(sport->port.membase + MXC_UARTSR1) & MXC_UARTSR1_TDRE)
+		mvf_transmit_buffer(sport);
+}
+
+
+static irqreturn_t mvf_txint(int irq, void *dev_id)
+{
+	struct mvf_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 */
+		writeb(sport->port.x_char, sport->port.membase + MXC_UARTDR);
+		goto out;
+	}
+
+	if (uart_circ_empty(xmit) || uart_tx_stopped(&sport->port)) {
+		mvf_stop_tx(&sport->port);
+		goto out;
+	}
+
+	mvf_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 mvf_rxint(int irq, void *dev_id)
+{
+	struct mvf_port *sport = dev_id;
+	unsigned int flg, ignored = 0;
+	struct tty_port *port = &sport->port.state->port;
+	unsigned long flags;
+	unsigned char r8, rx, sr;
+
+	spin_lock_irqsave(&sport->port.lock, flags);
+
+	while (!(readb(sport->port.membase + MXC_UARTSFIFO) &
+				MXC_UARTSFIFO_RXEMPT)) {
+		flg = TTY_NORMAL;
+		sport->port.icount.rx++;
+
+		/*  To clear the FE, OR, NF, FE, PE flags when set,
+		 *  read SR1 then read DR
+		 */
+		sr = readb(sport->port.membase + MXC_UARTSR1);
+
+		r8 = readb(sport->port.membase + MXC_UARTCR3) & MXC_UARTCR3_R8;
+		rx = readb(sport->port.membase + MXC_UARTDR);
+
+		if (sport->mark_en &&
+			(sport->format_9bits ? r8 : (rx == sport->ma_addr)))
+				continue;
+
+		if (uart_handle_sysrq_char(&sport->port, (unsigned char)rx))
+			continue;
+		if (sr & (MXC_UARTSR1_PE | MXC_UARTSR1_OR | MXC_UARTSR1_FE)) {
+			if (sr & MXC_UARTSR1_PE)
+				sport->port.icount.parity++;
+			else if (sr & MXC_UARTSR1_FE)
+				sport->port.icount.frame++;
+			if (sr & MXC_UARTSR1_OR)
+				sport->port.icount.overrun++;
+
+			if (sr & sport->port.ignore_status_mask) {
+				if (++ignored > 100)
+					goto out;
+				continue;
+			}
+
+			sr &= sport->port.read_status_mask;
+
+			if (sr & MXC_UARTSR1_PE)
+				flg = TTY_PARITY;
+			else if (sr & MXC_UARTSR1_FE)
+				flg = TTY_FRAME;
+			if (sr & MXC_UARTSR1_OR)
+				flg = TTY_OVERRUN;
+
+#ifdef SUPPORT_SYSRQ
+			sport->port.sysrq = 0;
+#endif
+		}
+
+		tty_insert_flip_char(port, rx, flg);
+	}
+
+out:
+	spin_unlock_irqrestore(&sport->port.lock, flags);
+
+	tty_flip_buffer_push(port);
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t mvf_int(int irq, void *dev_id)
+{
+	struct mvf_port *sport = dev_id;
+	unsigned int sts;
+
+	sts = readb(sport->port.membase + MXC_UARTSR1);
+
+	if (sts & MXC_UARTSR1_RDRF)
+			mvf_rxint(irq, dev_id);
+
+	if (sts & MXC_UARTSR1_TDRE &&
+		!(readb(sport->port.membase + MXC_UARTCR5) &
+			MXC_UARTCR5_TDMAS))
+		mvf_txint(irq, dev_id);
+
+	return IRQ_HANDLED;
+}
+
+/*
+ * Return TIOCSER_TEMT when transmitter is not busy.
+ */
+static unsigned int mvf_tx_empty(struct uart_port *port)
+{
+	struct mvf_port *sport = (struct mvf_port *)port;
+
+	return (readb(sport->port.membase + MXC_UARTSR1) & MXC_UARTSR1_TC) ?
+		TIOCSER_TEMT : 0;
+}
+
+static unsigned int mvf_get_mctrl(struct uart_port *port)
+{
+	struct mvf_port *sport = (struct mvf_port *)port;
+	unsigned int tmp = 0;
+	unsigned char reg;
+
+	reg = readb(sport->port.membase + MXC_UARTMODEM);
+	if (reg & MXC_UARTMODEM_TXCTSE)
+		tmp |= TIOCM_CTS;
+
+	if (reg & MXC_UARTMODEM_RXRTSE)
+		tmp |= TIOCM_RTS;
+
+	return tmp;
+}
+
+static void mvf_set_mctrl(struct uart_port *port, unsigned int mctrl)
+{
+	struct mvf_port *sport = (struct mvf_port *)port;
+	unsigned long temp;
+
+	temp = readb(sport->port.membase + MXC_UARTMODEM) &
+			~(MXC_UARTMODEM_RXRTSE | MXC_UARTMODEM_TXCTSE);
+
+	if (mctrl & TIOCM_RTS)
+		temp |= MXC_UARTMODEM_RXRTSE;
+	if (mctrl & TIOCM_CTS)
+		temp |= MXC_UARTMODEM_TXCTSE;
+
+	writeb(temp, sport->port.membase + MXC_UARTMODEM);
+}
+
+/*
+ * Interrupts always disabled.
+ */
+static void mvf_break_ctl(struct uart_port *port, int break_state)
+{
+	struct mvf_port *sport = (struct mvf_port *)port;
+	unsigned long flags;
+	unsigned char temp;
+
+	spin_lock_irqsave(&sport->port.lock, flags);
+
+	temp = readb(sport->port.membase + MXC_UARTCR2) & ~MXC_UARTCR2_SBK;
+
+	if (break_state != 0)
+		temp |= MXC_UARTCR2_SBK;
+
+	writeb(temp, sport->port.membase + MXC_UARTCR2);
+
+	spin_unlock_irqrestore(&sport->port.lock, flags);
+}
+
+#define TXTL 2
+#define RXTL 1
+
+static int mvf_setup_watermark(struct mvf_port *sport, unsigned int mode)
+{
+	unsigned char val, old_cr2, cr2;
+
+	/* set receiver/transmitter trigger level. */
+	old_cr2 = cr2 = readb(sport->port.membase + MXC_UARTCR2);
+	cr2 &= ~(MXC_UARTCR2_TIE | MXC_UARTCR2_TCIE | MXC_UARTCR2_TE |
+			MXC_UARTCR2_RIE | MXC_UARTCR2_RE);
+	writeb(cr2, sport->port.membase + MXC_UARTCR2);
+
+	val = TXTL;
+	writeb(val, sport->port.membase + MXC_UARTTWFIFO);
+	val = RXTL;
+	writeb(val, sport->port.membase + MXC_UARTRWFIFO);
+
+	/* Enable Tx and Rx FIFO */
+	val = readb(sport->port.membase + MXC_UARTPFIFO);
+	sport->tx_fifo_size = 0x1 << (((val >> MXC_UARTPFIFO_TXFIFOSIZE_OFF) &
+		MXC_UARTPFIFO_TXFIFOSIZE_MASK) + 1);
+	sport->rx_fifo_size = 0x1 << (((val >> MXC_UARTPFIFO_RXFIFOSIZE_OFF) &
+		MXC_UARTPFIFO_RXFIFOSIZE_MASK) + 1);
+	writeb(val | MXC_UARTPFIFO_TXFE | MXC_UARTPFIFO_RXFE,
+			sport->port.membase + MXC_UARTPFIFO);
+
+	/* Flush the Tx and Rx FIFO to a known state */
+	writeb(MXC_UARTCFIFO_TXFLUSH | MXC_UARTCFIFO_RXFLUSH,
+			sport->port.membase + MXC_UARTCFIFO);
+
+	/* restore CR2 */
+	writeb(old_cr2, sport->port.membase + MXC_UARTCR2);
+
+	return 0;
+}
+
+
+static int mvf_startup(struct uart_port *port)
+{
+	struct mvf_port *sport = (struct mvf_port *)port;
+	int retval;
+	unsigned long flags, temp;
+
+	if (sport->fifo_en)
+		mvf_setup_watermark(sport, 0);
+
+	temp = readb(sport->port.membase + MXC_UARTCR2);
+
+	writeb(temp & ~MXC_UARTCR2_RIE, sport->port.membase + MXC_UARTCR2);
+
+	/*
+	 * Allocate the IRQ(s)
+	 * Vybrid chips only have one interrupt.
+	 */
+	retval = request_irq(sport->port.irq, mvf_int, 0,
+				DRIVER_NAME, sport);
+	if (retval)
+		goto error_out1;
+
+	/* Enable the DMA ops for uart. */
+	if (sport->enable_dma) {
+		sport->dma_is_txing = 0;
+
+		/* enable DMA request generation */
+		temp = readb(sport->port.membase + MXC_UARTCR5);
+		temp |= MXC_UARTCR5_TDMAS;
+		writeb(temp, sport->port.membase + MXC_UARTCR5);
+
+		init_waitqueue_head(&sport->dma_wait);
+	}
+
+	spin_lock_irqsave(&sport->port.lock, flags);
+
+	/* Finally, clear and enable interrupts */
+	temp = readb(sport->port.membase + MXC_UARTCR2);
+	temp |= MXC_UARTCR2_RIE | MXC_UARTCR2_TIE;
+	writeb(temp, sport->port.membase + MXC_UARTCR2);
+
+	spin_unlock_irqrestore(&sport->port.lock, flags);
+
+	return 0;
+
+error_out1:
+	return retval;
+}
+
+static void mvf_shutdown(struct uart_port *port)
+{
+	struct mvf_port *sport = (struct mvf_port *)port;
+	unsigned char temp;
+	unsigned long flags;
+
+	spin_lock_irqsave(&sport->port.lock, flags);
+	temp = readb(sport->port.membase + MXC_UARTCR2);
+	temp &= ~(MXC_UARTCR2_TE | MXC_UARTCR2_RE);
+	writeb(temp, sport->port.membase + MXC_UARTCR2);
+	spin_unlock_irqrestore(&sport->port.lock, flags);
+
+	/*
+	 * 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 = readb(sport->port.membase + MXC_UARTCR2);
+	temp &= ~(MXC_UARTCR2_TIE | MXC_UARTCR2_TCIE | MXC_UARTCR2_RIE);
+	writeb(temp, sport->port.membase + MXC_UARTCR2);
+
+	spin_unlock_irqrestore(&sport->port.lock, flags);
+}
+
+static void
+mvf_set_termios(struct uart_port *port, struct ktermios *termios,
+		   struct ktermios *old)
+{
+	struct mvf_port *sport = (struct mvf_port *)port;
+	unsigned long flags;
+	unsigned char cr1, old_cr1, old_cr2, cr4, bdh, modem;
+	unsigned int  baud;
+	unsigned int old_csize = old ? old->c_cflag & CSIZE : CS8;
+	unsigned int sbr, brfa;
+
+	cr1 = old_cr1 = readb(sport->port.membase + MXC_UARTCR1);
+	old_cr2 = readb(sport->port.membase + MXC_UARTCR2);
+	cr4 = readb(sport->port.membase + MXC_UARTCR4);
+	bdh = readb(sport->port.membase + MXC_UARTBDH);
+	modem = readb(sport->port.membase + MXC_UARTMODEM);
+	/*
+	 * 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 CS8 and CS7,but CS7 must enable PE.
+	 * supported mode:
+	 *  - (7,e/o,1)
+	 *  - (8,n,1)
+	 *  - (8,m/s,1)
+	 *  - (8,e/o,1)
+	 */
+	while ((termios->c_cflag & CSIZE) != CS8 &&
+		(termios->c_cflag & CSIZE) != CS7) {
+		termios->c_cflag &= ~CSIZE;
+		termios->c_cflag |= old_csize;
+		old_csize = CS8;
+	}
+
+	if ((termios->c_cflag & CSIZE) == CS8 ||
+		(termios->c_cflag & CSIZE) == CS7)
+		cr1 = old_cr1 & ~MXC_UARTCR1_M;
+
+	if (termios->c_cflag & CMSPAR) {
+		if ((termios->c_cflag & CSIZE) != CS8) {
+			termios->c_cflag &= ~CSIZE;
+			termios->c_cflag |= CS8;
+		}
+		cr1 |= MXC_UARTCR1_M;
+	}
+
+	if (termios->c_cflag & CRTSCTS) {
+		if (sport->have_rtscts)
+			modem |= (MXC_UARTMODEM_RXRTSE | MXC_UARTMODEM_TXCTSE);
+
+	} else {
+		termios->c_cflag &= ~CRTSCTS;
+		modem &= ~(MXC_UARTMODEM_RXRTSE | MXC_UARTMODEM_TXCTSE);
+	}
+
+	if (termios->c_cflag & CSTOPB)
+		termios->c_cflag &= ~CSTOPB;
+
+	/* parity must enable when CS7 to match 8-bits format */
+	if ((termios->c_cflag & CSIZE) == CS7)
+		termios->c_cflag |= PARENB;
+
+	if ((termios->c_cflag & PARENB)) {
+		if (termios->c_cflag & CMSPAR) {
+			cr1 &= ~MXC_UARTCR1_PE;
+			cr1 |= MXC_UARTCR1_M;
+			sport->format_9bits = 1;
+		} else {
+			cr1 |= MXC_UARTCR1_PE;
+			if ((termios->c_cflag & CSIZE) == CS8)
+				cr1 |= MXC_UARTCR1_M;
+			if (termios->c_cflag & PARODD)
+				cr1 |= MXC_UARTCR1_PT;
+			else
+				cr1 &= ~MXC_UARTCR1_PT;
+		}
+	}
+
+	/*
+	 * Ask the core to calculate the divisor for us.
+	 */
+	baud = uart_get_baud_rate(port, termios, old, 50, port->uartclk / 16);
+
+	spin_lock_irqsave(&sport->port.lock, flags);
+
+	sport->port.read_status_mask = 0;
+	if (termios->c_iflag & INPCK)
+		sport->port.read_status_mask |=
+					(MXC_UARTSR1_FE | MXC_UARTSR1_PE);
+	if (termios->c_iflag & (BRKINT | PARMRK))
+		sport->port.read_status_mask |= MXC_UARTSR1_FE;
+
+	/*
+	 * Characters to ignore
+	 */
+	sport->port.ignore_status_mask = 0;
+	if (termios->c_iflag & IGNPAR)
+		sport->port.ignore_status_mask |= MXC_UARTSR1_PE;
+	if (termios->c_iflag & IGNBRK) {
+		sport->port.ignore_status_mask |= MXC_UARTSR1_FE;
+		/*
+		 * 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 |= MXC_UARTSR1_OR;
+	}
+
+	/*
+	 * Update the per-port timeout.
+	 */
+	uart_update_timeout(port, termios->c_cflag, baud);
+
+	/* wait transmit engin complete */
+	while (!(readb(sport->port.membase + MXC_UARTSR1) & MXC_UARTSR1_TC))
+		barrier();
+
+	writeb(old_cr2 & ~(MXC_UARTCR2_TE | MXC_UARTCR2_RE),
+			sport->port.membase + MXC_UARTCR2);
+
+	/* disable transmit and receive */
+	sbr = sport->port.uartclk / (16 * baud);
+	brfa = ((sport->port.uartclk - (16 * sbr * baud)) * 2)/baud;
+
+	bdh &= ~MXC_UARTBDH_SBR_MASK;
+	bdh |= (sbr >> 8) & 0x1F;
+
+	cr4 &= ~MXC_UARTCR4_BRFA_MASK;
+	brfa &= MXC_UARTCR4_BRFA_MASK;
+	writeb(cr4 | brfa, sport->port.membase + MXC_UARTCR4);
+	writeb(bdh, sport->port.membase + MXC_UARTBDH);
+	writeb(sbr & 0xFF, sport->port.membase + MXC_UARTBDL);
+	writeb(cr1, sport->port.membase + MXC_UARTCR1);
+	writeb(modem, sport->port.membase + MXC_UARTMODEM);
+
+	/* restore control register */
+	writeb(old_cr2, sport->port.membase + MXC_UARTCR2);
+
+	spin_unlock_irqrestore(&sport->port.lock, flags);
+}
+
+static const char *mvf_type(struct uart_port *port)
+{
+	struct mvf_port *sport = (struct mvf_port *)port;
+
+	return sport->port.type == PORT_MVF ? "MVF" : NULL;
+}
+
+/*
+ * Release the memory region(s) being used by 'port'.
+ */
+static void mvf_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, mmres->end - mmres->start + 1);
+}
+
+/*
+ * Request the memory region(s) being used by 'port'.
+ */
+static int mvf_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, mmres->end - mmres->start + 1,
+			"imx-uart");
+
+	return  ret ? 0 : -EBUSY;
+}
+
+/*
+ * Configure/autoconfigure the port.
+ */
+static void mvf_config_port(struct uart_port *port, int flags)
+{
+	struct mvf_port *sport = (struct mvf_port *)port;
+
+	if (flags & UART_CONFIG_TYPE &&
+	    mvf_request_port(&sport->port) == 0)
+		sport->port.type = PORT_MVF;
+}
+
+/*
+ * Verify the new serial_struct (for TIOCSSERIAL).
+ * The only change we allow are to the flags and type, and
+ * even then only between PORT_MVF and PORT_UNKNOWN
+ */
+static int
+mvf_verify_port(struct uart_port *port, struct serial_struct *ser)
+{
+	struct mvf_port *sport = (struct mvf_port *)port;
+	int ret = 0;
+
+	if (ser->type != PORT_UNKNOWN && ser->type != PORT_MVF)
+		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 (sport->port.iobase != ser->port)
+		ret = -EINVAL;
+	if (ser->hub6 != 0)
+		ret = -EINVAL;
+	return ret;
+}
+
+
+static struct uart_ops mvf_pops = {
+	.tx_empty	= mvf_tx_empty,
+	.set_mctrl	= mvf_set_mctrl,
+	.get_mctrl	= mvf_get_mctrl,
+	.stop_tx	= mvf_stop_tx,
+	.start_tx	= mvf_start_tx,
+	.stop_rx	= mvf_stop_rx,
+	.enable_ms	= mvf_enable_ms,
+	.break_ctl	= mvf_break_ctl,
+	.startup	= mvf_startup,
+	.shutdown	= mvf_shutdown,
+	.set_termios	= mvf_set_termios,
+	.type		= mvf_type,
+	.release_port	= mvf_release_port,
+	.request_port	= mvf_request_port,
+	.config_port	= mvf_config_port,
+	.verify_port	= mvf_verify_port,
+};
+
+static struct mvf_port *mvf_ports[UART_NR];
+
+#ifdef CONFIG_SERIAL_MVF_CONSOLE
+static void mvf_console_putchar(struct uart_port *port, int ch)
+{
+	struct mvf_port *sport = (struct mvf_port *)port;
+
+	while (!(readb(sport->port.membase + MXC_UARTSR1) & MXC_UARTSR1_TDRE))
+		barrier();
+
+	writeb(ch, sport->port.membase + MXC_UARTDR);
+}
+
+/*
+ * Interrupts are disabled on entering
+ */
+static void
+mvf_console_write(struct console *co, const char *s, unsigned int count)
+{
+	struct mvf_port *sport = mvf_ports[co->index];
+	unsigned int  old_cr2, cr2;
+	unsigned long flags;
+
+	spin_lock_irqsave(&sport->port.lock, flags);
+	/*
+	 * First, save UCR1/2 and then disable interrupts
+	 */
+	cr2 = old_cr2 = readb(sport->port.membase + MXC_UARTCR2);
+
+	cr2 |= (MXC_UARTCR2_TE |  MXC_UARTCR2_RE);
+	cr2 &= ~(MXC_UARTCR2_TIE | MXC_UARTCR2_TCIE | MXC_UARTCR2_RIE);
+
+	writeb(cr2, sport->port.membase + MXC_UARTCR2);
+
+	uart_console_write(&sport->port, s, count, mvf_console_putchar);
+
+	/*
+	 * wait for transmitter finish complete and restore CR2
+	 */
+	while (!(readb(sport->port.membase + MXC_UARTSR1) & MXC_UARTSR1_TC))
+		;
+
+	writeb(old_cr2, sport->port.membase + MXC_UARTCR2);
+	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
+mvf_console_get_options(struct mvf_port *sport, int *baud,
+			   int *parity, int *bits)
+{
+
+	if (readb(sport->port.membase + MXC_UARTCR2) &
+			(MXC_UARTCR2_TE | MXC_UARTCR2)) {
+		/* ok, the port was enabled */
+		unsigned char cr1, bdh, bdl, brfa;
+		unsigned int sbr, uartclk;
+		unsigned int baud_raw;
+
+		cr1 = readb(sport->port.membase + MXC_UARTCR1);
+
+		*parity = 'n';
+		if (cr1 & MXC_UARTCR1_PE) {
+			if (cr1 & MXC_UARTCR1_PT)
+				*parity = 'o';
+			else
+				*parity = 'e';
+		}
+
+		if (cr1 & MXC_UARTCR1_M)
+			*bits = 9;
+		else
+			*bits = 8;
+
+		bdh = readb(sport->port.membase + MXC_UARTBDH) &
+						MXC_UARTBDH_SBR_MASK;
+		bdl = readb(sport->port.membase + MXC_UARTBDL);
+		sbr = bdh;
+		sbr <<= 8;
+		sbr |= bdl;
+		brfa = readb(sport->port.membase + MXC_UARTCR4) &
+						MXC_UARTCR4_BRFA_MASK;
+		uartclk = clk_get_rate(sport->clk);
+		/*
+		 * Baud = mod_clk/(16*(sbr[13]+(brfa)/32)
+		 */
+		baud_raw = uartclk/(16 * (sbr + brfa/32));
+
+		if (*baud != baud_raw)
+			printk(KERN_INFO "Serial: Console IMX "
+					"rounded baud rate from %d to %d\n",
+				baud_raw, *baud);
+	}
+}
+
+static int __init
+mvf_console_setup(struct console *co, char *options)
+{
+	struct mvf_port *sport;
+	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.
+	 */
+	if (co->index == -1 || co->index >= ARRAY_SIZE(mvf_ports))
+		co->index = 0;
+	sport = mvf_ports[co->index];
+
+	if (sport == NULL)
+		return -ENODEV;
+
+	if (options)
+		uart_parse_options(options, &baud, &parity, &bits, &flow);
+	else
+		mvf_console_get_options(sport, &baud, &parity, &bits);
+
+	if (sport->fifo_en == 1)
+		mvf_setup_watermark(sport, 0);
+
+	return uart_set_options(&sport->port, co, baud, parity, bits, flow);
+}
+
+static struct uart_driver mvf_reg;
+static struct console mvf_console = {
+	.name		= DEV_NAME,
+	.write		= mvf_console_write,
+	.device		= uart_console_device,
+	.setup		= mvf_console_setup,
+	.flags		= CON_PRINTBUFFER,
+	.index		= -1,
+	.data		= &mvf_reg,
+};
+
+#define MVF_CONSOLE	(&mvf_console)
+#else
+#define MVF_CONSOLE	NULL
+#endif
+
+static struct uart_driver mvf_reg = {
+	.owner          = THIS_MODULE,
+	.driver_name    = DRIVER_NAME,
+	.dev_name       = DEV_NAME,
+	.major          = SERIAL_IMX_MAJOR,
+	.minor          = MINOR_START,
+	.nr             = ARRAY_SIZE(mvf_ports),
+	.cons           = MVF_CONSOLE,
+};
+
+static int serial_mvf_suspend(struct platform_device *dev, pm_message_t state)
+{
+	struct mvf_port *sport = platform_get_drvdata(dev);
+
+	/* Enable UART wakeup */
+
+	if (sport)
+		uart_suspend_port(&mvf_reg, &sport->port);
+
+	return 0;
+}
+
+static int serial_mvf_resume(struct platform_device *dev)
+{
+	struct mvf_port *sport = platform_get_drvdata(dev);
+
+	if (sport)
+		uart_resume_port(&mvf_reg, &sport->port);
+
+	/* Disable UART wakeup */
+
+	return 0;
+}
+
+static int serial_mvf_probe_dt(struct mvf_port *sport,
+		struct platform_device *pdev)
+{
+	struct device_node *np = pdev->dev.of_node;
+	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-fifo-mode", NULL))
+		sport->fifo_en = 1;
+
+	return 0;
+}
+
+static int serial_mvf_probe(struct platform_device *pdev)
+{
+	struct mvf_port *sport;
+	struct imxuart_platform_data *pdata;
+	void __iomem *base;
+	int ret = 0;
+	struct resource *res;
+
+	sport = devm_kzalloc(&pdev->dev, sizeof(*sport), GFP_KERNEL);
+	if (!sport)
+		return -ENOMEM;
+
+	pdev->dev.coherent_dma_mask = 0;
+
+	ret = serial_mvf_probe_dt(sport, pdev);
+	if (ret < 0)
+		return ret ;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!res)
+		return -ENODEV;
+
+	base = devm_ioremap(&pdev->dev, res->start, PAGE_SIZE);
+	if (!base)
+		return -ENOMEM;
+
+	sport->port.dev = &pdev->dev;
+	sport->port.mapbase = res->start;
+	sport->port.membase = base;
+	sport->port.type = PORT_MVF,
+	sport->port.iotype = UPIO_MEM;
+	sport->port.irq = platform_get_irq(pdev, 0);
+	sport->port.fifosize = 32;
+	sport->port.ops = &mvf_pops;
+	sport->port.flags = UPF_BOOT_AUTOCONF;
+	if (pdev->id >= 0)
+		sport->port.line = pdev->id;
+
+	sport->clk = devm_clk_get(&pdev->dev, "ipg");
+	if (IS_ERR(sport->clk)) {
+		ret = PTR_ERR(sport->clk);
+		dev_err(&pdev->dev, "failed to uart clk: %d\n", ret);
+		return ret;
+	}
+
+	clk_prepare_enable(sport->clk);
+
+	sport->port.uartclk = clk_get_rate(sport->clk);
+
+	mvf_ports[sport->port.line] = sport;
+
+	pdata = pdev->dev.platform_data;
+
+	/* all uarts support hardware RTS/CTS */
+	sport->have_rtscts = 1;
+
+	if (pdata && pdata->init) {
+		ret = pdata->init(pdev);
+		if (ret)
+			goto clkput;
+	}
+
+	ret = uart_add_one_port(&mvf_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);
+
+	return ret;
+}
+
+static int serial_mvf_remove(struct platform_device *pdev)
+{
+	struct imxuart_platform_data *pdata;
+	struct mvf_port *sport = platform_get_drvdata(pdev);
+
+	pdata = pdev->dev.platform_data;
+
+	platform_set_drvdata(pdev, NULL);
+
+	if (sport)
+		uart_remove_one_port(&mvf_reg, &sport->port);
+
+	clk_disable_unprepare(sport->clk);
+
+	if (pdata && pdata->exit)
+		pdata->exit(pdev);
+
+	return 0;
+}
+
+static struct platform_driver serial_mvf_driver = {
+	.probe		= serial_mvf_probe,
+	.remove		= serial_mvf_remove,
+
+	.suspend	= serial_mvf_suspend,
+	.resume		= serial_mvf_resume,
+	.id_table       = mvf_uart_devtype,
+	.driver		= {
+		.name	= "mvf-uart",
+		.owner	= THIS_MODULE,
+		.of_match_table = mvf_uart_dt_ids,
+	},
+};
+
+static int __init mvf_serial_init(void)
+{
+	int ret;
+
+	pr_info("Serial: Vybrid Family driver\n");
+
+	ret = uart_register_driver(&mvf_reg);
+	if (ret)
+		return ret;
+
+	ret = platform_driver_register(&serial_mvf_driver);
+	if (ret != 0)
+		uart_unregister_driver(&mvf_reg);
+
+	return 0;
+}
+
+static void __exit mvf_serial_exit(void)
+{
+	platform_driver_unregister(&serial_mvf_driver);
+	uart_unregister_driver(&mvf_reg);
+}
+
+module_init(mvf_serial_init);
+module_exit(mvf_serial_exit);
+
+MODULE_DESCRIPTION("Vybrid Family serial port driver");
+MODULE_LICENSE("GPL");
diff --git a/include/uapi/linux/serial_core.h b/include/uapi/linux/serial_core.h
index 74c2bf7..ffb7192 100644
--- a/include/uapi/linux/serial_core.h
+++ b/include/uapi/linux/serial_core.h
@@ -226,4 +226,7 @@
 /* Rocketport EXPRESS/INFINITY */
 #define PORT_RP2	102
 
+/* Freescale Vybrid uart */
+#define PORT_MVF	103
+
 #endif /* _UAPILINUX_SERIAL_CORE_H */
-- 
1.8.0

             reply	other threads:[~2013-04-12  7:10 UTC|newest]

Thread overview: 8+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2013-04-12  7:10 Jingchang Lu [this message]
2013-04-12  7:10 ` [PATCH] tty/serial: add MVF uart driver support Jingchang Lu
2013-04-12 12:30 ` Sascha Hauer
2013-04-12 12:30   ` Sascha Hauer
2013-04-13 20:41 ` Fabio Estevam
2013-04-13 20:41   ` Fabio Estevam
2013-04-14  8:42   ` Sascha Hauer
2013-04-14  8:42     ` Sascha Hauer

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=1365750655-3530-1-git-send-email-b35083@freescale.com \
    --to=b35083@freescale.com \
    --cc=linux-arm-kernel@lists.infradead.org \
    --cc=linux-serial@vger.kernel.org \
    --cc=shawn.guo@linaro.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.