Subject: [@num@/@total@] i.MX: add MX3/MX2 support for i.MX internal UART driver
From: Sascha Hauer <s.hauer@pengutronix.de>

This patch adds MX2 and MX3 support for the i.MX internal
uart driver. Note that on MX3 we do not have any clock
functions, so you have to adjust MX3_UART_REFFREQ for now.

Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>

---

 arch/arm/mach-mx3/Makefile          |    2 
 arch/arm/mach-mx3/devices.c         |  122 ++++++++++++
 arch/arm/mach-mx3/mx31ads.c         |    7 
 drivers/serial/Kconfig              |    2 
 drivers/serial/imx.c                |  359 +++++++++++++++++++++++++++++++-----
 include/asm-arm/arch-imx/imx-uart.h |    2 
 include/asm-arm/arch-mxc/imx-uart.h |   14 +
 7 files changed, 464 insertions(+), 44 deletions(-)

Index: arch/arm/mach-mx3/devices.c
===================================================================
--- /dev/null
+++ arch/arm/mach-mx3/devices.c
@@ -0,0 +1,122 @@
+/*
+ * Copyright 2006-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/platform_device.h>
+#include <linux/serial.h>
+#include <asm/hardware.h>
+#include <asm/arch/imx-uart.h>
+
+static struct resource uart0 = {
+	.start = UART1_BASE_ADDR,
+	.end = UART1_BASE_ADDR + 0x0B5,
+	.flags = IORESOURCE_MEM
+};
+
+static struct platform_device mxc_uart_device0 = {
+	.name = "imx-uart",
+	.id = 0,
+	.resource = &uart0,
+	.num_resources = 1,
+};
+
+static struct resource uart1 = {
+	.start = UART2_BASE_ADDR,
+	.end = UART2_BASE_ADDR + 0x0B5,
+	.flags = IORESOURCE_MEM
+};
+
+static struct platform_device mxc_uart_device1 = {
+	.name = "imx-uart",
+	.id = 1,
+	.resource = &uart1,
+	.num_resources = 1,
+};
+
+static struct resource uart2 = {
+	.start = UART3_BASE_ADDR,
+	.end = UART3_BASE_ADDR + 0x0B5,
+	.flags = IORESOURCE_MEM
+};
+
+static struct platform_device mxc_uart_device2 = {
+	.name = "imx-uart",
+	.id = 2,
+	.resource = &uart2,
+	.num_resources = 1,
+};
+
+static struct resource uart3 = {
+	.start = UART4_BASE_ADDR,
+	.end = UART4_BASE_ADDR + 0x0B5,
+	.flags = IORESOURCE_MEM
+};
+
+static struct platform_device mxc_uart_device3 = {
+	.name = "imx-uart",
+	.id = 3,
+	.resource = &uart3,
+	.num_resources = 1,
+};
+
+static struct resource uart4 = {
+	.start = UART5_BASE_ADDR,
+	.end = UART5_BASE_ADDR + 0x0B5,
+	.flags = IORESOURCE_MEM
+};
+
+static struct platform_device mxc_uart_device4 = {
+	.name = "imx-uart",
+	.id = 4,
+	.resource = &uart4,
+	.num_resources = 1,
+};
+
+/*
+ * Register only those UARTs that physically exist
+ */
+int __init imx_init_uart(int uart_no, struct imxuart_platform_data *pdata)
+{
+	switch (uart_no) {
+	case 0:
+		mxc_uart_device0.dev.platform_data = pdata;
+		platform_device_register(&mxc_uart_device0);
+		break;
+	case 1:
+		mxc_uart_device1.dev.platform_data = pdata;
+		platform_device_register(&mxc_uart_device1);
+		break;
+	case 2:
+		mxc_uart_device2.dev.platform_data = pdata;
+		platform_device_register(&mxc_uart_device2);
+		break;
+	case 3:
+		mxc_uart_device3.dev.platform_data = pdata;
+		platform_device_register(&mxc_uart_device3);
+		break;
+	case 4:
+		mxc_uart_device4.dev.platform_data = pdata;
+		platform_device_register(&mxc_uart_device4);
+		break;
+	default:
+		return -ENODEV;
+	}
+
+	return 0;
+}
+
Index: arch/arm/mach-mx3/mx31ads.c
===================================================================
--- arch/arm/mach-mx3/mx31ads.c.orig
+++ arch/arm/mach-mx3/mx31ads.c
@@ -29,6 +29,7 @@
 #include <asm/memory.h>
 #include <asm/mach/map.h>
 #include <asm/arch/common.h>
