fedora core 6 1.2949 + vserver 2.2.0
[linux-2.6.git] / drivers / serial / 8250.c
index 2ade801..545f508 100644 (file)
@@ -7,6 +7,9 @@
  *
  *  Copyright (C) 2001 Russell King.
  *
+ *  2005/09/16: Enabled higher baud rates for 16C95x.
+ *             (Mathias Adam <a2@adamis.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
  *  mapbase is the physical address of the IO port.
  *  membase is an 'ioremapped' cookie.
  */
-#include <linux/config.h>
+
+#if defined(CONFIG_SERIAL_8250_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ)
+#define SUPPORT_SYSRQ
+#endif
+
 #include <linux/module.h>
 #include <linux/moduleparam.h>
-#include <linux/tty.h>
 #include <linux/ioport.h>
 #include <linux/init.h>
 #include <linux/console.h>
 #include <linux/sysrq.h>
+#include <linux/delay.h>
+#include <linux/platform_device.h>
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
 #include <linux/serial_reg.h>
+#include <linux/serial_core.h>
 #include <linux/serial.h>
-#include <linux/serialP.h>
-#include <linux/delay.h>
-#include <linux/device.h>
+#include <linux/serial_8250.h>
+#include <linux/nmi.h>
+#include <linux/mutex.h>
 
 #include <asm/io.h>
 #include <asm/irq.h>
 
-#if defined(CONFIG_SERIAL_8250_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ)
-#define SUPPORT_SYSRQ
-#endif
-
-#include <linux/serial_core.h>
 #include "8250.h"
 
 /*
  * Configuration:
- *   share_irqs - whether we pass SA_SHIRQ to request_irq().  This option
+ *   share_irqs - whether we pass IRQF_SHARED to request_irq().  This option
  *                is unsafe when used on edge-triggered interrupts.
  */
-unsigned int share_irqs = SERIAL8250_SHARE_IRQS;
+static unsigned int share_irqs = SERIAL8250_SHARE_IRQS;
+
+static unsigned int nr_uarts = CONFIG_SERIAL_8250_RUNTIME_UARTS;
 
 /*
  * Debugging.
@@ -74,23 +82,9 @@ unsigned int share_irqs = SERIAL8250_SHARE_IRQS;
  */
 #define is_real_interrupt(irq) ((irq) != 0)
 
-/*
- * This converts from our new CONFIG_ symbols to the symbols
- * that asm/serial.h expects.  You _NEED_ to comment out the
- * linux/config.h include contained inside asm/serial.h for
- * this to work.
- */
-#undef CONFIG_SERIAL_MANY_PORTS
-#undef CONFIG_SERIAL_DETECT_IRQ
-#undef CONFIG_SERIAL_MULTIPORT
-#undef CONFIG_HUB6
-
 #ifdef CONFIG_SERIAL_8250_DETECT_IRQ
 #define CONFIG_SERIAL_DETECT_IRQ 1
 #endif
-#ifdef CONFIG_SERIAL_8250_MULTIPORT
-#define CONFIG_SERIAL_MULTIPORT 1
-#endif
 #ifdef CONFIG_SERIAL_8250_MANY_PORTS
 #define CONFIG_SERIAL_MANY_PORTS 1
 #endif
@@ -112,11 +106,11 @@ unsigned int share_irqs = SERIAL8250_SHARE_IRQS;
 #define SERIAL_PORT_DFNS
 #endif
 
-static struct old_serial_port old_serial_port[] = {
+static const struct old_serial_port old_serial_port[] = {
        SERIAL_PORT_DFNS /* defined in asm/serial.h */
 };
 
-#define UART_NR        (ARRAY_SIZE(old_serial_port) + CONFIG_SERIAL_8250_NR_UARTS)
+#define UART_NR        CONFIG_SERIAL_8250_NR_UARTS
 
 #ifdef CONFIG_SERIAL_8250_RSA
 
@@ -129,9 +123,9 @@ struct uart_8250_port {
        struct uart_port        port;
        struct timer_list       timer;          /* "no irq" timer */
        struct list_head        list;           /* ports on this IRQ */
-       unsigned int            capabilities;   /* port capabilities */
+       unsigned short          capabilities;   /* port capabilities */
+       unsigned short          bugs;           /* port bugs */
        unsigned int            tx_loadsz;      /* transmit fifo load size */
-       unsigned short          rev;
        unsigned char           acr;
        unsigned char           ier;
        unsigned char           lcr;
@@ -157,28 +151,159 @@ static struct irq_info irq_lists[NR_IRQS];
 /*
  * Here we define the default xmit fifo size used for each type of UART.
  */
-static const struct serial8250_config uart_config[PORT_MAX_8250+1] = {
-       { "unknown",    1,      1,      0 },
-       { "8250",       1,      1,      0 },
-       { "16450",      1,      1,      0 },
-       { "16550",      1,      1,      0 },
-       { "16550A",     16,     16,     UART_CAP_FIFO },
-       { "Cirrus",     1,      1,      0 },
-       { "ST16650",    1,      1,      UART_CAP_FIFO | UART_CAP_SLEEP | UART_CAP_EFR },
-       { "ST16650V2",  32,     16,     UART_CAP_FIFO | UART_CAP_SLEEP | UART_CAP_EFR },
-       { "TI16750",    64,     64,     UART_CAP_FIFO | UART_CAP_SLEEP },
-       { "Startech",   1,      1,      0 },
-       { "16C950/954", 128,    128,    UART_CAP_FIFO },
-       { "ST16654",    64,     32,     UART_CAP_FIFO | UART_CAP_SLEEP | UART_CAP_EFR },
-       { "XR16850",    128,    128,    UART_CAP_FIFO | UART_CAP_SLEEP | UART_CAP_EFR },
-       { "RSA",        2048,   2048,   UART_CAP_FIFO },
-       { "NS16550A",   16,     16,     UART_CAP_FIFO | UART_NATSEMI },
-       { "XScale",     32,     32,     UART_CAP_FIFO },
+static const struct serial8250_config uart_config[] = {
+       [PORT_UNKNOWN] = {
+               .name           = "unknown",
+               .fifo_size      = 1,
+               .tx_loadsz      = 1,
+       },
+       [PORT_8250] = {
+               .name           = "8250",
+               .fifo_size      = 1,
+               .tx_loadsz      = 1,
+       },
+       [PORT_16450] = {
+               .name           = "16450",
+               .fifo_size      = 1,
+               .tx_loadsz      = 1,
+       },
+       [PORT_16550] = {
+               .name           = "16550",
+               .fifo_size      = 1,
+               .tx_loadsz      = 1,
+       },
+       [PORT_16550A] = {
+               .name           = "16550A",
+               .fifo_size      = 16,
+               .tx_loadsz      = 16,
+               .fcr            = UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_10,
+               .flags          = UART_CAP_FIFO,
+       },
+       [PORT_CIRRUS] = {
+               .name           = "Cirrus",
+               .fifo_size      = 1,
+               .tx_loadsz      = 1,
+       },
+       [PORT_16650] = {
+               .name           = "ST16650",
+               .fifo_size      = 1,
+               .tx_loadsz      = 1,
+               .flags          = UART_CAP_FIFO | UART_CAP_EFR | UART_CAP_SLEEP,
+       },
+       [PORT_16650V2] = {
+               .name           = "ST16650V2",
+               .fifo_size      = 32,
+               .tx_loadsz      = 16,
+               .fcr            = UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_01 |
+                                 UART_FCR_T_TRIG_00,
+               .flags          = UART_CAP_FIFO | UART_CAP_EFR | UART_CAP_SLEEP,
+       },
+       [PORT_16750] = {
+               .name           = "TI16750",
+               .fifo_size      = 64,
+               .tx_loadsz      = 64,
+               .fcr            = UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_10 |
+                                 UART_FCR7_64BYTE,
+               .flags          = UART_CAP_FIFO | UART_CAP_SLEEP | UART_CAP_AFE,
+       },
+       [PORT_STARTECH] = {
+               .name           = "Startech",
+               .fifo_size      = 1,
+               .tx_loadsz      = 1,
+       },
+       [PORT_16C950] = {
+               .name           = "16C950/954",
+               .fifo_size      = 128,
+               .tx_loadsz      = 128,
+               .fcr            = UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_10,
+               .flags          = UART_CAP_FIFO,
+       },
+       [PORT_16654] = {
+               .name           = "ST16654",
+               .fifo_size      = 64,
+               .tx_loadsz      = 32,
+               .fcr            = UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_01 |
+                                 UART_FCR_T_TRIG_10,
+               .flags          = UART_CAP_FIFO | UART_CAP_EFR | UART_CAP_SLEEP,
+       },
+       [PORT_16850] = {
+               .name           = "XR16850",
+               .fifo_size      = 128,
+               .tx_loadsz      = 128,
+               .fcr            = UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_10,
+               .flags          = UART_CAP_FIFO | UART_CAP_EFR | UART_CAP_SLEEP,
+       },
+       [PORT_RSA] = {
+               .name           = "RSA",
+               .fifo_size      = 2048,
+               .tx_loadsz      = 2048,
+               .fcr            = UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_11,
+               .flags          = UART_CAP_FIFO,
+       },
+       [PORT_NS16550A] = {
+               .name           = "NS16550A",
+               .fifo_size      = 16,
+               .tx_loadsz      = 16,
+               .fcr            = UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_10,
+               .flags          = UART_CAP_FIFO | UART_NATSEMI,
+       },
+       [PORT_XSCALE] = {
+               .name           = "XScale",
+               .fifo_size      = 32,
+               .tx_loadsz      = 32,
+               .fcr            = UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_10,
+               .flags          = UART_CAP_FIFO | UART_CAP_UUE,
+       },
+};
+
+#ifdef CONFIG_SERIAL_8250_AU1X00
+
+/* Au1x00 UART hardware has a weird register layout */
+static const u8 au_io_in_map[] = {
+       [UART_RX]  = 0,
+       [UART_IER] = 2,
+       [UART_IIR] = 3,
+       [UART_LCR] = 5,
+       [UART_MCR] = 6,
+       [UART_LSR] = 7,
+       [UART_MSR] = 8,
 };
 
-static _INLINE_ unsigned int serial_in(struct uart_8250_port *up, int offset)
+static const u8 au_io_out_map[] = {
+       [UART_TX]  = 1,
+       [UART_IER] = 2,
+       [UART_FCR] = 4,
+       [UART_LCR] = 5,
+       [UART_MCR] = 6,
+};
+
+/* sane hardware needs no mapping */
+static inline int map_8250_in_reg(struct uart_8250_port *up, int offset)
 {
-       offset <<= up->port.regshift;
+       if (up->port.iotype != UPIO_AU)
+               return offset;
+       return au_io_in_map[offset];
+}
+
+static inline int map_8250_out_reg(struct uart_8250_port *up, int offset)
+{
+       if (up->port.iotype != UPIO_AU)
+               return offset;
+       return au_io_out_map[offset];
+}
+
+#else
+
+/* sane hardware needs no mapping */
+#define map_8250_in_reg(up, offset) (offset)
+#define map_8250_out_reg(up, offset) (offset)
+
+#endif
+
+static unsigned int serial_in(struct uart_8250_port *up, int offset)
+{
+       unsigned int tmp;
+       offset = map_8250_in_reg(up, offset) << up->port.regshift;
 
        switch (up->port.iotype) {
        case UPIO_HUB6:
@@ -191,15 +316,27 @@ static _INLINE_ unsigned int serial_in(struct uart_8250_port *up, int offset)
        case UPIO_MEM32:
                return readl(up->port.membase + offset);
 
+#ifdef CONFIG_SERIAL_8250_AU1X00
+       case UPIO_AU:
+               return __raw_readl(up->port.membase + offset);
+#endif
+
+       case UPIO_TSI:
+               if (offset == UART_IIR) {
+                       tmp = readl(up->port.membase + (UART_IIR & ~3));
+                       return (tmp >> 16) & 0xff; /* UART_IIR % 4 == 2 */
+               } else
+                       return readb(up->port.membase + offset);
+
        default:
                return inb(up->port.iobase + offset);
        }
 }
 
-static _INLINE_ void
+static void
 serial_out(struct uart_8250_port *up, int offset, int value)
 {
-       offset <<= up->port.regshift;
+       offset = map_8250_out_reg(up, offset) << up->port.regshift;
 
        switch (up->port.iotype) {
        case UPIO_HUB6:
@@ -215,6 +352,16 @@ serial_out(struct uart_8250_port *up, int offset, int value)
                writel(value, up->port.membase + offset);
                break;
 
+#ifdef CONFIG_SERIAL_8250_AU1X00
+       case UPIO_AU:
+               __raw_writel(value, up->port.membase + offset);
+               break;
+#endif
+       case UPIO_TSI:
+               if (!((offset == UART_IER) && (value & UART_IER_UUE)))
+                       writeb(value, up->port.membase + offset);
+               break;
+
        default:
                outb(value, up->port.iobase + offset);
        }
@@ -229,6 +376,40 @@ serial_out(struct uart_8250_port *up, int offset, int value)
 #define serial_inp(up, offset)         serial_in(up, offset)
 #define serial_outp(up, offset, value) serial_out(up, offset, value)
 
+/* Uart divisor latch read */
+static inline int _serial_dl_read(struct uart_8250_port *up)
+{
+       return serial_inp(up, UART_DLL) | serial_inp(up, UART_DLM) << 8;
+}
+
+/* Uart divisor latch write */
+static inline void _serial_dl_write(struct uart_8250_port *up, int value)
+{
+       serial_outp(up, UART_DLL, value & 0xff);
+       serial_outp(up, UART_DLM, value >> 8 & 0xff);
+}
+
+#ifdef CONFIG_SERIAL_8250_AU1X00
+/* Au1x00 haven't got a standard divisor latch */
+static int serial_dl_read(struct uart_8250_port *up)
+{
+       if (up->port.iotype == UPIO_AU)
+               return __raw_readl(up->port.membase + 0x28);
+       else
+               return _serial_dl_read(up);
+}
+
+static void serial_dl_write(struct uart_8250_port *up, int value)
+{
+       if (up->port.iotype == UPIO_AU)
+               __raw_writel(value, up->port.membase + 0x28);
+       else
+               _serial_dl_write(up, value);
+}
+#else
+#define serial_dl_read(up) _serial_dl_read(up)
+#define serial_dl_write(up, value) _serial_dl_write(up, value)
+#endif
 
 /*
  * For the 16C950
@@ -361,19 +542,20 @@ static void disable_rsa(struct uart_8250_port *up)
  */
 static int size_fifo(struct uart_8250_port *up)
 {
-       unsigned char old_fcr, old_mcr, old_dll, old_dlm;
+       unsigned char old_fcr, old_mcr, old_lcr;
+       unsigned short old_dl;
        int count;
 
+       old_lcr = serial_inp(up, UART_LCR);
+       serial_outp(up, UART_LCR, 0);
        old_fcr = serial_inp(up, UART_FCR);
        old_mcr = serial_inp(up, UART_MCR);
        serial_outp(up, UART_FCR, UART_FCR_ENABLE_FIFO |
                    UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT);
        serial_outp(up, UART_MCR, UART_MCR_LOOP);
        serial_outp(up, UART_LCR, UART_LCR_DLAB);
-       old_dll = serial_inp(up, UART_DLL);
-       old_dlm = serial_inp(up, UART_DLM);
-       serial_outp(up, UART_DLL, 0x01);
-       serial_outp(up, UART_DLM, 0x00);
+       old_dl = serial_dl_read(up);
+       serial_dl_write(up, 0x0001);
        serial_outp(up, UART_LCR, 0x03);
        for (count = 0; count < 256; count++)
                serial_outp(up, UART_TX, count);
@@ -384,12 +566,40 @@ static int size_fifo(struct uart_8250_port *up)
        serial_outp(up, UART_FCR, old_fcr);
        serial_outp(up, UART_MCR, old_mcr);
        serial_outp(up, UART_LCR, UART_LCR_DLAB);
-       serial_outp(up, UART_DLL, old_dll);
-       serial_outp(up, UART_DLM, old_dlm);
+       serial_dl_write(up, old_dl);
+       serial_outp(up, UART_LCR, old_lcr);
 
        return count;
 }
 
+/*
+ * Read UART ID using the divisor method - set DLL and DLM to zero
+ * and the revision will be in DLL and device type in DLM.  We
+ * preserve the device state across this.
+ */
+static unsigned int autoconfig_read_divisor_id(struct uart_8250_port *p)
+{
+       unsigned char old_dll, old_dlm, old_lcr;
+       unsigned int id;
+
+       old_lcr = serial_inp(p, UART_LCR);
+       serial_outp(p, UART_LCR, UART_LCR_DLAB);
+
+       old_dll = serial_inp(p, UART_DLL);
+       old_dlm = serial_inp(p, UART_DLM);
+
+       serial_outp(p, UART_DLL, 0);
+       serial_outp(p, UART_DLM, 0);
+
+       id = serial_inp(p, UART_DLL) | serial_inp(p, UART_DLM) << 8;
+
+       serial_outp(p, UART_DLL, old_dll);
+       serial_outp(p, UART_DLM, old_dlm);
+       serial_outp(p, UART_LCR, old_lcr);
+
+       return id;
+}
+
 /*
  * This is a helper routine to autodetect StarTech/Exar/Oxsemi UART's.
  * When this function is called we know it is at least a StarTech
@@ -402,7 +612,12 @@ static int size_fifo(struct uart_8250_port *up)
  */
 static void autoconfig_has_efr(struct uart_8250_port *up)
 {
-       unsigned char id1, id2, id3, rev, saved_dll, saved_dlm;
+       unsigned int id1, id2, id3, rev;
+
+       /*
+        * Everything with an EFR has SLEEP
+        */
+       up->capabilities |= UART_CAP_EFR | UART_CAP_SLEEP;
 
        /*
         * First we check to see if it's an Oxford Semiconductor UART.
@@ -435,7 +650,14 @@ static void autoconfig_has_efr(struct uart_8250_port *up)
        if (id1 == 0x16 && id2 == 0xC9 &&
            (id3 == 0x50 || id3 == 0x52 || id3 == 0x54)) {
                up->port.type = PORT_16C950;
-               up->rev = rev | (id3 << 8);
+
+               /*
+                * Enable work around for the Oxford Semiconductor 952 rev B
+                * chip which causes it to seriously miscalculate baud rates
+                * when DLL is 0.
+                */
+               if (id3 == 0x52 && rev == 0x01)
+                       up->bugs |= UART_BUG_QUOT;
                return;
        }
        
@@ -447,21 +669,11 @@ static void autoconfig_has_efr(struct uart_8250_port *up)
         *  0x12 - XR16C2850.
         *  0x14 - XR16C854.
         */
-       serial_outp(up, UART_LCR, UART_LCR_DLAB);
-       saved_dll = serial_inp(up, UART_DLL);
-       saved_dlm = serial_inp(up, UART_DLM);
-       serial_outp(up, UART_DLL, 0);
-       serial_outp(up, UART_DLM, 0);
-       id2 = serial_inp(up, UART_DLL);
-       id1 = serial_inp(up, UART_DLM);
-       serial_outp(up, UART_DLL, saved_dll);
-       serial_outp(up, UART_DLM, saved_dlm);
-
-       DEBUG_AUTOCONF("850id=%02x:%02x ", id1, id2);
-
-       if (id1 == 0x10 || id1 == 0x12 || id1 == 0x14) {
-               if (id1 == 0x10)
-                       up->rev = id2;
+       id1 = autoconfig_read_divisor_id(up);
+       DEBUG_AUTOCONF("850id=%04x ", id1);
+
+       id2 = id1 >> 8;
+       if (id2 == 0x10 || id2 == 0x12 || id2 == 0x14) {
                up->port.type = PORT_16850;
                return;
        }
@@ -503,6 +715,19 @@ static void autoconfig_8250(struct uart_8250_port *up)
                up->port.type = PORT_16450;
 }
 
+static int broken_efr(struct uart_8250_port *up)
+{
+       /*
+        * Exar ST16C2550 "A2" devices incorrectly detect as
+        * having an EFR, and report an ID of 0x0201.  See
+        * http://www.exar.com/info.php?pdf=dan180_oct2004.pdf
+        */
+       if (autoconfig_read_divisor_id(up) == 0x0201 && size_fifo(up) == 16)
+               return 1;
+
+       return 0;
+}
+
 /*
  * We know that the chip has FIFOs.  Does it have an EFR?  The
  * EFR is located in the same register position as the IIR and
@@ -512,8 +737,10 @@ static void autoconfig_8250(struct uart_8250_port *up)
 static void autoconfig_16550a(struct uart_8250_port *up)
 {
        unsigned char status1, status2;
+       unsigned int iersave;
 
        up->port.type = PORT_16550A;
+       up->capabilities |= UART_CAP_FIFO;
 
        /*
         * Check for presence of the EFR when DLAB is set.
@@ -525,6 +752,7 @@ static void autoconfig_16550a(struct uart_8250_port *up)
                if (serial_in(up, UART_EFR) != 0) {
                        DEBUG_AUTOCONF("EFRv1 ");
                        up->port.type = PORT_16650;
+                       up->capabilities |= UART_CAP_EFR | UART_CAP_SLEEP;
                } else {
                        DEBUG_AUTOCONF("Motorola 8xxx DUART ");
                }
@@ -537,7 +765,7 @@ static void autoconfig_16550a(struct uart_8250_port *up)
         * (other ST16C650V2 UARTs, TI16C752A, etc)
         */
        serial_outp(up, UART_LCR, 0xBF);
-       if (serial_in(up, UART_EFR) == 0) {
+       if (serial_in(up, UART_EFR) == 0 && !broken_efr(up)) {
                DEBUG_AUTOCONF("EFRv2 ");
                autoconfig_has_efr(up);
                return;
@@ -549,8 +777,6 @@ static void autoconfig_16550a(struct uart_8250_port *up)
         * from EXCR1. Switch back to bank 0, change it in MCR. Then
         * switch back to bank 2, read it from EXCR1 again and check
         * it's changed. If so, set baud_base in EXCR2 to 921600. -- dwmw2
-        * On PowerPC we don't want to change baud_base, as we have
-        * a number of different divisors.  -- Tom Rini
         */
        serial_outp(up, UART_LCR, 0);
        status1 = serial_in(up, UART_MCR);
@@ -566,17 +792,25 @@ static void autoconfig_16550a(struct uart_8250_port *up)
                serial_outp(up, UART_MCR, status1);
 
                if ((status2 ^ status1) & UART_MCR_LOOP) {
-#ifndef CONFIG_PPC
+                       unsigned short quot;
+
                        serial_outp(up, UART_LCR, 0xE0);
+
+                       quot = serial_dl_read(up);
+                       quot <<= 3;
+
                        status1 = serial_in(up, 0x04); /* EXCR1 */
                        status1 &= ~0xB0; /* Disable LOCK, mask out PRESL[01] */
                        status1 |= 0x10;  /* 1.625 divisor for baud_base --> 921600 */
                        serial_outp(up, 0x04, status1);
+                       
+                       serial_dl_write(up, quot);
+
                        serial_outp(up, UART_LCR, 0);
-                       up->port.uartclk = 921600*16;
-#endif
 
+                       up->port.uartclk = 921600*16;
                        up->port.type = PORT_NS16550A;
+                       up->capabilities |= UART_NATSEMI;
                        return;
                }
        }
@@ -595,13 +829,50 @@ static void autoconfig_16550a(struct uart_8250_port *up)
        serial_outp(up, UART_FCR, UART_FCR_ENABLE_FIFO | UART_FCR7_64BYTE);
        status2 = serial_in(up, UART_IIR) >> 5;
        serial_outp(up, UART_FCR, UART_FCR_ENABLE_FIFO);
+       serial_outp(up, UART_LCR, 0);
 
        DEBUG_AUTOCONF("iir1=%d iir2=%d ", status1, status2);
 
        if (status1 == 6 && status2 == 7) {
                up->port.type = PORT_16750;
+               up->capabilities |= UART_CAP_AFE | UART_CAP_SLEEP;
                return;
        }
+
+       /*
+        * Try writing and reading the UART_IER_UUE bit (b6).
+        * If it works, this is probably one of the Xscale platform's
+        * internal UARTs.
+        * We're going to explicitly set the UUE bit to 0 before
+        * trying to write and read a 1 just to make sure it's not
+        * already a 1 and maybe locked there before we even start start.
+        */
+       iersave = serial_in(up, UART_IER);
+       serial_outp(up, UART_IER, iersave & ~UART_IER_UUE);
+       if (!(serial_in(up, UART_IER) & UART_IER_UUE)) {
+               /*
+                * OK it's in a known zero state, try writing and reading
+                * without disturbing the current state of the other bits.
+                */
+               serial_outp(up, UART_IER, iersave | UART_IER_UUE);
+               if (serial_in(up, UART_IER) & UART_IER_UUE) {
+                       /*
+                        * It's an Xscale.
+                        * We'll leave the UART_IER_UUE bit set to 1 (enabled).
+                        */
+                       DEBUG_AUTOCONF("Xscale ");
+                       up->port.type = PORT_XSCALE;
+                       up->capabilities |= UART_CAP_UUE;
+                       return;
+               }
+       } else {
+               /*
+                * If we got here we couldn't force the IER_UUE bit to 0.
+                * Log it and continue.
+                */
+               DEBUG_AUTOCONF("Couldn't force IER_UUE to 0 ");
+       }
+       serial_outp(up, UART_IER, iersave);
 }
 
 /*
@@ -630,6 +901,9 @@ static void autoconfig(struct uart_8250_port *up, unsigned int probeflags)
        spin_lock_irqsave(&up->port.lock, flags);
 //     save_flags(flags); cli();
 
+       up->capabilities = 0;
+       up->bugs = 0;
+
        if (!(up->port.flags & UPF_BUGGY_UART)) {
                /*
                 * Do a simple existence test first; if we fail this,
@@ -692,7 +966,7 @@ static void autoconfig(struct uart_8250_port *up, unsigned int probeflags)
        /*
         * We're pretty sure there's a port here.  Lets find out what
         * type of port it is.  The IIR top two bits allows us to find
-        * out if its 8250 or 16450, 16550, 16550A or later.  This
+        * out if it's 8250 or 16450, 16550, 16550A or later.  This
         * determines what we test for next.
         *
         * We also initialise the EFR (if any) to zero for later.  The
@@ -738,8 +1012,22 @@ static void autoconfig(struct uart_8250_port *up, unsigned int probeflags)
                }
        }
 #endif
+
+#ifdef CONFIG_SERIAL_8250_AU1X00
+       /* if access method is AU, it is a 16550 with a quirk */
+       if (up->port.type == PORT_16550A && up->port.iotype == UPIO_AU)
+               up->bugs |= UART_BUG_NOMSR;
+#endif
+
        serial_outp(up, UART_LCR, save_lcr);
 
+       if (up->capabilities != uart_config[up->port.type].flags) {
+               printk(KERN_WARNING
+                      "ttyS%d: detected caps %08x should be %08x\n",
+                       up->port.line, up->capabilities,
+                       uart_config[up->port.type].flags);
+       }
+
        up->port.fifosize = uart_config[up->port.type].fifo_size;
        up->capabilities = uart_config[up->port.type].flags;
        up->tx_loadsz = uart_config[up->port.type].tx_loadsz;
@@ -757,7 +1045,10 @@ static void autoconfig(struct uart_8250_port *up, unsigned int probeflags)
        serial_outp(up, UART_MCR, save_mcr);
        serial8250_clear_fifos(up);
        (void)serial_in(up, UART_RX);
-       serial_outp(up, UART_IER, 0);
+       if (up->capabilities & UART_CAP_UUE)
+               serial_outp(up, UART_IER, UART_IER_UUE);
+       else
+               serial_outp(up, UART_IER, 0);
 
  out:  
        spin_unlock_irqrestore(&up->port.lock, flags);
@@ -814,32 +1105,52 @@ static void autoconfig_irq(struct uart_8250_port *up)
        up->port.irq = (irq > 0) ? irq : 0;
 }
 
-static void serial8250_stop_tx(struct uart_port *port, unsigned int tty_stop)
+static inline void __stop_tx(struct uart_8250_port *p)
+{
+       if (p->ier & UART_IER_THRI) {
+               p->ier &= ~UART_IER_THRI;
+               serial_out(p, UART_IER, p->ier);
+       }
+}
+
+static void serial8250_stop_tx(struct uart_port *port)
 {
        struct uart_8250_port *up = (struct uart_8250_port *)port;
 
-       if (up->ier & UART_IER_THRI) {
-               up->ier &= ~UART_IER_THRI;
-               serial_out(up, UART_IER, up->ier);
-       }
-       if (up->port.type == PORT_16C950 && tty_stop) {
+       __stop_tx(up);
+
+       /*
+        * We really want to stop the transmitter from sending.
+        */
+       if (up->port.type == PORT_16C950) {
                up->acr |= UART_ACR_TXDIS;
                serial_icr_write(up, UART_ACR, up->acr);
        }
 }
 
-static void serial8250_start_tx(struct uart_port *port, unsigned int tty_start)
+static void transmit_chars(struct uart_8250_port *up);
+
+static void serial8250_start_tx(struct uart_port *port)
 {
        struct uart_8250_port *up = (struct uart_8250_port *)port;
 
        if (!(up->ier & UART_IER_THRI)) {
                up->ier |= UART_IER_THRI;
                serial_out(up, UART_IER, up->ier);
+
+               if (up->bugs & UART_BUG_TXEN) {
+                       unsigned char lsr, iir;
+                       lsr = serial_in(up, UART_LSR);
+                       iir = serial_in(up, UART_IIR);
+                       if (lsr & UART_LSR_TEMT && iir & UART_IIR_NO_INT)
+                               transmit_chars(up);
+               }
        }
+
        /*
-        * We only do this from uart_start
+        * Re-enable the transmitter if we disabled it.
         */
-       if (tty_start && up->port.type == PORT_16C950) {
+       if (up->port.type == PORT_16C950 && up->acr & UART_ACR_TXDIS) {
                up->acr &= ~UART_ACR_TXDIS;
                serial_icr_write(up, UART_ACR, up->acr);
        }
@@ -858,35 +1169,44 @@ static void serial8250_enable_ms(struct uart_port *port)
 {
        struct uart_8250_port *up = (struct uart_8250_port *)port;
 
+       /* no MSR capabilities */
+       if (up->bugs & UART_BUG_NOMSR)
+               return;
+
        up->ier |= UART_IER_MSI;
        serial_out(up, UART_IER, up->ier);
 }
 
-static _INLINE_ void
-receive_chars(struct uart_8250_port *up, int *status, struct pt_regs *regs)
+static void
+receive_chars(struct uart_8250_port *up, int *status)
 {
        struct tty_struct *tty = up->port.info->tty;
-       unsigned char ch;
+       unsigned char ch, lsr = *status;
        int max_count = 256;
+       char flag;
 
        do {
-               if (unlikely(tty->flip.count >= TTY_FLIPBUF_SIZE)) {
-                       tty->flip.work.func((void *)tty);
-                       if (tty->flip.count >= TTY_FLIPBUF_SIZE)
-                               return; // if TTY_DONT_FLIP is set
-               }
                ch = serial_inp(up, UART_RX);
-               *tty->flip.char_buf_ptr = ch;
-               *tty->flip.flag_buf_ptr = TTY_NORMAL;
+               flag = TTY_NORMAL;
                up->port.icount.rx++;
 
-               if (unlikely(*status & (UART_LSR_BI | UART_LSR_PE |
-                                      UART_LSR_FE | UART_LSR_OE))) {
+#ifdef CONFIG_SERIAL_8250_CONSOLE
+               /*
+                * Recover the break flag from console xmit
+                */
+               if (up->port.line == up->port.cons->index) {
+                       lsr |= up->lsr_break_flag;
+                       up->lsr_break_flag = 0;
+               }
+#endif
+
+               if (unlikely(lsr & (UART_LSR_BI | UART_LSR_PE |
+                                   UART_LSR_FE | UART_LSR_OE))) {
                        /*
                         * For statistics only
                         */
-                       if (*status & UART_LSR_BI) {
-                               *status &= ~(UART_LSR_FE | UART_LSR_PE);
+                       if (lsr & UART_LSR_BI) {
+                               lsr &= ~(UART_LSR_FE | UART_LSR_PE);
                                up->port.icount.brk++;
                                /*
                                 * We do the SysRQ and SAK checking
@@ -896,59 +1216,41 @@ receive_chars(struct uart_8250_port *up, int *status, struct pt_regs *regs)
                                 */
                                if (uart_handle_break(&up->port))
                                        goto ignore_char;
-                       } else if (*status & UART_LSR_PE)
+                       } else if (lsr & UART_LSR_PE)
                                up->port.icount.parity++;
-                       else if (*status & UART_LSR_FE)
+                       else if (lsr & UART_LSR_FE)
                                up->port.icount.frame++;
-                       if (*status & UART_LSR_OE)
+                       if (lsr & UART_LSR_OE)
                                up->port.icount.overrun++;
 
                        /*
-                        * Mask off conditions which should be ingored.
+                        * Mask off conditions which should be ignored.
                         */
-                       *status &= up->port.read_status_mask;
+                       lsr &= up->port.read_status_mask;
 
-#ifdef CONFIG_SERIAL_8250_CONSOLE
-                       if (up->port.line == up->port.cons->index) {
-                               /* Recover the break flag from console xmit */
-                               *status |= up->lsr_break_flag;
-                               up->lsr_break_flag = 0;
-                       }
-#endif
-                       if (*status & UART_LSR_BI) {
+                       if (lsr & UART_LSR_BI) {
                                DEBUG_INTR("handling break....");
-                               *tty->flip.flag_buf_ptr = TTY_BREAK;
-                       } else if (*status & UART_LSR_PE)
-                               *tty->flip.flag_buf_ptr = TTY_PARITY;
-                       else if (*status & UART_LSR_FE)
-                               *tty->flip.flag_buf_ptr = TTY_FRAME;
+                               flag = TTY_BREAK;
+                       } else if (lsr & UART_LSR_PE)
+                               flag = TTY_PARITY;
+                       else if (lsr & UART_LSR_FE)
+                               flag = TTY_FRAME;
                }
-               if (uart_handle_sysrq_char(&up->port, ch, regs))
+               if (uart_handle_sysrq_char(&up->port, ch))
                        goto ignore_char;
-               if ((*status & up->port.ignore_status_mask) == 0) {
-                       tty->flip.flag_buf_ptr++;
-                       tty->flip.char_buf_ptr++;
-                       tty->flip.count++;
-               }
-               if ((*status & UART_LSR_OE) &&
-                   tty->flip.count < TTY_FLIPBUF_SIZE) {
-                       /*
-                        * Overrun is special, since it's reported
-                        * immediately, and doesn't affect the current
-                        * character.
-                        */
-                       *tty->flip.flag_buf_ptr = TTY_OVERRUN;
-                       tty->flip.flag_buf_ptr++;
-                       tty->flip.char_buf_ptr++;
-                       tty->flip.count++;
-               }
+
+               uart_insert_char(&up->port, lsr, UART_LSR_OE, ch, flag);
+
        ignore_char:
-               *status = serial_inp(up, UART_LSR);
-       } while ((*status & UART_LSR_DR) && (max_count-- > 0));
+               lsr = serial_inp(up, UART_LSR);
+       } while ((lsr & UART_LSR_DR) && (max_count-- > 0));
+       spin_unlock(&up->port.lock);
        tty_flip_buffer_push(tty);
+       spin_lock(&up->port.lock);
+       *status = lsr;
 }
 
-static _INLINE_ void transmit_chars(struct uart_8250_port *up)
+static void transmit_chars(struct uart_8250_port *up)
 {
        struct circ_buf *xmit = &up->port.info->xmit;
        int count;
@@ -959,8 +1261,12 @@ static _INLINE_ void transmit_chars(struct uart_8250_port *up)
                up->port.x_char = 0;
                return;
        }
-       if (uart_circ_empty(xmit) || uart_tx_stopped(&up->port)) {
-               serial8250_stop_tx(&up->port, 0);
+       if (uart_tx_stopped(&up->port)) {
+               serial8250_stop_tx(&up->port);
+               return;
+       }
+       if (uart_circ_empty(xmit)) {
+               __stop_tx(up);
                return;
        }
 
@@ -979,45 +1285,51 @@ static _INLINE_ void transmit_chars(struct uart_8250_port *up)
        DEBUG_INTR("THRE...");
 
        if (uart_circ_empty(xmit))
-               serial8250_stop_tx(&up->port, 0);
+               __stop_tx(up);
 }
 
-static _INLINE_ void check_modem_status(struct uart_8250_port *up)
+static unsigned int check_modem_status(struct uart_8250_port *up)
 {
-       int status;
-
-       status = serial_in(up, UART_MSR);
-
-       if ((status & UART_MSR_ANY_DELTA) == 0)
-               return;
-
-       if (status & UART_MSR_TERI)
-               up->port.icount.rng++;
-       if (status & UART_MSR_DDSR)
-               up->port.icount.dsr++;
-       if (status & UART_MSR_DDCD)
-               uart_handle_dcd_change(&up->port, status & UART_MSR_DCD);
-       if (status & UART_MSR_DCTS)
-               uart_handle_cts_change(&up->port, status & UART_MSR_CTS);
+       unsigned int status = serial_in(up, UART_MSR);
+
+       if (status & UART_MSR_ANY_DELTA && up->ier & UART_IER_MSI &&
+           up->port.info != NULL) {
+               if (status & UART_MSR_TERI)
+                       up->port.icount.rng++;
+               if (status & UART_MSR_DDSR)
+                       up->port.icount.dsr++;
+               if (status & UART_MSR_DDCD)
+                       uart_handle_dcd_change(&up->port, status & UART_MSR_DCD);
+               if (status & UART_MSR_DCTS)
+                       uart_handle_cts_change(&up->port, status & UART_MSR_CTS);
+
+               wake_up_interruptible(&up->port.info->delta_msr_wait);
+       }
 
-       wake_up_interruptible(&up->port.info->delta_msr_wait);
+       return status;
 }
 
 /*
  * This handles the interrupt from one port.
  */
 static inline void
-serial8250_handle_port(struct uart_8250_port *up, struct pt_regs *regs)
+serial8250_handle_port(struct uart_8250_port *up)
 {
-       unsigned int status = serial_inp(up, UART_LSR);
+       unsigned int status;
+
+       spin_lock(&up->port.lock);
+
+       status = serial_inp(up, UART_LSR);
 
        DEBUG_INTR("status = %x...", status);
 
        if (status & UART_LSR_DR)
-               receive_chars(up, &status, regs);
+               receive_chars(up, &status);
        check_modem_status(up);
        if (status & UART_LSR_THRE)
                transmit_chars(up);
+
+       spin_unlock(&up->port.lock);
 }
 
 /*
@@ -1034,11 +1346,11 @@ serial8250_handle_port(struct uart_8250_port *up, struct pt_regs *regs)
  * This means we need to loop through all ports. checking that they
  * don't have an interrupt pending.
  */
-static irqreturn_t serial8250_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+static irqreturn_t serial8250_interrupt(int irq, void *dev_id)
 {
        struct irq_info *i = dev_id;
        struct list_head *l, *end = NULL;
-       int pass_counter = 0;
+       int pass_counter = 0, handled = 0;
 
        DEBUG_INTR("serial8250_interrupt(%d)...", irq);
 
@@ -1053,9 +1365,9 @@ static irqreturn_t serial8250_interrupt(int irq, void *dev_id, struct pt_regs *r
 
                iir = serial_in(up, UART_IIR);
                if (!(iir & UART_IIR_NO_INT)) {
-                       spin_lock(&up->port.lock);
-                       serial8250_handle_port(up, regs);
-                       spin_unlock(&up->port.lock);
+                       serial8250_handle_port(up);
+
+                       handled = 1;
 
                        end = NULL;
                } else if (end == NULL)
@@ -1074,8 +1386,8 @@ static irqreturn_t serial8250_interrupt(int irq, void *dev_id, struct pt_regs *r
        spin_unlock(&i->lock);
 
        DEBUG_INTR("end.\n");
-       /* FIXME! Was it really ours? */
-       return IRQ_HANDLED;
+
+       return IRQ_RETVAL(handled);
 }
 
 /*
@@ -1104,7 +1416,7 @@ static void serial_do_unlink(struct irq_info *i, struct uart_8250_port *up)
 static int serial_link_irq_chain(struct uart_8250_port *up)
 {
        struct irq_info *i = irq_lists + up->port.irq;
-       int ret, irq_flags = up->port.flags & UPF_SHARE_IRQ ? SA_SHIRQ : 0;
+       int ret, irq_flags = up->port.flags & UPF_SHARE_IRQ ? IRQF_SHARED : 0;
 
        spin_lock_irq(&i->lock);
 
@@ -1152,11 +1464,8 @@ static void serial8250_timeout(unsigned long data)
        unsigned int iir;
 
        iir = serial_in(up, UART_IIR);
-       if (!(iir & UART_IIR_NO_INT)) {
-               spin_lock(&up->port.lock);
-               serial8250_handle_port(up, NULL);
-               spin_unlock(&up->port.lock);
-       }
+       if (!(iir & UART_IIR_NO_INT))
+               serial8250_handle_port(up);
 
        timeout = up->port.timeout;
        timeout = timeout > 6 ? (timeout / 2 - 2) : 1;
@@ -1179,13 +1488,10 @@ static unsigned int serial8250_tx_empty(struct uart_port *port)
 static unsigned int serial8250_get_mctrl(struct uart_port *port)
 {
        struct uart_8250_port *up = (struct uart_8250_port *)port;
-       unsigned long flags;
-       unsigned char status;
+       unsigned int status;
        unsigned int ret;
 
-       spin_lock_irqsave(&up->port.lock, flags);
-       status = serial_in(up, UART_MSR);
-       spin_unlock_irqrestore(&up->port.lock, flags);
+       status = check_modem_status(up);
 
        ret = 0;
        if (status & UART_MSR_DCD)
@@ -1238,6 +1544,7 @@ static int serial8250_startup(struct uart_port *port)
 {
        struct uart_8250_port *up = (struct uart_8250_port *)port;
        unsigned long flags;
+       unsigned char lsr, iir;
        int retval;
 
        up->capabilities = uart_config[up->port.type].flags;
@@ -1266,7 +1573,7 @@ static int serial8250_startup(struct uart_port *port)
 
        /*
         * Clear the FIFO buffers and disable them.
-        * (they will be reeanbled in set_termios())
+        * (they will be reenabled in set_termios())
         */
        serial8250_clear_fifos(up);
 
@@ -1341,6 +1648,26 @@ static int serial8250_startup(struct uart_port *port)
                        up->port.mctrl |= TIOCM_OUT2;
 
        serial8250_set_mctrl(&up->port, up->port.mctrl);
+
+       /*
+        * Do a quick test to see if we receive an
+        * interrupt when we enable the TX irq.
+        */
+       serial_outp(up, UART_IER, UART_IER_THRI);
+       lsr = serial_in(up, UART_LSR);
+       iir = serial_in(up, UART_IIR);
+       serial_outp(up, UART_IER, 0);
+
+       if (lsr & UART_LSR_TEMT && iir & UART_IIR_NO_INT) {
+               if (!(up->bugs & UART_BUG_TXEN)) {
+                       up->bugs |= UART_BUG_TXEN;
+                       pr_debug("ttyS%d - enabling bad tx status workarounds\n",
+                                port->line);
+               }
+       } else {
+               up->bugs &= ~UART_BUG_TXEN;
+       }
+
        spin_unlock_irqrestore(&up->port.lock, flags);
 
        /*
@@ -1433,6 +1760,14 @@ static unsigned int serial8250_get_divisor(struct uart_port *port, unsigned int
        else if ((port->flags & UPF_MAGIC_MULTIPLIER) &&
                 baud == (port->uartclk/8))
                quot = 0x8002;
+       /*
+        * For 16C950s UART_TCR is used in combination with divisor==1
+        * to achieve baud rates up to baud_base*4.
+        */
+       else if ((port->type == PORT_16C950) &&
+                baud > (port->uartclk/16))
+               quot = 1;
+
        else
                quot = uart_get_divisor(port, baud);
 
@@ -1440,32 +1775,32 @@ static unsigned int serial8250_get_divisor(struct uart_port *port, unsigned int
 }
 
 static void
-serial8250_set_termios(struct uart_port *port, struct termios *termios,
-                      struct termios *old)
+serial8250_set_termios(struct uart_port *port, struct ktermios *termios,
+                      struct ktermios *old)
 {
        struct uart_8250_port *up = (struct uart_8250_port *)port;
        unsigned char cval, fcr = 0;
        unsigned long flags;
-       unsigned int baud, quot;
+       unsigned int baud, quot, max_baud;
 
        switch (termios->c_cflag & CSIZE) {
        case CS5:
-               cval = 0x00;
+               cval = UART_LCR_WLEN5;
                break;
        case CS6:
-               cval = 0x01;
+               cval = UART_LCR_WLEN6;
                break;
        case CS7:
-               cval = 0x02;
+               cval = UART_LCR_WLEN7;
                break;
        default:
        case CS8:
-               cval = 0x03;
+               cval = UART_LCR_WLEN8;
                break;
        }
 
        if (termios->c_cflag & CSTOPB)
-               cval |= 0x04;
+               cval |= UART_LCR_STOP;
        if (termios->c_cflag & PARENB)
                cval |= UART_LCR_PARITY;
        if (!(termios->c_cflag & PARODD))
@@ -1478,40 +1813,35 @@ serial8250_set_termios(struct uart_port *port, struct termios *termios,
        /*
         * Ask the core to calculate the divisor for us.
         */
-       baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk/16); 
+       max_baud = (up->port.type == PORT_16C950 ? port->uartclk/4 : port->uartclk/16);
+       baud = uart_get_baud_rate(port, termios, old, 0, max_baud); 
        quot = serial8250_get_divisor(port, baud);
 
        /*
-        * Work around a bug in the Oxford Semiconductor 952 rev B
-        * chip which causes it to seriously miscalculate baud rates
-        * when DLL is 0.
+        * Oxford Semi 952 rev B workaround
         */
-       if ((quot & 0xff) == 0 && up->port.type == PORT_16C950 &&
-           up->rev == 0x5201)
+       if (up->bugs & UART_BUG_QUOT && (quot & 0xff) == 0)
                quot ++;
 
        if (up->capabilities & UART_CAP_FIFO && up->port.fifosize > 1) {
                if (baud < 2400)
                        fcr = UART_FCR_ENABLE_FIFO | UART_FCR_TRIGGER_1;
-#ifdef CONFIG_SERIAL_8250_RSA
-               else if (up->port.type == PORT_RSA)
-                       fcr = UART_FCR_ENABLE_FIFO | UART_FCR_TRIGGER_14;
-#endif
                else
-                       fcr = UART_FCR_ENABLE_FIFO | UART_FCR_TRIGGER_8;
+                       fcr = uart_config[up->port.type].fcr;
        }
 
        /*
-        * TI16C750: hardware flow control and 64 byte FIFOs. When AFE is
-        * enabled, RTS will be deasserted when the receive FIFO contains
-        * more characters than the trigger, or the MCR RTS bit is cleared.
+        * MCR-based auto flow control.  When AFE is enabled, RTS will be
+        * deasserted when the receive FIFO contains more characters than
+        * the trigger, or the MCR RTS bit is cleared.  In the case where
+        * the remote UART is not using CTS auto flow control, we must
+        * have sufficient FIFO entries for the latency of the remote
+        * UART to respond.  IOW, at least 32 bytes of FIFO.
         */
-       if (up->port.type == PORT_16750) {
+       if (up->capabilities & UART_CAP_AFE && up->port.fifosize >= 32) {
                up->mcr &= ~UART_MCR_AFE;
                if (termios->c_cflag & CRTSCTS)
                        up->mcr |= UART_MCR_AFE;
-
-               fcr |= UART_FCR7_64BYTE;
        }
 
        /*
@@ -1520,6 +1850,19 @@ serial8250_set_termios(struct uart_port *port, struct termios *termios,
         */
        spin_lock_irqsave(&up->port.lock, flags);
 
+       /* 
+        * 16C950 supports additional prescaler ratios between 1:16 and 1:4
+        * thus increasing max baud rate to uartclk/4.
+        */
+       if (up->port.type == PORT_16C950) {
+               if (baud == port->uartclk/4)
+                       serial_icr_write(up, UART_TCR, 0x4);
+               else if (baud == port->uartclk/8)
+                       serial_icr_write(up, UART_TCR, 0x8);
+               else
+                       serial_icr_write(up, UART_TCR, 0);
+       }
+       
        /*
         * Update the per-port timeout.
         */
@@ -1557,19 +1900,39 @@ serial8250_set_termios(struct uart_port *port, struct termios *termios,
         * CTS flow control flag and modem status interrupts
         */
        up->ier &= ~UART_IER_MSI;
-       if (UART_ENABLE_MS(&up->port, termios->c_cflag))
+       if (!(up->bugs & UART_BUG_NOMSR) &&
+                       UART_ENABLE_MS(&up->port, termios->c_cflag))
                up->ier |= UART_IER_MSI;
-       if (up->port.type == PORT_XSCALE)
+       if (up->capabilities & UART_CAP_UUE)
                up->ier |= UART_IER_UUE | UART_IER_RTOIE;
 
        serial_out(up, UART_IER, up->ier);
 
        if (up->capabilities & UART_CAP_EFR) {
+               unsigned char efr = 0;
+               /*
+                * TI16C752/Startech hardware flow control.  FIXME:
+                * - TI16C752 requires control thresholds to be set.
+                * - UART_MCR_RTS is ineffective if auto-RTS mode is enabled.
+                */
+               if (termios->c_cflag & CRTSCTS)
+                       efr |= UART_EFR_CTS;
+
                serial_outp(up, UART_LCR, 0xBF);
-               serial_outp(up, UART_EFR,
-                           termios->c_cflag & CRTSCTS ? UART_EFR_CTS :0);
+               serial_outp(up, UART_EFR, efr);
        }
 
+#ifdef CONFIG_ARCH_OMAP15XX
+       /* Workaround to enable 115200 baud on OMAP1510 internal ports */
+       if (cpu_is_omap1510() && is_omap_port((unsigned int)up->port.membase)) {
+               if (baud == 115200) {
+                       quot = 1;
+                       serial_out(up, UART_OMAP_OSC_12M_SEL, 1);
+               } else
+                       serial_out(up, UART_OMAP_OSC_12M_SEL, 0);
+       }
+#endif
+
        if (up->capabilities & UART_NATSEMI) {
                /* Switch to bank 2 not bank 1, to avoid resetting EXCR2 */
                serial_outp(up, UART_LCR, 0xe0);
@@ -1577,8 +1940,7 @@ serial8250_set_termios(struct uart_port *port, struct termios *termios,
                serial_outp(up, UART_LCR, cval | UART_LCR_DLAB);/* set DLAB */
        }
 
-       serial_outp(up, UART_DLL, quot & 0xff);         /* LS of divisor */
-       serial_outp(up, UART_DLM, quot >> 8);           /* MS of divisor */
+       serial_dl_write(up, quot);
 
        /*
         * LCR DLAB must be set to enable 64-byte FIFO mode. If the FCR
@@ -1613,167 +1975,147 @@ serial8250_pm(struct uart_port *port, unsigned int state,
 }
 
 /*
- * Resource handling.  This is complicated by the fact that resources
- * depend on the port type.  Maybe we should be claiming the standard
- * 8250 ports, and then trying to get other resources as necessary?
+ * Resource handling.
  */
-static int
-serial8250_request_std_resource(struct uart_8250_port *up, struct resource **res)
+static int serial8250_request_std_resource(struct uart_8250_port *up)
 {
        unsigned int size = 8 << up->port.regshift;
        int ret = 0;
 
        switch (up->port.iotype) {
+       case UPIO_AU:
+               size = 0x100000;
+               /* fall thru */
+       case UPIO_TSI:
+       case UPIO_MEM32:
        case UPIO_MEM:
-               if (up->port.mapbase) {
-                       *res = request_mem_region(up->port.mapbase, size, "serial");
-                       if (!*res)
-                               ret = -EBUSY;
+               if (!up->port.mapbase)
+                       break;
+
+               if (!request_mem_region(up->port.mapbase, size, "serial")) {
+                       ret = -EBUSY;
+                       break;
+               }
+
+               if (up->port.flags & UPF_IOREMAP) {
+                       up->port.membase = ioremap(up->port.mapbase, size);
+                       if (!up->port.membase) {
+                               release_mem_region(up->port.mapbase, size);
+                               ret = -ENOMEM;
+                       }
                }
                break;
 
        case UPIO_HUB6:
        case UPIO_PORT:
-               *res = request_region(up->port.iobase, size, "serial");
-               if (!*res)
+               if (!request_region(up->port.iobase, size, "serial"))
                        ret = -EBUSY;
                break;
        }
        return ret;
 }
 
-static int
-serial8250_request_rsa_resource(struct uart_8250_port *up, struct resource **res)
+static void serial8250_release_std_resource(struct uart_8250_port *up)
 {
        unsigned int size = 8 << up->port.regshift;
-       unsigned long start;
-       int ret = 0;
 
        switch (up->port.iotype) {
+       case UPIO_AU:
+               size = 0x100000;
+               /* fall thru */
+       case UPIO_TSI:
+       case UPIO_MEM32:
        case UPIO_MEM:
-               if (up->port.mapbase) {
-                       start = up->port.mapbase;
-                       start += UART_RSA_BASE << up->port.regshift;
-                       *res = request_mem_region(start, size, "serial-rsa");
-                       if (!*res)
-                               ret = -EBUSY;
+               if (!up->port.mapbase)
+                       break;
+
+               if (up->port.flags & UPF_IOREMAP) {
+                       iounmap(up->port.membase);
+                       up->port.membase = NULL;
                }
+
+               release_mem_region(up->port.mapbase, size);
                break;
 
        case UPIO_HUB6:
        case UPIO_PORT:
-               start = up->port.iobase;
-               start += UART_RSA_BASE << up->port.regshift;
-               *res = request_region(start, size, "serial-rsa");
-               if (!*res)
-                       ret = -EBUSY;
+               release_region(up->port.iobase, size);
                break;
        }
-
-       return ret;
 }
 
-static void serial8250_release_port(struct uart_port *port)
+static int serial8250_request_rsa_resource(struct uart_8250_port *up)
 {
-       struct uart_8250_port *up = (struct uart_8250_port *)port;
-       unsigned long start, offset = 0, size = 0;
-
-       if (up->port.type == PORT_RSA) {
-               offset = UART_RSA_BASE << up->port.regshift;
-               size = 8;
-       }
-
-       size <<= up->port.regshift;
+       unsigned long start = UART_RSA_BASE << up->port.regshift;
+       unsigned int size = 8 << up->port.regshift;
+       int ret = -EINVAL;
 
        switch (up->port.iotype) {
-       case UPIO_MEM:
-               if (up->port.mapbase) {
-                       /*
-                        * Unmap the area.
-                        */
-                       iounmap(up->port.membase);
-                       up->port.membase = NULL;
+       case UPIO_HUB6:
+       case UPIO_PORT:
+               start += up->port.iobase;
+               if (request_region(start, size, "serial-rsa"))
+                       ret = 0;
+               else
+                       ret = -EBUSY;
+               break;
+       }
 
-                       start = up->port.mapbase;
+       return ret;
+}
 
-                       if (size)
-                               release_mem_region(start + offset, size);
-                       release_mem_region(start, 8 << up->port.regshift);
-               }
-               break;
+static void serial8250_release_rsa_resource(struct uart_8250_port *up)
+{
+       unsigned long offset = UART_RSA_BASE << up->port.regshift;
+       unsigned int size = 8 << up->port.regshift;
 
+       switch (up->port.iotype) {
        case UPIO_HUB6:
        case UPIO_PORT:
-               start = up->port.iobase;
-
-               if (size)
-                       release_region(start + offset, size);
-               release_region(start + offset, 8 << up->port.regshift);
-               break;
-
-       default:
+               release_region(up->port.iobase + offset, size);
                break;
        }
 }
 
+static void serial8250_release_port(struct uart_port *port)
+{
+       struct uart_8250_port *up = (struct uart_8250_port *)port;
+
+       serial8250_release_std_resource(up);
+       if (up->port.type == PORT_RSA)
+               serial8250_release_rsa_resource(up);
+}
+
 static int serial8250_request_port(struct uart_port *port)
 {
        struct uart_8250_port *up = (struct uart_8250_port *)port;
-       struct resource *res = NULL, *res_rsa = NULL;
        int ret = 0;
 
-       if (up->port.type == PORT_RSA) {
-               ret = serial8250_request_rsa_resource(up, &res_rsa);
+       ret = serial8250_request_std_resource(up);
+       if (ret == 0 && up->port.type == PORT_RSA) {
+               ret = serial8250_request_rsa_resource(up);
                if (ret < 0)
-                       return ret;
+                       serial8250_release_std_resource(up);
        }
 
-       ret = serial8250_request_std_resource(up, &res);
-
-       /*
-        * If we have a mapbase, then request that as well.
-        */
-       if (ret == 0 && up->port.flags & UPF_IOREMAP) {
-               int size = res->end - res->start + 1;
-
-               up->port.membase = ioremap(up->port.mapbase, size);
-               if (!up->port.membase)
-                       ret = -ENOMEM;
-       }
-
-       if (ret < 0) {
-               if (res_rsa)
-                       release_resource(res_rsa);
-               if (res)
-                       release_resource(res);
-       }
        return ret;
 }
 
 static void serial8250_config_port(struct uart_port *port, int flags)
 {
        struct uart_8250_port *up = (struct uart_8250_port *)port;
-       struct resource *res_std = NULL, *res_rsa = NULL;
        int probeflags = PROBE_ANY;
        int ret;
 
-#ifdef CONFIG_MCA
-       /*
-        * Don't probe for MCA ports on non-MCA machines.
-        */
-       if (up->port.flags & UPF_BOOT_ONLYMCA && !MCA_bus)
-               return;
-#endif
-
        /*
         * Find the region that we can probe for.  This in turn
         * tells us whether we can probe for the type of port.
         */
-       ret = serial8250_request_std_resource(up, &res_std);
+       ret = serial8250_request_std_resource(up);
        if (ret < 0)
                return;
 
-       ret = serial8250_request_rsa_resource(up, &res_rsa);
+       ret = serial8250_request_rsa_resource(up);
        if (ret < 0)
                probeflags &= ~PROBE_RSA;
 
@@ -1782,14 +2124,10 @@ static void serial8250_config_port(struct uart_port *port, int flags)
        if (up->port.type != PORT_UNKNOWN && flags & UART_CONFIG_IRQ)
                autoconfig_irq(up);
 
-       /*
-        * If the port wasn't an RSA port, release the resource.
-        */
-       if (up->port.type != PORT_RSA && res_rsa)
-               release_resource(res_rsa);
-
-       if (up->port.type == PORT_UNKNOWN && res_std)
-               release_resource(res_std);
+       if (up->port.type != PORT_RSA && probeflags & PROBE_RSA)
+               serial8250_release_rsa_resource(up);
+       if (up->port.type == PORT_UNKNOWN)
+               serial8250_release_std_resource(up);
 }
 
 static int
@@ -1797,7 +2135,7 @@ serial8250_verify_port(struct uart_port *port, struct serial_struct *ser)
 {
        if (ser->irq >= NR_IRQS || ser->irq < 0 ||
            ser->baud_base < 9600 || ser->type < PORT_UNKNOWN ||
-           ser->type > PORT_MAX_8250 || ser->type == PORT_CIRRUS ||
+           ser->type >= ARRAY_SIZE(uart_config) || ser->type == PORT_CIRRUS ||
            ser->type == PORT_STARTECH)
                return -EINVAL;
        return 0;
@@ -1845,7 +2183,26 @@ static void __init serial8250_isa_init_ports(void)
                return;
        first = 0;
 
-       for (i = 0, up = serial8250_ports; i < ARRAY_SIZE(old_serial_port);
+       for (i = 0; i < nr_uarts; i++) {
+               struct uart_8250_port *up = &serial8250_ports[i];
+
+               up->port.line = i;
+               spin_lock_init(&up->port.lock);
+
+               init_timer(&up->timer);
+               up->timer.function = serial8250_timeout;
+
+               /*
+                * ALPHA_KLUDGE_MCR needs to be killed.
+                */
+               up->mcr_mask = ~ALPHA_KLUDGE_MCR;
+               up->mcr_force = ALPHA_KLUDGE_MCR;
+
+               up->port.ops = &serial8250_pops;
+       }
+
+       for (i = 0, up = serial8250_ports;
+            i < ARRAY_SIZE(old_serial_port) && i < nr_uarts;
             i++, up++) {
                up->port.iobase   = old_serial_port[i].port;
                up->port.irq      = irq_canonicalize(old_serial_port[i].irq);
@@ -1855,32 +2212,22 @@ static void __init serial8250_isa_init_ports(void)
                up->port.membase  = old_serial_port[i].iomem_base;
                up->port.iotype   = old_serial_port[i].io_type;
                up->port.regshift = old_serial_port[i].iomem_reg_shift;
-               up->port.ops      = &serial8250_pops;
                if (share_irqs)
                        up->port.flags |= UPF_SHARE_IRQ;
        }
 }
 
-static void __init serial8250_register_ports(struct uart_driver *drv)
+static void __init
+serial8250_register_ports(struct uart_driver *drv, struct device *dev)
 {
        int i;
 
        serial8250_isa_init_ports();
 
-       for (i = 0; i < UART_NR; i++) {
+       for (i = 0; i < nr_uarts; i++) {
                struct uart_8250_port *up = &serial8250_ports[i];
 
-               up->port.line = i;
-               up->port.ops = &serial8250_pops;
-               init_timer(&up->timer);
-               up->timer.function = serial8250_timeout;
-
-               /*
-                * ALPHA_KLUDGE_MCR needs to be killed.
-                */
-               up->mcr_mask = ~ALPHA_KLUDGE_MCR;
-               up->mcr_force = ALPHA_KLUDGE_MCR;
-
+               up->port.dev = dev;
                uart_add_one_port(drv, &up->port);
        }
 }
@@ -1892,7 +2239,7 @@ static void __init serial8250_register_ports(struct uart_driver *drv)
 /*
  *     Wait for transmitter & holding register to empty
  */
-static inline void wait_for_xmitr(struct uart_8250_port *up)
+static inline void wait_for_xmitr(struct uart_8250_port *up, int bits)
 {
        unsigned int status, tmout = 10000;
 
@@ -1906,17 +2253,26 @@ static inline void wait_for_xmitr(struct uart_8250_port *up)
                if (--tmout == 0)
                        break;
                udelay(1);
-       } while ((status & BOTH_EMPTY) != BOTH_EMPTY);
+       } while ((status & bits) != bits);
 
        /* Wait up to 1s for flow control if necessary */
        if (up->port.flags & UPF_CONS_FLOW) {
                tmout = 1000000;
-               while (--tmout &&
-                      ((serial_in(up, UART_MSR) & UART_MSR_CTS) == 0))
+               while (!(serial_in(up, UART_MSR) & UART_MSR_CTS) && --tmout) {
                        udelay(1);
+                       touch_nmi_watchdog();
+               }
        }
 }
 
+static void serial8250_console_putchar(struct uart_port *port, int ch)
+{
+       struct uart_8250_port *up = (struct uart_8250_port *)port;
+
+       wait_for_xmitr(up, UART_LSR_THRE);
+       serial_out(up, UART_TX, ch);
+}
+
 /*
  *     Print a string to the serial port trying not to disturb
  *     any possible real use of the port...
@@ -1927,42 +2283,43 @@ static void
 serial8250_console_write(struct console *co, const char *s, unsigned int count)
 {
        struct uart_8250_port *up = &serial8250_ports[co->index];
+       unsigned long flags;
        unsigned int ier;
-       int i;
+       int locked = 1;
+
+       touch_nmi_watchdog();
+
+       local_irq_save(flags);
+       if (up->port.sysrq) {
+               /* serial8250_handle_port() already took the lock */
+               locked = 0;
+       } else if (oops_in_progress) {
+               locked = spin_trylock(&up->port.lock);
+       } else
+               spin_lock(&up->port.lock);
 
        /*
-        *      First save the UER then disable the interrupts
+        *      First save the IER then disable the interrupts
         */
        ier = serial_in(up, UART_IER);
 
-       if (up->port.type == PORT_XSCALE)
+       if (up->capabilities & UART_CAP_UUE)
                serial_out(up, UART_IER, UART_IER_UUE);
        else
                serial_out(up, UART_IER, 0);
 
-       /*
-        *      Now, do each character
-        */
-       for (i = 0; i < count; i++, s++) {
-               wait_for_xmitr(up);
-
-               /*
-                *      Send the character out.
-                *      If a LF, also do CR...
-                */
-               serial_out(up, UART_TX, *s);
-               if (*s == 10) {
-                       wait_for_xmitr(up);
-                       serial_out(up, UART_TX, 13);
-               }
-       }
+       uart_console_write(&up->port, s, count, serial8250_console_putchar);
 
        /*
         *      Finally, wait for transmitter to become empty
         *      and restore the IER
         */
-       wait_for_xmitr(up);
+       wait_for_xmitr(up, BOTH_EMPTY);
        serial_out(up, UART_IER, ier);
+
+       if (locked)
+               spin_unlock(&up->port.lock);
+       local_irq_restore(flags);
 }
 
 static int __init serial8250_console_setup(struct console *co, char *options)
@@ -1978,17 +2335,12 @@ static int __init serial8250_console_setup(struct console *co, char *options)
         * if so, search for the first available port that does have
         * console support.
         */
-       if (co->index >= UART_NR)
+       if (co->index >= nr_uarts)
                co->index = 0;
        port = &serial8250_ports[co->index].port;
-       if (!port->ops)
+       if (!port->iobase && !port->membase)
                return -ENODEV;
 
-       /*
-        * Temporary fix.
-        */
-       spin_lock_init(&port->lock);
-
        if (options)
                uart_parse_options(options, &baud, &parity, &bits, &flow);
 
@@ -2014,13 +2366,38 @@ static int __init serial8250_console_init(void)
 }
 console_initcall(serial8250_console_init);
 
-static int __init serial8250_late_console_init(void)
+static int __init find_port(struct uart_port *p)
 {
-       if (!(serial8250_console.flags & CON_ENABLED))
+       int line;
+       struct uart_port *port;
+
+       for (line = 0; line < nr_uarts; line++) {
+               port = &serial8250_ports[line].port;
+               if (uart_match_port(p, port))
+                       return line;
+       }
+       return -ENODEV;
+}
+
+int __init serial8250_start_console(struct uart_port *port, char *options)
+{
+       int line;
+
+       line = find_port(port);
+       if (line < 0)
+               return -ENODEV;
+
+       add_preferred_console("ttyS", line, options);
+       printk("Adding console on ttyS%d at %s 0x%lx (options '%s')\n",
+               line, port->iotype == UPIO_MEM ? "MMIO" : "I/O port",
+               port->iotype == UPIO_MEM ? (unsigned long) port->mapbase :
+                   (unsigned long) port->iobase, options);
+       if (!(serial8250_console.flags & CON_ENABLED)) {
+               serial8250_console.flags &= ~CON_PRINTBUFFER;
                register_console(&serial8250_console);
-       return 0;
+       }
+       return line;
 }
-late_initcall(serial8250_late_console_init);
 
 #define SERIAL8250_CONSOLE     &serial8250_console
 #else
@@ -2030,7 +2407,6 @@ late_initcall(serial8250_late_console_init);
 static struct uart_driver serial8250_reg = {
        .owner                  = THIS_MODULE,
        .driver_name            = "serial",
-       .devfs_name             = "tts/",
        .dev_name               = "ttyS",
        .major                  = TTY_MAJOR,
        .minor                  = 64,
@@ -2039,59 +2415,11 @@ static struct uart_driver serial8250_reg = {
 };
 
 /*
- * register_serial and unregister_serial allows for 16x50 serial ports to be
- * configured at run-time, to support PCMCIA modems.
- */
-
-static int __register_serial(struct serial_struct *req, int line)
-{
-       struct uart_port port;
-
-       port.iobase   = req->port;
-       port.membase  = req->iomem_base;
-       port.irq      = req->irq;
-       port.uartclk  = req->baud_base * 16;
-       port.fifosize = req->xmit_fifo_size;
-       port.regshift = req->iomem_reg_shift;
-       port.iotype   = req->io_type;
-       port.flags    = req->flags | UPF_BOOT_AUTOCONF;
-       port.mapbase  = req->iomap_base;
-       port.line     = line;
-
-       if (share_irqs)
-               port.flags |= UPF_SHARE_IRQ;
-
-       if (HIGH_BITS_OFFSET)
-               port.iobase |= (long) req->port_high << HIGH_BITS_OFFSET;
-
-       /*
-        * If a clock rate wasn't specified by the low level
-        * driver, then default to the standard clock rate.
-        */
-       if (port.uartclk == 0)
-               port.uartclk = BASE_BAUD * 16;
-
-       return uart_register_port(&serial8250_reg, &port);
-}
-
-/**
- *     register_serial - configure a 16x50 serial port at runtime
- *     @req: request structure
- *
- *     Configure the serial port specified by the request. If the
- *     port exists and is in use an error is returned. If the port
- *     is not currently in the table it is added.
+ * early_serial_setup - early registration for 8250 ports
  *
- *     The port is then probed and if necessary the IRQ is autodetected
- *     If this fails an error is returned.
- *
- *     On success the port is ready to use and the line number is returned.
+ * Setup an 8250 port structure prior to console initialisation.  Use
+ * after console initialisation will cause undefined behaviour.
  */
-int register_serial(struct serial_struct *req)
-{
-       return __register_serial(req, -1);
-}
-
 int __init early_serial_setup(struct uart_port *port)
 {
        if (port->line >= ARRAY_SIZE(serial8250_ports))
@@ -2104,79 +2432,294 @@ int __init early_serial_setup(struct uart_port *port)
 }
 
 /**
- *     unregister_serial - remove a 16x50 serial port at runtime
- *     @line: serial line number
+ *     serial8250_suspend_port - suspend one serial port
+ *     @line:  serial line number
+ *
+ *     Suspend one serial port.
+ */
+void serial8250_suspend_port(int line)
+{
+       uart_suspend_port(&serial8250_reg, &serial8250_ports[line].port);
+}
+
+/**
+ *     serial8250_resume_port - resume one serial port
+ *     @line:  serial line number
  *
- *     Remove one serial port.  This may be called from interrupt
- *     context.
+ *     Resume one serial port.
  */
-void unregister_serial(int line)
+void serial8250_resume_port(int line)
 {
-       uart_unregister_port(&serial8250_reg, line);
+       uart_resume_port(&serial8250_reg, &serial8250_ports[line].port);
 }
 
 /*
- * This is for ISAPNP only.
+ * Register a set of serial devices attached to a platform device.  The
+ * list is terminated with a zero flags entry, which means we expect
+ * all entries to have at least UPF_BOOT_AUTOCONF set.
  */
-void serial8250_get_irq_map(unsigned int *map)
+static int __devinit serial8250_probe(struct platform_device *dev)
+{
+       struct plat_serial8250_port *p = dev->dev.platform_data;
+       struct uart_port port;
+       int ret, i;
+
+       memset(&port, 0, sizeof(struct uart_port));
+
+       for (i = 0; p && p->flags != 0; p++, i++) {
+               port.iobase     = p->iobase;
+               port.membase    = p->membase;
+               port.irq        = p->irq;
+               port.uartclk    = p->uartclk;
+               port.regshift   = p->regshift;
+               port.iotype     = p->iotype;
+               port.flags      = p->flags;
+               port.mapbase    = p->mapbase;
+               port.hub6       = p->hub6;
+               port.dev        = &dev->dev;
+               if (share_irqs)
+                       port.flags |= UPF_SHARE_IRQ;
+               ret = serial8250_register_port(&port);
+               if (ret < 0) {
+                       dev_err(&dev->dev, "unable to register port at index %d "
+                               "(IO%lx MEM%lx IRQ%d): %d\n", i,
+                               p->iobase, p->mapbase, p->irq, ret);
+               }
+       }
+       return 0;
+}
+
+/*
+ * Remove serial ports registered against a platform device.
+ */
+static int __devexit serial8250_remove(struct platform_device *dev)
+{
+       int i;
+
+       for (i = 0; i < nr_uarts; i++) {
+               struct uart_8250_port *up = &serial8250_ports[i];
+
+               if (up->port.dev == &dev->dev)
+                       serial8250_unregister_port(i);
+       }
+       return 0;
+}
+
+static int serial8250_suspend(struct platform_device *dev, pm_message_t state)
 {
        int i;
 
        for (i = 0; i < UART_NR; i++) {
-               if (serial8250_ports[i].port.type != PORT_UNKNOWN &&
-                   serial8250_ports[i].port.irq < 16)
-                       *map |= 1 << serial8250_ports[i].port.irq;
+               struct uart_8250_port *up = &serial8250_ports[i];
+
+               if (up->port.type != PORT_UNKNOWN && up->port.dev == &dev->dev)
+                       uart_suspend_port(&serial8250_reg, &up->port);
        }
+
+       return 0;
+}
+
+static int serial8250_resume(struct platform_device *dev)
+{
+       int i;
+
+       for (i = 0; i < UART_NR; i++) {
+               struct uart_8250_port *up = &serial8250_ports[i];
+
+               if (up->port.type != PORT_UNKNOWN && up->port.dev == &dev->dev)
+                       uart_resume_port(&serial8250_reg, &up->port);
+       }
+
+       return 0;
+}
+
+static struct platform_driver serial8250_isa_driver = {
+       .probe          = serial8250_probe,
+       .remove         = __devexit_p(serial8250_remove),
+       .suspend        = serial8250_suspend,
+       .resume         = serial8250_resume,
+       .driver         = {
+               .name   = "serial8250",
+               .owner  = THIS_MODULE,
+       },
+};
+
+/*
+ * This "device" covers _all_ ISA 8250-compatible serial devices listed
+ * in the table in include/asm/serial.h
+ */
+static struct platform_device *serial8250_isa_devs;
+
+/*
+ * serial8250_register_port and serial8250_unregister_port allows for
+ * 16x50 serial ports to be configured at run-time, to support PCMCIA
+ * modems and PCI multiport cards.
+ */
+static DEFINE_MUTEX(serial_mutex);
+
+static struct uart_8250_port *serial8250_find_match_or_unused(struct uart_port *port)
+{
+       int i;
+
+       /*
+        * First, find a port entry which matches.
+        */
+       for (i = 0; i < nr_uarts; i++)
+               if (uart_match_port(&serial8250_ports[i].port, port))
+                       return &serial8250_ports[i];
+
+       /*
+        * We didn't find a matching entry, so look for the first
+        * free entry.  We look for one which hasn't been previously
+        * used (indicated by zero iobase).
+        */
+       for (i = 0; i < nr_uarts; i++)
+               if (serial8250_ports[i].port.type == PORT_UNKNOWN &&
+                   serial8250_ports[i].port.iobase == 0)
+                       return &serial8250_ports[i];
+
+       /*
+        * That also failed.  Last resort is to find any entry which
+        * doesn't have a real port associated with it.
+        */
+       for (i = 0; i < nr_uarts; i++)
+               if (serial8250_ports[i].port.type == PORT_UNKNOWN)
+                       return &serial8250_ports[i];
+
+       return NULL;
 }
 
 /**
- *     serial8250_suspend_port - suspend one serial port
- *     @line:  serial line number
- *      @level: the level of port suspension, as per uart_suspend_port
+ *     serial8250_register_port - register a serial port
+ *     @port: serial port template
  *
- *     Suspend one serial port.
+ *     Configure the serial port specified by the request. If the
+ *     port exists and is in use, it is hung up and unregistered
+ *     first.
+ *
+ *     The port is then probed and if necessary the IRQ is autodetected
+ *     If this fails an error is returned.
+ *
+ *     On success the port is ready to use and the line number is returned.
  */
-void serial8250_suspend_port(int line)
+int serial8250_register_port(struct uart_port *port)
 {
-       uart_suspend_port(&serial8250_reg, &serial8250_ports[line].port);
+       struct uart_8250_port *uart;
+       int ret = -ENOSPC;
+
+       if (port->uartclk == 0)
+               return -EINVAL;
+
+       mutex_lock(&serial_mutex);
+
+       uart = serial8250_find_match_or_unused(port);
+       if (uart) {
+               uart_remove_one_port(&serial8250_reg, &uart->port);
+
+               uart->port.iobase   = port->iobase;
+               uart->port.membase  = port->membase;
+               uart->port.irq      = port->irq;
+               uart->port.uartclk  = port->uartclk;
+               uart->port.fifosize = port->fifosize;
+               uart->port.regshift = port->regshift;
+               uart->port.iotype   = port->iotype;
+               uart->port.flags    = port->flags | UPF_BOOT_AUTOCONF;
+               uart->port.mapbase  = port->mapbase;
+               if (port->dev)
+                       uart->port.dev = port->dev;
+
+               ret = uart_add_one_port(&serial8250_reg, &uart->port);
+               if (ret == 0)
+                       ret = uart->port.line;
+       }
+       mutex_unlock(&serial_mutex);
+
+       return ret;
 }
+EXPORT_SYMBOL(serial8250_register_port);
 
 /**
- *     serial8250_resume_port - resume one serial port
- *     @line:  serial line number
- *      @level: the level of port resumption, as per uart_resume_port
+ *     serial8250_unregister_port - remove a 16x50 serial port at runtime
+ *     @line: serial line number
  *
- *     Resume one serial port.
+ *     Remove one serial port.  This may not be called from interrupt
+ *     context.  We hand the port back to the our control.
  */
-void serial8250_resume_port(int line)
+void serial8250_unregister_port(int line)
 {
-       uart_resume_port(&serial8250_reg, &serial8250_ports[line].port);
+       struct uart_8250_port *uart = &serial8250_ports[line];
+
+       mutex_lock(&serial_mutex);
+       uart_remove_one_port(&serial8250_reg, &uart->port);
+       if (serial8250_isa_devs) {
+               uart->port.flags &= ~UPF_BOOT_AUTOCONF;
+               uart->port.type = PORT_UNKNOWN;
+               uart->port.dev = &serial8250_isa_devs->dev;
+               uart_add_one_port(&serial8250_reg, &uart->port);
+       } else {
+               uart->port.dev = NULL;
+       }
+       mutex_unlock(&serial_mutex);
 }
+EXPORT_SYMBOL(serial8250_unregister_port);
 
 static int __init serial8250_init(void)
 {
        int ret, i;
 
+       if (nr_uarts > UART_NR)
+               nr_uarts = UART_NR;
+
        printk(KERN_INFO "Serial: 8250/16550 driver $Revision: 1.90 $ "
-               "%d ports, IRQ sharing %sabled\n", (int) UART_NR,
+               "%d ports, IRQ sharing %sabled\n", nr_uarts,
                share_irqs ? "en" : "dis");
 
        for (i = 0; i < NR_IRQS; i++)
                spin_lock_init(&irq_lists[i].lock);
 
        ret = uart_register_driver(&serial8250_reg);
-       if (ret >= 0)
-               serial8250_register_ports(&serial8250_reg);
+       if (ret)
+               goto out;
+
+       serial8250_isa_devs = platform_device_alloc("serial8250",
+                                                   PLAT8250_DEV_LEGACY);
+       if (!serial8250_isa_devs) {
+               ret = -ENOMEM;
+               goto unreg_uart_drv;
+       }
+
+       ret = platform_device_add(serial8250_isa_devs);
+       if (ret)
+               goto put_dev;
+
+       serial8250_register_ports(&serial8250_reg, &serial8250_isa_devs->dev);
+
+       ret = platform_driver_register(&serial8250_isa_driver);
+       if (ret == 0)
+               goto out;
 
+       platform_device_del(serial8250_isa_devs);
+ put_dev:
+       platform_device_put(serial8250_isa_devs);
+ unreg_uart_drv:
+       uart_unregister_driver(&serial8250_reg);
+ out:
        return ret;
 }
 
 static void __exit serial8250_exit(void)
 {
-       int i;
+       struct platform_device *isa_dev = serial8250_isa_devs;
 
-       for (i = 0; i < UART_NR; i++)
-               uart_remove_one_port(&serial8250_reg, &serial8250_ports[i].port);
+       /*
+        * This tells serial8250_unregister_port() not to re-register
+        * the ports (thereby making serial8250_isa_driver permanently
+        * in use.)
+        */
+       serial8250_isa_devs = NULL;
+
+       platform_driver_unregister(&serial8250_isa_driver);
+       platform_device_unregister(isa_dev);
 
        uart_unregister_driver(&serial8250_reg);
 }
@@ -2184,9 +2727,6 @@ static void __exit serial8250_exit(void)
 module_init(serial8250_init);
 module_exit(serial8250_exit);
 
-EXPORT_SYMBOL(register_serial);
-EXPORT_SYMBOL(unregister_serial);
-EXPORT_SYMBOL(serial8250_get_irq_map);
 EXPORT_SYMBOL(serial8250_suspend_port);
 EXPORT_SYMBOL(serial8250_resume_port);
 
@@ -2197,8 +2737,11 @@ module_param(share_irqs, uint, 0644);
 MODULE_PARM_DESC(share_irqs, "Share IRQs with other non-8250/16x50 devices"
        " (unsafe)");
 
+module_param(nr_uarts, uint, 0644);
+MODULE_PARM_DESC(nr_uarts, "Maximum number of UARTs supported. (1-" __MODULE_STRING(CONFIG_SERIAL_8250_NR_UARTS) ")");
+
 #ifdef CONFIG_SERIAL_8250_RSA
-module_param_array(probe_rsa, ulong, probe_rsa_count, 0444);
+module_param_array(probe_rsa, ulong, &probe_rsa_count, 0444);
 MODULE_PARM_DESC(probe_rsa, "Probe I/O ports for RSA");
 #endif
 MODULE_ALIAS_CHARDEV_MAJOR(TTY_MAJOR);