From e3d13ff4b9d3b05d7a969153e2c049548e25deea Mon Sep 17 00:00:00 2001
From: Sascha Hauer <s.hauer@pengutronix.de>
Date: Sat, 5 Jul 2008 10:02:48 +0200
Subject: [PATCH] mxc: add MX3 support for i.MX internal UART driver

This patch adds MX3 support for the i.MX internal uart driver.

Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
---
 arch/arm/mach-mx3/Makefile          |   2 +-
 arch/arm/mach-mx3/devices.c         | 153 ++++++++++++++++++++++++++++
 drivers/serial/Kconfig              |   2 +-
 drivers/serial/imx.c                | 100 ++++++++++++++----
 include/asm-arm/arch-mxc/imx-uart.h |  32 ++++++
 5 files changed, 267 insertions(+), 22 deletions(-)
 create mode 100644 arch/arm/mach-mx3/devices.c
 create mode 100644 include/asm-arm/arch-mxc/imx-uart.h

diff --git a/arch/arm/mach-mx3/Makefile b/arch/arm/mach-mx3/Makefile
index febb37f23935..a788cb0b793e 100644
--- a/arch/arm/mach-mx3/Makefile
+++ b/arch/arm/mach-mx3/Makefile
@@ -4,5 +4,5 @@
 
 # Object file lists.
 
-obj-y			:= mm.o time.o clock.o
+obj-y			:= mm.o time.o clock.o devices.o
 obj-$(CONFIG_MACH_MX31ADS)	+= mx31ads.o
diff --git a/arch/arm/mach-mx3/devices.c b/arch/arm/mach-mx3/devices.c
new file mode 100644
index 000000000000..1bc6d23a1d58
--- /dev/null
+++ b/arch/arm/mach-mx3/devices.c
@@ -0,0 +1,153 @@
+/*
+ * Copyright 2006-2007 Freescale Semiconductor, Inc. All Rights Reserved.
+ * Copyright 2008 Sascha Hauer, 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,
+	}, {
+		.start = MXC_INT_UART1,
+		.end = MXC_INT_UART1,
+		.flags = IORESOURCE_IRQ,
+	},
+};
+
+static struct platform_device mxc_uart_device0 = {
+	.name = "imx-uart",
+	.id = 0,
+	.resource = uart0,
+	.num_resources = ARRAY_SIZE(uart0),
+};
+
+static struct resource uart1[] = {
+	{
+		.start = UART2_BASE_ADDR,
+		.end = UART2_BASE_ADDR + 0x0B5,
+		.flags = IORESOURCE_MEM,
+	}, {
+		.start = MXC_INT_UART2,
+		.end = MXC_INT_UART2,
+		.flags = IORESOURCE_IRQ,
+	},
+};
+
+static struct platform_device mxc_uart_device1 = {
+	.name = "imx-uart",
+	.id = 1,
+	.resource = uart1,
+	.num_resources = ARRAY_SIZE(uart1),
+};
+
+static struct resource uart2[] = {
+	{
+		.start = UART3_BASE_ADDR,
+		.end = UART3_BASE_ADDR + 0x0B5,
+		.flags = IORESOURCE_MEM,
+	}, {
+		.start = MXC_INT_UART3,
+		.end = MXC_INT_UART3,
+		.flags = IORESOURCE_IRQ,
+	},
+};
+
+static struct platform_device mxc_uart_device2 = {
+	.name = "imx-uart",
+	.id = 2,
+	.resource = uart2,
+	.num_resources = ARRAY_SIZE(uart2),
+};
+
+static struct resource uart3[] = {
+	{
+		.start = UART4_BASE_ADDR,
+		.end = UART4_BASE_ADDR + 0x0B5,
+		.flags = IORESOURCE_MEM,
+	}, {
+		.start = MXC_INT_UART4,
+		.end = MXC_INT_UART4,
+		.flags = IORESOURCE_IRQ,
+	},
+};
+
+static struct platform_device mxc_uart_device3 = {
+	.name = "imx-uart",
+	.id = 3,
+	.resource = uart3,
+	.num_resources = ARRAY_SIZE(uart3),
+};
+
+static struct resource uart4[] = {
+	{
+		.start = UART5_BASE_ADDR,
+		.end = UART5_BASE_ADDR + 0x0B5,
+		.flags = IORESOURCE_MEM,
+	}, {
+		.start = MXC_INT_UART5,
+		.end = MXC_INT_UART5,
+		.flags = IORESOURCE_IRQ,
+	},
+};
+
+static struct platform_device mxc_uart_device4 = {
+	.name = "imx-uart",
+	.id = 4,
+	.resource = uart4,
+	.num_resources = ARRAY_SIZE(uart4),
+};
+
+/*
+ * 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;
+}
+
diff --git a/drivers/serial/Kconfig b/drivers/serial/Kconfig
index 9bc42763623c..0843c5400684 100644
--- a/drivers/serial/Kconfig
+++ b/drivers/serial/Kconfig
@@ -753,7 +753,7 @@ config BFIN_UART3_CTSRTS
 
 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
diff --git a/drivers/serial/imx.c b/drivers/serial/imx.c
index 9e2162ebf1bb..549440b098b2 100644
--- a/drivers/serial/imx.c
+++ b/drivers/serial/imx.c
@@ -62,6 +62,11 @@
 #define UBIR  0xa4 /* BRM Incremental Register */
 #define UBMR  0xa8 /* BRM Modulator Register */
 #define UBRC  0xac /* Baud Rate Count Register */