+#include <asm/arch/imx-uart.h>
 
 /*!
  * @file mx31ads.c
@@ -118,11 +119,17 @@
 	iotable_init(mx31ads_io_desc, ARRAY_SIZE(mx31ads_io_desc));
 }
 
+static struct imxuart_platform_data uart0_pdata = {
+	.flags = IMXUART_HAVE_RTSCTS,
+};
+
 /*!
  * Board specific initialization.
  */
 static void __init mxc_board_init(void)
 {
+	imx_init_uart(0, &uart0_pdata);
+
 	mxc_init_extuart();
 }
 
Index: drivers/serial/Kconfig
===================================================================
--- drivers/serial/Kconfig.orig
+++ drivers/serial/Kconfig
@@ -753,7 +753,7 @@
 
 config SERIAL_IMX
 	bool "IMX serial port support"
-	depends on ARM && ARCH_IMX
+	depends on ARM && (ARCH_IMX || ARCH_MXC)
 	select SERIAL_CORE
 	help
 	  If you have a machine based on a Motorola IMX CPU you
Index: drivers/serial/imx.c
===================================================================
--- drivers/serial/imx.c.orig
+++ drivers/serial/imx.c
@@ -40,6 +40,7 @@
 #include <linux/tty_flip.h>
 #include <linux/serial_core.h>
 #include <linux/serial.h>
+#include <linux/clk.h>
 
 #include <asm/io.h>
 #include <asm/irq.h>
@@ -61,6 +62,11 @@
 #define UBIR  0xa4 /* BRM Incremental Register */
 #define UBMR  0xa8 /* BRM Modulator Register */
 #define UBRC  0xac /* Baud Rate Count Register */
+#if defined CONFIG_ARCH_MX2 || defined CONFIG_ARCH_MX3
+#define ONEMS 0xb0 /* One Millisecond register */
+#define UTS   0xb4 /* UART Test Register */
+#endif
+#ifdef CONFIG_ARCH_IMX
 #define BIPR1 0xb0 /* Incremental Preset Register 1 */
 #define BIPR2 0xb4 /* Incremental Preset Register 2 */
 #define BIPR3 0xb8 /* Incremental Preset Register 3 */
@@ -70,6 +76,7 @@
 #define BMPR3 0xc8 /* BRM Modulator Register 3 */
 #define BMPR4 0xcc /* BRM Modulator Register 4 */
 #define UTS   0xd0 /* UART Test Register */
+#endif
 
 /* UART Control Register Bit Fields.*/
 #define  URXD_CHARRDY    (1<<15)
@@ -89,7 +96,12 @@
 #define  UCR1_RTSDEN     (1<<5)	 /* RTS delta interrupt enable */
 #define  UCR1_SNDBRK     (1<<4)	 /* Send break */
 #define  UCR1_TDMAEN     (1<<3)	 /* Transmitter ready DMA enable */
+#ifdef CONFIG_ARCH_IMX
 #define  UCR1_UARTCLKEN  (1<<2)	 /* UART clock enabled */
+#endif
+#if defined CONFIG_ARCH_MX2 || defined CONFIG_ARCH_MX3
+#define  UCR1_UARTCLKEN  (0)	 /* not present on mx2/mx3 */
+#endif
 #define  UCR1_DOZE       (1<<1)	 /* Doze */
 #define  UCR1_UARTEN     (1<<0)	 /* UART enabled */
 #define  UCR2_ESCI     	 (1<<15) /* Escape seq interrupt enable */
