| serial-new-beta1.patch
|
| Files affected:
|   arch/arm/mach-s3c2410/Kconfig              |    6 	6 +	0 -	0 !
|   arch/arm/mach-s3c2410/Makefile             |    1 	1 +	0 -	0 !
|   arch/arm/mach-s3c2410/devs.c               |    6 	6 +	0 -	0 !
|   arch/arm/mach-s3c2410/mach-bast.c          |   54 	47 +	7 -	0 !
|   arch/arm/mach-s3c2410/mach-h1940.c         |    4 	1 +	3 -	0 !
|   arch/arm/mach-s3c2410/mach-smdk2410.c      |    6 	0 +	6 -	0 !
|   arch/arm/mach-s3c2410/mach-smdk2440.c      |  146 	146 +	0 -	0 !
|   arch/arm/mach-s3c2410/mach-vr1000.c        |   55 	49 +	6 -	0 !
|   arch/arm/mach-s3c2410/s3c2410.c            |   16 	4 +	12 -	0 !
|   arch/arm/mach-s3c2410/s3c2440.c            |   25 	21 +	4 -	0 !
|   arch/arm/mach-s3c2410/s3c2440.h            |    7 	6 +	1 -	0 !
|   drivers/serial/s3c2410.c                   | 1401 	1081 +	320 -	0 !
|   include/asm-arm/arch-s3c2410/dma.h         |    2 	1 +	1 -	0 !
|   include/asm-arm/arch-s3c2410/regs-serial.h |   90 	62 +	28 -	0 !
|   include/linux/serial_core.h                |    3 	3 +	0 -	0 !
|   15 files changed, 1434 insertions(+), 388 deletions(-)
|
| Ben Dooks, Thu, 04 Nov 2004 17:54:12 +0000

diff -urN -X ../dontdiff linux-2.6.10-rc1-bk14/include/linux/serial_core.h linux-2.6.10-rc1-bk14-serial1/include/linux/serial_core.h
--- linux-2.6.10-rc1-bk14/include/linux/serial_core.h	2004-11-04 16:58:44.000000000 +0000
+++ linux-2.6.10-rc1-bk14-serial1/include/linux/serial_core.h	2004-11-04 17:23:26.000000000 +0000
@@ -91,6 +91,9 @@
 /* MPC52xx type numbers */
 #define PORT_MPC52xx	59
 
+/* Samsung S3C2440 SoC */
+#define PORT_S3C2440	60
+
 #ifdef __KERNEL__
 
 #include <linux/config.h>
diff -urN -X ../dontdiff linux-2.6.10-rc1-bk14/include/asm-arm/arch-s3c2410/regs-serial.h linux-2.6.10-rc1-bk14-serial1/include/asm-arm/arch-s3c2410/regs-serial.h
--- linux-2.6.10-rc1-bk14/include/asm-arm/arch-s3c2410/regs-serial.h	2004-11-04 16:58:44.000000000 +0000
+++ linux-2.6.10-rc1-bk14-serial1/include/asm-arm/arch-s3c2410/regs-serial.h	2004-11-04 17:06:50.000000000 +0000
@@ -68,6 +68,12 @@
 #define S3C2410_LCON_STOPB	  (1<<2)
 #define S3C2410_LCON_IRM          (1<<6)
 
+#define S3C2440_UCON_CLKMASK	  (3<<10)
+#define S3C2440_UCON_PCLK	  (0<<10)
+#define S3C2440_UCON_UCLK	  (1<<10)
+#define S3C2440_UCON_PCLK2	  (2<<10)
+#define S3C2440_UCON_FCLK	  (3<<10)
+
 #define S3C2410_UCON_UCLK	  (1<<10)
 #define S3C2410_UCON_SBREAK	  (1<<4)
 
@@ -77,19 +83,35 @@
 #define S3C2410_UCON_RXIRQMODE	  (1<<0)
 #define S3C2410_UCON_RXFIFO_TOI	  (1<<7)
 
-#define S3C2410_UCON_DEFAULT	  (S3C2410_UCON_TXILEVEL | S3C2410_UCON_RXILEVEL \
-				   | S3C2410_UCON_TXIRQMODE | S3C2410_UCON_RXIRQMODE \
-				   | S3C2410_UCON_RXFIFO_TOI)
+#define S3C2410_UCON_DEFAULT	  (S3C2410_UCON_TXILEVEL  | \
+				   S3C2410_UCON_RXILEVEL  | \
+				   S3C2410_UCON_TXIRQMODE | \
+				   S3C2410_UCON_RXIRQMODE | \
+				   S3C2410_UCON_RXFIFO_TOI)
 
 #define S3C2410_UFCON_FIFOMODE	  (1<<0)
 #define S3C2410_UFCON_TXTRIG0	  (0<<6)
 #define S3C2410_UFCON_RXTRIG8	  (1<<4)
 #define S3C2410_UFCON_RXTRIG12	  (2<<4)
 
+/* S3C2440 FIFO trigger levels */
+#define S3C2440_UFCON_RXTRIG1	  (0<<4)
+#define S3C2440_UFCON_RXTRIG8	  (1<<4)
+#define S3C2440_UFCON_RXTRIG16	  (2<<4)
+#define S3C2440_UFCON_RXTRIG32	  (3<<4)
+
+#define S3C2440_UFCON_TXTRIG0	  (0<<6)
+#define S3C2440_UFCON_TXTRIG16	  (1<<6)
+#define S3C2440_UFCON_TXTRIG32	  (2<<6)
+#define S3C2440_UFCON_TXTRIG48	  (3<<6)
+
 #define S3C2410_UFCON_RESETBOTH	  (3<<1)
+#define S3C2410_UFCON_RESETTX	  (1<<2)
+#define S3C2410_UFCON_RESETRX	  (1<<1)
 
-#define S3C2410_UFCON_DEFAULT	  (S3C2410_UFCON_FIFOMODE | S3C2410_UFCON_TXTRIG0 \
-				  | S3C2410_UFCON_RXTRIG8 )
+#define S3C2410_UFCON_DEFAULT	  (S3C2410_UFCON_FIFOMODE | \
+				   S3C2410_UFCON_TXTRIG0  | \
+				   S3C2410_UFCON_RXTRIG8 )
 
 #define S3C2410_UFSTAT_TXFULL	  (1<<9)
 #define S3C2410_UFSTAT_RXFULL	  (1<<8)
@@ -111,32 +133,36 @@
 
 #define S3C2410_UERSTAT_OVERRUN	  (1<<0)
 #define S3C2410_UERSTAT_FRAME	  (1<<2)
-#define S3C2410_UERSTAT_ANY	  (S3C2410_UERSTAT_OVERRUN | S3C2410_UERSTAT_FRAME)
+#define S3C2410_UERSTAT_BREAK	  (1<<3)
+#define S3C2410_UERSTAT_ANY	  (S3C2410_UERSTAT_OVERRUN | \
+				   S3C2410_UERSTAT_FRAME | \
+				   S3C2410_UERSTAT_BREAK)
 
-/* fifo size information */
+#define S3C2410_UMSTAT_CTS	  (1<<0)
+#define S3C2410_UMSTAT_DeltaCTS	  (1<<2)
 
 #ifndef __ASSEMBLY__
-static inline int S3C2410_UFCON_RXC(int fcon)
-{
-	if (fcon & S3C2410_UFSTAT_RXFULL)
-		return 16;
-
-	return ((fcon) & S3C2410_UFSTAT_RXMASK) >> S3C2410_UFSTAT_RXSHIFT;
-}
-
-static inline int S3C2410_UFCON_TXC(int fcon)
-{
-	if (fcon & S3C2410_UFSTAT_TXFULL)
-		return 16;
 
-	return ((fcon) & S3C2410_UFSTAT_TXMASK) >> S3C2410_UFSTAT_TXSHIFT;
-}
-#endif /* __ASSEMBLY__ */
+/* struct s3c24xx_uart_clksrc
+ *
+ * this structure defines a named clock source that can be used for the
+ * uart, so that the best clock can be selected for the requested baud
+ * rate.
+ *
+ * min_baud and max_baud define the range of baud-rates this clock is
+ * acceptable for, if they are both zero, it is assumed any baud rate that
+ * can be generated from this clock will be used.
+ *
+ * divisor gives the divisor from the clock to the one seen by the uart
+*/
 
-#define S3C2410_UMSTAT_CTS	  (1<<0)
-#define S3C2410_UMSTAT_DeltaCTS	  (1<<2)
+struct s3c24xx_uart_clksrc {
+	const char	*name;
+	unsigned int	 divisor;
+	unsigned int	 min_baud;
+	unsigned int	 max_baud;
+};
 
-#ifndef __ASSEMBLY__
 /* configuration structure for per-machine configurations for the
  * serial port
  *
@@ -148,15 +174,23 @@
 	unsigned char	   hwport;	 /* hardware port number */
 	unsigned char	   unused;
 	unsigned short	   flags;
-
-	unsigned long	  *clock;	 /* pointer to clock rate */
+	unsigned long	   uart_flags;	 /* default uart flags */
 
 	unsigned long	   ucon;	 /* value of ucon for port */
 	unsigned long	   ulcon;	 /* value of ulcon for port */
 	unsigned long	   ufcon;	 /* value of ufcon for port */
+
+	struct s3c24xx_uart_clksrc *clocks;
+	unsigned int		    clocks_size;
 };
 
-extern struct s3c2410_uartcfg *s3c2410_uartcfgs;
+/* s3c24xx_uart_devs
+ *
+ * this is exported from the core as we cannot use driver_register(),
+ * or platform_add_device() before the console_initcall()
+*/
+
+extern struct platform_device *s3c24xx_uart_devs[3];
 
 #endif /* __ASSEMBLY__ */
 
diff -urN -X ../dontdiff linux-2.6.10-rc1-bk14/drivers/serial/s3c2410.c linux-2.6.10-rc1-bk14-serial1/drivers/serial/s3c2410.c
--- linux-2.6.10-rc1-bk14/drivers/serial/s3c2410.c	2004-10-18 22:54:08.000000000 +0100
+++ linux-2.6.10-rc1-bk14-serial1/drivers/serial/s3c2410.c	2004-11-04 17:50:29.000000000 +0000
@@ -1,7 +1,7 @@
 /*
- * linux/drivers/char/s3c2410.c
+ * linux/drivers/serial/s3c2410.c
  *
- * Driver for onboard UARTs on the Samsung S3C2410
+ * Driver for onboard UARTs on the Samsung S3C24XX
  *
  * Based on drivers/char/serial.c and drivers/char/21285.c
  *
@@ -9,54 +9,150 @@
  *
  * Changelog:
  *
- */
+ * 22-Jul-2004  BJD  Finished off device rewrite
+ *
+ * 21-Jul-2004  BJD  Thanks to <herbet@13thfloor.at> for pointing out
+ *                   problems with baud rate and loss of IR settings. Update
+ *                   to add configuration via platform_device structure
+ *
+ * 28-Sep-2004  BJD  Re-write for the following items
+ *		     - S3C2410 and S3C2440 serial support
+ *		     - Power Management support
+ *		     - Fix console via IrDA devices
+ *		     - SysReq (Herbert Pötzl)
+ *		     - Break character handling (Herbert Pötzl)
+ *		     - spin-lock initialisation (Dimitry Andric)
+ *		     - added clock control
+*/
+
+/* Hote on 2410 error handling
+ *
+ * The s3c2410 manual has a love/hate affair with the contents of the
+ * UERSTAT register in the UART blocks, and keeps marking some of the
+ * error bits as reserved. Having checked with the s3c2410x01,
+ * it copes with BREAKs properly, so I am happy to ignore the RESERVED
+ * feature from the latter versions of the manual.
+ *
+ * If it becomes aparrent that latter versions of the 2410 remove these
+ * bits, then action will have to be taken to differentiate the versions
+ * and change the policy on BREAK
+ *
+ * BJD, 04-Nov-2004
+*/
+
 #include <linux/config.h>
+
+#if defined(CONFIG_SERIAL_S3C2410_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ)
+#define SUPPORT_SYSRQ
+#endif
+
 #include <linux/module.h>
 #include <linux/tty.h>
 #include <linux/ioport.h>
 #include <linux/device.h>
 #include <linux/init.h>
+#include <linux/sysrq.h>
 #include <linux/console.h>
 #include <linux/serial_core.h>
 #include <linux/serial.h>
+#include <linux/delay.h>
 
 #include <asm/io.h>
 #include <asm/irq.h>
 
 #include <asm/hardware.h>
+#include <asm/hardware/clock.h>
+
 #include <asm/arch/regs-serial.h>