+#ifdef 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 */
@@ -71,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)
@@ -90,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
+#ifdef 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 */
@@ -164,8 +175,19 @@
 #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
+
+#ifdef CONFIG_ARCH_MX3
+#define SERIAL_IMX_MAJOR        207
+#define MINOR_START	        16
+#define DEV_NAME		"ttymxc"
+#define MAX_INTERNAL_IRQ	MXC_MAX_INT_LINES
+#endif
 
 /*
  * This determines how often we check the modem status signals
@@ -409,6 +431,26 @@ static irqreturn_t imx_rxint(int irq, void *dev_id)
 	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.
  */
@@ -514,21 +556,34 @@ static int imx_startup(struct uart_port *port)
 	writel(temp & ~UCR4_DREN, sport->port.membase + UCR4);
 
 	/*
-	 * Allocate the IRQ
+	 * Allocate the IRQ(s) i.MX1 has three interrupts whereas later
+	 * chips only have one interrupt.
 	 */
-	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 :
+	if (sport->txirq > 0) {
+		retval = request_irq(sport->rxirq, imx_rxint, 0,
+				DRIVER_NAME, sport);
+		if (retval)
+			goto error_out1;
+
+		retval = request_irq(sport->txirq, imx_txint, 0,
+				DRIVER_NAME, sport);
+		if (retval)
+			goto error_out2;
+
+		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
@@ -553,9 +608,11 @@ static int imx_startup(struct uart_port *port)
 	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;
 }
@@ -573,9 +630,12 @@ static void imx_shutdown(struct uart_port *port)
 	/*
 	 * Free the interrupts
 	 */
-	free_irq(sport->rtsirq, sport);
-	free_irq(sport->txirq, sport);
-	free_irq(sport->rxirq, sport);
+	if (sport->txirq > 0) {
+		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.
@@ -973,7 +1033,7 @@ imx_console_setup(struct console *co, char *options)
 
 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,
@@ -990,7 +1050,7 @@ static struct console imx_console = {
 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),
diff --git a/include/asm-arm/arch-mxc/imx-uart.h b/include/asm-arm/arch-mxc/imx-uart.h
new file mode 100644
index 000000000000..83fb72c4048a
--- /dev/null
+++ b/include/asm-arm/arch-mxc/imx-uart.h
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2008 by Sascha Hauer <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.
+ */
+
+#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
-- 
2.30.9