@@ -163,8 +175,20 @@
 #define  UTS_SOFTRST	 (1<<0)	 /* Software reset */
 
 /* We've been assigned a range on the "Low-density serial ports" major */
+#ifdef CONFIG_ARCH_IMX
 #define SERIAL_IMX_MAJOR	204
 #define MINOR_START		41
+#define DEV_NAME		"ttySMX"
+#define MAX_INTERNAL_IRQ	IMX_IRQS
+#endif
+
+#if defined CONFIG_ARCH_MX2 || defined CONFIG_ARCH_MX3
+#define SERIAL_IMX_MAJOR        207
+#define MINOR_START	        16
+#define DEV_NAME		"ttymxc"
+#define MX3_UART_REFFREQ	64000000
+#define MAX_INTERNAL_IRQ	MXC_MAX_INT_LINES
+#endif
 
 /*
  * This determines how often we check the modem status signals
@@ -182,8 +206,23 @@
 	unsigned int		old_status;
 	int			txirq,rxirq,rtsirq;
 	int			have_rtscts:1;
+	struct clk		*clk;
+	const char		*clk_name;
 };
 
+static int imx_uart_get_perclk(struct imx_port *sport)
+{
+#ifdef CONFIG_ARCH_IMX
+	return imx_get_perclk1();
+#endif
+#ifdef CONFIG_ARCH_MX2
+	return clk_get_rate(sport->clk);
+#endif
+#ifdef CONFIG_ARCH_MX3
+	return MX3_UART_REFFREQ; /* FIXME */
+#endif
+}
+
 /*
  * Handle any change of modem status signal since we were last called.
  */
@@ -405,6 +444,26 @@
 	return IRQ_HANDLED;
 }
 
+static irqreturn_t imx_int(int irq, void *dev_id)
+{
+	struct imx_port *sport = dev_id;
+	unsigned int sts;
+
+	sts = readl(sport->port.membase + USR1);
+
+	if (sts & USR1_RRDY)
+		imx_rxint(irq, dev_id);
+
+	if (sts & USR1_TRDY &&
+			readl(sport->port.membase + UCR1) & UCR1_TXMPTYEN)
+		imx_txint(irq, dev_id);
+
+	if (sts & USR1_RTSS)
+		imx_rtsint(irq, dev_id);
+
+	return IRQ_HANDLED;
+}
+
 /*
  * Return TIOCSER_TEMT when transmitter is not busy.
  */