+#include <asm/arch/regs-gpio.h>
+
+#include <asm/mach-types.h>
+
+/* structures */
+
+struct s3c24xx_uart_info {
+	char			*name;
+	unsigned int		type;
+	unsigned int		fifosize;
+	unsigned long		rx_fifomask;
+	unsigned long		rx_fifoshift;
+	unsigned long		tx_fifomask;
+	unsigned long		tx_fifoshift;
+	unsigned long		tx_fifofull;
+
+	/* clock source control */
+
+	int (*get_clksrc)(struct uart_port *, struct s3c24xx_uart_clksrc *clk);
+	int (*set_clksrc)(struct uart_port *, struct s3c24xx_uart_clksrc *clk);
+};
+
+struct s3c24xx_uart_port {
+	unsigned char			rx_claimed;
+	unsigned char			tx_claimed;
+       
+	struct s3c24xx_uart_info	*info;
+	struct s3c24xx_uart_clksrc	*clksrc;
+	struct clk			*clk;
+	struct clk			*baudclk;
+	struct uart_port		port;
+};
+
+
+/* configuration defines */
 
 #if 0
-#include <asm/debug-ll.h>
-#define dbg(x...) llprintk(x)
+#if 1
+/* send debug to the low-level output routines */
+
+extern void printascii(const char *);
+
+static void
+s3c24xx_serial_dbg(const char *fmt, ...)
+{
+	va_list va;
+	char buff[256];
+
+	va_start(va, fmt);
+	vsprintf(buff, fmt, va);
+	va_end(va);
+
+	printascii(buff);
+}
+
+#define dbg(x...) s3c24xx_serial_dbg(x)
+
 #else
-#define dbg(x...)
+#define dbg(x...) printk(KERN_DEBUG "s3c24xx: ");
+#endif
+#else /* no debug */
+#define dbg(x...) do {} while(0)
 #endif
 
-#define SERIAL_S3C2410_NAME	"ttySAC"
-#define SERIAL_S3C2410_MAJOR	204
-#define SERIAL_S3C2410_MINOR	64
+/* UART name and device definitions */
+
+#define S3C24XX_SERIAL_NAME	"ttySAC"
+#define S3C24XX_SERIAL_DEVFS    "tts/"
+#define S3C24XX_SERIAL_MAJOR	204
+#define S3C24XX_SERIAL_MINOR	64
+
+
+/* conversion functions */
+
+#define s3c24xx_dev_to_port(__dev) (struct uart_port *)dev_get_drvdata(__dev)
+#define s3c24xx_dev_to_cfg(__dev) (struct s3c2410_uartcfg *)((__dev)->platform_data)
 
 /* we can support 3 uarts, but not always use them */
 
 #define NR_PORTS (3)
 
-static const char serial_s3c2410_name[] = "Samsung S3C2410 UART";
-
 /* port irq numbers */
 
 #define TX_IRQ(port) ((port)->irq + 1)
 #define RX_IRQ(port) ((port)->irq)
 
-#define tx_enabled(port) ((port)->unused[0])
-#define rx_enabled(port) ((port)->unused[1])
-
-/* flag to ignore all characters comming in */
-#define RXSTAT_DUMMY_READ (0x10000000)
-
-/* access functions */
+/* register access controls */
 
-#define portaddr(port, reg) ((void *)((port)->membase + (reg)))
+#define portaddr(port, reg) ((void __iomem *)((port)->membase + (reg)))
 
 #define rd_regb(port, reg) (__raw_readb(portaddr(port, reg)))
 #define rd_regl(port, reg) (__raw_readl(portaddr(port, reg)))
@@ -67,58 +163,146 @@
 #define wr_regl(port, reg, val) \
   do { __raw_writel(val, portaddr(port, reg)); } while(0)
 
+/* macros to change one thing to another */
+
+#define tx_enabled(port) ((port)->unused[0])
+#define rx_enabled(port) ((port)->unused[1])
+
+/* flag to ignore all characters comming in */
+#define RXSTAT_DUMMY_READ (0x10000000)
+
+static int s3c24xx_serial_get_portno(struct uart_port *port);
+
+static inline struct s3c24xx_uart_port *to_ourport(struct uart_port *port)
+{
+	return container_of(port, struct s3c24xx_uart_port, port);
+}
+
+/* translate a port to the device name */
+
+static char *s3c24xx_serial_portname(struct uart_port *port)
+{
+	return to_platform_device(port->dev)->name;
+}
+
+static int s3c24xx_serial_txempty_nofifo(struct uart_port *port)
+{
+	return (rd_regl(port, S3C2410_UTRSTAT) & S3C2410_UTRSTAT_TXE);
+}
+
+static void s3c24xx_serial_rx_enable(struct uart_port *port)
+{
+	unsigned long flags;
+	unsigned int ucon, ufcon;
+	int count = 10000;
+
+	spin_lock_irqsave(&port->lock, flags);
+
+	while (--count && !s3c24xx_serial_txempty_nofifo(port))
+		udelay(100);
+
+	ufcon = rd_regl(port, S3C2410_UFCON);
+	ufcon |= S3C2410_UFCON_RESETRX;
+	wr_regl(port, S3C2410_UFCON, ufcon);
+
+	ucon = rd_regl(port, S3C2410_UCON);
+	ucon |= S3C2410_UCON_RXIRQMODE;
+	wr_regl(port, S3C2410_UCON, ucon);
+
+	rx_enabled(port) = 1;
+	spin_unlock_irqrestore(&port->lock, flags);
+}
 
+static void s3c24xx_serial_rx_disable(struct uart_port *port)
+{
+	unsigned long flags;
+	unsigned int ucon;
 
+	spin_lock_irqsave(&port->lock, flags);
 
-/* code */
+	ucon = rd_regl(port, S3C2410_UCON);
+	ucon &= ~S3C2410_UCON_RXIRQMODE;
+	wr_regl(port, S3C2410_UCON, ucon);
+
+	rx_enabled(port) = 0;
+	spin_unlock_irqrestore(&port->lock, flags);
+}
 
 static void
-serial_s3c2410_stop_tx(struct uart_port *port, unsigned int tty_stop)
+s3c24xx_serial_stop_tx(struct uart_port *port, unsigned int tty_stop)
 {
 	if (tx_enabled(port)) {
 		disable_irq(TX_IRQ(port));
 		tx_enabled(port) = 0;
+		if (port->flags & UPF_CONS_FLOW)
+			s3c24xx_serial_rx_enable(port);
 	}
 }
 
 static void
-serial_s3c2410_start_tx(struct uart_port *port, unsigned int tty_start)
+s3c24xx_serial_start_tx(struct uart_port *port, unsigned int tty_start)
 {
 	if (!tx_enabled(port)) {
+		if (port->flags & UPF_CONS_FLOW)
+			s3c24xx_serial_rx_disable(port);
+
 		enable_irq(TX_IRQ(port));
 		tx_enabled(port) = 1;
 	}
 }
 
-static void serial_s3c2410_stop_rx(struct uart_port *port)
+
+static void s3c24xx_serial_stop_rx(struct uart_port *port)
 {
 	if (rx_enabled(port)) {
-		dbg("serial_s3c2410_stop_rx: port=%p\n", port);
+		dbg("s3c24xx_serial_stop_rx: port=%p\n", port);
 		disable_irq(RX_IRQ(port));
 		rx_enabled(port) = 0;
 	}
 }
 
-static void serial_s3c2410_enable_ms(struct uart_port *port)
+static void s3c24xx_serial_enable_ms(struct uart_port *port)
+{
+}
+
+static struct s3c24xx_uart_info *s3c24xx_port_to_info(struct uart_port *port)
 {
+	return to_ourport(port)->info;
 }
 
+static struct s3c2410_uartcfg *s3c24xx_port_to_cfg(struct uart_port *port)
+{
+	if (port->dev == NULL)
+		return NULL;
+
+	return (struct s3c2410_uartcfg *)port->dev->platform_data;
+}
+
+static int s3c24xx_serial_rx_fifocnt(struct s3c24xx_uart_port *ourport,
+				     unsigned long ufstat)
+{
+	struct s3c24xx_uart_info *info = ourport->info;
+	return (ufstat & info->rx_fifomask) >> info->rx_fifoshift;
+} 
+
+
 /* ? - where has parity gone?? */
 #define S3C2410_UERSTAT_PARITY (0x1000)
 
 static irqreturn_t