@@ -472,12 +531,16 @@
 {
 	unsigned int val;
 	unsigned int ufcr_rfdiv;
+	unsigned int reffreq;
 
 	/* set receiver / transmitter trigger level.
 	 * RFDIV is set such way to satisfy requested uartclk value
 	 */
 	val = TXTL << 10 | RXTL;
-	ufcr_rfdiv = (imx_get_perclk1() + sport->port.uartclk / 2) / sport->port.uartclk;
+
+	reffreq = imx_uart_get_perclk(sport);
+
+	ufcr_rfdiv = (reffreq + sport->port.uartclk / 2) / sport->port.uartclk;
 
 	if(!ufcr_rfdiv)
 		ufcr_rfdiv = 1;
@@ -506,24 +569,37 @@
 	 * requesting IRQs
 	 */
 	temp = readl(sport->port.membase + UCR4);
+ 	temp |= (UCR1_RRDYEN | UCR1_RTSDEN | UCR1_UARTEN);
 	writel(temp & ~UCR4_DREN, sport->port.membase + UCR4);
 
 	/*
 	 * Allocate the IRQ
 	 */
-	retval = request_irq(sport->rxirq, imx_rxint, 0,
-			     DRIVER_NAME, sport);
-	if (retval) goto error_out1;
-
-	retval = request_irq(sport->txirq, imx_txint, 0,
-			     DRIVER_NAME, sport);
-	if (retval) goto error_out2;
+	if (sport->rxirq) {
+		retval = request_irq(sport->rxirq, imx_rxint, 0,
+				DRIVER_NAME, sport);
+		if (retval)
+			goto error_out1;
+
+		retval = request_irq(sport->txirq, imx_txint, 0,
+				DRIVER_NAME, sport);
+		if (retval)
+			goto error_out2;
 
-	retval = request_irq(sport->rtsirq, imx_rtsint,
-			     (sport->rtsirq < IMX_IRQS) ? 0 :
+		retval = request_irq(sport->rtsirq, imx_rtsint,
+			     (sport->rtsirq < MAX_INTERNAL_IRQ) ? 0 :
 			       IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING,
-			     DRIVER_NAME, sport);
-	if (retval) goto error_out3;
+				DRIVER_NAME, sport);
+		if (retval)
+			goto error_out3;
+	} else {
+		retval = request_irq(sport->port.irq, imx_int, 0,
+				DRIVER_NAME, sport);
+		if (retval) {
+			free_irq(sport->port.irq, sport);
+			goto error_out1;
+		}
+	}
 
 	/*
 	 * Finally, clear and enable interrupts
@@ -548,9 +624,11 @@
 	return 0;
 
 error_out3:
-	free_irq(sport->txirq, sport);
+	if (sport->txirq)
+		free_irq(sport->txirq, sport);
 error_out2:
-	free_irq(sport->rxirq, sport);
+	if (sport->rxirq)
+		free_irq(sport->rxirq, sport);
 error_out1:
 	return retval;
 }
@@ -568,9 +646,12 @@
 	/*
 	 * Free the interrupts
 	 */
-	free_irq(sport->rtsirq, sport);
-	free_irq(sport->txirq, sport);
-	free_irq(sport->rxirq, sport);
+	if (sport->rxirq) {
+		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.
@@ -589,6 +670,8 @@
 	unsigned long flags;
 	unsigned int ucr2, old_ucr1, old_txrxen, baud, quot;
 	unsigned int old_csize = old ? old->c_cflag & CSIZE : CS8;
+	unsigned int per_clk = imx_uart_get_perclk(sport);
+	unsigned int div, num, denom, ufcr;
 
 	/*
 	 * If we don't support modem control lines, don't allow
@@ -684,14 +767,27 @@
 			sport->port.membase + UCR2);
 	old_txrxen &= (UCR2_TXEN | UCR2_RXEN);
 
-	/* set the baud rate. We assume uartclk = 16 MHz
-	 *
-	 * baud * 16   UBIR - 1
-	 * --------- = --------
-	 *  uartclk    UBMR - 1
-	 */
-	writel((baud / 100) - 1, sport->port.membase + UBIR);
-	writel(10000 - 1, sport->port.membase + UBMR);
+	div = per_clk / ((baud * 16) + 1000);
+	if (div > 6)
+		div = 6;
+
+	sport->port.uartclk = per_clk / div;
+
+	num = (baud / 100) - 1;
+	denom = (sport->port.uartclk / 1600) - 1;
+	if ((denom < 65536) && (sport->port.uartclk > 1600)) {
+		writel(num, sport->port.membase + UBIR);
+		writel(denom, sport->port.membase + UBMR);
+	}
+
+	ufcr = readl(sport->port.membase + UFCR);
+	ufcr = (ufcr & (~UFCR_RFDIV)) |
+	    ((6 - div) << 7);
+	writel(ufcr, sport->port.membase + UFCR);
+
+#ifdef ONEMS
+	writel(per_clk / 1000, sport->port.membase + ONEMS);
+#endif
 
 	writel(old_ucr1, sport->port.membase + UCR1);
 
@@ -801,6 +897,7 @@
 	.verify_port	= imx_verify_port,
 };
 
+#ifdef CONFIG_ARCH_IMX
 static struct imx_port imx_ports[] = {
 	{
 	.txirq  = UART1_MINT_TX,
@@ -809,8 +906,8 @@
 	.port	= {
 		.type		= PORT_IMX,
 		.iotype		= UPIO_MEM,
-		.membase	= (void *)IMX_UART1_BASE,
-		.mapbase	= 0x00206000,
+ 		.membase	= (void *)IO_ADDRESS(IMX_UART1_BASE),
+ 		.mapbase	= IMX_UART1_BASE,
 		.irq		= UART1_MINT_RX,
 		.uartclk	= 16000000,
 		.fifosize	= 32,
@@ -825,8 +922,8 @@
 	.port	= {
 		.type		= PORT_IMX,
 		.iotype		= UPIO_MEM,
-		.membase	= (void *)IMX_UART2_BASE,
-		.mapbase	= 0x00207000,
+ 		.membase	= (void *)IO_ADDRESS(IMX_UART2_BASE),
+ 		.mapbase	= IMX_UART2_BASE,
 		.irq		= UART2_MINT_RX,
 		.uartclk	= 16000000,
 		.fifosize	= 32,
@@ -836,6 +933,168 @@
 	},
 	}
 };
+#endif
+
+#ifdef CONFIG_ARCH_MX2
+static struct imx_port imx_ports[] = {
+	{
+	.clk_name = "uart_clk.0",
+	.port	= {
+		.type		= PORT_IMX,
+		.iotype		= UPIO_MEM,
+		.membase	= (void *)IO_ADDRESS(UART1_BASE_ADDR),
+		.mapbase	= UART1_BASE_ADDR,
+		.irq		= INT_UART1,
+		.uartclk	= 16000000,
+		.fifosize	= 32,
+		.flags		= UPF_BOOT_AUTOCONF,
+		.ops		= &imx_pops,
+		.line		= 0,
+	},
+	}, {
+	.clk_name = "uart_clk.1",
+	.port	= {
+		.type		= PORT_IMX,
+		.iotype		= UPIO_MEM,
+		.membase	= (void *)IO_ADDRESS(UART2_BASE_ADDR),
+		.mapbase	= UART2_BASE_ADDR,
+		.irq		= INT_UART2,
+		.uartclk	= 16000000,
+		.fifosize	= 32,
+		.flags		= UPF_BOOT_AUTOCONF,
+		.ops		= &imx_pops,
+		.line		= 1,
+	},
+	}, {
+	.clk_name = "uart_clk.2",
+	.port	= {
+		.type		= PORT_IMX,
+		.iotype		= UPIO_MEM,
+		.membase	= (void *)IO_ADDRESS(UART3_BASE_ADDR),
+		.mapbase	= UART3_BASE_ADDR,
+		.irq		= INT_UART3,
+		.uartclk	= 16000000,
+		.fifosize	= 32,
+		.flags		= UPF_BOOT_AUTOCONF,
+		.ops		= &imx_pops,
+		.line		= 2,
+	},
+	}, {
+	.clk_name = "uart_clk.3",
+	.port	= {
+		.type		= PORT_IMX,
+		.iotype		= UPIO_MEM,
+		.membase	= (void *)IO_ADDRESS(UART4_BASE_ADDR),
+		.mapbase	= UART4_BASE_ADDR,
+		.irq		= INT_UART4,
+		.uartclk	= 16000000,
+		.fifosize	= 32,
+		.flags		= UPF_BOOT_AUTOCONF,
+		.ops		= &imx_pops,
+		.line		= 3,
+	},
+	}, {
+	.clk_name = "uart_clk.4",
+	.port	= {
+		.type		= PORT_IMX,
+		.iotype		= UPIO_MEM,
+		.membase	= (void *)IO_ADDRESS(UART5_BASE_ADDR),
+		.mapbase	= UART5_BASE_ADDR,
+		.irq		= INT_UART5,
+		.uartclk	= 16000000,
+		.fifosize	= 32,
+		.flags		= UPF_BOOT_AUTOCONF,
+		.ops		= &imx_pops,
+		.line		= 4,
+	},
+	}, {
+	.clk_name = "uart_clk.5",
+	.port	= {
+		.type		= PORT_IMX,
+		.iotype		= UPIO_MEM,
+		.membase	= (void *)IO_ADDRESS(UART6_BASE_ADDR),
+		.mapbase	= UART6_BASE_ADDR,
+		.irq		= INT_UART6,
+		.uartclk	= 16000000,
+		.fifosize	= 32,
+		.flags		= UPF_BOOT_AUTOCONF,
+		.ops		= &imx_pops,
+		.line		= 5,
+	},
+	}
+};
+#endif
+
+#ifdef CONFIG_ARCH_MX3
+static struct imx_port imx_ports[] = {
+	{
+	.port	= {
+		.type		= PORT_IMX,
+		.iotype		= UPIO_MEM,
+		.membase	= (void *)IO_ADDRESS(UART1_BASE_ADDR),
+		.mapbase	= UART1_BASE_ADDR,
+		.irq		= MXC_INT_UART1,
+		.uartclk	= 16000000,
+		.fifosize	= 32,
+		.flags		= UPF_BOOT_AUTOCONF,
+		.ops		= &imx_pops,
+		.line		= 0,
+	},
+	}, {
+	.port	= {
+		.type		= PORT_IMX,
+		.iotype		= UPIO_MEM,
+		.membase	= (void *)IO_ADDRESS(UART2_BASE_ADDR),
+		.mapbase	= UART2_BASE_ADDR,
+		.irq		= MXC_INT_UART2,
+		.uartclk	= 16000000,
+		.fifosize	= 32,
+		.flags		= UPF_BOOT_AUTOCONF,
+		.ops		= &imx_pops,
+		.line		= 1,
+	},
+	}, {
+	.port	= {
+		.type		= PORT_IMX,
+		.iotype		= UPIO_MEM,
+		.membase	= (void *)IO_ADDRESS(UART3_BASE_ADDR),
+		.mapbase	= UART3_BASE_ADDR,
+		.irq		= MXC_INT_UART3,
+		.uartclk	= 16000000,
+		.fifosize	= 32,
+		.flags		= UPF_BOOT_AUTOCONF,
+		.ops		= &imx_pops,
+		.line		= 2,
+	},
+	}, {
+	.port	= {
+		.type		= PORT_IMX,
+		.iotype		= UPIO_MEM,
+		.membase	= (void *)IO_ADDRESS(UART4_BASE_ADDR),
+		.mapbase	= UART4_BASE_ADDR,
+		.irq		= MXC_INT_UART4,
+		.uartclk	= 16000000,
+		.fifosize	= 32,
+		.flags		= UPF_BOOT_AUTOCONF,
+		.ops		= &imx_pops,
+		.line		= 3,
+	},
+	}, {
+	.port	= {
+		.type		= PORT_IMX,
+		.iotype		= UPIO_MEM,
+		.membase	= (void *)IO_ADDRESS(UART5_BASE_ADDR),
+		.mapbase	= UART5_BASE_ADDR,
+		.irq		= MXC_INT_UART5,
+		.uartclk	= 16000000,
+		.fifosize	= 32,
+		.flags		= UPF_BOOT_AUTOCONF,
+		.ops		= &imx_pops,
+		.line		= 4,
+	},
+	}
+};
+#endif
 
 /*
  * Setup the IMX serial ports.
@@ -944,7 +1203,8 @@
 		else
 			ucfr_rfdiv = 6 - ucfr_rfdiv;
 
-		uartclk = imx_get_perclk1();
+
+		uartclk = imx_uart_get_perclk(sport);
 		uartclk /= ucfr_rfdiv;
 
 		{	/*
@@ -985,7 +1245,12 @@
 	if (co->index == -1 || co->index >= ARRAY_SIZE(imx_ports))
 		co->index = 0;
 	sport = &imx_ports[co->index];
-
+#ifdef CONFIG_ARCH_MX2
+	if (sport->clk_name) {
+		sport->clk = clk_get(NULL, sport->clk_name);
+		clk_enable(sport->clk);
+	}
+#endif
 	if (options)
 		uart_parse_options(options, &baud, &parity, &bits, &flow);
 	else
@@ -998,7 +1263,7 @@
 
 static struct uart_driver imx_reg;
 static struct console imx_console = {
-	.name		= "ttySMX",
+	.name		= DEV_NAME,
 	.write		= imx_console_write,
 	.device		= uart_console_device,
 	.setup		= imx_console_setup,
@@ -1023,7 +1288,7 @@
 static struct uart_driver imx_reg = {
 	.owner          = THIS_MODULE,
 	.driver_name    = DRIVER_NAME,
-	.dev_name       = "ttySMX",
+	.dev_name       = DEV_NAME,
 	.major          = SERIAL_IMX_MAJOR,
 	.minor          = MINOR_START,
 	.nr             = ARRAY_SIZE(imx_ports),
@@ -1050,30 +1315,40 @@
         return 0;
 }
 
-static int serial_imx_probe(struct platform_device *dev)
+static int serial_imx_probe(struct platform_device *pdev)
 {
 	struct imxuart_platform_data *pdata;
 
-	imx_ports[dev->id].port.dev = &dev->dev;
+	imx_ports[pdev->id].port.dev = &pdev->dev;
 
-	pdata = (struct imxuart_platform_data *)dev->dev.platform_data;
+	pdata = (struct imxuart_platform_data *)pdev->dev.platform_data;
 	if(pdata && (pdata->flags & IMXUART_HAVE_RTSCTS))
-		imx_ports[dev->id].have_rtscts = 1;
+		imx_ports[pdev->id].have_rtscts = 0;
+
+	if (pdata->init)
+		pdata->init(pdev);
+
+	uart_add_one_port(&imx_reg, &imx_ports[pdev->id].port);
+	platform_set_drvdata(pdev, &imx_ports[pdev->id]);
 
-	uart_add_one_port(&imx_reg, &imx_ports[dev->id].port);
-	platform_set_drvdata(dev, &imx_ports[dev->id]);
 	return 0;
 }
 
-static int serial_imx_remove(struct platform_device *dev)
+static int serial_imx_remove(struct platform_device *pdev)
 {
-	struct imx_port *sport = platform_get_drvdata(dev);
+	struct imxuart_platform_data *pdata;
+	struct imx_port *sport = platform_get_drvdata(pdev);
 
-	platform_set_drvdata(dev, NULL);
+	pdata = (struct imxuart_platform_data *)pdev->dev.platform_data;
+
+	platform_set_drvdata(pdev, NULL);
 
 	if (sport)
 		uart_remove_one_port(&imx_reg, &sport->port);
 
+	if (pdata->exit)
+		pdata->exit(pdev);
+
 	return 0;
 }
 
Index: include/asm-arm/arch-imx/imx-uart.h
===================================================================
--- include/asm-arm/arch-imx/imx-uart.h.orig
+++ include/asm-arm/arch-imx/imx-uart.h
@@ -4,6 +4,8 @@
 #define IMXUART_HAVE_RTSCTS (1<<0)
 
 struct imxuart_platform_data {
+	int (*init)(struct platform_device *pdev);
+	void (*exit)(struct platform_device *pdev);
 	unsigned int flags;
 };
 
Index: include/asm-arm/arch-mxc/imx-uart.h
===================================================================
--- /dev/null
+++ include/asm-arm/arch-mxc/imx-uart.h
@@ -0,0 +1,14 @@
+#ifndef ASMARM_ARCH_UART_H
+#define ASMARM_ARCH_UART_H
+
+#define IMXUART_HAVE_RTSCTS (1<<0)
+
+struct imxuart_platform_data {
+	int (*init)(struct platform_device *pdev);
+	int (*exit)(struct platform_device *pdev);
+	unsigned int flags;
+};
+
+int __init imx_init_uart(int uart_no, struct imxuart_platform_data *pdata);
+
+#endif
Index: arch/arm/mach-mx3/Makefile
===================================================================
--- arch/arm/mach-mx3/Makefile.orig
+++ arch/arm/mach-mx3/Makefile
@@ -4,5 +4,5 @@
 
 # Object file lists.
 
-obj-y			:= mm.o time.o
+obj-y			:= mm.o time.o devices.o
 obj-$(CONFIG_MACH_MX31ADS)	+= mx31ads.o