-serial_s3c2410_rx_chars(int irq, void *dev_id, struct pt_regs *regs)
+s3c24xx_serial_rx_chars(int irq, void *dev_id, struct pt_regs *regs)
 {
-	struct uart_port *port = dev_id;
+	struct s3c24xx_uart_port *ourport = dev_id;
+	struct uart_port *port = &ourport->port;
 	struct tty_struct *tty = port->info->tty;
-	unsigned int ufcon, ch, rxs, ufstat;
-	int max_count = 256;
+	unsigned int ufcon, ch, ufstat, uerstat;
+	int max_count = 32;
 
 	while (max_count-- > 0) {
 		ufcon = rd_regl(port, S3C2410_UFCON);
 		ufstat = rd_regl(port, S3C2410_UFSTAT);
 
-		if (S3C2410_UFCON_RXC(ufstat) == 0)
+		if (s3c24xx_serial_rx_fifocnt(ourport, ufstat) == 0)
 			break;
 
 		if (tty->flip.count >= TTY_FLIPBUF_SIZE) {
@@ -129,35 +313,71 @@
 			}
 		}
 
+		uerstat = rd_regl(port, S3C2410_UERSTAT);
 		ch = rd_regb(port, S3C2410_URXH);
 
+		if (port->flags & UPF_CONS_FLOW) {
+			int txe = s3c24xx_serial_txempty_nofifo(port);
+
+			if (rx_enabled(port)) {
+				if (!txe) {
+					rx_enabled(port) = 0;
+					continue;
+				}
+			} else {
+				if (txe) {
+					ufcon |= S3C2410_UFCON_RESETRX;
+					wr_regl(port, S3C2410_UFCON, ufcon);
+					rx_enabled(port) = 1;
+					goto out;
+				}
+				continue;
+			}
+		}
+
+		/* insert the character into the buffer */
+
 		*tty->flip.char_buf_ptr = ch;
 		*tty->flip.flag_buf_ptr = TTY_NORMAL;
 		port->icount.rx++;
 
-		rxs = rd_regb(port, S3C2410_UERSTAT) | RXSTAT_DUMMY_READ;
+		if (uerstat & S3C2410_UERSTAT_ANY) {
+			dbg("rxerr: port %d ch=0x%02x, rxs=0x%08x\n",
+			    s3c24xx_serial_get_portno(port), ch, uerstat);
+
+			/* check for break */
+			if (uerstat & S3C2410_UERSTAT_BREAK) {
+				dbg("break!\n");
+				port->icount.brk++;
+				if (uart_handle_break(port))
+				    goto ignore_char;
+			}
 
-		if (rxs & S3C2410_UERSTAT_ANY) {
-			if (rxs & S3C2410_UERSTAT_FRAME)
+			if (uerstat & S3C2410_UERSTAT_FRAME)
 				port->icount.frame++;
-			if (rxs & S3C2410_UERSTAT_OVERRUN)
+			if (uerstat & S3C2410_UERSTAT_OVERRUN)
 				port->icount.overrun++;
 
-			rxs &= port->read_status_mask;
+			uerstat &= port->read_status_mask;
 
-			if (rxs & S3C2410_UERSTAT_PARITY)
+			if (uerstat & S3C2410_UERSTAT_BREAK)
+				*tty->flip.flag_buf_ptr = TTY_BREAK;
+			else if (uerstat & S3C2410_UERSTAT_PARITY)
 				*tty->flip.flag_buf_ptr = TTY_PARITY;
-			else if (rxs & ( S3C2410_UERSTAT_FRAME | S3C2410_UERSTAT_OVERRUN))
+			else if (uerstat & ( S3C2410_UERSTAT_FRAME | S3C2410_UERSTAT_OVERRUN))
 				*tty->flip.flag_buf_ptr = TTY_FRAME;
 		}
 
-		if ((rxs & port->ignore_status_mask) == 0) {
+		if (uart_handle_sysrq_char(port, ch, regs))
+			goto ignore_char;
+		
+		if ((uerstat & port->ignore_status_mask) == 0) {
 			tty->flip.flag_buf_ptr++;
 			tty->flip.char_buf_ptr++;
 			tty->flip.count++;
 		}
 
-		if ((rxs & S3C2410_UERSTAT_OVERRUN) &&
+		if ((uerstat & S3C2410_UERSTAT_OVERRUN) &&
 		    tty->flip.count < TTY_FLIPBUF_SIZE) {
 			/*
 			 * Overrun is special, since it's reported
@@ -168,6 +388,9 @@
 			*tty->flip.flag_buf_ptr++ = TTY_OVERRUN;
 			tty->flip.count++;
 		}
+
+	ignore_char:
+		continue;
 	}
 	tty_flip_buffer_push(tty);
 
@@ -176,9 +399,10 @@
 }
 
 static irqreturn_t
-serial_s3c2410_tx_chars(int irq, void *dev_id, struct pt_regs *regs)
+s3c24xx_serial_tx_chars(int irq, void *dev_id, struct pt_regs *regs)
 {
-	struct uart_port *port = (struct uart_port *)dev_id;
+	struct s3c24xx_uart_port *ourport = dev_id;
+	struct uart_port *port = &ourport->port;
 	struct circ_buf *xmit = &port->info->xmit;
 	int count = 256;
 
@@ -194,14 +418,14 @@
 	*/
 
 	if (uart_circ_empty(xmit) || uart_tx_stopped(port)) {
-		serial_s3c2410_stop_tx(port, 0);
+		s3c24xx_serial_stop_tx(port, 0);
 		goto out;
 	}
 
 	/* try and drain the buffer... */
 
 	while (!uart_circ_empty(xmit) && count-- > 0) {
-		if (rd_regl(port, S3C2410_UFSTAT) & S3C2410_UFSTAT_TXFULL)
+		if (rd_regl(port, S3C2410_UFSTAT) & ourport->info->tx_fifofull)
 			break;
 
 		wr_regb(port, S3C2410_UTXH, xmit->buf[xmit->tail]);
@@ -213,22 +437,33 @@
 		uart_write_wakeup(port);
 
 	if (uart_circ_empty(xmit))
-		serial_s3c2410_stop_tx(port, 0);
+		s3c24xx_serial_stop_tx(port, 0);
 
  out:
 	return IRQ_HANDLED;
 }
 
 static unsigned int
-serial_s3c2410_tx_empty(struct uart_port *port)
+s3c24xx_serial_tx_empty(struct uart_port *port)
 {
-	unsigned int ufcon = rd_regl(port, S3C2410_UFCON);
-	return (S3C2410_UFCON_TXC(ufcon) != 0) ? 0 : TIOCSER_TEMT;
+	struct s3c24xx_uart_info *info = s3c24xx_port_to_info(port);
+	unsigned long ufstat = rd_regl(port, S3C2410_UFSTAT);
+	unsigned long ufcon = rd_regl(port, S3C2410_UFCON);
+
+	if (ufcon & S3C2410_UFCON_FIFOMODE) {
+		if ((ufstat & info->tx_fifomask) != 0 ||
+		    (ufstat & info->tx_fifofull))
+			return 0;
+
+		return 1;
+	}
+	
+	return s3c24xx_serial_txempty_nofifo(port);
 }
 
 /* no modem control lines */
 static unsigned int
-serial_s3c2410_get_mctrl(struct uart_port *port)
+s3c24xx_serial_get_mctrl(struct uart_port *port)
 {
 	unsigned int umstat = rd_regb(port,S3C2410_UMSTAT);
 
@@ -239,12 +474,12 @@
 }
 
 static void
-serial_s3c2410_set_mctrl(struct uart_port *port, unsigned int mctrl)
+s3c24xx_serial_set_mctrl(struct uart_port *port, unsigned int mctrl)
 {
 	/* todo - possibly remove AFC and do manual CTS */
 }
 
-static void serial_s3c2410_break_ctl(struct uart_port *port, int break_state)
+static void s3c24xx_serial_break_ctl(struct uart_port *port, int break_state)
 {
 	unsigned long flags;
 	unsigned int ucon;
@@ -263,46 +498,233 @@
 	spin_unlock_irqrestore(&port->lock, flags);
 }
 
-static int serial_s3c2410_startup(struct uart_port *port)
+static void s3c24xx_serial_shutdown(struct uart_port *port)
 {
+	struct s3c24xx_uart_port *ourport = to_ourport(port);
+
+	if (ourport->tx_claimed) {
+		free_irq(TX_IRQ(port), ourport);
+		tx_enabled(port) = 0;
+		ourport->tx_claimed = 0;
+	}
+
+	if (ourport->rx_claimed) {
+		free_irq(RX_IRQ(port), ourport);
+		ourport->rx_claimed = 0;
+		rx_enabled(port) = 0;
+	}
+
+	if (ourport->clk && 0)
+		clk_disable(ourport->clk);
+}
+
+static int s3c24xx_serial_startup(struct uart_port *port)
+{
+	struct s3c24xx_uart_port *ourport = to_ourport(port);
+	unsigned long flags;
 	int ret;
 
-	tx_enabled(port) = 1;
-	rx_enabled(port) = 1;
+	dbg("s3c24xx_serial_startup: port=%p (%d) (%08lx,%p)\n",
+	    port, s3c24xx_serial_get_portno(port),
+	    port->mapbase, port->membase );
+
+	if (ourport->clk)
+		clk_enable(ourport->clk);
+
+	local_irq_save(flags);
 
-	dbg("serial_s3c2410_startup: port=%p (%p)\n",
-	    port, port->mapbase);
+	rx_enabled(port) = 1;
 
-	ret = request_irq(RX_IRQ(port), serial_s3c2410_rx_chars, 0,
-			  serial_s3c2410_name, port);
+	ret = request_irq(RX_IRQ(port),
+			  s3c24xx_serial_rx_chars, 0,
+			  s3c24xx_serial_portname(port), ourport);
 
-	if (ret != 0)
+	if (ret != 0) {
+		printk(KERN_ERR "cannot get irq %d\n", RX_IRQ(port));
 		return ret;
+	}
 
-	ret = request_irq(TX_IRQ(port), serial_s3c2410_tx_chars, 0,
-			  serial_s3c2410_name, port);
+	ourport->rx_claimed = 1;
+
+	dbg("requesting tx irq...\n");
+
+	tx_enabled(port) = 1;
+
+	ret = request_irq(TX_IRQ(port),
+			  s3c24xx_serial_tx_chars, 0,
+			  s3c24xx_serial_portname(port), ourport);
 
 	if (ret) {
-		free_irq(RX_IRQ(port), port);
-		return ret;
+		printk(KERN_ERR "cannot get irq %d\n", TX_IRQ(port));
+		goto err;
 	}
 
+	ourport->tx_claimed = 1;
+
+	dbg("s3c24xx_serial_startup ok\n");
+
 	/* the port reset code should have done the correct
-	 * register setup for the port controls */
+	 * register setup for the port controls */	
+
+	local_irq_restore(flags);
+	return ret;
 
+ err:
+	s3c24xx_serial_shutdown(port);
+	local_irq_restore(flags);
 	return ret;
 }
 
-static void serial_s3c2410_shutdown(struct uart_port *port)
+/* baud rate calculation
+ *
+ * The UARTs on the S3C2410/S3C2440 can take their clocks from a number
+ * of different sources, including the peripheral clock ("pclk") and an
+ * external clock ("uclk"). The S3C2440 also adds the core clock ("fclk")
+ * with a programmable extra divisor.
+ *
+ * The following code goes through the clock sources, and calculates the
+ * baud clocks (and the resultant actual baud rates) and then tries to
+ * pick the closest one and select that.
+ *
+ * NOTES:
+ *	1) there is no current code to properly select/deselect FCLK on
+ *	   the s3c2440, so only specify FCLK or non-FCLK in the clock
+ *	   sources for the UART
+ *
+*/
+
+
+#define MAX_CLKS (8)
+
+static struct s3c24xx_uart_clksrc tmp_clksrc = {
+	.name		= "pclk",
+	.min_baud	= 0,
+	.max_baud	= 0,
+	.divisor	= 1,
+};
+
+static inline int
+s3c24xx_serial_getsource(struct uart_port *port, struct s3c24xx_uart_clksrc *c)
+{
+	struct s3c24xx_uart_info *info = s3c24xx_port_to_info(port);
+
+	return (info->get_clksrc)(port, c);
+}
+
+static inline int
+s3c24xx_serial_setsource(struct uart_port *port, struct s3c24xx_uart_clksrc *c)
+{
+	struct s3c24xx_uart_info *info = s3c24xx_port_to_info(port);
+
+	return (info->set_clksrc)(port, c);
+}
+
+
+struct baud_calc {
+	struct s3c24xx_uart_clksrc	*clksrc;
+	unsigned int			 calc;
+	unsigned int			 quot;
+	struct clk			*src;
+};
+
+static int s3c24xx_serial_calcbaud(struct baud_calc *calc,
+				   struct uart_port *port,
+				   struct s3c24xx_uart_clksrc *clksrc,
+				   unsigned int baud)
 {
-	free_irq(TX_IRQ(port), port);
-	free_irq(RX_IRQ(port), port);
+	unsigned long rate;
+
+	calc->src = clk_get(port->dev, clksrc->name);
+	if (calc->src == NULL)
+		return 0;
+
+	rate = clk_get_rate(calc->src);
+
+	calc->clksrc = clksrc;
+	calc->quot = (rate + (8 * baud)) / (16 * baud);
+	calc->calc = (rate / (calc->quot * 16));
+
+	calc->quot--;
+	return 1;
+}
+
+static unsigned int
+s3c24xx_serial_getclk(struct uart_port *port,
+		      struct s3c24xx_uart_clksrc **clksrc,
+		      struct clk **clk,
+		      unsigned int baud)
+{
+	struct s3c2410_uartcfg *cfg = s3c24xx_port_to_cfg(port);
+	struct s3c24xx_uart_clksrc *clkp;
+	struct baud_calc res[MAX_CLKS];
+	struct baud_calc *resptr, *best, *sptr;
+	int i;
+
+	clkp = cfg->clocks;
+	best = NULL;
+
+	if (cfg->clocks_size < 2) {
+		if (cfg->clocks_size == 0)
+			clkp = &tmp_clksrc;
+
+		s3c24xx_serial_calcbaud(res, port, clkp, baud);		
+		best = res;
+
+	} else {
+		resptr = res;
+
+		for (i = 0; i < cfg->clocks_size; i++, clkp++) {
+			if (s3c24xx_serial_calcbaud(resptr, port, clkp, baud))
+				resptr++;
+		}
+	}
+
+	/* ok, we now need to select the best clock we found */
+
+	if (!best) {
+		unsigned int deviation = (1<<30)|((1<<30)-1);
+		int calc_deviation;
+
+		for (sptr = res; sptr < resptr; sptr++) {
+			printk(KERN_DEBUG 
+			       "found clk %p (%s) quot %d, calc %d\n",
+			       sptr->clksrc, sptr->clksrc->name,
+			       sptr->quot, sptr->calc);
+			
+			calc_deviation = baud - sptr->calc;
+			if (calc_deviation < 0)
+				calc_deviation = -calc_deviation;
+
+			if (calc_deviation < deviation) {
+				best = sptr;
+				deviation = calc_deviation;
+			}
+		}
+
+		printk(KERN_DEBUG "best %p (deviation %d)\n", best, deviation);
+		best = res;
+	}
+
+	printk(KERN_DEBUG "selected clock %p (%s) quot %d, calc %d\n",
+	       best->clksrc, best->clksrc->name, best->quot, best->calc);
+
+	/* store results to pass back */
+
+	*clksrc = best->clksrc;
+	*clk    = best->src;
+	
+	return best->quot;
 }
 
 static void
-serial_s3c2410_set_termios(struct uart_port *port, struct termios *termios,
+s3c24xx_serial_set_termios(struct uart_port *port,
+			   struct termios *termios,
 			   struct termios *old)
 {
+	struct s3c2410_uartcfg *cfg = s3c24xx_port_to_cfg(port);
+	struct s3c24xx_uart_port *ourport = to_ourport(port);
+	struct s3c24xx_uart_clksrc *clksrc;
+	struct clk *clk;
 	unsigned long flags;
 	unsigned int baud, quot;
 	unsigned int ulcon;
@@ -314,15 +736,24 @@
 	termios->c_cflag |= CLOCAL;
 
 	/*
-	 * We don't support BREAK character recognition.
-	 */
-	termios->c_iflag &= ~(IGNBRK | BRKINT);
-
-	/*
 	 * Ask the core to calculate the divisor for us.
 	 */
-	baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk/16);
-	quot = uart_get_divisor(port, baud);
+	baud = uart_get_baud_rate(port, termios, old, 0, 115200*8);
+	//quot = uart_get_divisor(port, baud) - 1;
+
+	if (baud == 38400 && (port->flags & UPF_SPD_MASK) == UPF_SPD_CUST)
+		quot = port->custom_divisor;
+	else
+		quot = s3c24xx_serial_getclk(port, &clksrc, &clk, baud);
+
+	/* check to see if we need  to change clock source */
+
+	if (ourport->clksrc != clksrc || ourport->baudclk != clk) {
+		s3c24xx_serial_setsource(port, clksrc);
+
+		ourport->clksrc = clksrc;
+		ourport->baudclk = clk;
+	}
 
 	switch (termios->c_cflag & CSIZE) {
 	case CS5:
@@ -344,6 +775,9 @@
 		break;
 	}
 
+	/* preserve original lcon IR settings */
+	ulcon |= (cfg->ulcon & S3C2410_LCON_IRM);
+
 	if (termios->c_cflag & CSTOPB)
 		ulcon |= S3C2410_LCON_STOPB;
 
@@ -356,18 +790,12 @@
 		ulcon |= S3C2410_LCON_PNONE;
 	}
 
-	/*
-	if (port->fifosize)
-	enable_fifo()
-	*/
-
 	spin_lock_irqsave(&port->lock, flags);
 
-	dbg("setting ulcon to %08x\n", ulcon);
-	//dbg("<flushing output from serial>\n");
+	dbg("setting ulcon to %08x, brddiv to %d\n", ulcon, quot);
 
-	/* set the ulcon register */
 	wr_regl(port, S3C2410_ULCON, ulcon);
+	wr_regl(port, S3C2410_UBRDIV, quot);
 
 	dbg("uart: ulcon = 0x%08x, ucon = 0x%08x, ufcon = 0x%08x\n",
 	    rd_regl(port, S3C2410_ULCON),
@@ -404,117 +832,163 @@
 	spin_unlock_irqrestore(&port->lock, flags);
 }
 
-static const char *serial_s3c2410_type(struct uart_port *port)
+static const char *s3c24xx_serial_type(struct uart_port *port)
 {
-	return port->type == PORT_S3C2410 ? "S3C2410" : NULL;
+	switch (port->type) {
+	case PORT_S3C2410:
+		return "S3C2410";
+	case PORT_S3C2440:
+		return "S3C2440";
+	default:
+		return NULL;
+	}
 }
 
 #define MAP_SIZE (0x100)
 
-static void
-serial_s3c2410_release_port(struct uart_port *port)
+static void s3c24xx_serial_release_port(struct uart_port *port)
 {
 	release_mem_region(port->mapbase, MAP_SIZE);
 }
 
-static int
-serial_s3c2410_request_port(struct uart_port *port)
+static int s3c24xx_serial_request_port(struct uart_port *port)
 {
-	return request_mem_region(port->mapbase, MAP_SIZE, serial_s3c2410_name)
-		!= NULL ? 0 : -EBUSY;
+	char *name = s3c24xx_serial_portname(port);
+	return request_mem_region(port->mapbase, MAP_SIZE, name) ? 0 : -EBUSY;
 }
 
-static void
-serial_s3c2410_config_port(struct uart_port *port, int flags)
+static void s3c24xx_serial_config_port(struct uart_port *port, int flags)
 {
+	struct s3c24xx_uart_info *info = s3c24xx_port_to_info(port);
+
 	if (flags & UART_CONFIG_TYPE &&
-	    serial_s3c2410_request_port(port) == 0)
-		port->type = PORT_S3C2410;
+	    s3c24xx_serial_request_port(port) == 0)
+		port->type = info->type;
 }
 
 /*
  * verify the new serial_struct (for TIOCSSERIAL).
  */
 static int
-serial_s3c2410_verify_port(struct uart_port *port, struct serial_struct *ser)
+s3c24xx_serial_verify_port(struct uart_port *port, struct serial_struct *ser)
 {
-	int ret = 0;
+	struct s3c24xx_uart_info *info = s3c24xx_port_to_info(port);
 
-	if (ser->type != PORT_UNKNOWN && ser->type != PORT_S3C2410)
-		ret = -EINVAL;
+	if (ser->type != PORT_UNKNOWN && ser->type != info->type)
+		return -EINVAL;
 
-	return ret;
+	return 0;
 }
 
-static struct uart_ops serial_s3c2410_ops = {
-	.tx_empty	= serial_s3c2410_tx_empty,
-	.get_mctrl	= serial_s3c2410_get_mctrl,
-	.set_mctrl	= serial_s3c2410_set_mctrl,
-	.stop_tx	= serial_s3c2410_stop_tx,
-	.start_tx	= serial_s3c2410_start_tx,
-	.stop_rx	= serial_s3c2410_stop_rx,
-	.enable_ms	= serial_s3c2410_enable_ms,
-	.break_ctl	= serial_s3c2410_break_ctl,
-	.startup	= serial_s3c2410_startup,
-	.shutdown	= serial_s3c2410_shutdown,
-	.set_termios	= serial_s3c2410_set_termios,
-	.type		= serial_s3c2410_type,
-	.release_port	= serial_s3c2410_release_port,
-	.request_port	= serial_s3c2410_request_port,
-	.config_port	= serial_s3c2410_config_port,
-	.verify_port	= serial_s3c2410_verify_port,
-};
-
-static struct uart_port serial_s3c2410_ports[NR_PORTS] = {
-	{
-		.membase	= 0,
-		.mapbase	= 0,
-		.iotype		= UPIO_MEM,
-		.irq		= IRQ_S3CUART_RX0,
-		.uartclk	= 0,
-		.fifosize	= 16,
-		.ops		= &serial_s3c2410_ops,
-		.flags		= UPF_BOOT_AUTOCONF,
-		.line		= 0,
+
+#ifdef CONFIG_SERIAL_S3C2410_CONSOLE
+
+static struct console s3c24xx_serial_console;
+
+#define S3C24XX_SERIAL_CONSOLE &s3c24xx_serial_console
+#else
+#define S3C24XX_SERIAL_CONSOLE NULL
+#endif
+
+static struct uart_ops s3c24xx_serial_ops = {
+	.tx_empty	= s3c24xx_serial_tx_empty,
+	.get_mctrl	= s3c24xx_serial_get_mctrl,
+	.set_mctrl	= s3c24xx_serial_set_mctrl,
+	.stop_tx	= s3c24xx_serial_stop_tx,
+	.start_tx	= s3c24xx_serial_start_tx,
+	.stop_rx	= s3c24xx_serial_stop_rx,
+	.enable_ms	= s3c24xx_serial_enable_ms,
+	.break_ctl	= s3c24xx_serial_break_ctl,
+	.startup	= s3c24xx_serial_startup,
+	.shutdown	= s3c24xx_serial_shutdown,
+	.set_termios	= s3c24xx_serial_set_termios,
+	.type		= s3c24xx_serial_type,
+	.release_port	= s3c24xx_serial_release_port,
+	.request_port	= s3c24xx_serial_request_port,
+	.config_port	= s3c24xx_serial_config_port,
+	.verify_port	= s3c24xx_serial_verify_port,
+};
+
+
+static struct uart_driver s3c24xx_uart_drv = {
+	.owner		= THIS_MODULE,
+	.dev_name	= "s3c2410_serial",
+	.nr		= 3,
+	.cons		= S3C24XX_SERIAL_CONSOLE,
+	.driver_name	= S3C24XX_SERIAL_NAME,
+	.devfs_name	= S3C24XX_SERIAL_DEVFS,
+	.major		= S3C24XX_SERIAL_MAJOR,
+	.minor		= S3C24XX_SERIAL_MINOR,
+};
+
+static struct s3c24xx_uart_port s3c24xx_serial_ports[NR_PORTS] = {
+	[0] = {
+		.port = {
+			.lock		= SPIN_LOCK_UNLOCKED,
+			.membase	= 0,
+			.mapbase	= 0,
+			.iotype		= UPIO_MEM,
+			.irq		= IRQ_S3CUART_RX0,
+			.uartclk	= 0,
+			.fifosize	= 16,
+			.ops		= &s3c24xx_serial_ops,
+			.flags		= UPF_BOOT_AUTOCONF,
+			.line		= 0,
+		}
+	},
+	[1] = {
+		.port = {
+			.lock		= SPIN_LOCK_UNLOCKED,
+			.membase	= 0,
+			.mapbase	= 0,
+			.iotype		= UPIO_MEM,
+			.irq		= IRQ_S3CUART_RX1,
+			.uartclk	= 0,
+			.fifosize	= 16,
+			.ops		= &s3c24xx_serial_ops,
+			.flags		= UPF_BOOT_AUTOCONF,
+			.line		= 1,
+		}
 	},
-	{
-		.membase	= 0,
-		.mapbase	= 0,
-		.iotype		= UPIO_MEM,
-		.irq		= IRQ_S3CUART_RX1,
-		.uartclk	= 0,
-		.fifosize	= 16,
-		.ops		= &serial_s3c2410_ops,
-		.flags		= UPF_BOOT_AUTOCONF,
-		.line		= 1,
-	}
 #if NR_PORTS > 2
-	,
-	{
-		.membase	= 0,
-		.mapbase	= 0,
-		.iotype		= UPIO_MEM,
-		.irq		= IRQ_S3CUART_RX2,
-		.uartclk	= 0,
-		.fifosize	= 16,
-		.ops		= &serial_s3c2410_ops,
-		.flags		= UPF_BOOT_AUTOCONF,
-		.line		= 2,
+
+	[2] = {
+		.port = {
+			.lock		= SPIN_LOCK_UNLOCKED,
+			.membase	= 0,
+			.mapbase	= 0,
+			.iotype		= UPIO_MEM,
+			.irq		= IRQ_S3CUART_RX2,
+			.uartclk	= 0,
+			.fifosize	= 16,
+			.ops		= &s3c24xx_serial_ops,
+			.flags		= UPF_BOOT_AUTOCONF,
+			.line		= 2,
+		}
 	}
 #endif
 };
 
+static int s3c24xx_serial_get_portno(struct uart_port *port)
+{
+	return to_ourport(port) - s3c24xx_serial_ports;
+}
+
 static int
-serial_s3c2410_resetport(struct uart_port *port,
+s3c24xx_serial_resetport(struct uart_port *port,
 			 struct s3c2410_uartcfg *cfg)
 {
 	/* ensure registers are setup */
 
-	dbg("serial_s3c2410_resetport: port=%p (%08x), cfg=%p\n",
+	dbg("s3c24xx_serial_resetport: port=%p (%08lx), cfg=%p\n",
 	    port, port->mapbase, cfg);
 
-	wr_regl(port, S3C2410_UCON,  cfg->ucon);
-	wr_regl(port, S3C2410_ULCON, cfg->ulcon);
+	if (!machine_is_rx3715()) {
+		wr_regl(port, S3C2410_UCON,  cfg->ucon);
+		wr_regl(port, S3C2410_ULCON, cfg->ulcon);
+	} else {
+		wr_regl(port, S3C2410_ULCON, cfg->ulcon);
+	}
 
 	/* reset both fifos */
 
@@ -524,105 +998,423 @@
 	return 0;
 }
 
-/* serial_s3c2410_init_ports
+/* s3c24xx_serial_init_port
  *
- * initialise the serial ports from the machine provided initialisation
- * data.
+ * initialise a single serial port from the platform device given
+ */
+
+static void s3c24xx_serial_init_port(struct s3c24xx_uart_port *ourport,
+				     struct s3c24xx_uart_info *info,
+				     struct platform_device *platdev)
+{
+	struct uart_port *port = &ourport->port;
+	struct s3c2410_uartcfg *cfg;
+
+	dbg("s3c24xx_serial_init_port: port=%p, platdev=%p\n", port, platdev);
+
+	if (platdev == NULL)
+		return;
+
+	cfg = s3c24xx_dev_to_cfg(&platdev->dev);
+
+	if (cfg->hwport > 3 || port->mapbase != 0)
+		return;
+
+	/* setup info for port */
+	port->dev	= &platdev->dev;
+	ourport->info	= info;
+
+	/* copy the info in from provided structure */
+	ourport->port.fifosize = info->fifosize;
+
+	dbg("s3c24xx_serial_init_port: port %p (hw %d)...\n",
+	    s3c24xx_serial_get_portno(port), cfg->hwport);
+
+	port->uartclk = 1;
+
+	if (cfg->uart_flags & UPF_CONS_FLOW) {
+		dbg("s3c24xx_serial_init_port: enabling flow control\n");
+		port->flags |= UPF_CONS_FLOW;
+	}
+
+	/* sort our the physical and virtual addresses for each UART */
+
+	switch (cfg->hwport) {
+	case 0:
+		port->mapbase = S3C2410_PA_UART0;
+		port->membase = (char *)S3C2410_VA_UART0;
+		port->irq     = IRQ_S3CUART_RX0;
+		ourport->clk  = clk_get(port->dev, "uart0");
+		break;
+
+	case 1:
+		port->mapbase = S3C2410_PA_UART1;
+		port->membase = (char *)S3C2410_VA_UART1;
+		port->irq     = IRQ_S3CUART_RX1;
+		ourport->clk  = clk_get(port->dev, "uart1");
+		break;
+
+	case 2:
+		port->mapbase = S3C2410_PA_UART2;
+		port->membase = (char *)S3C2410_VA_UART2;
+		port->irq     = IRQ_S3CUART_RX2;
+		ourport->clk  = clk_get(port->dev, "uart2");
+		break;
+	}
+
+	if (ourport->clk != NULL)
+		clk_use(ourport->clk);
+
+	dbg("port: map=%08x, mem=%08x, irq=%d, clock=%ld\n",
+	    port->mapbase, port->membase, port->irq, port->uartclk);
+
+	/* reset the fifos (and setup the uart) */
+	s3c24xx_serial_resetport(port, cfg);
+}
+
+/* Device driver serial port probe */
+
+static int probe_index = 0;
+
+int s3c24xx_serial_probe(struct device *_dev,
+			 struct s3c24xx_uart_info *info)
+{
+	struct s3c24xx_uart_port *ourport;
+	struct platform_device *dev = to_platform_device(_dev);
+
+	dbg("s3c24xx_serial_probe(%p, %p) %d\n", _dev, info, probe_index);
+	
+	ourport = &s3c24xx_serial_ports[probe_index];
+	probe_index++;
+
+	dbg("%s: initialising port %p...\n", __FUNCTION__, ourport);
+
+	s3c24xx_serial_init_port(ourport, info, dev);
+
+	dbg("%s: adding port\n", __FUNCTION__);
+	uart_add_one_port(&s3c24xx_uart_drv, &ourport->port);
+	dev_set_drvdata(_dev, &ourport->port);
+
+	return 0;
+}
+
+int s3c24xx_serial_remove(struct device *_dev)
+{
+	struct uart_port *port = s3c24xx_dev_to_port(_dev);
+
+	if (port)
+		uart_remove_one_port(&s3c24xx_uart_drv, port);
+
+	return 0;
+}
+
+/* UART power management code */
+
+#ifdef CONFIG_PM
+
+int s3c24xx_serial_suspend(struct device *dev, u32 state, u32 level)
+{
+	struct uart_port *port = s3c24xx_dev_to_port(dev);
+
+	if (port && level == SUSPEND_DISABLE)
+		uart_suspend_port(&s3c24xx_uart_drv, port);
+
+	return 0;
+}
+
+int s3c24xx_serial_resume(struct device *dev, u32 level)
+{
+	struct uart_port *port = s3c24xx_dev_to_port(dev);
+
+	if (port && level == RESUME_ENABLE) {
+		s3c24xx_serial_resetport(port, s3c24xx_port_to_cfg(port));
+		uart_resume_port(&s3c24xx_uart_drv, port);
+	}
+
+	return 0;
+}
+
+#else
+#define s3c24xx_serial_suspend NULL
+#define s3c24xx_serial_resume  NULL
+#endif
+
+int s3c24xx_serial_init(struct device_driver *drv,
+			struct s3c24xx_uart_info *info)
+{
+	dbg("s3c24xx_serial_init(%p,%p)\n", drv, info);
+	return driver_register(drv);
+}
+
+
+/* now comes the code to initialise either the s3c2410 or s3c2440 serial
+ * port information
 */
 
-static int serial_s3c2410_init_ports(void)
+/* cpu specific variations on the serial port support */
+
+#ifdef CONFIG_CPU_S3C2410
+
+static int s3c2410_serial_setsource(struct uart_port *port,
+				    struct s3c24xx_uart_clksrc *clk)
 {
-	struct uart_port *ptr = serial_s3c2410_ports;
-	struct s3c2410_uartcfg *cfg = s3c2410_uartcfgs;
-	static int inited = 0;
-	int i;
+	unsigned long ucon = rd_regl(port, S3C2410_UCON);
 
-	if (inited)
-		return 0;
-	inited = 1;
+	if (strcmp(clk->name, "uclk") == 0)
+		ucon |= S3C2410_UCON_UCLK;
+	else
+		ucon &= ~S3C2410_UCON_UCLK;
+
+	wr_regl(port, S3C2410_UCON, ucon);
+	return 0;
+}
 
-	dbg("serial_s3c2410_init_ports: initialising ports...\n");
+static int s3c2410_serial_getsource(struct uart_port *port,
+				    struct s3c24xx_uart_clksrc *clk)
+{
+	unsigned long ucon = rd_regl(port, S3C2410_UCON);
 
-	for (i = 0; i < NR_PORTS; i++, ptr++, cfg++) {
+	clk->divisor = 1;
+	clk->name = (ucon & S3C2410_UCON_UCLK) ? "uclk" : "pclk";
 
-		if (cfg->hwport > 3)
-			continue;
+	return 0;
+}
 
-		dbg("serial_s3c2410_init_ports: port %d (hw %d)...\n",
-		    i, cfg->hwport);
+static struct s3c24xx_uart_info s3c2410_uart_inf = {
+	.name		= "Samsung S3C2410 UART",
+	.type		= PORT_S3C2410,
+	.fifosize	= 16,
+	.rx_fifomask	= S3C2410_UFSTAT_RXMASK,
+	.rx_fifoshift	= S3C2410_UFSTAT_RXSHIFT,
+	.tx_fifofull	= S3C2410_UFSTAT_TXFULL,
+	.tx_fifomask	= S3C2410_UFSTAT_TXMASK,
+	.tx_fifoshift	= S3C2410_UFSTAT_TXSHIFT,
+	.get_clksrc	= s3c2410_serial_getsource,
+	.set_clksrc	= s3c2410_serial_setsource,
+};
 
-		if (cfg->clock != NULL)
-			ptr->uartclk = *cfg->clock;
+/* device management */
 
-		switch (cfg->hwport) {
-		case 0:
-			ptr->mapbase = S3C2410_PA_UART0;
-			ptr->membase = (char *)S3C2410_VA_UART0;
-			ptr->irq     = IRQ_S3CUART_RX0;
-			break;
+static int s3c2410_serial_probe(struct device *dev)
+{
+	return s3c24xx_serial_probe(dev, &s3c2410_uart_inf);
+}
 
-		case 1:
-			ptr->mapbase = S3C2410_PA_UART1;
-			ptr->membase = (char *)S3C2410_VA_UART1;
-			ptr->irq     = IRQ_S3CUART_RX1;
-			break;
+static struct device_driver s3c2410_serial_drv = {
+	.name		= "s3c2410-uart",
+	.bus		= &platform_bus_type,
+	.probe		= s3c2410_serial_probe,
+	.remove		= s3c24xx_serial_remove,
+	.suspend	= s3c24xx_serial_suspend,
+	.resume		= s3c24xx_serial_resume,
+};
 
-		case 2:
-			ptr->mapbase = S3C2410_PA_UART2;
-			ptr->membase = (char *)S3C2410_VA_UART2;
-			ptr->irq     = IRQ_S3CUART_RX2;
-			break;
-		}
+static inline int s3c2410_serial_init(void)
+{
+	return s3c24xx_serial_init(&s3c2410_serial_drv, &s3c2410_uart_inf);
+}
+
+static inline void s3c2410_serial_exit(void)
+{
+	driver_unregister(&s3c2410_serial_drv);
+}
+
+#define s3c2410_uart_inf_at &s3c2410_uart_inf
+#else
+
+static inline int s3c2410_serial_init(void)
+{
+	return 0;
+}
+
+static inline void s3c2410_serial_exit(void)
+{
+}
+
+#define s3c2410_uart_inf_at NULL
+
+#endif /* CONFIG_CPU_S3C2410 */
+
+#ifdef CONFIG_CPU_S3C2440
+static int s3c2440_serial_setsource(struct uart_port *port,
+				     struct s3c24xx_uart_clksrc *clk)
+{
+	unsigned long ucon = rd_regl(port, S3C2410_UCON);
+
+	// todo - proper fclk<>nonfclk switch //
+
+	ucon &= ~S3C2440_UCON_CLKMASK;
+
+	if (strcmp(clk->name, "uclk") == 0)
+		ucon |= S3C2440_UCON_UCLK;
+	else if (strcmp(clk->name, "pclk") == 0)
+		ucon |= S3C2440_UCON_PCLK;
+	else if (strcmp(clk->name, "fclk") == 0)
+		ucon |= S3C2440_UCON_FCLK;
+	else {
+		printk(KERN_ERR "unknown clock source %s\n", clk->name);
+		return -EINVAL;
+	}
+
+	wr_regl(port, S3C2410_UCON, ucon);
+	return 0;
+}
+
+
+static int s3c2440_serial_getsource(struct uart_port *port,
+				    struct s3c24xx_uart_clksrc *clk)
+{
+	unsigned long ucon = rd_regl(port, S3C2410_UCON);
+
+	switch (ucon & S3C2440_UCON_CLKMASK) {
+	case S3C2440_UCON_UCLK:
+		clk->divisor = 1;
+		clk->name = "uclk";
+		break;
 
-		if (ptr->mapbase == 0)
-			continue;
+	case S3C2440_UCON_PCLK:
+	case S3C2440_UCON_PCLK2:
+		clk->divisor = 1;
+		clk->name = "pclk";
+		break;
 
-		/* reset the fifos (and setup the uart */
-		serial_s3c2410_resetport(ptr, cfg);
+	case S3C2440_UCON_FCLK:
+		clk->divisor = 7; /* todo - work out divisor */
+		clk->name = "fclk";
+		break;
 	}
 
+
 	return 0;
 }
 
+
+static struct s3c24xx_uart_info s3c2440_uart_inf = {
+	.name		= "Samsung S3C2440 UART",
+	.type		= PORT_S3C2440,
+	.fifosize	= 64,
+	.rx_fifomask	= S3C2440_UFSTAT_RXMASK,
+	.rx_fifoshift	= S3C2440_UFSTAT_RXSHIFT,
+	.tx_fifofull	= S3C2440_UFSTAT_TXFULL,
+	.tx_fifomask	= S3C2440_UFSTAT_TXMASK,
+	.tx_fifoshift	= S3C2440_UFSTAT_TXSHIFT,
+	.get_clksrc	= s3c2440_serial_getsource,
+	.set_clksrc	= s3c2440_serial_setsource
+};
+
+/* device management */
+
+static int s3c2440_serial_probe(struct device *dev)
+{
+	dbg("s3c2440_serial_probe: dev=%p\n", dev);
+	return s3c24xx_serial_probe(dev, &s3c2440_uart_inf);
+}
+
+static struct device_driver s3c2440_serial_drv = {
+	.name		= "s3c2440-uart",
+	.bus		= &platform_bus_type,
+	.probe		= s3c2440_serial_probe,
+	.remove		= s3c24xx_serial_remove,
+	.suspend	= s3c24xx_serial_suspend,
+	.resume		= s3c24xx_serial_resume,
+};
+
+
+static inline int s3c2440_serial_init(void)
+{
+	return s3c24xx_serial_init(&s3c2440_serial_drv, &s3c2440_uart_inf);
+}
+
+static inline void s3c2440_serial_exit(void)
+{
+	driver_unregister(&s3c2440_serial_drv);
+}
+
+#define s3c2440_uart_inf_at &s3c2440_uart_inf
+#else
+
+static inline int s3c2440_serial_init(void)
+{
+	return 0;
+}
+
+static inline void s3c2440_serial_exit(void)
+{
+}
+
+#define s3c2440_uart_inf_at NULL
+#endif /* CONFIG_CPU_S3C2440 */
+
+/* module initialisation code */
+
+static int __init s3c24xx_serial_modinit(void)
+{
+	int ret;
+
+	ret = uart_register_driver(&s3c24xx_uart_drv);
+	if (ret < 0) {
+		printk(KERN_ERR "failed to register UART driver\n");
+		return -1;
+	}
+
+	s3c2410_serial_init();
+	s3c2440_serial_init();
+
+	return 0;
+}
+
+static void __exit s3c24xx_serial_modexit(void)
+{
+	s3c2410_serial_exit();
+	s3c2440_serial_exit();
+
+	uart_unregister_driver(&s3c24xx_uart_drv);
+}
+
+
+module_init(s3c24xx_serial_modinit);
+module_exit(s3c24xx_serial_modexit);
+
+/* Console code */			
+
 #ifdef CONFIG_SERIAL_S3C2410_CONSOLE
 
 static struct uart_port *cons_uart;
 
 static int
-serial_s3c2410_console_txrdy(struct uart_port *port, unsigned int ufcon)
+s3c24xx_serial_console_txrdy(struct uart_port *port, unsigned int ufcon)
 {
+	struct s3c24xx_uart_info *info = s3c24xx_port_to_info(port);
 	unsigned long ufstat, utrstat;
 
 	if (ufcon & S3C2410_UFCON_FIFOMODE) {
 		/* fifo mode - check ammount of data in fifo registers... */
 
 		ufstat = rd_regl(port, S3C2410_UFSTAT);
-
-		return S3C2410_UFCON_TXC(ufstat) < 12;
+		return (ufstat & info->tx_fifofull) ? 0 : 1;
 	}
 
 	/* in non-fifo mode, we go and use the tx buffer empty */
 
 	utrstat = rd_regl(port, S3C2410_UTRSTAT);
-
-	return (utrstat & S3C2410_UTRSTAT_TXFE) ? 1 : 0;
+	return (utrstat & S3C2410_UTRSTAT_TXE) ? 1 : 0;
 }
 
 static void
-serial_s3c2410_console_write(struct console *co, const char *s,
+s3c24xx_serial_console_write(struct console *co, const char *s,
 			     unsigned int count)
 {
 	int i;
 	unsigned int ufcon = rd_regl(cons_uart, S3C2410_UFCON);
 
 	for (i = 0; i < count; i++) {
-		while (!serial_s3c2410_console_txrdy(cons_uart, ufcon))
+		while (!s3c24xx_serial_console_txrdy(cons_uart, ufcon))
 			barrier();
 
 		wr_regb(cons_uart, S3C2410_UTXH, s[i]);
 
 		if (s[i] == '\n') {
-			while (!serial_s3c2410_console_txrdy(cons_uart, ufcon))
+			while (!s3c24xx_serial_console_txrdy(cons_uart, ufcon))
 				barrier();
 
 			wr_regb(cons_uart, S3C2410_UTXH, '\r');
@@ -631,17 +1423,21 @@
 }
 
 static void __init
-serial_s3c2410_get_options(struct uart_port *port, int *baud,
+s3c24xx_serial_get_options(struct uart_port *port, int *baud,
 			   int *parity, int *bits)
 {
-
-	unsigned int ulcon, ucon, ubrdiv;
+	struct s3c24xx_uart_clksrc clksrc;
+	struct clk *clk;
+	unsigned int ulcon;
+	unsigned int ucon;
+	unsigned int ubrdiv;
+	unsigned long rate;
 
 	ulcon  = rd_regl(port, S3C2410_ULCON);
 	ucon   = rd_regl(port, S3C2410_UCON);
 	ubrdiv = rd_regl(port, S3C2410_UBRDIV);
 
-	dbg("serial_s3c2410_get_options: port=%p\n"
+	dbg("s3c24xx_serial_get_options: port=%p\n"
 	    "registers: ulcon=%08x, ucon=%08x, ubdriv=%08x\n",
 	    port, ulcon, ucon, ubrdiv);
 
@@ -673,21 +1469,53 @@
 			*parity = 'o';
 			break;
 
-		default:
 		case S3C2410_LCON_PNONE:
+		default:
 			*parity = 'n';
 		}
 
 		/* now calculate the baud rate */
 
-		*baud = port->uartclk / ( 16 * (ubrdiv + 1));
+		s3c24xx_serial_getsource(port, &clksrc);
+
+		clk = clk_get(port->dev, clksrc.name);
+		if (clk != NULL)
+			rate = clk_get_rate(clk);
+		else
+			rate = 1;
+			
+
+		*baud = rate / ( 16 * (ubrdiv + 1));
 		dbg("calculated baud %d\n", *baud);
 	}
 
 }
 
+/* s3c24xx_serial_init_ports
+ *
+ * initialise the serial ports from the machine provided initialisation
+ * data.
+*/
+
+static int s3c24xx_serial_init_ports(struct s3c24xx_uart_info *info)
+{
+	struct s3c24xx_uart_port *ptr = s3c24xx_serial_ports;
+	struct platform_device **platdev_ptr;
+	int i;
+
+	dbg("s3c24xx_serial_init_ports: initialising ports...\n");
+
+	platdev_ptr = s3c24xx_uart_devs;
+
+	for (i = 0; i < NR_PORTS; i++, ptr++, platdev_ptr++) {
+		s3c24xx_serial_init_port(ptr, info, *platdev_ptr);
+	}
+
+	return 0;
+}
+
 static int __init
-serial_s3c2410_console_setup(struct console *co, char *options)
+s3c24xx_serial_console_setup(struct console *co, char *options)
 {
 	struct uart_port *port;
 	int baud = 9600;
@@ -695,23 +1523,26 @@
 	int parity = 'n';
 	int flow = 'n';
 
+	dbg("s3c24xx_serial_console_setup: co=%p (%d), %s\n",
+	    co, co->index, options);
+
 	/* is this a valid port */
 
 	if (co->index == -1 || co->index >= NR_PORTS)
 		co->index = 0;
 
-	port = &serial_s3c2410_ports[co->index];
+	port = &s3c24xx_serial_ports[co->index].port;
 
 	/* is the port configured? */
 
 	if (port->mapbase == 0x0) {
 		co->index = 0;
-		port = &serial_s3c2410_ports[co->index];
+		port = &s3c24xx_serial_ports[co->index].port;
 	}
 
 	cons_uart = port;
 
-	dbg("serial_s3c2410_console_setup: port=%p (%d)\n", port, co->index);
+	dbg("s3c24xx_serial_console_setup: port=%p (%d)\n", port, co->index);
 
 	/*
 	 * Check whether an invalid uart number has been specified, and
@@ -721,138 +1552,68 @@
 	if (options)
 		uart_parse_options(options, &baud, &parity, &bits, &flow);
 	else
-		serial_s3c2410_get_options(port, &baud, &parity, &bits);
+		s3c24xx_serial_get_options(port, &baud, &parity, &bits);
+
+	dbg("s3c24xx_serial_console_setup: baud %d\n", baud);
 
 	return uart_set_options(port, co, baud, parity, bits, flow);
 }
 
-static struct uart_driver s3c2410_uart_drv;
+/* s3c24xx_serial_initconsole
+ *
+ * initialise the console from one of the uart drivers
+*/
 
-static struct console serial_s3c2410_console =
+static struct console s3c24xx_serial_console =
 {
-	.name		= SERIAL_S3C2410_NAME,
-	.write		= serial_s3c2410_console_write,
+	.name		= S3C24XX_SERIAL_NAME,
 	.device		= uart_console_device,
-	.setup		= serial_s3c2410_console_setup,
 	.flags		= CON_PRINTBUFFER,
 	.index		= -1,
-	.data		= &s3c2410_uart_drv,
-};
-
-static int __init s3c2410_console_init(void)
-{
-	dbg("s3c2410_console_init:\n");
-
-	serial_s3c2410_init_ports();
-	register_console(&serial_s3c2410_console);
-	return 0;
-}
-console_initcall(s3c2410_console_init);
-
-#define SERIAL_S3C2410_CONSOLE	&serial_s3c2410_console
-#else
-#define SERIAL_S3C2410_CONSOLE	NULL
-#endif
-
-static struct uart_driver s3c2410_uart_drv = {
-	.owner			= THIS_MODULE,
-	.driver_name		= SERIAL_S3C2410_NAME,
-	.dev_name		= SERIAL_S3C2410_NAME,
-	.major			= SERIAL_S3C2410_MAJOR,
-	.minor			= SERIAL_S3C2410_MINOR,
-	.nr			= 3,
-	.cons			= SERIAL_S3C2410_CONSOLE,
-};
-
-/* device driver */
-
-static int s3c2410_serial_probe(struct device *_dev);
-static int s3c2410_serial_remove(struct device *_dev);
-
-static struct device_driver s3c2410_serial_drv = {
-	.name		= "s3c2410-uart",
-	.bus		= &platform_bus_type,
-	.probe		= s3c2410_serial_probe,
-	.remove		= s3c2410_serial_remove,
-	.suspend	= NULL,
-	.resume		= NULL,
+	.write		= s3c24xx_serial_console_write,
+	.setup		= s3c24xx_serial_console_setup
 };
 
-#define s3c2410_dev_to_port(__dev) (struct uart_port *)dev_get_drvdata(__dev)
 
-static int s3c2410_serial_probe(struct device *_dev)
+static int s3c24xx_serial_initconsole(void)
 {
-	struct platform_device *dev = to_platform_device(_dev);
-	struct resource *res = dev->resource;
-	int i;
-
-	dbg("s3c2410_serial_probe: dev=%p, _dev=%p, res=%p\n", _dev, dev, res);
-
-	for (i = 0; i < dev->num_resources; i++, res++)
-		if (res->flags & IORESOURCE_MEM)
-			break;
+	struct s3c24xx_uart_info *info;
+	struct platform_device *dev = s3c24xx_uart_devs[0];
 
-	if (i < dev->num_resources) {
-		struct uart_port *ptr = serial_s3c2410_ports;
-
-		for (i = 0; i < NR_PORTS; i++, ptr++) {
-			dbg("s3c2410_serial_probe: ptr=%p (%08x, %08x)\n",
-			    ptr, ptr->mapbase, ptr->membase);
-
-			if (ptr->mapbase != res->start)
-				continue;
+	dbg("s3c24xx_serial_initconsole\n");
 
-			dbg("s3c2410_serial_probe: got device %p: port=%p\n",
-			    _dev, ptr);
+	/* select driver based on the cpu */
 
-			uart_add_one_port(&s3c2410_uart_drv, ptr);
-			dev_set_drvdata(_dev, ptr);
-			break;
-		}
+	if (dev == NULL) {
+		printk(KERN_ERR "s3c24xx: no devices for console init\n");
+		return 0;
 	}
 
-	return 0;
-}
+	if (strcmp(dev->name, "s3c2410-uart") == 0) {
+		info = s3c2410_uart_inf_at;
+	} else if (strcmp(dev->name, "s3c2440-uart") == 0) {
+		info = s3c2440_uart_inf_at;
+	} else {
+		printk(KERN_ERR "s3c24xx: no driver for %s\n", dev->name);
+		return 0;
+	}
 
-static int s3c2410_serial_remove(struct device *_dev)
-{
-	struct uart_port *port = s3c2410_dev_to_port(_dev);
+	if (info == NULL) {
+		printk(KERN_ERR "s3c24xx: no driver for console\n");
+		return 0;
+	}
 
-	if (port)
-		uart_remove_one_port(&s3c2410_uart_drv, port);
+	s3c24xx_serial_console.data = &s3c24xx_uart_drv;
+	s3c24xx_serial_init_ports(info);
 
+	register_console(&s3c24xx_serial_console);
 	return 0;
 }
 
+console_initcall(s3c24xx_serial_initconsole);
 
-
-static int __init serial_s3c2410_init(void)
-{
-	int ret;
-
-	printk(KERN_INFO "S3C2410X Serial, (c) 2003 Simtec Electronics\n");
-
-	ret = uart_register_driver(&s3c2410_uart_drv);
-	if (ret != 0)
-		return ret;
-
-	ret = driver_register(&s3c2410_serial_drv);
-	if (ret) {
-		uart_unregister_driver(&s3c2410_uart_drv);
-	}
-
-	return ret;
-}
-
-static void __exit serial_s3c2410_exit(void)
-{
-	driver_unregister(&s3c2410_serial_drv);
-	uart_unregister_driver(&s3c2410_uart_drv);
-}
-
-module_init(serial_s3c2410_init);
-module_exit(serial_s3c2410_exit);
+#endif /* CONFIG_SERIAL_S3C2410_CONSOLE */
 
 MODULE_LICENSE("GPL");
 MODULE_AUTHOR("Ben Dooks <ben@simtec.co.uk>");
-MODULE_DESCRIPTION("Samsung S3C2410X (S3C2410) Serial driver");
+MODULE_DESCRIPTION("Samsung S3C2410/S3C2440 Serial port driver");
diff -urN -X ../dontdiff linux-2.6.10-rc1-bk14/arch/arm/mach-s3c2410/Kconfig linux-2.6.10-rc1-bk14-serial1/arch/arm/mach-s3c2410/Kconfig
--- linux-2.6.10-rc1-bk14/arch/arm/mach-s3c2410/Kconfig	2004-11-04 16:58:44.000000000 +0000
+++ linux-2.6.10-rc1-bk14-serial1/arch/arm/mach-s3c2410/Kconfig	2004-11-04 17:38:35.000000000 +0000
@@ -26,6 +26,12 @@
 	   Say Y here if you are using the SMDK2410 or the derived module A9M2410
            <http://www.fsforth.de>
 
+config ARCH_S3C2440
+	bool "SMDK2440"
+	select CPU_S3C2440
+	help
+	  Say Y here if you are using an SMDK2440
+
 config MACH_VR1000
 	bool "Thorcom VR1000"
 	select CPU_S3C2410
diff -urN -X ../dontdiff linux-2.6.10-rc1-bk14/arch/arm/mach-s3c2410/Makefile linux-2.6.10-rc1-bk14-serial1/arch/arm/mach-s3c2410/Makefile
--- linux-2.6.10-rc1-bk14/arch/arm/mach-s3c2410/Makefile	2004-11-04 16:58:44.000000000 +0000
+++ linux-2.6.10-rc1-bk14-serial1/arch/arm/mach-s3c2410/Makefile	2004-11-04 17:37:55.000000000 +0000
@@ -28,5 +28,6 @@
 obj-$(CONFIG_ARCH_BAST)		+= mach-bast.o usb-simtec.o
 obj-$(CONFIG_ARCH_H1940)	+= mach-h1940.o
 obj-$(CONFIG_ARCH_SMDK2410)	+= mach-smdk2410.o
+obj-$(CONFIG_ARCH_S3C2440)	+= mach-smdk2440.o
 obj-$(CONFIG_MACH_VR1000)	+= mach-vr1000.o usb-simtec.o
 
diff -urN -X ../dontdiff linux-2.6.10-rc1-bk14/arch/arm/mach-s3c2410/devs.c linux-2.6.10-rc1-bk14-serial1/arch/arm/mach-s3c2410/devs.c
--- linux-2.6.10-rc1-bk14/arch/arm/mach-s3c2410/devs.c	2004-10-18 22:53:43.000000000 +0100
+++ linux-2.6.10-rc1-bk14-serial1/arch/arm/mach-s3c2410/devs.c	2004-11-04 17:30:12.000000000 +0000
@@ -32,8 +32,14 @@
 #include <asm/io.h>
 #include <asm/irq.h>
 
+#include <asm/arch/regs-serial.h>
+
 #include "devs.h"
 
+/* Serial port registrations */
+
+struct platform_device *s3c24xx_uart_devs[3];
+
 /* USB Host Controller */
 
 static struct resource s3c_usb_resource[] = {
diff -urN -X ../dontdiff linux-2.6.10-rc1-bk14/arch/arm/mach-s3c2410/mach-bast.c linux-2.6.10-rc1-bk14-serial1/arch/arm/mach-s3c2410/mach-bast.c
--- linux-2.6.10-rc1-bk14/arch/arm/mach-s3c2410/mach-bast.c	2004-11-04 16:58:44.000000000 +0000
+++ linux-2.6.10-rc1-bk14-serial1/arch/arm/mach-s3c2410/mach-bast.c	2004-11-04 17:18:55.000000000 +0000
@@ -19,6 +19,7 @@
  *     06-Jan-2003 BJD  Updates for <arch/map.h>
  *     18-Jan-2003 BJD  Added serial port configuration
  *     05-Oct-2004 BJD  Power management code
+ *     04-Nov-2004 BJD  Updated serial port clocks
 */
 
 #include <linux/kernel.h>
@@ -47,6 +48,7 @@
 #include <asm/arch/regs-mem.h>
 
 #include "s3c2410.h"
+#include "clock.h"
 #include "devs.h"
 #include "cpu.h"
 #include "usb-simtec.h"
@@ -153,35 +155,50 @@
 #define ULCON S3C2410_LCON_CS8 | S3C2410_LCON_PNONE | S3C2410_LCON_STOPB
 #define UFCON S3C2410_UFCON_RXTRIG8 | S3C2410_UFCON_FIFOMODE
 
-/* base baud rate for all our UARTs */
-static unsigned long bast_serial_clock = 24*1000*1000;
+static struct s3c24xx_uart_clksrc bast_serial_clocks[] = {
+	[0] = {
+		.name		= "uclk",
+		.divisor	= 1,
+		.min_baud	= 0,
+		.max_baud	= 0,
+	},
+	[1] = {
+		.name		= "pclk",
+		.divisor	= 1,
+		.min_baud	= 0,
+		.max_baud	= 0.
+	}
+};
+
 
 static struct s3c2410_uartcfg bast_uartcfgs[] = {
 	[0] = {
 		.hwport	     = 0,
 		.flags	     = 0,
-		.clock	     = &bast_serial_clock,
 		.ucon	     = UCON,
 		.ulcon	     = ULCON,
 		.ufcon	     = UFCON,
+		.clocks	     = bast_serial_clocks,
+		.clocks_size = ARRAY_SIZE(bast_serial_clocks)
 	},
 	[1] = {
 		.hwport	     = 1,
 		.flags	     = 0,
-
-		.clock	     = &bast_serial_clock,
 		.ucon	     = UCON,
 		.ulcon	     = ULCON,
 		.ufcon	     = UFCON,
+		.clocks	     = bast_serial_clocks,
+		.clocks_size = ARRAY_SIZE(bast_serial_clocks)
 	},
 	/* port 2 is not actually used */
 	[2] = {
 		.hwport	     = 2,
 		.flags	     = 0,
-		.clock	     = &bast_serial_clock,
 		.ucon	     = UCON,
 		.ulcon	     = ULCON,
 		.ufcon	     = UFCON,
+		.clocks	     = bast_serial_clocks,
+		.clocks_size = ARRAY_SIZE(bast_serial_clocks)
 	}
 };
 
@@ -214,13 +231,36 @@
 	&bast_device_nor
 };
 
+static struct clk *bast_clocks[] = {
+	&s3c24xx_dclk0,
+	&s3c24xx_dclk1,
+	&s3c24xx_clkout0,
+	&s3c24xx_clkout1,
+	&s3c24xx_uclk,
+};
+
 static struct s3c24xx_board bast_board __initdata = {
 	.devices       = bast_devices,
-	.devices_count = ARRAY_SIZE(bast_devices)
+	.devices_count = ARRAY_SIZE(bast_devices),
+	.clocks	       = bast_clocks,
+	.clocks_count  = ARRAY_SIZE(bast_clocks)
 };
 
 void __init bast_map_io(void)
 {
+	/* initialise the clocks */
+
+	s3c24xx_dclk0.parent = NULL;
+	s3c24xx_dclk0.rate   = 12*1000*1000;
+
+	s3c24xx_dclk1.parent = NULL;
+	s3c24xx_dclk1.rate   = 24*1000*1000;
+
+	s3c24xx_clkout0.parent  = &s3c24xx_dclk0;
+	s3c24xx_clkout1.parent  = &s3c24xx_dclk1;
+
+	s3c24xx_uclk.parent  = &s3c24xx_clkout1;
+
 	s3c24xx_init_io(bast_iodesc, ARRAY_SIZE(bast_iodesc));
 	s3c2410_init_uarts(bast_uartcfgs, ARRAY_SIZE(bast_uartcfgs));
 	s3c24xx_set_board(&bast_board);
diff -urN -X ../dontdiff linux-2.6.10-rc1-bk14/arch/arm/mach-s3c2410/mach-h1940.c linux-2.6.10-rc1-bk14-serial1/arch/arm/mach-s3c2410/mach-h1940.c
--- linux-2.6.10-rc1-bk14/arch/arm/mach-s3c2410/mach-h1940.c	2004-11-04 16:58:44.000000000 +0000
+++ linux-2.6.10-rc1-bk14-serial1/arch/arm/mach-s3c2410/mach-h1940.c	2004-11-04 17:14:08.000000000 +0000
@@ -19,6 +19,7 @@
  *     04-Sep-2004 BJD  Changed uart init, renamed ipaq_ -> h1940_
  *     18-Oct-2004 BJD  Updated new board structure name
  *     03-Nov-2004 BJD  Updated for new LCD bits
+ *     04-Nov-2004 BJD  Change for new serial clock
 */
 
 #include <linux/kernel.h>
@@ -58,7 +59,6 @@
 	[0] = {
 		.hwport	     = 0,
 		.flags	     = 0,
-		.clock	     = &s3c24xx_pclk,
 		.ucon	     = 0x3c5,
 		.ulcon	     = 0x03,
 		.ufcon	     = 0x51,
@@ -66,7 +66,6 @@
 	[1] = {
 		.hwport	     = 1,
 		.flags	     = 0,
-		.clock	     = &s3c24xx_pclk,
 		.ucon	     = 0x245,
 		.ulcon	     = 0x03,
 		.ufcon	     = 0x00,
@@ -75,7 +74,6 @@
 	[2] = {
 		.hwport	     = 2,
 		.flags	     = 0,
-		.clock	     = &s3c24xx_pclk,
 		.ucon	     = 0x3c5,
 		.ulcon	     = 0x43,
 		.ufcon	     = 0x51,
diff -urN -X ../dontdiff linux-2.6.10-rc1-bk14/arch/arm/mach-s3c2410/mach-smdk2410.c linux-2.6.10-rc1-bk14-serial1/arch/arm/mach-s3c2410/mach-smdk2410.c
--- linux-2.6.10-rc1-bk14/arch/arm/mach-s3c2410/mach-smdk2410.c	2004-11-04 16:58:44.000000000 +0000
+++ linux-2.6.10-rc1-bk14-serial1/arch/arm/mach-s3c2410/mach-smdk2410.c	2004-11-04 17:15:57.000000000 +0000
@@ -58,14 +58,10 @@
 #define ULCON S3C2410_LCON_CS8 | S3C2410_LCON_PNONE | S3C2410_LCON_STOPB
 #define UFCON S3C2410_UFCON_RXTRIG8 | S3C2410_UFCON_FIFOMODE
 
-/* base baud rate for all our UARTs */
-static unsigned long smdk2410_serial_clock = 24*1000*1000;
-
 static struct s3c2410_uartcfg smdk2410_uartcfgs[] = {
 	[0] = {
 		.hwport	     = 0,
 		.flags	     = 0,
-		.clock	     = &smdk2410_serial_clock,
 		.ucon	     = UCON,
 		.ulcon	     = ULCON,
 		.ufcon	     = UFCON,
@@ -73,7 +69,6 @@
 	[1] = {
 		.hwport	     = 1,
 		.flags	     = 0,
-		.clock	     = &smdk2410_serial_clock,
 		.ucon	     = UCON,
 		.ulcon	     = ULCON,
 		.ufcon	     = UFCON,
@@ -81,7 +76,6 @@
 	[2] = {
 		.hwport	     = 2,
 		.flags	     = 0,
-		.clock	     = &smdk2410_serial_clock,
 		.ucon	     = UCON,
 		.ulcon	     = ULCON,
 		.ufcon	     = UFCON,
diff -urN -X ../dontdiff linux-2.6.10-rc1-bk14/arch/arm/mach-s3c2410/mach-smdk2440.c linux-2.6.10-rc1-bk14-serial1/arch/arm/mach-s3c2410/mach-smdk2440.c
--- linux-2.6.10-rc1-bk14/arch/arm/mach-s3c2410/mach-smdk2440.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.10-rc1-bk14-serial1/arch/arm/mach-s3c2410/mach-smdk2440.c	2004-11-04 17:40:44.000000000 +0000
@@ -0,0 +1,146 @@
+/* linux/arch/arm/mach-s3c2410/mach-smdk2440.c
+ *
+ * Copyright (c) 2004 Simtec Electronics
+ *	Ben Dooks <ben@simtec.co.uk>
+ *
+ * http://www.fluff.org/ben/smdk2440/
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Modifications:
+ *	01-Nov-2004 BJD Initial version
+*/
+
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/interrupt.h>
+#include <linux/list.h>
+#include <linux/timer.h>
+#include <linux/init.h>
+
+#include <asm/mach/arch.h>
+#include <asm/mach/map.h>
+#include <asm/mach/irq.h>
+
+#include <asm/hardware.h>
+#include <asm/hardware/iomd.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/mach-types.h>
+
+//#include <asm/debug-ll.h>
+#include <asm/arch/regs-serial.h>
+#include <asm/arch/regs-gpio.h>
+#include <asm/arch/idle.h>
+
+#include "s3c2410.h"
+#include "clock.h"
+#include "devs.h"
+#include "cpu.h"
+
+static struct map_desc smdk2440_iodesc[] __initdata = {
+	/* ISA IO Space map (memory space selected by A24) */
+
+	{ S3C2410_VA_ISA_WORD, S3C2410_CS2, SZ_16M, MT_DEVICE },
+	{ S3C2410_VA_ISA_BYTE, S3C2410_CS2, SZ_16M, MT_DEVICE },
+};
+
+#define UCON S3C2410_UCON_DEFAULT | S3C2410_UCON_UCLK
+#define ULCON S3C2410_LCON_CS8 | S3C2410_LCON_PNONE | S3C2410_LCON_STOPB
+#define UFCON S3C2410_UFCON_RXTRIG8 | S3C2410_UFCON_FIFOMODE
+
+static struct s3c2410_uartcfg smdk2440_uartcfgs[] = {
+	[0] = {
+		.hwport	     = 0,
+		.flags	     = 0,
+		.ucon	     = 0x3c5,
+		.ulcon	     = 0x03,
+		.ufcon	     = 0x51,
+	},
+	[1] = {
+		.hwport	     = 1,
+		.flags	     = 0,
+		.ucon	     = 0x3c5,
+		.ulcon	     = 0x03,
+		.ufcon	     = 0x51,
+	},
+	/* IR port */
+	[2] = {
+		.hwport	     = 2,
+		.flags	     = 0,
+		.ucon	     = 0x3c5,
+		.ulcon	     = 0x43,
+		.ufcon	     = 0x51,
+	}
+};
+
+static struct platform_device *smdk2440_devices[] __initdata = {
+	&s3c_device_usb,
+	&s3c_device_lcd,
+	&s3c_device_wdt,
+	&s3c_device_i2c,
+	&s3c_device_iis,
+};
+
+static struct s3c24xx_board smdk2440_board __initdata = {
+	.devices       = smdk2440_devices,
+	.devices_count = ARRAY_SIZE(smdk2440_devices)
+};
+
+void __init smdk2440_map_io(void)
+{
+	s3c24xx_xtal = 16934400;
+
+	s3c24xx_init_io(smdk2440_iodesc, ARRAY_SIZE(smdk2440_iodesc));
+	s3c2440_init_uarts(smdk2440_uartcfgs, ARRAY_SIZE(smdk2440_uartcfgs));
+	s3c24xx_set_board(&smdk2440_board);
+}
+
+void __init smdk2440_init_irq(void)
+{
+	s3c2410_init_irq();
+}
+
+/* smdk2440_idle
+ *
+ * set LED7 during idle phase
+*/
+
+static void smdk2440_idle(void)
+{
+	unsigned int pin = S3C2410_GPF7;
+
+	s3c2410_gpio_setpin(pin, 0);
+	s3c24xx_default_idle();
+	s3c2410_gpio_setpin(pin, 1);
+}
+
+void __init smdk2440_machine_init(void)
+{
+	/* Configure the LEDs */
+
+	s3c2410_gpio_cfgpin(S3C2410_GPF4, S3C2410_GPF4_OUTP);
+	s3c2410_gpio_cfgpin(S3C2410_GPF5, S3C2410_GPF5_OUTP);
+	s3c2410_gpio_cfgpin(S3C2410_GPF6, S3C2410_GPF6_OUTP);
+	s3c2410_gpio_cfgpin(S3C2410_GPF7, S3C2410_GPF7_OUTP);
+
+	s3c2410_gpio_setpin(S3C2410_GPF4, 0);
+	s3c2410_gpio_setpin(S3C2410_GPF5, 1);
+	s3c2410_gpio_setpin(S3C2410_GPF6, 1);
+	s3c2410_gpio_setpin(S3C2410_GPF7, 1);
+
+	s3c24xx_idle = smdk2440_idle;
+}
+
+MACHINE_START(S3C2440, "SMDK2440")
+     MAINTAINER("Ben Dooks <ben@fluff.org>")
+     BOOT_MEM(S3C2410_SDRAM_PA, S3C2410_PA_UART, S3C2410_VA_UART)
+     BOOT_PARAMS(S3C2410_SDRAM_PA + 0x100)
+     MAPIO(smdk2440_map_io)
+     INITIRQ(smdk2440_init_irq)
+     INIT_MACHINE(smdk2440_machine_init)
+
+     .timer		= &s3c2410_timer,
+MACHINE_END
diff -urN -X ../dontdiff linux-2.6.10-rc1-bk14/arch/arm/mach-s3c2410/mach-vr1000.c linux-2.6.10-rc1-bk14-serial1/arch/arm/mach-s3c2410/mach-vr1000.c
--- linux-2.6.10-rc1-bk14/arch/arm/mach-s3c2410/mach-vr1000.c	2004-11-04 16:58:44.000000000 +0000
+++ linux-2.6.10-rc1-bk14-serial1/arch/arm/mach-s3c2410/mach-vr1000.c	2004-11-04 17:20:35.000000000 +0000
@@ -17,6 +17,7 @@
  *     06-Aug-2004 BJD  Fixed call to time initialisation
  *     05-Apr-2004 BJD  Copied to make mach-vr1000.c
  *     18-Oct-2004 BJD  Updated board struct
+ *     04-Nov-2004 BJD  Clock and serial configuration update
 */
 
 #include <linux/kernel.h>
@@ -42,6 +43,7 @@
 #include <asm/arch/regs-serial.h>
 
 #include "s3c2410.h"
+#include "clock.h"
 #include "devs.h"
 #include "cpu.h"
 #include "usb-simtec.h"
@@ -113,34 +115,52 @@
 #define ULCON S3C2410_LCON_CS8 | S3C2410_LCON_PNONE | S3C2410_LCON_STOPB
 #define UFCON S3C2410_UFCON_RXTRIG8 | S3C2410_UFCON_FIFOMODE
 
-/* base baud rate for all our UARTs */
-static unsigned long vr1000_serial_clock = 3692307;
+/* uart clock source(s) */
+
+static struct s3c24xx_uart_clksrc vr1000_serial_clocks[] = {
+	[0] = {
+		.name		= "uclk",
+		.divisor	= 1,
+		.min_baud	= 0,
+		.max_baud	= 0,
+	},
+	[1] = {
+		.name		= "pclk",
+		.divisor	= 1,
+		.min_baud	= 0,
+		.max_baud	= 0.
+	}
+};
 
 static struct s3c2410_uartcfg vr1000_uartcfgs[] = {
 	[0] = {
 		.hwport	     = 0,
 		.flags	     = 0,
-		.clock	     = &vr1000_serial_clock,
 		.ucon	     = UCON,
 		.ulcon	     = ULCON,
 		.ufcon	     = UFCON,
+		.clocks	     = vr1000_serial_clocks,
+		.clocks_size = ARRAY_SIZE(vr1000_serial_clocks),
 	},
 	[1] = {
 		.hwport	     = 1,
 		.flags	     = 0,
-		.clock	     = &vr1000_serial_clock,
 		.ucon	     = UCON,
 		.ulcon	     = ULCON,
 		.ufcon	     = UFCON,
+		.clocks	     = vr1000_serial_clocks,
+		.clocks_size = ARRAY_SIZE(vr1000_serial_clocks),
 	},
 	/* port 2 is not actually used */
 	[2] = {
 		.hwport	     = 2,
 		.flags	     = 0,
-		.clock	     = &vr1000_serial_clock,
 		.ucon	     = UCON,
 		.ulcon	     = ULCON,
 		.ufcon	     = UFCON,
+		.clocks	     = vr1000_serial_clocks,
+		.clocks_size = ARRAY_SIZE(vr1000_serial_clocks),
+
 	}
 };
 
@@ -152,14 +172,37 @@
 	&s3c_device_iis,
 };
 
+static struct clk *vr1000_clocks[] = {
+	&s3c24xx_dclk0,
+	&s3c24xx_dclk1,
+	&s3c24xx_clkout0,
+	&s3c24xx_clkout1,
+	&s3c24xx_uclk,
+};
+
 static struct s3c24xx_board vr1000_board __initdata = {
 	.devices       = vr1000_devices,
-	.devices_count = ARRAY_SIZE(vr1000_devices)
+	.devices_count = ARRAY_SIZE(vr1000_devices),
+	.clocks	       = vr1000_clocks,
+	.clocks_count  = ARRAY_SIZE(vr1000_clocks),
 };
 
 
 void __init vr1000_map_io(void)
 {
+	/* initialise clock sources */
+
+	s3c24xx_dclk0.parent = NULL;
+	s3c24xx_dclk0.rate   = 12*1000*1000;
+
+	s3c24xx_dclk1.parent = NULL;
+	s3c24xx_dclk1.rate   = 3692307;
+
+	s3c24xx_clkout0.parent  = &s3c24xx_dclk0;
+	s3c24xx_clkout1.parent  = &s3c24xx_dclk1;
+
+	s3c24xx_uclk.parent  = &s3c24xx_clkout1;
+
 	s3c24xx_init_io(vr1000_iodesc, ARRAY_SIZE(vr1000_iodesc));
 	s3c2410_init_uarts(vr1000_uartcfgs, ARRAY_SIZE(vr1000_uartcfgs));
 	s3c24xx_set_board(&vr1000_board);
diff -urN -X ../dontdiff linux-2.6.10-rc1-bk14/arch/arm/mach-s3c2410/s3c2410.c linux-2.6.10-rc1-bk14-serial1/arch/arm/mach-s3c2410/s3c2410.c
--- linux-2.6.10-rc1-bk14/arch/arm/mach-s3c2410/s3c2410.c	2004-11-04 16:58:44.000000000 +0000
+++ linux-2.6.10-rc1-bk14-serial1/arch/arm/mach-s3c2410/s3c2410.c	2004-11-04 17:35:42.000000000 +0000
@@ -1,7 +1,7 @@
 /* linux/arch/arm/mach-s3c2410/s3c2410.c
  *
  * Copyright (c) 2003,2004 Simtec Electronics
- * Ben Dooks <ben@simtec.co.uk>
+ *	Ben Dooks <ben@simtec.co.uk>
  *
  * http://www.simtec.co.uk/products/EB2410ITX/
  *
@@ -16,6 +16,7 @@
  *     18-Jan-2004 BJD  Added serial port configuration
  *     21-Aug-2004 BJD  Added new struct s3c2410_board handler
  *     28-Sep-2004 BJD  Updates for new serial port bits
+ *     04-Nov-2004 BJD  Updated UART configuration process
 */
 
 #include <linux/kernel.h>
@@ -43,10 +44,6 @@
 
 int s3c2410_clock_tick_rate = 12*1000*1000;  /* current timers at 12MHz */
 
-/* serial port setup */
-
-struct s3c2410_uartcfg *s3c2410_uartcfgs;
-
 /* Initial IO mappings */
 
 static struct map_desc s3c2410_iodesc[] __initdata = {
@@ -141,12 +138,10 @@
 	struct platform_device *platdev;
 	int uart;
 
-	s3c2410_uartcfgs = cfg;		/* compatibility */
-
 	for (uart = 0; uart < no; uart++, cfg++) {
 		platdev = uart_devices[cfg->hwport];
 
-		s3c2410_uart_devices[uart] = platdev;
+		s3c24xx_uart_devs[uart] = platdev;
 		platdev->dev.platform_data = cfg;
 	}
 
@@ -199,10 +194,7 @@
 
 int __init s3c2410_init(void)
 {
-	int ret;
-
 	printk("S3C2410: Initialising architecture\n");
 
-	ret = platform_add_devices(uart_devices, ARRAY_SIZE(uart_devices));
-	return ret;
+	return platform_add_devices(s3c24xx_uart_devs, s3c2410_uart_count);
 }
diff -urN -X ../dontdiff linux-2.6.10-rc1-bk14/arch/arm/mach-s3c2410/s3c2440.c linux-2.6.10-rc1-bk14-serial1/arch/arm/mach-s3c2410/s3c2440.c
--- linux-2.6.10-rc1-bk14/arch/arm/mach-s3c2410/s3c2440.c	2004-11-04 16:58:44.000000000 +0000
+++ linux-2.6.10-rc1-bk14-serial1/arch/arm/mach-s3c2410/s3c2440.c	2004-11-04 17:39:36.000000000 +0000
@@ -13,6 +13,7 @@
  *	24-Aug-2004 BJD  Start of s3c2440 support
  *	12-Oct-2004 BJD	 Moved clock info out to clock.c
  *	01-Nov-2004 BJD  Fixed clock build code
+ *	04-Nov-2004 BJD  New serial registration
 */
 
 #include <linux/kernel.h>
@@ -122,6 +123,25 @@
 	&s3c_uart2
 };
 
+/* uart initialisation */
+
+static int __initdata s3c2440_uart_count;
+
+void __init s3c2440_init_uarts(struct s3c2410_uartcfg *cfg, int no)
+{
+	struct platform_device *platdev;
+	int uart;
+
+	for (uart = 0; uart < no; uart++, cfg++) {
+		platdev = uart_devices[cfg->hwport];
+
+		s3c24xx_uart_devs[uart] = platdev;
+		platdev->dev.platform_data = cfg;
+	}
+
+	s3c2440_uart_count = uart;
+}
+
 /* s3c2440 specific clock sources */
 
 static struct clk s3c2440_clk_cam = {
@@ -206,11 +226,8 @@
 
 int __init s3c2440_init(void)
 {
-	int ret;
-
 	printk("S3C2440: Initialising architecture\n");
 
-	ret = platform_add_devices(uart_devices, ARRAY_SIZE(uart_devices));
-	return ret;
+	return platform_add_devices(s3c24xx_uart_devs, s3c2440_uart_count);
 }
 
diff -urN -X ../dontdiff linux-2.6.10-rc1-bk14/arch/arm/mach-s3c2410/s3c2440.h linux-2.6.10-rc1-bk14-serial1/arch/arm/mach-s3c2410/s3c2440.h
--- linux-2.6.10-rc1-bk14/arch/arm/mach-s3c2410/s3c2440.h	2004-10-18 22:54:38.000000000 +0100
+++ linux-2.6.10-rc1-bk14-serial1/arch/arm/mach-s3c2410/s3c2440.h	2004-11-04 17:45:37.000000000 +0000
@@ -10,9 +10,14 @@
  * published by the Free Software Foundation.
  *
  * Modifications:
- *     24-Aug-2004 BJD  Start of S3C2440 CPU support
+ *	24-Aug-2004 BJD  Start of S3C2440 CPU support
+ *	04-Nov-2004 BJD  Added s3c2440_init_uarts()
 */
 
+struct s3c2410_uartcfg;
+
 extern void s3c2440_init_irq(void);
 
 extern void s3c2440_init_time(void);
+
+extern void s3c2440_init_uarts(struct s3c2410_uartcfg *cfg, int no);
