]> git.neil.brown.name Git - history.git/commitdiff
[PATCH] Serial driver stuff
authorRussell King <rmk@arm.linux.org.uk>
Sun, 21 Jul 2002 09:39:46 +0000 (02:39 -0700)
committerLinus Torvalds <torvalds@home.transmeta.com>
Sun, 21 Jul 2002 09:39:46 +0000 (02:39 -0700)
The serial layer is restructured to allow less code duplication (and
hence bug duplication) across various serial drivers.  Since ARM adds
six extra serial drivers, maintaining six copies of serial.c was not
my idea of fun.

Therefore, we've ended up with a core serial driver, which knows about
the interactions with the tty layer, and low-level hardware drivers,
which know all about the hardware.  The interface between the two is
described in "Documentation/serial/driver".

This patch completely removes the old serial.c driver and its associated
configuration options, as you requested at KS2002.  We keep a certain
amount of configuration compatibility with the per-architecture serial.h
file for the moment; this *will* be killed in the next round of patches.
The biggest user of this is x86, and since I don't have an x86 box to
test this stuff on, I think the changes are best kept separate.

27 files changed:
Documentation/serial/driver [new file with mode: 0644]
Makefile
drivers/Makefile
drivers/char/Config.in
drivers/char/Makefile
drivers/char/acpi_serial.c [deleted file]
drivers/char/pcmcia/Config.in
drivers/char/pcmcia/Makefile
drivers/char/pcmcia/serial_cs.c [deleted file]
drivers/char/serial.c [deleted file]
drivers/char/tty_io.c
drivers/serial/Config.help [new file with mode: 0644]
drivers/serial/Config.in [new file with mode: 0644]
drivers/serial/Makefile [new file with mode: 0644]
drivers/serial/serial_21285.c [new file with mode: 0644]
drivers/serial/serial_8250.c [new file with mode: 0644]
drivers/serial/serial_8250.h [new file with mode: 0644]
drivers/serial/serial_8250_cs.c [new file with mode: 0644]
drivers/serial/serial_8250_pci.c [new file with mode: 0644]
drivers/serial/serial_8250_pnp.c [new file with mode: 0644]
drivers/serial/serial_amba.c [new file with mode: 0644]
drivers/serial/serial_anakin.c [new file with mode: 0644]
drivers/serial/serial_clps711x.c [new file with mode: 0644]
drivers/serial/serial_core.c [new file with mode: 0644]
drivers/serial/serial_sa1100.c [new file with mode: 0644]
drivers/serial/serial_uart00.c [new file with mode: 0644]
include/linux/serial_core.h [new file with mode: 0644]

diff --git a/Documentation/serial/driver b/Documentation/serial/driver
new file mode 100644 (file)
index 0000000..7396d07
--- /dev/null
@@ -0,0 +1,300 @@
+
+                       Low Level Serial API
+                       --------------------
+
+
+   $Id: driver,v 1.9 2002/07/06 16:51:43 rmk Exp $
+
+
+This document is meant as a brief overview of some aspects of the new serial
+driver.  It is not complete, any questions you have should be directed to
+<rmk@arm.linux.org.uk>
+
+The reference implementation is contained within serial_amba.c.
+
+
+
+Low Level Serial Hardware Driver
+--------------------------------
+
+The low level serial hardware driver is responsible for supplying port
+information (defined by uart_port) and a set of control methods (defined
+by uart_ops) to the core serial driver.  The low level driver is also
+responsible for handling interrupts for the port, and providing any
+console support.
+
+
+Console Support
+---------------
+
+The serial core provides a few helper functions.  This includes identifing
+the correct port structure (via uart_get_console) and decoding command line
+arguments (uart_parse_options).
+
+
+Locking
+-------
+
+It is the responsibility of the low level hardware driver to perform the
+necessary locking using port->lock.  There are some exceptions (which
+are described in the uart_ops listing below.)
+
+There are three locks.  A per-port spinlock, a per-port tmpbuf semaphore,
+and an overall semaphore.
+
+From the core driver perspective, the port->lock locks the following
+data:
+
+       port->mctrl
+       port->icount
+       info->xmit.head (circ->head)
+       info->xmit.tail (circ->tail)
+
+The low level driver is free to use this lock to provide any additional
+locking.
+
+The core driver uses the info->tmpbuf_sem lock to prevent multi-threaded
+access to the info->tmpbuf bouncebuffer used for port writes.
+
+The port_sem semaphore is used to protect against ports being added/
+removed or reconfigured at inappropriate times.
+
+
+uart_ops
+--------
+
+The uart_ops structure is the main interface between serial_core and the
+hardware specific driver.  It contains all the methods to control the
+hardware.
+
+  tx_empty(port)
+       This function tests whether the transmitter fifo and shifter
+       for the port described by 'port' is empty.  If it is empty,
+       this function should return TIOCSER_TEMT, otherwise return 0.
+       If the port does not support this operation, then it should
+       return TIOCSER_TEMT.
+
+       Locking: none.
+       Interrupts: caller dependent.
+       This call must not sleep
+
+  set_mctrl(port, mctrl)
+       This function sets the modem control lines for port described
+       by 'port' to the state described by mctrl.  The relevant bits
+       of mctrl are:
+               - TIOCM_RTS     RTS signal.
+               - TIOCM_DTR     DTR signal.
+               - TIOCM_OUT1    OUT1 signal.
+               - TIOCM_OUT2    OUT2 signal.
+       If the appropriate bit is set, the signal should be driven
+       active.  If the bit is clear, the signal should be driven
+       inactive.
+
+       Locking: port->lock taken.
+       Interrupts: locally disabled.
+       This call must not sleep
+
+  get_mctrl(port)
+       Returns the current state of modem control inputs.  The state
+       of the outputs should not be returned, since the core keeps
+       track of their state.  The state information should include:
+               - TIOCM_DCD     state of DCD signal
+               - TIOCM_CTS     state of CTS signal
+               - TIOCM_DSR     state of DSR signal
+               - TIOCM_RI      state of RI signal
+       The bit is set if the signal is currently driven active.  If
+       the port does not support CTS, DCD or DSR, the driver should
+       indicate that the signal is permanently active.  If RI is
+       not available, the signal should not be indicated as active.
+
+       Locking: none.
+       Interrupts: caller dependent.
+       This call must not sleep
+
+  stop_tx(port,tty_stop)
+       Stop transmitting characters.  This might be due to the CTS
+       line becoming inactive or the tty layer indicating we want
+       to stop transmission.
+
+       tty_stop: 1 if this call is due to the TTY layer issuing a
+                 TTY stop to the driver (equiv to rs_stop).
+
+       Locking: none.
+       Interrupts: caller dependent.
+       This call must not sleep
+
+  start_tx(port,tty_start)
+       start transmitting characters.  (incidentally, nonempty will
+       always be nonzero, and shouldn't be used - it will be dropped).
+
+       tty_start: 1 if this call was due to the TTY layer issuing
+                  a TTY start to the driver (equiv to rs_start)
+
+       Locking: port->lock taken.
+       Interrupts: locally disabled.
+       This call must not sleep
+
+  stop_rx(port)
+       Stop receiving characters; the port is in the process of
+       being closed.
+
+       Locking: none.
+       Interrupts: caller dependent.
+       This call must not sleep
+
+  enable_ms(port)
+       Enable the modem status interrupts.
+
+       Locking: none.
+       Interrupts: caller dependent.
+
+  break_ctl(port,ctl)
+       Control the transmission of a break signal.  If ctl is
+       nonzero, the break signal should be transmitted.  The signal
+       should be terminated when another call is made with a zero
+       ctl.
+
+       Locking: none.
+       Interrupts: caller dependent.
+       This call must not sleep
+
+  startup(port)
+       Grab any interrupt resources and initialise any low level driver
+       state.  Enable the port for reception.  It should not activate
+       RTS nor DTR; this will be done via a separate call to set_mctrl.
+
+       Locking: port_sem taken.
+       Interrupts: globally disabled.
+
+  shutdown(port)
+       Disable the port, disable any break condition that may be in
+       effect, and free any interrupt resources.  It should not disable
+       RTS nor DTR; this will have already been done via a separate
+       call to set_mctrl.
+
+       Locking: port_sem taken.
+       Interrupts: caller dependent.
+
+  change_speed(port,cflag,iflag,quot)
+       Change the port parameters, including word length, parity, stop
+       bits.  Update read_status_mask and ignore_status_mask to indicate
+       the types of events we are interested in receiving.  Relevant
+       cflag bits are:
+               CSIZE   - word size
+               CSTOPB  - 2 stop bits
+               PARENB  - parity enable
+               PARODD  - odd parity (when PARENB is in force)
+               CREAD   - enable reception of characters (if not set,
+                         still receive characters from the port, but
+                         throw them away.
+               CRTSCTS - if set, enable CTS status change reporting
+               CLOCAL  - if not set, enable modem status change
+                         reporting.
+       Relevant iflag bits are:
+               INPCK   - enable frame and parity error events to be
+                         passed to the TTY layer.
+               BRKINT
+               PARMRK  - both of these enable break events to be
+                         passed to the TTY layer.
+
+               IGNPAR  - ignore parity and framing errors
+               IGNBRK  - ignore break errors,  If IGNPAR is also
+                         set, ignore overrun errors as well.
+       The interaction of the iflag bits is as follows (parity error
+       given as an example):
+       Parity error    INPCK   IGNPAR
+       None            n/a     n/a     character received
+       Yes             n/a     0       character discarded
+       Yes             0       1       character received, marked as
+                                       TTY_NORMAL
+       Yes             1       1       character received, marked as
+                                       TTY_PARITY
+
+       Locking: none.
+       Interrupts: caller dependent.
+       This call must not sleep
+
+  pm(port,state,oldstate)
+       perform any power management related activities on the specified
+       port.  state indicates the new state (defined by ACPI D0-D3),
+       oldstate indicates the previous state.  Essentially, D0 means
+       fully on, D3 means powered down.
+
+       This function should not be used to grab any resources.
+
+       Locking: none.
+       Interrupts: caller dependent.
+
+  type(port)
+       Return a pointer to a string constant describing the specified
+       port, or return NULL, in which case the string 'unknown' is
+       substituted.
+
+       Locking: none.
+       Interrupts: caller dependent.
+
+  release_port(port)
+       Release any memory and IO region resources currently in use by
+       the port.
+
+       Locking: none.
+       Interrupts: caller dependent.
+
+  request_port(port)
+       Request any memory and IO region resources required by the port.
+       If any fail, no resources should be registered when this function
+       returns, and it should return -EBUSY on failure.
+
+       Locking: none.
+       Interrupts: caller dependent.
+
+  config_port(port,type)
+       Perform any autoconfiguration steps required for the port.  `type`
+       contains a bit mask of the required configuration.  UART_CONFIG_TYPE
+       indicates that the port requires detection and identification.
+       port->type should be set to the type found, or PORT_UNKNOWN if
+       no port was detected.
+
+       UART_CONFIG_IRQ indicates autoconfiguration of the interrupt signal,
+       which should be probed using standard kernel autoprobing techniques.
+       This is not necessary on platforms where ports have interrupts
+       internally hard wired (eg, system on a chip implementations).
+
+       Locking: none.
+       Interrupts: caller dependent.
+
+  verify_port(port,serinfo)
+       Verify the new serial port information contained within serinfo is
+       suitable for this port type.
+
+       Locking: none.
+       Interrupts: caller dependent.
+
+  ioctl(port,cmd,arg)
+       Perform any port specific IOCTLs.  IOCTL commands must be defined
+       using the standard numbering system found in <asm/ioctl.h>
+
+       Locking: none.
+       Interrupts: caller dependent.
+
+
+Other notes
+-----------
+
+It is intended some day to drop the 'unused' entries from uart_port, and
+allow low level drivers to register their own individual uart_port's with
+the core.  This will allow drivers to use uart_port as a pointer to a
+structure containing both the uart_port entry with their own extensions,
+thus:
+
+       struct my_port {
+               struct uart_port        port;
+               int                     my_stuff;
+       };
+
+Todo
+----
+
+Please see the BUGS file in CVS at
+
+       http://cvs.arm.linux.org.uk/cgi/viewcvs.cgi/serial/BUGS
index 16948bb6f235173bc2b09eaf93e0f0e7ec6a4551..06d0f59c6695ac3652afaa27bd5a739b01c4f2f4 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -212,7 +212,7 @@ export MODLIB
 
 CPPFLAGS := -D__KERNEL__ -I$(HPATH)
 
-CFLAGS := $(CPPFLAGS) -Wall -Wstrict-prototypes -Wno-trigraphs -O2 \
+CFLAGS := $(CPPFLAGS) -Wall -Wstrict-prototypes -Wno-trigraphs -O2 -g \
          -fomit-frame-pointer -fno-strict-aliasing -fno-common
 AFLAGS := -D__ASSEMBLY__ $(CPPFLAGS)
 
index e858842760d39dca1f4f8b3d449ed660e1d4bbc9..3348f9765ba6521120689b5b21320849797cc7d4 100644 (file)
@@ -8,7 +8,7 @@
 obj-$(CONFIG_PCI)              += pci/
 obj-$(CONFIG_ACPI)             += acpi/
 obj-$(CONFIG_PARPORT)          += parport/
-obj-y                          += base/ char/ block/ misc/ net/ media/
+obj-y                          += base/ serial/ char/ block/ misc/ net/ media/
 obj-$(CONFIG_NUBUS)            += nubus/
 obj-$(CONFIG_ATM)              += atm/
 obj-$(CONFIG_IDE)              += ide/
index ccce94f8fd26251291ce51306f34b52d61a1215f..a2489a9daf9b5926f5400454b6265fd7dfed010c 100644 (file)
@@ -8,25 +8,6 @@ bool 'Virtual terminal' CONFIG_VT
 if [ "$CONFIG_VT" = "y" ]; then
    bool '  Support for console on virtual terminal' CONFIG_VT_CONSOLE
 fi
-tristate 'Standard/generic (8250/16550 and compatible UARTs) serial support' CONFIG_SERIAL
-if [ "$CONFIG_SERIAL" = "y" ]; then
-   bool '  Support for console on serial port' CONFIG_SERIAL_CONSOLE
-   if [ "$CONFIG_ARCH_ACORN" = "y" ]; then
-      tristate '   Atomwide serial port support' CONFIG_ATOMWIDE_SERIAL
-      tristate '   Dual serial port support' CONFIG_DUALSP_SERIAL
-   fi
-fi
-if [ "$CONFIG_ACPI" = "y" -a "$CONFIG_IA64" = "y" ]; then
-   bool '  Support for serial ports defined by ACPI tables' CONFIG_SERIAL_ACPI
-fi
-dep_mbool 'Extended dumb serial driver options' CONFIG_SERIAL_EXTENDED $CONFIG_SERIAL
-if [ "$CONFIG_SERIAL_EXTENDED" = "y" ]; then
-   bool '  Support more than 4 serial ports' CONFIG_SERIAL_MANY_PORTS
-   bool '  Support for sharing serial interrupts' CONFIG_SERIAL_SHARE_IRQ
-   bool '  Autodetect IRQ on standard ports (unsafe)' CONFIG_SERIAL_DETECT_IRQ
-   bool '  Support special multiport boards' CONFIG_SERIAL_MULTIPORT
-   bool '  Support the Bell Technologies HUB6 card' CONFIG_HUB6
-fi
 bool 'Non-standard serial port support' CONFIG_SERIAL_NONSTANDARD
 if [ "$CONFIG_SERIAL_NONSTANDARD" = "y" ]; then
    tristate '  Computone IntelliPort Plus serial support' CONFIG_COMPUTONE
@@ -85,15 +66,9 @@ fi
 if [ "$CONFIG_EXPERIMENTAL" = "y" -a "$CONFIG_ZORRO" = "y" ]; then
    tristate 'Commodore A2232 serial support (EXPERIMENTAL)' CONFIG_A2232
 fi
-if [ "$CONFIG_FOOTBRIDGE" = "y" ]; then
-   bool 'DC21285 serial port support' CONFIG_SERIAL_21285
-   if [ "$CONFIG_SERIAL_21285" = "y" ]; then
-      if [ "$CONFIG_OBSOLETE" = "y" ]; then
-         bool '  Use /dev/ttyS0 device (OBSOLETE)' CONFIG_SERIAL_21285_OLD
-      fi
-      bool '  Console on DC21285 serial port' CONFIG_SERIAL_21285_CONSOLE
-   fi
-fi
+
+source drivers/serial/Config.in
+
 bool 'Unix98 PTY support' CONFIG_UNIX98_PTYS
 if [ "$CONFIG_UNIX98_PTYS" = "y" ]; then
    int 'Maximum number of Unix98 PTYs in use (0-2048)' CONFIG_UNIX98_PTY_COUNT 256
index 07ed55ac10add6c76da0e7ef3461af80b0c7e7e9..650d9b6eb6de4f29f8ecf71c9763dd2f56cb2dde 100644 (file)
@@ -13,20 +13,18 @@ obj-y        += mem.o tty_io.o n_tty.o tty_ioctl.o raw.o pty.o misc.o random.o
 # This list comes from 'grep -l EXPORT_SYMBOL *.[hc]'.
 
 export-objs     :=     busmouse.o console.o keyboard.o sysrq.o \
-                       misc.o pty.o random.o selection.o serial.o \
+                       misc.o pty.o random.o selection.o \
                        sonypi.o tty_io.o tty_ioctl.o generic_serial.o rtc.o \
                        ip2main.o
 
 KEYMAP   =defkeymap.o
 KEYBD    =pc_keyb.o
 CONSOLE  =console.o
-SERIAL   =serial.o
 
 ifeq ($(ARCH),s390)
   KEYMAP   =
   KEYBD    =
   CONSOLE  =
-  SERIAL   =
 endif
 
 ifeq ($(ARCH),mips)
@@ -39,7 +37,6 @@ ifeq ($(ARCH),s390x)
   KEYMAP   =
   KEYBD    =
   CONSOLE  =
-  SERIAL   =
 endif
 
 ifeq ($(ARCH),m68k)
@@ -48,7 +45,6 @@ ifeq ($(ARCH),m68k)
    else
       KEYBD =
    endif
-   SERIAL   =
 endif
 
 ifeq ($(ARCH),arm)
@@ -96,15 +92,6 @@ endif
 
 ifeq ($(CONFIG_BAGET_MIPS),y)
   KEYBD    =
-  SERIAL   =
-endif
-
-ifeq ($(CONFIG_NINO),y)
-  SERIAL   =
-endif
-
-ifneq ($(CONFIG_SUN_SERIAL),)
-  SERIAL   =
 endif
 
 ifeq ($(CONFIG_QTRONIX_KEYBOARD),y)
@@ -113,11 +100,7 @@ ifeq ($(CONFIG_QTRONIX_KEYBOARD),y)
 endif
 
 obj-$(CONFIG_VT) += vt.o vc_screen.o consolemap.o consolemap_deftbl.o $(CONSOLE) selection.o
-obj-$(CONFIG_SERIAL) += $(SERIAL)
-obj-$(CONFIG_SERIAL_ACPI) += acpi_serial.o
-obj-$(CONFIG_SERIAL_21285) += serial_21285.o
-obj-$(CONFIG_SERIAL_SA1100) += serial_sa1100.o
-obj-$(CONFIG_SERIAL_AMBA) += serial_amba.o
+#obj-$(CONFIG_SERIAL) += $(SERIAL) # Fix for decserial.o
 
 ifndef CONFIG_SUN_KEYBOARD
   obj-$(CONFIG_VT) += keyboard.o $(KEYMAP) $(KEYBD)
diff --git a/drivers/char/acpi_serial.c b/drivers/char/acpi_serial.c
deleted file mode 100644 (file)
index f0c7f18..0000000
+++ /dev/null
@@ -1,203 +0,0 @@
-/*
- *  linux/drivers/char/acpi_serial.c
- *
- *  Copyright (C) 2000  Hewlett-Packard Co.
- *  Copyright (C) 2000  Khalid Aziz <khalid_aziz@hp.com>
- *
- *  Detect and initialize the headless console serial port defined in 
- *  SPCR table and debug serial port defined in DBGP table
- *
- */
-#include <linux/kernel.h>
-#include <linux/types.h>
-#include <linux/pci.h>
-#include <linux/pm.h>
-#include <linux/acpi.h>
-#include <linux/init.h>
-#include <linux/serial.h>
-#include <asm/serial.h>
-#include <asm/io.h>
-#include <linux/acpi_serial.h>
-/*#include <asm/acpi-ext.h>*/
-
-#undef SERIAL_DEBUG_ACPI
-
-/*
- * Query ACPI tables for a debug and a headless console serial
- * port. If found, add them to rs_table[]. A pointer to either SPCR
- * or DBGP table is passed as parameter. This function should be called 
- * before serial_console_init() is called to make sure the SPCR serial 
- * console will be available for use. IA-64 kernel calls this function
- * from within acpi.c when it encounters SPCR or DBGP tables as it parses 
- * the ACPI 2.0 tables during bootup.
- *
- */
-void __init setup_serial_acpi(void *tablep) 
-{
-       acpi_ser_t *acpi_ser_p;
-       struct serial_struct serial_req;
-       unsigned long iobase;
-       int global_sys_irq;
-
-#ifdef SERIAL_DEBUG_ACPI
-       printk("Entering setup_serial_acpi()\n");
-#endif
-
-       /* Now get the table */
-       if (tablep == NULL) {
-               return;
-       }
-
-       acpi_ser_p = (acpi_ser_t *)tablep;
-
-       /*
-        * Perform a sanity check on the table. Table should have a 
-        * signature of "SPCR" or "DBGP" and it should be atleast 52 bytes
-        * long.
-        */
-       if ((strncmp(acpi_ser_p->signature, ACPI_SPCRT_SIGNATURE, 
-                                       ACPI_SIG_LEN) != 0) && 
-               (strncmp(acpi_ser_p->signature, ACPI_DBGPT_SIGNATURE, 
-                                       ACPI_SIG_LEN) != 0)) {
-               return;
-       }
-       if (acpi_ser_p->length < 52) {
-               return;
-       }
-
-       iobase = (((u64) acpi_ser_p->base_addr.addrh) << 32) | acpi_ser_p->base_addr.addrl;
-       global_sys_irq = (acpi_ser_p->global_int[3] << 24) | 
-                       (acpi_ser_p->global_int[2] << 16) |
-                       (acpi_ser_p->global_int[1] << 8) |
-                       acpi_ser_p->global_int[0];
-
-#ifdef SERIAL_DEBUG_ACPI
-       printk("setup_serial_acpi(): table pointer = 0x%p\n", acpi_ser_p);
-       printk("                     sig = '%c%c%c%c'\n",
-                       acpi_ser_p->signature[0],
-                       acpi_ser_p->signature[1],
-                       acpi_ser_p->signature[2],
-                       acpi_ser_p->signature[3]);
-       printk("                     length = %d\n", acpi_ser_p->length);
-       printk("                     Rev = %d\n", acpi_ser_p->rev);
-       printk("                     Interface type = %d\n", acpi_ser_p->intfc_type);
-       printk("                     Base address = 0x%lX\n", iobase);
-       printk("                     IRQ = %d\n", acpi_ser_p->irq);
-       printk("                     Global System Int = %d\n", global_sys_irq);
-       printk("                     Baud rate = ");
-       switch (acpi_ser_p->baud) {
-               case ACPI_SERIAL_BAUD_9600:
-                       printk("9600\n");
-                       break;
-
-               case ACPI_SERIAL_BAUD_19200:
-                       printk("19200\n");
-                       break;
-
-               case ACPI_SERIAL_BAUD_57600:
-                       printk("57600\n");
-                       break;
-
-               case ACPI_SERIAL_BAUD_115200:
-                       printk("115200\n");
-                       break;
-
-               default:
-                       printk("Huh (%d)\n", acpi_ser_p->baud);
-                       break;
-
-       }
-       if (acpi_ser_p->base_addr.space_id == ACPI_SERIAL_PCICONF_SPACE) {
-               printk("                     PCI serial port:\n");
-               printk("                         Bus %d, Device %d, Vendor ID 0x%x, Dev ID 0x%x\n",
-               acpi_ser_p->pci_bus, acpi_ser_p->pci_dev,
-               acpi_ser_p->pci_vendor_id, acpi_ser_p->pci_dev_id);
-       }
-#endif
-
-       /* 
-        * Now build a serial_req structure to update the entry in
-        * rs_table for the headless console port.
-        */
-       switch (acpi_ser_p->intfc_type) {
-               case ACPI_SERIAL_INTFC_16550:
-                       serial_req.type = PORT_16550;
-                       serial_req.baud_base = BASE_BAUD;
-                       break;
-
-               case ACPI_SERIAL_INTFC_16450:
-                       serial_req.type = PORT_16450;
-                       serial_req.baud_base = BASE_BAUD;
-                       break;
-
-               default:
-                       serial_req.type = PORT_UNKNOWN;
-                       break;
-       }
-       if (strncmp(acpi_ser_p->signature, ACPI_SPCRT_SIGNATURE,
-                                       ACPI_SIG_LEN) == 0) {
-               serial_req.line = ACPI_SERIAL_CONSOLE_PORT;
-       }
-       else if (strncmp(acpi_ser_p->signature, ACPI_DBGPT_SIGNATURE, 
-                                       ACPI_SIG_LEN) == 0) {
-               serial_req.line = ACPI_SERIAL_DEBUG_PORT;
-       }
-       /*
-        * Check if this is an I/O mapped address or a memory mapped address
-        */
-       if (acpi_ser_p->base_addr.space_id == ACPI_SERIAL_MEM_SPACE) {
-               serial_req.port = 0;
-               serial_req.port_high = 0;
-               serial_req.iomem_base = (void *)ioremap(iobase, 64);
-               serial_req.io_type = SERIAL_IO_MEM;
-       }
-       else if (acpi_ser_p->base_addr.space_id == ACPI_SERIAL_IO_SPACE) {
-               serial_req.port = (unsigned long) iobase & 0xffffffff;
-               serial_req.port_high = (unsigned long)(((u64)iobase) >> 32);
-               serial_req.iomem_base = NULL;
-               serial_req.io_type = SERIAL_IO_PORT;
-       }
-       else if (acpi_ser_p->base_addr.space_id == ACPI_SERIAL_PCICONF_SPACE) {
-               printk("WARNING: No support for PCI serial console\n");
-               return;
-       }
-
-       /*
-        * If the table does not have IRQ information, use 0 for IRQ. 
-        * This will force rs_init() to probe for IRQ. 
-        */
-       if (acpi_ser_p->length < 53) {
-               serial_req.irq = 0;
-       }
-       else {
-               serial_req.flags = ASYNC_SKIP_TEST | ASYNC_BOOT_AUTOCONF | 
-                                       ASYNC_AUTO_IRQ;
-               if (acpi_ser_p->int_type & 
-                       (ACPI_SERIAL_INT_APIC | ACPI_SERIAL_INT_SAPIC)) {
-                       serial_req.irq = global_sys_irq;
-               }
-               else if (acpi_ser_p->int_type & ACPI_SERIAL_INT_PCAT) {
-                       serial_req.irq = acpi_ser_p->irq;
-               }
-               else {
-                       /*
-                        * IRQ type not being set would mean UART will
-                        * run in polling mode. Do not probe for IRQ in
-                        * that case.
-                        */
-                       serial_req.flags = ASYNC_SKIP_TEST|ASYNC_BOOT_AUTOCONF;
-               }
-       }
-
-       serial_req.xmit_fifo_size = serial_req.custom_divisor = 0;
-       serial_req.close_delay = serial_req.hub6 = serial_req.closing_wait = 0;
-       serial_req.iomem_reg_shift = 0;
-       if (early_serial_setup(&serial_req) < 0) {
-               printk("early_serial_setup() for ACPI serial console port failed\n");
-               return;
-       }
-
-#ifdef SERIAL_DEBUG_ACPI
-       printk("Leaving setup_serial_acpi()\n");
-#endif
-}
index 5905bd307d978e5f71340fc1ddc285aa626ab333..e44fa2a25ec8ffcc65052dc188c945bc2471c9fa 100644 (file)
@@ -5,7 +5,6 @@
 mainmenu_option next_comment
 comment 'PCMCIA character devices'
 
-dep_tristate 'PCMCIA serial device support' CONFIG_PCMCIA_SERIAL_CS $CONFIG_PCMCIA $CONFIG_SERIAL
 dep_tristate 'SyncLink PC Card support' CONFIG_SYNCLINK_CS $CONFIG_PCMCIA
 
 endmenu
index 0b499b55814b03573f8f8ed42fa434370717b015..a1db3f7061211000c68de201292a3e560c5f8c1c 100644 (file)
@@ -4,7 +4,6 @@
 # Makefile for the Linux PCMCIA char device drivers.
 #
 
-obj-$(CONFIG_PCMCIA_SERIAL_CS) += serial_cs.o
 obj-$(CONFIG_SYNCLINK_CS) += synclink_cs.o
 
 include $(TOPDIR)/Rules.make
diff --git a/drivers/char/pcmcia/serial_cs.c b/drivers/char/pcmcia/serial_cs.c
deleted file mode 100644 (file)
index 892f2c8..0000000
+++ /dev/null
@@ -1,667 +0,0 @@
-/*======================================================================
-
-    A driver for PCMCIA serial devices
-
-    serial_cs.c 1.123 2000/08/24 18:46:38
-
-    The contents of this file are subject to the Mozilla Public
-    License Version 1.1 (the "License"); you may not use this file
-    except in compliance with the License. You may obtain a copy of
-    the License at http://www.mozilla.org/MPL/
-
-    Software distributed under the License is distributed on an "AS
-    IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
-    implied. See the License for the specific language governing
-    rights and limitations under the License.
-
-    The initial developer of the original code is David A. Hinds
-    <dahinds@users.sourceforge.net>.  Portions created by David A. Hinds
-    are Copyright (C) 1999 David A. Hinds.  All Rights Reserved.
-
-    Alternatively, the contents of this file may be used under the
-    terms of the GNU General Public License version 2 (the "GPL"), in which
-    case the provisions of the GPL are applicable instead of the
-    above.  If you wish to allow the use of your version of this file
-    only under the terms of the GPL and not to allow others to use
-    your version of this file under the MPL, indicate your decision
-    by deleting the provisions above and replace them with the notice
-    and other provisions required by the GPL.  If you do not delete
-    the provisions above, a recipient may use your version of this
-    file under either the MPL or the GPL.
-    
-======================================================================*/
-
-#include <linux/module.h>
-#include <linux/kernel.h>
-#include <linux/init.h>
-#include <linux/sched.h>
-#include <linux/ptrace.h>
-#include <linux/slab.h>
-#include <linux/string.h>
-#include <linux/timer.h>
-#include <linux/tty.h>
-#include <linux/serial.h>
-#include <linux/major.h>
-#include <asm/io.h>
-#include <asm/system.h>
-
-#include <pcmcia/version.h>
-#include <pcmcia/cs_types.h>
-#include <pcmcia/cs.h>
-#include <pcmcia/cistpl.h>
-#include <pcmcia/ciscode.h>
-#include <pcmcia/ds.h>
-#include <pcmcia/cisreg.h>
-
-#ifdef PCMCIA_DEBUG
-static int pc_debug = PCMCIA_DEBUG;
-MODULE_PARM(pc_debug, "i");
-#define DEBUG(n, args...) if (pc_debug>(n)) printk(KERN_DEBUG args)
-static char *version =
-"serial_cs.c 1.123 2000/08/24 18:46:38 (David Hinds)";
-#else
-#define DEBUG(n, args...)
-#endif
-
-/*====================================================================*/
-
-/* Parameters that can be set with 'insmod' */
-
-/* Bit map of interrupts to choose from */
-static u_int irq_mask = 0xdeb8;
-static int irq_list[4] = { -1 };
-
-/* Enable the speaker? */
-static int do_sound = 1;
-
-MODULE_PARM(irq_mask, "i");
-MODULE_PARM(irq_list, "1-4i");
-MODULE_PARM(do_sound, "i");
-
-/*====================================================================*/
-
-/* Table of multi-port card ID's */
-
-typedef struct {
-    u_short    manfid;
-    u_short    prodid;
-    int                multi;          /* 1 = multifunction, > 1 = # ports */
-} multi_id_t;
-
-static multi_id_t multi_id[] = {
-    { MANFID_OMEGA, PRODID_OMEGA_QSP_100, 4 },
-    { MANFID_QUATECH, PRODID_QUATECH_DUAL_RS232, 2 },
-    { MANFID_QUATECH, PRODID_QUATECH_DUAL_RS232_D1, 2 },
-    { MANFID_QUATECH, PRODID_QUATECH_QUAD_RS232, 4 },
-    { MANFID_SOCKET, PRODID_SOCKET_DUAL_RS232, 2 },
-    { MANFID_INTEL, PRODID_INTEL_DUAL_RS232, 2 },
-    { MANFID_NATINST, PRODID_NATINST_QUAD_RS232, 4 }
-};
-#define MULTI_COUNT (sizeof(multi_id)/sizeof(multi_id_t))
-
-typedef struct serial_info_t {
-    dev_link_t link;
-    int                ndev;
-    int                multi;
-    int                slave;
-    int                manfid;
-    dev_node_t node[4];
-    int                line[4];
-} serial_info_t;
-
-static void serial_config(dev_link_t *link);
-static void serial_release(u_long arg);
-static int serial_event(event_t event, int priority,
-                       event_callback_args_t *args);
-
-static dev_info_t dev_info = "serial_cs";
-
-static dev_link_t *serial_attach(void);
-static void serial_detach(dev_link_t *);
-
-static dev_link_t *dev_list = NULL;
-
-/*====================================================================*/
-
-static void cs_error(client_handle_t handle, int func, int ret)
-{
-    error_info_t err = { func, ret };
-    CardServices(ReportError, handle, &err);
-}
-
-/*======================================================================
-
-    serial_attach() creates an "instance" of the driver, allocating
-    local data structures for one device.  The device is registered
-    with Card Services.
-
-======================================================================*/
-
-static dev_link_t *serial_attach(void)
-{
-    serial_info_t *info;
-    client_reg_t client_reg;
-    dev_link_t *link;
-    int i, ret;
-    
-    DEBUG(0, "serial_attach()\n");
-
-    /* Create new serial device */
-    info = kmalloc(sizeof(*info), GFP_KERNEL);
-    if (!info) return NULL;
-    memset(info, 0, sizeof(*info));
-    link = &info->link; link->priv = info;
-
-    link->release.function = &serial_release;
-    link->release.data = (u_long)link;
-    link->io.Attributes1 = IO_DATA_PATH_WIDTH_8;
-    link->io.NumPorts1 = 8;
-    link->irq.Attributes = IRQ_TYPE_EXCLUSIVE;
-    link->irq.IRQInfo1 = IRQ_INFO2_VALID|IRQ_LEVEL_ID;
-    if (irq_list[0] == -1)
-       link->irq.IRQInfo2 = irq_mask;
-    else
-       for (i = 0; i < 4; i++)
-           link->irq.IRQInfo2 |= 1 << irq_list[i];
-    link->conf.Attributes = CONF_ENABLE_IRQ;
-    link->conf.Vcc = 50;
-    if (do_sound) {
-       link->conf.Attributes |= CONF_ENABLE_SPKR;
-       link->conf.Status = CCSR_AUDIO_ENA;
-    }
-    link->conf.IntType = INT_MEMORY_AND_IO;
-    
-    /* Register with Card Services */
-    link->next = dev_list;
-    dev_list = link;
-    client_reg.dev_info = &dev_info;
-    client_reg.Attributes = INFO_IO_CLIENT | INFO_CARD_SHARE;
-    client_reg.EventMask =
-       CS_EVENT_CARD_INSERTION | CS_EVENT_CARD_REMOVAL |
-       CS_EVENT_RESET_PHYSICAL | CS_EVENT_CARD_RESET |
-       CS_EVENT_PM_SUSPEND | CS_EVENT_PM_RESUME;
-    client_reg.event_handler = &serial_event;
-    client_reg.Version = 0x0210;
-    client_reg.event_callback_args.client_data = link;
-    ret = CardServices(RegisterClient, &link->handle, &client_reg);
-    if (ret != CS_SUCCESS) {
-       cs_error(link->handle, RegisterClient, ret);
-       serial_detach(link);
-       return NULL;
-    }
-    
-    return link;
-} /* serial_attach */
-
-/*======================================================================
-
-    This deletes a driver "instance".  The device is de-registered
-    with Card Services.  If it has been released, all local data
-    structures are freed.  Otherwise, the structures will be freed
-    when the device is released.
-
-======================================================================*/
-
-static void serial_detach(dev_link_t *link)
-{
-    serial_info_t *info = link->priv;
-    dev_link_t **linkp;
-    int ret;
-
-    DEBUG(0, "serial_detach(0x%p)\n", link);
-    
-    /* Locate device structure */
-    for (linkp = &dev_list; *linkp; linkp = &(*linkp)->next)
-       if (*linkp == link) break;
-    if (*linkp == NULL)
-       return;
-
-    del_timer(&link->release);
-    if (link->state & DEV_CONFIG)
-       serial_release((u_long)link);
-    
-    if (link->handle) {
-       ret = CardServices(DeregisterClient, link->handle);
-       if (ret != CS_SUCCESS)
-           cs_error(link->handle, DeregisterClient, ret);
-    }
-    
-    /* Unlink device structure, free bits */
-    *linkp = link->next;
-    kfree(info);
-    
-} /* serial_detach */
-
-/*====================================================================*/
-
-static int setup_serial(serial_info_t *info, ioaddr_t port, int irq)
-{
-    struct serial_struct serial;
-    int line;
-    
-    memset(&serial, 0, sizeof(serial));
-    serial.port = port;
-    serial.irq = irq;
-    serial.flags = ASYNC_SKIP_TEST | ASYNC_SHARE_IRQ;
-    line = register_serial(&serial);
-    if (line < 0) {
-       printk(KERN_NOTICE "serial_cs: register_serial() at 0x%04lx,"
-              " irq %d failed\n", (u_long)serial.port, serial.irq);
-       return -1;
-    }
-    
-    info->line[info->ndev] = line;
-    sprintf(info->node[info->ndev].dev_name, "ttyS%d", line);
-    info->node[info->ndev].major = TTY_MAJOR;
-    info->node[info->ndev].minor = 0x40+line;
-    if (info->ndev > 0)
-       info->node[info->ndev-1].next = &info->node[info->ndev];
-    info->ndev++;
-    
-    return 0;
-}
-
-/*====================================================================*/
-
-static int get_tuple(int fn, client_handle_t handle, tuple_t *tuple,
-                    cisparse_t *parse)
-{
-    int i;
-    i = CardServices(fn, handle, tuple);
-    if (i != CS_SUCCESS) return CS_NO_MORE_ITEMS;
-    i = CardServices(GetTupleData, handle, tuple);
-    if (i != CS_SUCCESS) return i;
-    return CardServices(ParseTuple, handle, tuple, parse);
-}
-
-#define first_tuple(a, b, c) get_tuple(GetFirstTuple, a, b, c)
-#define next_tuple(a, b, c) get_tuple(GetNextTuple, a, b, c)
-
-/*====================================================================*/
-
-static int simple_config(dev_link_t *link)
-{
-    static ioaddr_t base[5] = { 0x3f8, 0x2f8, 0x3e8, 0x2e8, 0x0 };
-    client_handle_t handle = link->handle;
-    serial_info_t *info = link->priv;
-    tuple_t tuple;
-    u_char buf[256];
-    cisparse_t parse;
-    cistpl_cftable_entry_t *cf = &parse.cftable_entry;
-    config_info_t config;
-    int i, j, try;
-
-    /* If the card is already configured, look up the port and irq */
-    i = CardServices(GetConfigurationInfo, handle, &config);
-    if ((i == CS_SUCCESS) &&
-       (config.Attributes & CONF_VALID_CLIENT)) {
-       ioaddr_t port = 0;
-       if ((config.BasePort2 != 0) && (config.NumPorts2 == 8)) {
-           port = config.BasePort2;
-           info->slave = 1;
-       } else if ((info->manfid == MANFID_OSITECH) &&
-                  (config.NumPorts1 == 0x40)) {
-           port = config.BasePort1 + 0x28;
-           info->slave = 1;
-       }
-       if (info->slave)
-           return setup_serial(info, port, config.AssignedIRQ);
-    }
-    link->conf.Vcc = config.Vcc;
-    
-    /* First pass: look for a config entry that looks normal. */
-    tuple.TupleData = (cisdata_t *)buf;
-    tuple.TupleOffset = 0; tuple.TupleDataMax = 255;
-    tuple.Attributes = 0;
-    tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY;
-    /* Two tries: without IO aliases, then with aliases */
-    for (try = 0; try < 2; try++) {
-       i = first_tuple(handle, &tuple, &parse);
-       while (i != CS_NO_MORE_ITEMS) {
-           if (i != CS_SUCCESS) goto next_entry;
-           if (cf->vpp1.present & (1<<CISTPL_POWER_VNOM))
-               link->conf.Vpp1 = link->conf.Vpp2 =
-                   cf->vpp1.param[CISTPL_POWER_VNOM]/10000;
-           if ((cf->io.nwin > 0) && (cf->io.win[0].len == 8) &&
-               (cf->io.win[0].base != 0)) {
-               link->conf.ConfigIndex = cf->index;
-               link->io.BasePort1 = cf->io.win[0].base;
-               link->io.IOAddrLines = (try == 0) ?
-                   16 : cf->io.flags & CISTPL_IO_LINES_MASK;
-               i = CardServices(RequestIO, link->handle, &link->io);
-               if (i == CS_SUCCESS) goto found_port;
-           }
-       next_entry:
-           i = next_tuple(handle, &tuple, &parse);
-       }
-    }
-    
-    /* Second pass: try to find an entry that isn't picky about
-       its base address, then try to grab any standard serial port
-       address, and finally try to get any free port. */
-    i = first_tuple(handle, &tuple, &parse);
-    while (i != CS_NO_MORE_ITEMS) {
-       if ((i == CS_SUCCESS) && (cf->io.nwin > 0) &&
-           ((cf->io.flags & CISTPL_IO_LINES_MASK) <= 3)) {
-           link->conf.ConfigIndex = cf->index;
-           for (j = 0; j < 5; j++) {
-               link->io.BasePort1 = base[j];
-               link->io.IOAddrLines = base[j] ? 16 : 3;
-               i = CardServices(RequestIO, link->handle,
-                                &link->io);
-               if (i == CS_SUCCESS) goto found_port;
-           }
-       }
-       i = next_tuple(handle, &tuple, &parse);
-    }
-
-found_port:
-    if (i != CS_SUCCESS) {
-       printk(KERN_NOTICE "serial_cs: no usable port range found, giving up\n");
-       cs_error(link->handle, RequestIO, i);
-       return -1;
-    }
-    
-    i = CardServices(RequestIRQ, link->handle, &link->irq);
-    if (i != CS_SUCCESS) {
-       cs_error(link->handle, RequestIRQ, i);
-       link->irq.AssignedIRQ = 0;
-    }
-    if (info->multi && (info->manfid == MANFID_3COM))
-       link->conf.ConfigIndex &= ~(0x08);
-    i = CardServices(RequestConfiguration, link->handle, &link->conf);
-    if (i != CS_SUCCESS) {
-       cs_error(link->handle, RequestConfiguration, i);
-       return -1;
-    }
-
-    return setup_serial(info, link->io.BasePort1, link->irq.AssignedIRQ);
-}
-
-static int multi_config(dev_link_t *link)
-{
-    client_handle_t handle = link->handle;
-    serial_info_t *info = link->priv;
-    tuple_t tuple;
-    u_char buf[256];
-    cisparse_t parse;
-    cistpl_cftable_entry_t *cf = &parse.cftable_entry;
-    int i, base2 = 0;
-
-    tuple.TupleData = (cisdata_t *)buf;
-    tuple.TupleOffset = 0; tuple.TupleDataMax = 255;
-    tuple.Attributes = 0;
-    tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY;
-
-    /* First, look for a generic full-sized window */
-    link->io.NumPorts1 = info->multi * 8;
-    i = first_tuple(handle, &tuple, &parse);
-    while (i != CS_NO_MORE_ITEMS) {
-       /* The quad port cards have bad CIS's, so just look for a
-          window larger than 8 ports and assume it will be right */
-       if ((i == CS_SUCCESS) && (cf->io.nwin == 1) &&
-           (cf->io.win[0].len > 8)) {
-           link->conf.ConfigIndex = cf->index;
-           link->io.BasePort1 = cf->io.win[0].base;
-           link->io.IOAddrLines = cf->io.flags & CISTPL_IO_LINES_MASK;
-           i = CardServices(RequestIO, link->handle, &link->io);
-           base2 = link->io.BasePort1 + 8;
-           if (i == CS_SUCCESS) break;
-       }
-       i = next_tuple(handle, &tuple, &parse);
-    }
-
-    /* If that didn't work, look for two windows */
-    if (i != CS_SUCCESS) {
-       link->io.NumPorts1 = link->io.NumPorts2 = 8;
-       info->multi = 2;
-       i = first_tuple(handle, &tuple, &parse);
-       while (i != CS_NO_MORE_ITEMS) {
-           if ((i == CS_SUCCESS) && (cf->io.nwin == 2)) {
-               link->conf.ConfigIndex = cf->index;
-               link->io.BasePort1 = cf->io.win[0].base;
-               link->io.BasePort2 = cf->io.win[1].base;
-               link->io.IOAddrLines = cf->io.flags & CISTPL_IO_LINES_MASK;
-               i = CardServices(RequestIO, link->handle, &link->io);
-               base2 = link->io.BasePort2;
-               if (i == CS_SUCCESS) break;
-           }
-           i = next_tuple(handle, &tuple, &parse);
-       }
-    }
-    
-    if (i != CS_SUCCESS) {
-       cs_error(link->handle, RequestIO, i);
-       return -1;
-    }
-    
-    i = CardServices(RequestIRQ, link->handle, &link->irq);
-    if (i != CS_SUCCESS) {
-       printk(KERN_NOTICE "serial_cs: no usable port range found, giving up\n");
-       cs_error(link->handle, RequestIRQ, i);
-       link->irq.AssignedIRQ = 0;
-    }
-    /* Socket Dual IO: this enables irq's for second port */
-    if (info->multi && (info->manfid == MANFID_SOCKET)) {
-       link->conf.Present |= PRESENT_EXT_STATUS;
-       link->conf.ExtStatus = ESR_REQ_ATTN_ENA;
-    }
-    i = CardServices(RequestConfiguration, link->handle, &link->conf);
-    if (i != CS_SUCCESS) {
-       cs_error(link->handle, RequestConfiguration, i);
-       return -1;
-    }
-    
-    setup_serial(info, link->io.BasePort1, link->irq.AssignedIRQ);
-    /* The Nokia cards are not really multiport cards */
-    if (info->manfid == MANFID_NOKIA)
-       return 0;
-    for (i = 0; i < info->multi-1; i++)
-       setup_serial(info, base2+(8*i), link->irq.AssignedIRQ);
-    
-    return 0;
-}
-
-/*======================================================================
-
-    serial_config() is scheduled to run after a CARD_INSERTION event
-    is received, to configure the PCMCIA socket, and to make the
-    serial device available to the system.
-
-======================================================================*/
-
-#define CS_CHECK(fn, args...) \
-while ((last_ret=CardServices(last_fn=(fn), args))!=0) goto cs_failed
-
-void serial_config(dev_link_t *link)
-{
-    client_handle_t handle = link->handle;
-    serial_info_t *info = link->priv;
-    tuple_t tuple;
-    u_short buf[128];
-    cisparse_t parse;
-    cistpl_cftable_entry_t *cf = &parse.cftable_entry;
-    int i, last_ret, last_fn;
-
-    DEBUG(0, "serial_config(0x%p)\n", link);
-    
-    tuple.TupleData = (cisdata_t *)buf;
-    tuple.TupleOffset = 0; tuple.TupleDataMax = 255;
-    tuple.Attributes = 0;
-    /* Get configuration register information */
-    tuple.DesiredTuple = CISTPL_CONFIG;
-    last_ret = first_tuple(handle, &tuple, &parse);
-    if (last_ret != CS_SUCCESS) {
-       last_fn = ParseTuple;
-       goto cs_failed;
-    }
-    link->conf.ConfigBase = parse.config.base;
-    link->conf.Present = parse.config.rmask[0];
-    
-    /* Configure card */
-    link->state |= DEV_CONFIG;
-
-    /* Is this a compliant multifunction card? */
-    tuple.DesiredTuple = CISTPL_LONGLINK_MFC;
-    tuple.Attributes = TUPLE_RETURN_COMMON | TUPLE_RETURN_LINK;
-    info->multi = (first_tuple(handle, &tuple, &parse) == CS_SUCCESS);
-    
-    /* Is this a multiport card? */
-    tuple.DesiredTuple = CISTPL_MANFID;
-    if (first_tuple(handle, &tuple, &parse) == CS_SUCCESS) {
-       info->manfid = le16_to_cpu(buf[0]);
-       for (i = 0; i < MULTI_COUNT; i++)
-           if ((info->manfid == multi_id[i].manfid) &&
-               (le16_to_cpu(buf[1]) == multi_id[i].prodid))
-               break;
-       if (i < MULTI_COUNT)
-           info->multi = multi_id[i].multi;
-    }
-
-    /* Another check for dual-serial cards: look for either serial or
-       multifunction cards that ask for appropriate IO port ranges */
-    tuple.DesiredTuple = CISTPL_FUNCID;
-    if ((info->multi == 0) &&
-       ((first_tuple(handle, &tuple, &parse) != CS_SUCCESS) ||
-        (parse.funcid.func == CISTPL_FUNCID_MULTI) ||
-        (parse.funcid.func == CISTPL_FUNCID_SERIAL))) {
-       tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY;
-       if (first_tuple(handle, &tuple, &parse) == CS_SUCCESS) {
-           if ((cf->io.nwin == 1) && (cf->io.win[0].len % 8 == 0))
-               info->multi = cf->io.win[0].len >> 3;
-           if ((cf->io.nwin == 2) && (cf->io.win[0].len == 8) &&
-               (cf->io.win[1].len == 8))
-               info->multi = 2;
-       }
-    }
-    
-    if (info->multi > 1)
-       multi_config(link);
-    else
-       simple_config(link);
-    
-    if (info->ndev == 0)
-       goto failed;
-    
-    if (info->manfid == MANFID_IBM) {
-       conf_reg_t reg = { 0, CS_READ, 0x800, 0 };
-       CS_CHECK(AccessConfigurationRegister, link->handle, &reg);
-       reg.Action = CS_WRITE;
-       reg.Value = reg.Value | 1;
-       CS_CHECK(AccessConfigurationRegister, link->handle, &reg);
-    }
-
-    link->dev = &info->node[0];
-    link->state &= ~DEV_CONFIG_PENDING;
-    return;
-
-cs_failed:
-    cs_error(link->handle, last_fn, last_ret);
-failed:
-    serial_release((u_long)link);
-
-} /* serial_config */
-
-/*======================================================================
-
-    After a card is removed, serial_release() will unregister the net
-    device, and release the PCMCIA configuration.
-    
-======================================================================*/
-
-void serial_release(u_long arg)
-{
-    dev_link_t *link = (dev_link_t *)arg;
-    serial_info_t *info = link->priv;
-    int i;
-    
-    DEBUG(0, "serial_release(0x%p)\n", link);
-
-    for (i = 0; i < info->ndev; i++) {
-       unregister_serial(info->line[i]);
-    }
-    link->dev = NULL;
-
-    if (!info->slave) {
-       CardServices(ReleaseConfiguration, link->handle);
-       CardServices(ReleaseIO, link->handle, &link->io);
-       CardServices(ReleaseIRQ, link->handle, &link->irq);
-    }
-    
-    link->state &= ~DEV_CONFIG;
-
-} /* serial_release */
-
-/*======================================================================
-
-    The card status event handler.  Mostly, this schedules other
-    stuff to run after an event is received.  A CARD_REMOVAL event
-    also sets some flags to discourage the serial drivers from
-    talking to the ports.
-    
-======================================================================*/
-
-static int serial_event(event_t event, int priority,
-                       event_callback_args_t *args)
-{
-    dev_link_t *link = args->client_data;
-    serial_info_t *info = link->priv;
-    
-    DEBUG(1, "serial_event(0x%06x)\n", event);
-    
-    switch (event) {
-    case CS_EVENT_CARD_REMOVAL:
-       link->state &= ~DEV_PRESENT;
-       if (link->state & DEV_CONFIG)
-           mod_timer(&link->release, jiffies + HZ/20);
-       break;
-    case CS_EVENT_CARD_INSERTION:
-       link->state |= DEV_PRESENT | DEV_CONFIG_PENDING;
-       serial_config(link);
-       break;
-    case CS_EVENT_PM_SUSPEND:
-       link->state |= DEV_SUSPEND;
-       /* Fall through... */
-    case CS_EVENT_RESET_PHYSICAL:
-       if ((link->state & DEV_CONFIG) && !info->slave)
-           CardServices(ReleaseConfiguration, link->handle);
-       break;
-    case CS_EVENT_PM_RESUME:
-       link->state &= ~DEV_SUSPEND;
-       /* Fall through... */
-    case CS_EVENT_CARD_RESET:
-       if (DEV_OK(link) && !info->slave)
-           CardServices(RequestConfiguration, link->handle, &link->conf);
-       break;
-    }
-    return 0;
-} /* serial_event */
-
-/*====================================================================*/
-
-static int __init init_serial_cs(void)
-{
-    servinfo_t serv;
-    DEBUG(0, "%s\n", version);
-    CardServices(GetCardServicesInfo, &serv);
-    if (serv.Revision != CS_RELEASE_CODE) {
-       printk(KERN_NOTICE "serial_cs: Card Services release "
-              "does not match!\n");
-       return -1;
-    }
-    register_pccard_driver(&dev_info, &serial_attach, &serial_detach);
-    return 0;
-}
-
-static void __exit exit_serial_cs(void)
-{
-    DEBUG(0, "serial_cs: unloading\n");
-    unregister_pccard_driver(&dev_info);
-    while (dev_list != NULL)
-       serial_detach(dev_list);
-}
-
-module_init(init_serial_cs);
-module_exit(exit_serial_cs);
-
-MODULE_LICENSE("GPL");
diff --git a/drivers/char/serial.c b/drivers/char/serial.c
deleted file mode 100644 (file)
index c3cd00f..0000000
+++ /dev/null
@@ -1,6003 +0,0 @@
-/*
- *  linux/drivers/char/serial.c
- *
- *  Copyright (C) 1991, 1992  Linus Torvalds
- *  Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 
- *             1998, 1999  Theodore Ts'o
- *
- *  Extensively rewritten by Theodore Ts'o, 8/16/92 -- 9/14/92.  Now
- *  much more extensible to support other serial cards based on the
- *  16450/16550A UART's.  Added support for the AST FourPort and the
- *  Accent Async board.  
- *
- *  set_serial_info fixed to set the flags, custom divisor, and uart
- *     type fields.  Fix suggested by Michael K. Johnson 12/12/92.
- *
- *  11/95: TIOCMIWAIT, TIOCGICOUNT by Angelo Haritsis <ah@doc.ic.ac.uk>
- *
- *  03/96: Modularised by Angelo Haritsis <ah@doc.ic.ac.uk>
- *
- *  rs_set_termios fixed to look also for changes of the input
- *      flags INPCK, BRKINT, PARMRK, IGNPAR and IGNBRK.
- *                                            Bernd Anhäupl 05/17/96.
- *
- *  1/97:  Extended dumb serial ports are a config option now.  
- *         Saves 4k.   Michael A. Griffith <grif@acm.org>
- * 
- *  8/97: Fix bug in rs_set_termios with RTS
- *        Stanislav V. Voronyi <stas@uanet.kharkov.ua>
- *
- *  3/98: Change the IRQ detection, use of probe_irq_o*(),
- *       suppress TIOCSERGWILD and TIOCSERSWILD
- *       Etienne Lorrain <etienne.lorrain@ibm.net>
- *
- *  4/98: Added changes to support the ARM architecture proposed by
- *       Russell King
- *
- *  5/99: Updated to include support for the XR16C850 and ST16C654
- *        uarts.  Stuart MacDonald <stuartm@connecttech.com>
- *
- *  8/99: Generalized PCI support added.  Theodore Ts'o
- * 
- *  3/00: Rid circular buffer of redundant xmit_cnt.  Fix a
- *       few races on freeing buffers too.
- *       Alan Modra <alan@linuxcare.com>
- *
- *  5/00: Support for the RSA-DV II/S card added.
- *       Kiyokazu SUTO <suto@ks-and-ks.ne.jp>
- * 
- *  6/00: Remove old-style timer, use timer_list
- *        Andrew Morton <andrewm@uow.edu.au>
- *
- *  7/00: Support Timedia/Sunix/Exsys PCI cards
- *
- *  7/00: fix some returns on failure not using MOD_DEC_USE_COUNT.
- *       Arnaldo Carvalho de Melo <acme@conectiva.com.br>
- *
- * 10/00: add in optional software flow control for serial console.
- *       Kanoj Sarcar <kanoj@sgi.com>  (Modified by Theodore Ts'o)
- *
- */
-
-static char *serial_version = "5.05c";
-static char *serial_revdate = "2001-07-08";
-
-/*
- * Serial driver configuration section.  Here are the various options:
- *
- * CONFIG_HUB6
- *             Enables support for the venerable Bell Technologies
- *             HUB6 card.
- *
- * CONFIG_SERIAL_MANY_PORTS
- *             Enables support for ports beyond the standard, stupid
- *             COM 1/2/3/4.
- *
- * CONFIG_SERIAL_MULTIPORT
- *             Enables support for special multiport board support.
- *
- * CONFIG_SERIAL_SHARE_IRQ
- *             Enables support for multiple serial ports on one IRQ
- *
- * CONFIG_SERIAL_DETECT_IRQ
- *             Enable the autodetection of IRQ on standart ports
- *
- * SERIAL_PARANOIA_CHECK
- *             Check the magic number for the async_structure where
- *             ever possible.
- *
- * CONFIG_SERIAL_ACPI
- *             Enable support for serial console port and serial 
- *             debug port as defined by the SPCR and DBGP tables in 
- *             ACPI 2.0.
- */
-
-#include <linux/config.h>
-#include <linux/version.h>
-
-#undef SERIAL_PARANOIA_CHECK
-#define CONFIG_SERIAL_NOPAUSE_IO
-#define SERIAL_DO_RESTART
-
-#if 0
-/* These defines are normally controlled by the autoconf.h */
-#define CONFIG_SERIAL_MANY_PORTS
-#define CONFIG_SERIAL_SHARE_IRQ
-#define CONFIG_SERIAL_DETECT_IRQ
-#define CONFIG_SERIAL_MULTIPORT
-#define CONFIG_HUB6
-#endif
-
-#ifdef CONFIG_PCI
-#define ENABLE_SERIAL_PCI
-#ifndef CONFIG_SERIAL_SHARE_IRQ
-#define CONFIG_SERIAL_SHARE_IRQ
-#endif
-#ifndef CONFIG_SERIAL_MANY_PORTS
-#define CONFIG_SERIAL_MANY_PORTS
-#endif
-#endif
-
-#ifdef CONFIG_SERIAL_ACPI
-#define ENABLE_SERIAL_ACPI
-#endif
-
-
-/* Set of debugging defines */
-
-#undef SERIAL_DEBUG_INTR
-#undef SERIAL_DEBUG_OPEN
-#undef SERIAL_DEBUG_FLOW
-#undef SERIAL_DEBUG_RS_WAIT_UNTIL_SENT
-#undef SERIAL_DEBUG_PCI
-#undef SERIAL_DEBUG_AUTOCONF
-
-/* Sanity checks */
-
-#ifdef CONFIG_SERIAL_MULTIPORT
-#ifndef CONFIG_SERIAL_SHARE_IRQ
-#define CONFIG_SERIAL_SHARE_IRQ
-#endif
-#endif
-
-#ifdef CONFIG_HUB6
-#ifndef CONFIG_SERIAL_MANY_PORTS
-#define CONFIG_SERIAL_MANY_PORTS
-#endif
-#ifndef CONFIG_SERIAL_SHARE_IRQ
-#define CONFIG_SERIAL_SHARE_IRQ
-#endif
-#endif
-
-#ifdef MODULE
-#undef CONFIG_SERIAL_CONSOLE
-#endif
-
-#define CONFIG_SERIAL_RSA
-
-#define RS_STROBE_TIME (10*HZ)
-#define RS_ISR_PASS_LIMIT 256
-
-#if defined(__i386__) && (defined(CONFIG_M386) || defined(CONFIG_M486))
-#define SERIAL_INLINE
-#endif
-  
-/*
- * End of serial driver configuration section.
- */
-
-#include <linux/module.h>
-
-#include <linux/types.h>
-#ifdef LOCAL_HEADERS
-#include "serial_local.h"
-#else
-#include <linux/serial.h>
-#include <linux/serialP.h>
-#include <linux/serial_reg.h>
-#include <asm/serial.h>
-#define LOCAL_VERSTRING ""
-#endif
-
-#include <linux/errno.h>
-#include <linux/signal.h>
-#include <linux/sched.h>
-#include <linux/timer.h>
-#include <linux/interrupt.h>
-#include <linux/tty.h>
-#include <linux/tty_flip.h>
-#include <linux/major.h>
-#include <linux/string.h>
-#include <linux/fcntl.h>
-#include <linux/ptrace.h>
-#include <linux/ioport.h>
-#include <linux/mm.h>
-#include <linux/slab.h>
-#if (LINUX_VERSION_CODE >= 131343)
-#include <linux/init.h>
-#endif
-#if (LINUX_VERSION_CODE >= 131336)
-#include <asm/uaccess.h>
-#endif
-#include <linux/delay.h>
-#ifdef CONFIG_SERIAL_CONSOLE
-#include <linux/console.h>
-#endif
-#ifdef ENABLE_SERIAL_PCI
-#include <linux/pci.h>
-#endif
-
-#include <linux/isapnp.h>
-#ifdef __ISAPNP__
-#ifndef ENABLE_SERIAL_PNP
-#define ENABLE_SERIAL_PNP
-#endif
-#endif
-
-#ifdef CONFIG_MAGIC_SYSRQ
-#include <linux/sysrq.h>
-#endif
-
-/*
- * All of the compatibilty code so we can compile serial.c against
- * older kernels is hidden in serial_compat.h
- */
-#if defined(LOCAL_HEADERS) || (LINUX_VERSION_CODE < 0x020317) /* 2.3.23 */
-#include "serial_compat.h"
-#endif
-
-#include <asm/system.h>
-#include <asm/io.h>
-#include <asm/irq.h>
-#include <asm/bitops.h>
-
-#ifdef CONFIG_MAC_SERIAL
-#define SERIAL_DEV_OFFSET      2
-#else
-#define SERIAL_DEV_OFFSET      0
-#endif
-
-#ifdef SERIAL_INLINE
-#define _INLINE_ inline
-#else
-#define _INLINE_
-#endif
-
-static char *serial_name = "Serial driver";
-
-static DECLARE_TASK_QUEUE(tq_serial);
-
-static struct tty_driver serial_driver, callout_driver;
-static int serial_refcount;
-
-static struct timer_list serial_timer;
-
-/* serial subtype definitions */
-#ifndef SERIAL_TYPE_NORMAL
-#define SERIAL_TYPE_NORMAL     1
-#define SERIAL_TYPE_CALLOUT    2
-#endif
-
-/* number of characters left in xmit buffer before we ask for more */
-#define WAKEUP_CHARS 256
-
-/*
- * IRQ_timeout         - How long the timeout should be for each IRQ
- *                             should be after the IRQ has been active.
- */
-
-static struct async_struct *IRQ_ports[NR_IRQS];
-#ifdef CONFIG_SERIAL_MULTIPORT
-static struct rs_multiport_struct rs_multiport[NR_IRQS];
-#endif
-static int IRQ_timeout[NR_IRQS];
-#ifdef CONFIG_SERIAL_CONSOLE
-static struct console sercons;
-static int lsr_break_flag;
-#endif
-#if defined(CONFIG_SERIAL_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ)
-static unsigned long break_pressed; /* break, really ... */
-#endif
-
-static unsigned detect_uart_irq (struct serial_state * state);
-static void autoconfig(struct serial_state * state);
-static void change_speed(struct async_struct *info, struct termios *old);
-static void rs_wait_until_sent(struct tty_struct *tty, int timeout);
-
-/*
- * Here we define the default xmit fifo size used for each type of
- * UART
- */
-static struct serial_uart_config uart_config[] = {
-       { "unknown", 1, 0 }, 
-       { "8250", 1, 0 }, 
-       { "16450", 1, 0 }, 
-       { "16550", 1, 0 }, 
-       { "16550A", 16, UART_CLEAR_FIFO | UART_USE_FIFO }, 
-       { "cirrus", 1, 0 },     /* usurped by cyclades.c */
-       { "ST16650", 1, UART_CLEAR_FIFO | UART_STARTECH }, 
-       { "ST16650V2", 32, UART_CLEAR_FIFO | UART_USE_FIFO |
-                 UART_STARTECH }, 
-       { "TI16750", 64, UART_CLEAR_FIFO | UART_USE_FIFO},
-       { "Startech", 1, 0},    /* usurped by cyclades.c */
-       { "16C950/954", 128, UART_CLEAR_FIFO | UART_USE_FIFO},
-       { "ST16654", 64, UART_CLEAR_FIFO | UART_USE_FIFO |
-                 UART_STARTECH }, 
-       { "XR16850", 128, UART_CLEAR_FIFO | UART_USE_FIFO |
-                 UART_STARTECH },
-       { "RSA", 2048, UART_CLEAR_FIFO | UART_USE_FIFO }, 
-       { 0, 0}
-};
-
-#if defined(CONFIG_SERIAL_RSA) && defined(MODULE)
-
-#define PORT_RSA_MAX 4
-static int probe_rsa[PORT_RSA_MAX];
-static int force_rsa[PORT_RSA_MAX];
-
-MODULE_PARM(probe_rsa, "1-" __MODULE_STRING(PORT_RSA_MAX) "i");
-MODULE_PARM_DESC(probe_rsa, "Probe I/O ports for RSA");
-MODULE_PARM(force_rsa, "1-" __MODULE_STRING(PORT_RSA_MAX) "i");
-MODULE_PARM_DESC(force_rsa, "Force I/O ports for RSA");
-#endif /* CONFIG_SERIAL_RSA  */
-
-static struct serial_state rs_table[RS_TABLE_SIZE] = {
-       SERIAL_PORT_DFNS        /* Defined in serial.h */
-};
-
-#define NR_PORTS       (sizeof(rs_table)/sizeof(struct serial_state))
-
-#if (defined(ENABLE_SERIAL_PCI) || defined(ENABLE_SERIAL_PNP))
-#define NR_PCI_BOARDS  8
-
-static struct pci_board_inst   serial_pci_board[NR_PCI_BOARDS];
-
-#ifndef IS_PCI_REGION_IOPORT
-#define IS_PCI_REGION_IOPORT(dev, r) (pci_resource_flags((dev), (r)) & \
-                                     IORESOURCE_IO)
-#endif
-#ifndef IS_PCI_REGION_IOMEM
-#define IS_PCI_REGION_IOMEM(dev, r) (pci_resource_flags((dev), (r)) & \
-                                     IORESOURCE_MEM)
-#endif
-#ifndef PCI_IRQ_RESOURCE
-#define PCI_IRQ_RESOURCE(dev, r) ((dev)->irq_resource[r].start)
-#endif
-#ifndef pci_get_subvendor
-#define pci_get_subvendor(dev) ((dev)->subsystem_vendor)
-#define pci_get_subdevice(dev)  ((dev)->subsystem_device)
-#endif
-#endif /* ENABLE_SERIAL_PCI || ENABLE_SERIAL_PNP  */
-
-#ifndef PREPARE_FUNC
-#define PREPARE_FUNC(dev)  (dev->prepare)
-#define ACTIVATE_FUNC(dev)  (dev->activate)
-#define DEACTIVATE_FUNC(dev)  (dev->deactivate)
-#endif
-
-#define HIGH_BITS_OFFSET ((sizeof(long)-sizeof(int))*8)
-
-static struct tty_struct *serial_table[NR_PORTS];
-static struct termios *serial_termios[NR_PORTS];
-static struct termios *serial_termios_locked[NR_PORTS];
-
-
-#if defined(MODULE) && defined(SERIAL_DEBUG_MCOUNT)
-#define DBG_CNT(s) printk("(%s): [%x] refc=%d, serc=%d, ttyc=%d -> %s\n", \
- kdevname(tty->device), (info->flags), serial_refcount,info->count,tty->count,s)
-#else
-#define DBG_CNT(s)
-#endif
-
-/*
- * tmp_buf is used as a temporary buffer by serial_write.  We need to
- * lock it in case the copy_from_user blocks while swapping in a page,
- * and some other program tries to do a serial write at the same time.
- * Since the lock will only come under contention when the system is
- * swapping and available memory is low, it makes sense to share one
- * buffer across all the serial ports, since it significantly saves
- * memory if large numbers of serial ports are open.
- */
-static unsigned char *tmp_buf;
-#ifdef DECLARE_MUTEX
-static DECLARE_MUTEX(tmp_buf_sem);
-#else
-static struct semaphore tmp_buf_sem = MUTEX;
-#endif
-
-
-static inline int serial_paranoia_check(struct async_struct *info,
-                                       kdev_t device, const char *routine)
-{
-#ifdef SERIAL_PARANOIA_CHECK
-       static const char *badmagic =
-               "Warning: bad magic number for serial struct (%s) in %s\n";
-       static const char *badinfo =
-               "Warning: null async_struct for (%s) in %s\n";
-
-       if (!info) {
-               printk(badinfo, kdevname(device), routine);
-               return 1;
-       }
-       if (info->magic != SERIAL_MAGIC) {
-               printk(badmagic, kdevname(device), routine);
-               return 1;
-       }
-#endif
-       return 0;
-}
-
-static _INLINE_ unsigned int serial_in(struct async_struct *info, int offset)
-{
-       switch (info->io_type) {
-#ifdef CONFIG_HUB6
-       case SERIAL_IO_HUB6:
-               outb(info->hub6 - 1 + offset, info->port);
-               return inb(info->port+1);
-#endif
-       case SERIAL_IO_MEM:
-               return readb((unsigned long) info->iomem_base +
-                            (offset<<info->iomem_reg_shift));
-#ifdef CONFIG_SERIAL_GSC
-       case SERIAL_IO_GSC:
-               return gsc_readb(info->iomem_base + offset);
-#endif
-       default:
-               return inb(info->port + offset);
-       }
-}
-
-static _INLINE_ void serial_out(struct async_struct *info, int offset,
-                               int value)
-{
-       switch (info->io_type) {
-#ifdef CONFIG_HUB6
-       case SERIAL_IO_HUB6:
-               outb(info->hub6 - 1 + offset, info->port);
-               outb(value, info->port+1);
-               break;
-#endif
-       case SERIAL_IO_MEM:
-               writeb(value, (unsigned long) info->iomem_base +
-                             (offset<<info->iomem_reg_shift));
-               break;
-#ifdef CONFIG_SERIAL_GSC
-       case SERIAL_IO_GSC:
-               gsc_writeb(value, info->iomem_base + offset);
-               break;
-#endif
-       default:
-               outb(value, info->port+offset);
-       }
-}
-
-/*
- * We used to support using pause I/O for certain machines.  We
- * haven't supported this for a while, but just in case it's badly
- * needed for certain old 386 machines, I've left these #define's
- * in....
- */
-#define serial_inp(info, offset)               serial_in(info, offset)
-#define serial_outp(info, offset, value)       serial_out(info, offset, value)
-
-
-/*
- * For the 16C950
- */
-void serial_icr_write(struct async_struct *info, int offset, int  value)
-{
-       serial_out(info, UART_SCR, offset);
-       serial_out(info, UART_ICR, value);
-}
-
-unsigned int serial_icr_read(struct async_struct *info, int offset)
-{
-       int     value;
-
-       serial_icr_write(info, UART_ACR, info->ACR | UART_ACR_ICRRD);
-       serial_out(info, UART_SCR, offset);
-       value = serial_in(info, UART_ICR);
-       serial_icr_write(info, UART_ACR, info->ACR);
-       return value;
-}
-
-/*
- * ------------------------------------------------------------
- * rs_stop() and rs_start()
- *
- * This routines are called before setting or resetting tty->stopped.
- * They enable or disable transmitter interrupts, as necessary.
- * ------------------------------------------------------------
- */
-static void rs_stop(struct tty_struct *tty)
-{
-       struct async_struct *info = (struct async_struct *)tty->driver_data;
-       unsigned long flags;
-
-       if (serial_paranoia_check(info, tty->device, "rs_stop"))
-               return;
-       
-       save_flags(flags); cli();
-       if (info->IER & UART_IER_THRI) {
-               info->IER &= ~UART_IER_THRI;
-               serial_out(info, UART_IER, info->IER);
-       }
-       if (info->state->type == PORT_16C950) {
-               info->ACR |= UART_ACR_TXDIS;
-               serial_icr_write(info, UART_ACR, info->ACR);
-       }
-       restore_flags(flags);
-}
-
-static void rs_start(struct tty_struct *tty)
-{
-       struct async_struct *info = (struct async_struct *)tty->driver_data;
-       unsigned long flags;
-       
-       if (serial_paranoia_check(info, tty->device, "rs_start"))
-               return;
-       
-       save_flags(flags); cli();
-       if (info->xmit.head != info->xmit.tail
-           && info->xmit.buf
-           && !(info->IER & UART_IER_THRI)) {
-               info->IER |= UART_IER_THRI;
-               serial_out(info, UART_IER, info->IER);
-       }
-       if (info->state->type == PORT_16C950) {
-               info->ACR &= ~UART_ACR_TXDIS;
-               serial_icr_write(info, UART_ACR, info->ACR);
-       }
-       restore_flags(flags);
-}
-
-/*
- * ----------------------------------------------------------------------
- *
- * Here starts the interrupt handling routines.  All of the following
- * subroutines are declared as inline and are folded into
- * rs_interrupt().  They were separated out for readability's sake.
- *
- * Note: rs_interrupt() is a "fast" interrupt, which means that it
- * runs with interrupts turned off.  People who may want to modify
- * rs_interrupt() should try to keep the interrupt handler as fast as
- * possible.  After you are done making modifications, it is not a bad
- * idea to do:
- * 
- * gcc -S -DKERNEL -Wall -Wstrict-prototypes -O6 -fomit-frame-pointer serial.c
- *
- * and look at the resulting assemble code in serial.s.
- *
- *                             - Ted Ts'o (tytso@mit.edu), 7-Mar-93
- * -----------------------------------------------------------------------
- */
-
-/*
- * This routine is used by the interrupt handler to schedule
- * processing in the software interrupt portion of the driver.
- */
-static _INLINE_ void rs_sched_event(struct async_struct *info,
-                                 int event)
-{
-       info->event |= 1 << event;
-       queue_task(&info->tqueue, &tq_serial);
-       mark_bh(SERIAL_BH);
-}
-
-static _INLINE_ void receive_chars(struct async_struct *info,
-                                int *status, struct pt_regs * regs)
-{
-       struct tty_struct *tty = info->tty;
-       unsigned char ch;
-       struct  async_icount *icount;
-       int     max_count = 256;
-
-       icount = &info->state->icount;
-       do {
-               if (tty->flip.count >= TTY_FLIPBUF_SIZE) {
-                       tty->flip.tqueue.routine((void *) tty);
-                       if (tty->flip.count >= TTY_FLIPBUF_SIZE)
-                               return;         // if TTY_DONT_FLIP is set
-               }
-               ch = serial_inp(info, UART_RX);
-               *tty->flip.char_buf_ptr = ch;
-               icount->rx++;
-               
-#ifdef SERIAL_DEBUG_INTR
-               printk("DR%02x:%02x...", ch, *status);
-#endif
-               *tty->flip.flag_buf_ptr = 0;
-               if (*status & (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);
-                               icount->brk++;
-                               /*
-                                * We do the SysRQ and SAK checking
-                                * here because otherwise the break
-                                * may get masked by ignore_status_mask
-                                * or read_status_mask.
-                                */
-#if defined(CONFIG_SERIAL_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ)
-                               if (info->line == sercons.index) {
-                                       if (!break_pressed) {
-                                               break_pressed = jiffies;
-                                               goto ignore_char;
-                                       }
-                                       break_pressed = 0;
-                               }
-#endif
-                               if (info->flags & ASYNC_SAK)
-                                       do_SAK(tty);
-                       } else if (*status & UART_LSR_PE)
-                               icount->parity++;
-                       else if (*status & UART_LSR_FE)
-                               icount->frame++;
-                       if (*status & UART_LSR_OE)
-                               icount->overrun++;
-
-                       /*
-                        * Mask off conditions which should be ignored.
-                        */
-                       *status &= info->read_status_mask;
-
-#ifdef CONFIG_SERIAL_CONSOLE
-                       if (info->line == sercons.index) {
-                               /* Recover the break flag from console xmit */
-                               *status |= lsr_break_flag;
-                               lsr_break_flag = 0;
-                       }
-#endif
-                       if (*status & (UART_LSR_BI)) {
-#ifdef SERIAL_DEBUG_INTR
-                               printk("handling break....");
-#endif
-                               *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;
-               }
-#if defined(CONFIG_SERIAL_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ)
-               if (break_pressed && info->line == sercons.index) {
-                       if (ch != 0 &&
-                           time_before(jiffies, break_pressed + HZ*5)) {
-                               handle_sysrq(ch, regs, NULL);
-                               break_pressed = 0;
-                               goto ignore_char;
-                       }
-                       break_pressed = 0;
-               }
-#endif
-               if ((*status & info->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.count++;
-                       tty->flip.flag_buf_ptr++;
-                       tty->flip.char_buf_ptr++;
-               }
-#if defined(CONFIG_SERIAL_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ)
-       ignore_char:
-#endif
-               *status = serial_inp(info, UART_LSR);
-       } while ((*status & UART_LSR_DR) && (max_count-- > 0));
-#if (LINUX_VERSION_CODE > 131394) /* 2.1.66 */
-       tty_flip_buffer_push(tty);
-#else
-       queue_task_irq_off(&tty->flip.tqueue, &tq_timer);
-#endif 
-}
-
-static _INLINE_ void transmit_chars(struct async_struct *info, int *intr_done)
-{
-       int count;
-
-       if (info->x_char) {
-               serial_outp(info, UART_TX, info->x_char);
-               info->state->icount.tx++;
-               info->x_char = 0;
-               if (intr_done)
-                       *intr_done = 0;
-               return;
-       }
-       if (info->xmit.head == info->xmit.tail
-           || info->tty->stopped
-           || info->tty->hw_stopped) {
-               info->IER &= ~UART_IER_THRI;
-               serial_out(info, UART_IER, info->IER);
-               return;
-       }
-       
-       count = info->xmit_fifo_size;
-       do {
-               serial_out(info, UART_TX, info->xmit.buf[info->xmit.tail]);
-               info->xmit.tail = (info->xmit.tail + 1) & (SERIAL_XMIT_SIZE-1);
-               info->state->icount.tx++;
-               if (info->xmit.head == info->xmit.tail)
-                       break;
-       } while (--count > 0);
-       
-       if (CIRC_CNT(info->xmit.head,
-                    info->xmit.tail,
-                    SERIAL_XMIT_SIZE) < WAKEUP_CHARS)
-               rs_sched_event(info, RS_EVENT_WRITE_WAKEUP);
-
-#ifdef SERIAL_DEBUG_INTR
-       printk("THRE...");
-#endif
-       if (intr_done)
-               *intr_done = 0;
-
-       if (info->xmit.head == info->xmit.tail) {
-               info->IER &= ~UART_IER_THRI;
-               serial_out(info, UART_IER, info->IER);
-       }
-}
-
-static _INLINE_ void check_modem_status(struct async_struct *info)
-{
-       int     status;
-       struct  async_icount *icount;
-       
-       status = serial_in(info, UART_MSR);
-
-       if (status & UART_MSR_ANY_DELTA) {
-               icount = &info->state->icount;
-               /* update input line counters */
-               if (status & UART_MSR_TERI)
-                       icount->rng++;
-               if (status & UART_MSR_DDSR)
-                       icount->dsr++;
-               if (status & UART_MSR_DDCD) {
-                       icount->dcd++;
-#ifdef CONFIG_HARD_PPS
-                       if ((info->flags & ASYNC_HARDPPS_CD) &&
-                           (status & UART_MSR_DCD))
-                               hardpps();
-#endif
-               }
-               if (status & UART_MSR_DCTS)
-                       icount->cts++;
-               wake_up_interruptible(&info->delta_msr_wait);
-       }
-
-       if ((info->flags & ASYNC_CHECK_CD) && (status & UART_MSR_DDCD)) {
-#if (defined(SERIAL_DEBUG_OPEN) || defined(SERIAL_DEBUG_INTR))
-               printk("ttys%d CD now %s...", info->line,
-                      (status & UART_MSR_DCD) ? "on" : "off");
-#endif         
-               if (status & UART_MSR_DCD)
-                       wake_up_interruptible(&info->open_wait);
-               else if (!((info->flags & ASYNC_CALLOUT_ACTIVE) &&
-                          (info->flags & ASYNC_CALLOUT_NOHUP))) {
-#ifdef SERIAL_DEBUG_OPEN
-                       printk("doing serial hangup...");
-#endif
-                       if (info->tty)
-                               tty_hangup(info->tty);
-               }
-       }
-       if (info->flags & ASYNC_CTS_FLOW) {
-               if (info->tty->hw_stopped) {
-                       if (status & UART_MSR_CTS) {
-#if (defined(SERIAL_DEBUG_INTR) || defined(SERIAL_DEBUG_FLOW))
-                               printk("CTS tx start...");
-#endif
-                               info->tty->hw_stopped = 0;
-                               info->IER |= UART_IER_THRI;
-                               serial_out(info, UART_IER, info->IER);
-                               rs_sched_event(info, RS_EVENT_WRITE_WAKEUP);
-                               return;
-                       }
-               } else {
-                       if (!(status & UART_MSR_CTS)) {
-#if (defined(SERIAL_DEBUG_INTR) || defined(SERIAL_DEBUG_FLOW))
-                               printk("CTS tx stop...");
-#endif
-                               info->tty->hw_stopped = 1;
-                               info->IER &= ~UART_IER_THRI;
-                               serial_out(info, UART_IER, info->IER);
-                       }
-               }
-       }
-}
-
-#ifdef CONFIG_SERIAL_SHARE_IRQ
-/*
- * This is the serial driver's generic interrupt routine
- */
-static void rs_interrupt(int irq, void *dev_id, struct pt_regs * regs)
-{
-       int status;
-       struct async_struct * info;
-       int pass_counter = 0;
-       struct async_struct *end_mark = 0;
-#ifdef CONFIG_SERIAL_MULTIPORT 
-       int first_multi = 0;
-       struct rs_multiport_struct *multi;
-#endif
-
-#ifdef SERIAL_DEBUG_INTR
-       printk("rs_interrupt(%d)...", irq);
-#endif
-
-       info = IRQ_ports[irq];
-       if (!info)
-               return;
-
-#ifdef CONFIG_SERIAL_MULTIPORT 
-       multi = &rs_multiport[irq];
-       if (multi->port_monitor)
-               first_multi = inb(multi->port_monitor);
-#endif
-
-       do {
-               if (!info->tty ||
-                   (serial_in(info, UART_IIR) & UART_IIR_NO_INT)) {
-                       if (!end_mark)
-                               end_mark = info;
-                       goto next;
-               }
-#ifdef SERIAL_DEBUG_INTR
-               printk("IIR = %x...", serial_in(info, UART_IIR));
-#endif
-               end_mark = 0;
-
-               info->last_active = jiffies;
-
-               status = serial_inp(info, UART_LSR);
-#ifdef SERIAL_DEBUG_INTR
-               printk("status = %x...", status);
-#endif
-               if (status & UART_LSR_DR)
-                       receive_chars(info, &status, regs);
-               check_modem_status(info);
-               if (status & UART_LSR_THRE)
-                       transmit_chars(info, 0);
-
-       next:
-               info = info->next_port;
-               if (!info) {
-                       info = IRQ_ports[irq];
-                       if (pass_counter++ > RS_ISR_PASS_LIMIT) {
-#if 0
-                               printk("rs loop break\n");
-#endif
-                               break;  /* Prevent infinite loops */
-                       }
-                       continue;
-               }
-       } while (end_mark != info);
-#ifdef CONFIG_SERIAL_MULTIPORT 
-       if (multi->port_monitor)
-               printk("rs port monitor (normal) irq %d: 0x%x, 0x%x\n",
-                      info->state->irq, first_multi,
-                      inb(multi->port_monitor));
-#endif
-#ifdef SERIAL_DEBUG_INTR
-       printk("end.\n");
-#endif
-}
-#endif /* #ifdef CONFIG_SERIAL_SHARE_IRQ */
-
-
-/*
- * This is the serial driver's interrupt routine for a single port
- */
-static void rs_interrupt_single(int irq, void *dev_id, struct pt_regs * regs)
-{
-       int status;
-       int pass_counter = 0;
-       struct async_struct * info;
-#ifdef CONFIG_SERIAL_MULTIPORT 
-       int first_multi = 0;
-       struct rs_multiport_struct *multi;
-#endif
-       
-#ifdef SERIAL_DEBUG_INTR
-       printk("rs_interrupt_single(%d)...", irq);
-#endif
-
-       info = IRQ_ports[irq];
-       if (!info || !info->tty)
-               return;
-
-#ifdef CONFIG_SERIAL_MULTIPORT 
-       multi = &rs_multiport[irq];
-       if (multi->port_monitor)
-               first_multi = inb(multi->port_monitor);
-#endif
-
-       do {
-               status = serial_inp(info, UART_LSR);
-#ifdef SERIAL_DEBUG_INTR
-               printk("status = %x...", status);
-#endif
-               if (status & UART_LSR_DR)
-                       receive_chars(info, &status, regs);
-               check_modem_status(info);
-               if (status & UART_LSR_THRE)
-                       transmit_chars(info, 0);
-               if (pass_counter++ > RS_ISR_PASS_LIMIT) {
-#if 0
-                       printk("rs_single loop break.\n");
-#endif
-                       break;
-               }
-#ifdef SERIAL_DEBUG_INTR
-               printk("IIR = %x...", serial_in(info, UART_IIR));
-#endif
-       } while (!(serial_in(info, UART_IIR) & UART_IIR_NO_INT));
-       info->last_active = jiffies;
-#ifdef CONFIG_SERIAL_MULTIPORT 
-       if (multi->port_monitor)
-               printk("rs port monitor (single) irq %d: 0x%x, 0x%x\n",
-                      info->state->irq, first_multi,
-                      inb(multi->port_monitor));
-#endif
-#ifdef SERIAL_DEBUG_INTR
-       printk("end.\n");
-#endif
-}
-
-#ifdef CONFIG_SERIAL_MULTIPORT 
-/*
- * This is the serial driver's for multiport boards
- */
-static void rs_interrupt_multi(int irq, void *dev_id, struct pt_regs * regs)
-{
-       int status;
-       struct async_struct * info;
-       int pass_counter = 0;
-       int first_multi= 0;
-       struct rs_multiport_struct *multi;
-
-#ifdef SERIAL_DEBUG_INTR
-       printk("rs_interrupt_multi(%d)...", irq);
-#endif
-
-       info = IRQ_ports[irq];
-       if (!info)
-               return;
-       multi = &rs_multiport[irq];
-       if (!multi->port1) {
-               /* Should never happen */
-               printk("rs_interrupt_multi: NULL port1!\n");
-               return;
-       }
-       if (multi->port_monitor)
-               first_multi = inb(multi->port_monitor);
-       
-       while (1) {
-               if (!info->tty ||
-                   (serial_in(info, UART_IIR) & UART_IIR_NO_INT))
-                       goto next;
-
-               info->last_active = jiffies;
-
-               status = serial_inp(info, UART_LSR);
-#ifdef SERIAL_DEBUG_INTR
-               printk("status = %x...", status);
-#endif
-               if (status & UART_LSR_DR)
-                       receive_chars(info, &status, regs);
-               check_modem_status(info);
-               if (status & UART_LSR_THRE)
-                       transmit_chars(info, 0);
-
-       next:
-               info = info->next_port;
-               if (info)
-                       continue;
-
-               info = IRQ_ports[irq];
-               /*
-                * The user was a bonehead, and misconfigured their
-                * multiport info.  Rather than lock up the kernel
-                * in an infinite loop, if we loop too many times,
-                * print a message and break out of the loop.
-                */
-               if (pass_counter++ > RS_ISR_PASS_LIMIT) {
-                       printk("Misconfigured multiport serial info "
-                              "for irq %d.  Breaking out irq loop\n", irq);
-                       break; 
-               }
-               if (multi->port_monitor)
-                       printk("rs port monitor irq %d: 0x%x, 0x%x\n",
-                              info->state->irq, first_multi,
-                              inb(multi->port_monitor));
-               if ((inb(multi->port1) & multi->mask1) != multi->match1)
-                       continue;
-               if (!multi->port2)
-                       break;
-               if ((inb(multi->port2) & multi->mask2) != multi->match2)
-                       continue;
-               if (!multi->port3)
-                       break;
-               if ((inb(multi->port3) & multi->mask3) != multi->match3)
-                       continue;
-               if (!multi->port4)
-                       break;
-               if ((inb(multi->port4) & multi->mask4) != multi->match4)
-                       continue;
-               break;
-       } 
-#ifdef SERIAL_DEBUG_INTR
-       printk("end.\n");
-#endif
-}
-#endif
-
-/*
- * -------------------------------------------------------------------
- * Here ends the serial interrupt routines.
- * -------------------------------------------------------------------
- */
-
-/*
- * This routine is used to handle the "bottom half" processing for the
- * serial driver, known also the "software interrupt" processing.
- * This processing is done at the kernel interrupt level, after the
- * rs_interrupt() has returned, BUT WITH INTERRUPTS TURNED ON.  This
- * is where time-consuming activities which can not be done in the
- * interrupt driver proper are done; the interrupt driver schedules
- * them using rs_sched_event(), and they get done here.
- */
-static void do_serial_bh(void)
-{
-       run_task_queue(&tq_serial);
-}
-
-static void do_softint(void *private_)
-{
-       struct async_struct     *info = (struct async_struct *) private_;
-       struct tty_struct       *tty;
-       
-       tty = info->tty;
-       if (!tty)
-               return;
-
-       if (test_and_clear_bit(RS_EVENT_WRITE_WAKEUP, &info->event)) {
-               if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
-                   tty->ldisc.write_wakeup)
-                       (tty->ldisc.write_wakeup)(tty);
-               wake_up_interruptible(&tty->write_wait);
-#ifdef SERIAL_HAVE_POLL_WAIT
-               wake_up_interruptible(&tty->poll_wait);
-#endif
-       }
-}
-
-/*
- * This subroutine is called when the RS_TIMER goes off.  It is used
- * by the serial driver to handle ports that do not have an interrupt
- * (irq=0).  This doesn't work very well for 16450's, but gives barely
- * passable results for a 16550A.  (Although at the expense of much
- * CPU overhead).
- */
-static void rs_timer(unsigned long dummy)
-{
-       static unsigned long last_strobe;
-       struct async_struct *info;
-       unsigned int    i;
-       unsigned long flags;
-
-       if ((jiffies - last_strobe) >= RS_STROBE_TIME) {
-               for (i=0; i < NR_IRQS; i++) {
-                       info = IRQ_ports[i];
-                       if (!info)
-                               continue;
-                       save_flags(flags); cli();
-#ifdef CONFIG_SERIAL_SHARE_IRQ
-                       if (info->next_port) {
-                               do {
-                                       serial_out(info, UART_IER, 0);
-                                       info->IER |= UART_IER_THRI;
-                                       serial_out(info, UART_IER, info->IER);
-                                       info = info->next_port;
-                               } while (info);
-#ifdef CONFIG_SERIAL_MULTIPORT
-                               if (rs_multiport[i].port1)
-                                       rs_interrupt_multi(i, NULL, NULL);
-                               else
-#endif
-                                       rs_interrupt(i, NULL, NULL);
-                       } else
-#endif /* CONFIG_SERIAL_SHARE_IRQ */
-                               rs_interrupt_single(i, NULL, NULL);
-                       restore_flags(flags);
-               }
-       }
-       last_strobe = jiffies;
-       mod_timer(&serial_timer, jiffies + RS_STROBE_TIME);
-
-       if (IRQ_ports[0]) {
-               save_flags(flags); cli();
-#ifdef CONFIG_SERIAL_SHARE_IRQ
-               rs_interrupt(0, NULL, NULL);
-#else
-               rs_interrupt_single(0, NULL, NULL);
-#endif
-               restore_flags(flags);
-
-               mod_timer(&serial_timer, jiffies + IRQ_timeout[0]);
-       }
-}
-
-/*
- * ---------------------------------------------------------------
- * Low level utility subroutines for the serial driver:  routines to
- * figure out the appropriate timeout for an interrupt chain, routines
- * to initialize and startup a serial port, and routines to shutdown a
- * serial port.  Useful stuff like that.
- * ---------------------------------------------------------------
- */
-
-/*
- * This routine figures out the correct timeout for a particular IRQ.
- * It uses the smallest timeout of all of the serial ports in a
- * particular interrupt chain.  Now only used for IRQ 0....
- */
-static void figure_IRQ_timeout(int irq)
-{
-       struct  async_struct    *info;
-       int     timeout = 60*HZ;        /* 60 seconds === a long time :-) */
-
-       info = IRQ_ports[irq];
-       if (!info) {
-               IRQ_timeout[irq] = 60*HZ;
-               return;
-       }
-       while (info) {
-               if (info->timeout < timeout)
-                       timeout = info->timeout;
-               info = info->next_port;
-       }
-       if (!irq)
-               timeout = timeout / 2;
-       IRQ_timeout[irq] = (timeout > 3) ? timeout-2 : 1;
-}
-
-#ifdef CONFIG_SERIAL_RSA
-/* Attempts to turn on the RSA FIFO.  Returns zero on failure */
-static int enable_rsa(struct async_struct *info)
-{
-       unsigned char mode;
-       int result;
-       unsigned long flags;
-
-       save_flags(flags); cli();
-       mode = serial_inp(info, UART_RSA_MSR);
-       result = mode & UART_RSA_MSR_FIFO;
-
-       if (!result) {
-               serial_outp(info, UART_RSA_MSR, mode | UART_RSA_MSR_FIFO);
-               mode = serial_inp(info, UART_RSA_MSR);
-               result = mode & UART_RSA_MSR_FIFO;
-       }
-
-       restore_flags(flags);
-       return result;
-}
-
-/* Attempts to turn off the RSA FIFO.  Returns zero on failure */
-static int disable_rsa(struct async_struct *info)
-{
-       unsigned char mode;
-       int result;
-       unsigned long flags;
-
-       save_flags(flags); cli();
-       mode = serial_inp(info, UART_RSA_MSR);
-       result = !(mode & UART_RSA_MSR_FIFO);
-
-       if (!result) {
-               serial_outp(info, UART_RSA_MSR, mode & ~UART_RSA_MSR_FIFO);
-               mode = serial_inp(info, UART_RSA_MSR);
-               result = !(mode & UART_RSA_MSR_FIFO);
-       }
-
-       restore_flags(flags);
-       return result;
-}
-#endif /* CONFIG_SERIAL_RSA */
-
-static int startup(struct async_struct * info)
-{
-       unsigned long flags;
-       int     retval=0;
-       void (*handler)(int, void *, struct pt_regs *);
-       struct serial_state *state= info->state;
-       unsigned long page;
-#ifdef CONFIG_SERIAL_MANY_PORTS
-       unsigned short ICP;
-#endif
-
-       page = get_zeroed_page(GFP_KERNEL);
-       if (!page)
-               return -ENOMEM;
-
-       save_flags(flags); cli();
-
-       if (info->flags & ASYNC_INITIALIZED) {
-               free_page(page);
-               goto errout;
-       }
-
-       if (!CONFIGURED_SERIAL_PORT(state) || !state->type) {
-               if (info->tty)
-                       set_bit(TTY_IO_ERROR, &info->tty->flags);
-               free_page(page);
-               goto errout;
-       }
-       if (info->xmit.buf)
-               free_page(page);
-       else
-               info->xmit.buf = (unsigned char *) page;
-
-#ifdef SERIAL_DEBUG_OPEN
-       printk("starting up ttys%d (irq %d)...", info->line, state->irq);
-#endif
-
-       if (uart_config[state->type].flags & UART_STARTECH) {
-               /* Wake up UART */
-               serial_outp(info, UART_LCR, 0xBF);
-               serial_outp(info, UART_EFR, UART_EFR_ECB);
-               /*
-                * Turn off LCR == 0xBF so we actually set the IER
-                * register on the XR16C850
-                */
-               serial_outp(info, UART_LCR, 0);
-               serial_outp(info, UART_IER, 0);
-               /*
-                * Now reset LCR so we can turn off the ECB bit
-                */
-               serial_outp(info, UART_LCR, 0xBF);
-               serial_outp(info, UART_EFR, 0);
-               /*
-                * For a XR16C850, we need to set the trigger levels
-                */
-               if (state->type == PORT_16850) {
-                       serial_outp(info, UART_FCTR, UART_FCTR_TRGD |
-                                       UART_FCTR_RX);
-                       serial_outp(info, UART_TRG, UART_TRG_96);
-                       serial_outp(info, UART_FCTR, UART_FCTR_TRGD |
-                                       UART_FCTR_TX);
-                       serial_outp(info, UART_TRG, UART_TRG_96);
-               }
-               serial_outp(info, UART_LCR, 0);
-       }
-
-       if (state->type == PORT_16750) {
-               /* Wake up UART */
-               serial_outp(info, UART_IER, 0);
-       }
-
-       if (state->type == PORT_16C950) {
-               /* Wake up and initialize UART */
-               info->ACR = 0;
-               serial_outp(info, UART_LCR, 0xBF);
-               serial_outp(info, UART_EFR, UART_EFR_ECB);
-               serial_outp(info, UART_IER, 0);
-               serial_outp(info, UART_LCR, 0);
-               serial_icr_write(info, UART_CSR, 0); /* Reset the UART */
-               serial_outp(info, UART_LCR, 0xBF);
-               serial_outp(info, UART_EFR, UART_EFR_ECB);
-               serial_outp(info, UART_LCR, 0);
-       }
-
-#ifdef CONFIG_SERIAL_RSA
-       /*
-        * If this is an RSA port, see if we can kick it up to the
-        * higher speed clock.
-        */
-       if (state->type == PORT_RSA) {
-               if (state->baud_base != SERIAL_RSA_BAUD_BASE &&
-                   enable_rsa(info))
-                       state->baud_base = SERIAL_RSA_BAUD_BASE;
-               if (state->baud_base == SERIAL_RSA_BAUD_BASE)
-                       serial_outp(info, UART_RSA_FRR, 0);
-       }
-#endif
-
-       /*
-        * Clear the FIFO buffers and disable them
-        * (they will be reenabled in change_speed())
-        */
-       if (uart_config[state->type].flags & UART_CLEAR_FIFO) {
-               serial_outp(info, UART_FCR, UART_FCR_ENABLE_FIFO);
-               serial_outp(info, UART_FCR, (UART_FCR_ENABLE_FIFO |
-                                            UART_FCR_CLEAR_RCVR |
-                                            UART_FCR_CLEAR_XMIT));
-               serial_outp(info, UART_FCR, 0);
-       }
-
-       /*
-        * Clear the interrupt registers.
-        */
-       (void) serial_inp(info, UART_LSR);
-       (void) serial_inp(info, UART_RX);
-       (void) serial_inp(info, UART_IIR);
-       (void) serial_inp(info, UART_MSR);
-
-       /*
-        * At this point there's no way the LSR could still be 0xFF;
-        * if it is, then bail out, because there's likely no UART
-        * here.
-        */
-       if (!(info->flags & ASYNC_BUGGY_UART) &&
-           (serial_inp(info, UART_LSR) == 0xff)) {
-               printk("ttyS%d: LSR safety check engaged!\n", state->line);
-               if (capable(CAP_SYS_ADMIN)) {
-                       if (info->tty)
-                               set_bit(TTY_IO_ERROR, &info->tty->flags);
-               } else
-                       retval = -ENODEV;
-               goto errout;
-       }
-       
-       /*
-        * Allocate the IRQ if necessary
-        */
-       if (state->irq && (!IRQ_ports[state->irq] ||
-                         !IRQ_ports[state->irq]->next_port)) {
-               if (IRQ_ports[state->irq]) {
-#ifdef CONFIG_SERIAL_SHARE_IRQ
-                       free_irq(state->irq, &IRQ_ports[state->irq]);
-#ifdef CONFIG_SERIAL_MULTIPORT                         
-                       if (rs_multiport[state->irq].port1)
-                               handler = rs_interrupt_multi;
-                       else
-#endif
-                               handler = rs_interrupt;
-#else
-                       retval = -EBUSY;
-                       goto errout;
-#endif /* CONFIG_SERIAL_SHARE_IRQ */
-               } else 
-                       handler = rs_interrupt_single;
-
-               retval = request_irq(state->irq, handler, SA_SHIRQ,
-                                    "serial", &IRQ_ports[state->irq]);
-               if (retval) {
-                       if (capable(CAP_SYS_ADMIN)) {
-                               if (info->tty)
-                                       set_bit(TTY_IO_ERROR,
-                                               &info->tty->flags);
-                               retval = 0;
-                       }
-                       goto errout;
-               }
-       }
-
-       /*
-        * Insert serial port into IRQ chain.
-        */
-       info->prev_port = 0;
-       info->next_port = IRQ_ports[state->irq];
-       if (info->next_port)
-               info->next_port->prev_port = info;
-       IRQ_ports[state->irq] = info;
-       figure_IRQ_timeout(state->irq);
-
-       /*
-        * Now, initialize the UART 
-        */
-       serial_outp(info, UART_LCR, UART_LCR_WLEN8);    /* reset DLAB */
-
-       info->MCR = 0;
-       if (info->tty->termios->c_cflag & CBAUD)
-               info->MCR = UART_MCR_DTR | UART_MCR_RTS;
-#ifdef CONFIG_SERIAL_MANY_PORTS
-       if (info->flags & ASYNC_FOURPORT) {
-               if (state->irq == 0)
-                       info->MCR |= UART_MCR_OUT1;
-       } else
-#endif
-       {
-               if (state->irq != 0)
-                       info->MCR |= UART_MCR_OUT2;
-       }
-       info->MCR |= ALPHA_KLUDGE_MCR;          /* Don't ask */
-       serial_outp(info, UART_MCR, info->MCR);
-       
-       /*
-        * Finally, enable interrupts
-        */
-       info->IER = UART_IER_MSI | UART_IER_RLSI | UART_IER_RDI;
-       serial_outp(info, UART_IER, info->IER); /* enable interrupts */
-       
-#ifdef CONFIG_SERIAL_MANY_PORTS
-       if (info->flags & ASYNC_FOURPORT) {
-               /* Enable interrupts on the AST Fourport board */
-               ICP = (info->port & 0xFE0) | 0x01F;
-               outb_p(0x80, ICP);
-               (void) inb_p(ICP);
-       }
-#endif
-
-       /*
-        * And clear the interrupt registers again for luck.
-        */
-       (void)serial_inp(info, UART_LSR);
-       (void)serial_inp(info, UART_RX);
-       (void)serial_inp(info, UART_IIR);
-       (void)serial_inp(info, UART_MSR);
-
-       if (info->tty)
-               clear_bit(TTY_IO_ERROR, &info->tty->flags);
-       info->xmit.head = info->xmit.tail = 0;
-
-       /*
-        * Set up serial timers...
-        */
-       mod_timer(&serial_timer, jiffies + 2*HZ/100);
-
-       /*
-        * Set up the tty->alt_speed kludge
-        */
-#if (LINUX_VERSION_CODE >= 131394) /* Linux 2.1.66 */
-       if (info->tty) {
-               if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI)
-                       info->tty->alt_speed = 57600;
-               if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI)
-                       info->tty->alt_speed = 115200;
-               if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_SHI)
-                       info->tty->alt_speed = 230400;
-               if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_WARP)
-                       info->tty->alt_speed = 460800;
-       }
-#endif
-       
-       /*
-        * and set the speed of the serial port
-        */
-       change_speed(info, 0);
-
-       info->flags |= ASYNC_INITIALIZED;
-       restore_flags(flags);
-       return 0;
-       
-errout:
-       restore_flags(flags);
-       return retval;
-}
-
-/*
- * This routine will shutdown a serial port; interrupts are disabled, and
- * DTR is dropped if the hangup on close termio flag is on.
- */
-static void shutdown(struct async_struct * info)
-{
-       unsigned long   flags;
-       struct serial_state *state;
-       int             retval;
-
-       if (!(info->flags & ASYNC_INITIALIZED))
-               return;
-
-       state = info->state;
-
-#ifdef SERIAL_DEBUG_OPEN
-       printk("Shutting down serial port %d (irq %d)....", info->line,
-              state->irq);
-#endif
-       
-       save_flags(flags); cli(); /* Disable interrupts */
-
-       /*
-        * clear delta_msr_wait queue to avoid mem leaks: we may free the irq
-        * here so the queue might never be waken up
-        */
-       wake_up_interruptible(&info->delta_msr_wait);
-       
-       /*
-        * First unlink the serial port from the IRQ chain...
-        */
-       if (info->next_port)
-               info->next_port->prev_port = info->prev_port;
-       if (info->prev_port)
-               info->prev_port->next_port = info->next_port;
-       else
-               IRQ_ports[state->irq] = info->next_port;
-       figure_IRQ_timeout(state->irq);
-       
-       /*
-        * Free the IRQ, if necessary
-        */
-       if (state->irq && (!IRQ_ports[state->irq] ||
-                         !IRQ_ports[state->irq]->next_port)) {
-               if (IRQ_ports[state->irq]) {
-                       free_irq(state->irq, &IRQ_ports[state->irq]);
-                       retval = request_irq(state->irq, rs_interrupt_single,
-                                            SA_SHIRQ, "serial",
-                                            &IRQ_ports[state->irq]);
-                       
-                       if (retval)
-                               printk("serial shutdown: request_irq: error %d"
-                                      "  Couldn't reacquire IRQ.\n", retval);
-               } else
-                       free_irq(state->irq, &IRQ_ports[state->irq]);
-       }
-
-       if (info->xmit.buf) {
-               unsigned long pg = (unsigned long) info->xmit.buf;
-               info->xmit.buf = 0;
-               free_page(pg);
-       }
-
-       info->IER = 0;
-       serial_outp(info, UART_IER, 0x00);      /* disable all intrs */
-#ifdef CONFIG_SERIAL_MANY_PORTS
-       if (info->flags & ASYNC_FOURPORT) {
-               /* reset interrupts on the AST Fourport board */
-               (void) inb((info->port & 0xFE0) | 0x01F);
-               info->MCR |= UART_MCR_OUT1;
-       } else
-#endif
-               info->MCR &= ~UART_MCR_OUT2;
-       info->MCR |= ALPHA_KLUDGE_MCR;          /* Don't ask */
-       
-       /* disable break condition */
-       serial_out(info, UART_LCR, serial_inp(info, UART_LCR) & ~UART_LCR_SBC);
-       
-       if (!info->tty || (info->tty->termios->c_cflag & HUPCL))
-               info->MCR &= ~(UART_MCR_DTR|UART_MCR_RTS);
-       serial_outp(info, UART_MCR, info->MCR);
-
-       /* disable FIFO's */    
-       serial_outp(info, UART_FCR, (UART_FCR_ENABLE_FIFO |
-                                    UART_FCR_CLEAR_RCVR |
-                                    UART_FCR_CLEAR_XMIT));
-       serial_outp(info, UART_FCR, 0);
-
-#ifdef CONFIG_SERIAL_RSA
-       /*
-        * Reset the RSA board back to 115kbps compat mode.
-        */
-       if ((state->type == PORT_RSA) &&
-           (state->baud_base == SERIAL_RSA_BAUD_BASE &&
-            disable_rsa(info)))
-               state->baud_base = SERIAL_RSA_BAUD_BASE_LO;
-#endif
-       
-
-       (void)serial_in(info, UART_RX);    /* read data port to reset things */
-       
-       if (info->tty)
-               set_bit(TTY_IO_ERROR, &info->tty->flags);
-
-       if (uart_config[info->state->type].flags & UART_STARTECH) {
-               /* Arrange to enter sleep mode */
-               serial_outp(info, UART_LCR, 0xBF);
-               serial_outp(info, UART_EFR, UART_EFR_ECB);
-               serial_outp(info, UART_LCR, 0);
-               serial_outp(info, UART_IER, UART_IERX_SLEEP);
-               serial_outp(info, UART_LCR, 0xBF);
-               serial_outp(info, UART_EFR, 0);
-               serial_outp(info, UART_LCR, 0);
-       }
-       if (info->state->type == PORT_16750) {
-               /* Arrange to enter sleep mode */
-               serial_outp(info, UART_IER, UART_IERX_SLEEP);
-       }
-       info->flags &= ~ASYNC_INITIALIZED;
-       restore_flags(flags);
-}
-
-#if (LINUX_VERSION_CODE < 131394) /* Linux 2.1.66 */
-static int baud_table[] = {
-       0, 50, 75, 110, 134, 150, 200, 300,
-       600, 1200, 1800, 2400, 4800, 9600, 19200,
-       38400, 57600, 115200, 230400, 460800, 0 };
-
-static int tty_get_baud_rate(struct tty_struct *tty)
-{
-       struct async_struct * info = (struct async_struct *)tty->driver_data;
-       unsigned int cflag, i;
-
-       cflag = tty->termios->c_cflag;
-
-       i = cflag & CBAUD;
-       if (i & CBAUDEX) {
-               i &= ~CBAUDEX;
-               if (i < 1 || i > 2) 
-                       tty->termios->c_cflag &= ~CBAUDEX;
-               else
-                       i += 15;
-       }
-       if (i == 15) {
-               if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI)
-                       i += 1;
-               if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI)
-                       i += 2;
-       }
-       return baud_table[i];
-}
-#endif
-
-/*
- * This routine is called to set the UART divisor registers to match
- * the specified baud rate for a serial port.
- */
-static void change_speed(struct async_struct *info,
-                        struct termios *old_termios)
-{
-       int     quot = 0, baud_base, baud;
-       unsigned cflag, cval, fcr = 0;
-       int     bits;
-       unsigned long   flags;
-
-       if (!info->tty || !info->tty->termios)
-               return;
-       cflag = info->tty->termios->c_cflag;
-       if (!CONFIGURED_SERIAL_PORT(info))
-               return;
-
-       /* byte size and parity */
-       switch (cflag & CSIZE) {
-             case CS5: cval = 0x00; bits = 7; break;
-             case CS6: cval = 0x01; bits = 8; break;
-             case CS7: cval = 0x02; bits = 9; break;
-             case CS8: cval = 0x03; bits = 10; break;
-             /* Never happens, but GCC is too dumb to figure it out */
-             default:  cval = 0x00; bits = 7; break;
-             }
-       if (cflag & CSTOPB) {
-               cval |= 0x04;
-               bits++;
-       }
-       if (cflag & PARENB) {
-               cval |= UART_LCR_PARITY;
-               bits++;
-       }
-       if (!(cflag & PARODD))
-               cval |= UART_LCR_EPAR;
-#ifdef CMSPAR
-       if (cflag & CMSPAR)
-               cval |= UART_LCR_SPAR;
-#endif
-
-       /* Determine divisor based on baud rate */
-       baud = tty_get_baud_rate(info->tty);
-       if (!baud)
-               baud = 9600;    /* B0 transition handled in rs_set_termios */
-#ifdef CONFIG_SERIAL_RSA
-       if ((info->state->type == PORT_RSA) &&
-           (info->state->baud_base != SERIAL_RSA_BAUD_BASE) &&
-           enable_rsa(info))
-               info->state->baud_base = SERIAL_RSA_BAUD_BASE;
-#endif
-       baud_base = info->state->baud_base;
-       if (info->state->type == PORT_16C950) {
-               if (baud <= baud_base)
-                       serial_icr_write(info, UART_TCR, 0);
-               else if (baud <= 2*baud_base) {
-                       serial_icr_write(info, UART_TCR, 0x8);
-                       baud_base = baud_base * 2;
-               } else if (baud <= 4*baud_base) {
-                       serial_icr_write(info, UART_TCR, 0x4);
-                       baud_base = baud_base * 4;
-               } else
-                       serial_icr_write(info, UART_TCR, 0);
-       }
-       if (baud == 38400 &&
-           ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_CUST))
-               quot = info->state->custom_divisor;
-       else {
-               if (baud == 134)
-                       /* Special case since 134 is really 134.5 */
-                       quot = (2*baud_base / 269);
-               else if (baud)
-                       quot = baud_base / baud;
-       }
-       /* If the quotient is zero refuse the change */
-       if (!quot && old_termios) {
-               info->tty->termios->c_cflag &= ~CBAUD;
-               info->tty->termios->c_cflag |= (old_termios->c_cflag & CBAUD);
-               baud = tty_get_baud_rate(info->tty);
-               if (!baud)
-                       baud = 9600;
-               if (baud == 38400 &&
-                   ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_CUST))
-                       quot = info->state->custom_divisor;
-               else {
-                       if (baud == 134)
-                               /* Special case since 134 is really 134.5 */
-                               quot = (2*baud_base / 269);
-                       else if (baud)
-                               quot = baud_base / baud;
-               }
-       }
-       /* As a last resort, if the quotient is zero, default to 9600 bps */
-       if (!quot)
-               quot = baud_base / 9600;
-       /*
-        * Work around a bug in the Oxford Semiconductor 952 rev B
-        * chip which causes it to seriously miscalculate baud rates
-        * when DLL is 0.
-        */
-       if (((quot & 0xFF) == 0) && (info->state->type == PORT_16C950) &&
-           (info->state->revision == 0x5201))
-               quot++;
-       
-       info->quot = quot;
-       info->timeout = ((info->xmit_fifo_size*HZ*bits*quot) / baud_base);
-       info->timeout += HZ/50;         /* Add .02 seconds of slop */
-
-       /* Set up FIFO's */
-       if (uart_config[info->state->type].flags & UART_USE_FIFO) {
-               if ((info->state->baud_base / quot) < 2400)
-                       fcr = UART_FCR_ENABLE_FIFO | UART_FCR_TRIGGER_1;
-#ifdef CONFIG_SERIAL_RSA
-               else if (info->state->type == PORT_RSA)
-                       fcr = UART_FCR_ENABLE_FIFO | UART_FCR_TRIGGER_14;
-#endif
-               else
-                       fcr = UART_FCR_ENABLE_FIFO | UART_FCR_TRIGGER_8;
-       }
-       if (info->state->type == PORT_16750)
-               fcr |= UART_FCR7_64BYTE;
-       
-       /* CTS flow control flag and modem status interrupts */
-       info->IER &= ~UART_IER_MSI;
-       if (info->flags & ASYNC_HARDPPS_CD)
-               info->IER |= UART_IER_MSI;
-       if (cflag & CRTSCTS) {
-               info->flags |= ASYNC_CTS_FLOW;
-               info->IER |= UART_IER_MSI;
-       } else
-               info->flags &= ~ASYNC_CTS_FLOW;
-       if (cflag & CLOCAL)
-               info->flags &= ~ASYNC_CHECK_CD;
-       else {
-               info->flags |= ASYNC_CHECK_CD;
-               info->IER |= UART_IER_MSI;
-       }
-       serial_out(info, UART_IER, info->IER);
-
-       /*
-        * Set up parity check flag
-        */
-#define RELEVANT_IFLAG(iflag) (iflag & (IGNBRK|BRKINT|IGNPAR|PARMRK|INPCK))
-
-       info->read_status_mask = UART_LSR_OE | UART_LSR_THRE | UART_LSR_DR;
-       if (I_INPCK(info->tty))
-               info->read_status_mask |= UART_LSR_FE | UART_LSR_PE;
-       if (I_BRKINT(info->tty) || I_PARMRK(info->tty))
-               info->read_status_mask |= UART_LSR_BI;
-       
-       /*
-        * Characters to ignore
-        */
-       info->ignore_status_mask = 0;
-       if (I_IGNPAR(info->tty))
-               info->ignore_status_mask |= UART_LSR_PE | UART_LSR_FE;
-       if (I_IGNBRK(info->tty)) {
-               info->ignore_status_mask |= UART_LSR_BI;
-               /*
-                * If we're ignore parity and break indicators, ignore 
-                * overruns too.  (For real raw support).
-                */
-               if (I_IGNPAR(info->tty))
-                       info->ignore_status_mask |= UART_LSR_OE;
-       }
-       /*
-        * !!! ignore all characters if CREAD is not set
-        */
-       if ((cflag & CREAD) == 0)
-               info->ignore_status_mask |= UART_LSR_DR;
-       save_flags(flags); cli();
-       if (uart_config[info->state->type].flags & UART_STARTECH) {
-               serial_outp(info, UART_LCR, 0xBF);
-               serial_outp(info, UART_EFR,
-                           (cflag & CRTSCTS) ? UART_EFR_CTS : 0);
-       }
-       serial_outp(info, UART_LCR, cval | UART_LCR_DLAB);      /* set DLAB */
-       serial_outp(info, UART_DLL, quot & 0xff);       /* LS of divisor */
-       serial_outp(info, UART_DLM, quot >> 8);         /* MS of divisor */
-       if (info->state->type == PORT_16750)
-               serial_outp(info, UART_FCR, fcr);       /* set fcr */
-       serial_outp(info, UART_LCR, cval);              /* reset DLAB */
-       info->LCR = cval;                               /* Save LCR */
-       if (info->state->type != PORT_16750) {
-               if (fcr & UART_FCR_ENABLE_FIFO) {
-                       /* emulated UARTs (Lucent Venus 167x) need two steps */
-                       serial_outp(info, UART_FCR, UART_FCR_ENABLE_FIFO);
-               }
-               serial_outp(info, UART_FCR, fcr);       /* set fcr */
-       }
-       restore_flags(flags);
-}
-
-static void rs_put_char(struct tty_struct *tty, unsigned char ch)
-{
-       struct async_struct *info = (struct async_struct *)tty->driver_data;
-       unsigned long flags;
-
-       if (serial_paranoia_check(info, tty->device, "rs_put_char"))
-               return;
-
-       if (!tty || !info->xmit.buf)
-               return;
-
-       save_flags(flags); cli();
-       if (CIRC_SPACE(info->xmit.head,
-                      info->xmit.tail,
-                      SERIAL_XMIT_SIZE) == 0) {
-               restore_flags(flags);
-               return;
-       }
-
-       info->xmit.buf[info->xmit.head] = ch;
-       info->xmit.head = (info->xmit.head + 1) & (SERIAL_XMIT_SIZE-1);
-       restore_flags(flags);
-}
-
-static void rs_flush_chars(struct tty_struct *tty)
-{
-       struct async_struct *info = (struct async_struct *)tty->driver_data;
-       unsigned long flags;
-                               
-       if (serial_paranoia_check(info, tty->device, "rs_flush_chars"))
-               return;
-
-       if (info->xmit.head == info->xmit.tail
-           || tty->stopped
-           || tty->hw_stopped
-           || !info->xmit.buf)
-               return;
-
-       save_flags(flags); cli();
-       info->IER |= UART_IER_THRI;
-       serial_out(info, UART_IER, info->IER);
-       restore_flags(flags);
-}
-
-static int rs_write(struct tty_struct * tty, int from_user,
-                   const unsigned char *buf, int count)
-{
-       int     c, ret = 0;
-       struct async_struct *info = (struct async_struct *)tty->driver_data;
-       unsigned long flags;
-                               
-       if (serial_paranoia_check(info, tty->device, "rs_write"))
-               return 0;
-
-       if (!tty || !info->xmit.buf || !tmp_buf)
-               return 0;
-
-       save_flags(flags);
-       if (from_user) {
-               down(&tmp_buf_sem);
-               while (1) {
-                       int c1;
-                       c = CIRC_SPACE_TO_END(info->xmit.head,
-                                             info->xmit.tail,
-                                             SERIAL_XMIT_SIZE);
-                       if (count < c)
-                               c = count;
-                       if (c <= 0)
-                               break;
-
-                       c -= copy_from_user(tmp_buf, buf, c);
-                       if (!c) {
-                               if (!ret)
-                                       ret = -EFAULT;
-                               break;
-                       }
-                       cli();
-                       c1 = CIRC_SPACE_TO_END(info->xmit.head,
-                                              info->xmit.tail,
-                                              SERIAL_XMIT_SIZE);
-                       if (c1 < c)
-                               c = c1;
-                       memcpy(info->xmit.buf + info->xmit.head, tmp_buf, c);
-                       info->xmit.head = ((info->xmit.head + c) &
-                                          (SERIAL_XMIT_SIZE-1));
-                       restore_flags(flags);
-                       buf += c;
-                       count -= c;
-                       ret += c;
-               }
-               up(&tmp_buf_sem);
-       } else {
-               cli();
-               while (1) {
-                       c = CIRC_SPACE_TO_END(info->xmit.head,
-                                             info->xmit.tail,
-                                             SERIAL_XMIT_SIZE);
-                       if (count < c)
-                               c = count;
-                       if (c <= 0) {
-                               break;
-                       }
-                       memcpy(info->xmit.buf + info->xmit.head, buf, c);
-                       info->xmit.head = ((info->xmit.head + c) &
-                                          (SERIAL_XMIT_SIZE-1));
-                       buf += c;
-                       count -= c;
-                       ret += c;
-               }
-               restore_flags(flags);
-       }
-       if (info->xmit.head != info->xmit.tail
-           && !tty->stopped
-           && !tty->hw_stopped
-           && !(info->IER & UART_IER_THRI)) {
-               info->IER |= UART_IER_THRI;
-               serial_out(info, UART_IER, info->IER);
-       }
-       return ret;
-}
-
-static int rs_write_room(struct tty_struct *tty)
-{
-       struct async_struct *info = (struct async_struct *)tty->driver_data;
-
-       if (serial_paranoia_check(info, tty->device, "rs_write_room"))
-               return 0;
-       return CIRC_SPACE(info->xmit.head, info->xmit.tail, SERIAL_XMIT_SIZE);
-}
-
-static int rs_chars_in_buffer(struct tty_struct *tty)
-{
-       struct async_struct *info = (struct async_struct *)tty->driver_data;
-                               
-       if (serial_paranoia_check(info, tty->device, "rs_chars_in_buffer"))
-               return 0;
-       return CIRC_CNT(info->xmit.head, info->xmit.tail, SERIAL_XMIT_SIZE);
-}
-
-static void rs_flush_buffer(struct tty_struct *tty)
-{
-       struct async_struct *info = (struct async_struct *)tty->driver_data;
-       unsigned long flags;
-       
-       if (serial_paranoia_check(info, tty->device, "rs_flush_buffer"))
-               return;
-       save_flags(flags); cli();
-       info->xmit.head = info->xmit.tail = 0;
-       restore_flags(flags);
-       wake_up_interruptible(&tty->write_wait);
-#ifdef SERIAL_HAVE_POLL_WAIT
-       wake_up_interruptible(&tty->poll_wait);
-#endif
-       if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
-           tty->ldisc.write_wakeup)
-               (tty->ldisc.write_wakeup)(tty);
-}
-
-/*
- * This function is used to send a high-priority XON/XOFF character to
- * the device
- */
-static void rs_send_xchar(struct tty_struct *tty, char ch)
-{
-       struct async_struct *info = (struct async_struct *)tty->driver_data;
-
-       if (serial_paranoia_check(info, tty->device, "rs_send_char"))
-               return;
-
-       info->x_char = ch;
-       if (ch) {
-               /* Make sure transmit interrupts are on */
-               info->IER |= UART_IER_THRI;
-               serial_out(info, UART_IER, info->IER);
-       }
-}
-
-/*
- * ------------------------------------------------------------
- * rs_throttle()
- * 
- * This routine is called by the upper-layer tty layer to signal that
- * incoming characters should be throttled.
- * ------------------------------------------------------------
- */
-static void rs_throttle(struct tty_struct * tty)
-{
-       struct async_struct *info = (struct async_struct *)tty->driver_data;
-       unsigned long flags;
-#ifdef SERIAL_DEBUG_THROTTLE
-       char    buf[64];
-       
-       printk("throttle %s: %d....\n", tty_name(tty, buf),
-              tty->ldisc.chars_in_buffer(tty));
-#endif
-
-       if (serial_paranoia_check(info, tty->device, "rs_throttle"))
-               return;
-       
-       if (I_IXOFF(tty))
-               rs_send_xchar(tty, STOP_CHAR(tty));
-
-       if (tty->termios->c_cflag & CRTSCTS)
-               info->MCR &= ~UART_MCR_RTS;
-
-       save_flags(flags); cli();
-       serial_out(info, UART_MCR, info->MCR);
-       restore_flags(flags);
-}
-
-static void rs_unthrottle(struct tty_struct * tty)
-{
-       struct async_struct *info = (struct async_struct *)tty->driver_data;
-       unsigned long flags;
-#ifdef SERIAL_DEBUG_THROTTLE
-       char    buf[64];
-       
-       printk("unthrottle %s: %d....\n", tty_name(tty, buf),
-              tty->ldisc.chars_in_buffer(tty));
-#endif
-
-       if (serial_paranoia_check(info, tty->device, "rs_unthrottle"))
-               return;
-       
-       if (I_IXOFF(tty)) {
-               if (info->x_char)
-                       info->x_char = 0;
-               else
-                       rs_send_xchar(tty, START_CHAR(tty));
-       }
-       if (tty->termios->c_cflag & CRTSCTS)
-               info->MCR |= UART_MCR_RTS;
-       save_flags(flags); cli();
-       serial_out(info, UART_MCR, info->MCR);
-       restore_flags(flags);
-}
-
-/*
- * ------------------------------------------------------------
- * rs_ioctl() and friends
- * ------------------------------------------------------------
- */
-
-static int get_serial_info(struct async_struct * info,
-                          struct serial_struct * retinfo)
-{
-       struct serial_struct tmp;
-       struct serial_state *state = info->state;
-   
-       if (!retinfo)
-               return -EFAULT;
-       memset(&tmp, 0, sizeof(tmp));
-       tmp.type = state->type;
-       tmp.line = state->line;
-       tmp.port = state->port;
-       if (HIGH_BITS_OFFSET)
-               tmp.port_high = state->port >> HIGH_BITS_OFFSET;
-       else
-               tmp.port_high = 0;
-       tmp.irq = state->irq;
-       tmp.flags = state->flags;
-       tmp.xmit_fifo_size = state->xmit_fifo_size;
-       tmp.baud_base = state->baud_base;
-       tmp.close_delay = state->close_delay;
-       tmp.closing_wait = state->closing_wait;
-       tmp.custom_divisor = state->custom_divisor;
-       tmp.hub6 = state->hub6;
-       tmp.io_type = state->io_type;
-       if (copy_to_user(retinfo,&tmp,sizeof(*retinfo)))
-               return -EFAULT;
-       return 0;
-}
-
-static int set_serial_info(struct async_struct * info,
-                          struct serial_struct * new_info)
-{
-       struct serial_struct new_serial;
-       struct serial_state old_state, *state;
-       unsigned int            i,change_irq,change_port;
-       int                     retval = 0;
-       unsigned long           new_port;
-
-       if (copy_from_user(&new_serial,new_info,sizeof(new_serial)))
-               return -EFAULT;
-       state = info->state;
-       old_state = *state;
-
-       new_port = new_serial.port;
-       if (HIGH_BITS_OFFSET)
-               new_port += (unsigned long) new_serial.port_high << HIGH_BITS_OFFSET;
-
-       change_irq = new_serial.irq != state->irq;
-       change_port = (new_port != ((int) state->port)) ||
-               (new_serial.hub6 != state->hub6);
-  
-       if (!capable(CAP_SYS_ADMIN)) {
-               if (change_irq || change_port ||
-                   (new_serial.baud_base != state->baud_base) ||
-                   (new_serial.type != state->type) ||
-                   (new_serial.close_delay != state->close_delay) ||
-                   (new_serial.xmit_fifo_size != state->xmit_fifo_size) ||
-                   ((new_serial.flags & ~ASYNC_USR_MASK) !=
-                    (state->flags & ~ASYNC_USR_MASK)))
-                       return -EPERM;
-               state->flags = ((state->flags & ~ASYNC_USR_MASK) |
-                              (new_serial.flags & ASYNC_USR_MASK));
-               info->flags = ((info->flags & ~ASYNC_USR_MASK) |
-                              (new_serial.flags & ASYNC_USR_MASK));
-               state->custom_divisor = new_serial.custom_divisor;
-               goto check_and_exit;
-       }
-
-       new_serial.irq = irq_cannonicalize(new_serial.irq);
-
-       if ((new_serial.irq >= NR_IRQS) || (new_serial.irq < 0) || 
-           (new_serial.baud_base < 9600)|| (new_serial.type < PORT_UNKNOWN) ||
-           (new_serial.type > PORT_MAX) || (new_serial.type == PORT_CIRRUS) ||
-           (new_serial.type == PORT_STARTECH)) {
-               return -EINVAL;
-       }
-
-       if ((new_serial.type != state->type) ||
-           (new_serial.xmit_fifo_size <= 0))
-               new_serial.xmit_fifo_size =
-                       uart_config[new_serial.type].dfl_xmit_fifo_size;
-
-       /* Make sure address is not already in use */
-       if (new_serial.type) {
-               for (i = 0 ; i < NR_PORTS; i++)
-                       if ((state != &rs_table[i]) &&
-                           (rs_table[i].port == new_port) &&
-                           rs_table[i].type)
-                               return -EADDRINUSE;
-       }
-
-       if ((change_port || change_irq) && (state->count > 1))
-               return -EBUSY;
-
-       /*
-        * OK, past this point, all the error checking has been done.
-        * At this point, we start making changes.....
-        */
-
-       state->baud_base = new_serial.baud_base;
-       state->flags = ((state->flags & ~ASYNC_FLAGS) |
-                       (new_serial.flags & ASYNC_FLAGS));
-       info->flags = ((state->flags & ~ASYNC_INTERNAL_FLAGS) |
-                      (info->flags & ASYNC_INTERNAL_FLAGS));
-       state->custom_divisor = new_serial.custom_divisor;
-       state->close_delay = new_serial.close_delay * HZ/100;
-       state->closing_wait = new_serial.closing_wait * HZ/100;
-#if (LINUX_VERSION_CODE > 0x20100)
-       info->tty->low_latency = (info->flags & ASYNC_LOW_LATENCY) ? 1 : 0;
-#endif
-       info->xmit_fifo_size = state->xmit_fifo_size =
-               new_serial.xmit_fifo_size;
-
-       if ((state->type != PORT_UNKNOWN) && state->port) {
-#ifdef CONFIG_SERIAL_RSA
-               if (old_state.type == PORT_RSA)
-                       release_region(state->port + UART_RSA_BASE, 16);
-               else
-#endif
-               release_region(state->port,8);
-       }
-       state->type = new_serial.type;
-       if (change_port || change_irq) {
-               /*
-                * We need to shutdown the serial port at the old
-                * port/irq combination.
-                */
-               shutdown(info);
-               state->irq = new_serial.irq;
-               info->port = state->port = new_port;
-               info->hub6 = state->hub6 = new_serial.hub6;
-               if (info->hub6)
-                       info->io_type = state->io_type = SERIAL_IO_HUB6;
-               else if (info->io_type == SERIAL_IO_HUB6)
-                       info->io_type = state->io_type = SERIAL_IO_PORT;
-       }
-       if ((state->type != PORT_UNKNOWN) && state->port) {
-#ifdef CONFIG_SERIAL_RSA
-               if (state->type == PORT_RSA)
-                       request_region(state->port + UART_RSA_BASE,
-                                      16, "serial_rsa(set)");
-               else
-#endif
-                       request_region(state->port,8,"serial(set)");
-       }
-
-       
-check_and_exit:
-       if (!state->port || !state->type)
-               return 0;
-       if (info->flags & ASYNC_INITIALIZED) {
-               if (((old_state.flags & ASYNC_SPD_MASK) !=
-                    (state->flags & ASYNC_SPD_MASK)) ||
-                   (old_state.custom_divisor != state->custom_divisor)) {
-#if (LINUX_VERSION_CODE >= 131394) /* Linux 2.1.66 */
-                       if ((state->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI)
-                               info->tty->alt_speed = 57600;
-                       if ((state->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI)
-                               info->tty->alt_speed = 115200;
-                       if ((state->flags & ASYNC_SPD_MASK) == ASYNC_SPD_SHI)
-                               info->tty->alt_speed = 230400;
-                       if ((state->flags & ASYNC_SPD_MASK) == ASYNC_SPD_WARP)
-                               info->tty->alt_speed = 460800;
-#endif
-                       change_speed(info, 0);
-               }
-       } else
-               retval = startup(info);
-       return retval;
-}
-
-
-/*
- * get_lsr_info - get line status register info
- *
- * Purpose: Let user call ioctl() to get info when the UART physically
- *         is emptied.  On bus types like RS485, the transmitter must
- *         release the bus after transmitting. This must be done when
- *         the transmit shift register is empty, not be done when the
- *         transmit holding register is empty.  This functionality
- *         allows an RS485 driver to be written in user space. 
- */
-static int get_lsr_info(struct async_struct * info, unsigned int *value)
-{
-       unsigned char status;
-       unsigned int result;
-       unsigned long flags;
-
-       save_flags(flags); cli();
-       status = serial_in(info, UART_LSR);
-       restore_flags(flags);
-       result = ((status & UART_LSR_TEMT) ? TIOCSER_TEMT : 0);
-
-       /*
-        * If we're about to load something into the transmit
-        * register, we'll pretend the transmitter isn't empty to
-        * avoid a race condition (depending on when the transmit
-        * interrupt happens).
-        */
-       if (info->x_char || 
-           ((CIRC_CNT(info->xmit.head, info->xmit.tail,
-                      SERIAL_XMIT_SIZE) > 0) &&
-            !info->tty->stopped && !info->tty->hw_stopped))
-               result &= ~TIOCSER_TEMT;
-
-       if (copy_to_user(value, &result, sizeof(int)))
-               return -EFAULT;
-       return 0;
-}
-
-
-static int get_modem_info(struct async_struct * info, unsigned int *value)
-{
-       unsigned char control, status;
-       unsigned int result;
-       unsigned long flags;
-
-       control = info->MCR;
-       save_flags(flags); cli();
-       status = serial_in(info, UART_MSR);
-       restore_flags(flags);
-       result =  ((control & UART_MCR_RTS) ? TIOCM_RTS : 0)
-               | ((control & UART_MCR_DTR) ? TIOCM_DTR : 0)
-#ifdef TIOCM_OUT1
-               | ((control & UART_MCR_OUT1) ? TIOCM_OUT1 : 0)
-               | ((control & UART_MCR_OUT2) ? TIOCM_OUT2 : 0)
-#endif
-               | ((status  & UART_MSR_DCD) ? TIOCM_CAR : 0)
-               | ((status  & UART_MSR_RI) ? TIOCM_RNG : 0)
-               | ((status  & UART_MSR_DSR) ? TIOCM_DSR : 0)
-               | ((status  & UART_MSR_CTS) ? TIOCM_CTS : 0);
-
-       if (copy_to_user(value, &result, sizeof(int)))
-               return -EFAULT;
-       return 0;
-}
-
-static int set_modem_info(struct async_struct * info, unsigned int cmd,
-                         unsigned int *value)
-{
-       unsigned int arg;
-       unsigned long flags;
-
-       if (copy_from_user(&arg, value, sizeof(int)))
-               return -EFAULT;
-
-       switch (cmd) {
-       case TIOCMBIS: 
-               if (arg & TIOCM_RTS)
-                       info->MCR |= UART_MCR_RTS;
-               if (arg & TIOCM_DTR)
-                       info->MCR |= UART_MCR_DTR;
-#ifdef TIOCM_OUT1
-               if (arg & TIOCM_OUT1)
-                       info->MCR |= UART_MCR_OUT1;
-               if (arg & TIOCM_OUT2)
-                       info->MCR |= UART_MCR_OUT2;
-#endif
-               if (arg & TIOCM_LOOP)
-                       info->MCR |= UART_MCR_LOOP;
-               break;
-       case TIOCMBIC:
-               if (arg & TIOCM_RTS)
-                       info->MCR &= ~UART_MCR_RTS;
-               if (arg & TIOCM_DTR)
-                       info->MCR &= ~UART_MCR_DTR;
-#ifdef TIOCM_OUT1
-               if (arg & TIOCM_OUT1)
-                       info->MCR &= ~UART_MCR_OUT1;
-               if (arg & TIOCM_OUT2)
-                       info->MCR &= ~UART_MCR_OUT2;
-#endif
-               if (arg & TIOCM_LOOP)
-                       info->MCR &= ~UART_MCR_LOOP;
-               break;
-       case TIOCMSET:
-               info->MCR = ((info->MCR & ~(UART_MCR_RTS |
-#ifdef TIOCM_OUT1
-                                           UART_MCR_OUT1 |
-                                           UART_MCR_OUT2 |
-#endif
-                                           UART_MCR_LOOP |
-                                           UART_MCR_DTR))
-                            | ((arg & TIOCM_RTS) ? UART_MCR_RTS : 0)
-#ifdef TIOCM_OUT1
-                            | ((arg & TIOCM_OUT1) ? UART_MCR_OUT1 : 0)
-                            | ((arg & TIOCM_OUT2) ? UART_MCR_OUT2 : 0)
-#endif
-                            | ((arg & TIOCM_LOOP) ? UART_MCR_LOOP : 0)
-                            | ((arg & TIOCM_DTR) ? UART_MCR_DTR : 0));
-               break;
-       default:
-               return -EINVAL;
-       }
-       save_flags(flags); cli();
-       info->MCR |= ALPHA_KLUDGE_MCR;          /* Don't ask */
-       serial_out(info, UART_MCR, info->MCR);
-       restore_flags(flags);
-       return 0;
-}
-
-static int do_autoconfig(struct async_struct * info)
-{
-       int irq, retval;
-       
-       if (!capable(CAP_SYS_ADMIN))
-               return -EPERM;
-       
-       if (info->state->count > 1)
-               return -EBUSY;
-       
-       shutdown(info);
-
-       autoconfig(info->state);
-       if ((info->state->flags & ASYNC_AUTO_IRQ) &&
-           (info->state->port != 0  || info->state->iomem_base != 0) &&
-           (info->state->type != PORT_UNKNOWN)) {
-               irq = detect_uart_irq(info->state);
-               if (irq > 0)
-                       info->state->irq = irq;
-       }
-
-       retval = startup(info);
-       if (retval)
-               return retval;
-       return 0;
-}
-
-/*
- * rs_break() --- routine which turns the break handling on or off
- */
-#if (LINUX_VERSION_CODE < 131394) /* Linux 2.1.66 */
-static void send_break(        struct async_struct * info, int duration)
-{
-       if (!CONFIGURED_SERIAL_PORT(info))
-               return;
-       current->state = TASK_INTERRUPTIBLE;
-       current->timeout = jiffies + duration;
-       cli();
-       info->LCR |= UART_LCR_SBC;
-       serial_out(info, UART_LCR, info->LCR);
-       schedule();
-       info->LCR &= ~UART_LCR_SBC;
-       serial_out(info, UART_LCR, info->LCR);
-       sti();
-}
-#else
-static void rs_break(struct tty_struct *tty, int break_state)
-{
-       struct async_struct * info = (struct async_struct *)tty->driver_data;
-       unsigned long flags;
-       
-       if (serial_paranoia_check(info, tty->device, "rs_break"))
-               return;
-
-       if (!CONFIGURED_SERIAL_PORT(info))
-               return;
-       save_flags(flags); cli();
-       if (break_state == -1)
-               info->LCR |= UART_LCR_SBC;
-       else
-               info->LCR &= ~UART_LCR_SBC;
-       serial_out(info, UART_LCR, info->LCR);
-       restore_flags(flags);
-}
-#endif
-
-#ifdef CONFIG_SERIAL_MULTIPORT
-static int get_multiport_struct(struct async_struct * info,
-                               struct serial_multiport_struct *retinfo)
-{
-       struct serial_multiport_struct ret;
-       struct rs_multiport_struct *multi;
-       
-       multi = &rs_multiport[info->state->irq];
-
-       ret.port_monitor = multi->port_monitor;
-       
-       ret.port1 = multi->port1;
-       ret.mask1 = multi->mask1;
-       ret.match1 = multi->match1;
-       
-       ret.port2 = multi->port2;
-       ret.mask2 = multi->mask2;
-       ret.match2 = multi->match2;
-       
-       ret.port3 = multi->port3;
-       ret.mask3 = multi->mask3;
-       ret.match3 = multi->match3;
-       
-       ret.port4 = multi->port4;
-       ret.mask4 = multi->mask4;
-       ret.match4 = multi->match4;
-
-       ret.irq = info->state->irq;
-
-       if (copy_to_user(retinfo,&ret,sizeof(*retinfo)))
-               return -EFAULT;
-       return 0;
-}
-
-static int set_multiport_struct(struct async_struct * info,
-                               struct serial_multiport_struct *in_multi)
-{
-       struct serial_multiport_struct new_multi;
-       struct rs_multiport_struct *multi;
-       struct serial_state *state;
-       int     was_multi, now_multi;
-       int     retval;
-       void (*handler)(int, void *, struct pt_regs *);
-
-       if (!capable(CAP_SYS_ADMIN))
-               return -EPERM;
-       state = info->state;
-       
-       if (copy_from_user(&new_multi, in_multi,
-                          sizeof(struct serial_multiport_struct)))
-               return -EFAULT;
-       
-       if (new_multi.irq != state->irq || state->irq == 0 ||
-           !IRQ_ports[state->irq])
-               return -EINVAL;
-
-       multi = &rs_multiport[state->irq];
-       was_multi = (multi->port1 != 0);
-       
-       multi->port_monitor = new_multi.port_monitor;
-       
-       if (multi->port1)
-               release_region(multi->port1,1);
-       multi->port1 = new_multi.port1;
-       multi->mask1 = new_multi.mask1;
-       multi->match1 = new_multi.match1;
-       if (multi->port1)
-               request_region(multi->port1,1,"serial(multiport1)");
-
-       if (multi->port2)
-               release_region(multi->port2,1);
-       multi->port2 = new_multi.port2;
-       multi->mask2 = new_multi.mask2;
-       multi->match2 = new_multi.match2;
-       if (multi->port2)
-               request_region(multi->port2,1,"serial(multiport2)");
-
-       if (multi->port3)
-               release_region(multi->port3,1);
-       multi->port3 = new_multi.port3;
-       multi->mask3 = new_multi.mask3;
-       multi->match3 = new_multi.match3;
-       if (multi->port3)
-               request_region(multi->port3,1,"serial(multiport3)");
-
-       if (multi->port4)
-               release_region(multi->port4,1);
-       multi->port4 = new_multi.port4;
-       multi->mask4 = new_multi.mask4;
-       multi->match4 = new_multi.match4;
-       if (multi->port4)
-               request_region(multi->port4,1,"serial(multiport4)");
-
-       now_multi = (multi->port1 != 0);
-       
-       if (IRQ_ports[state->irq]->next_port &&
-           (was_multi != now_multi)) {
-               free_irq(state->irq, &IRQ_ports[state->irq]);
-               if (now_multi)
-                       handler = rs_interrupt_multi;
-               else
-                       handler = rs_interrupt;
-
-               retval = request_irq(state->irq, handler, SA_SHIRQ,
-                                    "serial", &IRQ_ports[state->irq]);
-               if (retval) {
-                       printk("Couldn't reallocate serial interrupt "
-                              "driver!!\n");
-               }
-       }
-       return 0;
-}
-#endif
-
-static int rs_ioctl(struct tty_struct *tty, struct file * file,
-                   unsigned int cmd, unsigned long arg)
-{
-       struct async_struct * info = (struct async_struct *)tty->driver_data;
-       struct async_icount cprev, cnow;        /* kernel counter temps */
-       struct serial_icounter_struct icount;
-       unsigned long flags;
-#if (LINUX_VERSION_CODE < 131394) /* Linux 2.1.66 */
-       int retval, tmp;
-#endif
-       
-       if (serial_paranoia_check(info, tty->device, "rs_ioctl"))
-               return -ENODEV;
-
-       if ((cmd != TIOCGSERIAL) && (cmd != TIOCSSERIAL) &&
-           (cmd != TIOCSERCONFIG) && (cmd != TIOCSERGSTRUCT) &&
-           (cmd != TIOCMIWAIT) && (cmd != TIOCGICOUNT)) {
-               if (tty->flags & (1 << TTY_IO_ERROR))
-                   return -EIO;
-       }
-       
-       switch (cmd) {
-#if (LINUX_VERSION_CODE < 131394) /* Linux 2.1.66 */
-               case TCSBRK:    /* SVID version: non-zero arg --> no break */
-                       retval = tty_check_change(tty);
-                       if (retval)
-                               return retval;
-                       tty_wait_until_sent(tty, 0);
-                       if (signal_pending(current))
-                               return -EINTR;
-                       if (!arg) {
-                               send_break(info, HZ/4); /* 1/4 second */
-                               if (signal_pending(current))
-                                       return -EINTR;
-                       }
-                       return 0;
-               case TCSBRKP:   /* support for POSIX tcsendbreak() */
-                       retval = tty_check_change(tty);
-                       if (retval)
-                               return retval;
-                       tty_wait_until_sent(tty, 0);
-                       if (signal_pending(current))
-                               return -EINTR;
-                       send_break(info, arg ? arg*(HZ/10) : HZ/4);
-                       if (signal_pending(current))
-                               return -EINTR;
-                       return 0;
-               case TIOCGSOFTCAR:
-                       tmp = C_CLOCAL(tty) ? 1 : 0;
-                       if (copy_to_user((void *)arg, &tmp, sizeof(int)))
-                               return -EFAULT;
-                       return 0;
-               case TIOCSSOFTCAR:
-                       if (copy_from_user(&tmp, (void *)arg, sizeof(int)))
-                               return -EFAULT;
-
-                       tty->termios->c_cflag =
-                               ((tty->termios->c_cflag & ~CLOCAL) |
-                                (tmp ? CLOCAL : 0));
-                       return 0;
-#endif
-               case TIOCMGET:
-                       return get_modem_info(info, (unsigned int *) arg);
-               case TIOCMBIS:
-               case TIOCMBIC:
-               case TIOCMSET:
-                       return set_modem_info(info, cmd, (unsigned int *) arg);
-               case TIOCGSERIAL:
-                       return get_serial_info(info,
-                                              (struct serial_struct *) arg);
-               case TIOCSSERIAL:
-                       return set_serial_info(info,
-                                              (struct serial_struct *) arg);
-               case TIOCSERCONFIG:
-                       return do_autoconfig(info);
-
-               case TIOCSERGETLSR: /* Get line status register */
-                       return get_lsr_info(info, (unsigned int *) arg);
-
-               case TIOCSERGSTRUCT:
-                       if (copy_to_user((struct async_struct *) arg,
-                                        info, sizeof(struct async_struct)))
-                               return -EFAULT;
-                       return 0;
-                               
-#ifdef CONFIG_SERIAL_MULTIPORT
-               case TIOCSERGETMULTI:
-                       return get_multiport_struct(info,
-                                      (struct serial_multiport_struct *) arg);
-               case TIOCSERSETMULTI:
-                       return set_multiport_struct(info,
-                                      (struct serial_multiport_struct *) arg);
-#endif
-                       
-               /*
-                * Wait for any of the 4 modem inputs (DCD,RI,DSR,CTS) to change
-                * - mask passed in arg for lines of interest
-                *   (use |'ed TIOCM_RNG/DSR/CD/CTS for masking)
-                * Caller should use TIOCGICOUNT to see which one it was
-                */
-               case TIOCMIWAIT:
-                       save_flags(flags); cli();
-                       /* note the counters on entry */
-                       cprev = info->state->icount;
-                       restore_flags(flags);
-                       /* Force modem status interrupts on */
-                       info->IER |= UART_IER_MSI;
-                       serial_out(info, UART_IER, info->IER);
-                       while (1) {
-                               interruptible_sleep_on(&info->delta_msr_wait);
-                               /* see if a signal did it */
-                               if (signal_pending(current))
-                                       return -ERESTARTSYS;
-                               save_flags(flags); cli();
-                               cnow = info->state->icount; /* atomic copy */
-                               restore_flags(flags);
-                               if (cnow.rng == cprev.rng && cnow.dsr == cprev.dsr && 
-                                   cnow.dcd == cprev.dcd && cnow.cts == cprev.cts)
-                                       return -EIO; /* no change => error */
-                               if ( ((arg & TIOCM_RNG) && (cnow.rng != cprev.rng)) ||
-                                    ((arg & TIOCM_DSR) && (cnow.dsr != cprev.dsr)) ||
-                                    ((arg & TIOCM_CD)  && (cnow.dcd != cprev.dcd)) ||
-                                    ((arg & TIOCM_CTS) && (cnow.cts != cprev.cts)) ) {
-                                       return 0;
-                               }
-                               cprev = cnow;
-                       }
-                       /* NOTREACHED */
-
-               /* 
-                * Get counter of input serial line interrupts (DCD,RI,DSR,CTS)
-                * Return: write counters to the user passed counter struct
-                * NB: both 1->0 and 0->1 transitions are counted except for
-                *     RI where only 0->1 is counted.
-                */
-               case TIOCGICOUNT:
-                       save_flags(flags); cli();
-                       cnow = info->state->icount;
-                       restore_flags(flags);
-                       icount.cts = cnow.cts;
-                       icount.dsr = cnow.dsr;
-                       icount.rng = cnow.rng;
-                       icount.dcd = cnow.dcd;
-                       icount.rx = cnow.rx;
-                       icount.tx = cnow.tx;
-                       icount.frame = cnow.frame;
-                       icount.overrun = cnow.overrun;
-                       icount.parity = cnow.parity;
-                       icount.brk = cnow.brk;
-                       icount.buf_overrun = cnow.buf_overrun;
-                       
-                       if (copy_to_user((void *)arg, &icount, sizeof(icount)))
-                               return -EFAULT;
-                       return 0;
-               case TIOCSERGWILD:
-               case TIOCSERSWILD:
-                       /* "setserial -W" is called in Debian boot */
-                       printk ("TIOCSER?WILD ioctl obsolete, ignored.\n");
-                       return 0;
-
-               default:
-                       return -ENOIOCTLCMD;
-               }
-       return 0;
-}
-
-static void rs_set_termios(struct tty_struct *tty, struct termios *old_termios)
-{
-       struct async_struct *info = (struct async_struct *)tty->driver_data;
-       unsigned long flags;
-       unsigned int cflag = tty->termios->c_cflag;
-       
-       if (   (cflag == old_termios->c_cflag)
-           && (   RELEVANT_IFLAG(tty->termios->c_iflag) 
-               == RELEVANT_IFLAG(old_termios->c_iflag)))
-         return;
-
-       change_speed(info, old_termios);
-
-       /* Handle transition to B0 status */
-       if ((old_termios->c_cflag & CBAUD) &&
-           !(cflag & CBAUD)) {
-               info->MCR &= ~(UART_MCR_DTR|UART_MCR_RTS);
-               save_flags(flags); cli();
-               serial_out(info, UART_MCR, info->MCR);
-               restore_flags(flags);
-       }
-       
-       /* Handle transition away from B0 status */
-       if (!(old_termios->c_cflag & CBAUD) &&
-           (cflag & CBAUD)) {
-               info->MCR |= UART_MCR_DTR;
-               if (!(tty->termios->c_cflag & CRTSCTS) || 
-                   !test_bit(TTY_THROTTLED, &tty->flags)) {
-                       info->MCR |= UART_MCR_RTS;
-               }
-               save_flags(flags); cli();
-               serial_out(info, UART_MCR, info->MCR);
-               restore_flags(flags);
-       }
-       
-       /* Handle turning off CRTSCTS */
-       if ((old_termios->c_cflag & CRTSCTS) &&
-           !(tty->termios->c_cflag & CRTSCTS)) {
-               tty->hw_stopped = 0;
-               rs_start(tty);
-       }
-
-#if 0
-       /*
-        * No need to wake up processes in open wait, since they
-        * sample the CLOCAL flag once, and don't recheck it.
-        * XXX  It's not clear whether the current behavior is correct
-        * or not.  Hence, this may change.....
-        */
-       if (!(old_termios->c_cflag & CLOCAL) &&
-           (tty->termios->c_cflag & CLOCAL))
-               wake_up_interruptible(&info->open_wait);
-#endif
-}
-
-/*
- * ------------------------------------------------------------
- * rs_close()
- * 
- * This routine is called when the serial port gets closed.  First, we
- * wait for the last remaining data to be sent.  Then, we unlink its
- * async structure from the interrupt chain if necessary, and we free
- * that IRQ if nothing is left in the chain.
- * ------------------------------------------------------------
- */
-static void rs_close(struct tty_struct *tty, struct file * filp)
-{
-       struct async_struct * info = (struct async_struct *)tty->driver_data;
-       struct serial_state *state;
-       unsigned long flags;
-
-       if (!info || serial_paranoia_check(info, tty->device, "rs_close"))
-               return;
-
-       state = info->state;
-       
-       save_flags(flags); cli();
-       
-       if (tty_hung_up_p(filp)) {
-               DBG_CNT("before DEC-hung");
-               MOD_DEC_USE_COUNT;
-               restore_flags(flags);
-               return;
-       }
-       
-#ifdef SERIAL_DEBUG_OPEN
-       printk("rs_close ttys%d, count = %d\n", info->line, state->count);
-#endif
-       if ((tty->count == 1) && (state->count != 1)) {
-               /*
-                * Uh, oh.  tty->count is 1, which means that the tty
-                * structure will be freed.  state->count should always
-                * be one in these conditions.  If it's greater than
-                * one, we've got real problems, since it means the
-                * serial port won't be shutdown.
-                */
-               printk("rs_close: bad serial port count; tty->count is 1, "
-                      "state->count is %d\n", state->count);
-               state->count = 1;
-       }
-       if (--state->count < 0) {
-               printk("rs_close: bad serial port count for ttys%d: %d\n",
-                      info->line, state->count);
-               state->count = 0;
-       }
-       if (state->count) {
-               DBG_CNT("before DEC-2");
-               MOD_DEC_USE_COUNT;
-               restore_flags(flags);
-               return;
-       }
-       info->flags |= ASYNC_CLOSING;
-       restore_flags(flags);
-       /*
-        * Save the termios structure, since this port may have
-        * separate termios for callout and dialin.
-        */
-       if (info->flags & ASYNC_NORMAL_ACTIVE)
-               info->state->normal_termios = *tty->termios;
-       if (info->flags & ASYNC_CALLOUT_ACTIVE)
-               info->state->callout_termios = *tty->termios;
-       /*
-        * Now we wait for the transmit buffer to clear; and we notify 
-        * the line discipline to only process XON/XOFF characters.
-        */
-       tty->closing = 1;
-       if (info->closing_wait != ASYNC_CLOSING_WAIT_NONE)
-               tty_wait_until_sent(tty, info->closing_wait);
-       /*
-        * At this point we stop accepting input.  To do this, we
-        * disable the receive line status interrupts, and tell the
-        * interrupt driver to stop checking the data ready bit in the
-        * line status register.
-        */
-       info->IER &= ~UART_IER_RLSI;
-       info->read_status_mask &= ~UART_LSR_DR;
-       if (info->flags & ASYNC_INITIALIZED) {
-               serial_out(info, UART_IER, info->IER);
-               /*
-                * Before we drop DTR, make sure the UART transmitter
-                * has completely drained; this is especially
-                * important if there is a transmit FIFO!
-                */
-               rs_wait_until_sent(tty, info->timeout);
-       }
-       shutdown(info);
-       if (tty->driver.flush_buffer)
-               tty->driver.flush_buffer(tty);
-       if (tty->ldisc.flush_buffer)
-               tty->ldisc.flush_buffer(tty);
-       tty->closing = 0;
-       info->event = 0;
-       info->tty = 0;
-       if (info->blocked_open) {
-               if (info->close_delay) {
-                       set_current_state(TASK_INTERRUPTIBLE);
-                       schedule_timeout(info->close_delay);
-               }
-               wake_up_interruptible(&info->open_wait);
-       }
-       info->flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CALLOUT_ACTIVE|
-                        ASYNC_CLOSING);
-       wake_up_interruptible(&info->close_wait);
-       MOD_DEC_USE_COUNT;
-}
-
-/*
- * rs_wait_until_sent() --- wait until the transmitter is empty
- */
-static void rs_wait_until_sent(struct tty_struct *tty, int timeout)
-{
-       struct async_struct * info = (struct async_struct *)tty->driver_data;
-       unsigned long orig_jiffies, char_time;
-       int lsr;
-       
-       if (serial_paranoia_check(info, tty->device, "rs_wait_until_sent"))
-               return;
-
-       if (info->state->type == PORT_UNKNOWN)
-               return;
-
-       if (info->xmit_fifo_size == 0)
-               return; /* Just in case.... */
-
-       orig_jiffies = jiffies;
-       /*
-        * Set the check interval to be 1/5 of the estimated time to
-        * send a single character, and make it at least 1.  The check
-        * interval should also be less than the timeout.
-        * 
-        * Note: we have to use pretty tight timings here to satisfy
-        * the NIST-PCTS.
-        */
-       char_time = (info->timeout - HZ/50) / info->xmit_fifo_size;
-       char_time = char_time / 5;
-       if (char_time == 0)
-               char_time = 1;
-       if (timeout && timeout < char_time)
-               char_time = timeout;
-       /*
-        * If the transmitter hasn't cleared in twice the approximate
-        * amount of time to send the entire FIFO, it probably won't
-        * ever clear.  This assumes the UART isn't doing flow
-        * control, which is currently the case.  Hence, if it ever
-        * takes longer than info->timeout, this is probably due to a
-        * UART bug of some kind.  So, we clamp the timeout parameter at
-        * 2*info->timeout.
-        */
-       if (!timeout || timeout > 2*info->timeout)
-               timeout = 2*info->timeout;
-#ifdef SERIAL_DEBUG_RS_WAIT_UNTIL_SENT
-       printk("In rs_wait_until_sent(%d) check=%lu...", timeout, char_time);
-       printk("jiff=%lu...", jiffies);
-#endif
-       while (!((lsr = serial_inp(info, UART_LSR)) & UART_LSR_TEMT)) {
-#ifdef SERIAL_DEBUG_RS_WAIT_UNTIL_SENT
-               printk("lsr = %d (jiff=%lu)...", lsr, jiffies);
-#endif
-               set_current_state(TASK_INTERRUPTIBLE);
-               schedule_timeout(char_time);
-               if (signal_pending(current))
-                       break;
-               if (timeout && time_after(jiffies, orig_jiffies + timeout))
-                       break;
-       }
-#ifdef SERIAL_DEBUG_RS_WAIT_UNTIL_SENT
-       printk("lsr = %d (jiff=%lu)...done\n", lsr, jiffies);
-#endif
-}
-
-/*
- * rs_hangup() --- called by tty_hangup() when a hangup is signaled.
- */
-static void rs_hangup(struct tty_struct *tty)
-{
-       struct async_struct * info = (struct async_struct *)tty->driver_data;
-       struct serial_state *state = info->state;
-       
-       if (serial_paranoia_check(info, tty->device, "rs_hangup"))
-               return;
-
-       state = info->state;
-       
-       rs_flush_buffer(tty);
-       if (info->flags & ASYNC_CLOSING)
-               return;
-       shutdown(info);
-       info->event = 0;
-       state->count = 0;
-       info->flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CALLOUT_ACTIVE);
-       info->tty = 0;
-       wake_up_interruptible(&info->open_wait);
-}
-
-/*
- * ------------------------------------------------------------
- * rs_open() and friends
- * ------------------------------------------------------------
- */
-static int block_til_ready(struct tty_struct *tty, struct file * filp,
-                          struct async_struct *info)
-{
-       DECLARE_WAITQUEUE(wait, current);
-       struct serial_state *state = info->state;
-       int             retval;
-       int             do_clocal = 0, extra_count = 0;
-       unsigned long   flags;
-
-       /*
-        * If the device is in the middle of being closed, then block
-        * until it's done, and then try again.
-        */
-       if (tty_hung_up_p(filp) ||
-           (info->flags & ASYNC_CLOSING)) {
-               if (info->flags & ASYNC_CLOSING)
-                       interruptible_sleep_on(&info->close_wait);
-#ifdef SERIAL_DO_RESTART
-               return ((info->flags & ASYNC_HUP_NOTIFY) ?
-                       -EAGAIN : -ERESTARTSYS);
-#else
-               return -EAGAIN;
-#endif
-       }
-
-       /*
-        * If this is a callout device, then just make sure the normal
-        * device isn't being used.
-        */
-       if (tty->driver.subtype == SERIAL_TYPE_CALLOUT) {
-               if (info->flags & ASYNC_NORMAL_ACTIVE)
-                       return -EBUSY;
-               if ((info->flags & ASYNC_CALLOUT_ACTIVE) &&
-                   (info->flags & ASYNC_SESSION_LOCKOUT) &&
-                   (info->session != current->session))
-                   return -EBUSY;
-               if ((info->flags & ASYNC_CALLOUT_ACTIVE) &&
-                   (info->flags & ASYNC_PGRP_LOCKOUT) &&
-                   (info->pgrp != current->pgrp))
-                   return -EBUSY;
-               info->flags |= ASYNC_CALLOUT_ACTIVE;
-               return 0;
-       }
-       
-       /*
-        * If non-blocking mode is set, or the port is not enabled,
-        * then make the check up front and then exit.
-        */
-       if ((filp->f_flags & O_NONBLOCK) ||
-           (tty->flags & (1 << TTY_IO_ERROR))) {
-               if (info->flags & ASYNC_CALLOUT_ACTIVE)
-                       return -EBUSY;
-               info->flags |= ASYNC_NORMAL_ACTIVE;
-               return 0;
-       }
-
-       if (info->flags & ASYNC_CALLOUT_ACTIVE) {
-               if (state->normal_termios.c_cflag & CLOCAL)
-                       do_clocal = 1;
-       } else {
-               if (tty->termios->c_cflag & CLOCAL)
-                       do_clocal = 1;
-       }
-       
-       /*
-        * Block waiting for the carrier detect and the line to become
-        * free (i.e., not in use by the callout).  While we are in
-        * this loop, state->count is dropped by one, so that
-        * rs_close() knows when to free things.  We restore it upon
-        * exit, either normal or abnormal.
-        */
-       retval = 0;
-       add_wait_queue(&info->open_wait, &wait);
-#ifdef SERIAL_DEBUG_OPEN
-       printk("block_til_ready before block: ttys%d, count = %d\n",
-              state->line, state->count);
-#endif
-       save_flags(flags); cli();
-       if (!tty_hung_up_p(filp)) {
-               extra_count = 1;
-               state->count--;
-       }
-       restore_flags(flags);
-       info->blocked_open++;
-       while (1) {
-               save_flags(flags); cli();
-               if (!(info->flags & ASYNC_CALLOUT_ACTIVE) &&
-                   (tty->termios->c_cflag & CBAUD))
-                       serial_out(info, UART_MCR,
-                                  serial_inp(info, UART_MCR) |
-                                  (UART_MCR_DTR | UART_MCR_RTS));
-               restore_flags(flags);
-               set_current_state(TASK_INTERRUPTIBLE);
-               if (tty_hung_up_p(filp) ||
-                   !(info->flags & ASYNC_INITIALIZED)) {
-#ifdef SERIAL_DO_RESTART
-                       if (info->flags & ASYNC_HUP_NOTIFY)
-                               retval = -EAGAIN;
-                       else
-                               retval = -ERESTARTSYS;  
-#else
-                       retval = -EAGAIN;
-#endif
-                       break;
-               }
-               if (!(info->flags & ASYNC_CALLOUT_ACTIVE) &&
-                   !(info->flags & ASYNC_CLOSING) &&
-                   (do_clocal || (serial_in(info, UART_MSR) &
-                                  UART_MSR_DCD)))
-                       break;
-               if (signal_pending(current)) {
-                       retval = -ERESTARTSYS;
-                       break;
-               }
-#ifdef SERIAL_DEBUG_OPEN
-               printk("block_til_ready blocking: ttys%d, count = %d\n",
-                      info->line, state->count);
-#endif
-               schedule();
-       }
-       set_current_state(TASK_RUNNING);
-       remove_wait_queue(&info->open_wait, &wait);
-       if (extra_count)
-               state->count++;
-       info->blocked_open--;
-#ifdef SERIAL_DEBUG_OPEN
-       printk("block_til_ready after blocking: ttys%d, count = %d\n",
-              info->line, state->count);
-#endif
-       if (retval)
-               return retval;
-       info->flags |= ASYNC_NORMAL_ACTIVE;
-       return 0;
-}
-
-static int get_async_struct(int line, struct async_struct **ret_info)
-{
-       struct async_struct *info;
-       struct serial_state *sstate;
-
-       sstate = rs_table + line;
-       sstate->count++;
-       info = sstate->info;
-
-       /*
-        * If the async_struct is already allocated, do the fastpath.
-        */
-       if (info)
-               goto out;
-
-       info = kmalloc(sizeof(struct async_struct), GFP_KERNEL);
-       if (!info) {
-               sstate->count--;
-               return -ENOMEM;
-       }
-
-       memset(info, 0, sizeof(struct async_struct));
-       init_waitqueue_head(&info->open_wait);
-       init_waitqueue_head(&info->close_wait);
-       init_waitqueue_head(&info->delta_msr_wait);
-       info->magic = SERIAL_MAGIC;
-       info->port = sstate->port;
-       info->hub6 = sstate->hub6;
-       info->flags = sstate->flags;
-       info->xmit_fifo_size = sstate->xmit_fifo_size;
-       info->state = sstate;
-       info->line = line;
-       info->iomem_base = sstate->iomem_base;
-       info->iomem_reg_shift = sstate->iomem_reg_shift;
-       info->io_type = sstate->io_type;
-       info->tqueue.routine = do_softint;
-       info->tqueue.data = info;
-
-       if (sstate->info) {
-               kfree(info);
-               info = sstate->info;
-       } else {
-               sstate->info = info;
-       }
-
-out:
-       /*
-        * If this is the first open, copy over some timeouts.
-        */
-       if (sstate->count == 1) {
-               info->closing_wait = sstate->closing_wait;
-       }
-       *ret_info = info;
-       return 0;
-}
-
-/*
- * This routine is called whenever a serial port is opened.  It
- * enables interrupts for a serial port, linking in its async structure into
- * the IRQ chain.   It also performs the serial-specific
- * initialization for the tty structure.
- */
-static int rs_open(struct tty_struct *tty, struct file * filp)
-{
-       struct async_struct     *info;
-       int                     retval, line;
-       unsigned long           page;
-
-       MOD_INC_USE_COUNT;
-       line = minor(tty->device) - tty->driver.minor_start;
-       if ((line < 0) || (line >= NR_PORTS)) {
-               MOD_DEC_USE_COUNT;
-               return -ENODEV;
-       }
-       retval = get_async_struct(line, &info);
-       if (retval) {
-               MOD_DEC_USE_COUNT;
-               return retval;
-       }
-       tty->driver_data = info;
-       info->tty = tty;
-       if (serial_paranoia_check(info, tty->device, "rs_open")) {
-               MOD_DEC_USE_COUNT;              
-               return -ENODEV;
-       }
-
-#ifdef SERIAL_DEBUG_OPEN
-       printk("rs_open %s%d, count = %d\n", tty->driver.name, info->line,
-              info->state->count);
-#endif
-#if (LINUX_VERSION_CODE > 0x20100)
-       info->tty->low_latency = (info->flags & ASYNC_LOW_LATENCY) ? 1 : 0;
-#endif
-
-       /*
-        *      This relies on lock_kernel() stuff so wants tidying for 2.5
-        */
-       if (!tmp_buf) {
-               page = get_zeroed_page(GFP_KERNEL);
-               if (!page) {
-                       MOD_DEC_USE_COUNT;
-                       return -ENOMEM;
-               }
-               if (tmp_buf)
-                       free_page(page);
-               else
-                       tmp_buf = (unsigned char *) page;
-       }
-
-       /*
-        * If the port is the middle of closing, bail out now
-        */
-       if (tty_hung_up_p(filp) ||
-           (info->flags & ASYNC_CLOSING)) {
-               if (info->flags & ASYNC_CLOSING)
-                       interruptible_sleep_on(&info->close_wait);
-               MOD_DEC_USE_COUNT;
-#ifdef SERIAL_DO_RESTART
-               return ((info->flags & ASYNC_HUP_NOTIFY) ?
-                       -EAGAIN : -ERESTARTSYS);
-#else
-               return -EAGAIN;
-#endif
-       }
-
-       /*
-        * Start up serial port
-        */
-       retval = startup(info);
-       if (retval) {
-               MOD_DEC_USE_COUNT;
-               return retval;
-       }
-
-       retval = block_til_ready(tty, filp, info);
-       if (retval) {
-#ifdef SERIAL_DEBUG_OPEN
-               printk("rs_open returning after block_til_ready with %d\n",
-                      retval);
-#endif
-               MOD_DEC_USE_COUNT;
-               return retval;
-       }
-
-       if ((info->state->count == 1) &&
-           (info->flags & ASYNC_SPLIT_TERMIOS)) {
-               if (tty->driver.subtype == SERIAL_TYPE_NORMAL)
-                       *tty->termios = info->state->normal_termios;
-               else 
-                       *tty->termios = info->state->callout_termios;
-               change_speed(info, 0);
-       }
-#ifdef CONFIG_SERIAL_CONSOLE
-       if (sercons.cflag && sercons.index == line) {
-               tty->termios->c_cflag = sercons.cflag;
-               sercons.cflag = 0;
-               change_speed(info, 0);
-       }
-#endif
-       info->session = current->session;
-       info->pgrp = current->pgrp;
-
-#ifdef SERIAL_DEBUG_OPEN
-       printk("rs_open ttys%d successful...", info->line);
-#endif
-       return 0;
-}
-
-/*
- * /proc fs routines....
- */
-
-static inline int line_info(char *buf, struct serial_state *state)
-{
-       struct async_struct *info = state->info, scr_info;
-       char    stat_buf[30], control, status;
-       int     ret;
-       unsigned long flags;
-
-       ret = sprintf(buf, "%d: uart:%s port:%lX irq:%d",
-                     state->line, uart_config[state->type].name, 
-                     state->port, state->irq);
-
-       if (!state->port || (state->type == PORT_UNKNOWN)) {
-               ret += sprintf(buf+ret, "\n");
-               return ret;
-       }
-
-       /*
-        * Figure out the current RS-232 lines
-        */
-       if (!info) {
-               info = &scr_info;       /* This is just for serial_{in,out} */
-
-               info->magic = SERIAL_MAGIC;
-               info->port = state->port;
-               info->flags = state->flags;
-               info->hub6 = state->hub6;
-               info->io_type = state->io_type;
-               info->iomem_base = state->iomem_base;
-               info->iomem_reg_shift = state->iomem_reg_shift;
-               info->quot = 0;
-               info->tty = 0;
-       }
-       save_flags(flags); cli();
-       status = serial_in(info, UART_MSR);
-       control = info != &scr_info ? info->MCR : serial_in(info, UART_MCR);
-       restore_flags(flags); 
-
-       stat_buf[0] = 0;
-       stat_buf[1] = 0;
-       if (control & UART_MCR_RTS)
-               strcat(stat_buf, "|RTS");
-       if (status & UART_MSR_CTS)
-               strcat(stat_buf, "|CTS");
-       if (control & UART_MCR_DTR)
-               strcat(stat_buf, "|DTR");
-       if (status & UART_MSR_DSR)
-               strcat(stat_buf, "|DSR");
-       if (status & UART_MSR_DCD)
-               strcat(stat_buf, "|CD");
-       if (status & UART_MSR_RI)
-               strcat(stat_buf, "|RI");
-
-       if (info->quot) {
-               ret += sprintf(buf+ret, " baud:%d",
-                              state->baud_base / info->quot);
-       }
-
-       ret += sprintf(buf+ret, " tx:%d rx:%d",
-                     state->icount.tx, state->icount.rx);
-
-       if (state->icount.frame)
-               ret += sprintf(buf+ret, " fe:%d", state->icount.frame);
-       
-       if (state->icount.parity)
-               ret += sprintf(buf+ret, " pe:%d", state->icount.parity);
-       
-       if (state->icount.brk)
-               ret += sprintf(buf+ret, " brk:%d", state->icount.brk);  
-
-       if (state->icount.overrun)
-               ret += sprintf(buf+ret, " oe:%d", state->icount.overrun);
-
-       /*
-        * Last thing is the RS-232 status lines
-        */
-       ret += sprintf(buf+ret, " %s\n", stat_buf+1);
-       return ret;
-}
-
-int rs_read_proc(char *page, char **start, off_t off, int count,
-                int *eof, void *data)
-{
-       int i, len = 0, l;
-       off_t   begin = 0;
-
-       len += sprintf(page, "serinfo:1.0 driver:%s%s revision:%s\n",
-                      serial_version, LOCAL_VERSTRING, serial_revdate);
-       for (i = 0; i < NR_PORTS && len < 4000; i++) {
-               l = line_info(page + len, &rs_table[i]);
-               len += l;
-               if (len+begin > off+count)
-                       goto done;
-               if (len+begin < off) {
-                       begin += len;
-                       len = 0;
-               }
-       }
-       *eof = 1;
-done:
-       if (off >= len+begin)
-               return 0;
-       *start = page + (off-begin);
-       return ((count < begin+len-off) ? count : begin+len-off);
-}
-
-/*
- * ---------------------------------------------------------------------
- * rs_init() and friends
- *
- * rs_init() is called at boot-time to initialize the serial driver.
- * ---------------------------------------------------------------------
- */
-
-/*
- * This routine prints out the appropriate serial driver version
- * number, and identifies which options were configured into this
- * driver.
- */
-static char serial_options[] __initdata =
-#ifdef CONFIG_HUB6
-       " HUB-6"
-#define SERIAL_OPT
-#endif
-#ifdef CONFIG_SERIAL_MANY_PORTS
-       " MANY_PORTS"
-#define SERIAL_OPT
-#endif
-#ifdef CONFIG_SERIAL_MULTIPORT
-       " MULTIPORT"
-#define SERIAL_OPT
-#endif
-#ifdef CONFIG_SERIAL_SHARE_IRQ
-       " SHARE_IRQ"
-#define SERIAL_OPT
-#endif
-#ifdef CONFIG_SERIAL_DETECT_IRQ
-       " DETECT_IRQ"
-#define SERIAL_OPT
-#endif
-#ifdef ENABLE_SERIAL_PCI
-       " SERIAL_PCI"
-#define SERIAL_OPT
-#endif
-#ifdef ENABLE_SERIAL_PNP
-       " ISAPNP"
-#define SERIAL_OPT
-#endif
-#ifdef ENABLE_SERIAL_ACPI
-       " SERIAL_ACPI"
-#define SERIAL_OPT
-#endif
-#ifdef SERIAL_OPT
-       " enabled\n";
-#else
-       " no serial options enabled\n";
-#endif
-#undef SERIAL_OPT
-
-static _INLINE_ void show_serial_version(void)
-{
-       printk(KERN_INFO "%s version %s%s (%s) with%s", serial_name,
-              serial_version, LOCAL_VERSTRING, serial_revdate,
-              serial_options);
-}
-
-/*
- * This routine detect the IRQ of a serial port by clearing OUT2 when
- * no UART interrupt are requested (IER = 0) (*GPL*). This seems to work at
- * each time, as long as no other device permanently request the IRQ.
- * If no IRQ is detected, or multiple IRQ appear, this function returns 0.
- * The variable "state" and the field "state->port" should not be null.
- */
-static unsigned detect_uart_irq (struct serial_state * state)
-{
-       int irq;
-       unsigned long irqs;
-       unsigned char save_mcr, save_ier;
-       struct async_struct scr_info; /* serial_{in,out} because HUB6 */
-
-#ifdef CONFIG_SERIAL_MANY_PORTS
-       unsigned char save_ICP=0; /* no warning */
-       unsigned short ICP=0;
-
-       if (state->flags & ASYNC_FOURPORT)  {
-               ICP = (state->port & 0xFE0) | 0x01F;
-               save_ICP = inb_p(ICP);
-               outb_p(0x80, ICP);
-               (void) inb_p(ICP);
-       }
-#endif
-       scr_info.magic = SERIAL_MAGIC;
-       scr_info.state = state;
-       scr_info.port = state->port;
-       scr_info.flags = state->flags;
-#ifdef CONFIG_HUB6
-       scr_info.hub6 = state->hub6;
-#endif
-       scr_info.io_type = state->io_type;
-       scr_info.iomem_base = state->iomem_base;
-       scr_info.iomem_reg_shift = state->iomem_reg_shift;
-
-       /* forget possible initially masked and pending IRQ */
-       probe_irq_off(probe_irq_on());
-       save_mcr = serial_inp(&scr_info, UART_MCR);
-       save_ier = serial_inp(&scr_info, UART_IER);
-       serial_outp(&scr_info, UART_MCR, UART_MCR_OUT1 | UART_MCR_OUT2);
-       
-       irqs = probe_irq_on();
-       serial_outp(&scr_info, UART_MCR, 0);
-       udelay (10);
-       if (state->flags & ASYNC_FOURPORT)  {
-               serial_outp(&scr_info, UART_MCR,
-                           UART_MCR_DTR | UART_MCR_RTS);
-       } else {
-               serial_outp(&scr_info, UART_MCR,
-                           UART_MCR_DTR | UART_MCR_RTS | UART_MCR_OUT2);
-       }
-       serial_outp(&scr_info, UART_IER, 0x0f); /* enable all intrs */
-       (void)serial_inp(&scr_info, UART_LSR);
-       (void)serial_inp(&scr_info, UART_RX);
-       (void)serial_inp(&scr_info, UART_IIR);
-       (void)serial_inp(&scr_info, UART_MSR);
-       serial_outp(&scr_info, UART_TX, 0xFF);
-       udelay (20);
-       irq = probe_irq_off(irqs);
-
-       serial_outp(&scr_info, UART_MCR, save_mcr);
-       serial_outp(&scr_info, UART_IER, save_ier);
-#ifdef CONFIG_SERIAL_MANY_PORTS
-       if (state->flags & ASYNC_FOURPORT)
-               outb_p(save_ICP, ICP);
-#endif
-       return (irq > 0)? irq : 0;
-}
-
-/*
- * This is a quickie test to see how big the FIFO is.
- * It doesn't work at all the time, more's the pity.
- */
-static int size_fifo(struct async_struct *info)
-{
-       unsigned char old_fcr, old_mcr, old_dll, old_dlm;
-       int count;
-
-       old_fcr = serial_inp(info, UART_FCR);
-       old_mcr = serial_inp(info, UART_MCR);
-       serial_outp(info, UART_FCR, UART_FCR_ENABLE_FIFO |
-                   UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT);
-       serial_outp(info, UART_MCR, UART_MCR_LOOP);
-       serial_outp(info, UART_LCR, UART_LCR_DLAB);
-       old_dll = serial_inp(info, UART_DLL);
-       old_dlm = serial_inp(info, UART_DLM);
-       serial_outp(info, UART_DLL, 0x01);
-       serial_outp(info, UART_DLM, 0x00);
-       serial_outp(info, UART_LCR, 0x03);
-       for (count = 0; count < 256; count++)
-               serial_outp(info, UART_TX, count);
-       mdelay(20);
-       for (count = 0; (serial_inp(info, UART_LSR) & UART_LSR_DR) &&
-            (count < 256); count++)
-               serial_inp(info, UART_RX);
-       serial_outp(info, UART_FCR, old_fcr);
-       serial_outp(info, UART_MCR, old_mcr);
-       serial_outp(info, UART_LCR, UART_LCR_DLAB);
-       serial_outp(info, UART_DLL, old_dll);
-       serial_outp(info, UART_DLM, old_dlm);
-
-       return count;
-}
-
-/*
- * 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
- * 16650 V2, but it might be one of several StarTech UARTs, or one of
- * its clones.  (We treat the broken original StarTech 16650 V1 as a
- * 16550, and why not?  Startech doesn't seem to even acknowledge its
- * existence.)
- * 
- * What evil have men's minds wrought...
- */
-static void autoconfig_startech_uarts(struct async_struct *info,
-                                     struct serial_state *state,
-                                     unsigned long flags)
-{
-       unsigned char scratch, scratch2, scratch3, scratch4;
-
-       /*
-        * First we check to see if it's an Oxford Semiconductor UART.
-        *
-        * If we have to do this here because some non-National
-        * Semiconductor clone chips lock up if you try writing to the
-        * LSR register (which serial_icr_read does)
-        */
-       if (state->type == PORT_16550A) {
-               /*
-                * EFR [4] must be set else this test fails
-                *
-                * This shouldn't be necessary, but Mike Hudson
-                * (Exoray@isys.ca) claims that it's needed for 952
-                * dual UART's (which are not recommended for new designs).
-                */
-               info->ACR = 0;
-               serial_out(info, UART_LCR, 0xBF);
-               serial_out(info, UART_EFR, 0x10);
-               serial_out(info, UART_LCR, 0x00);
-               /* Check for Oxford Semiconductor 16C950 */
-               scratch = serial_icr_read(info, UART_ID1);
-               scratch2 = serial_icr_read(info, UART_ID2);
-               scratch3 = serial_icr_read(info, UART_ID3);
-               
-               if (scratch == 0x16 && scratch2 == 0xC9 &&
-                   (scratch3 == 0x50 || scratch3 == 0x52 ||
-                    scratch3 == 0x54)) {
-                       state->type = PORT_16C950;
-                       state->revision = serial_icr_read(info, UART_REV) |
-                               (scratch3 << 8);
-                       return;
-               }
-       }
-       
-       /*
-        * We check for a XR16C850 by setting DLL and DLM to 0, and
-        * then reading back DLL and DLM.  If DLM reads back 0x10,
-        * then the UART is a XR16C850 and the DLL contains the chip
-        * revision.  If DLM reads back 0x14, then the UART is a
-        * XR16C854.
-        * 
-        */
-
-       /* Save the DLL and DLM */
-
-       serial_outp(info, UART_LCR, UART_LCR_DLAB);
-       scratch3 = serial_inp(info, UART_DLL);
-       scratch4 = serial_inp(info, UART_DLM);
-
-       serial_outp(info, UART_DLL, 0);
-       serial_outp(info, UART_DLM, 0);
-       scratch2 = serial_inp(info, UART_DLL);
-       scratch = serial_inp(info, UART_DLM);
-       serial_outp(info, UART_LCR, 0);
-
-       if (scratch == 0x10 || scratch == 0x14) {
-               if (scratch == 0x10)
-                       state->revision = scratch2;
-               state->type = PORT_16850;
-               return;
-       }
-
-       /* Restore the DLL and DLM */
-
-       serial_outp(info, UART_LCR, UART_LCR_DLAB);
-       serial_outp(info, UART_DLL, scratch3);
-       serial_outp(info, UART_DLM, scratch4);
-       serial_outp(info, UART_LCR, 0);
-       /*
-        * We distinguish between the '654 and the '650 by counting
-        * how many bytes are in the FIFO.  I'm using this for now,
-        * since that's the technique that was sent to me in the
-        * serial driver update, but I'm not convinced this works.
-        * I've had problems doing this in the past.  -TYT
-        */
-       if (size_fifo(info) == 64)
-               state->type = PORT_16654;
-       else
-               state->type = PORT_16650V2;
-}
-
-/*
- * This routine is called by rs_init() to initialize a specific serial
- * port.  It determines what type of UART chip this serial port is
- * using: 8250, 16450, 16550, 16550A.  The important question is
- * whether or not this UART is a 16550A or not, since this will
- * determine whether or not we can use its FIFO features or not.
- */
-static void autoconfig(struct serial_state * state)
-{
-       unsigned char status1, status2, scratch, scratch2, scratch3;
-       unsigned char save_lcr, save_mcr;
-       struct async_struct *info, scr_info;
-       unsigned long flags;
-
-       state->type = PORT_UNKNOWN;
-
-#ifdef SERIAL_DEBUG_AUTOCONF
-       printk("Testing ttyS%d (0x%04lx, 0x%04x)...\n", state->line,
-              state->port, (unsigned) state->iomem_base);
-#endif
-       
-       if (!CONFIGURED_SERIAL_PORT(state))
-               return;
-               
-       info = &scr_info;       /* This is just for serial_{in,out} */
-
-       info->magic = SERIAL_MAGIC;
-       info->state = state;
-       info->port = state->port;
-       info->flags = state->flags;
-#ifdef CONFIG_HUB6
-       info->hub6 = state->hub6;
-#endif
-       info->io_type = state->io_type;
-       info->iomem_base = state->iomem_base;
-       info->iomem_reg_shift = state->iomem_reg_shift;
-
-       save_flags(flags); cli();
-       
-       if (!(state->flags & ASYNC_BUGGY_UART) &&
-           !state->iomem_base) {
-               /*
-                * Do a simple existence test first; if we fail this,
-                * there's no point trying anything else.
-                * 
-                * 0x80 is used as a nonsense port to prevent against
-                * false positives due to ISA bus float.  The
-                * assumption is that 0x80 is a non-existent port;
-                * which should be safe since include/asm/io.h also
-                * makes this assumption.
-                */
-               scratch = serial_inp(info, UART_IER);
-               serial_outp(info, UART_IER, 0);
-#ifdef __i386__
-               outb(0xff, 0x080);
-#endif
-               scratch2 = serial_inp(info, UART_IER);
-               serial_outp(info, UART_IER, 0x0F);
-#ifdef __i386__
-               outb(0, 0x080);
-#endif
-               scratch3 = serial_inp(info, UART_IER);
-               serial_outp(info, UART_IER, scratch);
-               if (scratch2 || scratch3 != 0x0F) {
-#ifdef SERIAL_DEBUG_AUTOCONF
-                       printk("serial: ttyS%d: simple autoconfig failed "
-                              "(%02x, %02x)\n", state->line, 
-                              scratch2, scratch3);
-#endif
-                       restore_flags(flags);
-                       return;         /* We failed; there's nothing here */
-               }
-       }
-
-       save_mcr = serial_in(info, UART_MCR);
-       save_lcr = serial_in(info, UART_LCR);
-
-       /* 
-        * Check to see if a UART is really there.  Certain broken
-        * internal modems based on the Rockwell chipset fail this
-        * test, because they apparently don't implement the loopback
-        * test mode.  So this test is skipped on the COM 1 through
-        * COM 4 ports.  This *should* be safe, since no board
-        * manufacturer would be stupid enough to design a board
-        * that conflicts with COM 1-4 --- we hope!
-        */
-       if (!(state->flags & ASYNC_SKIP_TEST)) {
-               serial_outp(info, UART_MCR, UART_MCR_LOOP | 0x0A);
-               status1 = serial_inp(info, UART_MSR) & 0xF0;
-               serial_outp(info, UART_MCR, save_mcr);
-               if (status1 != 0x90) {
-#ifdef SERIAL_DEBUG_AUTOCONF
-                       printk("serial: ttyS%d: no UART loopback failed\n",
-                              state->line);
-#endif
-                       restore_flags(flags);
-                       return;
-               }
-       }
-       serial_outp(info, UART_LCR, 0xBF); /* set up for StarTech test */
-       serial_outp(info, UART_EFR, 0); /* EFR is the same as FCR */
-       serial_outp(info, UART_LCR, 0);
-       serial_outp(info, UART_FCR, UART_FCR_ENABLE_FIFO);
-       scratch = serial_in(info, UART_IIR) >> 6;
-       switch (scratch) {
-               case 0:
-                       state->type = PORT_16450;
-                       break;
-               case 1:
-                       state->type = PORT_UNKNOWN;
-                       break;
-               case 2:
-                       state->type = PORT_16550;
-                       break;
-               case 3:
-                       state->type = PORT_16550A;
-                       break;
-       }
-       if (state->type == PORT_16550A) {
-               /* Check for Startech UART's */
-               serial_outp(info, UART_LCR, UART_LCR_DLAB);
-               if (serial_in(info, UART_EFR) == 0) {
-                       state->type = PORT_16650;
-               } else {
-                       serial_outp(info, UART_LCR, 0xBF);
-                       if (serial_in(info, UART_EFR) == 0)
-                               autoconfig_startech_uarts(info, state, flags);
-               }
-       }
-       if (state->type == PORT_16550A) {
-               /* Check for TI 16750 */
-               serial_outp(info, UART_LCR, save_lcr | UART_LCR_DLAB);
-               serial_outp(info, UART_FCR,
-                           UART_FCR_ENABLE_FIFO | UART_FCR7_64BYTE);
-               scratch = serial_in(info, UART_IIR) >> 5;
-               if (scratch == 7) {
-                       /*
-                        * If this is a 16750, and not a cheap UART
-                        * clone, then it should only go into 64 byte
-                        * mode if the UART_FCR7_64BYTE bit was set
-                        * while UART_LCR_DLAB was latched.
-                        */
-                       serial_outp(info, UART_FCR, UART_FCR_ENABLE_FIFO);
-                       serial_outp(info, UART_LCR, 0);
-                       serial_outp(info, UART_FCR,
-                                   UART_FCR_ENABLE_FIFO | UART_FCR7_64BYTE);
-                       scratch = serial_in(info, UART_IIR) >> 5;
-                       if (scratch == 6)
-                               state->type = PORT_16750;
-               }
-               serial_outp(info, UART_FCR, UART_FCR_ENABLE_FIFO);
-       }
-#if defined(CONFIG_SERIAL_RSA) && defined(MODULE)
-       if (state->type == PORT_16550A) {
-               int i;
-
-               for (i = 0 ; i < PORT_RSA_MAX ; ++i) {
-                       if (!probe_rsa[i] && !force_rsa[i])
-                               break;
-                       if (((probe_rsa[i] != state->port) ||
-                            check_region(state->port + UART_RSA_BASE, 16)) &&
-                           (force_rsa[i] != state->port))
-                               continue;
-                       if (!enable_rsa(info))
-                               continue;
-                       state->type = PORT_RSA;
-                       state->baud_base = SERIAL_RSA_BAUD_BASE;
-                       break;
-               }
-       }
-#endif
-       serial_outp(info, UART_LCR, save_lcr);
-       if (state->type == PORT_16450) {
-               scratch = serial_in(info, UART_SCR);
-               serial_outp(info, UART_SCR, 0xa5);
-               status1 = serial_in(info, UART_SCR);
-               serial_outp(info, UART_SCR, 0x5a);
-               status2 = serial_in(info, UART_SCR);
-               serial_outp(info, UART_SCR, scratch);
-
-               if ((status1 != 0xa5) || (status2 != 0x5a))
-                       state->type = PORT_8250;
-       }
-       state->xmit_fifo_size = uart_config[state->type].dfl_xmit_fifo_size;
-
-       if (state->type == PORT_UNKNOWN) {
-               restore_flags(flags);
-               return;
-       }
-
-       if (info->port) {
-#ifdef CONFIG_SERIAL_RSA
-               if (state->type == PORT_RSA)
-                       request_region(info->port + UART_RSA_BASE, 16,
-                                      "serial_rsa(auto)");
-               else
-#endif
-                       request_region(info->port,8,"serial(auto)");
-       }
-
-       /*
-        * Reset the UART.
-        */
-#ifdef CONFIG_SERIAL_RSA
-       if (state->type == PORT_RSA)
-               serial_outp(info, UART_RSA_FRR, 0);
-#endif
-       serial_outp(info, UART_MCR, save_mcr);
-       serial_outp(info, UART_FCR, (UART_FCR_ENABLE_FIFO |
-                                    UART_FCR_CLEAR_RCVR |
-                                    UART_FCR_CLEAR_XMIT));
-       serial_outp(info, UART_FCR, 0);
-       (void)serial_in(info, UART_RX);
-       serial_outp(info, UART_IER, 0);
-       
-       restore_flags(flags);
-}
-
-int register_serial(struct serial_struct *req);
-void unregister_serial(int line);
-
-#if (LINUX_VERSION_CODE > 0x20100)
-EXPORT_SYMBOL(register_serial);
-EXPORT_SYMBOL(unregister_serial);
-#else
-static struct symbol_table serial_syms = {
-#include <linux/symtab_begin.h>
-       X(register_serial),
-       X(unregister_serial),
-#include <linux/symtab_end.h>
-};
-#endif
-
-
-#if defined(ENABLE_SERIAL_PCI) || defined(ENABLE_SERIAL_PNP) 
-
-static void __devinit printk_pnp_dev_id(unsigned short vendor,
-                                    unsigned short device)
-{
-       printk("%c%c%c%x%x%x%x",
-              'A' + ((vendor >> 2) & 0x3f) - 1,
-              'A' + (((vendor & 3) << 3) | ((vendor >> 13) & 7)) - 1,
-              'A' + ((vendor >> 8) & 0x1f) - 1,
-              (device >> 4) & 0x0f,
-              device & 0x0f,
-              (device >> 12) & 0x0f,
-              (device >> 8) & 0x0f);
-}
-
-static _INLINE_ int get_pci_port(struct pci_dev *dev,
-                                 struct pci_board *board,
-                                 struct serial_struct *req,
-                                 int idx)
-{
-       unsigned long port;
-       int base_idx;
-       int max_port;
-       int offset;
-
-       base_idx = SPCI_FL_GET_BASE(board->flags);
-       if (board->flags & SPCI_FL_BASE_TABLE)
-               base_idx += idx;
-
-       if (board->flags & SPCI_FL_REGION_SZ_CAP) {
-               max_port = pci_resource_len(dev, base_idx) / 8;
-               if (idx >= max_port)
-                       return 1;
-       }
-                       
-       offset = board->first_uart_offset;
-
-       /* Timedia/SUNIX uses a mixture of BARs and offsets */
-       /* Ugh, this is ugly as all hell --- TYT */
-       if(dev->vendor == PCI_VENDOR_ID_TIMEDIA )  /* 0x1409 */
-               switch(idx) {
-                       case 0: base_idx=0;
-                               break;
-                       case 1: base_idx=0; offset=8;
-                               break;
-                       case 2: base_idx=1; 
-                               break;
-                       case 3: base_idx=1; offset=8;
-                               break;
-                       case 4: /* BAR 2*/
-                       case 5: /* BAR 3 */
-                       case 6: /* BAR 4*/
-                       case 7: base_idx=idx-2; /* BAR 5*/
-               }
-
-       /* Some Titan cards are also a little weird */
-       if (dev->vendor == PCI_VENDOR_ID_TITAN &&
-           (dev->device == PCI_DEVICE_ID_TITAN_400L ||
-            dev->device == PCI_DEVICE_ID_TITAN_800L)) {
-               switch (idx) {
-               case 0: base_idx = 1;
-                       break;
-               case 1: base_idx = 2;
-                       break;
-               default:
-                       base_idx = 4;
-                       offset = 8 * (idx - 2);
-               }
-               
-       }
-  
-       port =  pci_resource_start(dev, base_idx) + offset;
-
-       if ((board->flags & SPCI_FL_BASE_TABLE) == 0)
-               port += idx * (board->uart_offset ? board->uart_offset : 8);
-
-       if (IS_PCI_REGION_IOPORT(dev, base_idx)) {
-               req->port = port;
-               if (HIGH_BITS_OFFSET)
-                       req->port_high = port >> HIGH_BITS_OFFSET;
-               else
-                       req->port_high = 0;
-               return 0;
-       }
-       req->io_type = SERIAL_IO_MEM;
-       req->iomem_base = ioremap(port, board->uart_offset);
-       req->iomem_reg_shift = board->reg_shift;
-       req->port = 0;
-       return 0;
-}
-
-static _INLINE_ int get_pci_irq(struct pci_dev *dev,
-                               struct pci_board *board,
-                               int idx)
-{
-       int base_idx;
-
-       if ((board->flags & SPCI_FL_IRQRESOURCE) == 0)
-               return dev->irq;
-
-       base_idx = SPCI_FL_GET_IRQBASE(board->flags);
-       if (board->flags & SPCI_FL_IRQ_TABLE)
-               base_idx += idx;
-       
-       return PCI_IRQ_RESOURCE(dev, base_idx);
-}
-
-/*
- * Common enabler code shared by both PCI and ISAPNP probes
- */
-static void __devinit start_pci_pnp_board(struct pci_dev *dev,
-                                      struct pci_board *board)
-{
-       int k, line;
-       struct serial_struct serial_req;
-       int base_baud;
-
-       if (PREPARE_FUNC(dev) && (PREPARE_FUNC(dev))(dev) < 0) {
-              printk("serial: PNP device '");
-              printk_pnp_dev_id(dev->vendor, dev->device);
-              printk("' prepare failed\n");
-              return;
-       }
-
-       if (ACTIVATE_FUNC(dev) && (ACTIVATE_FUNC(dev))(dev) < 0) {
-              printk("serial: PNP device '");
-              printk_pnp_dev_id(dev->vendor, dev->device);
-              printk("' activate failed\n");
-              return;
-       }
-
-       /*
-        * Run the initialization function, if any
-        */
-       if (board->init_fn && ((board->init_fn)(dev, board, 1) != 0))
-               return;
-
-       /*
-        * Register the serial board in the array if we need to
-        * shutdown the board on a module unload or card removal
-        */
-       if (DEACTIVATE_FUNC(dev) || board->init_fn) {
-               for (k=0; k < NR_PCI_BOARDS; k++)
-                       if (serial_pci_board[k].dev == 0)
-                               break;
-               if (k >= NR_PCI_BOARDS)
-                       return;
-               serial_pci_board[k].board = *board;
-               serial_pci_board[k].dev = dev;
-       }
-
-       base_baud = board->base_baud;
-       if (!base_baud)
-               base_baud = BASE_BAUD;
-       memset(&serial_req, 0, sizeof(serial_req));
-
-       for (k=0; k < board->num_ports; k++) {
-               serial_req.irq = get_pci_irq(dev, board, k);
-               if (get_pci_port(dev, board, &serial_req, k))
-                       break;
-               serial_req.flags = ASYNC_SKIP_TEST | ASYNC_AUTOPROBE;
-#ifdef SERIAL_DEBUG_PCI
-               printk("Setup PCI/PNP port: port %x, irq %d, type %d\n",
-                      serial_req.port, serial_req.irq, serial_req.io_type);
-#endif
-               line = register_serial(&serial_req);
-               if (line < 0)
-                       break;
-               rs_table[line].baud_base = base_baud;
-               rs_table[line].dev = dev;
-       }
-}
-#endif /* ENABLE_SERIAL_PCI || ENABLE_SERIAL_PNP */
-
-#ifdef ENABLE_SERIAL_PCI
-/*
- * Some PCI serial cards using the PLX 9050 PCI interface chip require
- * that the card interrupt be explicitly enabled or disabled.  This
- * seems to be mainly needed on card using the PLX which also use I/O
- * mapped memory.
- */
-static int __devinit
-pci_plx9050_fn(struct pci_dev *dev, struct pci_board *board, int enable)
-{
-       u8 data, *p, irq_config;
-       int pci_config;
-
-       irq_config = 0x41;
-       pci_config = PCI_COMMAND_MEMORY;
-       if (dev->vendor == PCI_VENDOR_ID_PANACOM)
-               irq_config = 0x43;
-       if ((dev->vendor == PCI_VENDOR_ID_PLX) &&
-           (dev->device == PCI_DEVICE_ID_PLX_ROMULUS)) {
-               /*
-                * As the megawolf cards have the int pins active
-                * high, and have 2 UART chips, both ints must be
-                * enabled on the 9050. Also, the UARTS are set in
-                * 16450 mode by default, so we have to enable the
-                * 16C950 'enhanced' mode so that we can use the deep
-                * FIFOs
-                */
-               irq_config = 0x5b;
-               pci_config = PCI_COMMAND_MEMORY | PCI_COMMAND_IO;
-       }
-       
-       pci_read_config_byte(dev, PCI_COMMAND, &data);
-
-       if (enable)
-               pci_write_config_byte(dev, PCI_COMMAND,
-                                     data | pci_config);
-       
-       /* enable/disable interrupts */
-       p = ioremap(pci_resource_start(dev, 0), 0x80);
-       writel(enable ? irq_config : 0x00, (unsigned long)p + 0x4c);
-       iounmap(p);
-
-       if (!enable)
-               pci_write_config_byte(dev, PCI_COMMAND,
-                                     data & ~pci_config);
-       return 0;
-}
-
-
-/*
- * SIIG serial cards have an PCI interface chip which also controls
- * the UART clocking frequency. Each UART can be clocked independently
- * (except cards equiped with 4 UARTs) and initial clocking settings
- * are stored in the EEPROM chip. It can cause problems because this
- * version of serial driver doesn't support differently clocked UART's
- * on single PCI card. To prevent this, initialization functions set
- * high frequency clocking for all UART's on given card. It is safe (I
- * hope) because it doesn't touch EEPROM settings to prevent conflicts
- * with other OSes (like M$ DOS).
- *
- *  SIIG support added by Andrey Panin <pazke@mail.tp.ru>, 10/1999
- * 
- * There is two family of SIIG serial cards with different PCI
- * interface chip and different configuration methods:
- *     - 10x cards have control registers in IO and/or memory space;
- *     - 20x cards have control registers in standard PCI configuration space.
- */
-
-#define PCI_DEVICE_ID_SIIG_1S_10x (PCI_DEVICE_ID_SIIG_1S_10x_550 & 0xfffc)
-#define PCI_DEVICE_ID_SIIG_2S_10x (PCI_DEVICE_ID_SIIG_2S_10x_550 & 0xfff8)
-
-static int __devinit
-pci_siig10x_fn(struct pci_dev *dev, struct pci_board *board, int enable)
-{
-       u16 data, *p;
-
-       if (!enable) return 0;
-
-       p = ioremap(pci_resource_start(dev, 0), 0x80);
-
-       switch (dev->device & 0xfff8) {
-               case PCI_DEVICE_ID_SIIG_1S_10x:         /* 1S */
-                       data = 0xffdf;
-                       break;
-               case PCI_DEVICE_ID_SIIG_2S_10x:         /* 2S, 2S1P */
-                       data = 0xf7ff;
-                       break;
-               default:                                /* 1S1P, 4S */
-                       data = 0xfffb;
-                       break;
-       }
-
-       writew(readw((unsigned long) p + 0x28) & data, (unsigned long) p + 0x28);
-       iounmap(p);
-       return 0;
-}
-
-#define PCI_DEVICE_ID_SIIG_2S_20x (PCI_DEVICE_ID_SIIG_2S_20x_550 & 0xfffc)
-#define PCI_DEVICE_ID_SIIG_2S1P_20x (PCI_DEVICE_ID_SIIG_2S1P_20x_550 & 0xfffc)
-
-static int __devinit
-pci_siig20x_fn(struct pci_dev *dev, struct pci_board *board, int enable)
-{
-       u8 data;
-
-       if (!enable) return 0;
-
-       /* Change clock frequency for the first UART. */
-       pci_read_config_byte(dev, 0x6f, &data);
-       pci_write_config_byte(dev, 0x6f, data & 0xef);
-
-       /* If this card has 2 UART, we have to do the same with second UART. */
-       if (((dev->device & 0xfffc) == PCI_DEVICE_ID_SIIG_2S_20x) ||
-           ((dev->device & 0xfffc) == PCI_DEVICE_ID_SIIG_2S1P_20x)) {
-               pci_read_config_byte(dev, 0x73, &data);
-               pci_write_config_byte(dev, 0x73, data & 0xef);
-       }
-       return 0;
-}
-
-/* Added for EKF Intel i960 serial boards */
-static int __devinit
-pci_inteli960ni_fn(struct pci_dev *dev,
-                  struct pci_board *board,
-                  int enable)
-{
-       unsigned long oldval;
-       
-       if (!(pci_get_subdevice(dev) & 0x1000))
-               return(-1);
-
-       if (!enable) /* is there something to deinit? */
-               return(0);
-   
-#ifdef SERIAL_DEBUG_PCI
-       printk(KERN_DEBUG " Subsystem ID %lx (intel 960)\n",
-              (unsigned long) board->subdevice);
-#endif
-       /* is firmware started? */
-       pci_read_config_dword(dev, 0x44, (void*) &oldval); 
-       if (oldval == 0x00001000L) { /* RESET value */ 
-               printk(KERN_DEBUG "Local i960 firmware missing");
-               return(-1); 
-       }
-       return(0);
-}
-
-/*
- * Timedia has an explosion of boards, and to avoid the PCI table from
- * growing *huge*, we use this function to collapse some 70 entries
- * in the PCI table into one, for sanity's and compactness's sake.
- */
-static unsigned short timedia_single_port[] = {
-       0x4025, 0x4027, 0x4028, 0x5025, 0x5027, 0 };
-static unsigned short timedia_dual_port[] = {
-       0x0002, 0x4036, 0x4037, 0x4038, 0x4078, 0x4079, 0x4085,
-       0x4088, 0x4089, 0x5037, 0x5078, 0x5079, 0x5085, 0x6079, 
-       0x7079, 0x8079, 0x8137, 0x8138, 0x8237, 0x8238, 0x9079, 
-       0x9137, 0x9138, 0x9237, 0x9238, 0xA079, 0xB079, 0xC079,
-       0xD079, 0 };
-static unsigned short timedia_quad_port[] = {
-       0x4055, 0x4056, 0x4095, 0x4096, 0x5056, 0x8156, 0x8157, 
-       0x8256, 0x8257, 0x9056, 0x9156, 0x9157, 0x9158, 0x9159, 
-       0x9256, 0x9257, 0xA056, 0xA157, 0xA158, 0xA159, 0xB056,
-       0xB157, 0 };
-static unsigned short timedia_eight_port[] = {
-       0x4065, 0x4066, 0x5065, 0x5066, 0x8166, 0x9066, 0x9166, 
-       0x9167, 0x9168, 0xA066, 0xA167, 0xA168, 0 };
-static struct timedia_struct {
-       int num;
-       unsigned short *ids;
-} timedia_data[] = {
-       { 1, timedia_single_port },
-       { 2, timedia_dual_port },
-       { 4, timedia_quad_port },
-       { 8, timedia_eight_port },
-       { 0, 0 }
-};
-
-static int __devinit
-pci_timedia_fn(struct pci_dev *dev, struct pci_board *board, int enable)
-{
-       int     i, j;
-       unsigned short *ids;
-
-       if (!enable)
-               return 0;
-
-       for (i=0; timedia_data[i].num; i++) {
-               ids = timedia_data[i].ids;
-               for (j=0; ids[j]; j++) {
-                       if (pci_get_subdevice(dev) == ids[j]) {
-                               board->num_ports = timedia_data[i].num;
-                               return 0;
-                       }
-               }
-       }
-       return 0;
-}
-
-static int __devinit
-pci_xircom_fn(struct pci_dev *dev, struct pci_board *board, int enable)
-{
-       __set_current_state(TASK_UNINTERRUPTIBLE);
-       schedule_timeout(HZ/10);
-       return 0;
-}
-
-/*
- * This is the configuration table for all of the PCI serial boards
- * which we support.  It is directly indexed by the pci_board_num_t enum
- * value, which is encoded in the pci_device_id PCI probe table's
- * driver_data member.
- */
-enum pci_board_num_t {
-       pbn_b0_1_115200,
-       pbn_default = 0,
-
-       pbn_b0_2_115200,
-       pbn_b0_4_115200,
-
-       pbn_b0_1_921600,
-       pbn_b0_2_921600,
-       pbn_b0_4_921600,
-
-       pbn_b0_bt_1_115200,
-       pbn_b0_bt_2_115200,
-       pbn_b0_bt_1_460800,
-       pbn_b0_bt_2_460800,
-
-       pbn_b1_1_115200,
-       pbn_b1_2_115200,
-       pbn_b1_4_115200,
-       pbn_b1_8_115200,
-
-       pbn_b1_2_921600,
-       pbn_b1_4_921600,
-       pbn_b1_8_921600,
-
-       pbn_b1_2_1382400,
-       pbn_b1_4_1382400,
-       pbn_b1_8_1382400,
-
-       pbn_b2_8_115200,
-       pbn_b2_4_460800,
-       pbn_b2_8_460800,
-       pbn_b2_16_460800,
-       pbn_b2_4_921600,
-       pbn_b2_8_921600,
-
-       pbn_b2_bt_1_115200,
-       pbn_b2_bt_2_115200,
-       pbn_b2_bt_4_115200,
-       pbn_b2_bt_2_921600,
-
-       pbn_panacom,
-       pbn_panacom2,
-       pbn_panacom4,
-       pbn_plx_romulus,
-       pbn_oxsemi,
-       pbn_timedia,
-       pbn_intel_i960,
-       pbn_sgi_ioc3,
-#ifdef CONFIG_DDB5074
-       pbn_nec_nile4,
-#endif
-#if 0
-       pbn_dci_pccom8,
-#endif
-       pbn_xircom_combo,
-
-       pbn_siig10x_0,
-       pbn_siig10x_1,
-       pbn_siig10x_2,
-       pbn_siig10x_4,
-       pbn_siig20x_0,
-       pbn_siig20x_2,
-       pbn_siig20x_4,
-       
-       pbn_computone_4,
-       pbn_computone_6,
-       pbn_computone_8,
-};
-
-static struct pci_board pci_boards[] __devinitdata = {
-       /*
-        * PCI Flags, Number of Ports, Base (Maximum) Baud Rate,
-        * Offset to get to next UART's registers,
-        * Register shift to use for memory-mapped I/O,
-        * Initialization function, first UART offset
-        */
-
-       /* Generic serial board, pbn_b0_1_115200, pbn_default */
-       { SPCI_FL_BASE0, 1, 115200 },           /* pbn_b0_1_115200,
-                                                  pbn_default */
-
-       { SPCI_FL_BASE0, 2, 115200 },           /* pbn_b0_2_115200 */
-       { SPCI_FL_BASE0, 4, 115200 },           /* pbn_b0_4_115200 */
-
-       { SPCI_FL_BASE0, 1, 921600 },           /* pbn_b0_1_921600 */
-       { SPCI_FL_BASE0, 2, 921600 },           /* pbn_b0_2_921600 */
-       { SPCI_FL_BASE0, 4, 921600 },           /* pbn_b0_4_921600 */
-
-       { SPCI_FL_BASE0 | SPCI_FL_BASE_TABLE, 1, 115200 }, /* pbn_b0_bt_1_115200 */
-       { SPCI_FL_BASE0 | SPCI_FL_BASE_TABLE, 2, 115200 }, /* pbn_b0_bt_2_115200 */
-       { SPCI_FL_BASE0 | SPCI_FL_BASE_TABLE, 1, 460800 }, /* pbn_b0_bt_1_460800 */
-       { SPCI_FL_BASE0 | SPCI_FL_BASE_TABLE, 2, 460800 }, /* pbn_b0_bt_2_460800 */
-
-       { SPCI_FL_BASE1, 1, 115200 },           /* pbn_b1_1_115200 */
-       { SPCI_FL_BASE1, 2, 115200 },           /* pbn_b1_2_115200 */
-       { SPCI_FL_BASE1, 4, 115200 },           /* pbn_b1_4_115200 */
-       { SPCI_FL_BASE1, 8, 115200 },           /* pbn_b1_8_115200 */
-
-       { SPCI_FL_BASE1, 2, 921600 },           /* pbn_b1_2_921600 */
-       { SPCI_FL_BASE1, 4, 921600 },           /* pbn_b1_4_921600 */
-       { SPCI_FL_BASE1, 8, 921600 },           /* pbn_b1_8_921600 */
-
-       { SPCI_FL_BASE1, 2, 1382400 },          /* pbn_b1_2_1382400 */
-       { SPCI_FL_BASE1, 4, 1382400 },          /* pbn_b1_4_1382400 */
-       { SPCI_FL_BASE1, 8, 1382400 },          /* pbn_b1_8_1382400 */
-
-       { SPCI_FL_BASE2, 8, 115200 },           /* pbn_b2_8_115200 */
-       { SPCI_FL_BASE2, 4, 460800 },           /* pbn_b2_4_460800 */
-       { SPCI_FL_BASE2, 8, 460800 },           /* pbn_b2_8_460800 */
-       { SPCI_FL_BASE2, 16, 460800 },          /* pbn_b2_16_460800 */
-       { SPCI_FL_BASE2, 4, 921600 },           /* pbn_b2_4_921600 */
-       { SPCI_FL_BASE2, 8, 921600 },           /* pbn_b2_8_921600 */
-
-       { SPCI_FL_BASE2 | SPCI_FL_BASE_TABLE, 1, 115200 }, /* pbn_b2_bt_1_115200 */
-       { SPCI_FL_BASE2 | SPCI_FL_BASE_TABLE, 2, 115200 }, /* pbn_b2_bt_2_115200 */
-       { SPCI_FL_BASE2 | SPCI_FL_BASE_TABLE, 4, 115200 }, /* pbn_b2_bt_4_115200 */
-       { SPCI_FL_BASE2 | SPCI_FL_BASE_TABLE, 2, 921600 }, /* pbn_b2_bt_2_921600 */
-
-       { SPCI_FL_BASE2, 2, 921600, /* IOMEM */            /* pbn_panacom */
-               0x400, 7, pci_plx9050_fn },
-       { SPCI_FL_BASE2 | SPCI_FL_BASE_TABLE, 2, 921600,   /* pbn_panacom2 */
-               0x400, 7, pci_plx9050_fn },
-       { SPCI_FL_BASE2 | SPCI_FL_BASE_TABLE, 4, 921600,   /* pbn_panacom4 */
-               0x400, 7, pci_plx9050_fn },
-       { SPCI_FL_BASE2, 4, 921600,                        /* pbn_plx_romulus */
-               0x20, 2, pci_plx9050_fn, 0x03 },
-               /* This board uses the size of PCI Base region 0 to
-                * signal now many ports are available */
-       { SPCI_FL_BASE0 | SPCI_FL_REGION_SZ_CAP, 32, 115200 }, /* pbn_oxsemi */
-       { SPCI_FL_BASE_TABLE, 1, 921600,                   /* pbn_timedia */
-               0, 0, pci_timedia_fn },
-       /* EKF addition for i960 Boards form EKF with serial port */
-       { SPCI_FL_BASE0, 32, 921600, /* max 256 ports */   /* pbn_intel_i960 */
-               8<<2, 2, pci_inteli960ni_fn, 0x10000},
-       { SPCI_FL_BASE0 | SPCI_FL_IRQRESOURCE,             /* pbn_sgi_ioc3 */
-               1, 458333, 0, 0, 0, 0x20178 },
-#ifdef CONFIG_DDB5074
-       /*
-        * NEC Vrc-5074 (Nile 4) builtin UART.
-        * Conditionally compiled in since this is a motherboard device.
-        */
-       { SPCI_FL_BASE0, 1, 520833,                        /* pbn_nec_nile4 */
-               64, 3, NULL, 0x300 },
-#endif
-#if 0  /* PCI_DEVICE_ID_DCI_PCCOM8 ? */                   /* pbn_dci_pccom8 */
-       { SPCI_FL_BASE3, 8, 115200, 8 },
-#endif
-       { SPCI_FL_BASE0, 1, 115200,                       /* pbn_xircom_combo */
-               0, 0, pci_xircom_fn },
-
-       { SPCI_FL_BASE2, 1, 460800,                        /* pbn_siig10x_0 */
-               0, 0, pci_siig10x_fn },
-       { SPCI_FL_BASE2, 1, 921600,                        /* pbn_siig10x_1 */
-               0, 0, pci_siig10x_fn },
-       { SPCI_FL_BASE2 | SPCI_FL_BASE_TABLE, 2, 921600,   /* pbn_siig10x_2 */
-               0, 0, pci_siig10x_fn },
-       { SPCI_FL_BASE2 | SPCI_FL_BASE_TABLE, 4, 921600,   /* pbn_siig10x_4 */
-               0, 0, pci_siig10x_fn },
-       { SPCI_FL_BASE0, 1, 921600,                        /* pbn_siix20x_0 */
-               0, 0, pci_siig20x_fn },
-       { SPCI_FL_BASE0 | SPCI_FL_BASE_TABLE, 2, 921600,   /* pbn_siix20x_2 */
-               0, 0, pci_siig20x_fn },
-       { SPCI_FL_BASE0 | SPCI_FL_BASE_TABLE, 4, 921600,   /* pbn_siix20x_4 */
-               0, 0, pci_siig20x_fn },
-
-       { SPCI_FL_BASE0, 4, 921600, /* IOMEM */            /* pbn_computone_4 */
-               0x40, 2, NULL, 0x200 },
-       { SPCI_FL_BASE0, 6, 921600, /* IOMEM */            /* pbn_computone_6 */
-               0x40, 2, NULL, 0x200 },
-       { SPCI_FL_BASE0, 8, 921600, /* IOMEM */            /* pbn_computone_8 */
-               0x40, 2, NULL, 0x200 },
-};
-
-/*
- * Given a complete unknown PCI device, try to use some heuristics to
- * guess what the configuration might be, based on the pitiful PCI
- * serial specs.  Returns 0 on success, 1 on failure.
- */
-static int __devinit serial_pci_guess_board(struct pci_dev *dev,
-                                          struct pci_board *board)
-{
-       int     num_iomem = 0, num_port = 0, first_port = -1;
-       int     i;
-       
-       /*
-        * If it is not a communications device or the programming
-        * interface is greater than 6, give up.
-        *
-        * (Should we try to make guesses for multiport serial devices
-        * later?) 
-        */
-       if ((((dev->class >> 8) != PCI_CLASS_COMMUNICATION_SERIAL) &&
-           ((dev->class >> 8) != PCI_CLASS_COMMUNICATION_MODEM)) ||
-           (dev->class & 0xff) > 6)
-               return 1;
-
-       for (i=0; i < 6; i++) {
-               if (IS_PCI_REGION_IOPORT(dev, i)) {
-                       num_port++;
-                       if (first_port == -1)
-                               first_port = i;
-               }
-               if (IS_PCI_REGION_IOMEM(dev, i))
-                       num_iomem++;
-       }
-
-       /*
-        * If there is 1 or 0 iomem regions, and exactly one port, use
-        * it.
-        */
-       if (num_iomem <= 1 && num_port == 1) {
-               board->flags = first_port;
-               return 0;
-       }
-       return 1;
-}
-
-static int __devinit serial_init_one(struct pci_dev *dev,
-                                    const struct pci_device_id *ent)
-{
-       struct pci_board *board, tmp;
-       int rc;
-
-       board = &pci_boards[ent->driver_data];
-
-       rc = pci_enable_device(dev);
-       if (rc) return rc;
-
-       if (ent->driver_data == pbn_default &&
-           serial_pci_guess_board(dev, board))
-               return -ENODEV;
-       else if (serial_pci_guess_board(dev, &tmp) == 0) {
-               printk(KERN_INFO "Redundant entry in serial pci_table.  "
-                      "Please send the output of\n"
-                      "lspci -vv, this message (%04x,%04x,%04x,%04x)\n"
-                      "and the manufacturer and name of "
-                      "serial board or modem board\n"
-                      "to serial-pci-info@lists.sourceforge.net.\n",
-                      dev->vendor, dev->device,
-                      pci_get_subvendor(dev), pci_get_subdevice(dev));
-       }
-                      
-       start_pci_pnp_board(dev, board);
-
-       return 0;
-}
-
-static void __devexit serial_remove_one(struct pci_dev *dev)
-{
-       int     i;
-
-       /*
-        * Iterate through all of the ports finding those that belong
-        * to this PCI device.
-        */
-       for(i = 0; i < NR_PORTS; i++) {
-               if (rs_table[i].dev != dev)
-                       continue;
-               unregister_serial(i);
-               rs_table[i].dev = 0;
-       }
-       /*
-        * Now execute any board-specific shutdown procedure
-        */
-       for (i=0; i < NR_PCI_BOARDS; i++) {
-               struct pci_board_inst *brd = &serial_pci_board[i];
-
-               if (serial_pci_board[i].dev != dev)
-                       continue;
-               if (brd->board.init_fn)
-                       (brd->board.init_fn)(brd->dev, &brd->board, 0);
-               if (DEACTIVATE_FUNC(brd->dev))
-                       (DEACTIVATE_FUNC(brd->dev))(brd->dev);
-               serial_pci_board[i].dev = 0;
-       }
-}
-
-
-static struct pci_device_id serial_pci_tbl[] __devinitdata = {
-       {       PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V960,
-               PCI_SUBVENDOR_ID_CONNECT_TECH,
-               PCI_SUBDEVICE_ID_CONNECT_TECH_BH8_232, 0, 0,
-               pbn_b1_8_1382400 },
-       {       PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V960,
-               PCI_SUBVENDOR_ID_CONNECT_TECH,
-               PCI_SUBDEVICE_ID_CONNECT_TECH_BH4_232, 0, 0,
-               pbn_b1_4_1382400 },
-       {       PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V960,
-               PCI_SUBVENDOR_ID_CONNECT_TECH,
-               PCI_SUBDEVICE_ID_CONNECT_TECH_BH2_232, 0, 0,
-               pbn_b1_2_1382400 },
-       {       PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V351,
-               PCI_SUBVENDOR_ID_CONNECT_TECH,
-               PCI_SUBDEVICE_ID_CONNECT_TECH_BH8_232, 0, 0,
-               pbn_b1_8_1382400 },
-       {       PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V351,
-               PCI_SUBVENDOR_ID_CONNECT_TECH,
-               PCI_SUBDEVICE_ID_CONNECT_TECH_BH4_232, 0, 0,
-               pbn_b1_4_1382400 },
-       {       PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V351,
-               PCI_SUBVENDOR_ID_CONNECT_TECH,
-               PCI_SUBDEVICE_ID_CONNECT_TECH_BH2_232, 0, 0,
-               pbn_b1_2_1382400 },
-       {       PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V351,
-               PCI_SUBVENDOR_ID_CONNECT_TECH,
-               PCI_SUBDEVICE_ID_CONNECT_TECH_BH8_485, 0, 0,
-               pbn_b1_8_921600 },
-       {       PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V351,
-               PCI_SUBVENDOR_ID_CONNECT_TECH,
-               PCI_SUBDEVICE_ID_CONNECT_TECH_BH8_485_4_4, 0, 0,
-               pbn_b1_8_921600 },
-       {       PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V351,
-               PCI_SUBVENDOR_ID_CONNECT_TECH,
-               PCI_SUBDEVICE_ID_CONNECT_TECH_BH4_485, 0, 0,
-               pbn_b1_4_921600 },
-       {       PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V351,
-               PCI_SUBVENDOR_ID_CONNECT_TECH,
-               PCI_SUBDEVICE_ID_CONNECT_TECH_BH4_485_2_2, 0, 0,
-               pbn_b1_4_921600 },
-       {       PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V351,
-               PCI_SUBVENDOR_ID_CONNECT_TECH,
-               PCI_SUBDEVICE_ID_CONNECT_TECH_BH2_485, 0, 0,
-               pbn_b1_2_921600 },
-       {       PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V351,
-               PCI_SUBVENDOR_ID_CONNECT_TECH,
-               PCI_SUBDEVICE_ID_CONNECT_TECH_BH8_485_2_6, 0, 0,
-               pbn_b1_8_921600 },
-       {       PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V351,
-               PCI_SUBVENDOR_ID_CONNECT_TECH,
-               PCI_SUBDEVICE_ID_CONNECT_TECH_BH081101V1, 0, 0,
-               pbn_b1_8_921600 },
-       {       PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V351,
-               PCI_SUBVENDOR_ID_CONNECT_TECH,
-               PCI_SUBDEVICE_ID_CONNECT_TECH_BH041101V1, 0, 0,
-               pbn_b1_4_921600 },
-
-       {       PCI_VENDOR_ID_SEALEVEL, PCI_DEVICE_ID_SEALEVEL_U530,
-               PCI_ANY_ID, PCI_ANY_ID, 0, 0, 
-               pbn_b2_bt_1_115200 },
-       {       PCI_VENDOR_ID_SEALEVEL, PCI_DEVICE_ID_SEALEVEL_UCOMM2,
-               PCI_ANY_ID, PCI_ANY_ID, 0, 0, 
-               pbn_b2_bt_2_115200 },
-       {       PCI_VENDOR_ID_SEALEVEL, PCI_DEVICE_ID_SEALEVEL_UCOMM422,
-               PCI_ANY_ID, PCI_ANY_ID, 0, 0, 
-               pbn_b2_bt_4_115200 },
-       {       PCI_VENDOR_ID_SEALEVEL, PCI_DEVICE_ID_SEALEVEL_UCOMM232,
-               PCI_ANY_ID, PCI_ANY_ID, 0, 0, 
-               pbn_b2_bt_2_115200 },
-       {       PCI_VENDOR_ID_SEALEVEL, PCI_DEVICE_ID_SEALEVEL_COMM4,
-               PCI_ANY_ID, PCI_ANY_ID, 0, 0, 
-               pbn_b2_bt_4_115200 },
-       {       PCI_VENDOR_ID_SEALEVEL, PCI_DEVICE_ID_SEALEVEL_COMM8,
-               PCI_ANY_ID, PCI_ANY_ID, 0, 0, 
-               pbn_b2_8_115200 },
-
-       {       PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_GTEK_SERIAL2,
-               PCI_ANY_ID, PCI_ANY_ID, 0, 0,
-               pbn_b2_bt_2_115200 },
-       {       PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_SPCOM200,
-               PCI_ANY_ID, PCI_ANY_ID, 0, 0,
-               pbn_b2_bt_2_921600 },
-       /* VScom SPCOM800, from sl@s.pl */
-       {       PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_SPCOM800, 
-               PCI_ANY_ID, PCI_ANY_ID, 0, 0, 
-               pbn_b2_8_921600 },
-       {       PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_1077,
-               PCI_ANY_ID, PCI_ANY_ID, 0, 0, 
-               pbn_b2_4_921600 },
-       {       PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9050,
-               PCI_SUBVENDOR_ID_KEYSPAN,
-               PCI_SUBDEVICE_ID_KEYSPAN_SX2, 0, 0,
-               pbn_panacom },
-       {       PCI_VENDOR_ID_PANACOM, PCI_DEVICE_ID_PANACOM_QUADMODEM,
-               PCI_ANY_ID, PCI_ANY_ID, 0, 0,
-               pbn_panacom4 },
-       {       PCI_VENDOR_ID_PANACOM, PCI_DEVICE_ID_PANACOM_DUALMODEM,
-               PCI_ANY_ID, PCI_ANY_ID, 0, 0,
-               pbn_panacom2 },
-       {       PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9050,
-               PCI_SUBVENDOR_ID_CHASE_PCIFAST,
-               PCI_SUBDEVICE_ID_CHASE_PCIFAST4, 0, 0, 
-               pbn_b2_4_460800 },
-       {       PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9050,
-               PCI_SUBVENDOR_ID_CHASE_PCIFAST,
-               PCI_SUBDEVICE_ID_CHASE_PCIFAST8, 0, 0, 
-               pbn_b2_8_460800 },
-       {       PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9050,
-               PCI_SUBVENDOR_ID_CHASE_PCIFAST,
-               PCI_SUBDEVICE_ID_CHASE_PCIFAST16, 0, 0, 
-               pbn_b2_16_460800 },
-       {       PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9050,
-               PCI_SUBVENDOR_ID_CHASE_PCIFAST,
-               PCI_SUBDEVICE_ID_CHASE_PCIFAST16FMC, 0, 0, 
-               pbn_b2_16_460800 },
-       {       PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9050,
-               PCI_SUBVENDOR_ID_CHASE_PCIRAS,
-               PCI_SUBDEVICE_ID_CHASE_PCIRAS4, 0, 0, 
-               pbn_b2_4_460800 },
-       {       PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9050,
-               PCI_SUBVENDOR_ID_CHASE_PCIRAS,
-               PCI_SUBDEVICE_ID_CHASE_PCIRAS8, 0, 0, 
-               pbn_b2_8_460800 },
-       /* Megawolf Romulus PCI Serial Card, from Mike Hudson */
-       /* (Exoray@isys.ca) */
-       {       PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_ROMULUS,
-               0x10b5, 0x106a, 0, 0,
-               pbn_plx_romulus },
-       {       PCI_VENDOR_ID_QUATECH, PCI_DEVICE_ID_QUATECH_QSC100,
-               PCI_ANY_ID, PCI_ANY_ID, 0, 0, 
-               pbn_b1_4_115200 },
-       {       PCI_VENDOR_ID_QUATECH, PCI_DEVICE_ID_QUATECH_DSC100,
-               PCI_ANY_ID, PCI_ANY_ID, 0, 0, 
-               pbn_b1_2_115200 },
-       {       PCI_VENDOR_ID_QUATECH, PCI_DEVICE_ID_QUATECH_ESC100D,
-               PCI_ANY_ID, PCI_ANY_ID, 0, 0, 
-               pbn_b1_8_115200 },
-       {       PCI_VENDOR_ID_QUATECH, PCI_DEVICE_ID_QUATECH_ESC100M,
-               PCI_ANY_ID, PCI_ANY_ID, 0, 0, 
-               pbn_b1_8_115200 },
-       {       PCI_VENDOR_ID_SPECIALIX, PCI_DEVICE_ID_OXSEMI_16PCI954,
-               PCI_VENDOR_ID_SPECIALIX, PCI_SUBDEVICE_ID_SPECIALIX_SPEED4, 0, 0, 
-               pbn_b0_4_921600 },
-       {       PCI_VENDOR_ID_OXSEMI, PCI_DEVICE_ID_OXSEMI_16PCI954,
-               PCI_ANY_ID, PCI_ANY_ID, 0, 0, 
-               pbn_b0_4_115200 },
-       {       PCI_VENDOR_ID_OXSEMI, PCI_DEVICE_ID_OXSEMI_16PCI952,
-               PCI_ANY_ID, PCI_ANY_ID, 0, 0, 
-               pbn_b0_2_115200 },
-
-       /* Digitan DS560-558, from jimd@esoft.com */
-       {       PCI_VENDOR_ID_ATT, PCI_DEVICE_ID_ATT_VENUS_MODEM,
-               PCI_ANY_ID, PCI_ANY_ID, 0, 0, 
-               pbn_b1_1_115200 },
-
-       /* 3Com US Robotics 56k Voice Internal PCI model 5610 */
-       {       PCI_VENDOR_ID_USR, 0x1008,
-               PCI_ANY_ID, PCI_ANY_ID, },
-
-       /* Titan Electronic cards */
-       {       PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_100,
-               PCI_ANY_ID, PCI_ANY_ID, 0, 0, 
-               pbn_b0_1_921600 },
-       {       PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_200,
-               PCI_ANY_ID, PCI_ANY_ID, 0, 0, 
-               pbn_b0_2_921600 },
-       {       PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_400,
-               PCI_ANY_ID, PCI_ANY_ID, 0, 0, 
-               pbn_b0_4_921600 },
-       {       PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_800B,
-               PCI_ANY_ID, PCI_ANY_ID, 0, 0, 
-               pbn_b0_4_921600 },
-       {       PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_100L,
-               PCI_ANY_ID, PCI_ANY_ID,
-               SPCI_FL_BASE1, 1, 921600 },
-       {       PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_200L,
-               PCI_ANY_ID, PCI_ANY_ID,
-               SPCI_FL_BASE1 | SPCI_FL_BASE_TABLE, 2, 921600 },
-       /* The 400L and 800L have a custom hack in get_pci_port */
-       {       PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_400L,
-               PCI_ANY_ID, PCI_ANY_ID,
-               SPCI_FL_BASE_TABLE, 4, 921600 },
-       {       PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_800L,
-               PCI_ANY_ID, PCI_ANY_ID,
-               SPCI_FL_BASE_TABLE, 8, 921600 },
-
-       {       PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S_10x_550,
-               PCI_ANY_ID, PCI_ANY_ID, 0, 0,
-               pbn_siig10x_0 },
-       {       PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S_10x_650,
-               PCI_ANY_ID, PCI_ANY_ID, 0, 0,
-               pbn_siig10x_0 },
-       {       PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S_10x_850,
-               PCI_ANY_ID, PCI_ANY_ID, 0, 0,
-               pbn_siig10x_0 },
-       {       PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S1P_10x_550,
-               PCI_ANY_ID, PCI_ANY_ID, 0, 0,
-               pbn_siig10x_1 },
-       {       PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S1P_10x_650,
-               PCI_ANY_ID, PCI_ANY_ID, 0, 0,
-               pbn_siig10x_1 },
-       {       PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S1P_10x_850,
-               PCI_ANY_ID, PCI_ANY_ID, 0, 0,
-               pbn_siig10x_1 },
-       {       PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S_10x_550,
-               PCI_ANY_ID, PCI_ANY_ID, 0, 0,
-               pbn_siig10x_2 },
-       {       PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S_10x_650,
-               PCI_ANY_ID, PCI_ANY_ID, 0, 0,
-               pbn_siig10x_2 },
-       {       PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S_10x_850,
-               PCI_ANY_ID, PCI_ANY_ID, 0, 0,
-               pbn_siig10x_2 },
-       {       PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S1P_10x_550,
-               PCI_ANY_ID, PCI_ANY_ID, 0, 0,
-               pbn_siig10x_2 },
-       {       PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S1P_10x_650,
-               PCI_ANY_ID, PCI_ANY_ID, 0, 0,
-               pbn_siig10x_2 },
-       {       PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S1P_10x_850,
-               PCI_ANY_ID, PCI_ANY_ID, 0, 0,
-               pbn_siig10x_2 },
-       {       PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_4S_10x_550,
-               PCI_ANY_ID, PCI_ANY_ID, 0, 0,
-               pbn_siig10x_4 },
-       {       PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_4S_10x_650,
-               PCI_ANY_ID, PCI_ANY_ID, 0, 0,
-               pbn_siig10x_4 },
-       {       PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_4S_10x_850,
-               PCI_ANY_ID, PCI_ANY_ID, 0, 0,
-               pbn_siig10x_4 },
-       {       PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S_20x_550,
-               PCI_ANY_ID, PCI_ANY_ID, 0, 0,
-               pbn_siig20x_0 },
-       {       PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S_20x_650,
-               PCI_ANY_ID, PCI_ANY_ID, 0, 0,
-               pbn_siig20x_0 },
-       {       PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S_20x_850,
-               PCI_ANY_ID, PCI_ANY_ID, 0, 0,
-               pbn_siig20x_0 },
-       {       PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S1P_20x_550,
-               PCI_ANY_ID, PCI_ANY_ID, 0, 0,
-               pbn_siig20x_0 },
-       {       PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S1P_20x_650,
-               PCI_ANY_ID, PCI_ANY_ID, 0, 0,
-               pbn_siig20x_0 },
-       {       PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S1P_20x_850,
-               PCI_ANY_ID, PCI_ANY_ID, 0, 0,
-               pbn_siig20x_0 },
-       {       PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2P1S_20x_550,
-               PCI_ANY_ID, PCI_ANY_ID, 0, 0,
-               pbn_siig20x_0 },
-       {       PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2P1S_20x_650,
-               PCI_ANY_ID, PCI_ANY_ID, 0, 0,
-               pbn_siig20x_0 },
-       {       PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2P1S_20x_850,
-               PCI_ANY_ID, PCI_ANY_ID, 0, 0,
-               pbn_siig20x_0 },
-       {       PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S_20x_550,
-               PCI_ANY_ID, PCI_ANY_ID, 0, 0,
-               pbn_siig20x_2 },
-       {       PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S_20x_650,
-               PCI_ANY_ID, PCI_ANY_ID, 0, 0,
-               pbn_siig20x_2 },
-       {       PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S_20x_850,
-               PCI_ANY_ID, PCI_ANY_ID, 0, 0,
-               pbn_siig20x_2 },
-       {       PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S1P_20x_550,
-               PCI_ANY_ID, PCI_ANY_ID, 0, 0,
-               pbn_siig20x_2 },
-       {       PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S1P_20x_650,
-               PCI_ANY_ID, PCI_ANY_ID, 0, 0,
-               pbn_siig20x_2 },
-       {       PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S1P_20x_850,
-               PCI_ANY_ID, PCI_ANY_ID, 0, 0,
-               pbn_siig20x_2 },
-       {       PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_4S_20x_550,
-               PCI_ANY_ID, PCI_ANY_ID, 0, 0,
-               pbn_siig20x_4 },
-       {       PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_4S_20x_650,
-               PCI_ANY_ID, PCI_ANY_ID, 0, 0,
-               pbn_siig20x_4 },
-       {       PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_4S_20x_850,
-               PCI_ANY_ID, PCI_ANY_ID, 0, 0,
-               pbn_siig20x_4 },
-
-       /* Computone devices submitted by Doug McNash dmcnash@computone.com */
-       {       PCI_VENDOR_ID_COMPUTONE, PCI_DEVICE_ID_COMPUTONE_PG,
-               PCI_SUBVENDOR_ID_COMPUTONE, PCI_SUBDEVICE_ID_COMPUTONE_PG4,
-               0, 0, pbn_computone_4 },
-       {       PCI_VENDOR_ID_COMPUTONE, PCI_DEVICE_ID_COMPUTONE_PG,
-               PCI_SUBVENDOR_ID_COMPUTONE, PCI_SUBDEVICE_ID_COMPUTONE_PG8,
-               0, 0, pbn_computone_8 },
-       {       PCI_VENDOR_ID_COMPUTONE, PCI_DEVICE_ID_COMPUTONE_PG,
-               PCI_SUBVENDOR_ID_COMPUTONE, PCI_SUBDEVICE_ID_COMPUTONE_PG6,
-               0, 0, pbn_computone_6 },
-
-       {       PCI_VENDOR_ID_OXSEMI, PCI_DEVICE_ID_OXSEMI_16PCI95N,
-               PCI_ANY_ID, PCI_ANY_ID, 0, 0, pbn_oxsemi },
-       {       PCI_VENDOR_ID_TIMEDIA, PCI_DEVICE_ID_TIMEDIA_1889,
-               PCI_VENDOR_ID_TIMEDIA, PCI_ANY_ID, 0, 0, pbn_timedia },
-
-       {       PCI_VENDOR_ID_LAVA, PCI_DEVICE_ID_LAVA_DSERIAL,
-               PCI_ANY_ID, PCI_ANY_ID, 0, 0,
-               pbn_b0_bt_2_115200 },
-       {       PCI_VENDOR_ID_LAVA, PCI_DEVICE_ID_LAVA_QUATRO_A,
-               PCI_ANY_ID, PCI_ANY_ID, 0, 0,
-               pbn_b0_bt_2_115200 },
-       {       PCI_VENDOR_ID_LAVA, PCI_DEVICE_ID_LAVA_QUATRO_B,
-               PCI_ANY_ID, PCI_ANY_ID, 0, 0,
-               pbn_b0_bt_2_115200 },
-       {       PCI_VENDOR_ID_LAVA, PCI_DEVICE_ID_LAVA_PORT_PLUS,
-               PCI_ANY_ID, PCI_ANY_ID, 0, 0,
-               pbn_b0_bt_2_460800 },
-       {       PCI_VENDOR_ID_LAVA, PCI_DEVICE_ID_LAVA_QUAD_A,
-               PCI_ANY_ID, PCI_ANY_ID, 0, 0,
-               pbn_b0_bt_2_460800 },
-       {       PCI_VENDOR_ID_LAVA, PCI_DEVICE_ID_LAVA_QUAD_B,
-               PCI_ANY_ID, PCI_ANY_ID, 0, 0,
-               pbn_b0_bt_2_460800 },
-       {       PCI_VENDOR_ID_LAVA, PCI_DEVICE_ID_LAVA_SSERIAL,
-               PCI_ANY_ID, PCI_ANY_ID, 0, 0,
-               pbn_b0_bt_1_115200 },
-       {       PCI_VENDOR_ID_LAVA, PCI_DEVICE_ID_LAVA_PORT_650,
-               PCI_ANY_ID, PCI_ANY_ID, 0, 0,
-               pbn_b0_bt_1_460800 },
-
-       /* RAStel 2 port modem, gerg@moreton.com.au */
-       {       PCI_VENDOR_ID_MORETON, PCI_DEVICE_ID_RASTEL_2PORT,
-               PCI_ANY_ID, PCI_ANY_ID, 0, 0,
-               pbn_b2_bt_2_115200 },
-
-       /* EKF addition for i960 Boards form EKF with serial port */
-       {       PCI_VENDOR_ID_INTEL, 0x1960,
-               0xE4BF, PCI_ANY_ID, 0, 0,
-               pbn_intel_i960 },
-
-       /* Xircom Cardbus/Ethernet combos */
-       {       PCI_VENDOR_ID_XIRCOM, PCI_DEVICE_ID_XIRCOM_X3201_MDM,
-               PCI_ANY_ID, PCI_ANY_ID, 0, 0,
-               pbn_xircom_combo },
-
-       /*
-        * Untested PCI modems, sent in from various folks...
-        */
-
-       /* Elsa Model 56K PCI Modem, from Andreas Rath <arh@01019freenet.de> */
-       {       PCI_VENDOR_ID_ROCKWELL, 0x1004,
-               0x1048, 0x1500, 0, 0,
-               pbn_b1_1_115200 },
-
-       {       PCI_VENDOR_ID_SGI, PCI_DEVICE_ID_SGI_IOC3,
-               0xFF00, 0, 0, 0,
-               pbn_sgi_ioc3 },
-
-#ifdef CONFIG_DDB5074
-       /*
-        * NEC Vrc-5074 (Nile 4) builtin UART.
-        * Conditionally compiled in since this is a motherboard device.
-        */
-       {       PCI_VENDOR_ID_NEC, PCI_DEVICE_ID_NEC_NILE4,
-               PCI_ANY_ID, PCI_ANY_ID, 0, 0,
-               pbn_nec_nile4 },
-#endif
-
-#if 0  /* PCI_DEVICE_ID_DCI_PCCOM8 ? */
-       {       PCI_VENDOR_ID_DCI, PCI_DEVICE_ID_DCI_PCCOM8,
-               PCI_ANY_ID, PCI_ANY_ID, 0, 0,
-               pbn_dci_pccom8 },
-#endif
-
-       { PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID,
-        PCI_CLASS_COMMUNICATION_SERIAL << 8, 0xffff00, },
-       { PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID,
-        PCI_CLASS_COMMUNICATION_MODEM << 8, 0xffff00, },
-       { 0, }
-};
-
-MODULE_DEVICE_TABLE(pci, serial_pci_tbl);
-
-static struct pci_driver serial_pci_driver = {
-       name:           "serial",
-       probe:          serial_init_one,
-       remove:        __devexit_p(serial_remove_one),
-       id_table:       serial_pci_tbl,
-};
-
-
-/*
- * Query PCI space for known serial boards
- * If found, add them to the PCI device space in rs_table[]
- *
- * Accept a maximum of eight boards
- *
- */
-static void __devinit probe_serial_pci(void) 
-{
-#ifdef SERIAL_DEBUG_PCI
-       printk(KERN_DEBUG "Entered probe_serial_pci()\n");
-#endif
-
-       /* Register call PCI serial devices.  Null out
-        * the driver name upon failure, as a signal
-        * not to attempt to unregister the driver later
-        */
-       if (pci_module_init (&serial_pci_driver) != 0)
-               serial_pci_driver.name = "";
-
-#ifdef SERIAL_DEBUG_PCI
-       printk(KERN_DEBUG "Leaving probe_serial_pci() (probe finished)\n");
-#endif
-       return;
-}
-
-#endif /* ENABLE_SERIAL_PCI */
-
-#ifdef ENABLE_SERIAL_PNP
-
-struct pnp_board {
-       unsigned short vendor;
-       unsigned short device;
-};
-
-static struct pnp_board pnp_devices[] __devinitdata = {
-       /* Archtek America Corp. */
-       /* Archtek SmartLink Modem 3334BT Plug & Play */
-       {       ISAPNP_VENDOR('A', 'A', 'C'), ISAPNP_DEVICE(0x000F) },
-       /* Anchor Datacomm BV */
-       /* SXPro 144 External Data Fax Modem Plug & Play */
-       {       ISAPNP_VENDOR('A', 'D', 'C'), ISAPNP_DEVICE(0x0001) },
-       /* SXPro 288 External Data Fax Modem Plug & Play */
-       {       ISAPNP_VENDOR('A', 'D', 'C'), ISAPNP_DEVICE(0x0002) },
-       /* Rockwell 56K ACF II Fax+Data+Voice Modem */
-       {       ISAPNP_VENDOR('A', 'K', 'Y'), ISAPNP_DEVICE(0x1021) },
-       /* AZT3005 PnP SOUND DEVICE */
-       {       ISAPNP_VENDOR('A', 'Z', 'T'), ISAPNP_DEVICE(0x4001) },
-       /* Best Data Products Inc. Smart One 336F PnP Modem */
-       {       ISAPNP_VENDOR('B', 'D', 'P'), ISAPNP_DEVICE(0x3336) },
-       /*  Boca Research */
-       /* Boca Complete Ofc Communicator 14.4 Data-FAX */
-       {       ISAPNP_VENDOR('B', 'R', 'I'), ISAPNP_DEVICE(0x0A49) },
-       /* Boca Research 33,600 ACF Modem */
-       {       ISAPNP_VENDOR('B', 'R', 'I'), ISAPNP_DEVICE(0x1400) },
-       /* Boca 33.6 Kbps Internal FD34FSVD */
-       {       ISAPNP_VENDOR('B', 'R', 'I'), ISAPNP_DEVICE(0x3400) },
-       /* Boca 33.6 Kbps Internal FD34FSVD */
-       {       ISAPNP_VENDOR('B', 'R', 'I'), ISAPNP_DEVICE(0x0A49) },
-       /* Best Data Products Inc. Smart One 336F PnP Modem */
-       {       ISAPNP_VENDOR('B', 'D', 'P'), ISAPNP_DEVICE(0x3336) },
-       /* Computer Peripherals Inc */
-       /* EuroViVa CommCenter-33.6 SP PnP */
-       {       ISAPNP_VENDOR('C', 'P', 'I'), ISAPNP_DEVICE(0x4050) },
-       /* Creative Labs */
-       /* Creative Labs Phone Blaster 28.8 DSVD PnP Voice */
-       {       ISAPNP_VENDOR('C', 'T', 'L'), ISAPNP_DEVICE(0x3001) },
-       /* Creative Labs Modem Blaster 28.8 DSVD PnP Voice */
-       {       ISAPNP_VENDOR('C', 'T', 'L'), ISAPNP_DEVICE(0x3011) },
-       /* Creative */
-       /* Creative Modem Blaster Flash56 DI5601-1 */
-       {       ISAPNP_VENDOR('D', 'M', 'B'), ISAPNP_DEVICE(0x1032) },
-       /* Creative Modem Blaster V.90 DI5660 */
-       {       ISAPNP_VENDOR('D', 'M', 'B'), ISAPNP_DEVICE(0x2001) },
-       /* FUJITSU */
-       /* Fujitsu 33600 PnP-I2 R Plug & Play */
-       {       ISAPNP_VENDOR('F', 'U', 'J'), ISAPNP_DEVICE(0x0202) },
-       /* Fujitsu FMV-FX431 Plug & Play */
-       {       ISAPNP_VENDOR('F', 'U', 'J'), ISAPNP_DEVICE(0x0205) },
-       /* Fujitsu 33600 PnP-I4 R Plug & Play */
-       {       ISAPNP_VENDOR('F', 'U', 'J'), ISAPNP_DEVICE(0x0206) },
-       /* Fujitsu Fax Voice 33600 PNP-I5 R Plug & Play */
-       {       ISAPNP_VENDOR('F', 'U', 'J'), ISAPNP_DEVICE(0x0209) },
-       /* Archtek America Corp. */
-       /* Archtek SmartLink Modem 3334BT Plug & Play */
-       {       ISAPNP_VENDOR('G', 'V', 'C'), ISAPNP_DEVICE(0x000F) },
-       /* Hayes */
-       /* Hayes Optima 288 V.34-V.FC + FAX + Voice Plug & Play */
-       {       ISAPNP_VENDOR('H', 'A', 'Y'), ISAPNP_DEVICE(0x0001) },
-       /* Hayes Optima 336 V.34 + FAX + Voice PnP */
-       {       ISAPNP_VENDOR('H', 'A', 'Y'), ISAPNP_DEVICE(0x000C) },
-       /* Hayes Optima 336B V.34 + FAX + Voice PnP */
-       {       ISAPNP_VENDOR('H', 'A', 'Y'), ISAPNP_DEVICE(0x000D) },
-       /* Hayes Accura 56K Ext Fax Modem PnP */
-       {       ISAPNP_VENDOR('H', 'A', 'Y'), ISAPNP_DEVICE(0x5670) },
-       /* Hayes Accura 56K Ext Fax Modem PnP */
-       {       ISAPNP_VENDOR('H', 'A', 'Y'), ISAPNP_DEVICE(0x5674) },
-       /* Hayes Accura 56K Fax Modem PnP */
-       {       ISAPNP_VENDOR('H', 'A', 'Y'), ISAPNP_DEVICE(0x5675) },
-       /* Hayes 288, V.34 + FAX */
-       {       ISAPNP_VENDOR('H', 'A', 'Y'), ISAPNP_DEVICE(0xF000) },
-       /* Hayes Optima 288 V.34 + FAX + Voice, Plug & Play */
-       {       ISAPNP_VENDOR('H', 'A', 'Y'), ISAPNP_DEVICE(0xF001) },
-       /* IBM */
-       /* IBM Thinkpad 701 Internal Modem Voice */
-       {       ISAPNP_VENDOR('I', 'B', 'M'), ISAPNP_DEVICE(0x0033) },
-       /* Intertex */
-       /* Intertex 28k8 33k6 Voice EXT PnP */
-       {       ISAPNP_VENDOR('I', 'X', 'D'), ISAPNP_DEVICE(0xC801) },
-       /* Intertex 33k6 56k Voice EXT PnP */
-       {       ISAPNP_VENDOR('I', 'X', 'D'), ISAPNP_DEVICE(0xC901) },
-       /* Intertex 28k8 33k6 Voice SP EXT PnP */
-       {       ISAPNP_VENDOR('I', 'X', 'D'), ISAPNP_DEVICE(0xD801) },
-       /* Intertex 33k6 56k Voice SP EXT PnP */
-       {       ISAPNP_VENDOR('I', 'X', 'D'), ISAPNP_DEVICE(0xD901) },
-       /* Intertex 28k8 33k6 Voice SP INT PnP */
-       {       ISAPNP_VENDOR('I', 'X', 'D'), ISAPNP_DEVICE(0xF401) },
-       /* Intertex 28k8 33k6 Voice SP EXT PnP */
-       {       ISAPNP_VENDOR('I', 'X', 'D'), ISAPNP_DEVICE(0xF801) },
-       /* Intertex 33k6 56k Voice SP EXT PnP */
-       {       ISAPNP_VENDOR('I', 'X', 'D'), ISAPNP_DEVICE(0xF901) },
-       /* Kortex International */
-       /* KORTEX 28800 Externe PnP */
-       {       ISAPNP_VENDOR('K', 'O', 'R'), ISAPNP_DEVICE(0x4522) },
-       /* KXPro 33.6 Vocal ASVD PnP */
-       {       ISAPNP_VENDOR('K', 'O', 'R'), ISAPNP_DEVICE(0xF661) },
-       /* Lasat */
-       /* LASAT Internet 33600 PnP */
-       {       ISAPNP_VENDOR('L', 'A', 'S'), ISAPNP_DEVICE(0x4040) },
-       /* Lasat Safire 560 PnP */
-       {       ISAPNP_VENDOR('L', 'A', 'S'), ISAPNP_DEVICE(0x4540) },
-       /* Lasat Safire 336  PnP */
-       {       ISAPNP_VENDOR('L', 'A', 'S'), ISAPNP_DEVICE(0x5440) },
-       /* Microcom, Inc. */
-       /* Microcom TravelPorte FAST V.34 Plug & Play */
-       {       ISAPNP_VENDOR('M', 'N', 'P'), ISAPNP_DEVICE(0x281) },
-       /* Microcom DeskPorte V.34 FAST or FAST+ Plug & Play */
-       {       ISAPNP_VENDOR('M', 'N', 'P'), ISAPNP_DEVICE(0x0336) },
-       /* Microcom DeskPorte FAST EP 28.8 Plug & Play */
-       {       ISAPNP_VENDOR('M', 'N', 'P'), ISAPNP_DEVICE(0x0339) },
-       /* Microcom DeskPorte 28.8P Plug & Play */
-       {       ISAPNP_VENDOR('M', 'N', 'P'), ISAPNP_DEVICE(0x0342) },
-       /* Microcom DeskPorte FAST ES 28.8 Plug & Play */
-       {       ISAPNP_VENDOR('M', 'N', 'P'), ISAPNP_DEVICE(0x0500) },
-       /* Microcom DeskPorte FAST ES 28.8 Plug & Play */
-       {       ISAPNP_VENDOR('M', 'N', 'P'), ISAPNP_DEVICE(0x0501) },
-       /* Microcom DeskPorte 28.8S Internal Plug & Play */
-       {       ISAPNP_VENDOR('M', 'N', 'P'), ISAPNP_DEVICE(0x0502) },
-       /* Motorola */
-       /* Motorola BitSURFR Plug & Play */
-       {       ISAPNP_VENDOR('M', 'O', 'T'), ISAPNP_DEVICE(0x1105) },
-       /* Motorola TA210 Plug & Play */
-       {       ISAPNP_VENDOR('M', 'O', 'T'), ISAPNP_DEVICE(0x1111) },
-       /* Motorola HMTA 200 (ISDN) Plug & Play */
-       {       ISAPNP_VENDOR('M', 'O', 'T'), ISAPNP_DEVICE(0x1114) },
-       /* Motorola BitSURFR Plug & Play */
-       {       ISAPNP_VENDOR('M', 'O', 'T'), ISAPNP_DEVICE(0x1115) },
-       /* Motorola Lifestyle 28.8 Internal */
-       {       ISAPNP_VENDOR('M', 'O', 'T'), ISAPNP_DEVICE(0x1190) },
-       /* Motorola V.3400 Plug & Play */
-       {       ISAPNP_VENDOR('M', 'O', 'T'), ISAPNP_DEVICE(0x1501) },
-       /* Motorola Lifestyle 28.8 V.34 Plug & Play */
-       {       ISAPNP_VENDOR('M', 'O', 'T'), ISAPNP_DEVICE(0x1502) },
-       /* Motorola Power 28.8 V.34 Plug & Play */
-       {       ISAPNP_VENDOR('M', 'O', 'T'), ISAPNP_DEVICE(0x1505) },
-       /* Motorola ModemSURFR External 28.8 Plug & Play */
-       {       ISAPNP_VENDOR('M', 'O', 'T'), ISAPNP_DEVICE(0x1509) },
-       /* Motorola Premier 33.6 Desktop Plug & Play */
-       {       ISAPNP_VENDOR('M', 'O', 'T'), ISAPNP_DEVICE(0x150A) },
-       /* Motorola VoiceSURFR 56K External PnP */
-       {       ISAPNP_VENDOR('M', 'O', 'T'), ISAPNP_DEVICE(0x150F) },
-       /* Motorola ModemSURFR 56K External PnP */
-       {       ISAPNP_VENDOR('M', 'O', 'T'), ISAPNP_DEVICE(0x1510) },
-       /* Motorola ModemSURFR 56K Internal PnP */
-       {       ISAPNP_VENDOR('M', 'O', 'T'), ISAPNP_DEVICE(0x1550) },
-       /* Motorola ModemSURFR Internal 28.8 Plug & Play */
-       {       ISAPNP_VENDOR('M', 'O', 'T'), ISAPNP_DEVICE(0x1560) },
-       /* Motorola Premier 33.6 Internal Plug & Play */
-       {       ISAPNP_VENDOR('M', 'O', 'T'), ISAPNP_DEVICE(0x1580) },
-       /* Motorola OnlineSURFR 28.8 Internal Plug & Play */
-       {       ISAPNP_VENDOR('M', 'O', 'T'), ISAPNP_DEVICE(0x15B0) },
-       /* Motorola VoiceSURFR 56K Internal PnP */
-       {       ISAPNP_VENDOR('M', 'O', 'T'), ISAPNP_DEVICE(0x15F0) },
-       /* Com 1 */
-       /*  Deskline K56 Phone System PnP */
-       {       ISAPNP_VENDOR('M', 'V', 'X'), ISAPNP_DEVICE(0x00A1) },
-       /* PC Rider K56 Phone System PnP */
-       {       ISAPNP_VENDOR('M', 'V', 'X'), ISAPNP_DEVICE(0x00F2) },
-       /* Pace 56 Voice Internal Plug & Play Modem */
-       {       ISAPNP_VENDOR('P', 'M', 'C'), ISAPNP_DEVICE(0x2430) },
-       /* Generic */
-       /* Generic standard PC COM port  */
-       {       ISAPNP_VENDOR('P', 'N', 'P'), ISAPNP_DEVICE(0x0500) },
-       /* Generic 16550A-compatible COM port */
-       {       ISAPNP_VENDOR('P', 'N', 'P'), ISAPNP_DEVICE(0x0501) },
-       /* Compaq 14400 Modem */
-       {       ISAPNP_VENDOR('P', 'N', 'P'), ISAPNP_DEVICE(0xC000) },
-       /* Compaq 2400/9600 Modem */
-       {       ISAPNP_VENDOR('P', 'N', 'P'), ISAPNP_DEVICE(0xC001) },
-       /* Dial-Up Networking Serial Cable between 2 PCs */
-       {       ISAPNP_VENDOR('P', 'N', 'P'), ISAPNP_DEVICE(0xC031) },
-       /* Dial-Up Networking Parallel Cable between 2 PCs */
-       {       ISAPNP_VENDOR('P', 'N', 'P'), ISAPNP_DEVICE(0xC032) },
-       /* Standard 9600 bps Modem */
-       {       ISAPNP_VENDOR('P', 'N', 'P'), ISAPNP_DEVICE(0xC100) },
-       /* Standard 14400 bps Modem */
-       {       ISAPNP_VENDOR('P', 'N', 'P'), ISAPNP_DEVICE(0xC101) },
-       /*  Standard 28800 bps Modem*/
-       {       ISAPNP_VENDOR('P', 'N', 'P'), ISAPNP_DEVICE(0xC102) },
-       /*  Standard Modem*/
-       {       ISAPNP_VENDOR('P', 'N', 'P'), ISAPNP_DEVICE(0xC103) },
-       /*  Standard 9600 bps Modem*/
-       {       ISAPNP_VENDOR('P', 'N', 'P'), ISAPNP_DEVICE(0xC104) },
-       /*  Standard 14400 bps Modem*/
-       {       ISAPNP_VENDOR('P', 'N', 'P'), ISAPNP_DEVICE(0xC105) },
-       /*  Standard 28800 bps Modem*/
-       {       ISAPNP_VENDOR('P', 'N', 'P'), ISAPNP_DEVICE(0xC106) },
-       /*  Standard Modem */
-       {       ISAPNP_VENDOR('P', 'N', 'P'), ISAPNP_DEVICE(0xC107) },
-       /* Standard 9600 bps Modem */
-       {       ISAPNP_VENDOR('P', 'N', 'P'), ISAPNP_DEVICE(0xC108) },
-       /* Standard 14400 bps Modem */
-       {       ISAPNP_VENDOR('P', 'N', 'P'), ISAPNP_DEVICE(0xC109) },
-       /* Standard 28800 bps Modem */
-       {       ISAPNP_VENDOR('P', 'N', 'P'), ISAPNP_DEVICE(0xC10A) },
-       /* Standard Modem */
-       {       ISAPNP_VENDOR('P', 'N', 'P'), ISAPNP_DEVICE(0xC10B) },
-       /* Standard 9600 bps Modem */
-       {       ISAPNP_VENDOR('P', 'N', 'P'), ISAPNP_DEVICE(0xC10C) },
-       /* Standard 14400 bps Modem */
-       {       ISAPNP_VENDOR('P', 'N', 'P'), ISAPNP_DEVICE(0xC10D) },
-       /* Standard 28800 bps Modem */
-       {       ISAPNP_VENDOR('P', 'N', 'P'), ISAPNP_DEVICE(0xC10E) },
-       /* Standard Modem */
-       {       ISAPNP_VENDOR('P', 'N', 'P'), ISAPNP_DEVICE(0xC10F) },
-       /* Standard PCMCIA Card Modem */
-       {       ISAPNP_VENDOR('P', 'N', 'P'), ISAPNP_DEVICE(0x2000) },
-       /* Rockwell */
-       /* Modular Technology */
-       /* Rockwell 33.6 DPF Internal PnP */
-       /* Modular Technology 33.6 Internal PnP */
-       {       ISAPNP_VENDOR('R', 'O', 'K'), ISAPNP_DEVICE(0x0030) },
-       /* Kortex International */
-       /* KORTEX 14400 Externe PnP */
-       {       ISAPNP_VENDOR('R', 'O', 'K'), ISAPNP_DEVICE(0x0100) },
-       /* Viking Components, Inc */
-       /* Viking 28.8 INTERNAL Fax+Data+Voice PnP */
-       {       ISAPNP_VENDOR('R', 'O', 'K'), ISAPNP_DEVICE(0x4920) },
-       /* Rockwell */
-       /* British Telecom */
-       /* Modular Technology */
-       /* Rockwell 33.6 DPF External PnP */
-       /* BT Prologue 33.6 External PnP */
-       /* Modular Technology 33.6 External PnP */
-       {       ISAPNP_VENDOR('R', 'S', 'S'), ISAPNP_DEVICE(0x00A0) },
-       /* Viking 56K FAX INT */
-       {       ISAPNP_VENDOR('R', 'S', 'S'), ISAPNP_DEVICE(0x0262) },
-       /* SupraExpress 28.8 Data/Fax PnP modem */
-       {       ISAPNP_VENDOR('S', 'U', 'P'), ISAPNP_DEVICE(0x1310) },
-       /* SupraExpress 33.6 Data/Fax PnP modem */
-       {       ISAPNP_VENDOR('S', 'U', 'P'), ISAPNP_DEVICE(0x1421) },
-       /* SupraExpress 33.6 Data/Fax PnP modem */
-       {       ISAPNP_VENDOR('S', 'U', 'P'), ISAPNP_DEVICE(0x1590) },
-       /* SupraExpress 33.6 Data/Fax PnP modem */
-       {       ISAPNP_VENDOR('S', 'U', 'P'), ISAPNP_DEVICE(0x1760) },
-       /* Phoebe Micro */
-       /* Phoebe Micro 33.6 Data Fax 1433VQH Plug & Play */
-       {       ISAPNP_VENDOR('T', 'E', 'X'), ISAPNP_DEVICE(0x0011) },
-       /* Archtek America Corp. */
-       /* Archtek SmartLink Modem 3334BT Plug & Play */
-       {       ISAPNP_VENDOR('U', 'A', 'C'), ISAPNP_DEVICE(0x000F) },
-       /* 3Com Corp. */
-       /* Gateway Telepath IIvi 33.6 */
-       {       ISAPNP_VENDOR('U', 'S', 'R'), ISAPNP_DEVICE(0x0000) },
-       /*  Sportster Vi 14.4 PnP FAX Voicemail */
-       {       ISAPNP_VENDOR('U', 'S', 'R'), ISAPNP_DEVICE(0x0004) },
-       /* U.S. Robotics 33.6K Voice INT PnP */
-       {       ISAPNP_VENDOR('U', 'S', 'R'), ISAPNP_DEVICE(0x0006) },
-       /* U.S. Robotics 33.6K Voice EXT PnP */
-       {       ISAPNP_VENDOR('U', 'S', 'R'), ISAPNP_DEVICE(0x0007) },
-       /* U.S. Robotics 33.6K Voice INT PnP */
-       {       ISAPNP_VENDOR('U', 'S', 'R'), ISAPNP_DEVICE(0x2002) },
-       /* U.S. Robotics 56K Voice INT PnP */
-       {       ISAPNP_VENDOR('U', 'S', 'R'), ISAPNP_DEVICE(0x2070) },
-       /* U.S. Robotics 56K Voice EXT PnP */
-       {       ISAPNP_VENDOR('U', 'S', 'R'), ISAPNP_DEVICE(0x2080) },
-       /* U.S. Robotics 56K FAX INT */
-       {       ISAPNP_VENDOR('U', 'S', 'R'), ISAPNP_DEVICE(0x3031) },
-       /* U.S. Robotics 56K Voice INT PnP */
-       {       ISAPNP_VENDOR('U', 'S', 'R'), ISAPNP_DEVICE(0x3070) },
-       /* U.S. Robotics 56K Voice EXT PnP */
-       {       ISAPNP_VENDOR('U', 'S', 'R'), ISAPNP_DEVICE(0x3080) },
-       /* U.S. Robotics 56K Voice INT PnP */
-       {       ISAPNP_VENDOR('U', 'S', 'R'), ISAPNP_DEVICE(0x3090) },
-       /* U.S. Robotics 56K Message  */
-       {       ISAPNP_VENDOR('U', 'S', 'R'), ISAPNP_DEVICE(0x9100) },
-       /*  U.S. Robotics 56K FAX EXT PnP*/
-       {       ISAPNP_VENDOR('U', 'S', 'R'), ISAPNP_DEVICE(0x9160) },
-       /*  U.S. Robotics 56K FAX INT PnP*/
-       {       ISAPNP_VENDOR('U', 'S', 'R'), ISAPNP_DEVICE(0x9170) },
-       /*  U.S. Robotics 56K Voice EXT PnP*/
-       {       ISAPNP_VENDOR('U', 'S', 'R'), ISAPNP_DEVICE(0x9180) },
-       /*  U.S. Robotics 56K Voice INT PnP*/
-       {       ISAPNP_VENDOR('U', 'S', 'R'), ISAPNP_DEVICE(0x9190) },
-       {       0, }
-};
-
-static void inline avoid_irq_share(struct pci_dev *dev)
-{
-       int i;
-       unsigned long map = 0x1FF8;
-       struct serial_state *state = rs_table;
-       struct isapnp_irq *irq;
-       struct isapnp_resources *res = dev->sysdata;
-
-       for (i = 0; i < NR_PORTS; i++) {
-               if (state->type != PORT_UNKNOWN)
-                       clear_bit(state->irq, &map);
-               state++;
-       }
-
-       for ( ; res; res = res->alt)
-               for(irq = res->irq; irq; irq = irq->next)
-                       irq->map = map;
-}
-
-static char *modem_names[] __devinitdata = {
-       "MODEM", "Modem", "modem", "FAX", "Fax", "fax",
-       "56K", "56k", "K56", "33.6", "28.8", "14.4",
-       "33,600", "28,800", "14,400", "33.600", "28.800", "14.400",
-       "33600", "28800", "14400", "V.90", "V.34", "V.32", 0
-};
-
-static int __devinit check_name(char *name)
-{
-       char **tmp = modem_names;
-
-       while (*tmp) {
-               if (strstr(name, *tmp))
-                       return 1;
-               tmp++;
-       }
-       return 0;
-}
-
-static int inline check_compatible_id(struct pci_dev *dev)
-{
-       int i;
-       for (i = 0; i < DEVICE_COUNT_COMPATIBLE; i++)
-              if ((dev->vendor_compatible[i] ==
-                   ISAPNP_VENDOR('P', 'N', 'P')) &&
-                  (swab16(dev->device_compatible[i]) >= 0xc000) &&
-                  (swab16(dev->device_compatible[i]) <= 0xdfff))
-                      return 0;
-       return 1;
-}
-
-/*
- * Given a complete unknown ISA PnP device, try to use some heuristics to
- * detect modems. Currently use such heuristic set:
- *     - dev->name or dev->bus->name must contain "modem" substring;
- *     - device must have only one IO region (8 byte long) with base adress
- *       0x2e8, 0x3e8, 0x2f8 or 0x3f8.
- *
- * Such detection looks very ugly, but can detect at least some of numerous
- * ISA PnP modems, alternatively we must hardcode all modems in pnp_devices[]
- * table.
- */
-static int _INLINE_ serial_pnp_guess_board(struct pci_dev *dev,
-                                         struct pci_board *board)
-{
-       struct isapnp_resources *res = (struct isapnp_resources *)dev->sysdata;
-       struct isapnp_resources *resa;
-
-       if (!(check_name(dev->name) || check_name(dev->bus->name)) &&
-          !(check_compatible_id(dev)))
-              return 1;
-
-       if (!res || res->next)
-              return 1;
-
-       for (resa = res->alt; resa; resa = resa->alt) {
-              struct isapnp_port *port;
-              for (port = res->port; port; port = port->next)
-                      if ((port->size == 8) &&
-                          ((port->min == 0x2f8) ||
-                           (port->min == 0x3f8) ||
-                           (port->min == 0x2e8) ||
-                           (port->min == 0x3e8)))
-                              return 0;
-       }
-
-       return 1;
-}
-
-static void __devinit probe_serial_pnp(void)
-{
-       struct pci_dev *dev = NULL;
-       struct pnp_board *pnp_board;
-       struct pci_board board;
-
-#ifdef SERIAL_DEBUG_PNP
-       printk("Entered probe_serial_pnp()\n");
-#endif
-       if (!isapnp_present()) {
-#ifdef SERIAL_DEBUG_PNP
-               printk("Leaving probe_serial_pnp() (no isapnp)\n");
-#endif
-               return;
-       }
-
-       isapnp_for_each_dev(dev) {
-              if (dev->active)
-                      continue;
-
-              memset(&board, 0, sizeof(board));
-              board.flags = SPCI_FL_BASE0 | SPCI_FL_PNPDEFAULT;
-              board.num_ports = 1;
-              board.base_baud = 115200;
-              
-              for (pnp_board = pnp_devices; pnp_board->vendor; pnp_board++)
-                      if ((dev->vendor == pnp_board->vendor) &&
-                          (dev->device == pnp_board->device))
-                              break;
-
-              if (pnp_board->vendor) {
-                      /* Special case that's more efficient to hardcode */
-                      if ((pnp_board->vendor == ISAPNP_VENDOR('A', 'K', 'Y') &&
-                           pnp_board->device == ISAPNP_DEVICE(0x1021)))
-                              board.flags |= SPCI_FL_NO_SHIRQ;
-              } else {
-                      if (serial_pnp_guess_board(dev, &board))
-                              continue;
-              }
-              
-              if (board.flags & SPCI_FL_NO_SHIRQ)
-                      avoid_irq_share(dev);
-              start_pci_pnp_board(dev, &board);
-       }
-
-#ifdef SERIAL_DEBUG_PNP
-       printk("Leaving probe_serial_pnp() (probe finished)\n");
-#endif
-       return;
-}
-
-#endif /* ENABLE_SERIAL_PNP */
-
-/*
- * The serial driver boot-time initialization code!
- */
-static int __init rs_init(void)
-{
-       int i;
-       struct serial_state * state;
-
-       init_bh(SERIAL_BH, do_serial_bh);
-       init_timer(&serial_timer);
-       serial_timer.function = rs_timer;
-       mod_timer(&serial_timer, jiffies + RS_STROBE_TIME);
-
-       for (i = 0; i < NR_IRQS; i++) {
-               IRQ_ports[i] = 0;
-               IRQ_timeout[i] = 0;
-#ifdef CONFIG_SERIAL_MULTIPORT
-               memset(&rs_multiport[i], 0,
-                      sizeof(struct rs_multiport_struct));
-#endif
-       }
-#ifdef CONFIG_SERIAL_CONSOLE
-       /*
-        *      The interrupt of the serial console port
-        *      can't be shared.
-        */
-       if (sercons.flags & CON_CONSDEV) {
-               for(i = 0; i < NR_PORTS; i++)
-                       if (i != sercons.index &&
-                           rs_table[i].irq == rs_table[sercons.index].irq)
-                               rs_table[i].irq = 0;
-       }
-#endif
-       show_serial_version();
-
-       /* Initialize the tty_driver structure */
-       
-       memset(&serial_driver, 0, sizeof(struct tty_driver));
-       serial_driver.magic = TTY_DRIVER_MAGIC;
-#if (LINUX_VERSION_CODE > 0x20100)
-       serial_driver.driver_name = "serial";
-#endif
-#if (LINUX_VERSION_CODE > 0x2032D && defined(CONFIG_DEVFS_FS))
-       serial_driver.name = "tts/%d";
-#else
-       serial_driver.name = "ttyS";
-#endif
-       serial_driver.major = TTY_MAJOR;
-       serial_driver.minor_start = 64 + SERIAL_DEV_OFFSET;
-       serial_driver.num = NR_PORTS;
-       serial_driver.type = TTY_DRIVER_TYPE_SERIAL;
-       serial_driver.subtype = SERIAL_TYPE_NORMAL;
-       serial_driver.init_termios = tty_std_termios;
-       serial_driver.init_termios.c_cflag =
-               B9600 | CS8 | CREAD | HUPCL | CLOCAL;
-       serial_driver.flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_NO_DEVFS;
-       serial_driver.refcount = &serial_refcount;
-       serial_driver.table = serial_table;
-       serial_driver.termios = serial_termios;
-       serial_driver.termios_locked = serial_termios_locked;
-
-       serial_driver.open = rs_open;
-       serial_driver.close = rs_close;
-       serial_driver.write = rs_write;
-       serial_driver.put_char = rs_put_char;
-       serial_driver.flush_chars = rs_flush_chars;
-       serial_driver.write_room = rs_write_room;
-       serial_driver.chars_in_buffer = rs_chars_in_buffer;
-       serial_driver.flush_buffer = rs_flush_buffer;
-       serial_driver.ioctl = rs_ioctl;
-       serial_driver.throttle = rs_throttle;
-       serial_driver.unthrottle = rs_unthrottle;
-       serial_driver.set_termios = rs_set_termios;
-       serial_driver.stop = rs_stop;
-       serial_driver.start = rs_start;
-       serial_driver.hangup = rs_hangup;
-#if (LINUX_VERSION_CODE >= 131394) /* Linux 2.1.66 */
-       serial_driver.break_ctl = rs_break;
-#endif
-#if (LINUX_VERSION_CODE >= 131343)
-       serial_driver.send_xchar = rs_send_xchar;
-       serial_driver.wait_until_sent = rs_wait_until_sent;
-       serial_driver.read_proc = rs_read_proc;
-#endif
-       
-       /*
-        * The callout device is just like normal device except for
-        * major number and the subtype code.
-        */
-       callout_driver = serial_driver;
-#if (LINUX_VERSION_CODE > 0x2032D && defined(CONFIG_DEVFS_FS))
-       callout_driver.name = "cua/%d";
-#else
-       callout_driver.name = "cua";
-#endif
-       callout_driver.major = TTYAUX_MAJOR;
-       callout_driver.subtype = SERIAL_TYPE_CALLOUT;
-#if (LINUX_VERSION_CODE >= 131343)
-       callout_driver.read_proc = 0;
-       callout_driver.proc_entry = 0;
-#endif
-
-       if (tty_register_driver(&serial_driver))
-               panic("Couldn't register serial driver\n");
-       if (tty_register_driver(&callout_driver))
-               panic("Couldn't register callout driver\n");
-       
-       for (i = 0, state = rs_table; i < NR_PORTS; i++,state++) {
-               state->magic = SSTATE_MAGIC;
-               state->line = i;
-               state->type = PORT_UNKNOWN;
-               state->custom_divisor = 0;
-               state->close_delay = 5*HZ/10;
-               state->closing_wait = 30*HZ;
-               state->callout_termios = callout_driver.init_termios;
-               state->normal_termios = serial_driver.init_termios;
-               state->icount.cts = state->icount.dsr = 
-                       state->icount.rng = state->icount.dcd = 0;
-               state->icount.rx = state->icount.tx = 0;
-               state->icount.frame = state->icount.parity = 0;
-               state->icount.overrun = state->icount.brk = 0;
-               state->irq = irq_cannonicalize(state->irq);
-               if (state->hub6)
-                       state->io_type = SERIAL_IO_HUB6;
-               if (state->port && check_region(state->port,8))
-                       continue;
-#ifdef CONFIG_MCA                      
-               if ((state->flags & ASYNC_BOOT_ONLYMCA) && !MCA_bus)
-                       continue;
-#endif                 
-               if (state->flags & ASYNC_BOOT_AUTOCONF)
-                       autoconfig(state);
-       }
-       for (i = 0, state = rs_table; i < NR_PORTS; i++,state++) {
-               if (state->type == PORT_UNKNOWN)
-                       continue;
-               if (   (state->flags & ASYNC_BOOT_AUTOCONF)
-                   && (state->flags & ASYNC_AUTO_IRQ)
-                   && (state->port != 0 || state->iomem_base != 0))
-                       state->irq = detect_uart_irq(state);
-               if (state->io_type == SERIAL_IO_MEM) {
-                       printk(KERN_INFO"ttyS%02d%s at 0x%px (irq = %d) is a %s\n",
-                              state->line + SERIAL_DEV_OFFSET,
-                              (state->flags & ASYNC_FOURPORT) ? " FourPort" : "",
-                              state->iomem_base, state->irq,
-                              uart_config[state->type].name);
-               }
-               else {
-                       printk(KERN_INFO "ttyS%02d%s at 0x%04lx (irq = %d) is a %s\n",
-                              state->line + SERIAL_DEV_OFFSET,
-                              (state->flags & ASYNC_FOURPORT) ? " FourPort" : "",
-                              state->port, state->irq,
-                              uart_config[state->type].name);
-               }
-               tty_register_devfs(&serial_driver, 0,
-                                  serial_driver.minor_start + state->line);
-               tty_register_devfs(&callout_driver, 0,
-                                  callout_driver.minor_start + state->line);
-       }
-#ifdef ENABLE_SERIAL_PCI
-       probe_serial_pci();
-#endif
-#ifdef ENABLE_SERIAL_PNP
-       probe_serial_pnp();
-#endif
-       return 0;
-}
-
-/*
- * This is for use by architectures that know their serial console 
- * attributes only at run time. Not to be invoked after rs_init().
- */
-int __init early_serial_setup(struct serial_struct *req)
-{
-       int i = req->line;
-
-       if (i >= NR_IRQS)
-               return(-ENOENT);
-       rs_table[i].magic = 0;
-       rs_table[i].baud_base = req->baud_base;
-       rs_table[i].port = req->port;
-       if (HIGH_BITS_OFFSET)
-               rs_table[i].port += (unsigned long) req->port_high << 
-                                                       HIGH_BITS_OFFSET;
-       rs_table[i].irq = req->irq;
-       rs_table[i].flags = req->flags;
-       rs_table[i].close_delay = req->close_delay;
-       rs_table[i].io_type = req->io_type;
-       rs_table[i].hub6 = req->hub6;
-       rs_table[i].iomem_base = req->iomem_base;
-       rs_table[i].iomem_reg_shift = req->iomem_reg_shift;
-       rs_table[i].type = req->type;
-       rs_table[i].xmit_fifo_size = req->xmit_fifo_size;
-       rs_table[i].custom_divisor = req->custom_divisor;
-       rs_table[i].closing_wait = req->closing_wait;
-       return(0);
-}
-
-/*
- * register_serial and unregister_serial allows for 16x50 serial ports to be
- * configured at run-time, to support PCMCIA modems.
- */
-/**
- *     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.
- *
- *     The port is then probed and if neccessary 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.
- */
-int register_serial(struct serial_struct *req)
-{
-       int i;
-       unsigned long flags;
-       struct serial_state *state;
-       struct async_struct *info;
-       unsigned long port;
-
-       port = req->port;
-       if (HIGH_BITS_OFFSET)
-               port += (unsigned long) req->port_high << HIGH_BITS_OFFSET;
-
-       save_flags(flags); cli();
-       for (i = 0; i < NR_PORTS; i++) {
-               if ((rs_table[i].port == port) &&
-                   (rs_table[i].iomem_base == req->iomem_base))
-                       break;
-       }
-#ifdef __i386__
-       if (i == NR_PORTS) {
-               for (i = 4; i < NR_PORTS; i++)
-                       if ((rs_table[i].type == PORT_UNKNOWN) &&
-                           (rs_table[i].count == 0))
-                               break;
-       }
-#endif
-       if (i == NR_PORTS) {
-               for (i = 0; i < NR_PORTS; i++)
-                       if ((rs_table[i].type == PORT_UNKNOWN) &&
-                           (rs_table[i].count == 0))
-                               break;
-       }
-       if (i == NR_PORTS) {
-               restore_flags(flags);
-               return -1;
-       }
-       state = &rs_table[i];
-       if (rs_table[i].count) {
-               restore_flags(flags);
-               printk("Couldn't configure serial #%d (port=%ld,irq=%d): "
-                      "device already open\n", i, port, req->irq);
-               return -1;
-       }
-       state->irq = req->irq;
-       state->port = port;
-       state->flags = req->flags;
-       state->io_type = req->io_type;
-       state->iomem_base = req->iomem_base;
-       state->iomem_reg_shift = req->iomem_reg_shift;
-       if (req->baud_base)
-               state->baud_base = req->baud_base;
-       if ((info = state->info) != NULL) {
-               info->port = port;
-               info->flags = req->flags;
-               info->io_type = req->io_type;
-               info->iomem_base = req->iomem_base;
-               info->iomem_reg_shift = req->iomem_reg_shift;
-       }
-       autoconfig(state);
-       if (state->type == PORT_UNKNOWN) {
-               restore_flags(flags);
-               printk("register_serial(): autoconfig failed\n");
-               return -1;
-       }
-       restore_flags(flags);
-
-       if ((state->flags & ASYNC_AUTO_IRQ) && CONFIGURED_SERIAL_PORT(state))
-               state->irq = detect_uart_irq(state);
-
-       printk(KERN_INFO "ttyS%02d at %s 0x%04lx (irq = %d) is a %s\n",
-             state->line + SERIAL_DEV_OFFSET,
-             state->iomem_base ? "iomem" : "port",
-             state->iomem_base ? (unsigned long)state->iomem_base :
-             state->port, state->irq, uart_config[state->type].name);
-       tty_register_devfs(&serial_driver, 0,
-                          serial_driver.minor_start + state->line); 
-       tty_register_devfs(&callout_driver, 0,
-                          callout_driver.minor_start + state->line);
-       return state->line + SERIAL_DEV_OFFSET;
-}
-
-/**
- *     unregister_serial - deconfigure a 16x50 serial port
- *     @line: line to deconfigure
- *
- *     The port specified is deconfigured and its resources are freed. Any
- *     user of the port is disconnected as if carrier was dropped. Line is
- *     the port number returned by register_serial().
- */
-
-void unregister_serial(int line)
-{
-       unsigned long flags;
-       struct serial_state *state = &rs_table[line];
-
-       save_flags(flags); cli();
-       if (state->info && state->info->tty)
-               tty_hangup(state->info->tty);
-       state->type = PORT_UNKNOWN;
-       printk(KERN_INFO "ttyS%02d unloaded\n", state->line);
-       /* These will be hidden, because they are devices that will no longer
-        * be available to the system. (ie, PCMCIA modems, once ejected)
-        */
-       tty_unregister_devfs(&serial_driver,
-                            serial_driver.minor_start + state->line);
-       tty_unregister_devfs(&callout_driver,
-                            callout_driver.minor_start + state->line);
-       restore_flags(flags);
-}
-
-static void __exit rs_fini(void) 
-{
-       unsigned long flags;
-       int e1, e2;
-       int i;
-       struct async_struct *info;
-
-       /* printk("Unloading %s: version %s\n", serial_name, serial_version); */
-       del_timer_sync(&serial_timer);
-       save_flags(flags); cli();
-        remove_bh(SERIAL_BH);
-       if ((e1 = tty_unregister_driver(&serial_driver)))
-               printk("serial: failed to unregister serial driver (%d)\n",
-                      e1);
-       if ((e2 = tty_unregister_driver(&callout_driver)))
-               printk("serial: failed to unregister callout driver (%d)\n", 
-                      e2);
-       restore_flags(flags);
-
-       for (i = 0; i < NR_PORTS; i++) {
-               if ((info = rs_table[i].info)) {
-                       rs_table[i].info = NULL;
-                       kfree(info);
-               }
-               if ((rs_table[i].type != PORT_UNKNOWN) && rs_table[i].port) {
-#ifdef CONFIG_SERIAL_RSA
-                       if (rs_table[i].type == PORT_RSA)
-                               release_region(rs_table[i].port +
-                                              UART_RSA_BASE, 16);
-                       else
-#endif
-                               release_region(rs_table[i].port, 8);
-               }
-#if defined(ENABLE_SERIAL_PCI) || defined(ENABLE_SERIAL_PNP)
-               if (rs_table[i].iomem_base)
-                       iounmap(rs_table[i].iomem_base);
-#endif
-       }
-#if defined(ENABLE_SERIAL_PCI) || defined(ENABLE_SERIAL_PNP)
-       for (i=0; i < NR_PCI_BOARDS; i++) {
-               struct pci_board_inst *brd = &serial_pci_board[i];
-
-               if (serial_pci_board[i].dev == 0)
-                       continue;
-               if (brd->board.init_fn)
-                       (brd->board.init_fn)(brd->dev, &brd->board, 0);
-               if (DEACTIVATE_FUNC(brd->dev))
-                       (DEACTIVATE_FUNC(brd->dev))(brd->dev);
-       }
-#endif 
-       if (tmp_buf) {
-               unsigned long pg = (unsigned long) tmp_buf;
-               tmp_buf = NULL;
-               free_page(pg);
-       }
-       
-#ifdef ENABLE_SERIAL_PCI
-       if (serial_pci_driver.name[0])
-               pci_unregister_driver (&serial_pci_driver);
-#endif
-}
-
-module_init(rs_init);
-module_exit(rs_fini);
-MODULE_DESCRIPTION("Standard/generic (dumb) serial driver");
-MODULE_AUTHOR("Theodore Ts'o <tytso@mit.edu>");
-MODULE_LICENSE("GPL");
-
-
-/*
- * ------------------------------------------------------------
- * Serial console driver
- * ------------------------------------------------------------
- */
-#ifdef CONFIG_SERIAL_CONSOLE
-
-#define BOTH_EMPTY (UART_LSR_TEMT | UART_LSR_THRE)
-
-static struct async_struct async_sercons;
-
-/*
- *     Wait for transmitter & holding register to empty
- */
-static inline void wait_for_xmitr(struct async_struct *info)
-{
-       unsigned int status, tmout = 1000000;
-
-       do {
-               status = serial_in(info, UART_LSR);
-
-               if (status & UART_LSR_BI)
-                       lsr_break_flag = UART_LSR_BI;
-               
-               if (--tmout == 0)
-                       break;
-       } while((status & BOTH_EMPTY) != BOTH_EMPTY);
-
-       /* Wait for flow control if necessary */
-       if (info->flags & ASYNC_CONS_FLOW) {
-               tmout = 1000000;
-               while (--tmout &&
-                      ((serial_in(info, UART_MSR) & UART_MSR_CTS) == 0));
-       }       
-}
-
-
-/*
- *     Print a string to the serial port trying not to disturb
- *     any possible real use of the port...
- *
- *     The console must be locked when we get here.
- */
-static void serial_console_write(struct console *co, const char *s,
-                               unsigned count)
-{
-       static struct async_struct *info = &async_sercons;
-       int ier;
-       unsigned i;
-
-       /*
-        *      First save the IER then disable the interrupts
-        */
-       ier = serial_in(info, UART_IER);
-       serial_out(info, UART_IER, 0x00);
-
-       /*
-        *      Now, do each character
-        */
-       for (i = 0; i < count; i++, s++) {
-               wait_for_xmitr(info);
-
-               /*
-                *      Send the character out.
-                *      If a LF, also do CR...
-                */
-               serial_out(info, UART_TX, *s);
-               if (*s == 10) {
-                       wait_for_xmitr(info);
-                       serial_out(info, UART_TX, 13);
-               }
-       }
-
-       /*
-        *      Finally, Wait for transmitter & holding register to empty
-        *      and restore the IER
-        */
-       wait_for_xmitr(info);
-       serial_out(info, UART_IER, ier);
-}
-
-static kdev_t serial_console_device(struct console *c)
-{
-       return mk_kdev(TTY_MAJOR, 64 + c->index);
-}
-
-/*
- *     Setup initial baud/bits/parity/flow control. We do two things here:
- *     - construct a cflag setting for the first rs_open()
- *     - initialize the serial port
- *     Return non-zero if we didn't find a serial port.
- */
-static int __init serial_console_setup(struct console *co, char *options)
-{
-       static struct async_struct *info;
-       struct serial_state *state;
-       unsigned cval;
-       int     baud = 9600;
-       int     bits = 8;
-       int     parity = 'n';
-       int     doflow = 0;
-       int     cflag = CREAD | HUPCL | CLOCAL;
-       int     quot = 0;
-       char    *s;
-
-       if (options) {
-               baud = simple_strtoul(options, NULL, 10);
-               s = options;
-               while(*s >= '0' && *s <= '9')
-                       s++;
-               if (*s) parity = *s++;
-               if (*s) bits   = *s++ - '0';
-               if (*s) doflow = (*s++ == 'r');
-       }
-
-       /*
-        *      Now construct a cflag setting.
-        */
-       switch(baud) {
-               case 1200:
-                       cflag |= B1200;
-                       break;
-               case 2400:
-                       cflag |= B2400;
-                       break;
-               case 4800:
-                       cflag |= B4800;
-                       break;
-               case 19200:
-                       cflag |= B19200;
-                       break;
-               case 38400:
-                       cflag |= B38400;
-                       break;
-               case 57600:
-                       cflag |= B57600;
-                       break;
-               case 115200:
-                       cflag |= B115200;
-                       break;
-               case 9600:
-               default:
-                       cflag |= B9600;
-                       /*
-                        * Set this to a sane value to prevent a divide error
-                        */
-                       baud  = 9600;
-                       break;
-       }
-       switch(bits) {
-               case 7:
-                       cflag |= CS7;
-                       break;
-               default:
-               case 8:
-                       cflag |= CS8;
-                       break;
-       }
-       switch(parity) {
-               case 'o': case 'O':
-                       cflag |= PARODD;
-                       break;
-               case 'e': case 'E':
-                       cflag |= PARENB;
-                       break;
-       }
-       co->cflag = cflag;
-
-       /*
-        *      Divisor, bytesize and parity
-        */
-       state = rs_table + co->index;
-       if (doflow)
-               state->flags |= ASYNC_CONS_FLOW;
-       info = &async_sercons;
-       info->magic = SERIAL_MAGIC;
-       info->state = state;
-       info->port = state->port;
-       info->flags = state->flags;
-#ifdef CONFIG_HUB6
-       info->hub6 = state->hub6;
-#endif
-       info->io_type = state->io_type;
-       info->iomem_base = state->iomem_base;
-       info->iomem_reg_shift = state->iomem_reg_shift;
-       quot = state->baud_base / baud;
-       cval = cflag & (CSIZE | CSTOPB);
-#if defined(__powerpc__) || defined(__alpha__)
-       cval >>= 8;
-#else /* !__powerpc__ && !__alpha__ */
-       cval >>= 4;
-#endif /* !__powerpc__ && !__alpha__ */
-       if (cflag & PARENB)
-               cval |= UART_LCR_PARITY;
-       if (!(cflag & PARODD))
-               cval |= UART_LCR_EPAR;
-
-       /*
-        *      Disable UART interrupts, set DTR and RTS high
-        *      and set speed.
-        */
-       serial_out(info, UART_LCR, cval | UART_LCR_DLAB);       /* set DLAB */
-       serial_out(info, UART_DLL, quot & 0xff);        /* LS of divisor */
-       serial_out(info, UART_DLM, quot >> 8);          /* MS of divisor */
-       serial_out(info, UART_LCR, cval);               /* reset DLAB */
-       serial_out(info, UART_IER, 0);
-       serial_out(info, UART_MCR, UART_MCR_DTR | UART_MCR_RTS);
-
-       /*
-        *      If we read 0xff from the LSR, there is no UART here.
-        */
-       if (serial_in(info, UART_LSR) == 0xff)
-               return -1;
-
-       return 0;
-}
-
-static struct console sercons = {
-       name:           "ttyS",
-       write:          serial_console_write,
-       device:         serial_console_device,
-       setup:          serial_console_setup,
-       flags:          CON_PRINTBUFFER,
-       index:          -1,
-};
-
-/*
- *     Register console.
- */
-void __init serial_console_init(void)
-{
-       register_console(&sercons);
-}
-#endif
-
-/*
-  Local variables:
-  compile-command: "gcc -D__KERNEL__ -I../../include -Wall -Wstrict-prototypes -O2 -fomit-frame-pointer -fno-strict-aliasing -pipe -fno-strength-reduce -march=i586 -DMODULE -DMODVERSIONS -include ../../include/linux/modversions.h   -DEXPORT_SYMTAB -c serial.c"
-  End:
-*/
index 59567e8b8fa692f87f491f7154e2fea39e492d6a..ce8b07ccc5f4183b5c2c06305f6ce897c4ee9449 100644 (file)
@@ -150,8 +150,7 @@ extern void con3215_init(void);
 extern void tty3215_init(void);
 extern void tub3270_con_init(void);
 extern void tub3270_init(void);
-extern void rs285_console_init(void);
-extern void sa1100_rs_console_init(void);
+extern void uart_console_init(void);
 extern void sgi_serial_console_init(void);
 extern void sci_console_init(void);
 extern void tx3912_console_init(void);
@@ -2221,18 +2220,12 @@ void __init console_init(void)
 #ifdef CONFIG_STDIO_CONSOLE
        stdio_console_init();
 #endif
-#ifdef CONFIG_SERIAL_21285_CONSOLE
-       rs285_console_init();
-#endif
-#ifdef CONFIG_SERIAL_SA1100_CONSOLE
-       sa1100_rs_console_init();
+#ifdef CONFIG_SERIAL_CORE_CONSOLE
+       uart_console_init();
 #endif
 #ifdef CONFIG_ARC_CONSOLE
        arc_console_init();
 #endif
-#ifdef CONFIG_SERIAL_AMBA_CONSOLE
-       ambauart_console_init();
-#endif
 #ifdef CONFIG_SERIAL_TX3912_CONSOLE
        tx3912_console_init();
 #endif
diff --git a/drivers/serial/Config.help b/drivers/serial/Config.help
new file mode 100644 (file)
index 0000000..c9f938f
--- /dev/null
@@ -0,0 +1,225 @@
+# $Id: Config.help,v 1.5 2002/07/06 17:16:24 rmk Exp $
+
+CONFIG_SERIAL_8250
+  This selects whether you want to include the driver for the standard
+  serial ports.  The standard answer is Y.  People who might say N
+  here are those that are setting up dedicated Ethernet WWW/FTP
+  servers, or users that have one of the various bus mice instead of a
+  serial mouse and don't intend to use their machine's standard serial
+  port for anything.  (Note that the Cyclades and Stallion multi
+  serial port drivers do not need this driver built in for them to
+  work.)
+
+  If you want to compile this driver as a module, say M here and read
+  <file:Documentation/modules.txt>.  The module will be called
+  serial.o.
+  [WARNING: Do not compile this driver as a module if you are using
+  non-standard serial ports, since the configuration information will
+  be lost when the driver is unloaded.  This limitation may be lifted
+  in the future.]
+
+  BTW1: If you have a mouseman serial mouse which is not recognized by
+  the X window system, try running gpm first.
+
+  BTW2: If you intend to use a software modem (also called Winmodem)
+  under Linux, forget it.  These modems are crippled and require
+  proprietary drivers which are only available under Windows.
+
+  Most people will say Y or M here, so that they can use serial mice,
+  modems and similar devices connecting to the standard serial ports.
+
+CONFIG_SERIAL_8250_CONSOLE
+  If you say Y here, it will be possible to use a serial port as the
+  system console (the system console is the device which receives all
+  kernel messages and warnings and which allows logins in single user
+  mode). This could be useful if some terminal or printer is connected
+  to that serial port.
+
+  Even if you say Y here, the currently visible virtual console
+  (/dev/tty0) will still be used as the system console by default, but
+  you can alter that using a kernel command line option such as
+  "console=ttyS1". (Try "man bootparam" or see the documentation of
+  your boot loader (lilo or loadlin) about how to pass options to the
+  kernel at boot time.)
+
+  If you don't have a VGA card installed and you say Y here, the
+  kernel will automatically use the first serial line, /dev/ttyS0, as
+  system console.
+
+  If unsure, say N.
+
+CONFIG_SERIAL_8250_CS
+  Say Y here to enable support for 16-bit PCMCIA serial devices,
+  including serial port cards, modems, and the modem functions of
+  multi-function Ethernet/modem cards. (PCMCIA- or PC-cards are
+  credit-card size devices often used with laptops.)
+
+  This driver is also available as a module ( = code which can be
+  inserted in and removed from the running kernel whenever you want).
+  The module will be called serial_cs.o.  If you want to compile it as
+  a module, say M here and read <file:Documentation/modules.txt>.
+  If unsure, say N.
+
+CONFIG_SERIAL_8250_EXTENDED
+  If you wish to use any non-standard features of the standard "dumb"
+  driver, say Y here. This includes HUB6 support, shared serial
+  interrupts, special multiport support, support for more than the
+  four COM 1/2/3/4 boards, etc.
+
+  Note that the answer to this question won't directly affect the
+  kernel: saying N will just cause the configurator to skip all
+  the questions about serial driver options. If unsure, say N.
+
+CONFIG_SERIAL_8250_MANY_PORTS
+  Say Y here if you have dumb serial boards other than the four
+  standard COM 1/2/3/4 ports. This may happen if you have an AST
+  FourPort, Accent Async, Boca (read the Boca mini-HOWTO, available
+  from <http://www.linuxdoc.org/docs.html#howto>), or other custom
+  serial port hardware which acts similar to standard serial port
+  hardware. If you only use the standard COM 1/2/3/4 ports, you can
+  say N here to save some memory. You can also say Y if you have an
+  "intelligent" multiport card such as Cyclades, Digiboards, etc.
+
+CONFIG_SERIAL_8250_SHARE_IRQ
+  Some serial boards have hardware support which allows multiple dumb
+  serial ports on the same board to share a single IRQ. To enable
+  support for this in the serial driver, say Y here.
+
+CONFIG_SERIAL_8250_DETECT_IRQ
+  Say Y here if you want the kernel to try to guess which IRQ
+  to use for your serial port.
+
+  This is considered unsafe; it is far better to configure the IRQ in
+  a boot script using the setserial command.
+
+  If unsure, say N.
+
+CONFIG_SERIAL_8250_MULTIPORT
+  Some multiport serial ports have special ports which are used to
+  signal when there are any serial ports on the board which need
+  servicing. Say Y here to enable the serial driver to take advantage
+  of those special I/O ports.
+
+CONFIG_SERIAL_8250_RSA
+  ::: To be written :::
+
+CONFIG_ATOMWIDE_SERIAL
+  If you have an Atomwide Serial card for an Acorn system, say Y to
+  this option.  The driver can handle 1, 2, or 3 port cards.
+  If unsure, say N.
+
+CONFIG_DUALSP_SERIAL
+  If you have the Serial Port's dual serial card for an Acorn system,
+  say Y to this option.  If unsure, say N.
+
+CONFIG_SERIAL_ANAKIN
+  ::: To be written :::
+CONFIG_SERIAL_ANAKIN_CONSOLE
+  ::: To be written :::
+
+  Even if you say Y here, the currently visible virtual console
+  (/dev/tty0) will still be used as the system console by default, but
+  you can alter that using a kernel command line option such as
+  "console=ttyAN0". (Try "man bootparam" or see the documentation of
+  your boot loader (lilo or loadlin) about how to pass options to the
+  kernel at boot time.)
+
+CONFIG_ANAKIN_DEFAULT_BAUDRATE
+  ::: To be written :::
+
+CONFIG_SERIAL_AMBA
+  This selects the ARM(R) AMBA(R) PrimeCell UART.  If you have an
+  Integrator platform, say Y or M here.
+
+  If unsure, say N.
+
+CONFIG_SERIAL_AMBA_CONSOLE
+  Say Y here if you wish to use an AMBA PrimeCell UART as the system
+  console (the system console is the device which receives all kernel
+  messages and warnings and which allows logins in single user mode).
+
+  Even if you say Y here, the currently visible framebuffer console
+  (/dev/tty0) will still be used as the system console by default, but
+  you can alter that using a kernel command line option such as
+  "console=ttyAM0". (Try "man bootparam" or see the documentation of
+  your boot loader (lilo or loadlin) about how to pass options to the
+  kernel at boot time.)
+
+CONFIG_SERIAL_CLPS711X
+  ::: To be written :::
+
+CONFIG_SERIAL_CLPS711X_CONSOLE
+  ::: To be written :::
+
+  Even if you say Y here, the currently visible virtual console
+  (/dev/tty0) will still be used as the system console by default, but
+  you can alter that using a kernel command line option such as
+  "console=ttyCL1". (Try "man bootparam" or see the documentation of
+  your boot loader (lilo or loadlin) about how to pass options to the
+  kernel at boot time.)
+
+CONFIG_SERIAL_CLPS711X_OLD_NAME
+  ::: To be written :::
+
+CONFIG_SERIAL_21285
+  If you have a machine based on a 21285 (Footbridge) StrongARM(R)/
+  PCI bridge you can enable its onboard serial port by enabling this
+  option.
+
+CONFIG_SERIAL_21285_OLD
+  Use the old /dev/ttyS name, major 4 minor 64.  This is obsolete
+  and will be removed during later 2.5 development.
+
+CONFIG_SERIAL_21285_CONSOLE
+  If you have enabled the serial port on the 21285 footbridge you can
+  make it the console by answering Y to this option.
+
+  Even if you say Y here, the currently visible virtual console
+  (/dev/tty0) will still be used as the system console by default, but
+  you can alter that using a kernel command line option such as
+  "console=ttyFB". (Try "man bootparam" or see the documentation of
+  your boot loader (lilo or loadlin) about how to pass options to the
+  kernel at boot time.)
+
+CONFIG_SERIAL_UART00
+  Say Y here if you want to use the hard logic uart on Excalibur. This
+  driver also supports soft logic implentations of this uart core.
+
+CONFIG_SERIAL_UART00_CONSOLE
+  Say Y here if you want to support a serial console on an Excalibur
+  hard logic uart or uart00 IP core.
+
+  Even if you say Y here, the currently visible virtual console
+  (/dev/tty0) will still be used as the system console by default, but
+  you can alter that using a kernel command line option such as
+  "console=ttyS1". (Try "man bootparam" or see the documentation of
+  your boot loader (lilo or loadlin) about how to pass options to the
+  kernel at boot time.)
+
+CONFIG_SERIAL_SA1100
+  If you have a machine based on a SA1100/SA1110 StrongARM(R) CPU you
+  can enable its onboard serial port by enabling this option.
+  Please read <file:Documentation/arm/SA1100/serial_UART> for further
+  info.
+
+CONFIG_SERIAL_SA1100_CONSOLE
+  If you have enabled the serial port on the SA1100/SA1110 StrongARM
+  CPU you can make it the console by answering Y to this option.
+
+  Even if you say Y here, the currently visible virtual console
+  (/dev/tty0) will still be used as the system console by default, but
+  you can alter that using a kernel command line option such as
+  "console=ttySA0". (Try "man bootparam" or see the documentation of
+  your boot loader (lilo or loadlin) about how to pass options to the
+  kernel at boot time.)
+
+#CONFIG_SERIAL_L7200
+#  If you have a LinkUp Systems L7200 board you can enable its two
+#  onboard serial ports by enabling this option. The device numbers
+#  are major ID 4 with minor 64 and 65 respectively.
+#
+#CONFIG_SERIAL_L7200_CONSOLE
+#  If you have enabled the serial ports on the L7200 development board
+#  you can make the first serial port the console by answering Y to
+#  this option.
+
diff --git a/drivers/serial/Config.in b/drivers/serial/Config.in
new file mode 100644 (file)
index 0000000..7783775
--- /dev/null
@@ -0,0 +1,77 @@
+#
+# Serial device configuration
+#
+# $Id: Config.in,v 1.15 2002/07/06 17:16:24 rmk Exp $
+#
+mainmenu_option next_comment
+comment 'Serial drivers'
+
+#
+# The new 8250/16550 serial drivers
+dep_tristate '8250/16550 and compatible serial support (EXPERIMENTAL)' CONFIG_SERIAL_8250 $CONFIG_EXPERIMENTAL
+dep_bool '  Console on 8250/16550 and compatible serial port (EXPERIMENTAL)' CONFIG_SERIAL_8250_CONSOLE $CONFIG_SERIAL_8250 $CONFIG_EXPERIMENTAL
+dep_tristate '  8250/16550 PCMCIA device support' CONFIG_SERIAL_8250_CS $CONFIG_PCMCIA $CONFIG_SERIAL_8250
+
+dep_mbool 'Extended 8250/16550 serial driver options' CONFIG_SERIAL_8250_EXTENDED $CONFIG_SERIAL_8250
+dep_bool '  Support more than 4 serial ports' CONFIG_SERIAL_8250_MANY_PORTS $CONFIG_SERIAL_8250_EXTENDED
+dep_bool '  Support for sharing serial interrupts' CONFIG_SERIAL_8250_SHARE_IRQ $CONFIG_SERIAL_8250_EXTENDED
+dep_bool '  Autodetect IRQ on standard ports (unsafe)' CONFIG_SERIAL_8250_DETECT_IRQ $CONFIG_SERIAL_8250_EXTENDED
+dep_bool '  Support special multiport boards' CONFIG_SERIAL_8250_MULTIPORT $CONFIG_SERIAL_8250_EXTENDED
+dep_bool '  Support RSA serial ports' CONFIG_SERIAL_8250_RSA $CONFIG_SERIAL_8250_EXTENDED
+
+comment 'Non-8250 serial port support'
+
+if [ "$CONFIG_ARM" = "y" ]; then
+   dep_tristate 'Acorn Atomwide 16550 serial port support' CONFIG_ATOMWIDE_SERIAL $CONFIG_ARCH_ACORN $CONFIG_SERIAL_8250
+   dep_tristate 'Acorn Dual 16550 serial port support' CONFIG_DUALSP_SERIAL $CONFIG_ARCH_ACORN $CONFIG_SERIAL_8250
+   dep_bool 'Anakin serial port support' CONFIG_SERIAL_ANAKIN $CONFIG_ARCH_ANAKIN
+   dep_bool '  Console on Anakin serial port' CONFIG_SERIAL_ANAKIN_CONSOLE $CONFIG_SERIAL_ANAKIN
+   if [ "$CONFIG_SERIAL_ANAKIN" = "y" ]; then
+      int  '  Default Anakin serial baudrate' CONFIG_ANAKIN_DEFAULT_BAUDRATE 9600
+   fi
+
+   dep_tristate 'ARM AMBA serial port support' CONFIG_SERIAL_AMBA $CONFIG_ARCH_INTEGRATOR
+   dep_bool '  Support for console on AMBA serial port' CONFIG_SERIAL_AMBA_CONSOLE $CONFIG_SERIAL_AMBA
+   if [ "$CONFIG_SERIAL_AMBA" = "y" ]; then
+      define_bool CONFIG_SERIAL_INTEGRATOR y
+   fi
+
+   dep_tristate 'CLPS711X serial port support' CONFIG_SERIAL_CLPS711X $CONFIG_ARCH_CLPS711X
+   dep_bool '  Support for console on CLPS711X serial port' CONFIG_SERIAL_CLPS711X_CONSOLE $CONFIG_SERIAL_CLPS711X
+   dep_bool '  Use the old 2.4 names for CLPS711X serial port' CONFIG_SERIAL_CLPS711X_OLD_NAME $CONFIG_SERIAL_CLPS711X
+
+   dep_tristate 'DC21285 serial port support' CONFIG_SERIAL_21285 $CONFIG_FOOTBRIDGE
+   dep_bool '  Use /dev/ttyS0 device (OBSOLETE)' CONFIG_SERIAL_21285_OLD $CONFIG_SERIAL_21285 $CONFIG_OBSOLETE
+   dep_bool '  Console on DC21285 serial port' CONFIG_SERIAL_21285_CONSOLE $CONFIG_SERIAL_21285
+
+   dep_bool 'Excalibur serial port (uart00) support' CONFIG_SERIAL_UART00 $CONFIG_ARCH_CAMELOT
+   dep_bool '  Support for console on Excalibur serial port' CONFIG_SERIAL_UART00_CONSOLE $CONFIG_SERIAL_UART00
+
+   dep_bool 'SA1100 serial port support' CONFIG_SERIAL_SA1100 $CONFIG_ARCH_SA1100
+   dep_bool '  Console on SA1100 serial port' CONFIG_SERIAL_SA1100_CONSOLE $CONFIG_SERIAL_SA1100
+fi
+
+if [ "$CONFIG_SERIAL_AMBA" = "y"   -o "$CONFIG_SERIAL_CLPS711X" = "y" -o \
+     "$CONFIG_SERIAL_21285" = "y"  -o "$CONFIG_SERIAL_SA1100" = "y" -o \
+     "$CONFIG_SERIAL_ANAKIN" = "y" -o "$CONFIG_SERIAL_UART00" = "y" -o \
+     "$CONFIG_SERIAL_8250" = "y"   -o "$CONFIG_SERIAL_ROCKETPORT" = "y" ]; then
+   define_bool CONFIG_SERIAL_CORE y
+else
+   if [ "$CONFIG_SERIAL_AMBA" = "m"   -o "$CONFIG_SERIAL_CLPS711X" = "m" -o \
+        "$CONFIG_SERIAL_21285" = "m"  -o "$CONFIG_SERIAL_SA1100" = "m" -o \
+        "$CONFIG_SERIAL_ANAKIN" = "m" -o "$CONFIG_SERIAL_UART00" = "m" -o \
+        "$CONFIG_SERIAL_8250" = "m"   -o "$CONFIG_SERIAL_ROCKETPORT" = "m" ]; then
+      define_bool CONFIG_SERIAL_CORE m
+   fi
+fi
+if [ "$CONFIG_SERIAL_AMBA_CONSOLE" = "y" -o \
+     "$CONFIG_SERIAL_CLPS711X_CONSOLE" = "y" -o \
+     "$CONFIG_SERIAL_21285_CONSOLE" = "y" -o \
+     "$CONFIG_SERIAL_SA1100_CONSOLE" = "y" -o \
+     "$CONFIG_SERIAL_ANAKIN_CONSOLE" = "y" -o \
+     "$CONFIG_SERIAL_UART00_CONSOLE" = "y" -o \
+     "$CONFIG_SERIAL_8250_CONSOLE" = "y" ]; then
+   define_bool CONFIG_SERIAL_CORE_CONSOLE y
+fi
+
+endmenu
diff --git a/drivers/serial/Makefile b/drivers/serial/Makefile
new file mode 100644 (file)
index 0000000..613e9d9
--- /dev/null
@@ -0,0 +1,22 @@
+#
+# Makefile for the kernel serial device drivers.
+#
+#  $Id: Makefile,v 1.7 2002/07/06 17:16:24 rmk Exp $
+#
+
+export-objs    := serial_core.o serial_8250.o
+
+serial-8250-y :=
+serial-8250-$(CONFIG_PCI) += serial_8250_pci.o
+serial-8250-$(CONFIG_ISAPNP) += serial_8250_pnp.o
+obj-$(CONFIG_SERIAL_CORE) += serial_core.o
+obj-$(CONFIG_SERIAL_21285) += serial_21285.o
+obj-$(CONFIG_SERIAL_8250) += serial_8250.o $(serial-8250-y)
+obj-$(CONFIG_SERIAL_8250_CS) += serial_8250_cs.o
+obj-$(CONFIG_SERIAL_ANAKIN) += serial_anakin.o
+obj-$(CONFIG_SERIAL_AMBA) += serial_amba.o
+obj-$(CONFIG_SERIAL_CLPS711X) += serial_clps711x.o
+obj-$(CONFIG_SERIAL_SA1100) += serial_sa1100.o
+obj-$(CONFIG_SERIAL_UART00) += serial_uart00.o
+
+include $(TOPDIR)/Rules.make
diff --git a/drivers/serial/serial_21285.c b/drivers/serial/serial_21285.c
new file mode 100644 (file)
index 0000000..0b63b3a
--- /dev/null
@@ -0,0 +1,553 @@
+/*
+ * linux/drivers/char/serial_21285.c
+ *
+ * Driver for the serial port on the 21285 StrongArm-110 core logic chip.
+ *
+ * Based on drivers/char/serial.c
+ *
+ *  $Id: serial_21285.c,v 1.32 2002/07/21 08:57:55 rmk Exp $
+ */
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <linux/signal.h>
+#include <linux/sched.h>
+#include <linux/interrupt.h>
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
+#include <linux/serial.h>
+#include <linux/major.h>
+#include <linux/ptrace.h>
+#include <linux/ioport.h>
+#include <linux/mm.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <linux/console.h>
+#include <linux/serial_core.h>
+
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/uaccess.h>
+#include <asm/hardware/dec21285.h>
+#include <asm/hardware.h>
+
+#define BAUD_BASE              (mem_fclk_21285/64)
+
+#define SERIAL_21285_NAME      "ttyFB"
+#define SERIAL_21285_MAJOR     204
+#define SERIAL_21285_MINOR     4
+
+#define RXSTAT_DUMMY_READ      0x80000000
+#define RXSTAT_FRAME           (1 << 0)
+#define RXSTAT_PARITY          (1 << 1)
+#define RXSTAT_OVERRUN         (1 << 2)
+#define RXSTAT_ANYERR          (RXSTAT_FRAME|RXSTAT_PARITY|RXSTAT_OVERRUN)
+
+#define H_UBRLCR_BREAK         (1 << 0)
+#define H_UBRLCR_PARENB                (1 << 1)
+#define H_UBRLCR_PAREVN                (1 << 2)
+#define H_UBRLCR_STOPB         (1 << 3)
+#define H_UBRLCR_FIFO          (1 << 4)
+
+static const char serial21285_name[] = "Footbridge UART";
+
+#define tx_enabled(port)       ((port)->unused[0])
+#define rx_enabled(port)       ((port)->unused[1])
+
+/*
+ * The documented expression for selecting the divisor is:
+ *  BAUD_BASE / baud - 1
+ * However, typically BAUD_BASE is not divisible by baud, so
+ * we want to select the divisor that gives us the minimum
+ * error.  Therefore, we want:
+ *  int(BAUD_BASE / baud - 0.5) ->
+ *  int(BAUD_BASE / baud - (baud >> 1) / baud) ->
+ *  int((BAUD_BASE - (baud >> 1)) / baud)
+ */
+
+static void __serial21285_stop_tx(struct uart_port *port)
+{
+       if (tx_enabled(port)) {
+               disable_irq(IRQ_CONTX);
+               tx_enabled(port) = 0;
+       }
+}
+
+static void
+serial21285_stop_tx(struct uart_port *port, unsigned int tty_stop)
+{
+       unsigned long flags;
+
+       spin_lock_irqsave(&port->lock, flags);
+       __serial21285_stop_tx(port);
+       spin_unlock_irqrestore(&port->lock, flags);
+}
+
+static void
+serial21285_start_tx(struct uart_port *port, unsigned int tty_start)
+{
+       unsigned long flags;
+
+       spin_lock_irqsave(&port->lock, flags);
+       if (!tx_enabled(port)) {
+               enable_irq(IRQ_CONTX);
+               tx_enabled(port) = 1;
+       }
+       spin_unlock_irqrestore(&port->lock, flags);
+}
+
+static void serial21285_stop_rx(struct uart_port *port)
+{
+       unsigned long flags;
+
+       spin_lock_irqsave(&port->lock, flags);
+       if (rx_enabled(port)) {
+               disable_irq(IRQ_CONRX);
+               rx_enabled(port) = 0;
+       }
+       spin_unlock_irqrestore(&port->lock, flags);
+}
+
+static void serial21285_enable_ms(struct uart_port *port)
+{
+}
+
+static void serial21285_rx_chars(int irq, void *dev_id, struct pt_regs *regs)
+{
+       struct uart_port *port = dev_id;
+       struct tty_struct *tty = port->info->tty;
+       unsigned int status, ch, rxs, max_count = 256;
+
+       status = *CSR_UARTFLG;
+       while (!(status & 0x10) && max_count--) {
+               if (tty->flip.count >= TTY_FLIPBUF_SIZE) {
+                       tty->flip.tqueue.routine((void *)tty);
+                       if (tty->flip.count >= TTY_FLIPBUF_SIZE) {
+                               printk(KERN_WARNING "TTY_DONT_FLIP set\n");
+                               return;
+                       }
+               }
+
+               ch = *CSR_UARTDR;
+
+               *tty->flip.char_buf_ptr = ch;
+               *tty->flip.flag_buf_ptr = TTY_NORMAL;
+               port->icount.rx++;
+
+               rxs = *CSR_RXSTAT | RXSTAT_DUMMY_READ;
+               if (rxs & RXSTAT_ANYERR) {
+                       if (rxs & RXSTAT_PARITY)
+                               port->icount.parity++;
+                       else if (rxs & RXSTAT_FRAME)
+                               port->icount.frame++;
+                       if (rxs & RXSTAT_OVERRUN)
+                               port->icount.overrun++;
+
+                       rxs &= port->read_status_mask;
+
+                       if (rxs & RXSTAT_PARITY)
+                               *tty->flip.flag_buf_ptr = TTY_PARITY;
+                       else if (rxs & RXSTAT_FRAME)
+                               *tty->flip.flag_buf_ptr = TTY_FRAME;
+               }
+
+               if ((rxs & port->ignore_status_mask) == 0) {
+                       tty->flip.flag_buf_ptr++;
+                       tty->flip.char_buf_ptr++;
+                       tty->flip.count++;
+               }
+               if ((rxs & RXSTAT_OVERRUN) &&
+                   tty->flip.count < TTY_FLIPBUF_SIZE) {
+                       /*
+                        * Overrun is special, since it's reported
+                        * immediately, and doesn't affect the current
+                        * character.
+                        */
+                       *tty->flip.char_buf_ptr++ = 0;
+                       *tty->flip.flag_buf_ptr++ = TTY_OVERRUN;
+                       tty->flip.count++;
+               }
+               status = *CSR_UARTFLG;
+       }
+       tty_flip_buffer_push(tty);
+}
+
+static void serial21285_tx_chars(int irq, void *dev_id, struct pt_regs *regs)
+{
+       struct uart_port *port = dev_id;
+       struct circ_buf *xmit = &port->info->xmit;
+       int count = 256;
+
+       if (port->x_char) {
+               *CSR_UARTDR = port->x_char;
+               port->icount.tx++;
+               port->x_char = 0;
+               return;
+       }
+       if (uart_circ_empty(xmit) || uart_tx_stopped(port)) {
+               __serial21285_stop_tx(port);
+               return;
+       }
+
+       do {
+               *CSR_UARTDR = xmit->buf[xmit->tail];
+               xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
+               port->icount.tx++;
+               if (uart_circ_empty(xmit))
+                       break;
+       } while (--count > 0 && !(*CSR_UARTFLG & 0x20));
+
+       if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
+               uart_event(port, EVT_WRITE_WAKEUP);
+
+       if (uart_circ_empty(xmit))
+               __serial21285_stop_tx(port);
+}
+
+static unsigned int serial21285_tx_empty(struct uart_port *port)
+{
+       return (*CSR_UARTFLG & 8) ? 0 : TIOCSER_TEMT;
+}
+
+/* no modem control lines */
+static unsigned int serial21285_get_mctrl(struct uart_port *port)
+{
+       return TIOCM_CAR | TIOCM_DSR | TIOCM_CTS;
+}
+
+static void serial21285_set_mctrl(struct uart_port *port, unsigned int mctrl)
+{
+}
+
+static void serial21285_break_ctl(struct uart_port *port, int break_state)
+{
+       unsigned long flags;
+       unsigned int h_lcr;
+
+       spin_lock_irqsave(&port->lock, flags);
+       h_lcr = *CSR_H_UBRLCR;
+       if (break_state)
+               h_lcr |= H_UBRLCR_BREAK;
+       else
+               h_lcr &= ~H_UBRLCR_BREAK;
+       *CSR_H_UBRLCR = h_lcr;
+       spin_unlock_irqrestore(&port->lock, flags);
+}
+
+static int serial21285_startup(struct uart_port *port)
+{
+       int ret;
+
+       tx_enabled(port) = 1;
+       rx_enabled(port) = 1;
+
+       ret = request_irq(IRQ_CONRX, serial21285_rx_chars, 0,
+                         serial21285_name, port);
+       if (ret == 0) {
+               ret = request_irq(IRQ_CONTX, serial21285_tx_chars, 0,
+                                 serial21285_name, port);
+               if (ret)
+                       free_irq(IRQ_CONRX, port);
+       }
+
+       return ret;
+}
+
+static void serial21285_shutdown(struct uart_port *port)
+{
+       free_irq(IRQ_CONTX, port);
+       free_irq(IRQ_CONRX, port);
+}
+
+static void
+serial21285_change_speed(struct uart_port *port, unsigned int cflag,
+                        unsigned int iflag, unsigned int quot)
+{
+       unsigned int h_lcr;
+
+       switch (cflag & CSIZE) {
+       case CS5:
+               h_lcr = 0x00;
+               break;
+       case CS6:
+               h_lcr = 0x20;
+               break;
+       case CS7:
+               h_lcr = 0x40;
+               break;
+       default: /* CS8 */
+               h_lcr = 0x60;
+               break;
+       }
+
+       if (cflag & CSTOPB)
+               h_lcr |= H_UBRLCR_STOPB;
+       if (cflag & PARENB) {
+               h_lcr |= H_UBRLCR_PARENB;
+               if (!(cflag & PARODD))
+                       h_lcr |= H_UBRLCR_PAREVN;
+       }
+
+       if (port->fifosize)
+               h_lcr |= H_UBRLCR_FIFO;
+
+       port->read_status_mask = RXSTAT_OVERRUN;
+       if (iflag & INPCK)
+               port->read_status_mask |= RXSTAT_FRAME | RXSTAT_PARITY;
+
+       /*
+        * Characters to ignore
+        */
+       port->ignore_status_mask = 0;
+       if (iflag & IGNPAR)
+               port->ignore_status_mask |= RXSTAT_FRAME | RXSTAT_PARITY;
+       if (iflag & IGNBRK && iflag & IGNPAR)
+               port->ignore_status_mask |= RXSTAT_OVERRUN;
+
+       /*
+        * Ignore all characters if CREAD is not set.
+        */
+       if ((cflag & CREAD) == 0)
+               port->ignore_status_mask |= RXSTAT_DUMMY_READ;
+
+       quot -= 1;
+
+       *CSR_UARTCON = 0;
+       *CSR_L_UBRLCR = quot & 0xff;
+       *CSR_M_UBRLCR = (quot >> 8) & 0x0f;
+       *CSR_H_UBRLCR = h_lcr;
+       *CSR_UARTCON = 1;
+}
+
+static const char *serial21285_type(struct uart_port *port)
+{
+       return port->type == PORT_21285 ? "DC21285" : NULL;
+}
+
+static void serial21285_release_port(struct uart_port *port)
+{
+       release_mem_region(port->mapbase, 32);
+}
+
+static int serial21285_request_port(struct uart_port *port)
+{
+       return request_mem_region(port->mapbase, 32, serial21285_name)
+                        != NULL ? 0 : -EBUSY;
+}
+
+static void serial21285_config_port(struct uart_port *port, int flags)
+{
+       if (flags & UART_CONFIG_TYPE && serial21285_request_port(port) == 0)
+               port->type = PORT_21285;
+}
+
+/*
+ * verify the new serial_struct (for TIOCSSERIAL).
+ */
+static int serial21285_verify_port(struct uart_port *port, struct serial_struct *ser)
+{
+       int ret = 0;
+       if (ser->type != PORT_UNKNOWN && ser->type != PORT_21285)
+               ret = -EINVAL;
+       if (ser->irq != NO_IRQ)
+               ret = -EINVAL;
+       if (ser->baud_base != port->uartclk / 16)
+               ret = -EINVAL;
+       return ret;
+}
+
+static struct uart_ops serial21285_ops = {
+       tx_empty:       serial21285_tx_empty,
+       get_mctrl:      serial21285_get_mctrl,
+       set_mctrl:      serial21285_set_mctrl,
+       stop_tx:        serial21285_stop_tx,
+       start_tx:       serial21285_start_tx,
+       stop_rx:        serial21285_stop_rx,
+       enable_ms:      serial21285_enable_ms,
+       break_ctl:      serial21285_break_ctl,
+       startup:        serial21285_startup,
+       shutdown:       serial21285_shutdown,
+       change_speed:   serial21285_change_speed,
+       type:           serial21285_type,
+       release_port:   serial21285_release_port,
+       request_port:   serial21285_request_port,
+       config_port:    serial21285_config_port,
+       verify_port:    serial21285_verify_port,
+};
+
+static struct uart_port serial21285_port = {
+       membase:        0,
+       mapbase:        0x42000160,
+       iotype:         SERIAL_IO_MEM,
+       irq:            NO_IRQ,
+       uartclk:        0,
+       fifosize:       16,
+       ops:            &serial21285_ops,
+       flags:          ASYNC_BOOT_AUTOCONF,
+};
+
+static void serial21285_setup_ports(void)
+{
+       serial21285_port.uartclk = mem_fclk_21285 / 4;
+}
+
+#ifdef CONFIG_SERIAL_21285_CONSOLE
+
+static void
+serial21285_console_write(struct console *co, const char *s,
+                         unsigned int count)
+{
+       int i;
+
+       for (i = 0; i < count; i++) {
+               while (*CSR_UARTFLG & 0x20)
+                       barrier();
+               *CSR_UARTDR = s[i];
+               if (s[i] == '\n') {
+                       while (*CSR_UARTFLG & 0x20)
+                               barrier();
+                       *CSR_UARTDR = '\r';
+               }
+       }
+}
+
+static kdev_t serial21285_console_device(struct console *c)
+{
+       return mk_kdev(SERIAL_21285_MAJOR, SERIAL_21285_MINOR);
+}
+
+static void __init
+serial21285_get_options(struct uart_port *port, int *baud,
+                       int *parity, int *bits)
+{
+       if (*CSR_UARTCON == 1) {
+               unsigned int tmp;
+
+               tmp = *CSR_H_UBRLCR;
+               switch (tmp & 0x60) {
+               case 0x00:
+                       *bits = 5;
+                       break;
+               case 0x20:
+                       *bits = 6;
+                       break;
+               case 0x40:
+                       *bits = 7;
+                       break;
+               default:
+               case 0x60:
+                       *bits = 8;
+                       break;
+               }
+
+               if (tmp & H_UBRLCR_PARENB) {
+                       *parity = 'o';
+                       if (tmp & H_UBRLCR_PAREVN)
+                               *parity = 'e';
+               }
+
+               tmp = *CSR_L_UBRLCR | (*CSR_M_UBRLCR << 8);
+
+               *baud = port->uartclk / (16 * (tmp + 1));
+       }
+}
+
+static int __init serial21285_console_setup(struct console *co, char *options)
+{
+       struct uart_port *port = &serial21285_port;
+       int baud = 9600;
+       int bits = 8;
+       int parity = 'n';
+       int flow = 'n';
+
+       if (machine_is_personal_server())
+               baud = 57600;
+
+       /*
+        * Check whether an invalid uart number has been specified, and
+        * if so, search for the first available port that does have
+        * console support.
+        */
+       if (options)
+               uart_parse_options(options, &baud, &parity, &bits, &flow);
+       else
+               serial21285_get_options(port, &baud, &parity, &bits);
+
+       return uart_set_options(port, co, baud, parity, bits, flow);
+}
+
+#ifdef CONFIG_SERIAL_21285_OLD
+static struct console serial21285_old_cons =
+{
+       name:           SERIAL_21285_OLD_NAME,
+       write:          serial21285_console_write,
+       device:         serial21285_console_device,
+       setup:          serial21285_console_setup,
+       flags:          CON_PRINTBUFFER,
+       index:          -1,
+};
+#endif
+
+static struct console serial21285_console =
+{
+       name:           SERIAL_21285_NAME,
+       write:          serial21285_console_write,
+       device:         serial21285_console_device,
+       setup:          serial21285_console_setup,
+       flags:          CON_PRINTBUFFER,
+       index:          -1,
+};
+
+void __init rs285_console_init(void)
+{
+       serial21285_setup_ports();
+       register_console(&serial21285_console);
+}
+
+#define SERIAL_21285_CONSOLE   &serial21285_console
+#else
+#define SERIAL_21285_CONSOLE   NULL
+#endif
+
+static struct uart_driver serial21285_reg = {
+       owner:                  THIS_MODULE,
+       driver_name:            "ttyFB",
+#ifdef CONFIG_DEVFS_FS
+       dev_name:               "ttyFB%d",
+#else
+       dev_name:               "ttyFB",
+#endif
+       major:                  SERIAL_21285_MAJOR,
+       minor:                  SERIAL_21285_MINOR,
+       nr:                     1,
+       cons:                   SERIAL_21285_CONSOLE,
+};
+
+static int __init serial21285_init(void)
+{
+       int ret;
+
+       printk(KERN_INFO "Serial: 21285 driver $Revision: 1.32 $\n");
+
+       serial21285_setup_ports();
+
+       ret = uart_register_driver(&serial21285_reg);
+       if (ret == 0)
+               uart_add_one_port(&serial21285_reg, &serial21285_port);
+
+       return ret;
+}
+
+static void __exit serial21285_exit(void)
+{
+       uart_remove_one_port(&serial21285_reg, &serial21285_port);
+       uart_unregister_driver(&serial21285_reg);
+}
+
+module_init(serial21285_init);
+module_exit(serial21285_exit);
+
+EXPORT_NO_SYMBOLS;
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Intel Footbridge (21285) serial driver $Revision: 1.32 $");
diff --git a/drivers/serial/serial_8250.c b/drivers/serial/serial_8250.c
new file mode 100644 (file)
index 0000000..b763e3f
--- /dev/null
@@ -0,0 +1,2015 @@
+/*
+ *  linux/drivers/char/serial_8250.c
+ *
+ *  Driver for 8250/16550-type serial ports
+ *
+ *  Based on drivers/char/serial.c, by Linus Torvalds, Theodore Ts'o.
+ *
+ *  Copyright (C) 2001 Russell King.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ *  $Id: serial_8250.c,v 1.80 2002/07/21 08:57:55 rmk Exp $
+ *
+ * A note about mapbase / membase
+ *
+ *  mapbase is the physical address of the IO port.  Currently, we don't
+ *  support this very well, and it may well be dropped from this driver
+ *  in future.  As such, mapbase should be NULL.
+ *
+ *  membase is an 'ioremapped' cookie.  This is compatible with the old
+ *  serial.c driver, and is currently the preferred form.
+ */
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/compiler.h>
+#include <linux/errno.h>
+#include <linux/sched.h>
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
+#include <linux/major.h>
+#include <linux/slab.h>
+#include <linux/ptrace.h>
+#include <linux/ioport.h>
+#include <linux/init.h>
+#include <linux/serial.h>
+#include <linux/console.h>
+#include <linux/sysrq.h>
+#include <linux/serial_reg.h>
+#include <linux/serialP.h>
+#include <linux/delay.h>
+#include <linux/kmod.h>
+
+#include <asm/system.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/uaccess.h>
+#include <asm/bitops.h>
+
+#if defined(CONFIG_SERIAL_8250_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ)
+#define SUPPORT_SYSRQ
+#endif
+
+#include <linux/serial_core.h>
+#include "serial_8250.h"
+
+/*
+ * Configuration:
+ *   share_irqs - whether we pass SA_SHIRQ to request_irq().  This option
+ *                is unsafe when used on edge-triggered interrupts.
+ */
+unsigned int share_irqs = SERIAL8250_SHARE_IRQS;
+
+/*
+ * Debugging.
+ */
+#if 0
+#define DEBUG_AUTOCONF(fmt...) printk(fmt)
+#else
+#define DEBUG_AUTOCONF(fmt...) do { } while (0)
+#endif
+
+#if 0
+#define DEBUG_INTR(fmt...)     printk(fmt)
+#else
+#define DEBUG_INTR(fmt...)     do { } while (0)
+#endif
+
+#define PASS_LIMIT     256
+
+/*
+ * We default to IRQ0 for the "no irq" hack.   Some
+ * machine types want others as well - they're free
+ * to redefine this in their header file.
+ */
+#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
+
+/*
+ * HUB6 is always on.  This will be removed once the header
+ * files have been cleaned.
+ */
+#define CONFIG_HUB6 1
+#define CONFIG_SERIAL_MANY_PORTS 1
+
+#include <asm/serial.h>
+
+static struct old_serial_port old_serial_port[] = {
+       SERIAL_PORT_DFNS /* defined in asm/serial.h */
+};
+
+#define UART_NR        ARRAY_SIZE(old_serial_port)
+
+#if defined(CONFIG_SERIAL_8250_RSA) && defined(MODULE)
+
+#define PORT_RSA_MAX 4
+static int probe_rsa[PORT_RSA_MAX];
+static int force_rsa[PORT_RSA_MAX];
+#endif /* CONFIG_SERIAL_8250_RSA  */
+
+struct uart_8250_port {
+       struct uart_port        port;
+       struct timer_list       timer;          /* "no irq" timer */
+       struct list_head        list;           /* ports on this IRQ */
+       unsigned char           acr;
+       unsigned char           ier;
+       unsigned char           rev;
+       unsigned char           lcr;
+       unsigned int            lsr_break_flag;
+
+       /*
+        * We provide a per-port pm hook.
+        */
+       void                    (*pm)(struct uart_port *port,
+                                     unsigned int state, unsigned int old);
+};
+
+struct irq_info {
+       spinlock_t              lock;
+       struct list_head        *head;
+};
+
+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 serial_uart_config uart_config[PORT_MAX_8250+1] = {
+       { "unknown",    1,      0 },
+       { "8250",       1,      0 },
+       { "16450",      1,      0 },
+       { "16550",      1,      0 },
+       { "16550A",     16,     UART_CLEAR_FIFO | UART_USE_FIFO },
+       { "Cirrus",     1,      0 },
+       { "ST16650",    1,      UART_CLEAR_FIFO | UART_STARTECH },
+       { "ST16650V2",  32,     UART_CLEAR_FIFO | UART_USE_FIFO | UART_STARTECH },
+       { "TI16750",    64,     UART_CLEAR_FIFO | UART_USE_FIFO },
+       { "Startech",   1,      0 },
+       { "16C950/954", 128,    UART_CLEAR_FIFO | UART_USE_FIFO },
+       { "ST16654",    64,     UART_CLEAR_FIFO | UART_USE_FIFO | UART_STARTECH },
+       { "XR16850",    128,    UART_CLEAR_FIFO | UART_USE_FIFO | UART_STARTECH },
+       { "RSA",        2048,   UART_CLEAR_FIFO | UART_USE_FIFO }
+};
+
+static _INLINE_ unsigned int serial_in(struct uart_8250_port *up, int offset)
+{
+       offset <<= up->port.regshift;
+
+       switch (up->port.iotype) {
+       case SERIAL_IO_HUB6:
+               outb(up->port.hub6 - 1 + offset, up->port.iobase);
+               return inb(up->port.iobase + 1);
+
+       case SERIAL_IO_MEM:
+               return readb(up->port.membase + offset);
+
+       default:
+               return inb(up->port.iobase + offset);
+       }
+}
+
+static _INLINE_ void
+serial_out(struct uart_8250_port *up, int offset, int value)
+{
+       offset <<= up->port.regshift;
+
+       switch (up->port.iotype) {
+       case SERIAL_IO_HUB6:
+               outb(up->port.hub6 - 1 + offset, up->port.iobase);
+               outb(value, up->port.iobase + 1);
+               break;
+
+       case SERIAL_IO_MEM:
+               writeb(value, up->port.membase + offset);
+               break;
+
+       default:
+               outb(value, up->port.iobase + offset);
+       }
+}
+
+/*
+ * We used to support using pause I/O for certain machines.  We
+ * haven't supported this for a while, but just in case it's badly
+ * needed for certain old 386 machines, I've left these #define's
+ * in....
+ */
+#define serial_inp(up, offset)         serial_in(up, offset)
+#define serial_outp(up, offset, value) serial_out(up, offset, value)
+
+
+/*
+ * For the 16C950
+ */
+static void serial_icr_write(struct uart_8250_port *up, int offset, int value)
+{
+       serial_out(up, UART_SCR, offset);
+       serial_out(up, UART_ICR, value);
+}
+
+static unsigned int serial_icr_read(struct uart_8250_port *up, int offset)
+{
+       unsigned int value;
+
+       serial_icr_write(up, UART_ACR, up->acr | UART_ACR_ICRRD);
+       serial_out(up, UART_SCR, offset);
+       value = serial_in(up, UART_ICR);
+       serial_icr_write(up, UART_ACR, up->acr);
+
+       return value;
+}
+
+#ifdef CONFIG_SERIAL_8250_RSA
+/*
+ * Attempts to turn on the RSA FIFO.  Returns zero on failure.
+ * We set the port uart clock rate if we succeed.
+ */
+static int __enable_rsa(struct uart_8250_port *up)
+{
+       unsigned char mode;
+       int result;
+
+       mode = serial_inp(up, UART_RSA_MSR);
+       result = mode & UART_RSA_MSR_FIFO;
+
+       if (!result) {
+               serial_outp(up, UART_RSA_MSR, mode | UART_RSA_MSR_FIFO);
+               mode = serial_inp(up, UART_RSA_MSR);
+               result = mode & UART_RSA_MSR_FIFO;
+       }
+
+       if (result)
+               up->port.uartclk = SERIAL_RSA_BAUD_BASE * 16;
+
+       return result;
+}
+
+static void enable_rsa(struct uart_8250_port *up)
+{
+       if (up->port.type == PORT_RSA) {
+               if (up->port.uartclk != SERIAL_RSA_BAUD_BASE * 16) {
+                       spin_lock_irq(&up->port.lock);
+                       __enable_rsa(up);
+                       spin_unlock_irq(&up->port.lock);
+               }
+               if (up->port.uartclk == SERIAL_RSA_BAUD_BASE * 16)
+                       serial_outp(up, UART_RSA_FRR, 0);
+       }
+}
+
+/*
+ * Attempts to turn off the RSA FIFO.  Returns zero on failure.
+ * It is unknown why interrupts were disabled in here.  However,
+ * the caller is expected to preserve this behaviour by grabbing
+ * the spinlock before calling this function.
+ */
+static void disable_rsa(struct uart_8250_port *up)
+{
+       unsigned char mode;
+       int result;
+
+       if (up->port.type == PORT_RSA &&
+           up->port.uartclk == SERIAL_RSA_BAUD_BASE * 16) {
+               spin_lock_irq(&up->port.lock);
+
+               mode = serial_inp(up, UART_RSA_MSR);
+               result = !(mode & UART_RSA_MSR_FIFO);
+
+               if (!result) {
+                       serial_outp(up, UART_RSA_MSR, mode & ~UART_RSA_MSR_FIFO);
+                       mode = serial_inp(up, UART_RSA_MSR);
+                       result = !(mode & UART_RSA_MSR_FIFO);
+               }
+
+               if (result)
+                       up->port.uartclk = SERIAL_RSA_BAUD_BASE_LO * 16;
+               spin_unlock_irq(&up->port.lock);
+       }
+}
+#endif /* CONFIG_SERIAL_8250_RSA */
+
+/*
+ * This is a quickie test to see how big the FIFO is.
+ * It doesn't work at all the time, more's the pity.
+ */
+static int size_fifo(struct uart_8250_port *up)
+{
+       unsigned char old_fcr, old_mcr, old_dll, old_dlm;
+       int count;
+
+       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);
+       serial_outp(up, UART_LCR, 0x03);
+       for (count = 0; count < 256; count++)
+               serial_outp(up, UART_TX, count);
+       mdelay(20);/* FIXME - schedule_timeout */
+       for (count = 0; (serial_inp(up, UART_LSR) & UART_LSR_DR) &&
+            (count < 256); count++)
+               serial_inp(up, UART_RX);
+       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);
+
+       return count;
+}
+
+/*
+ * 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
+ * 16650 V2, but it might be one of several StarTech UARTs, or one of
+ * its clones.  (We treat the broken original StarTech 16650 V1 as a
+ * 16550, and why not?  Startech doesn't seem to even acknowledge its
+ * existence.)
+ * 
+ * What evil have men's minds wrought...
+ */
+static void
+autoconfig_startech_uarts(struct uart_8250_port *up)
+{
+       unsigned char scratch, scratch2, scratch3, scratch4;
+
+       /*
+        * First we check to see if it's an Oxford Semiconductor UART.
+        *
+        * If we have to do this here because some non-National
+        * Semiconductor clone chips lock up if you try writing to the
+        * LSR register (which serial_icr_read does)
+        */
+       if (up->port.type == PORT_16550A) {
+               /*
+                * EFR [4] must be set else this test fails
+                *
+                * This shouldn't be necessary, but Mike Hudson
+                * (Exoray@isys.ca) claims that it's needed for 952
+                * dual UART's (which are not recommended for new designs).
+                */
+               up->acr = 0;
+               serial_out(up, UART_LCR, 0xBF);
+               serial_out(up, UART_EFR, 0x10);
+               serial_out(up, UART_LCR, 0x00);
+               /* Check for Oxford Semiconductor 16C950 */
+               scratch = serial_icr_read(up, UART_ID1);
+               scratch2 = serial_icr_read(up, UART_ID2);
+               scratch3 = serial_icr_read(up, UART_ID3);
+               
+               if (scratch == 0x16 && scratch2 == 0xC9 &&
+                   (scratch3 == 0x50 || scratch3 == 0x52 ||
+                    scratch3 == 0x54)) {
+                       up->port.type = PORT_16C950;
+                       up->rev = serial_icr_read(up, UART_REV) |
+                                                 (scratch3 << 8);
+                       return;
+               }
+       }
+       
+       /*
+        * We check for a XR16C850 by setting DLL and DLM to 0, and then
+        * reading back DLL and DLM.  The chip type depends on the DLM
+        * value read back:
+        *  0x10 - XR16C850 and the DLL contains the chip revision.
+        *  0x12 - XR16C2850.
+        *  0x14 - XR16C854.
+        */
+
+       /* Save the DLL and DLM */
+
+       serial_outp(up, UART_LCR, UART_LCR_DLAB);
+       scratch3 = serial_inp(up, UART_DLL);
+       scratch4 = serial_inp(up, UART_DLM);
+
+       serial_outp(up, UART_DLL, 0);
+       serial_outp(up, UART_DLM, 0);
+       scratch2 = serial_inp(up, UART_DLL);
+       scratch = serial_inp(up, UART_DLM);
+       serial_outp(up, UART_LCR, 0);
+
+       if (scratch == 0x10 || scratch == 0x12 || scratch == 0x14) {
+               if (scratch == 0x10)
+                       up->rev = scratch2;
+               up->port.type = PORT_16850;
+               return;
+       }
+
+       /* Restore the DLL and DLM */
+
+       serial_outp(up, UART_LCR, UART_LCR_DLAB);
+       serial_outp(up, UART_DLL, scratch3);
+       serial_outp(up, UART_DLM, scratch4);
+       serial_outp(up, UART_LCR, 0);
+
+       /*
+        * We distinguish between the '654 and the '650 by counting
+        * how many bytes are in the FIFO.  I'm using this for now,
+        * since that's the technique that was sent to me in the
+        * serial driver update, but I'm not convinced this works.
+        * I've had problems doing this in the past.  -TYT
+        */
+       if (size_fifo(up) == 64)
+               up->port.type = PORT_16654;
+       else
+               up->port.type = PORT_16650V2;
+}
+
+/*
+ * This routine is called by rs_init() to initialize a specific serial
+ * port.  It determines what type of UART chip this serial port is
+ * using: 8250, 16450, 16550, 16550A.  The important question is
+ * whether or not this UART is a 16550A or not, since this will
+ * determine whether or not we can use its FIFO features or not.
+ */
+static void autoconfig(struct uart_8250_port *up, unsigned int probeflags)
+{
+       unsigned char status1, status2, scratch, scratch2, scratch3;
+       unsigned char save_lcr, save_mcr;
+       unsigned long flags;
+
+       DEBUG_AUTOCONF("Testing ttyS%d (0x%04x, 0x%08lx)...\n",
+                       up->port.line, up->port.iobase, up->port.membase);
+
+       if (!up->port.iobase && !up->port.membase)
+               return;
+
+       /*
+        * We really do need global IRQs disabled here - we're going to
+        * be frobbing the chips IRQ enable register to see if it exists.
+        */
+       spin_lock_irqsave(&up->port.lock, flags);
+//     save_flags(flags); cli();
+
+       if (!(up->port.flags & ASYNC_BUGGY_UART)) {
+               /*
+                * Do a simple existence test first; if we fail this,
+                * there's no point trying anything else.
+                * 
+                * 0x80 is used as a nonsense port to prevent against
+                * false positives due to ISA bus float.  The
+                * assumption is that 0x80 is a non-existent port;
+                * which should be safe since include/asm/io.h also
+                * makes this assumption.
+                */
+               scratch = serial_inp(up, UART_IER);
+               serial_outp(up, UART_IER, 0);
+#ifdef __i386__
+               outb(0xff, 0x080);
+#endif
+               scratch2 = serial_inp(up, UART_IER);
+               serial_outp(up, UART_IER, 0x0F);
+#ifdef __i386__
+               outb(0, 0x080);
+#endif
+               scratch3 = serial_inp(up, UART_IER);
+               serial_outp(up, UART_IER, scratch);
+               if (scratch2 != 0 || scratch3 != 0x0F) {
+                       /*
+                        * We failed; there's nothing here
+                        */
+                       DEBUG_AUTOCONF("serial: ttyS%d: simple autoconfig "
+                                      "failed (%02x, %02x)\n",
+                                      up->port.line, scratch2, scratch3);
+                       goto out;
+               }
+       }
+
+       save_mcr = serial_in(up, UART_MCR);
+       save_lcr = serial_in(up, UART_LCR);
+
+       /* 
+        * Check to see if a UART is really there.  Certain broken
+        * internal modems based on the Rockwell chipset fail this
+        * test, because they apparently don't implement the loopback
+        * test mode.  So this test is skipped on the COM 1 through
+        * COM 4 ports.  This *should* be safe, since no board
+        * manufacturer would be stupid enough to design a board
+        * that conflicts with COM 1-4 --- we hope!
+        */
+       if (!(up->port.flags & ASYNC_SKIP_TEST)) {
+               serial_outp(up, UART_MCR, UART_MCR_LOOP | 0x0A);
+               status1 = serial_inp(up, UART_MSR) & 0xF0;
+               serial_outp(up, UART_MCR, save_mcr);
+               if (status1 != 0x90) {
+                       DEBUG_AUTOCONF("serial: ttyS%d: no UART loopback "
+                                      "failed\n", up->port.line);
+                       goto out;
+               }
+       }
+       serial_outp(up, UART_LCR, 0xBF); /* set up for StarTech test */
+       serial_outp(up, UART_EFR, 0);   /* EFR is the same as FCR */
+       serial_outp(up, UART_LCR, 0);
+       serial_outp(up, UART_FCR, UART_FCR_ENABLE_FIFO);
+       scratch = serial_in(up, UART_IIR) >> 6;
+       switch (scratch) {
+               case 0:
+                       up->port.type = PORT_16450;
+                       break;
+               case 1:
+                       up->port.type = PORT_UNKNOWN;
+                       break;
+               case 2:
+                       up->port.type = PORT_16550;
+                       break;
+               case 3:
+                       up->port.type = PORT_16550A;
+                       break;
+       }
+       if (up->port.type == PORT_16550A) {
+               /* Check for Startech UART's */
+               serial_outp(up, UART_LCR, UART_LCR_DLAB);
+               if (serial_in(up, UART_EFR) == 0) {
+                       up->port.type = PORT_16650;
+               } else {
+                       serial_outp(up, UART_LCR, 0xBF);
+                       if (serial_in(up, UART_EFR) == 0)
+                               autoconfig_startech_uarts(up);
+               }
+       }
+       if (up->port.type == PORT_16550A) {
+               /* Check for TI 16750 */
+               serial_outp(up, UART_LCR, save_lcr | UART_LCR_DLAB);
+               serial_outp(up, UART_FCR,
+                           UART_FCR_ENABLE_FIFO | UART_FCR7_64BYTE);
+               scratch = serial_in(up, UART_IIR) >> 5;
+               if (scratch == 7) {
+                       /*
+                        * If this is a 16750, and not a cheap UART
+                        * clone, then it should only go into 64 byte
+                        * mode if the UART_FCR7_64BYTE bit was set
+                        * while UART_LCR_DLAB was latched.
+                        */
+                       serial_outp(up, UART_FCR, UART_FCR_ENABLE_FIFO);
+                       serial_outp(up, UART_LCR, 0);
+                       serial_outp(up, UART_FCR,
+                                   UART_FCR_ENABLE_FIFO | UART_FCR7_64BYTE);
+                       scratch = serial_in(up, UART_IIR) >> 5;
+                       if (scratch == 6)
+                               up->port.type = PORT_16750;
+               }
+               serial_outp(up, UART_FCR, UART_FCR_ENABLE_FIFO);
+       }
+#if defined(CONFIG_SERIAL_8250_RSA) && defined(MODULE)
+       /*
+        * Only probe for RSA ports if we got the region.
+        */
+       if (up->port.type == PORT_16550A && probeflags & PROBE_RSA) {
+               int i;
+
+               for (i = 0 ; i < PORT_RSA_MAX ; ++i) {
+                       if (!probe_rsa[i] && !force_rsa[i])
+                               break;
+                       if (((probe_rsa[i] != up->port.iobase) ||
+                            check_region(up->port.iobase + UART_RSA_BASE, 16)) &&
+                           (force_rsa[i] != up->port.iobase))
+                               continue;
+                       if (__enable_rsa(up)) {
+                               up->port.type = PORT_RSA;
+                               break;
+                       }
+               }
+       }
+#endif
+       serial_outp(up, UART_LCR, save_lcr);
+       if (up->port.type == PORT_16450) {
+               scratch = serial_in(up, UART_SCR);
+               serial_outp(up, UART_SCR, 0xa5);
+               status1 = serial_in(up, UART_SCR);
+               serial_outp(up, UART_SCR, 0x5a);
+               status2 = serial_in(up, UART_SCR);
+               serial_outp(up, UART_SCR, scratch);
+
+               if ((status1 != 0xa5) || (status2 != 0x5a))
+                       up->port.type = PORT_8250;
+       }
+
+       up->port.fifosize = uart_config[up->port.type].dfl_xmit_fifo_size;
+
+       if (up->port.type == PORT_UNKNOWN)
+               goto out;
+
+       /*
+        * Reset the UART.
+        */
+#ifdef CONFIG_SERIAL_8250_RSA
+       if (up->port.type == PORT_RSA)
+               serial_outp(up, UART_RSA_FRR, 0);
+#endif
+       serial_outp(up, UART_MCR, save_mcr);
+       serial_outp(up, UART_FCR, (UART_FCR_ENABLE_FIFO |
+                                    UART_FCR_CLEAR_RCVR |
+                                    UART_FCR_CLEAR_XMIT));
+       serial_outp(up, UART_FCR, 0);
+       (void)serial_in(up, UART_RX);
+       serial_outp(up, UART_IER, 0);
+
+ out:  
+       spin_unlock_irqrestore(&up->port.lock, flags);
+//     restore_flags(flags);
+#ifdef CONFIG_SERIAL_8250_RSA
+       if (up->port.iobase && up->port.type == PORT_RSA) {
+               release_region(up->port.iobase, 8);
+               request_region(up->port.iobase + UART_RSA_BASE, 16,
+                              "serial_rsa");
+       }
+#endif
+}
+
+static void autoconfig_irq(struct uart_8250_port *up)
+{
+       unsigned char save_mcr, save_ier;
+       unsigned char save_ICP = 0;
+       unsigned int ICP = 0;
+       unsigned long irqs;
+       int irq;
+
+       if (up->port.flags & ASYNC_FOURPORT) {
+               ICP = (up->port.iobase & 0xfe0) | 0x1f;
+               save_ICP = inb_p(ICP);
+               outb_p(0x80, ICP);
+               (void) inb_p(ICP);
+       }
+
+       /* forget possible initially masked and pending IRQ */
+       probe_irq_off(probe_irq_on());
+       save_mcr = serial_inp(up, UART_MCR);
+       save_ier = serial_inp(up, UART_IER);
+       serial_outp(up, UART_MCR, UART_MCR_OUT1 | UART_MCR_OUT2);
+       
+       irqs = probe_irq_on();
+       serial_outp(up, UART_MCR, 0);
+       udelay (10);
+       if (up->port.flags & ASYNC_FOURPORT)  {
+               serial_outp(up, UART_MCR,
+                           UART_MCR_DTR | UART_MCR_RTS);
+       } else {
+               serial_outp(up, UART_MCR,
+                           UART_MCR_DTR | UART_MCR_RTS | UART_MCR_OUT2);
+       }
+       serial_outp(up, UART_IER, 0x0f);        /* enable all intrs */
+       (void)serial_inp(up, UART_LSR);
+       (void)serial_inp(up, UART_RX);
+       (void)serial_inp(up, UART_IIR);
+       (void)serial_inp(up, UART_MSR);
+       serial_outp(up, UART_TX, 0xFF);
+       udelay (20);
+       irq = probe_irq_off(irqs);
+
+       serial_outp(up, UART_MCR, save_mcr);
+       serial_outp(up, UART_IER, save_ier);
+
+       if (up->port.flags & ASYNC_FOURPORT)
+               outb_p(save_ICP, ICP);
+
+       up->port.irq = (irq > 0) ? irq : 0;
+}
+
+static void __serial8250_stop_tx(struct uart_port *port, unsigned int tty_stop)
+{
+       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) {
+               up->acr |= UART_ACR_TXDIS;
+               serial_icr_write(up, UART_ACR, up->acr);
+       }
+}
+
+static void serial8250_stop_tx(struct uart_port *port, unsigned int tty_stop)
+{
+       struct uart_8250_port *up = (struct uart_8250_port *)port;
+       unsigned long flags;
+
+       spin_lock_irqsave(&up->port.lock, flags);
+       __serial8250_stop_tx(port, tty_stop);
+       spin_unlock_irqrestore(&up->port.lock, flags);
+}
+
+static void serial8250_start_tx(struct uart_port *port, unsigned int tty_start)
+{
+       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);
+       }
+       /*
+        * We only do this from uart_start
+        */
+       if (tty_start && up->port.type == PORT_16C950) {
+               up->acr &= ~UART_ACR_TXDIS;
+               serial_icr_write(up, UART_ACR, up->acr);
+       }
+}
+
+static void serial8250_stop_rx(struct uart_port *port)
+{
+       struct uart_8250_port *up = (struct uart_8250_port *)port;
+       unsigned long flags;
+
+       spin_lock_irqsave(&up->port.lock, flags);
+       up->ier &= ~UART_IER_RLSI;
+       up->port.read_status_mask &= ~UART_LSR_DR;
+       serial_out(up, UART_IER, up->ier);
+       spin_unlock_irqrestore(&up->port.lock, flags);
+}
+
+static void serial8250_enable_ms(struct uart_port *port)
+{
+       struct uart_8250_port *up = (struct uart_8250_port *)port;
+       unsigned long flags;
+
+       spin_lock_irqsave(&up->port.lock, flags);
+       up->ier |= UART_IER_MSI;
+       serial_out(up, UART_IER, up->ier);
+       spin_unlock_irqrestore(&up->port.lock, flags);
+}
+
+static _INLINE_ void
+receive_chars(struct uart_8250_port *up, int *status, struct pt_regs *regs)
+{
+       struct tty_struct *tty = up->port.info->tty;
+       unsigned char ch;
+       int max_count = 256;
+
+       do {
+               if (unlikely(tty->flip.count >= TTY_FLIPBUF_SIZE)) {
+                       tty->flip.tqueue.routine((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;
+               up->port.icount.rx++;
+
+               if (unlikely(*status & (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);
+                               up->port.icount.brk++;
+                               /*
+                                * We do the SysRQ and SAK checking
+                                * here because otherwise the break
+                                * may get masked by ignore_status_mask
+                                * or read_status_mask.
+                                */
+                               if (uart_handle_break(&up->port))
+                                       goto ignore_char;
+                       } else if (*status & UART_LSR_PE)
+                               up->port.icount.parity++;
+                       else if (*status & UART_LSR_FE)
+                               up->port.icount.frame++;
+                       if (*status & UART_LSR_OE)
+                               up->port.icount.overrun++;
+
+                       /*
+                        * Mask off conditions which should be ingored.
+                        */
+                       *status &= 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) {
+                               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;
+               }
+               if (uart_handle_sysrq_char(&up->port, ch, regs))
+                       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++;
+               }
+       ignore_char:
+               *status = serial_inp(up, UART_LSR);
+       } while ((*status & UART_LSR_DR) && (max_count-- > 0));
+       tty_flip_buffer_push(tty);
+}
+
+static _INLINE_ void transmit_chars(struct uart_8250_port *up)
+{
+       struct circ_buf *xmit = &up->port.info->xmit;
+       int count;
+
+       if (up->port.x_char) {
+               serial_outp(up, UART_TX, up->port.x_char);
+               up->port.icount.tx++;
+               up->port.x_char = 0;
+               return;
+       }
+       if (uart_circ_empty(xmit) || uart_tx_stopped(&up->port)) {
+               __serial8250_stop_tx(&up->port, 0);
+               return;
+       }
+
+       count = up->port.fifosize;
+       do {
+               serial_out(up, UART_TX, xmit->buf[xmit->tail]);
+               xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
+               up->port.icount.tx++;
+               if (uart_circ_empty(xmit))
+                       break;
+       } while (--count > 0);
+
+       if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
+               uart_event(&up->port, EVT_WRITE_WAKEUP);
+
+       DEBUG_INTR("THRE...");
+
+       if (uart_circ_empty(xmit))
+               __serial8250_stop_tx(&up->port, 0);
+}
+
+static _INLINE_ void 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);
+
+       wake_up_interruptible(&up->port.info->delta_msr_wait);
+}
+
+/*
+ * This handles the interrupt from one port.
+ */
+static inline void
+serial8250_handle_port(struct uart_8250_port *up, struct pt_regs *regs)
+{
+       unsigned int status = serial_inp(up, UART_LSR);
+
+       DEBUG_INTR("status = %x...", status);
+
+       if (status & UART_LSR_DR)
+               receive_chars(up, &status, regs);
+       check_modem_status(up);
+       if (status & UART_LSR_THRE)
+               transmit_chars(up);
+}
+
+/*
+ * This is the serial driver's interrupt routine.
+ *
+ * Arjan thinks the old way was overly complex, so it got simplified.
+ * Alan disagrees, saying that need the complexity to handle the weird
+ * nature of ISA shared interrupts.  (This is a special exception.)
+ *
+ * In order to handle ISA shared interrupts properly, we need to check
+ * that all ports have been serviced, and therefore the ISA interrupt
+ * line has been de-asserted.
+ *
+ * This means we need to loop through all ports. checking that they
+ * don't have an interrupt pending.
+ */
+static void serial8250_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+       struct irq_info *i = dev_id;
+       struct list_head *l, *end = NULL;
+       int pass_counter = 0;
+
+       DEBUG_INTR("serial8250_interrupt(%d)...", irq);
+
+       spin_lock(&i->lock);
+
+       l = i->head;
+       do {
+               struct uart_8250_port *up;
+               unsigned int iir;
+
+               up = list_entry(l, struct uart_8250_port, list);
+
+               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);
+
+                       end = NULL;
+               } else if (end == NULL)
+                       end = l;
+
+               l = l->next;
+
+               if (l == i->head && pass_counter++ > PASS_LIMIT) {
+                       /* If we hit this, we're dead. */
+                       printk(KERN_ERR "serial8250: too much work for "
+                               "irq%d\n", irq);
+                       break;
+               }
+       } while (l != end);
+
+       spin_unlock(&i->lock);
+
+       DEBUG_INTR("end.\n");
+}
+
+/*
+ * To support ISA shared interrupts, we need to have one interrupt
+ * handler that ensures that the IRQ line has been deasserted
+ * before returning.  Failing to do this will result in the IRQ
+ * line being stuck active, and, since ISA irqs are edge triggered,
+ * no more IRQs will be seen.
+ */
+static int serial_link_irq_chain(struct uart_8250_port *up)
+{
+       struct irq_info *i = irq_lists + up->port.irq;
+       int ret, irq_flags = share_irqs ? SA_SHIRQ : 0;
+
+       spin_lock_irq(&i->lock);
+
+       if (i->head) {
+               list_add(&up->list, i->head);
+               spin_unlock_irq(&i->lock);
+
+               ret = 0;
+       } else {
+               INIT_LIST_HEAD(&up->list);
+               i->head = &up->list;
+               spin_unlock_irq(&i->lock);
+
+               ret = request_irq(up->port.irq, serial8250_interrupt,
+                                 irq_flags, "serial", i);
+       }
+
+       return ret;
+}
+
+static void serial_unlink_irq_chain(struct uart_8250_port *up)
+{
+       struct irq_info *i = irq_lists + up->port.irq;
+
+       BUG_ON(i->head == NULL);
+
+       if (list_empty(i->head))
+               free_irq(up->port.irq, i);
+
+       spin_lock_irq(&i->lock);
+
+       if (!list_empty(i->head)) {
+               if (i->head == &up->list)
+                       i->head = i->head->next;
+               list_del(&up->list);
+       } else {
+               BUG_ON(i->head != &up->list);
+               i->head = NULL;
+       }
+
+       spin_unlock_irq(&i->lock);
+}
+
+/*
+ * This function is used to handle ports that do not have an
+ * interrupt.  This doesn't work very well for 16450's, but gives
+ * barely passable results for a 16550A.  (Although at the expense
+ * of much CPU overhead).
+ */
+static void serial8250_timeout(unsigned long data)
+{
+       struct uart_8250_port *up = (struct uart_8250_port *)data;
+       unsigned int timeout;
+       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);
+       }
+
+       timeout = up->port.timeout;
+       timeout = timeout > 6 ? (timeout / 2 - 2) : 1;
+       mod_timer(&up->timer, jiffies + timeout);
+}
+
+static unsigned int serial8250_tx_empty(struct uart_port *port)
+{
+       struct uart_8250_port *up = (struct uart_8250_port *)port;
+       unsigned long flags;
+       unsigned int ret;
+
+       spin_lock_irqsave(&up->port.lock, flags);
+       ret = serial_in(up, UART_LSR) & UART_LSR_TEMT ? TIOCSER_TEMT : 0;
+       spin_unlock_irqrestore(&up->port.lock, flags);
+
+       return ret;
+}
+
+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 ret;
+
+       spin_lock_irqsave(&up->port.lock, flags);
+       status = serial_in(up, UART_MSR);
+       spin_unlock_irqrestore(&up->port.lock, flags);
+
+       ret = 0;
+       if (status & UART_MSR_DCD)
+               ret |= TIOCM_CAR;
+       if (status & UART_MSR_RI)
+               ret |= TIOCM_RNG;
+       if (status & UART_MSR_DSR)
+               ret |= TIOCM_DSR;
+       if (status & UART_MSR_CTS)
+               ret |= TIOCM_CTS;
+       return ret;
+}
+
+static void serial8250_set_mctrl(struct uart_port *port, unsigned int mctrl)
+{
+       struct uart_8250_port *up = (struct uart_8250_port *)port;
+       unsigned char mcr = ALPHA_KLUDGE_MCR;
+
+       if (mctrl & TIOCM_RTS)
+               mcr |= UART_MCR_RTS;
+       if (mctrl & TIOCM_DTR)
+               mcr |= UART_MCR_DTR;
+       if (mctrl & TIOCM_OUT1)
+               mcr |= UART_MCR_OUT1;
+       if (mctrl & TIOCM_OUT2)
+               mcr |= UART_MCR_OUT2;
+       if (mctrl & TIOCM_LOOP)
+               mcr |= UART_MCR_LOOP;
+
+       serial_out(up, UART_MCR, mcr);
+}
+
+static void serial8250_break_ctl(struct uart_port *port, int break_state)
+{
+       struct uart_8250_port *up = (struct uart_8250_port *)port;
+       unsigned long flags;
+
+       spin_lock_irqsave(&up->port.lock, flags);
+       if (break_state == -1)
+               up->lcr |= UART_LCR_SBC;
+       else
+               up->lcr &= ~UART_LCR_SBC;
+       serial_out(up, UART_LCR, up->lcr);
+       spin_unlock_irqrestore(&up->port.lock, flags);
+}
+
+static int serial8250_startup(struct uart_port *port)
+{
+       struct uart_8250_port *up = (struct uart_8250_port *)port;
+       unsigned long flags;
+       int retval;
+
+       if (up->port.type == PORT_16C950) {
+               /* Wake up and initialize UART */
+               up->acr = 0;
+               serial_outp(up, UART_LCR, 0xBF);
+               serial_outp(up, UART_EFR, UART_EFR_ECB);
+               serial_outp(up, UART_IER, 0);
+               serial_outp(up, UART_LCR, 0);
+               serial_icr_write(up, UART_CSR, 0); /* Reset the UART */
+               serial_outp(up, UART_LCR, 0xBF);
+               serial_outp(up, UART_EFR, UART_EFR_ECB);
+               serial_outp(up, UART_LCR, 0);
+       }
+
+#ifdef CONFIG_SERIAL_8250_RSA
+       /*
+        * If this is an RSA port, see if we can kick it up to the
+        * higher speed clock.
+        */
+       enable_rsa(up);
+#endif
+
+       /*
+        * Clear the FIFO buffers and disable them.
+        * (they will be reeanbled in change_speed())
+        */
+       if (uart_config[up->port.type].flags & UART_CLEAR_FIFO) {
+               serial_outp(up, UART_FCR, UART_FCR_ENABLE_FIFO);
+               serial_outp(up, UART_FCR, UART_FCR_ENABLE_FIFO |
+                               UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT);
+               serial_outp(up, UART_FCR, 0);
+       }
+
+       /*
+        * Clear the interrupt registers.
+        */
+       (void) serial_inp(up, UART_LSR);
+       (void) serial_inp(up, UART_RX);
+       (void) serial_inp(up, UART_IIR);
+       (void) serial_inp(up, UART_MSR);
+
+       /*
+        * At this point, there's no way the LSR could still be 0xff;
+        * if it is, then bail out, because there's likely no UART
+        * here.
+        */
+       if (!(up->port.flags & ASYNC_BUGGY_UART) &&
+           (serial_inp(up, UART_LSR) == 0xff)) {
+               printk("ttyS%d: LSR safety check engaged!\n", up->port.line);
+               return -ENODEV;
+       }
+
+       /*
+        * If the "interrupt" for this port doesn't correspond with any
+        * hardware interrupt, we use a timer-based system.  The original
+        * driver used to do this with IRQ0.
+        */
+       if (!is_real_interrupt(up->port.irq)) {
+               unsigned int timeout = up->port.timeout;
+
+               timeout = timeout > 6 ? (timeout / 2 - 2) : 1;
+
+               up->timer.data = (unsigned long)up;
+               mod_timer(&up->timer, jiffies + timeout);
+       } else {
+               retval = serial_link_irq_chain(up);
+               if (retval)
+                       return retval;
+       }
+
+       /*
+        * Now, initialize the UART
+        */
+       serial_outp(up, UART_LCR, UART_LCR_WLEN8);
+
+       spin_lock_irqsave(&up->port.lock, flags);
+       if (up->port.flags & ASYNC_FOURPORT) {
+               if (!is_real_interrupt(up->port.irq))
+                       up->port.mctrl |= TIOCM_OUT1;
+       } else
+               /*
+                * Most PC uarts need OUT2 raised to enable interrupts.
+                */
+               if (is_real_interrupt(up->port.irq))
+                       up->port.mctrl |= TIOCM_OUT2;
+
+       serial8250_set_mctrl(&up->port, up->port.mctrl);
+       spin_unlock_irqrestore(&up->port.lock, flags);
+
+       /*
+        * Finally, enable interrupts.  Note: Modem status interrupts
+        * are set via change_speed(), which will be occuring imminently
+        * anyway, so we don't enable them here.
+        */
+       up->ier = UART_IER_RLSI | UART_IER_RDI;
+       serial_outp(up, UART_IER, up->ier);
+
+       if (up->port.flags & ASYNC_FOURPORT) {
+               unsigned int icp;
+               /*
+                * Enable interrupts on the AST Fourport board
+                */
+               icp = (up->port.iobase & 0xfe0) | 0x01f;
+               outb_p(0x80, icp);
+               (void) inb_p(icp);
+       }
+
+       /*
+        * And clear the interrupt registers again for luck.
+        */
+       (void) serial_inp(up, UART_LSR);
+       (void) serial_inp(up, UART_RX);
+       (void) serial_inp(up, UART_IIR);
+       (void) serial_inp(up, UART_MSR);
+
+       return 0;
+}
+
+static void serial8250_shutdown(struct uart_port *port)
+{
+       struct uart_8250_port *up = (struct uart_8250_port *)port;
+       unsigned long flags;
+
+       /*
+        * Disable interrupts from this port
+        */
+       up->ier = 0;
+       serial_outp(up, UART_IER, 0);
+
+       spin_lock_irqsave(&up->port.lock, flags);
+       if (up->port.flags & ASYNC_FOURPORT) {
+               /* reset interrupts on the AST Fourport board */
+               inb((up->port.iobase & 0xfe0) | 0x1f);
+               up->port.mctrl |= TIOCM_OUT1;
+       } else
+               up->port.mctrl &= ~TIOCM_OUT2;
+
+       serial8250_set_mctrl(&up->port, up->port.mctrl);
+       spin_unlock_irqrestore(&up->port.lock, flags);
+
+       /*
+        * Disable break condition and FIFOs
+        */
+       serial_out(up, UART_LCR, serial_inp(up, UART_LCR) & ~UART_LCR_SBC);
+       serial_outp(up, UART_FCR, UART_FCR_ENABLE_FIFO |
+                                 UART_FCR_CLEAR_RCVR |
+                                 UART_FCR_CLEAR_XMIT);
+       serial_outp(up, UART_FCR, 0);
+
+#ifdef CONFIG_SERIAL_8250_RSA
+       /*
+        * Reset the RSA board back to 115kbps compat mode.
+        */
+       disable_rsa(up);
+#endif
+
+       /*
+        * Read data port to reset things, and then unlink from
+        * the IRQ chain.
+        */
+       (void) serial_in(up, UART_RX);
+
+       if (!is_real_interrupt(up->port.irq))
+               del_timer_sync(&up->timer);
+       else
+               serial_unlink_irq_chain(up);
+}
+
+static void
+serial8250_change_speed(struct uart_port *port, unsigned int cflag,
+                       unsigned int iflag, unsigned int quot)
+{
+       struct uart_8250_port *up = (struct uart_8250_port *)port;
+       unsigned char cval, fcr = 0;
+       unsigned long flags;
+
+       printk("+++ change_speed port %p cflag %08x quot %d\n", port, cflag, quot);
+
+       switch (cflag & CSIZE) {
+       case CS5:
+               cval = 0x00;
+               break;
+       case CS6:
+               cval = 0x01;
+               break;
+       case CS7:
+               cval = 0x02;
+               break;
+       default:
+       case CS8:
+               cval = 0x03;
+               break;
+       }
+
+       if (cflag & CSTOPB)
+               cval |= 0x04;
+       if (cflag & PARENB)
+               cval |= UART_LCR_PARITY;
+       if (!(cflag & PARODD))
+               cval |= UART_LCR_EPAR;
+#ifdef CMSPAR
+       if (cflag & CMSPAR)
+               cval |= UART_LCR_SPAR;
+#endif
+
+       /*
+        * Work around a bug in the Oxford Semiconductor 952 rev B
+        * chip which causes it to seriously miscalculate baud rates
+        * when DLL is 0.
+        */
+       if ((quot & 0xff) == 0 && up->port.type == PORT_16C950 &&
+           up->rev == 0x5201)
+               quot ++;
+
+       if (uart_config[up->port.type].flags & UART_USE_FIFO) {
+               if ((up->port.uartclk / quot) < (2400 * 16))
+                       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;
+       }
+       if (up->port.type == PORT_16750)
+               fcr |= UART_FCR7_64BYTE;
+
+       up->port.read_status_mask = UART_LSR_OE | UART_LSR_THRE | UART_LSR_DR;
+       if (iflag & IGNPAR)
+               up->port.read_status_mask |= UART_LSR_FE | UART_LSR_PE;
+       if (iflag & (BRKINT | PARMRK))
+               up->port.read_status_mask |= UART_LSR_BI;
+
+       /*
+        * Characteres to ignore
+        */
+       up->port.ignore_status_mask = 0;
+       if (iflag & IGNPAR)
+               up->port.ignore_status_mask |= UART_LSR_PE | UART_LSR_FE;
+       if (iflag & IGNBRK) {
+               up->port.ignore_status_mask |= UART_LSR_BI;
+               /*
+                * If we're ignoring parity and break indicators,
+                * ignore overruns too (for real raw support).
+                */
+               if (iflag & IGNPAR)
+                       up->port.ignore_status_mask |= UART_LSR_OE;
+       }
+
+       /*
+        * ignore all characters if CREAD is not set
+        */
+       if ((cflag & CREAD) == 0)
+               up->port.ignore_status_mask |= UART_LSR_DR;
+
+       /*
+        * CTS flow control flag and modem status interrupts
+        */
+       up->ier &= ~UART_IER_MSI;
+       if (UART_ENABLE_MS(&up->port, cflag))
+               up->ier |= UART_IER_MSI;
+
+       /*
+        * Ok, we're now changing the port state.  Do it with
+        * interrupts disabled.
+        */
+       spin_lock_irqsave(&up->port.lock, flags);
+       serial_out(up, UART_IER, up->ier);
+
+       if (uart_config[up->port.type].flags & UART_STARTECH) {
+               serial_outp(up, UART_LCR, 0xBF);
+               serial_outp(up, UART_EFR, cflag & CRTSCTS ? UART_EFR_CTS :0);
+       }
+       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 */
+       if (up->port.type == PORT_16750)
+               serial_outp(up, UART_FCR, fcr);         /* set fcr */
+       serial_outp(up, UART_LCR, cval);                /* reset DLAB */
+       up->lcr = cval;                                 /* Save LCR */
+       if (up->port.type != PORT_16750) {
+               if (fcr & UART_FCR_ENABLE_FIFO) {
+                       /* emulated UARTs (Lucent Venus 167x) need two steps */
+                       serial_outp(up, UART_FCR, UART_FCR_ENABLE_FIFO);
+               }
+               serial_outp(up, UART_FCR, fcr);         /* set fcr */
+       }
+       spin_unlock_irqrestore(&up->port.lock, flags);
+}
+
+static void
+serial8250_pm(struct uart_port *port, unsigned int state,
+             unsigned int oldstate)
+{
+       struct uart_8250_port *up = (struct uart_8250_port *)port;
+       if (state) {
+               /* sleep */
+               if (uart_config[up->port.type].flags & UART_STARTECH) {
+                       /* Arrange to enter sleep mode */
+                       serial_outp(up, UART_LCR, 0xBF);
+                       serial_outp(up, UART_EFR, UART_EFR_ECB);
+                       serial_outp(up, UART_LCR, 0);
+                       serial_outp(up, UART_IER, UART_IERX_SLEEP);
+                       serial_outp(up, UART_LCR, 0xBF);
+                       serial_outp(up, UART_EFR, 0);
+                       serial_outp(up, UART_LCR, 0);
+               }
+               if (up->port.type == PORT_16750) {
+                       /* Arrange to enter sleep mode */
+                       serial_outp(up, UART_IER, UART_IERX_SLEEP);
+               }
+
+               if (up->pm)
+                       up->pm(port, state, oldstate);
+       } else {
+               /* wake */
+               if (uart_config[up->port.type].flags & UART_STARTECH) {
+                       /* Wake up UART */
+                       serial_outp(up, UART_LCR, 0xBF);
+                       serial_outp(up, UART_EFR, UART_EFR_ECB);
+                       /*
+                        * Turn off LCR == 0xBF so we actually set the IER
+                        * register on the XR16C850
+                        */
+                       serial_outp(up, UART_LCR, 0);
+                       serial_outp(up, UART_IER, 0);
+                       /*
+                        * Now reset LCR so we can turn off the ECB bit
+                        */
+                       serial_outp(up, UART_LCR, 0xBF);
+                       serial_outp(up, UART_EFR, 0);
+                       /*
+                        * For a XR16C850, we need to set the trigger levels
+                        */
+                       if (up->port.type == PORT_16850) {
+                               unsigned char fctr;
+
+                               fctr = serial_inp(up, UART_FCTR) &
+                                        ~(UART_FCTR_RX | UART_FCTR_TX);
+                               serial_outp(up, UART_FCTR, fctr |
+                                               UART_FCTR_TRGD |
+                                               UART_FCTR_RX);
+                               serial_outp(up, UART_TRG, UART_TRG_96);
+                               serial_outp(up, UART_FCTR, fctr |
+                                               UART_FCTR_TRGD |
+                                               UART_FCTR_TX);
+                               serial_outp(up, UART_TRG, UART_TRG_96);
+                       }
+                       serial_outp(up, UART_LCR, 0);
+               }
+
+               if (up->port.type == PORT_16750) {
+                       /* Wake up UART */
+                       serial_outp(up, UART_IER, 0);
+               }
+
+               if (up->pm)
+                       up->pm(port, state, oldstate);
+       }
+}
+
+/*
+ * 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?
+ */
+static int
+serial8250_request_std_resource(struct uart_8250_port *up, struct resource **res)
+{
+       unsigned int size = 8 << up->port.regshift;
+       int ret = 0;
+
+       switch (up->port.iotype) {
+       case SERIAL_IO_MEM:
+               if (up->port.mapbase) {
+                       *res = request_mem_region(up->port.mapbase, size, "serial");
+                       if (!*res)
+                               ret = -EBUSY;
+               }
+               break;
+
+       case SERIAL_IO_HUB6:
+       case SERIAL_IO_PORT:
+               *res = request_region(up->port.iobase, size, "serial");
+               if (!*res)
+                       ret = -EBUSY;
+               break;
+       }
+       return ret;
+}
+
+static int
+serial8250_request_rsa_resource(struct uart_8250_port *up, struct resource **res)
+{
+       unsigned int size = 8 << up->port.regshift;
+       unsigned long start;
+       int ret = 0;
+
+       switch (up->port.iotype) {
+       case SERIAL_IO_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;
+               }
+               break;
+
+       case SERIAL_IO_HUB6:
+       case SERIAL_IO_PORT:
+               start = up->port.iobase;
+               start += UART_RSA_BASE << up->port.regshift;
+               *res = request_region(start, size, "serial-rsa");
+               if (!*res)
+                       ret = -EBUSY;
+               break;
+       }
+
+       return ret;
+}
+
+static void serial8250_release_port(struct uart_port *port)
+{
+       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;
+
+       switch (up->port.iotype) {
+       case SERIAL_IO_MEM:
+               if (up->port.mapbase) {
+                       /*
+                        * Unmap the area.
+                        */
+                       iounmap(up->port.membase);
+                       up->port.membase = NULL;
+
+                       start = up->port.mapbase;
+
+                       if (size)
+                               release_mem_region(start + offset, size);
+                       release_mem_region(start, 8 << up->port.regshift);
+               }
+               break;
+
+       case SERIAL_IO_HUB6:
+       case SERIAL_IO_PORT:
+               start = up->port.iobase;
+
+               if (size)
+                       release_region(start + offset, size);
+               release_region(start + offset, 8 << up->port.regshift);
+               break;
+
+       default:
+               break;
+       }
+}
+
+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 = -EBUSY;
+
+       if (up->port.type == PORT_RSA) {
+               ret = serial8250_request_rsa_resource(up, &res_rsa);
+               if (ret)
+                       return ret;
+       }
+
+       ret = serial8250_request_std_resource(up, &res);
+
+       /*
+        * If we have a mapbase, then request that as well.
+        */
+       if (res != NULL && up->port.iotype == SERIAL_IO_MEM &&
+           up->port.mapbase) {
+               int size = res->end - res->start + 1;
+
+               up->port.membase = ioremap(up->port.mapbase, size);
+               if (!up->port.membase)
+                       ret = -ENOMEM;
+       }
+
+       if (ret) {
+               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 & ASYNC_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);
+       if (ret)
+               return;
+
+       ret = serial8250_request_rsa_resource(up, &res_rsa);
+       if (ret)
+               probeflags &= ~PROBE_RSA;
+
+       if (flags & UART_CONFIG_TYPE)
+               autoconfig(up, probeflags);
+       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)
+               release_resource(res_std);
+}
+
+static int
+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 == PORT_STARTECH)
+               return -EINVAL;
+       return 0;
+}
+
+static const char *
+serial8250_type(struct uart_port *port)
+{
+       int type = port->type;
+
+       if (type >= ARRAY_SIZE(uart_config))
+               type = 0;
+       return uart_config[type].name;
+}
+
+static struct uart_ops serial8250_pops = {
+       tx_empty:       serial8250_tx_empty,
+       set_mctrl:      serial8250_set_mctrl,
+       get_mctrl:      serial8250_get_mctrl,
+       stop_tx:        serial8250_stop_tx,
+       start_tx:       serial8250_start_tx,
+       stop_rx:        serial8250_stop_rx,
+       enable_ms:      serial8250_enable_ms,
+       break_ctl:      serial8250_break_ctl,
+       startup:        serial8250_startup,
+       shutdown:       serial8250_shutdown,
+       change_speed:   serial8250_change_speed,
+       pm:             serial8250_pm,
+       type:           serial8250_type,
+       release_port:   serial8250_release_port,
+       request_port:   serial8250_request_port,
+       config_port:    serial8250_config_port,
+       verify_port:    serial8250_verify_port,
+};
+
+static struct uart_8250_port serial8250_ports[UART_NR];
+
+static void __init serial8250_isa_init_ports(void)
+{
+       static int first = 1;
+       int i;
+
+       if (!first)
+               return;
+       first = 0;
+
+       for (i = 0; i < ARRAY_SIZE(old_serial_port); i++) {
+               serial8250_ports[i].port.iobase  = old_serial_port[i].port;
+               serial8250_ports[i].port.irq     = irq_cannonicalize(old_serial_port[i].irq);
+               serial8250_ports[i].port.uartclk = old_serial_port[i].base_baud * 16;
+               serial8250_ports[i].port.flags   = old_serial_port[i].flags;
+               serial8250_ports[i].port.ops     = &serial8250_pops;
+       }
+}
+
+static void __init serial8250_register_ports(struct uart_driver *drv)
+{
+       int i;
+
+       serial8250_isa_init_ports();
+
+       for (i = 0; i < UART_NR; i++) {
+               serial8250_ports[i].port.line = i;
+               serial8250_ports[i].port.ops = &serial8250_pops;
+               init_timer(&serial8250_ports[i].timer);
+               serial8250_ports[i].timer.function = serial8250_timeout;
+               uart_add_one_port(drv, &serial8250_ports[i].port);
+       }
+}
+
+#ifdef CONFIG_SERIAL_8250_CONSOLE
+
+#define BOTH_EMPTY (UART_LSR_TEMT | UART_LSR_THRE)
+
+/*
+ *     Wait for transmitter & holding register to empty
+ */
+static inline void wait_for_xmitr(struct uart_8250_port *up)
+{
+       unsigned int status, tmout = 10000;
+
+       /* Wait up to 10ms for the character(s) to be sent. */
+       do {
+               status = serial_in(up, UART_LSR);
+
+               if (status & UART_LSR_BI)
+                       up->lsr_break_flag = UART_LSR_BI;
+
+               if (--tmout == 0)
+                       break;
+               udelay(1);
+       } while ((status & BOTH_EMPTY) != BOTH_EMPTY);
+
+       /* Wait up to 1s for flow control if necessary */
+       if (up->port.flags & ASYNC_CONS_FLOW) {
+               tmout = 1000000;
+               while (--tmout &&
+                      ((serial_in(up, UART_MSR) & UART_MSR_CTS) == 0))
+                       udelay(1);
+       }
+}
+
+/*
+ *     Print a string to the serial port trying not to disturb
+ *     any possible real use of the port...
+ *
+ *     The console_lock must be held when we get here.
+ */
+static void
+serial8250_console_write(struct console *co, const char *s, unsigned int count)
+{
+       struct uart_8250_port *up = &serial8250_ports[co->index];
+       unsigned int ier;
+       int i;
+
+       /*
+        *      First save the UER then disable the interrupts
+        */
+       ier = serial_in(up, UART_IER);
+       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);
+               }
+       }
+
+       /*
+        *      Finally, wait for transmitter to become empty
+        *      and restore the IER
+        */
+       wait_for_xmitr(up);
+       serial_out(up, UART_IER, ier);
+}
+
+static kdev_t serial8250_console_device(struct console *co)
+{
+       return mk_kdev(TTY_MAJOR, 64 + co->index);
+}
+
+static int __init serial8250_console_setup(struct console *co, char *options)
+{
+       struct uart_port *port;
+       int baud = 9600;
+       int bits = 8;
+       int parity = 'n';
+       int flow = 'n';
+
+       /*
+        * Check whether an invalid uart number has been specified, and
+        * if so, search for the first available port that does have
+        * console support.
+        */
+       if (co->index >= UART_NR)
+               co->index = 0;
+       port = &serial8250_ports[co->index].port;
+       printk("+++ index %d port %p iobase %x\n", co->index, port, port->iobase);
+
+       /*
+        * Temporary fix.
+        */
+       spin_lock_init(&port->lock);
+
+       if (options)
+               uart_parse_options(options, &baud, &parity, &bits, &flow);
+       printk("+++ baud %d bits %d parity %c flow %c\n", baud, parity, bits, flow);
+
+       return uart_set_options(port, co, baud, parity, bits, flow);
+}
+
+static struct console serial8250_console = {
+       name:           "ttyS",
+       write:          serial8250_console_write,
+       device:         serial8250_console_device,
+       setup:          serial8250_console_setup,
+       flags:          CON_PRINTBUFFER,
+       index:          -1,
+};
+
+void __init serial8250_console_init(void)
+{
+       serial8250_isa_init_ports();
+       register_console(&serial8250_console);
+}
+
+#define SERIAL8250_CONSOLE     &serial8250_console
+#else
+#define SERIAL8250_CONSOLE     NULL
+#endif
+
+static struct uart_driver serial8250_reg = {
+       owner:                  THIS_MODULE,
+       driver_name:            "serial",
+#ifdef CONFIG_DEVFS_FS
+       dev_name:               "tts/%d",
+#else
+       dev_name:               "ttyS",
+#endif
+       major:                  TTY_MAJOR,
+       minor:                  64,
+       nr:                     UART_NR,
+       cons:                   SERIAL8250_CONSOLE,
+};
+
+/*
+ * 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 | ASYNC_BOOT_AUTOCONF;
+       port.line     = line;
+
+       if (HIGH_BITS_OFFSET)
+               port.iobase |= 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.
+ *
+ *     The port is then probed and if neccessary 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.
+ */
+int register_serial(struct serial_struct *req)
+{
+       return __register_serial(req, -1);
+}
+
+int __init early_serial_setup(struct serial_struct *req)
+{
+       __register_serial(req, req->line);
+       return 0;
+}
+
+/**
+ *     unregister_serial - remove a 16x50 serial port at runtime
+ *     @line: serial line number
+ *
+ *     Remove one serial port.  This may be called from interrupt
+ *     context.
+ */
+void unregister_serial(int line)
+{
+       uart_unregister_port(&serial8250_reg, line);
+}
+
+/*
+ * This is for ISAPNP only.
+ */
+void serial8250_get_irq_map(unsigned int *map)
+{
+       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;
+       }
+}
+
+static int __init serial8250_init(void)
+{
+       int ret, i;
+
+       printk(KERN_INFO "Serial: 8250/16550 driver $Revision: 1.80 $ "
+               "IRQ sharing %sabled\n", 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)
+               return ret;
+
+       serial8250_register_ports(&serial8250_reg);
+       return 0;
+}
+
+static void __exit serial8250_exit(void)
+{
+       int i;
+
+       for (i = 0; i < UART_NR; i++)
+               uart_remove_one_port(&serial8250_reg, &serial8250_ports[i].port);
+
+       uart_unregister_driver(&serial8250_reg);
+}
+
+module_init(serial8250_init);
+module_exit(serial8250_exit);
+
+EXPORT_SYMBOL(register_serial);
+EXPORT_SYMBOL(unregister_serial);
+EXPORT_SYMBOL(serial8250_get_irq_map);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Generic 8250/16x50 serial driver $Revision: 1.80 $");
+
+MODULE_PARM(share_irqs, "i");
+MODULE_PARM_DESC(share_irqs, "Share IRQs with other non-8250/16x50 devices"
+       " (unsafe)");
+
+#if defined(CONFIG_SERIAL_8250_RSA) && defined(MODULE)
+MODULE_PARM(probe_rsa, "1-" __MODULE_STRING(PORT_RSA_MAX) "i");
+MODULE_PARM_DESC(probe_rsa, "Probe I/O ports for RSA");
+MODULE_PARM(force_rsa, "1-" __MODULE_STRING(PORT_RSA_MAX) "i");
+MODULE_PARM_DESC(force_rsa, "Force I/O ports for RSA");
+#endif
diff --git a/drivers/serial/serial_8250.h b/drivers/serial/serial_8250.h
new file mode 100644 (file)
index 0000000..1002d1d
--- /dev/null
@@ -0,0 +1,60 @@
+/*
+ *  linux/drivers/char/serial_8250.h
+ *
+ *  Driver for 8250/16550-type serial ports
+ *
+ *  Based on drivers/char/serial.c, by Linus Torvalds, Theodore Ts'o.
+ *
+ *  Copyright (C) 2001 Russell King.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ *  $Id: serial_8250.h,v 1.7 2002/07/06 16:24:46 rmk Exp $
+ */
+
+#include <linux/config.h>
+
+struct serial8250_probe {
+       struct module   *owner;
+       int             (*pci_init_one)(struct pci_dev *dev);
+       void            (*pci_remove_one)(struct pci_dev *dev);
+       void            (*pnp_init)(void);
+};
+
+int serial8250_register_probe(struct serial8250_probe *probe);
+void serial8250_unregister_probe(struct serial8250_probe *probe);
+void serial8250_get_irq_map(unsigned int *map);
+
+struct old_serial_port {
+       unsigned int uart;
+       unsigned int base_baud;
+       unsigned int port;
+       unsigned int irq;
+       unsigned int flags;
+};
+
+#undef SERIAL_DEBUG_PCI
+
+#if defined(__i386__) && (defined(CONFIG_M386) || defined(CONFIG_M486))
+#define SERIAL_INLINE
+#endif
+  
+#ifdef SERIAL_INLINE
+#define _INLINE_ inline
+#else
+#define _INLINE_
+#endif
+
+#define PROBE_RSA      (1 << 0)
+#define PROBE_ANY      (~0)
+
+#define HIGH_BITS_OFFSET ((sizeof(long)-sizeof(int))*8)
+
+#ifdef CONFIG_SERIAL_8250_SHARE_IRQ
+#define SERIAL8250_SHARE_IRQS 1
+#else
+#define SERIAL8250_SHARE_IRQS 0
+#endif
diff --git a/drivers/serial/serial_8250_cs.c b/drivers/serial/serial_8250_cs.c
new file mode 100644 (file)
index 0000000..2166123
--- /dev/null
@@ -0,0 +1,719 @@
+/*======================================================================
+
+    A driver for PCMCIA serial devices
+
+    serial_cs.c 1.123 2000/08/24 18:46:38
+
+    The contents of this file are subject to the Mozilla Public
+    License Version 1.1 (the "License"); you may not use this file
+    except in compliance with the License. You may obtain a copy of
+    the License at http://www.mozilla.org/MPL/
+
+    Software distributed under the License is distributed on an "AS
+    IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+    implied. See the License for the specific language governing
+    rights and limitations under the License.
+
+    The initial developer of the original code is David A. Hinds
+    <dahinds@users.sourceforge.net>.  Portions created by David A. Hinds
+    are Copyright (C) 1999 David A. Hinds.  All Rights Reserved.
+
+    Alternatively, the contents of this file may be used under the
+    terms of the GNU General Public License version 2 (the "GPL"), in which
+    case the provisions of the GPL are applicable instead of the
+    above.  If you wish to allow the use of your version of this file
+    only under the terms of the GPL and not to allow others to use
+    your version of this file under the MPL, indicate your decision
+    by deleting the provisions above and replace them with the notice
+    and other provisions required by the GPL.  If you do not delete
+    the provisions above, a recipient may use your version of this
+    file under either the MPL or the GPL.
+    
+======================================================================*/
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/sched.h>
+#include <linux/ptrace.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/timer.h>
+#include <linux/tty.h>
+#include <linux/serial.h>
+#include <linux/major.h>
+#include <linux/tqueue.h>
+#include <asm/io.h>
+#include <asm/system.h>
+
+#include <pcmcia/version.h>
+#include <pcmcia/cs_types.h>
+#include <pcmcia/cs.h>
+#include <pcmcia/cistpl.h>
+#include <pcmcia/ciscode.h>
+#include <pcmcia/ds.h>
+#include <pcmcia/cisreg.h>
+
+#ifdef PCMCIA_DEBUG
+static int pc_debug = PCMCIA_DEBUG;
+MODULE_PARM(pc_debug, "i");
+#define DEBUG(n, args...) if (pc_debug>(n)) printk(KERN_DEBUG args)
+static char *version = "serial_cs.c 1.123 2000/08/24 18:46:38 (David Hinds)";
+#else
+#define DEBUG(n, args...)
+#endif
+
+/*====================================================================*/
+
+/* Parameters that can be set with 'insmod' */
+
+/* Bit map of interrupts to choose from */
+static u_int irq_mask = 0xdeb8;
+static int irq_list[4] = { -1 };
+
+/* Enable the speaker? */
+static int do_sound = 1;
+
+MODULE_PARM(irq_mask, "i");
+MODULE_PARM(irq_list, "1-4i");
+MODULE_PARM(do_sound, "i");
+
+/*====================================================================*/
+
+/* Table of multi-port card ID's */
+
+struct multi_id {
+       u_short manfid;
+       u_short prodid;
+       int multi;              /* 1 = multifunction, > 1 = # ports */
+};
+
+static struct multi_id multi_id[] = {
+       { MANFID_OMEGA,   PRODID_OMEGA_QSP_100,         4 },
+       { MANFID_QUATECH, PRODID_QUATECH_DUAL_RS232,    2 },
+       { MANFID_QUATECH, PRODID_QUATECH_DUAL_RS232_D1, 2 },
+       { MANFID_QUATECH, PRODID_QUATECH_QUAD_RS232,    4 },
+       { MANFID_SOCKET,  PRODID_SOCKET_DUAL_RS232,     2 },
+       { MANFID_INTEL,   PRODID_INTEL_DUAL_RS232,      2 },
+       { MANFID_NATINST, PRODID_NATINST_QUAD_RS232,    4 }
+};
+#define MULTI_COUNT (sizeof(multi_id)/sizeof(struct multi_id))
+
+typedef struct serial_info {
+       dev_link_t              link;
+       int                     ndev;
+       int                     multi;
+       int                     slave;
+       int                     manfid;
+       dev_node_t              node[4];
+       int                     line[4];
+       struct tq_struct        remove;
+} serial_info_t;
+
+static void serial_config(dev_link_t * link);
+static int serial_event(event_t event, int priority,
+                       event_callback_args_t * args);
+
+static dev_info_t dev_info = "serial_cs";
+
+static dev_link_t *serial_attach(void);
+static void serial_detach(dev_link_t *);
+
+static dev_link_t *dev_list = NULL;
+
+/*====================================================================*/
+
+static void
+cs_error(client_handle_t handle, int func, int ret)
+{
+       error_info_t err = { func, ret };
+       CardServices(ReportError, handle, &err);
+}
+
+/*======================================================================
+
+    After a card is removed, do_serial_release() will unregister
+    the serial device(s), and release the PCMCIA configuration.
+    
+======================================================================*/
+
+/*
+ * This always runs in process context.
+ */
+static void do_serial_release(void *arg)
+{
+       struct serial_info *info = arg;
+       int i;
+
+       DEBUG(0, "serial_release(0x%p)\n", &info->link);
+
+       /*
+        * Recheck to see if the device is still configured.
+        */
+       if (info->link.state & DEV_CONFIG) {
+               for (i = 0; i < info->ndev; i++)
+                       unregister_serial(info->line[i]);
+
+               info->link.dev = NULL;
+
+               if (!info->slave) {
+                       CardServices(ReleaseConfiguration, info->link.handle);
+                       CardServices(ReleaseIO, info->link.handle, &info->link.io);
+                       CardServices(ReleaseIRQ, info->link.handle, &info->link.irq);
+               }
+
+               info->link.state &= ~DEV_CONFIG;
+       }
+}
+
+/*
+ * This may be called from IRQ context.
+ */
+static void serial_remove(dev_link_t *link)
+{
+       struct serial_info *info = link->priv;
+
+       link->state &= ~DEV_PRESENT;
+
+       /*
+        * FIXME: Since the card has probably been removed,
+        * we should call into the serial layer and hang up
+        * the ports on the card immediately.
+        */
+
+       if (link->state & DEV_CONFIG)
+               schedule_task(&info->remove);
+}
+
+/*======================================================================
+
+    serial_attach() creates an "instance" of the driver, allocating
+    local data structures for one device.  The device is registered
+    with Card Services.
+
+======================================================================*/
+
+static dev_link_t *serial_attach(void)
+{
+       serial_info_t *info;
+       client_reg_t client_reg;
+       dev_link_t *link;
+       int i, ret;
+
+       DEBUG(0, "serial_attach()\n");
+
+       /* Create new serial device */
+       info = kmalloc(sizeof (*info), GFP_KERNEL);
+       if (!info)
+               return NULL;
+       memset(info, 0, sizeof (*info));
+       link = &info->link;
+       link->priv = info;
+
+       INIT_TQUEUE(&info->remove, do_serial_release, info);
+
+       link->io.Attributes1 = IO_DATA_PATH_WIDTH_8;
+       link->io.NumPorts1 = 8;
+       link->irq.Attributes = IRQ_TYPE_EXCLUSIVE;
+       link->irq.IRQInfo1 = IRQ_INFO2_VALID | IRQ_LEVEL_ID;
+       if (irq_list[0] == -1)
+               link->irq.IRQInfo2 = irq_mask;
+       else
+               for (i = 0; i < 4; i++)
+                       link->irq.IRQInfo2 |= 1 << irq_list[i];
+       link->conf.Attributes = CONF_ENABLE_IRQ;
+       link->conf.Vcc = 50;
+       if (do_sound) {
+               link->conf.Attributes |= CONF_ENABLE_SPKR;
+               link->conf.Status = CCSR_AUDIO_ENA;
+       }
+       link->conf.IntType = INT_MEMORY_AND_IO;
+
+       /* Register with Card Services */
+       link->next = dev_list;
+       dev_list = link;
+       client_reg.dev_info = &dev_info;
+       client_reg.Attributes = INFO_IO_CLIENT | INFO_CARD_SHARE;
+       client_reg.EventMask =
+           CS_EVENT_CARD_INSERTION | CS_EVENT_CARD_REMOVAL |
+           CS_EVENT_RESET_PHYSICAL | CS_EVENT_CARD_RESET |
+           CS_EVENT_PM_SUSPEND | CS_EVENT_PM_RESUME;
+       client_reg.event_handler = &serial_event;
+       client_reg.Version = 0x0210;
+       client_reg.event_callback_args.client_data = link;
+       ret = CardServices(RegisterClient, &link->handle, &client_reg);
+       if (ret != CS_SUCCESS) {
+               cs_error(link->handle, RegisterClient, ret);
+               serial_detach(link);
+               return NULL;
+       }
+
+       return link;
+}
+
+/*======================================================================
+
+    This deletes a driver "instance".  The device is de-registered
+    with Card Services.  If it has been released, all local data
+    structures are freed.  Otherwise, the structures will be freed
+    when the device is released.
+
+======================================================================*/
+
+static void serial_detach(dev_link_t * link)
+{
+       serial_info_t *info = link->priv;
+       dev_link_t **linkp;
+       int ret;
+
+       DEBUG(0, "serial_detach(0x%p)\n", link);
+
+       /* Locate device structure */
+       for (linkp = &dev_list; *linkp; linkp = &(*linkp)->next)
+               if (*linkp == link)
+                       break;
+       if (*linkp == NULL)
+               return;
+
+       /*
+        * Ensure any outstanding scheduled tasks are completed.
+        */
+       flush_scheduled_tasks();
+
+       /*
+        * Ensure that the ports have been released.
+        */
+       do_serial_release(info);
+
+       if (link->handle) {
+               ret = CardServices(DeregisterClient, link->handle);
+               if (ret != CS_SUCCESS)
+                       cs_error(link->handle, DeregisterClient, ret);
+       }
+
+       /* Unlink device structure, free bits */
+       *linkp = link->next;
+       kfree(info);
+}
+
+/*====================================================================*/
+
+static int setup_serial(serial_info_t * info, ioaddr_t port, int irq)
+{
+       struct serial_struct serial;
+       int line;
+
+       memset(&serial, 0, sizeof (serial));
+       serial.port = port;
+       serial.irq = irq;
+       serial.flags = ASYNC_SKIP_TEST | ASYNC_SHARE_IRQ;
+       line = register_serial(&serial);
+       if (line < 0) {
+               printk(KERN_NOTICE "serial_cs: register_serial() at 0x%04lx,"
+                      " irq %d failed\n", (u_long) serial.port, serial.irq);
+               return -1;
+       }
+
+       info->line[info->ndev] = line;
+       sprintf(info->node[info->ndev].dev_name, "ttyS%d", line);
+       info->node[info->ndev].major = TTY_MAJOR;
+       info->node[info->ndev].minor = 0x40 + line;
+       if (info->ndev > 0)
+               info->node[info->ndev - 1].next = &info->node[info->ndev];
+       info->ndev++;
+
+       return 0;
+}
+
+/*====================================================================*/
+
+static int
+get_tuple(int fn, client_handle_t handle, tuple_t * tuple, cisparse_t * parse)
+{
+       int i;
+       i = CardServices(fn, handle, tuple);
+       if (i != CS_SUCCESS)
+               return CS_NO_MORE_ITEMS;
+       i = CardServices(GetTupleData, handle, tuple);
+       if (i != CS_SUCCESS)
+               return i;
+       return CardServices(ParseTuple, handle, tuple, parse);
+}
+
+#define first_tuple(a, b, c) get_tuple(GetFirstTuple, a, b, c)
+#define next_tuple(a, b, c) get_tuple(GetNextTuple, a, b, c)
+
+/*====================================================================*/
+
+static int simple_config(dev_link_t * link)
+{
+       static ioaddr_t base[5] = { 0x3f8, 0x2f8, 0x3e8, 0x2e8, 0x0 };
+       client_handle_t handle = link->handle;
+       serial_info_t *info = link->priv;
+       tuple_t tuple;
+       u_char buf[256];
+       cisparse_t parse;
+       cistpl_cftable_entry_t *cf = &parse.cftable_entry;
+       config_info_t config;
+       int i, j, try;
+
+       /* If the card is already configured, look up the port and irq */
+       i = CardServices(GetConfigurationInfo, handle, &config);
+       if ((i == CS_SUCCESS) && (config.Attributes & CONF_VALID_CLIENT)) {
+               ioaddr_t port = 0;
+               if ((config.BasePort2 != 0) && (config.NumPorts2 == 8)) {
+                       port = config.BasePort2;
+                       info->slave = 1;
+               } else if ((info->manfid == MANFID_OSITECH) &&
+                          (config.NumPorts1 == 0x40)) {
+                       port = config.BasePort1 + 0x28;
+                       info->slave = 1;
+               }
+               if (info->slave)
+                       return setup_serial(info, port, config.AssignedIRQ);
+       }
+       link->conf.Vcc = config.Vcc;
+
+       /* First pass: look for a config entry that looks normal. */
+       tuple.TupleData = (cisdata_t *) buf;
+       tuple.TupleOffset = 0;
+       tuple.TupleDataMax = 255;
+       tuple.Attributes = 0;
+       tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY;
+       /* Two tries: without IO aliases, then with aliases */
+       for (try = 0; try < 2; try++) {
+               i = first_tuple(handle, &tuple, &parse);
+               while (i != CS_NO_MORE_ITEMS) {
+                       if (i != CS_SUCCESS)
+                               goto next_entry;
+                       if (cf->vpp1.present & (1 << CISTPL_POWER_VNOM))
+                               link->conf.Vpp1 = link->conf.Vpp2 =
+                                   cf->vpp1.param[CISTPL_POWER_VNOM] / 10000;
+                       if ((cf->io.nwin > 0) && (cf->io.win[0].len == 8) &&
+                           (cf->io.win[0].base != 0)) {
+                               link->conf.ConfigIndex = cf->index;
+                               link->io.BasePort1 = cf->io.win[0].base;
+                               link->io.IOAddrLines = (try == 0) ?
+                                   16 : cf->io.flags & CISTPL_IO_LINES_MASK;
+                               i =
+                                   CardServices(RequestIO, link->handle,
+                                                &link->io);
+                               if (i == CS_SUCCESS)
+                                       goto found_port;
+                       }
+                     next_entry:
+                       i = next_tuple(handle, &tuple, &parse);
+               }
+       }
+
+       /* Second pass: try to find an entry that isn't picky about
+          its base address, then try to grab any standard serial port
+          address, and finally try to get any free port. */
+       i = first_tuple(handle, &tuple, &parse);
+       while (i != CS_NO_MORE_ITEMS) {
+               if ((i == CS_SUCCESS) && (cf->io.nwin > 0) &&
+                   ((cf->io.flags & CISTPL_IO_LINES_MASK) <= 3)) {
+                       link->conf.ConfigIndex = cf->index;
+                       for (j = 0; j < 5; j++) {
+                               link->io.BasePort1 = base[j];
+                               link->io.IOAddrLines = base[j] ? 16 : 3;
+                               i = CardServices(RequestIO, link->handle,
+                                                &link->io);
+                               if (i == CS_SUCCESS)
+                                       goto found_port;
+                       }
+               }
+               i = next_tuple(handle, &tuple, &parse);
+       }
+
+      found_port:
+       if (i != CS_SUCCESS) {
+               printk(KERN_NOTICE
+                      "serial_cs: no usable port range found, giving up\n");
+               cs_error(link->handle, RequestIO, i);
+               return -1;
+       }
+
+       i = CardServices(RequestIRQ, link->handle, &link->irq);
+       if (i != CS_SUCCESS) {
+               cs_error(link->handle, RequestIRQ, i);
+               link->irq.AssignedIRQ = 0;
+       }
+       if (info->multi && (info->manfid == MANFID_3COM))
+               link->conf.ConfigIndex &= ~(0x08);
+       i = CardServices(RequestConfiguration, link->handle, &link->conf);
+       if (i != CS_SUCCESS) {
+               cs_error(link->handle, RequestConfiguration, i);
+               return -1;
+       }
+
+       return setup_serial(info, link->io.BasePort1, link->irq.AssignedIRQ);
+}
+
+static int multi_config(dev_link_t * link)
+{
+       client_handle_t handle = link->handle;
+       serial_info_t *info = link->priv;
+       tuple_t tuple;
+       u_char buf[256];
+       cisparse_t parse;
+       cistpl_cftable_entry_t *cf = &parse.cftable_entry;
+       int i, base2 = 0;
+
+       tuple.TupleData = (cisdata_t *) buf;
+       tuple.TupleOffset = 0;
+       tuple.TupleDataMax = 255;
+       tuple.Attributes = 0;
+       tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY;
+
+       /* First, look for a generic full-sized window */
+       link->io.NumPorts1 = info->multi * 8;
+       i = first_tuple(handle, &tuple, &parse);
+       while (i != CS_NO_MORE_ITEMS) {
+               /* The quad port cards have bad CIS's, so just look for a
+                  window larger than 8 ports and assume it will be right */
+               if ((i == CS_SUCCESS) && (cf->io.nwin == 1) &&
+                   (cf->io.win[0].len > 8)) {
+                       link->conf.ConfigIndex = cf->index;
+                       link->io.BasePort1 = cf->io.win[0].base;
+                       link->io.IOAddrLines =
+                           cf->io.flags & CISTPL_IO_LINES_MASK;
+                       i = CardServices(RequestIO, link->handle, &link->io);
+                       base2 = link->io.BasePort1 + 8;
+                       if (i == CS_SUCCESS)
+                               break;
+               }
+               i = next_tuple(handle, &tuple, &parse);
+       }
+
+       /* If that didn't work, look for two windows */
+       if (i != CS_SUCCESS) {
+               link->io.NumPorts1 = link->io.NumPorts2 = 8;
+               info->multi = 2;
+               i = first_tuple(handle, &tuple, &parse);
+               while (i != CS_NO_MORE_ITEMS) {
+                       if ((i == CS_SUCCESS) && (cf->io.nwin == 2)) {
+                               link->conf.ConfigIndex = cf->index;
+                               link->io.BasePort1 = cf->io.win[0].base;
+                               link->io.BasePort2 = cf->io.win[1].base;
+                               link->io.IOAddrLines =
+                                   cf->io.flags & CISTPL_IO_LINES_MASK;
+                               i =
+                                   CardServices(RequestIO, link->handle,
+                                                &link->io);
+                               base2 = link->io.BasePort2;
+                               if (i == CS_SUCCESS)
+                                       break;
+                       }
+                       i = next_tuple(handle, &tuple, &parse);
+               }
+       }
+
+       if (i != CS_SUCCESS) {
+               cs_error(link->handle, RequestIO, i);
+               return -1;
+       }
+
+       i = CardServices(RequestIRQ, link->handle, &link->irq);
+       if (i != CS_SUCCESS) {
+               printk(KERN_NOTICE
+                      "serial_cs: no usable port range found, giving up\n");
+               cs_error(link->handle, RequestIRQ, i);
+               link->irq.AssignedIRQ = 0;
+       }
+       /* Socket Dual IO: this enables irq's for second port */
+       if (info->multi && (info->manfid == MANFID_SOCKET)) {
+               link->conf.Present |= PRESENT_EXT_STATUS;
+               link->conf.ExtStatus = ESR_REQ_ATTN_ENA;
+       }
+       i = CardServices(RequestConfiguration, link->handle, &link->conf);
+       if (i != CS_SUCCESS) {
+               cs_error(link->handle, RequestConfiguration, i);
+               return -1;
+       }
+
+       setup_serial(info, link->io.BasePort1, link->irq.AssignedIRQ);
+       /* The Nokia cards are not really multiport cards */
+       if (info->manfid == MANFID_NOKIA)
+               return 0;
+       for (i = 0; i < info->multi - 1; i++)
+               setup_serial(info, base2 + (8 * i), link->irq.AssignedIRQ);
+
+       return 0;
+}
+
+/*======================================================================
+
+    serial_config() is scheduled to run after a CARD_INSERTION event
+    is received, to configure the PCMCIA socket, and to make the
+    serial device available to the system.
+
+======================================================================*/
+
+#define CS_CHECK(fn, args...) \
+while ((last_ret=CardServices(last_fn=(fn), args))!=0) goto cs_failed
+
+void serial_config(dev_link_t * link)
+{
+       client_handle_t handle = link->handle;
+       serial_info_t *info = link->priv;
+       tuple_t tuple;
+       u_short buf[128];
+       cisparse_t parse;
+       cistpl_cftable_entry_t *cf = &parse.cftable_entry;
+       int i, last_ret, last_fn;
+
+       DEBUG(0, "serial_config(0x%p)\n", link);
+
+       tuple.TupleData = (cisdata_t *) buf;
+       tuple.TupleOffset = 0;
+       tuple.TupleDataMax = 255;
+       tuple.Attributes = 0;
+       /* Get configuration register information */
+       tuple.DesiredTuple = CISTPL_CONFIG;
+       last_ret = first_tuple(handle, &tuple, &parse);
+       if (last_ret != CS_SUCCESS) {
+               last_fn = ParseTuple;
+               goto cs_failed;
+       }
+       link->conf.ConfigBase = parse.config.base;
+       link->conf.Present = parse.config.rmask[0];
+
+       /* Configure card */
+       link->state |= DEV_CONFIG;
+
+       /* Is this a compliant multifunction card? */
+       tuple.DesiredTuple = CISTPL_LONGLINK_MFC;
+       tuple.Attributes = TUPLE_RETURN_COMMON | TUPLE_RETURN_LINK;
+       info->multi = (first_tuple(handle, &tuple, &parse) == CS_SUCCESS);
+
+       /* Is this a multiport card? */
+       tuple.DesiredTuple = CISTPL_MANFID;
+       if (first_tuple(handle, &tuple, &parse) == CS_SUCCESS) {
+               info->manfid = le16_to_cpu(buf[0]);
+               for (i = 0; i < MULTI_COUNT; i++)
+                       if ((info->manfid == multi_id[i].manfid) &&
+                           (le16_to_cpu(buf[1]) == multi_id[i].prodid))
+                               break;
+               if (i < MULTI_COUNT)
+                       info->multi = multi_id[i].multi;
+       }
+
+       /* Another check for dual-serial cards: look for either serial or
+          multifunction cards that ask for appropriate IO port ranges */
+       tuple.DesiredTuple = CISTPL_FUNCID;
+       if ((info->multi == 0) &&
+           ((first_tuple(handle, &tuple, &parse) != CS_SUCCESS) ||
+            (parse.funcid.func == CISTPL_FUNCID_MULTI) ||
+            (parse.funcid.func == CISTPL_FUNCID_SERIAL))) {
+               tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY;
+               if (first_tuple(handle, &tuple, &parse) == CS_SUCCESS) {
+                       if ((cf->io.nwin == 1) && (cf->io.win[0].len % 8 == 0))
+                               info->multi = cf->io.win[0].len >> 3;
+                       if ((cf->io.nwin == 2) && (cf->io.win[0].len == 8) &&
+                           (cf->io.win[1].len == 8))
+                               info->multi = 2;
+               }
+       }
+
+       if (info->multi > 1)
+               multi_config(link);
+       else
+               simple_config(link);
+
+       if (info->ndev == 0)
+               goto failed;
+
+       if (info->manfid == MANFID_IBM) {
+               conf_reg_t reg = { 0, CS_READ, 0x800, 0 };
+               CS_CHECK(AccessConfigurationRegister, link->handle, &reg);
+               reg.Action = CS_WRITE;
+               reg.Value = reg.Value | 1;
+               CS_CHECK(AccessConfigurationRegister, link->handle, &reg);
+       }
+
+       link->dev = &info->node[0];
+       link->state &= ~DEV_CONFIG_PENDING;
+       return;
+
+ cs_failed:
+       cs_error(link->handle, last_fn, last_ret);
+ failed:
+       do_serial_release(info);
+}
+
+/*======================================================================
+
+    The card status event handler.  Mostly, this schedules other
+    stuff to run after an event is received.  A CARD_REMOVAL event
+    also sets some flags to discourage the serial drivers from
+    talking to the ports.
+    
+======================================================================*/
+
+static int
+serial_event(event_t event, int priority, event_callback_args_t * args)
+{
+       dev_link_t *link = args->client_data;
+       serial_info_t *info = link->priv;
+
+       DEBUG(1, "serial_event(0x%06x)\n", event);
+
+       switch (event) {
+       case CS_EVENT_CARD_REMOVAL:
+               serial_remove(link);
+               break;
+
+       case CS_EVENT_CARD_INSERTION:
+               link->state |= DEV_PRESENT | DEV_CONFIG_PENDING;
+               serial_config(link);
+               break;
+
+       case CS_EVENT_PM_SUSPEND:
+               link->state |= DEV_SUSPEND;
+               /* Fall through... */
+       case CS_EVENT_RESET_PHYSICAL:
+               if ((link->state & DEV_CONFIG) && !info->slave)
+                       CardServices(ReleaseConfiguration, link->handle);
+               break;
+
+       case CS_EVENT_PM_RESUME:
+               link->state &= ~DEV_SUSPEND;
+               /* Fall through... */
+       case CS_EVENT_CARD_RESET:
+               if (DEV_OK(link) && !info->slave)
+                       CardServices(RequestConfiguration, link->handle,
+                                    &link->conf);
+               break;
+       }
+       return 0;
+}
+
+/*====================================================================*/
+
+static int __init init_serial_cs(void)
+{
+       servinfo_t serv;
+       DEBUG(0, "%s\n", version);
+       CardServices(GetCardServicesInfo, &serv);
+       if (serv.Revision != CS_RELEASE_CODE) {
+               printk(KERN_NOTICE "serial_cs: Card Services release "
+                      "does not match!\n");
+               return -1;
+       }
+       register_pccard_driver(&dev_info, &serial_attach, &serial_detach);
+       return 0;
+}
+
+static void __exit exit_serial_cs(void)
+{
+       DEBUG(0, "serial_cs: unloading\n");
+       unregister_pccard_driver(&dev_info);
+       while (dev_list != NULL)
+               serial_detach(dev_list);
+}
+
+module_init(init_serial_cs);
+module_exit(exit_serial_cs);
+
+MODULE_LICENSE("GPL");
diff --git a/drivers/serial/serial_8250_pci.c b/drivers/serial/serial_8250_pci.c
new file mode 100644 (file)
index 0000000..5b20507
--- /dev/null
@@ -0,0 +1,1138 @@
+/*
+ *  linux/drivers/char/serial_8250_pci.c
+ *
+ *  Probe module for 8250/16550-type PCI serial ports.
+ *
+ *  Based on drivers/char/serial.c, by Linus Torvalds, Theodore Ts'o.
+ *
+ *  Copyright (C) 2001 Russell King, All Rights Reserved.
+ *
+ * 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.
+ *
+ *  $Id: serial_8250_pci.c,v 1.18 2002/03/10 22:32:08 rmk Exp $
+ */
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/pci.h>
+#include <linux/sched.h>
+#include <linux/string.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/serial.h>
+
+/* 2.4.6 compatibility cruft - to be removed with the old serial.c code */
+#define pci_board __pci_board
+#include <linux/serialP.h>
+#undef pci_board
+
+#include <asm/bitops.h>
+#include <asm/byteorder.h>
+#include <asm/serial.h>
+
+#include "serial_8250.h"
+
+
+#ifndef IS_PCI_REGION_IOPORT
+#define IS_PCI_REGION_IOPORT(dev, r) (pci_resource_flags((dev), (r)) & \
+                                     IORESOURCE_IO)
+#endif
+#ifndef IS_PCI_REGION_IOMEM
+#define IS_PCI_REGION_IOMEM(dev, r) (pci_resource_flags((dev), (r)) & \
+                                     IORESOURCE_MEM)
+#endif
+#ifndef PCI_IRQ_RESOURCE
+#define PCI_IRQ_RESOURCE(dev, r) ((dev)->irq_resource[r].start)
+#endif
+
+#ifndef pci_get_subvendor
+#define pci_get_subvendor(dev) ((dev)->subsystem_vendor)
+#define pci_get_subdevice(dev)  ((dev)->subsystem_device)
+#endif
+
+struct serial_private {
+       unsigned int nr;
+       struct pci_board *board;
+       int line[0];
+};
+
+struct pci_board {
+       int flags;
+       int num_ports;
+       int base_baud;
+       int uart_offset;
+       int reg_shift;
+       int (*init_fn)(struct pci_dev *dev, struct pci_board *board,
+                       int enable);
+       int first_uart_offset;
+};
+
+static int
+get_pci_port(struct pci_dev *dev, struct pci_board *board,
+            struct serial_struct *req, int idx)
+{
+       unsigned long port;
+       int base_idx;
+       int max_port;
+       int offset;
+
+       base_idx = SPCI_FL_GET_BASE(board->flags);
+       if (board->flags & SPCI_FL_BASE_TABLE)
+               base_idx += idx;
+
+       if (board->flags & SPCI_FL_REGION_SZ_CAP) {
+               max_port = pci_resource_len(dev, base_idx) / 8;
+               if (idx >= max_port)
+                       return 1;
+       }
+                       
+       offset = board->first_uart_offset;
+
+       /*
+        * Timedia/SUNIX uses a mixture of BARs and offsets
+        * Ugh, this is ugly as all hell --- TYT
+        */
+       if (dev->vendor == PCI_VENDOR_ID_TIMEDIA)
+               switch(idx) {
+               case 0:
+                       base_idx = 0;
+                       break;
+               case 1:
+                       base_idx = 0;
+                       offset = 8;
+                       break;
+               case 2:
+                       base_idx = 1; 
+                       break;
+               case 3:
+                       base_idx = 1;
+                       offset = 8;
+                       break;
+               case 4: /* BAR 2 */
+               case 5: /* BAR 3 */
+               case 6: /* BAR 4 */
+               case 7: /* BAR 5 */
+                       base_idx = idx - 2;
+               }
+
+       /* AFAVLAB uses a different mixture of BARs and offsets */
+       /* Not that ugly ;) -- HW */
+       if (dev->vendor == PCI_VENDOR_ID_AFAVLAB && idx >= 4) {
+               base_idx = 4;
+               offset = (idx - 4) * 8;
+       }
+
+       /* Some Titan cards are also a little weird */
+       if (dev->vendor == PCI_VENDOR_ID_TITAN &&
+           (dev->device == PCI_DEVICE_ID_TITAN_400L ||
+            dev->device == PCI_DEVICE_ID_TITAN_800L)) {
+               switch (idx) {
+               case 0: base_idx = 1;
+                       break;
+               case 1: base_idx = 2;
+                       break;
+               default:
+                       base_idx = 4;
+                       offset = 8 * (idx - 2);
+               }
+       }
+  
+       port =  pci_resource_start(dev, base_idx) + offset;
+
+       if ((board->flags & SPCI_FL_BASE_TABLE) == 0)
+               port += idx * (board->uart_offset ? board->uart_offset : 8);
+
+       if (IS_PCI_REGION_IOPORT(dev, base_idx)) {
+               req->port = port;
+               if (HIGH_BITS_OFFSET)
+                       req->port_high = port >> HIGH_BITS_OFFSET;
+               else
+                       req->port_high = 0;
+               return 0;
+       }
+       req->io_type = SERIAL_IO_MEM;
+       req->iomem_base = ioremap(port, board->uart_offset);
+       if (req->iomem_base == NULL)
+               return -ENOMEM;
+       req->iomem_reg_shift = board->reg_shift;
+       req->port = 0;
+       return 0;
+}
+
+static _INLINE_ int
+get_pci_irq(struct pci_dev *dev, struct pci_board *board, int idx)
+{
+       int base_idx;
+
+       if ((board->flags & SPCI_FL_IRQRESOURCE) == 0)
+               return dev->irq;
+
+       base_idx = SPCI_FL_GET_IRQBASE(board->flags);
+       if (board->flags & SPCI_FL_IRQ_TABLE)
+               base_idx += idx;
+       
+       return PCI_IRQ_RESOURCE(dev, base_idx);
+}
+
+/*
+ * Some PCI serial cards using the PLX 9050 PCI interface chip require
+ * that the card interrupt be explicitly enabled or disabled.  This
+ * seems to be mainly needed on card using the PLX which also use I/O
+ * mapped memory.
+ */
+static int __devinit
+pci_plx9050_fn(struct pci_dev *dev, struct pci_board *board, int enable)
+{
+       u8 *p, irq_config = 0;
+
+       if (enable) {
+               irq_config = 0x41;
+               if (dev->vendor == PCI_VENDOR_ID_PANACOM)
+                       irq_config = 0x43;
+               if ((dev->vendor == PCI_VENDOR_ID_PLX) &&
+                   (dev->device == PCI_DEVICE_ID_PLX_ROMULUS)) {
+                       /*
+                        * As the megawolf cards have the int pins active
+                        * high, and have 2 UART chips, both ints must be
+                        * enabled on the 9050. Also, the UARTS are set in
+                        * 16450 mode by default, so we have to enable the
+                        * 16C950 'enhanced' mode so that we can use the
+                        * deep FIFOs
+                        */
+                       irq_config = 0x5b;
+               }
+       }
+
+       /*
+        * enable/disable interrupts
+        */
+       p = ioremap(pci_resource_start(dev, 0), 0x80);
+       if (p == NULL)
+               return -ENOMEM;
+       writel(irq_config, (unsigned long)p + 0x4c);
+
+       /*
+        * Read the register back to ensure that it took effect.
+        */
+       readl((unsigned long)p + 0x4c);
+       iounmap(p);
+
+       return 0;
+}
+
+
+/*
+ * SIIG serial cards have an PCI interface chip which also controls
+ * the UART clocking frequency. Each UART can be clocked independently
+ * (except cards equiped with 4 UARTs) and initial clocking settings
+ * are stored in the EEPROM chip. It can cause problems because this
+ * version of serial driver doesn't support differently clocked UART's
+ * on single PCI card. To prevent this, initialization functions set
+ * high frequency clocking for all UART's on given card. It is safe (I
+ * hope) because it doesn't touch EEPROM settings to prevent conflicts
+ * with other OSes (like M$ DOS).
+ *
+ *  SIIG support added by Andrey Panin <pazke@mail.tp.ru>, 10/1999
+ * 
+ * There is two family of SIIG serial cards with different PCI
+ * interface chip and different configuration methods:
+ *     - 10x cards have control registers in IO and/or memory space;
+ *     - 20x cards have control registers in standard PCI configuration space.
+ */
+
+#define PCI_DEVICE_ID_SIIG_1S_10x (PCI_DEVICE_ID_SIIG_1S_10x_550 & 0xfffc)
+#define PCI_DEVICE_ID_SIIG_2S_10x (PCI_DEVICE_ID_SIIG_2S_10x_550 & 0xfff8)
+
+static int __devinit
+pci_siig10x_fn(struct pci_dev *dev, struct pci_board *board, int enable)
+{
+       u16 data, *p;
+
+       if (!enable)
+               return 0;
+
+       switch (dev->device & 0xfff8) {
+               case PCI_DEVICE_ID_SIIG_1S_10x: /* 1S */
+                       data = 0xffdf;
+                       break;
+               case PCI_DEVICE_ID_SIIG_2S_10x: /* 2S, 2S1P */
+                       data = 0xf7ff;
+                       break;
+               default:                        /* 1S1P, 4S */
+                       data = 0xfffb;
+                       break;
+       }
+
+       p = ioremap(pci_resource_start(dev, 0), 0x80);
+       if (p == NULL)
+               return -ENOMEM;
+
+       writew(readw((unsigned long) p + 0x28) & data, (unsigned long) p + 0x28);
+       iounmap(p);
+       return 0;
+}
+
+#define PCI_DEVICE_ID_SIIG_2S_20x (PCI_DEVICE_ID_SIIG_2S_20x_550 & 0xfffc)
+#define PCI_DEVICE_ID_SIIG_2S1P_20x (PCI_DEVICE_ID_SIIG_2S1P_20x_550 & 0xfffc)
+
+static int __devinit
+pci_siig20x_fn(struct pci_dev *dev, struct pci_board *board, int enable)
+{
+       u8 data;
+
+       if (!enable)
+               return 0;
+
+       /* Change clock frequency for the first UART. */
+       pci_read_config_byte(dev, 0x6f, &data);
+       pci_write_config_byte(dev, 0x6f, data & 0xef);
+
+       /* If this card has 2 UART, we have to do the same with second UART. */
+       if (((dev->device & 0xfffc) == PCI_DEVICE_ID_SIIG_2S_20x) ||
+           ((dev->device & 0xfffc) == PCI_DEVICE_ID_SIIG_2S1P_20x)) {
+               pci_read_config_byte(dev, 0x73, &data);
+               pci_write_config_byte(dev, 0x73, data & 0xef);
+       }
+       return 0;
+}
+
+/* Added for EKF Intel i960 serial boards */
+static int __devinit
+pci_inteli960ni_fn(struct pci_dev *dev, struct pci_board *board, int enable)
+{
+       unsigned long oldval;
+
+       if (!(pci_get_subdevice(dev) & 0x1000))
+               return -ENODEV;
+
+       if (!enable) /* is there something to deinit? */
+               return 0;
+   
+       /* is firmware started? */
+       pci_read_config_dword(dev, 0x44, (void*) &oldval); 
+       if (oldval == 0x00001000L) { /* RESET value */ 
+               printk(KERN_DEBUG "Local i960 firmware missing");
+               return -ENODEV;
+       }
+       return 0;
+}
+
+/*
+ * Timedia has an explosion of boards, and to avoid the PCI table from
+ * growing *huge*, we use this function to collapse some 70 entries
+ * in the PCI table into one, for sanity's and compactness's sake.
+ */
+static unsigned short timedia_single_port[] = {
+       0x4025, 0x4027, 0x4028, 0x5025, 0x5027, 0
+};
+
+static unsigned short timedia_dual_port[] = {
+       0x0002, 0x4036, 0x4037, 0x4038, 0x4078, 0x4079, 0x4085,
+       0x4088, 0x4089, 0x5037, 0x5078, 0x5079, 0x5085, 0x6079, 
+       0x7079, 0x8079, 0x8137, 0x8138, 0x8237, 0x8238, 0x9079, 
+       0x9137, 0x9138, 0x9237, 0x9238, 0xA079, 0xB079, 0xC079,
+       0xD079, 0
+};
+
+static unsigned short timedia_quad_port[] = {
+       0x4055, 0x4056, 0x4095, 0x4096, 0x5056, 0x8156, 0x8157, 
+       0x8256, 0x8257, 0x9056, 0x9156, 0x9157, 0x9158, 0x9159, 
+       0x9256, 0x9257, 0xA056, 0xA157, 0xA158, 0xA159, 0xB056,
+       0xB157, 0
+};
+
+static unsigned short timedia_eight_port[] = {
+       0x4065, 0x4066, 0x5065, 0x5066, 0x8166, 0x9066, 0x9166, 
+       0x9167, 0x9168, 0xA066, 0xA167, 0xA168, 0
+};
+
+static struct timedia_struct {
+       int num;
+       unsigned short *ids;
+} timedia_data[] = {
+       { 1, timedia_single_port },
+       { 2, timedia_dual_port },
+       { 4, timedia_quad_port },
+       { 8, timedia_eight_port },
+       { 0, 0 }
+};
+
+static int __devinit
+pci_timedia_fn(struct pci_dev *dev, struct pci_board *board, int enable)
+{
+       int     i, j;
+       unsigned short *ids;
+
+       if (!enable)
+               return 0;
+
+       for (i = 0; timedia_data[i].num; i++) {
+               ids = timedia_data[i].ids;
+               for (j = 0; ids[j]; j++) {
+                       if (pci_get_subdevice(dev) == ids[j]) {
+                               board->num_ports = timedia_data[i].num;
+                               return 0;
+                       }
+               }
+       }
+       return 0;
+}
+
+static int __devinit
+pci_xircom_fn(struct pci_dev *dev, struct pci_board *board, int enable)
+{
+       __set_current_state(TASK_UNINTERRUPTIBLE);
+       schedule_timeout(HZ/10);
+       return 0;
+}
+
+/*
+ * This is the configuration table for all of the PCI serial boards
+ * which we support.  It is directly indexed by the pci_board_num_t enum
+ * value, which is encoded in the pci_device_id PCI probe table's
+ * driver_data member.
+ */
+enum pci_board_num_t {
+       pbn_b0_1_115200,
+       pbn_default = 0,
+
+       pbn_b0_2_115200,
+       pbn_b0_4_115200,
+
+       pbn_b0_1_921600,
+       pbn_b0_2_921600,
+       pbn_b0_4_921600,
+
+       pbn_b0_bt_1_115200,
+       pbn_b0_bt_2_115200,
+       pbn_b0_bt_8_115200,
+       pbn_b0_bt_1_460800,
+       pbn_b0_bt_2_460800,
+
+       pbn_b1_1_115200,
+       pbn_b1_2_115200,
+       pbn_b1_4_115200,
+       pbn_b1_8_115200,
+
+       pbn_b1_2_921600,
+       pbn_b1_4_921600,
+       pbn_b1_8_921600,
+
+       pbn_b1_2_1382400,
+       pbn_b1_4_1382400,
+       pbn_b1_8_1382400,
+
+       pbn_b2_8_115200,
+       pbn_b2_4_460800,
+       pbn_b2_8_460800,
+       pbn_b2_16_460800,
+       pbn_b2_4_921600,
+       pbn_b2_8_921600,
+
+       pbn_b2_bt_1_115200,
+       pbn_b2_bt_2_115200,
+       pbn_b2_bt_4_115200,
+       pbn_b2_bt_2_921600,
+
+       pbn_panacom,
+       pbn_panacom2,
+       pbn_panacom4,
+       pbn_plx_romulus,
+       pbn_oxsemi,
+       pbn_timedia,
+       pbn_intel_i960,
+       pbn_sgi_ioc3,
+       pbn_nec_nile4,
+
+       pbn_dci_pccom4,
+       pbn_dci_pccom8,
+
+       pbn_xircom_combo,
+
+       pbn_siig10x_0,
+       pbn_siig10x_1,
+       pbn_siig10x_2,
+       pbn_siig10x_4,
+       pbn_siig20x_0,
+       pbn_siig20x_2,
+       pbn_siig20x_4,
+       
+       pbn_computone_4,
+       pbn_computone_6,
+       pbn_computone_8,
+};
+
+static struct pci_board pci_boards[] __devinitdata = {
+       /*
+        * PCI Flags, Number of Ports, Base (Maximum) Baud Rate,
+        * Offset to get to next UART's registers,
+        * Register shift to use for memory-mapped I/O,
+        * Initialization function, first UART offset
+        */
+
+       /* Generic serial board, pbn_b0_1_115200, pbn_default */
+       { SPCI_FL_BASE0, 1, 115200 },           /* pbn_b0_1_115200,
+                                                  pbn_default */
+
+       { SPCI_FL_BASE0, 2, 115200 },           /* pbn_b0_2_115200 */
+       { SPCI_FL_BASE0, 4, 115200 },           /* pbn_b0_4_115200 */
+
+       { SPCI_FL_BASE0, 1, 921600 },           /* pbn_b0_1_921600 */
+       { SPCI_FL_BASE0, 2, 921600 },           /* pbn_b0_2_921600 */
+       { SPCI_FL_BASE0, 4, 921600 },           /* pbn_b0_4_921600 */
+
+       { SPCI_FL_BASE0 | SPCI_FL_BASE_TABLE, 1, 115200 }, /* pbn_b0_bt_1_115200 */
+       { SPCI_FL_BASE0 | SPCI_FL_BASE_TABLE, 2, 115200 }, /* pbn_b0_bt_2_115200 */
+       { SPCI_FL_BASE0 | SPCI_FL_BASE_TABLE, 8, 115200 }, /* pbn_b0_bt_8_115200 */
+       { SPCI_FL_BASE0 | SPCI_FL_BASE_TABLE, 1, 460800 }, /* pbn_b0_bt_1_460800 */
+       { SPCI_FL_BASE0 | SPCI_FL_BASE_TABLE, 2, 460800 }, /* pbn_b0_bt_2_460800 */
+
+       { SPCI_FL_BASE1, 1, 115200 },           /* pbn_b1_1_115200 */
+       { SPCI_FL_BASE1, 2, 115200 },           /* pbn_b1_2_115200 */
+       { SPCI_FL_BASE1, 4, 115200 },           /* pbn_b1_4_115200 */
+       { SPCI_FL_BASE1, 8, 115200 },           /* pbn_b1_8_115200 */
+
+       { SPCI_FL_BASE1, 2, 921600 },           /* pbn_b1_2_921600 */
+       { SPCI_FL_BASE1, 4, 921600 },           /* pbn_b1_4_921600 */
+       { SPCI_FL_BASE1, 8, 921600 },           /* pbn_b1_8_921600 */
+
+       { SPCI_FL_BASE1, 2, 1382400 },          /* pbn_b1_2_1382400 */
+       { SPCI_FL_BASE1, 4, 1382400 },          /* pbn_b1_4_1382400 */
+       { SPCI_FL_BASE1, 8, 1382400 },          /* pbn_b1_8_1382400 */
+
+       { SPCI_FL_BASE2, 8, 115200 },           /* pbn_b2_8_115200 */
+       { SPCI_FL_BASE2, 4, 460800 },           /* pbn_b2_4_460800 */
+       { SPCI_FL_BASE2, 8, 460800 },           /* pbn_b2_8_460800 */
+       { SPCI_FL_BASE2, 16, 460800 },          /* pbn_b2_16_460800 */
+       { SPCI_FL_BASE2, 4, 921600 },           /* pbn_b2_4_921600 */
+       { SPCI_FL_BASE2, 8, 921600 },           /* pbn_b2_8_921600 */
+
+       { SPCI_FL_BASE2 | SPCI_FL_BASE_TABLE, 1, 115200 }, /* pbn_b2_bt_1_115200 */
+       { SPCI_FL_BASE2 | SPCI_FL_BASE_TABLE, 2, 115200 }, /* pbn_b2_bt_2_115200 */
+       { SPCI_FL_BASE2 | SPCI_FL_BASE_TABLE, 4, 115200 }, /* pbn_b2_bt_4_115200 */
+       { SPCI_FL_BASE2 | SPCI_FL_BASE_TABLE, 2, 921600 }, /* pbn_b2_bt_2_921600 */
+
+       { SPCI_FL_BASE2, 2, 921600, /* IOMEM */            /* pbn_panacom */
+               0x400, 7, pci_plx9050_fn },
+       { SPCI_FL_BASE2 | SPCI_FL_BASE_TABLE, 2, 921600,   /* pbn_panacom2 */
+               0x400, 7, pci_plx9050_fn },
+       { SPCI_FL_BASE2 | SPCI_FL_BASE_TABLE, 4, 921600,   /* pbn_panacom4 */
+               0x400, 7, pci_plx9050_fn },
+       { SPCI_FL_BASE2, 4, 921600,                        /* pbn_plx_romulus */
+               0x20, 2, pci_plx9050_fn, 0x03 },
+               /* This board uses the size of PCI Base region 0 to
+                * signal now many ports are available */
+       { SPCI_FL_BASE0 | SPCI_FL_REGION_SZ_CAP, 32, 115200 }, /* pbn_oxsemi */
+       { SPCI_FL_BASE_TABLE, 1, 921600,                   /* pbn_timedia */
+               0, 0, pci_timedia_fn },
+       /* EKF addition for i960 Boards form EKF with serial port */
+       { SPCI_FL_BASE0, 32, 921600, /* max 256 ports */   /* pbn_intel_i960 */
+               8<<2, 2, pci_inteli960ni_fn, 0x10000},
+       { SPCI_FL_BASE0 | SPCI_FL_IRQRESOURCE,             /* pbn_sgi_ioc3 */
+               1, 458333, 0, 0, 0, 0x20178 },
+
+       /*
+        * NEC Vrc-5074 (Nile 4) builtin UART.
+        */
+       { SPCI_FL_BASE0, 1, 520833,                        /* pbn_nec_nile4 */
+               64, 3, NULL, 0x300 },
+
+       { SPCI_FL_BASE3, 4, 115200, 8 },                   /* pbn_dci_pccom4 */
+       { SPCI_FL_BASE3, 8, 115200, 8 },                   /* pbn_dci_pccom8 */
+
+       { SPCI_FL_BASE0, 1, 115200,                        /* pbn_xircom_combo */
+               0, 0, pci_xircom_fn },
+
+       { SPCI_FL_BASE2, 1, 460800,                        /* pbn_siig10x_0 */
+               0, 0, pci_siig10x_fn },
+       { SPCI_FL_BASE2, 1, 921600,                        /* pbn_siig10x_1 */
+               0, 0, pci_siig10x_fn },
+       { SPCI_FL_BASE2 | SPCI_FL_BASE_TABLE, 2, 921600,   /* pbn_siig10x_2 */
+               0, 0, pci_siig10x_fn },
+       { SPCI_FL_BASE2 | SPCI_FL_BASE_TABLE, 4, 921600,   /* pbn_siig10x_4 */
+               0, 0, pci_siig10x_fn },
+       { SPCI_FL_BASE0, 1, 921600,                        /* pbn_siix20x_0 */
+               0, 0, pci_siig20x_fn },
+       { SPCI_FL_BASE0 | SPCI_FL_BASE_TABLE, 2, 921600,   /* pbn_siix20x_2 */
+               0, 0, pci_siig20x_fn },
+       { SPCI_FL_BASE0 | SPCI_FL_BASE_TABLE, 4, 921600,   /* pbn_siix20x_4 */
+               0, 0, pci_siig20x_fn },
+
+       { SPCI_FL_BASE0, 4, 921600, /* IOMEM */            /* pbn_computone_4 */
+               0x40, 2, NULL, 0x200 },
+       { SPCI_FL_BASE0, 6, 921600, /* IOMEM */            /* pbn_computone_6 */
+               0x40, 2, NULL, 0x200 },
+       { SPCI_FL_BASE0, 8, 921600, /* IOMEM */            /* pbn_computone_8 */
+               0x40, 2, NULL, 0x200 },
+};
+
+/*
+ * Given a complete unknown PCI device, try to use some heuristics to
+ * guess what the configuration might be, based on the pitiful PCI
+ * serial specs.  Returns 0 on success, 1 on failure.
+ */
+static int __devinit
+serial_pci_guess_board(struct pci_dev *dev, struct pci_board *board)
+{
+       int num_iomem = 0, num_port = 0, first_port = -1;
+       int i;
+       
+       /*
+        * If it is not a communications device or the programming
+        * interface is greater than 6, give up.
+        *
+        * (Should we try to make guesses for multiport serial devices
+        * later?) 
+        */
+       if ((((dev->class >> 8) != PCI_CLASS_COMMUNICATION_SERIAL) &&
+           ((dev->class >> 8) != PCI_CLASS_COMMUNICATION_MODEM)) ||
+           (dev->class & 0xff) > 6)
+               return 1;
+
+       for (i = 0; i < 6; i++) {
+               if (IS_PCI_REGION_IOPORT(dev, i)) {
+                       num_port++;
+                       if (first_port == -1)
+                               first_port = i;
+               }
+               if (IS_PCI_REGION_IOMEM(dev, i))
+                       num_iomem++;
+       }
+
+       /*
+        * If there is 1 or 0 iomem regions, and exactly one port, use
+        * it.
+        */
+       if (num_iomem <= 1 && num_port == 1) {
+               board->flags = first_port;
+               return 0;
+       }
+       return 1;
+}
+
+/*
+ * return an error code to refuse.
+ *
+ * serial_struct is 60 bytes.
+ */
+static int __devinit pci_init_one(struct pci_dev *dev, const struct pci_device_id *ent)
+{
+       struct serial_private *priv;
+       struct pci_board *board, tmp;
+       struct serial_struct serial_req;
+       int base_baud, rc, k;
+
+       if (ent->driver_data >= ARRAY_SIZE(pci_boards)) {
+               printk(KERN_ERR "pci_init_one: invalid driver_data: %ld\n",
+                       ent->driver_data);
+               return -EINVAL;
+       }
+
+       board = &pci_boards[ent->driver_data];
+
+       rc = pci_enable_device(dev);
+       if (rc)
+               return rc;
+
+       if (ent->driver_data == pbn_default &&
+           serial_pci_guess_board(dev, board))
+               return -ENODEV;
+       else if (serial_pci_guess_board(dev, &tmp) == 0) {
+               printk(KERN_INFO "Redundant entry in serial pci_table.  "
+                      "Please send the output of\n"
+                      "lspci -vv, this message (%d,%d,%d,%d)\n"
+                      "and the manufacturer and name of "
+                      "serial board or modem board\n"
+                      "to serial-pci-info@lists.sourceforge.net.\n",
+                      dev->vendor, dev->device,
+                      pci_get_subvendor(dev), pci_get_subdevice(dev));
+       }
+
+       priv = kmalloc(sizeof(struct serial_private) +
+                             sizeof(unsigned int) * board->num_ports,
+                             GFP_KERNEL);
+       if (!priv)
+               return -ENOMEM;
+
+       /*
+        * Run the initialization function, if any
+        */
+       if (board->init_fn) {
+               rc = board->init_fn(dev, board, 1);
+               if (rc != 0) {
+                       kfree(priv);
+                       return rc;
+               }
+       }
+
+       base_baud = board->base_baud;
+       if (!base_baud)
+               base_baud = BASE_BAUD;
+       memset(&serial_req, 0, sizeof(serial_req));
+       for (k = 0; k < board->num_ports; k++) {
+               serial_req.irq = get_pci_irq(dev, board, k);
+               if (get_pci_port(dev, board, &serial_req, k))
+                       break;
+#ifdef SERIAL_DEBUG_PCI
+               printk("Setup PCI/PNP port: port %x, irq %d, type %d\n",
+                      serial_req.port, serial_req.irq, serial_req.io_type);
+#endif
+               serial_req.flags = ASYNC_SKIP_TEST | ASYNC_AUTOPROBE;
+               serial_req.baud_base = base_baud;
+               
+               priv->line[k] = register_serial(&serial_req);
+               if (priv->line[k] < 0)
+                       break;
+       }
+
+       priv->board = board;
+       priv->nr = k;
+
+       pci_set_drvdata(dev, priv);
+
+       return 0;
+}
+
+static void __devexit pci_remove_one(struct pci_dev *dev)
+{
+       struct serial_private *priv = pci_get_drvdata(dev);
+       int i;
+
+       pci_set_drvdata(dev, NULL);
+
+       if (priv) {
+               for (i = 0; i < priv->nr; i++)
+                       unregister_serial(priv->line[i]);
+
+               priv->board->init_fn(dev, priv->board, 0);
+
+               pci_disable_device(dev);
+
+               kfree(priv);
+       }
+}
+
+static struct pci_device_id serial_pci_tbl[] __devinitdata = {
+       {       PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V960,
+               PCI_SUBVENDOR_ID_CONNECT_TECH,
+               PCI_SUBDEVICE_ID_CONNECT_TECH_BH8_232, 0, 0,
+               pbn_b1_8_1382400 },
+       {       PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V960,
+               PCI_SUBVENDOR_ID_CONNECT_TECH,
+               PCI_SUBDEVICE_ID_CONNECT_TECH_BH4_232, 0, 0,
+               pbn_b1_4_1382400 },
+       {       PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V960,
+               PCI_SUBVENDOR_ID_CONNECT_TECH,
+               PCI_SUBDEVICE_ID_CONNECT_TECH_BH2_232, 0, 0,
+               pbn_b1_2_1382400 },
+       {       PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V351,
+               PCI_SUBVENDOR_ID_CONNECT_TECH,
+               PCI_SUBDEVICE_ID_CONNECT_TECH_BH8_232, 0, 0,
+               pbn_b1_8_1382400 },
+       {       PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V351,
+               PCI_SUBVENDOR_ID_CONNECT_TECH,
+               PCI_SUBDEVICE_ID_CONNECT_TECH_BH4_232, 0, 0,
+               pbn_b1_4_1382400 },
+       {       PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V351,
+               PCI_SUBVENDOR_ID_CONNECT_TECH,
+               PCI_SUBDEVICE_ID_CONNECT_TECH_BH2_232, 0, 0,
+               pbn_b1_2_1382400 },
+       {       PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V351,
+               PCI_SUBVENDOR_ID_CONNECT_TECH,
+               PCI_SUBDEVICE_ID_CONNECT_TECH_BH8_485, 0, 0,
+               pbn_b1_8_921600 },
+       {       PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V351,
+               PCI_SUBVENDOR_ID_CONNECT_TECH,
+               PCI_SUBDEVICE_ID_CONNECT_TECH_BH8_485_4_4, 0, 0,
+               pbn_b1_8_921600 },
+       {       PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V351,
+               PCI_SUBVENDOR_ID_CONNECT_TECH,
+               PCI_SUBDEVICE_ID_CONNECT_TECH_BH4_485, 0, 0,
+               pbn_b1_4_921600 },
+       {       PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V351,
+               PCI_SUBVENDOR_ID_CONNECT_TECH,
+               PCI_SUBDEVICE_ID_CONNECT_TECH_BH4_485_2_2, 0, 0,
+               pbn_b1_4_921600 },
+       {       PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V351,
+               PCI_SUBVENDOR_ID_CONNECT_TECH,
+               PCI_SUBDEVICE_ID_CONNECT_TECH_BH2_485, 0, 0,
+               pbn_b1_2_921600 },
+       {       PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V351,
+               PCI_SUBVENDOR_ID_CONNECT_TECH,
+               PCI_SUBDEVICE_ID_CONNECT_TECH_BH8_485_2_6, 0, 0,
+               pbn_b1_8_921600 },
+       {       PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V351,
+               PCI_SUBVENDOR_ID_CONNECT_TECH,
+               PCI_SUBDEVICE_ID_CONNECT_TECH_BH081101V1, 0, 0,
+               pbn_b1_8_921600 },
+       {       PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V351,
+               PCI_SUBVENDOR_ID_CONNECT_TECH,
+               PCI_SUBDEVICE_ID_CONNECT_TECH_BH041101V1, 0, 0,
+               pbn_b1_4_921600 },
+
+       {       PCI_VENDOR_ID_SEALEVEL, PCI_DEVICE_ID_SEALEVEL_U530,
+               PCI_ANY_ID, PCI_ANY_ID, 0, 0, 
+               pbn_b2_bt_1_115200 },
+       {       PCI_VENDOR_ID_SEALEVEL, PCI_DEVICE_ID_SEALEVEL_UCOMM2,
+               PCI_ANY_ID, PCI_ANY_ID, 0, 0, 
+               pbn_b2_bt_2_115200 },
+       {       PCI_VENDOR_ID_SEALEVEL, PCI_DEVICE_ID_SEALEVEL_UCOMM422,
+               PCI_ANY_ID, PCI_ANY_ID, 0, 0, 
+               pbn_b2_bt_4_115200 },
+       {       PCI_VENDOR_ID_SEALEVEL, PCI_DEVICE_ID_SEALEVEL_UCOMM232,
+               PCI_ANY_ID, PCI_ANY_ID, 0, 0, 
+               pbn_b2_bt_2_115200 },
+       {       PCI_VENDOR_ID_SEALEVEL, PCI_DEVICE_ID_SEALEVEL_COMM4,
+               PCI_ANY_ID, PCI_ANY_ID, 0, 0, 
+               pbn_b2_bt_4_115200 },
+       {       PCI_VENDOR_ID_SEALEVEL, PCI_DEVICE_ID_SEALEVEL_COMM8,
+               PCI_ANY_ID, PCI_ANY_ID, 0, 0, 
+               pbn_b2_8_115200 },
+
+       {       PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_GTEK_SERIAL2,
+               PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+               pbn_b2_bt_2_115200 },
+       {       PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_SPCOM200,
+               PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+               pbn_b2_bt_2_921600 },
+       /* VScom SPCOM800, from sl@s.pl */
+       {       PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_SPCOM800, 
+               PCI_ANY_ID, PCI_ANY_ID, 0, 0, 
+               pbn_b2_8_921600 },
+       {       PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_1077,
+               PCI_ANY_ID, PCI_ANY_ID, 0, 0, 
+               pbn_b2_4_921600 },
+       {       PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9050,
+               PCI_SUBVENDOR_ID_KEYSPAN,
+               PCI_SUBDEVICE_ID_KEYSPAN_SX2, 0, 0,
+               pbn_panacom },
+       {       PCI_VENDOR_ID_PANACOM, PCI_DEVICE_ID_PANACOM_QUADMODEM,
+               PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+               pbn_panacom4 },
+       {       PCI_VENDOR_ID_PANACOM, PCI_DEVICE_ID_PANACOM_DUALMODEM,
+               PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+               pbn_panacom2 },
+       {       PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9050,
+               PCI_SUBVENDOR_ID_CHASE_PCIFAST,
+               PCI_SUBDEVICE_ID_CHASE_PCIFAST4, 0, 0, 
+               pbn_b2_4_460800 },
+       {       PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9050,
+               PCI_SUBVENDOR_ID_CHASE_PCIFAST,
+               PCI_SUBDEVICE_ID_CHASE_PCIFAST8, 0, 0, 
+               pbn_b2_8_460800 },
+       {       PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9050,
+               PCI_SUBVENDOR_ID_CHASE_PCIFAST,
+               PCI_SUBDEVICE_ID_CHASE_PCIFAST16, 0, 0, 
+               pbn_b2_16_460800 },
+       {       PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9050,
+               PCI_SUBVENDOR_ID_CHASE_PCIFAST,
+               PCI_SUBDEVICE_ID_CHASE_PCIFAST16FMC, 0, 0, 
+               pbn_b2_16_460800 },
+       {       PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9050,
+               PCI_SUBVENDOR_ID_CHASE_PCIRAS,
+               PCI_SUBDEVICE_ID_CHASE_PCIRAS4, 0, 0, 
+               pbn_b2_4_460800 },
+       {       PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9050,
+               PCI_SUBVENDOR_ID_CHASE_PCIRAS,
+               PCI_SUBDEVICE_ID_CHASE_PCIRAS8, 0, 0, 
+               pbn_b2_8_460800 },
+       /* Megawolf Romulus PCI Serial Card, from Mike Hudson */
+       /* (Exoray@isys.ca) */
+       {       PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_ROMULUS,
+               0x10b5, 0x106a, 0, 0,
+               pbn_plx_romulus },
+       {       PCI_VENDOR_ID_QUATECH, PCI_DEVICE_ID_QUATECH_QSC100,
+               PCI_ANY_ID, PCI_ANY_ID, 0, 0, 
+               pbn_b1_4_115200 },
+       {       PCI_VENDOR_ID_QUATECH, PCI_DEVICE_ID_QUATECH_DSC100,
+               PCI_ANY_ID, PCI_ANY_ID, 0, 0, 
+               pbn_b1_2_115200 },
+       {       PCI_VENDOR_ID_QUATECH, PCI_DEVICE_ID_QUATECH_ESC100D,
+               PCI_ANY_ID, PCI_ANY_ID, 0, 0, 
+               pbn_b1_8_115200 },
+       {       PCI_VENDOR_ID_QUATECH, PCI_DEVICE_ID_QUATECH_ESC100M,
+               PCI_ANY_ID, PCI_ANY_ID, 0, 0, 
+               pbn_b1_8_115200 },
+       {       PCI_VENDOR_ID_SPECIALIX, PCI_DEVICE_ID_OXSEMI_16PCI954,
+               PCI_VENDOR_ID_SPECIALIX, PCI_SUBDEVICE_ID_SPECIALIX_SPEED4, 0, 0, 
+               pbn_b0_4_921600 },
+       {       PCI_VENDOR_ID_OXSEMI, PCI_DEVICE_ID_OXSEMI_16PCI954,
+               PCI_ANY_ID, PCI_ANY_ID, 0, 0, 
+               pbn_b0_4_115200 },
+       {       PCI_VENDOR_ID_OXSEMI, PCI_DEVICE_ID_OXSEMI_16PCI952,
+               PCI_ANY_ID, PCI_ANY_ID, 0, 0, 
+               pbn_b0_2_115200 },
+
+       /* Digitan DS560-558, from jimd@esoft.com */
+       {       PCI_VENDOR_ID_ATT, PCI_DEVICE_ID_ATT_VENUS_MODEM,
+               PCI_ANY_ID, PCI_ANY_ID, 0, 0, 
+               pbn_b1_1_115200 },
+
+       /* 3Com US Robotics 56k Voice Internal PCI model 5610 */
+       {       PCI_VENDOR_ID_USR, 0x1008,
+               PCI_ANY_ID, PCI_ANY_ID, },
+
+       /* Titan Electronic cards */
+       {       PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_100,
+               PCI_ANY_ID, PCI_ANY_ID, 0, 0, 
+               pbn_b0_1_921600 },
+       {       PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_200,
+               PCI_ANY_ID, PCI_ANY_ID, 0, 0, 
+               pbn_b0_2_921600 },
+       {       PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_400,
+               PCI_ANY_ID, PCI_ANY_ID, 0, 0, 
+               pbn_b0_4_921600 },
+       {       PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_800B,
+               PCI_ANY_ID, PCI_ANY_ID, 0, 0, 
+               pbn_b0_4_921600 },
+       {       PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_100L,
+               PCI_ANY_ID, PCI_ANY_ID,
+               SPCI_FL_BASE1, 1, 921600 },
+       {       PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_200L,
+               PCI_ANY_ID, PCI_ANY_ID,
+               SPCI_FL_BASE1 | SPCI_FL_BASE_TABLE, 2, 921600 },
+       /* The 400L and 800L have a custom hack in get_pci_port */
+       {       PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_400L,
+               PCI_ANY_ID, PCI_ANY_ID,
+               SPCI_FL_BASE_TABLE, 4, 921600 },
+       {       PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_800L,
+               PCI_ANY_ID, PCI_ANY_ID,
+               SPCI_FL_BASE_TABLE, 8, 921600 },
+
+       {       PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S_10x_550,
+               PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+               pbn_siig10x_0 },
+       {       PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S_10x_650,
+               PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+               pbn_siig10x_0 },
+       {       PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S_10x_850,
+               PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+               pbn_siig10x_0 },
+       {       PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S1P_10x_550,
+               PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+               pbn_siig10x_1 },
+       {       PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S1P_10x_650,
+               PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+               pbn_siig10x_1 },
+       {       PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S1P_10x_850,
+               PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+               pbn_siig10x_1 },
+       {       PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S_10x_550,
+               PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+               pbn_siig10x_2 },
+       {       PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S_10x_650,
+               PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+               pbn_siig10x_2 },
+       {       PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S_10x_850,
+               PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+               pbn_siig10x_2 },
+       {       PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S1P_10x_550,
+               PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+               pbn_siig10x_2 },
+       {       PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S1P_10x_650,
+               PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+               pbn_siig10x_2 },
+       {       PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S1P_10x_850,
+               PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+               pbn_siig10x_2 },
+       {       PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_4S_10x_550,
+               PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+               pbn_siig10x_4 },
+       {       PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_4S_10x_650,
+               PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+               pbn_siig10x_4 },
+       {       PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_4S_10x_850,
+               PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+               pbn_siig10x_4 },
+       {       PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S_20x_550,
+               PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+               pbn_siig20x_0 },
+       {       PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S_20x_650,
+               PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+               pbn_siig20x_0 },
+       {       PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S_20x_850,
+               PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+               pbn_siig20x_0 },
+       {       PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S1P_20x_550,
+               PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+               pbn_siig20x_0 },
+       {       PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S1P_20x_650,
+               PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+               pbn_siig20x_0 },
+       {       PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S1P_20x_850,
+               PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+               pbn_siig20x_0 },
+       {       PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2P1S_20x_550,
+               PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+               pbn_siig20x_0 },
+       {       PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2P1S_20x_650,
+               PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+               pbn_siig20x_0 },
+       {       PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2P1S_20x_850,
+               PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+               pbn_siig20x_0 },
+       {       PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S_20x_550,
+               PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+               pbn_siig20x_2 },
+       {       PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S_20x_650,
+               PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+               pbn_siig20x_2 },
+       {       PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S_20x_850,
+               PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+               pbn_siig20x_2 },
+       {       PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S1P_20x_550,
+               PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+               pbn_siig20x_2 },
+       {       PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S1P_20x_650,
+               PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+               pbn_siig20x_2 },
+       {       PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S1P_20x_850,
+               PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+               pbn_siig20x_2 },
+       {       PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_4S_20x_550,
+               PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+               pbn_siig20x_4 },
+       {       PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_4S_20x_650,
+               PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+               pbn_siig20x_4 },
+       {       PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_4S_20x_850,
+               PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+               pbn_siig20x_4 },
+
+       /* Computone devices submitted by Doug McNash dmcnash@computone.com */
+       {       PCI_VENDOR_ID_COMPUTONE, PCI_DEVICE_ID_COMPUTONE_PG,
+               PCI_SUBVENDOR_ID_COMPUTONE, PCI_SUBDEVICE_ID_COMPUTONE_PG4,
+               0, 0, pbn_computone_4 },
+       {       PCI_VENDOR_ID_COMPUTONE, PCI_DEVICE_ID_COMPUTONE_PG,
+               PCI_SUBVENDOR_ID_COMPUTONE, PCI_SUBDEVICE_ID_COMPUTONE_PG8,
+               0, 0, pbn_computone_8 },
+       {       PCI_VENDOR_ID_COMPUTONE, PCI_DEVICE_ID_COMPUTONE_PG,
+               PCI_SUBVENDOR_ID_COMPUTONE, PCI_SUBDEVICE_ID_COMPUTONE_PG6,
+               0, 0, pbn_computone_6 },
+
+       {       PCI_VENDOR_ID_OXSEMI, PCI_DEVICE_ID_OXSEMI_16PCI95N,
+               PCI_ANY_ID, PCI_ANY_ID, 0, 0, pbn_oxsemi },
+       {       PCI_VENDOR_ID_TIMEDIA, PCI_DEVICE_ID_TIMEDIA_1889,
+               PCI_VENDOR_ID_TIMEDIA, PCI_ANY_ID, 0, 0, pbn_timedia },
+
+       {       PCI_VENDOR_ID_LAVA, PCI_DEVICE_ID_LAVA_DSERIAL,
+               PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+               pbn_b0_bt_2_115200 },
+       /* AFAVLAB serial card, from Harald Welte <laforge@gnumonks.org> */
+       {       PCI_VENDOR_ID_AFAVLAB, PCI_DEVICE_ID_AFAVLAB_P028,
+               PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+               pbn_b0_bt_8_115200 },
+
+       {       PCI_VENDOR_ID_LAVA, PCI_DEVICE_ID_LAVA_QUATRO_A,
+               PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+               pbn_b0_bt_2_115200 },
+       {       PCI_VENDOR_ID_LAVA, PCI_DEVICE_ID_LAVA_QUATRO_B,
+               PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+               pbn_b0_bt_2_115200 },
+       {       PCI_VENDOR_ID_LAVA, PCI_DEVICE_ID_LAVA_PORT_PLUS,
+               PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+               pbn_b0_bt_2_460800 },
+       {       PCI_VENDOR_ID_LAVA, PCI_DEVICE_ID_LAVA_QUAD_A,
+               PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+               pbn_b0_bt_2_460800 },
+       {       PCI_VENDOR_ID_LAVA, PCI_DEVICE_ID_LAVA_QUAD_B,
+               PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+               pbn_b0_bt_2_460800 },
+       {       PCI_VENDOR_ID_LAVA, PCI_DEVICE_ID_LAVA_SSERIAL,
+               PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+               pbn_b0_bt_1_115200 },
+       {       PCI_VENDOR_ID_LAVA, PCI_DEVICE_ID_LAVA_PORT_650,
+               PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+               pbn_b0_bt_1_460800 },
+
+       /* RAStel 2 port modem, gerg@moreton.com.au */
+       {       PCI_VENDOR_ID_MORETON, PCI_DEVICE_ID_RASTEL_2PORT,
+               PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+               pbn_b2_bt_2_115200 },
+
+       /* EKF addition for i960 Boards form EKF with serial port */
+       {       PCI_VENDOR_ID_INTEL, 0x1960,
+               0xE4BF, PCI_ANY_ID, 0, 0,
+               pbn_intel_i960 },
+
+       /* Xircom Cardbus/Ethernet combos */
+       {       PCI_VENDOR_ID_XIRCOM, PCI_DEVICE_ID_XIRCOM_X3201_MDM,
+               PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+               pbn_xircom_combo },
+
+       /*
+        * Untested PCI modems, sent in from various folks...
+        */
+
+       /* Elsa Model 56K PCI Modem, from Andreas Rath <arh@01019freenet.de> */
+       {       PCI_VENDOR_ID_ROCKWELL, 0x1004,
+               0x1048, 0x1500, 0, 0,
+               pbn_b1_1_115200 },
+
+       {       PCI_VENDOR_ID_SGI, PCI_DEVICE_ID_SGI_IOC3,
+               0xFF00, 0, 0, 0,
+               pbn_sgi_ioc3 },
+
+       /*
+        * NEC Vrc-5074 (Nile 4) builtin UART.
+        */
+       {       PCI_VENDOR_ID_NEC, PCI_DEVICE_ID_NEC_NILE4,
+               PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+               pbn_nec_nile4 },
+
+       {       PCI_VENDOR_ID_DCI, PCI_DEVICE_ID_DCI_PCCOM4,
+               PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+               pbn_dci_pccom4 },
+       {       PCI_VENDOR_ID_DCI, PCI_DEVICE_ID_DCI_PCCOM8,
+               PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+               pbn_dci_pccom8 },
+
+       {       PCI_ANY_ID, PCI_ANY_ID,
+               PCI_ANY_ID, PCI_ANY_ID,
+               PCI_CLASS_COMMUNICATION_SERIAL << 8,
+               0xffff00, },
+       {       PCI_ANY_ID, PCI_ANY_ID,
+               PCI_ANY_ID, PCI_ANY_ID,
+               PCI_CLASS_COMMUNICATION_MODEM << 8,
+               0xffff00, },
+       {       PCI_ANY_ID, PCI_ANY_ID,
+               PCI_ANY_ID, PCI_ANY_ID,
+               PCI_CLASS_COMMUNICATION_MULTISERIAL << 8,
+               0xffff00, },
+       { 0, }
+};
+
+#ifndef __devexit_p
+#if defined(MODULE) || defined(CONFIG_HOTPLUG)
+#define __devexit_p(x) x
+#else
+#define __devexit_p(x) NULL
+#endif
+#endif
+
+static struct pci_driver serial_pci_driver = {
+       name:           "serial",
+       probe:          pci_init_one,
+       remove:         __devexit_p(pci_remove_one),
+       id_table:       serial_pci_tbl,
+};
+
+static int __init serial8250_pci_init(void)
+{
+       return pci_module_init(&serial_pci_driver);
+}
+
+static void __exit serial8250_pci_exit(void)
+{
+       pci_unregister_driver(&serial_pci_driver);
+}
+
+module_init(serial8250_pci_init);
+module_exit(serial8250_pci_exit);
+
+EXPORT_NO_SYMBOLS;
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Generic 8250/16x50 PCI serial probe module");
+MODULE_DEVICE_TABLE(pci, serial_pci_tbl);
diff --git a/drivers/serial/serial_8250_pnp.c b/drivers/serial/serial_8250_pnp.c
new file mode 100644 (file)
index 0000000..3715959
--- /dev/null
@@ -0,0 +1,548 @@
+/*
+ *  linux/drivers/char/serial_8250_pnp.c
+ *
+ *  Probe module for 8250/16550-type ISAPNP serial ports.
+ *
+ *  Based on drivers/char/serial.c, by Linus Torvalds, Theodore Ts'o.
+ *
+ *  Copyright (C) 2001 Russell King, All Rights Reserved.
+ *
+ * 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.
+ *
+ *  $Id: serial_8250_pnp.c,v 1.9 2002/02/18 19:20:29 rmk Exp $
+ */
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/pci.h>
+#include <linux/isapnp.h>
+#include <linux/string.h>
+#include <linux/kernel.h>
+#include <linux/serial.h>
+#include <linux/serialP.h>
+
+#include <asm/bitops.h>
+#include <asm/byteorder.h>
+#include <asm/serial.h>
+
+#include "serial_8250.h"
+
+struct pnpbios_device_id
+{
+       char id[8];
+       unsigned long driver_data;
+};
+
+static const struct pnpbios_device_id pnp_dev_table[] = {
+       /* Archtek America Corp. */
+       /* Archtek SmartLink Modem 3334BT Plug & Play */
+       {       "AAC000F",              0       },
+       /* Anchor Datacomm BV */
+       /* SXPro 144 External Data Fax Modem Plug & Play */
+       {       "ADC0001",              0       },
+       /* SXPro 288 External Data Fax Modem Plug & Play */
+       {       "ADC0002",              0       },
+       /* Rockwell 56K ACF II Fax+Data+Voice Modem */
+       {       "AKY1021",              SPCI_FL_NO_SHIRQ        },
+       /* AZT3005 PnP SOUND DEVICE */
+       {       "AZT4001",              0       },
+       /* Best Data Products Inc. Smart One 336F PnP Modem */
+       {       "BDP3336",              0       },
+       /*  Boca Research */
+       /* Boca Complete Ofc Communicator 14.4 Data-FAX */
+       {       "BRI0A49",              0       },
+       /* Boca Research 33,600 ACF Modem */
+       {       "BRI1400",              0       },
+       /* Boca 33.6 Kbps Internal FD34FSVD */
+       {       "BRI3400",              0       },
+       /* Boca 33.6 Kbps Internal FD34FSVD */
+       {       "BRI0A49",              0       },
+       /* Best Data Products Inc. Smart One 336F PnP Modem */
+       {       "BDP3336",              0       },
+       /* Computer Peripherals Inc */
+       /* EuroViVa CommCenter-33.6 SP PnP */
+       {       "CPI4050",              0       },
+       /* Creative Labs */
+       /* Creative Labs Phone Blaster 28.8 DSVD PnP Voice */
+       {       "CTL3001",              0       },
+       /* Creative Labs Modem Blaster 28.8 DSVD PnP Voice */
+       {       "CTL3011",              0       },
+       /* Creative */
+       /* Creative Modem Blaster Flash56 DI5601-1 */
+       {       "DMB1032",              0       },
+       /* Creative Modem Blaster V.90 DI5660 */
+       {       "DMB2001",              0       },
+       /* FUJITSU */
+       /* Fujitsu 33600 PnP-I2 R Plug & Play */
+       {       "FUJ0202",              0       },
+       /* Fujitsu FMV-FX431 Plug & Play */
+       {       "FUJ0205",              0       },
+       /* Fujitsu 33600 PnP-I4 R Plug & Play */
+       {       "FUJ0206",              0       },
+       /* Fujitsu Fax Voice 33600 PNP-I5 R Plug & Play */
+       {       "FUJ0209",              0       },
+       /* Archtek America Corp. */
+       /* Archtek SmartLink Modem 3334BT Plug & Play */
+       {       "GVC000F",              0       },
+       /* Hayes */
+       /* Hayes Optima 288 V.34-V.FC + FAX + Voice Plug & Play */
+       {       "HAY0001",              0       },
+       /* Hayes Optima 336 V.34 + FAX + Voice PnP */
+       {       "HAY000C",              0       },
+       /* Hayes Optima 336B V.34 + FAX + Voice PnP */
+       {       "HAY000D",              0       },
+       /* Hayes Accura 56K Ext Fax Modem PnP */
+       {       "HAY5670",              0       },
+       /* Hayes Accura 56K Ext Fax Modem PnP */
+       {       "HAY5674",              0       },
+       /* Hayes Accura 56K Fax Modem PnP */
+       {       "HAY5675",              0       },
+       /* Hayes 288, V.34 + FAX */
+       {       "HAYF000",              0       },
+       /* Hayes Optima 288 V.34 + FAX + Voice, Plug & Play */
+       {       "HAYF001",              0       },
+       /* IBM */
+       /* IBM Thinkpad 701 Internal Modem Voice */
+       {       "IBM0033",              0       },
+       /* Intertex */
+       /* Intertex 28k8 33k6 Voice EXT PnP */
+       {       "IXDC801",              0       },
+       /* Intertex 33k6 56k Voice EXT PnP */
+       {       "IXDC901",              0       },
+       /* Intertex 28k8 33k6 Voice SP EXT PnP */
+       {       "IXDD801",              0       },
+       /* Intertex 33k6 56k Voice SP EXT PnP */
+       {       "IXDD901",              0       },
+       /* Intertex 28k8 33k6 Voice SP INT PnP */
+       {       "IXDF401",              0       },
+       /* Intertex 28k8 33k6 Voice SP EXT PnP */
+       {       "IXDF801",              0       },
+       /* Intertex 33k6 56k Voice SP EXT PnP */
+       {       "IXDF901",              0       },
+       /* Kortex International */
+       /* KORTEX 28800 Externe PnP */
+       {       "KOR4522",              0       },
+       /* KXPro 33.6 Vocal ASVD PnP */
+       {       "KORF661",              0       },
+       /* Lasat */
+       /* LASAT Internet 33600 PnP */
+       {       "LAS4040",              0       },
+       /* Lasat Safire 560 PnP */
+       {       "LAS4540",              0       },
+       /* Lasat Safire 336  PnP */
+       {       "LAS5440",              0       },
+       /* Microcom, Inc. */
+       /* Microcom TravelPorte FAST V.34 Plug & Play */
+       {       "MNP0281",              0       },
+       /* Microcom DeskPorte V.34 FAST or FAST+ Plug & Play */
+       {       "MNP0336",              0       },
+       /* Microcom DeskPorte FAST EP 28.8 Plug & Play */
+       {       "MNP0339",              0       },
+       /* Microcom DeskPorte 28.8P Plug & Play */
+       {       "MNP0342",              0       },
+       /* Microcom DeskPorte FAST ES 28.8 Plug & Play */
+       {       "MNP0500",              0       },
+       /* Microcom DeskPorte FAST ES 28.8 Plug & Play */
+       {       "MNP0501",              0       },
+       /* Microcom DeskPorte 28.8S Internal Plug & Play */
+       {       "MNP0502",              0       },
+       /* Motorola */
+       /* Motorola BitSURFR Plug & Play */
+       {       "MOT1105",              0       },
+       /* Motorola TA210 Plug & Play */
+       {       "MOT1111",              0       },
+       /* Motorola HMTA 200 (ISDN) Plug & Play */
+       {       "MOT1114",              0       },
+       /* Motorola BitSURFR Plug & Play */
+       {       "MOT1115",              0       },
+       /* Motorola Lifestyle 28.8 Internal */
+       {       "MOT1190",              0       },
+       /* Motorola V.3400 Plug & Play */
+       {       "MOT1501",              0       },
+       /* Motorola Lifestyle 28.8 V.34 Plug & Play */
+       {       "MOT1502",              0       },
+       /* Motorola Power 28.8 V.34 Plug & Play */
+       {       "MOT1505",              0       },
+       /* Motorola ModemSURFR External 28.8 Plug & Play */
+       {       "MOT1509",              0       },
+       /* Motorola Premier 33.6 Desktop Plug & Play */
+       {       "MOT150A",              0       },
+       /* Motorola VoiceSURFR 56K External PnP */
+       {       "MOT150F",              0       },
+       /* Motorola ModemSURFR 56K External PnP */
+       {       "MOT1510",              0       },
+       /* Motorola ModemSURFR 56K Internal PnP */
+       {       "MOT1550",              0       },
+       /* Motorola ModemSURFR Internal 28.8 Plug & Play */
+       {       "MOT1560",              0       },
+       /* Motorola Premier 33.6 Internal Plug & Play */
+       {       "MOT1580",              0       },
+       /* Motorola OnlineSURFR 28.8 Internal Plug & Play */
+       {       "MOT15B0",              0       },
+       /* Motorola VoiceSURFR 56K Internal PnP */
+       {       "MOT15F0",              0       },
+       /* Com 1 */
+       /*  Deskline K56 Phone System PnP */
+       {       "MVX00A1",              0       },
+       /* PC Rider K56 Phone System PnP */
+       {       "MVX00F2",              0       },
+       /* Pace 56 Voice Internal Plug & Play Modem */
+       {       "PMC2430",              0       },
+       /* Generic */
+       /* Generic standard PC COM port  */
+       {       "PNP0500",              0       },
+       /* Generic 16550A-compatible COM port */
+       {       "PNP0501",              0       },
+       /* Compaq 14400 Modem */
+       {       "PNPC000",              0       },
+       /* Compaq 2400/9600 Modem */
+       {       "PNPC001",              0       },
+       /* Dial-Up Networking Serial Cable between 2 PCs */
+       {       "PNPC031",              0       },
+       /* Dial-Up Networking Parallel Cable between 2 PCs */
+       {       "PNPC032",              0       },
+       /* Standard 9600 bps Modem */
+       {       "PNPC100",              0       },
+       /* Standard 14400 bps Modem */
+       {       "PNPC101",              0       },
+       /*  Standard 28800 bps Modem*/
+       {       "PNPC102",              0       },
+       /*  Standard Modem*/
+       {       "PNPC103",              0       },
+       /*  Standard 9600 bps Modem*/
+       {       "PNPC104",              0       },
+       /*  Standard 14400 bps Modem*/
+       {       "PNPC105",              0       },
+       /*  Standard 28800 bps Modem*/
+       {       "PNPC106",              0       },
+       /*  Standard Modem */
+       {       "PNPC107",              0       },
+       /* Standard 9600 bps Modem */
+       {       "PNPC108",              0       },
+       /* Standard 14400 bps Modem */
+       {       "PNPC109",              0       },
+       /* Standard 28800 bps Modem */
+       {       "PNPC10A",              0       },
+       /* Standard Modem */
+       {       "PNPC10B",              0       },
+       /* Standard 9600 bps Modem */
+       {       "PNPC10C",              0       },
+       /* Standard 14400 bps Modem */
+       {       "PNPC10D",              0       },
+       /* Standard 28800 bps Modem */
+       {       "PNPC10E",              0       },
+       /* Standard Modem */
+       {       "PNPC10F",              0       },
+       /* Standard PCMCIA Card Modem */
+       {       "PNP2000",              0       },
+       /* Rockwell */
+       /* Modular Technology */
+       /* Rockwell 33.6 DPF Internal PnP */
+       /* Modular Technology 33.6 Internal PnP */
+       {       "ROK0030",              0       },
+       /* Kortex International */
+       /* KORTEX 14400 Externe PnP */
+       {       "ROK0100",              0       },
+       /* Viking Components, Inc */
+       /* Viking 28.8 INTERNAL Fax+Data+Voice PnP */
+       {       "ROK4920",              0       },
+       /* Rockwell */
+       /* British Telecom */
+       /* Modular Technology */
+       /* Rockwell 33.6 DPF External PnP */
+       /* BT Prologue 33.6 External PnP */
+       /* Modular Technology 33.6 External PnP */
+       {       "RSS00A0",              0       },
+       /* Viking 56K FAX INT */
+       {       "RSS0262",              0       },
+       /* SupraExpress 28.8 Data/Fax PnP modem */
+       {       "SUP1310",              0       },
+       /* SupraExpress 33.6 Data/Fax PnP modem */
+       {       "SUP1421",              0       },
+       /* SupraExpress 33.6 Data/Fax PnP modem */
+       {       "SUP1590",              0       },
+       /* SupraExpress 33.6 Data/Fax PnP modem */
+       {       "SUP1760",              0       },
+       /* Phoebe Micro */
+       /* Phoebe Micro 33.6 Data Fax 1433VQH Plug & Play */
+       {       "TEX0011",              0       },
+       /* Archtek America Corp. */
+       /* Archtek SmartLink Modem 3334BT Plug & Play */
+       {       "UAC000F",              0       },
+       /* 3Com Corp. */
+       /* Gateway Telepath IIvi 33.6 */
+       {       "USR0000",              0       },
+       /*  Sportster Vi 14.4 PnP FAX Voicemail */
+       {       "USR0004",              0       },
+       /* U.S. Robotics 33.6K Voice INT PnP */
+       {       "USR0006",              0       },
+       /* U.S. Robotics 33.6K Voice EXT PnP */
+       {       "USR0007",              0       },
+       /* U.S. Robotics 33.6K Voice INT PnP */
+       {       "USR2002",              0       },
+       /* U.S. Robotics 56K Voice INT PnP */
+       {       "USR2070",              0       },
+       /* U.S. Robotics 56K Voice EXT PnP */
+       {       "USR2080",              0       },
+       /* U.S. Robotics 56K FAX INT */
+       {       "USR3031",              0       },
+       /* U.S. Robotics 56K Voice INT PnP */
+       {       "USR3070",              0       },
+       /* U.S. Robotics 56K Voice EXT PnP */
+       {       "USR3080",              0       },
+       /* U.S. Robotics 56K Voice INT PnP */
+       {       "USR3090",              0       },
+       /* U.S. Robotics 56K Message  */
+       {       "USR9100",              0       },
+       /* U.S. Robotics 56K FAX EXT PnP*/
+       {       "USR9160",              0       },
+       /* U.S. Robotics 56K FAX INT PnP*/
+       {       "USR9170",              0       },
+       /* U.S. Robotics 56K Voice EXT PnP*/
+       {       "USR9180",              0       },
+       /* U.S. Robotics 56K Voice INT PnP*/
+       {       "USR9190",              0       },
+       {       "",                     0       }
+};
+
+static void inline avoid_irq_share(struct pci_dev *dev)
+{
+       unsigned int map = 0x1FF8;
+       struct isapnp_irq *irq;
+       struct isapnp_resources *res = dev->sysdata;
+
+       serial8250_get_irq_map(&map);
+
+       for ( ; res; res = res->alt)
+               for (irq = res->irq; irq; irq = irq->next)
+                       irq->map = map;
+}
+
+static char *modem_names[] __devinitdata = {
+       "MODEM", "Modem", "modem", "FAX", "Fax", "fax",
+       "56K", "56k", "K56", "33.6", "28.8", "14.4",
+       "33,600", "28,800", "14,400", "33.600", "28.800", "14.400",
+       "33600", "28800", "14400", "V.90", "V.34", "V.32", 0
+};
+
+static int __devinit check_name(char *name)
+{
+       char **tmp;
+
+       for (tmp = modem_names; *tmp; tmp++)
+               if (strstr(name, *tmp))
+                       return 1;
+
+       return 0;
+}
+
+static int inline check_compatible_id(struct pci_dev *dev)
+{
+       int i;
+       for (i = 0; i < DEVICE_COUNT_COMPATIBLE; i++)
+               if ((dev->vendor_compatible[i] ==
+                    ISAPNP_VENDOR('P', 'N', 'P')) &&
+                   (swab16(dev->device_compatible[i]) >= 0xc000) &&
+                   (swab16(dev->device_compatible[i]) <= 0xdfff))
+                       return 0;
+       return 1;
+}
+
+/*
+ * Given a complete unknown ISA PnP device, try to use some heuristics to
+ * detect modems. Currently use such heuristic set:
+ *     - dev->name or dev->bus->name must contain "modem" substring;
+ *     - device must have only one IO region (8 byte long) with base adress
+ *       0x2e8, 0x3e8, 0x2f8 or 0x3f8.
+ *
+ * Such detection looks very ugly, but can detect at least some of numerous
+ * ISA PnP modems, alternatively we must hardcode all modems in pnp_devices[]
+ * table.
+ */
+static int serial_pnp_guess_board(struct pci_dev *dev, int *flags)
+{
+       struct isapnp_resources *res = (struct isapnp_resources *)dev->sysdata;
+       struct isapnp_resources *resa;
+
+       if (!(check_name(dev->name) || check_name(dev->bus->name)) &&
+           !(check_compatible_id(dev)))
+               return -ENODEV;
+
+       if (!res || res->next)
+               return -ENODEV;
+
+       for (resa = res->alt; resa; resa = resa->alt) {
+               struct isapnp_port *port;
+               for (port = res->port; port; port = port->next)
+                       if ((port->size == 8) &&
+                           ((port->min == 0x2f8) ||
+                            (port->min == 0x3f8) ||
+                            (port->min == 0x2e8) ||
+                            (port->min == 0x3e8)))
+                               return 0;
+       }
+
+       return -ENODEV;
+}
+
+static int
+pnp_init_one(struct pci_dev *dev, const struct pnpbios_device_id *ent,
+            char *slot_name)
+{
+       struct serial_struct serial_req;
+       int ret, line, flags = ent ? ent->driver_data : 0;
+
+       if (!ent) {
+               ret = serial_pnp_guess_board(dev, &flags);
+               if (ret)
+                       return ret;
+       }
+
+       if (dev->prepare(dev) < 0) {
+               printk("serial: PNP device '%s' prepare failed\n",
+                       slot_name);
+               return -ENODEV;
+       }
+
+       if (dev->active)
+               return -ENODEV;
+
+       if (flags & SPCI_FL_NO_SHIRQ)
+               avoid_irq_share(dev);
+
+       if (dev->activate(dev) < 0) {
+               printk("serial: PNP device '%s' activate failed\n",
+                       slot_name);
+               return -ENODEV;
+       }
+
+       memset(&serial_req, 0, sizeof(serial_req));
+       serial_req.irq = dev->irq_resource[0].start;
+       serial_req.port = pci_resource_start(dev, 0);
+       if (HIGH_BITS_OFFSET)
+               serial_req.port = pci_resource_start(dev, 0) >> HIGH_BITS_OFFSET;
+
+#ifdef SERIAL_DEBUG_PNP
+       printk("Setup PNP port: port %x, irq %d, type %d\n",
+              serial_req.port, serial_req.irq, serial_req.io_type);
+#endif
+
+       serial_req.flags = ASYNC_SKIP_TEST | ASYNC_AUTOPROBE;
+       serial_req.baud_base = 115200;
+       line = register_serial(&serial_req);
+
+       if (line >= 0) {
+               pci_set_drvdata(dev, (void *)(line + 1));
+
+               /*
+                * Public health warning: remove this once the 2.5
+                * pnpbios_module_init() stuff is incorporated.
+                */
+               dev->driver = (void *)pnp_dev_table;
+       } else
+               dev->deactivate(dev);
+
+       return line >= 0 ? 0 : -ENODEV;
+}
+
+static void pnp_remove_one(struct pci_dev *dev)
+{
+       int line = (int)pci_get_drvdata(dev);
+
+       if (line) {
+               pci_set_drvdata(dev, NULL);
+
+               unregister_serial(line - 1);
+
+               dev->deactivate(dev);
+       }
+}
+
+static char hex[] = "0123456789ABCDEF";
+
+/*
+ * This function should vanish when 2.5 comes around and
+ * we have pnpbios_module_init()
+ */
+static int pnp_init(void)
+{
+       const struct pnpbios_device_id *id;
+       struct pci_dev *dev = NULL;
+       int nr = 0, rc = -ENODEV;
+
+#ifdef SERIAL_DEBUG_PNP
+       printk("Entered probe_serial_pnp()\n");
+#endif
+
+       isapnp_for_each_dev(dev) {
+               char slot_name[8];
+               u32 pnpid;
+
+               if (dev->active)
+                       continue;
+
+               pnpid = dev->vendor << 16 | dev->device;
+               pnpid = cpu_to_le32(pnpid);
+
+#define HEX(id,a) hex[((id)>>a) & 15]
+#define CHAR(id,a) (0x40 + (((id)>>a) & 31))
+               slot_name[0] = CHAR(pnpid, 26);
+               slot_name[1] = CHAR(pnpid, 21);
+               slot_name[2] = CHAR(pnpid, 16);
+               slot_name[3] = HEX(pnpid, 12);
+               slot_name[4] = HEX(pnpid, 8);
+               slot_name[5] = HEX(pnpid, 4);
+               slot_name[6] = HEX(pnpid, 0);
+               slot_name[7] = '\0';
+               
+               for (id = pnp_dev_table; id->id[0]; id++)
+                       if (memcmp(id->id, slot_name, 7) == 0)
+                               break;
+
+               if (id->id[0])
+                       rc = pnp_init_one(dev, id, slot_name);
+               else
+                       rc = pnp_init_one(dev, NULL, slot_name);
+
+               if (rc == 0)
+                       nr++;
+       }
+
+#ifdef SERIAL_DEBUG_PNP
+       printk("Leaving probe_serial_pnp() (probe finished)\n");
+#endif
+
+       return nr == 0 ? rc : 0;
+}
+
+static int __init serial8250_pnp_init(void)
+{
+       if (!isapnp_present()) {
+#ifdef SERIAL_DEBUG_PNP
+               printk("Leaving probe_serial_pnp() (no isapnp)\n");
+#endif
+               return -ENODEV;
+       }
+       return pnp_init();
+}
+
+static void __exit serial8250_pnp_exit(void)
+{
+       struct pci_dev *dev = NULL;
+
+       isapnp_for_each_dev(dev) {
+               if (dev->driver != (void *)pnp_dev_table)
+                       continue;
+               pnp_remove_one(dev);
+       }
+}
+
+module_init(serial8250_pnp_init);
+module_exit(serial8250_pnp_exit);
+
+EXPORT_NO_SYMBOLS;
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Generic 8250/16x50 PNPBIOS serial probe module");
+MODULE_DEVICE_TABLE(pnpbios, pnp_dev_table);
diff --git a/drivers/serial/serial_amba.c b/drivers/serial/serial_amba.c
new file mode 100644 (file)
index 0000000..016f717
--- /dev/null
@@ -0,0 +1,783 @@
+/*
+ *  linux/drivers/char/serial_amba.c
+ *
+ *  Driver for AMBA serial ports
+ *
+ *  Based on drivers/char/serial.c, by Linus Torvalds, Theodore Ts'o.
+ *
+ *  Copyright 1999 ARM Limited
+ *  Copyright (C) 2000 Deep Blue Solutions Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ *  $Id: serial_amba.c,v 1.35 2002/07/21 08:57:55 rmk Exp $
+ *
+ * This is a generic driver for ARM AMBA-type serial ports.  They
+ * have a lot of 16550-like features, but are not register compatable.
+ * Note that although they do have CTS, DCD and DSR inputs, they do
+ * not have an RI input, nor do they have DTR or RTS outputs.  If
+ * required, these have to be supplied via some other means (eg, GPIO)
+ * and hooked into this driver.
+ */
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <linux/signal.h>
+#include <linux/sched.h>
+#include <linux/interrupt.h>
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
+#include <linux/major.h>
+#include <linux/string.h>
+#include <linux/fcntl.h>
+#include <linux/ptrace.h>
+#include <linux/ioport.h>
+#include <linux/mm.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <linux/circ_buf.h>
+#include <linux/serial.h>
+#include <linux/console.h>
+#include <linux/sysrq.h>
+
+#include <asm/system.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/uaccess.h>
+#include <asm/bitops.h>
+
+#if defined(CONFIG_SERIAL_AMBA_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ)
+#define SUPPORT_SYSRQ
+#endif
+
+#include <linux/serial_core.h>
+
+#include <asm/hardware/serial_amba.h>
+
+#define UART_NR                2
+
+#define SERIAL_AMBA_MAJOR      204
+#define SERIAL_AMBA_MINOR      16
+#define SERIAL_AMBA_NR         UART_NR
+
+#define AMBA_ISR_PASS_LIMIT    256
+
+/*
+ * Access macros for the AMBA UARTs
+ */
+#define UART_GET_INT_STATUS(p) readb((p)->membase + AMBA_UARTIIR)
+#define UART_PUT_ICR(p, c)     writel((c), (p)->membase + AMBA_UARTICR)
+#define UART_GET_FR(p)         readb((p)->membase + AMBA_UARTFR)
+#define UART_GET_CHAR(p)       readb((p)->membase + AMBA_UARTDR)
+#define UART_PUT_CHAR(p, c)    writel((c), (p)->membase + AMBA_UARTDR)
+#define UART_GET_RSR(p)                readb((p)->membase + AMBA_UARTRSR)
+#define UART_GET_CR(p)         readb((p)->membase + AMBA_UARTCR)
+#define UART_PUT_CR(p,c)       writel((c), (p)->membase + AMBA_UARTCR)
+#define UART_GET_LCRL(p)       readb((p)->membase + AMBA_UARTLCR_L)
+#define UART_PUT_LCRL(p,c)     writel((c), (p)->membase + AMBA_UARTLCR_L)
+#define UART_GET_LCRM(p)       readb((p)->membase + AMBA_UARTLCR_M)
+#define UART_PUT_LCRM(p,c)     writel((c), (p)->membase + AMBA_UARTLCR_M)
+#define UART_GET_LCRH(p)       readb((p)->membase + AMBA_UARTLCR_H)
+#define UART_PUT_LCRH(p,c)     writel((c), (p)->membase + AMBA_UARTLCR_H)
+#define UART_RX_DATA(s)                (((s) & AMBA_UARTFR_RXFE) == 0)
+#define UART_TX_READY(s)       (((s) & AMBA_UARTFR_TXFF) == 0)
+#define UART_TX_EMPTY(p)       ((UART_GET_FR(p) & AMBA_UARTFR_TMSK) == 0)
+
+#define UART_DUMMY_RSR_RX      256
+#define UART_PORT_SIZE         64
+
+/*
+ * On the Integrator platform, the port RTS and DTR are provided by
+ * bits in the following SC_CTRLS register bits:
+ *        RTS  DTR
+ *  UART0  7    6
+ *  UART1  5    4
+ */
+#define SC_CTRLC       (IO_ADDRESS(INTEGRATOR_SC_BASE) + INTEGRATOR_SC_CTRLC_OFFSET)
+#define SC_CTRLS       (IO_ADDRESS(INTEGRATOR_SC_BASE) + INTEGRATOR_SC_CTRLS_OFFSET)
+
+/*
+ * We wrap our port structure around the generic uart_port.
+ */
+struct uart_amba_port {
+       struct uart_port        port;
+       unsigned int            dtr_mask;
+       unsigned int            rts_mask;
+       unsigned int            old_status;
+};
+
+static void __ambauart_stop_tx(struct uart_port *port)
+{
+       unsigned int cr;
+
+       cr = UART_GET_CR(port);
+       cr &= ~AMBA_UARTCR_TIE;
+       UART_PUT_CR(port, cr);
+}
+
+static void ambauart_stop_tx(struct uart_port *port, unsigned int tty_stop)
+{
+       unsigned long flags;
+
+       spin_lock_irqsave(&port->lock, flags);
+       __ambauart_stop_tx(port);
+       spin_unlock_irqrestore(&port->lock, flags);
+}
+
+static void ambauart_start_tx(struct uart_port *port, unsigned int tty_start)
+{
+       unsigned int cr;
+
+       cr = UART_GET_CR(port);
+       cr |= AMBA_UARTCR_TIE;
+       UART_PUT_CR(port, cr);
+}
+
+static void ambauart_stop_rx(struct uart_port *port)
+{
+       unsigned long flags;
+       unsigned int cr;
+
+       spin_lock_irqsave(&port->lock, flags);
+       cr = UART_GET_CR(port);
+       cr &= ~(AMBA_UARTCR_RIE | AMBA_UARTCR_RTIE);
+       UART_PUT_CR(port, cr);
+       spin_unlock_irqrestore(&port->lock, flags);
+}
+
+static void ambauart_enable_ms(struct uart_port *port)
+{
+       unsigned long flags;
+       unsigned int cr;
+
+       spin_lock_irqsave(&port->lock, flags);
+       cr = UART_GET_CR(port);
+       cr |= AMBA_UARTCR_MSIE;
+       UART_PUT_CR(port, cr);
+       spin_unlock_irqrestore(&port->lock, flags);
+}
+
+static void
+#ifdef SUPPORT_SYSRQ
+ambauart_rx_chars(struct uart_port *port, struct pt_regs *regs)
+#else
+ambauart_rx_chars(struct uart_port *port)
+#endif
+{
+       struct tty_struct *tty = port->info->tty;
+       unsigned int status, ch, rsr, max_count = 256;
+
+       status = UART_GET_FR(port);
+       while (UART_RX_DATA(status) && max_count--) {
+               if (tty->flip.count >= TTY_FLIPBUF_SIZE) {
+                       tty->flip.tqueue.routine((void *)tty);
+                       if (tty->flip.count >= TTY_FLIPBUF_SIZE) {
+                               printk(KERN_WARNING "TTY_DONT_FLIP set\n");
+                               return;
+                       }
+               }
+
+               ch = UART_GET_CHAR(port);
+
+               *tty->flip.char_buf_ptr = ch;
+               *tty->flip.flag_buf_ptr = TTY_NORMAL;
+               port->icount.rx++;
+
+               /*
+                * Note that the error handling code is
+                * out of the main execution path
+                */
+               rsr = UART_GET_RSR(port) | UART_DUMMY_RSR_RX;
+               if (rsr & AMBA_UARTRSR_ANY) {
+                       if (rsr & AMBA_UARTRSR_BE) {
+                               rsr &= ~(AMBA_UARTRSR_FE | AMBA_UARTRSR_PE);
+                               port->icount.brk++;
+                               if (uart_handle_break(port))
+                                       goto ignore_char;
+                       } else if (rsr & AMBA_UARTRSR_PE)
+                               port->icount.parity++;
+                       else if (rsr & AMBA_UARTRSR_FE)
+                               port->icount.frame++;
+                       if (rsr & AMBA_UARTRSR_OE)
+                               port->icount.overrun++;
+
+                       rsr &= port->read_status_mask;
+
+                       if (rsr & AMBA_UARTRSR_BE)
+                               *tty->flip.flag_buf_ptr = TTY_BREAK;
+                       else if (rsr & AMBA_UARTRSR_PE)
+                               *tty->flip.flag_buf_ptr = TTY_PARITY;
+                       else if (rsr & AMBA_UARTRSR_FE)
+                               *tty->flip.flag_buf_ptr = TTY_FRAME;
+               }
+
+               if (uart_handle_sysrq_char(port, ch, regs))
+                       goto ignore_char;
+
+               if ((rsr & port->ignore_status_mask) == 0) {
+                       tty->flip.flag_buf_ptr++;
+                       tty->flip.char_buf_ptr++;
+                       tty->flip.count++;
+               }
+               if ((rsr & AMBA_UARTRSR_OE) &&
+                   tty->flip.count < TTY_FLIPBUF_SIZE) {
+                       /*
+                        * Overrun is special, since it's reported
+                        * immediately, and doesn't affect the current
+                        * character
+                        */
+                       *tty->flip.char_buf_ptr++ = 0;
+                       *tty->flip.flag_buf_ptr++ = TTY_OVERRUN;
+                       tty->flip.count++;
+               }
+       ignore_char:
+               status = UART_GET_FR(port);
+       }
+       tty_flip_buffer_push(tty);
+       return;
+}
+
+static void ambauart_tx_chars(struct uart_port *port)
+{
+       struct circ_buf *xmit = &port->info->xmit;
+       int count;
+
+       if (port->x_char) {
+               UART_PUT_CHAR(port, port->x_char);
+               port->icount.tx++;
+               port->x_char = 0;
+               return;
+       }
+       if (uart_circ_empty(xmit) || uart_tx_stopped(port)) {
+               __ambauart_stop_tx(port);
+               return;
+       }
+
+       count = port->fifosize >> 1;
+       do {
+               UART_PUT_CHAR(port, xmit->buf[xmit->tail]);
+               xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
+               port->icount.tx++;
+               if (uart_circ_empty(xmit))
+                       break;
+       } while (--count > 0);
+
+       if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
+               uart_event(port, EVT_WRITE_WAKEUP);
+
+       if (uart_circ_empty(xmit))
+               __ambauart_stop_tx(port);
+}
+
+static void ambauart_modem_status(struct uart_port *port)
+{
+       struct uart_amba_port *uap = (struct uart_amba_port *)port;
+       unsigned int status, delta;
+
+       UART_PUT_ICR(&uap->port, 0);
+
+       status = UART_GET_FR(&uap->port) & AMBA_UARTFR_MODEM_ANY;
+
+       delta = status ^ uap->old_status;
+       uap->old_status = status;
+
+       if (!delta)
+               return;
+
+       if (delta & AMBA_UARTFR_DCD)
+               uart_handle_dcd_change(&uap->port, status & AMBA_UARTFR_DCD);
+
+       if (delta & AMBA_UARTFR_DSR)
+               uap->port.icount.dsr++;
+
+       if (delta & AMBA_UARTFR_CTS)
+               uart_handle_cts_change(&uap->port, status & AMBA_UARTFR_CTS);
+
+       wake_up_interruptible(&uap->port.info->delta_msr_wait);
+}
+
+static void ambauart_int(int irq, void *dev_id, struct pt_regs *regs)
+{
+       struct uart_port *port = dev_id;
+       unsigned int status, pass_counter = AMBA_ISR_PASS_LIMIT;
+
+       status = UART_GET_INT_STATUS(port);
+       do {
+               if (status & (AMBA_UARTIIR_RTIS | AMBA_UARTIIR_RIS))
+#ifdef SUPPORT_SYSRQ
+                       ambauart_rx_chars(port, regs);
+#else
+                       ambauart_rx_chars(port);
+#endif
+               if (status & AMBA_UARTIIR_MIS)
+                       ambauart_modem_status(port);
+               if (status & AMBA_UARTIIR_TIS)
+                       ambauart_tx_chars(port);
+
+               if (pass_counter-- == 0)
+                       break;
+
+               status = UART_GET_INT_STATUS(port);
+       } while (status & (AMBA_UARTIIR_RTIS | AMBA_UARTIIR_RIS |
+                          AMBA_UARTIIR_TIS));
+}
+
+static unsigned int ambauart_tx_empty(struct uart_port *port)
+{
+       return UART_GET_FR(port) & AMBA_UARTFR_BUSY ? 0 : TIOCSER_TEMT;
+}
+
+static unsigned int ambauart_get_mctrl(struct uart_port *port)
+{
+       unsigned int result = 0;
+       unsigned int status;
+
+       status = UART_GET_FR(port);
+       if (status & AMBA_UARTFR_DCD)
+               result |= TIOCM_CAR;
+       if (status & AMBA_UARTFR_DSR)
+               result |= TIOCM_DSR;
+       if (status & AMBA_UARTFR_CTS)
+               result |= TIOCM_CTS;
+
+       return result;
+}
+
+static void ambauart_set_mctrl(struct uart_port *port, unsigned int mctrl)
+{
+       struct uart_amba_port *uap = (struct uart_amba_port *)port;
+       unsigned int ctrls = 0, ctrlc = 0;
+
+       if (mctrl & TIOCM_RTS)
+               ctrlc |= uap->rts_mask;
+       else
+               ctrls |= uap->rts_mask;
+
+       if (mctrl & TIOCM_DTR)
+               ctrlc |= uap->dtr_mask;
+       else
+               ctrls |= uap->dtr_mask;
+
+       __raw_writel(ctrls, SC_CTRLS);
+       __raw_writel(ctrlc, SC_CTRLC);
+}
+
+static void ambauart_break_ctl(struct uart_port *port, int break_state)
+{
+       unsigned long flags;
+       unsigned int lcr_h;
+
+       spin_lock_irqsave(&port->lock, flags);
+       lcr_h = UART_GET_LCRH(port);
+       if (break_state == -1)
+               lcr_h |= AMBA_UARTLCR_H_BRK;
+       else
+               lcr_h &= ~AMBA_UARTLCR_H_BRK;
+       UART_PUT_LCRH(port, lcr_h);
+       spin_unlock_irqrestore(&port->lock, flags);
+}
+
+static int ambauart_startup(struct uart_port *port)
+{
+       struct uart_amba_port *uap = (struct uart_amba_port *)port;
+       int retval;
+
+       /*
+        * Allocate the IRQ
+        */
+       retval = request_irq(port->irq, ambauart_int, 0, "amba", port);
+       if (retval)
+               return retval;
+
+       /*
+        * initialise the old status of the modem signals
+        */
+       uap->old_status = UART_GET_FR(port) & AMBA_UARTFR_MODEM_ANY;
+
+       /*
+        * Finally, enable interrupts
+        */
+       UART_PUT_CR(port, AMBA_UARTCR_UARTEN | AMBA_UARTCR_RIE |
+                         AMBA_UARTCR_RTIE);
+
+       return 0;
+}
+
+static void ambauart_shutdown(struct uart_port *port)
+{
+       /*
+        * Free the interrupt
+        */
+       free_irq(port->irq, port);
+
+       /*
+        * disable all interrupts, disable the port
+        */
+       UART_PUT_CR(port, 0);
+
+       /* disable break condition and fifos */
+       UART_PUT_LCRH(port, UART_GET_LCRH(port) &
+               ~(AMBA_UARTLCR_H_BRK | AMBA_UARTLCR_H_FEN));
+}
+
+static void
+ambauart_change_speed(struct uart_port *port, unsigned int cflag,
+                     unsigned int iflag, unsigned int quot)
+{
+       unsigned int lcr_h, old_cr;
+       unsigned long flags;
+
+       /* byte size and parity */
+       switch (cflag & CSIZE) {
+       case CS5:
+               lcr_h = AMBA_UARTLCR_H_WLEN_5;
+               break;
+       case CS6:
+               lcr_h = AMBA_UARTLCR_H_WLEN_6;
+               break;
+       case CS7:
+               lcr_h = AMBA_UARTLCR_H_WLEN_7;
+               break;
+       default: // CS8
+               lcr_h = AMBA_UARTLCR_H_WLEN_8;
+               break;
+       }
+       if (cflag & CSTOPB)
+               lcr_h |= AMBA_UARTLCR_H_STP2;
+       if (cflag & PARENB) {
+               lcr_h |= AMBA_UARTLCR_H_PEN;
+               if (!(cflag & PARODD))
+                       lcr_h |= AMBA_UARTLCR_H_EPS;
+       }
+       if (port->fifosize > 1)
+               lcr_h |= AMBA_UARTLCR_H_FEN;
+
+       port->read_status_mask = AMBA_UARTRSR_OE;
+       if (iflag & INPCK)
+               port->read_status_mask |= AMBA_UARTRSR_FE | AMBA_UARTRSR_PE;
+       if (iflag & (BRKINT | PARMRK))
+               port->read_status_mask |= AMBA_UARTRSR_BE;
+
+       /*
+        * Characters to ignore
+        */
+       port->ignore_status_mask = 0;
+       if (iflag & IGNPAR)
+               port->ignore_status_mask |= AMBA_UARTRSR_FE | AMBA_UARTRSR_PE;
+       if (iflag & IGNBRK) {
+               port->ignore_status_mask |= AMBA_UARTRSR_BE;
+               /*
+                * If we're ignoring parity and break indicators,
+                * ignore overruns too (for real raw support).
+                */
+               if (iflag & IGNPAR)
+                       port->ignore_status_mask |= AMBA_UARTRSR_OE;
+       }
+
+       /*
+        * Ignore all characters if CREAD is not set.
+        */
+       if ((cflag & CREAD) == 0)
+               port->ignore_status_mask |= UART_DUMMY_RSR_RX;
+
+       /* first, disable everything */
+       spin_lock_irqsave(&port->lock, flags);
+       old_cr = UART_GET_CR(port) & ~AMBA_UARTCR_MSIE;
+
+       if (UART_ENABLE_MS(port, cflag))
+               old_cr |= AMBA_UARTCR_MSIE;
+
+       UART_PUT_CR(port, 0);
+
+       /* Set baud rate */
+       quot -= 1;
+       UART_PUT_LCRM(port, ((quot & 0xf00) >> 8));
+       UART_PUT_LCRL(port, (quot & 0xff));
+
+       /*
+        * ----------v----------v----------v----------v-----
+        * NOTE: MUST BE WRITTEN AFTER UARTLCR_M & UARTLCR_L
+        * ----------^----------^----------^----------^-----
+        */
+       UART_PUT_LCRH(port, lcr_h);
+       UART_PUT_CR(port, old_cr);
+
+       spin_unlock_irqrestore(&port->lock, flags);
+}
+
+static const char *ambauart_type(struct uart_port *port)
+{
+       return port->type == PORT_AMBA ? "AMBA" : NULL;
+}
+
+/*
+ * Release the memory region(s) being used by 'port'
+ */
+static void ambauart_release_port(struct uart_port *port)
+{
+       release_mem_region(port->mapbase, UART_PORT_SIZE);
+}
+
+/*
+ * Request the memory region(s) being used by 'port'
+ */
+static int ambauart_request_port(struct uart_port *port)
+{
+       return request_mem_region(port->mapbase, UART_PORT_SIZE, "serial_amba")
+                       != NULL ? 0 : -EBUSY;
+}
+
+/*
+ * Configure/autoconfigure the port.
+ */
+static void ambauart_config_port(struct uart_port *port, int flags)
+{
+       if (flags & UART_CONFIG_TYPE) {
+               port->type = PORT_AMBA;
+               ambauart_request_port(port);
+       }
+}
+
+/*
+ * verify the new serial_struct (for TIOCSSERIAL).
+ */
+static int ambauart_verify_port(struct uart_port *port, struct serial_struct *ser)
+{
+       int ret = 0;
+       if (ser->type != PORT_UNKNOWN && ser->type != PORT_AMBA)
+               ret = -EINVAL;
+       if (ser->irq < 0 || ser->irq >= NR_IRQS)
+               ret = -EINVAL;
+       if (ser->baud_base < 9600)
+               ret = -EINVAL;
+       return ret;
+}
+
+static struct uart_ops amba_pops = {
+       tx_empty:       ambauart_tx_empty,
+       set_mctrl:      ambauart_set_mctrl,
+       get_mctrl:      ambauart_get_mctrl,
+       stop_tx:        ambauart_stop_tx,
+       start_tx:       ambauart_start_tx,
+       stop_rx:        ambauart_stop_rx,
+       enable_ms:      ambauart_enable_ms,
+       break_ctl:      ambauart_break_ctl,
+       startup:        ambauart_startup,
+       shutdown:       ambauart_shutdown,
+       change_speed:   ambauart_change_speed,
+       type:           ambauart_type,
+       release_port:   ambauart_release_port,
+       request_port:   ambauart_request_port,
+       config_port:    ambauart_config_port,
+       verify_port:    ambauart_verify_port,
+};
+
+static struct uart_amba_port amba_ports[UART_NR] = {
+       {
+               port:   {
+                       membase:        (void *)IO_ADDRESS(INTEGRATOR_UART0_BASE),
+                       mapbase:        INTEGRATOR_UART0_BASE,
+                       iotype:         SERIAL_IO_MEM,
+                       irq:            IRQ_UARTINT0,
+                       uartclk:        14745600,
+                       fifosize:       16,
+                       ops:            &amba_pops,
+                       flags:          ASYNC_BOOT_AUTOCONF,
+                       line:           0,
+               },
+               dtr_mask:       1 << 5,
+               rts_mask:       1 << 4,
+       },
+       {
+               port:   {
+                       membase:        (void *)IO_ADDRESS(INTEGRATOR_UART1_BASE),
+                       mapbase:        INTEGRATOR_UART1_BASE,
+                       iotype:         SERIAL_IO_MEM,
+                       irq:            IRQ_UARTINT1,
+                       uartclk:        14745600,
+                       fifosize:       16,
+                       ops:            &amba_pops,
+                       flags:          ASYNC_BOOT_AUTOCONF,
+                       line:           1,
+               },
+               dtr_mask:       1 << 7,
+               rts_mask:       1 << 6,
+       }
+};
+
+#ifdef CONFIG_SERIAL_AMBA_CONSOLE
+
+static void
+ambauart_console_write(struct console *co, const char *s, unsigned int count)
+{
+       struct uart_port *port = &amba_ports[co->index].port;
+       unsigned int status, old_cr;
+       int i;
+
+       /*
+        *      First save the CR then disable the interrupts
+        */
+       old_cr = UART_GET_CR(port);
+       UART_PUT_CR(port, AMBA_UARTCR_UARTEN);
+
+       /*
+        *      Now, do each character
+        */
+       for (i = 0; i < count; i++) {
+               do {
+                       status = UART_GET_FR(port);
+               } while (!UART_TX_READY(status));
+               UART_PUT_CHAR(port, s[i]);
+               if (s[i] == '\n') {
+                       do {
+                               status = UART_GET_FR(port);
+                       } while (!UART_TX_READY(status));
+                       UART_PUT_CHAR(port, '\r');
+               }
+       }
+
+       /*
+        *      Finally, wait for transmitter to become empty
+        *      and restore the TCR
+        */
+       do {
+               status = UART_GET_FR(port);
+       } while (status & AMBA_UARTFR_BUSY);
+       UART_PUT_CR(port, old_cr);
+}
+
+static kdev_t ambauart_console_device(struct console *co)
+{
+       return mk_kdev(SERIAL_AMBA_MAJOR, SERIAL_AMBA_MINOR + co->index);
+}
+
+static void __init
+ambauart_console_get_options(struct uart_port *port, int *baud,
+                            int *parity, int *bits)
+{
+       if (UART_GET_CR(port) & AMBA_UARTCR_UARTEN) {
+               unsigned int lcr_h, quot;
+               lcr_h = UART_GET_LCRH(port);
+
+               *parity = 'n';
+               if (lcr_h & AMBA_UARTLCR_H_PEN) {
+                       if (lcr_h & AMBA_UARTLCR_H_EPS)
+                               *parity = 'e';
+                       else
+                               *parity = 'o';
+               }
+
+               if ((lcr_h & 0x60) == AMBA_UARTLCR_H_WLEN_7)
+                       *bits = 7;
+               else
+                       *bits = 8;
+
+               quot = UART_GET_LCRL(port) | UART_GET_LCRM(port) << 8;
+               *baud = port->uartclk / (16 * (quot + 1));
+       }
+}
+
+static int __init ambauart_console_setup(struct console *co, char *options)
+{
+       struct uart_port *port;
+       int baud = 38400;
+       int bits = 8;
+       int parity = 'n';
+       int flow = 'n';
+
+       /*
+        * Check whether an invalid uart number has been specified, and
+        * if so, search for the first available port that does have
+        * console support.
+        */
+       if (co->index >= UART_NR)
+               co->index = 0;
+       port = &amba_ports[co->index].port;
+
+       if (options)
+               uart_parse_options(options, &baud, &parity, &bits, &flow);
+       else
+               ambauart_console_get_options(port, &baud, &parity, &bits);
+
+       return uart_set_options(port, co, baud, parity, bits, flow);
+}
+
+static struct console amba_console = {
+       name:           "ttyAM",
+       write:          ambauart_console_write,
+       device:         ambauart_console_device,
+       setup:          ambauart_console_setup,
+       flags:          CON_PRINTBUFFER,
+       index:          -1,
+};
+
+void __init ambauart_console_init(void)
+{
+       register_console(&amba_console);
+}
+
+#define AMBA_CONSOLE   &amba_console
+#else
+#define AMBA_CONSOLE   NULL
+#endif
+
+static struct uart_driver amba_reg = {
+       owner:                  THIS_MODULE,
+       driver_name:            "ttyAM",
+#ifdef CONFIG_DEVFS_FS
+       dev_name:               "ttyAM%d",
+#else
+       dev_name:               "ttyAM",
+#endif
+       major:                  SERIAL_AMBA_MAJOR,
+       minor:                  SERIAL_AMBA_MINOR,
+       nr:                     UART_NR,
+       cons:                   AMBA_CONSOLE,
+};
+
+static int __init ambauart_init(void)
+{
+       int ret;
+
+       printk(KERN_INFO "Serial: AMBA driver $Revision: 1.35 $\n");
+
+       ret = uart_register_driver(&amba_reg);
+       if (ret == 0) {
+               int i;
+
+               for (i = 0; i < UART_NR; i++)
+                       uart_add_one_port(&amba_reg, &amba_ports[i].port);
+       }
+       return ret;
+}
+
+static void __exit ambauart_exit(void)
+{
+       int i;
+
+       for (i = 0; i < UART_NR; i++)
+               uart_remove_one_port(&amba_reg, &amba_ports[i].port);
+
+       uart_unregister_driver(&amba_reg);
+}
+
+module_init(ambauart_init);
+module_exit(ambauart_exit);
+
+EXPORT_NO_SYMBOLS;
+
+MODULE_AUTHOR("ARM Ltd/Deep Blue Solutions Ltd");
+MODULE_DESCRIPTION("ARM AMBA serial port driver $Revision: 1.35 $");
+MODULE_LICENSE("GPL");
diff --git a/drivers/serial/serial_anakin.c b/drivers/serial/serial_anakin.c
new file mode 100644 (file)
index 0000000..f9ee4c2
--- /dev/null
@@ -0,0 +1,546 @@
+/*
+ *  linux/drivers/char/serial_anakin.c
+ *
+ *  Based on driver for AMBA serial ports, by ARM Limited,
+ *  Deep Blue Solutions Ltd., Linus Torvalds and Theodore Ts'o.
+ *
+ *  Copyright (C) 2001 Aleph One Ltd. for Acunia N.V.
+ *
+ *  Copyright (C) 2001 Blue Mug, Inc. for Acunia N.V.
+ *
+ * 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.
+ *
+ *  Changelog:
+ *   20-Apr-2001 TTC   Created
+ *   05-May-2001 W/TTC Updated for serial_core.c
+ *   27-Jun-2001 jonm  Minor changes; add mctrl support, switch to 
+ *                     SA_INTERRUPT. Works reliably now. No longer requires
+ *                     changes to the serial_core API.
+ *
+ *  $Id: serial_anakin.c,v 1.27 2002/07/20 17:10:03 rmk Exp $
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <linux/signal.h>
+#include <linux/sched.h>
+#include <linux/interrupt.h>
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
+#include <linux/major.h>
+#include <linux/string.h>
+#include <linux/fcntl.h>
+#include <linux/ptrace.h>
+#include <linux/ioport.h>
+#include <linux/mm.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <linux/circ_buf.h>
+#include <linux/serial.h>
+#include <linux/console.h>
+#include <linux/sysrq.h>
+
+#include <asm/system.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/uaccess.h>
+#include <asm/bitops.h>
+
+#include <linux/serial_core.h>
+
+#include <asm/arch/serial_reg.h>
+
+#define UART_NR                        5
+
+#define SERIAL_ANAKIN_NAME     "ttyAN"
+#define SERIAL_ANAKIN_MAJOR    204
+#define SERIAL_ANAKIN_MINOR    32
+
+static unsigned int txenable[NR_IRQS];         /* Software interrupt register */
+
+static inline unsigned int
+anakin_in(struct uart_port *port, unsigned int offset)
+{
+       return __raw_readl(port->base + offset);
+}
+
+static inline void
+anakin_out(struct uart_port *port, unsigned int offset, unsigned int value)
+{
+       __raw_writel(value, port->base + offset);
+}
+
+static void
+anakin_stop_tx(struct uart_port *port, unsigned int tty_stop)
+{
+       txenable[port->irq] = 0;
+}
+
+static inline void
+anakin_transmit_buffer(struct uart_port *port)
+{
+       struct circ_buf *xmit = &port->info->xmit;
+
+       while (!(anakin_in(port, 0x10) & TXEMPTY));
+       anakin_out(port, 0x14, xmit->buf[xmit->tail]);
+       anakin_out(port, 0x18, anakin_in(port, 0x18) | SENDREQUEST);
+       xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE-1);
+        port->icount.tx++;
+
+       if (uart_circ_empty(xmit))
+               anakin_stop_tx(port, 0); 
+}
+
+static inline void
+anakin_transmit_x_char(struct uart_port *port)
+{
+       anakin_out(port, 0x14, port->x_char);
+       anakin_out(port, 0x18, anakin_in(port, 0x18) | SENDREQUEST);
+       port->icount.tx++;
+       port->x_char = 0;
+}
+
+static void
+anakin_start_tx(struct uart_port *port, unsigned int tty_start)
+{
+       unsigned int flags;
+
+       spin_lock_irqsave(&port->lock, flags);
+
+       // is it this... or below
+       if (!txenable[port->irq]) {
+               txenable[port->irq] = TXENABLE;
+
+               if ((anakin_in(port, 0x10) & TXEMPTY)) {
+                   anakin_transmit_buffer(port);
+               }
+       }
+
+       spin_unlock_irqrestore(&port->lock, flags);
+}
+
+static void
+anakin_stop_rx(struct uart_port *port)
+{
+       unsigned long flags;
+
+       spin_lock_irqsave(&port->lock, flags);
+       while (anakin_in(port, 0x10) & RXRELEASE) 
+           anakin_in(port, 0x14);
+       anakin_out(port, 0x18, anakin_in(port, 0x18) | BLOCKRX);
+       spin_unlock_irqrestore(&port->lock, flags);
+}
+
+static void
+anakin_enable_ms(struct uart_port *port)
+{
+}
+
+static inline void
+anakin_rx_chars(struct uart_port *port)
+{
+       unsigned int ch;
+       struct tty_struct *tty = port->info->tty;
+
+       if (!(anakin_in(port, 0x10) & RXRELEASE))
+               return;
+
+       ch = anakin_in(port, 0x14) & 0xff;
+
+       if (tty->flip.count < TTY_FLIPBUF_SIZE) {
+               *tty->flip.char_buf_ptr++ = ch;
+               *tty->flip.flag_buf_ptr++ = TTY_NORMAL;
+               port->icount.rx++;
+               tty->flip.count++;
+       } 
+       tty_flip_buffer_push(tty);
+}
+
+static inline void
+anakin_overrun_chars(struct uart_port *port)
+{
+       unsigned int ch;
+
+       ch = anakin_in(port, 0x14);
+       port->icount.overrun++;
+}
+
+static inline void
+anakin_tx_chars(struct uart_port *port)
+{
+       struct circ_buf *xmit = &port->info->xmit;
+
+       if (port->x_char) {
+               anakin_transmit_x_char(port);
+               return; 
+       }
+
+       if (uart_circ_empty(xmit) || uart_tx_stopped(port)) {
+               anakin_stop_tx(port, 0);
+               return;
+       }
+
+       anakin_transmit_buffer(port);
+
+       if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
+               uart_event(port, EVT_WRITE_WAKEUP);
+}
+
+static void
+anakin_int(int irq, void *dev_id, struct pt_regs *regs)
+{
+       unsigned int status;
+       struct uart_port *port = dev_id;
+
+       status = anakin_in(port, 0x1c);
+
+       if (status & RX) 
+               anakin_rx_chars(port);
+
+       if (status & OVERRUN) 
+               anakin_overrun_chars(port);
+
+       if (txenable[port->irq] && (status & TX)) 
+               anakin_tx_chars(port);
+}
+
+static unsigned int
+anakin_tx_empty(struct uart_port *port)
+{
+       return anakin_in(port, 0x10) & TXEMPTY ? TIOCSER_TEMT : 0;
+}
+
+static unsigned int
+anakin_get_mctrl(struct uart_port *port)
+{
+       unsigned int status = 0;
+
+       status |= (anakin_in(port, 0x10) & CTS ? TIOCM_CTS : 0);
+       status |= (anakin_in(port, 0x18) & DCD ? TIOCM_CAR : 0);
+       status |= (anakin_in(port, 0x18) & DTR ? TIOCM_DTR : 0);
+       status |= (anakin_in(port, 0x18) & RTS ? TIOCM_RTS : 0);
+       
+       return status;
+}
+
+static void
+anakin_set_mctrl(struct uart_port *port, unsigned int mctrl)
+{
+       unsigned int status;
+
+       status = anakin_in(port, 0x18);
+
+       if (mctrl & TIOCM_RTS) 
+               status |= RTS;
+       else 
+               status &= ~RTS;
+
+       if (mctrl & TIOCM_CAR)
+               status |= DCD;
+       else 
+               status &= ~DCD;
+
+       anakin_out(port, 0x18, status);
+}
+
+static void
+anakin_break_ctl(struct uart_port *port, int break_state)
+{
+       unsigned long flags;
+       unsigned int status;
+
+       spin_lock_irqsave(&port->lock, flags);
+       status = anakin_in(port, 0x20);
+
+       if (break_state == -1)
+               status |= SETBREAK;
+       else
+               status &= ~SETBREAK;
+
+       anakin_out(port, 0x20, status);
+       spin_unlock_irqrestore(&port->lock, flags);
+}
+
+static int anakin_startup(struct uart_port *port)
+{
+       int retval;
+       unsigned int read,write;
+
+       /*
+        * Allocate the IRQ
+        */
+       retval = request_irq(port->irq, anakin_int, SA_INTERRUPT,
+                            "serial_anakin", port);
+       if (retval)
+               return retval;
+
+       /*
+        * initialise the old status of the modem signals
+        */
+       port->old_status = 0;
+
+       /*
+        * Finally, disable IRQ and softIRQs for first byte)
+        */
+       txenable[port->irq] = 0;
+       read = anakin_in(port, 0x18);
+       write = (read & ~(RTS | DTR | BLOCKRX)) | IRQENABLE;
+       anakin_out(port, 0x18, write);
+
+       return 0;
+}
+
+static void anakin_shutdown(struct uart_port *port)
+{
+       /*
+        * Free the interrupt
+        */
+       free_irq(port->irq, port);
+
+       /*
+        * disable all interrupts, disable the port
+        */
+       anakin_out(port, 0x18, anakin_in(port, 0x18) & ~IRQENABLE);
+}
+
+static void
+anakin_change_speed(struct uart_port *port, unsigned int cflag,
+                   unsigned int iflag, unsigned int quot)
+{
+       unsigned int flags;
+
+       spin_lock_irqsave(&port->lock, flags);
+       while (!(anakin_in(port, 0x10) & TXEMPTY));
+       anakin_out(port, 0x10, (anakin_in(port, 0x10) & ~PRESCALER)
+                       | (quot << 3));
+
+       //parity always set to none
+       anakin_out(port, 0x18, anakin_in(port, 0x18) & ~PARITY);
+       spin_unlock_irqrestore(&port->lock, flags);
+}
+
+static const char *anakin_type(struct port *port)
+{
+       return port->type == PORT_ANAKIN ? "ANAKIN" : NULL;
+}
+
+static struct uart_ops anakin_pops = {
+       tx_empty:       anakin_tx_empty,
+       set_mctrl:      anakin_set_mctrl,
+       get_mctrl:      anakin_get_mctrl,
+       stop_tx:        anakin_stop_tx,
+       start_tx:       anakin_start_tx,
+       stop_rx:        anakin_stop_rx,
+       enable_ms:      anakin_enable_ms,
+       break_ctl:      anakin_break_ctl,
+       startup:        anakin_startup,
+       shutdown:       anakin_shutdown,
+       change_speed:   anakin_change_speed,
+       type:           anakin_type,
+};
+
+static struct uart_port anakin_ports[UART_NR] = {
+       {
+               base:           IO_BASE + UART0,
+               irq:            IRQ_UART0,
+               uartclk:        3686400,
+               fifosize:       0,
+               ops:            &anakin_pops,
+               flags:          ASYNC_BOOT_AUTOCONF,
+               line:           0,
+       },
+       {
+               base:           IO_BASE + UART1,
+               irq:            IRQ_UART1,
+               uartclk:        3686400,
+               fifosize:       0,
+               ops:            &anakin_pops,
+               flags:          ASYNC_BOOT_AUTOCONF,
+               line:           1,
+       },
+       {
+               base:           IO_BASE + UART2,
+               irq:            IRQ_UART2,
+               uartclk:        3686400,
+               fifosize:       0,
+               ops:            &anakin_pops,
+               flags:          ASYNC_BOOT_AUTOCONF,
+               line:           2,
+       },
+       {
+               base:           IO_BASE + UART3,
+               irq:            IRQ_UART3,
+               uartclk:        3686400,
+               fifosize:       0,
+               ops:            &anakin_pops,
+               flags:          ASYNC_BOOT_AUTOCONF,
+               line:           3,
+       },
+       {
+               base:           IO_BASE + UART4,
+               irq:            IRQ_UART4,
+               uartclk:        3686400,
+               fifosize:       0,
+               ops:            &anakin_pops,
+               flags:          ASYNC_BOOT_AUTOCONF,
+               line:           4,
+       },
+};
+
+
+#ifdef CONFIG_SERIAL_ANAKIN_CONSOLE
+
+static void
+anakin_console_write(struct console *co, const char *s, unsigned int count)
+{
+       struct uart_port *port = &anakin_ports[co->index];
+       unsigned int flags, status, i;
+
+       /*
+        *      First save the status then disable the interrupts
+        */
+       local_irq_save(flags);
+       status = anakin_in(port, 0x18);
+       anakin_out(port, 0x18, status & ~IRQENABLE);
+       local_irq_restore(flags);
+
+       /*
+        *      Now, do each character
+        */
+       for (i = 0; i < count; i++, s++) {
+               while (!(anakin_in(port, 0x10) & TXEMPTY));
+
+               /*
+                *      Send the character out.
+                *      If a LF, also do CR...
+                */
+               anakin_out(port, 0x14, *s);
+               anakin_out(port, 0x18, anakin_in(port, 0x18) | SENDREQUEST);
+
+               if (*s == 10) {
+                       while (!(anakin_in(port, 0x10) & TXEMPTY));
+                       anakin_out(port, 0x14, 13);
+                       anakin_out(port, 0x18, anakin_in(port, 0x18)
+                                       | SENDREQUEST);
+               }
+       }
+
+       /*
+        *      Finally, wait for transmitter to become empty
+        *      and restore the interrupts
+        */
+       while (!(anakin_in(port, 0x10) & TXEMPTY));
+
+       if (status & IRQENABLE) {
+               local_irq_save(flags);
+               anakin_out(port, 0x18, anakin_in(port, 0x18) | IRQENABLE);
+               local_irq_restore(flags);
+       }
+}
+
+static kdev_t
+anakin_console_device(struct console *co)
+{
+       return mk_kdev(SERIAL_ANAKIN_MAJOR, SERIAL_ANAKIN_MINOR + co->index);
+}
+
+/*
+ * Read the current UART setup.
+ */
+static void __init
+anakin_console_get_options(struct uart_port *port, int *baud, int *parity, int *bits)
+{
+       int paritycode;
+
+       *baud = GETBAUD (anakin_in(port, 0x10) & PRESCALER);
+       paritycode = GETPARITY(anakin_in(port, 0x18) & PARITY);
+       switch (paritycode) {
+         case NONEPARITY: *parity = 'n'; break;
+         case ODDPARITY: *parity = 'o'; break;
+         case EVENPARITY: *parity = 'e'; break;
+       }
+       *bits = 8;
+}
+
+static int __init
+anakin_console_setup(struct console *co, char *options)
+{
+       struct uart_port *port;
+       int baud = CONFIG_ANAKIN_DEFAULT_BAUDRATE;
+       int bits = 8;
+       int parity = 'n';
+
+       /*
+        * Check whether an invalid uart number has been specified, and
+        * if so, search for the first available port that does have
+        * console support.
+        */
+       if (co->index >= UART_NR)
+               co->index = 0;
+       port = &anakin_ports[co->index];
+
+       if (options)
+               uart_parse_options(options, &baud, &parity, &bits);
+       else
+               anakin_console_get_options(port, &baud, &parity, &bits);
+
+       return uart_set_options(port, co, baud, parity, bits);
+}
+
+static struct console anakin_console = {
+       name:           SERIAL_ANAKIN_NAME,
+       write:          anakin_console_write,
+       device:         anakin_console_device,
+       setup:          anakin_console_setup,
+       flags:          CON_PRINTBUFFER,
+       index:          -1,
+};
+
+void __init
+anakin_console_init(void)
+{
+       register_console(&anakin_console);
+}
+
+#define ANAKIN_CONSOLE         &anakin_console
+#else
+#define ANAKIN_CONSOLE         NULL
+#endif
+
+static struct uart_register anakin_reg = {
+       driver_name:            SERIAL_ANAKIN_NAME,
+       dev_name:               SERIAL_ANAKIN_NAME,
+       major:                  SERIAL_ANAKIN_MAJOR,
+       minor:                  SERIAL_ANAKIN_MINOR,
+       nr:                     UART_NR,
+       cons:                   ANAKIN_CONSOLE,
+};
+
+static int __init
+anakin_init(void)
+{
+       int ret;
+
+       printk(KERN_INFO "Serial: Anakin driver $Revision: 1.27 $\n");
+
+       ret = uart_register_driver(&anakin_reg);
+       if (ret == 0) {
+               int i;
+
+               for (i = 0; i < UART_NR; i++)
+                       uart_add_one_port(&anakin_reg, &anakin_ports[i]);
+       }
+       return ret;
+}
+
+__initcall(anakin_init);
+
+MODULE_DESCRIPTION("Anakin serial driver");
+MODULE_AUTHOR("Tak-Shing Chan <chan@aleph1.co.uk>");
+MODULE_SUPPORTED_DEVICE("ttyAN");
+MODULE_LICENSE("GPL");
+
+EXPORT_NO_SYMBOLS;
diff --git a/drivers/serial/serial_clps711x.c b/drivers/serial/serial_clps711x.c
new file mode 100644 (file)
index 0000000..500b9a2
--- /dev/null
@@ -0,0 +1,643 @@
+/*
+ *  linux/drivers/char/serial_clps711x.c
+ *
+ *  Driver for CLPS711x serial ports
+ *
+ *  Based on drivers/char/serial.c, by Linus Torvalds, Theodore Ts'o.
+ *
+ *  Copyright 1999 ARM Limited
+ *  Copyright (C) 2000 Deep Blue Solutions Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ *  $Id: serial_clps711x.c,v 1.38 2002/07/21 08:57:55 rmk Exp $
+ *
+ */
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <linux/signal.h>
+#include <linux/sched.h>
+#include <linux/interrupt.h>
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
+#include <linux/major.h>
+#include <linux/string.h>
+#include <linux/fcntl.h>
+#include <linux/ptrace.h>
+#include <linux/ioport.h>
+#include <linux/mm.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <linux/circ_buf.h>
+#include <linux/serial.h>
+#include <linux/console.h>
+#include <linux/sysrq.h>
+#include <linux/spinlock.h>
+
+#include <asm/bitops.h>
+#include <asm/hardware.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/system.h>
+#include <asm/uaccess.h>
+
+#if defined(CONFIG_SERIAL_CLPS711X_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ)
+#define SUPPORT_SYSRQ
+#endif
+
+#include <linux/serial_core.h>
+
+#include <asm/hardware/clps7111.h>
+
+#define UART_NR                2
+
+#ifndef CONFIG_SERIAL_CLPS711X_OLD_NAME
+#define SERIAL_CLPS711X_NAME   "ttyCL"
+#define SERIAL_CLPS711X_MAJOR  204
+#define SERIAL_CLPS711X_MINOR  40
+#define SERIAL_CLPS711X_NR     UART_NR
+
+#else
+#warning The old names/device number for this driver if compatabity is needed
+#define SERIAL_CLPS711X_NAME    "ttyAM"
+#define SERIAL_CLPS711X_MAJOR   204
+#define SERIAL_CLPS711X_MINOR   16
+#define SERIAL_CLPS711X_NR      UART_NR
+
+#endif
+
+/*
+ * We use the relevant SYSCON register as a base address for these ports.
+ */
+#define UBRLCR(port)           ((port)->iobase + UBRLCR1 - SYSCON1)
+#define UARTDR(port)           ((port)->iobase + UARTDR1 - SYSCON1)
+#define SYSFLG(port)           ((port)->iobase + SYSFLG1 - SYSCON1)
+#define SYSCON(port)           ((port)->iobase + SYSCON1 - SYSCON1)
+
+#define TX_IRQ(port)           ((port)->irq)
+#define RX_IRQ(port)           ((port)->irq + 1)
+
+#define UART_ANY_ERR           (UARTDR_FRMERR | UARTDR_PARERR | UARTDR_OVERR)
+
+#define tx_enabled(port)       ((port)->unused[0])
+
+static void
+__clps711xuart_stop_tx(struct uart_port *port)
+{
+       if (tx_enabled(port)) {
+               disable_irq(TX_IRQ(port));
+               tx_enabled(port) = 0;
+       }
+}
+
+static void
+clps711xuart_stop_tx(struct uart_port *port, unsigned int tty_stop)
+{
+       unsigned long flags;
+
+       spin_lock_irqsave(&port->lock, flags);
+       __clps711xuart_stop_tx(port);
+       spin_unlock_irqrestore(&port->lock, flags);
+}
+
+static void
+clps711xuart_start_tx(struct uart_port *port, unsigned int tty_start)
+{
+       unsigned long flags;
+
+       spin_lock_irqsave(&port->lock, flags);
+       if (!tx_enabled(port)) {
+               enable_irq(TX_IRQ(port));
+               tx_enabled(port) = 1;
+       }
+       spin_unlock_irqrestore(&port->lock, flags);
+}
+
+static void clps711xuart_stop_rx(struct uart_port *port)
+{
+       disable_irq(RX_IRQ(port));
+}
+
+static void clps711xuart_enable_ms(struct uart_port *port)
+{
+}
+
+static void clps711xuart_int_rx(int irq, void *dev_id, struct pt_regs *regs)
+{
+       struct uart_port *port = dev_id;
+       struct tty_struct *tty = port->info->tty;
+       unsigned int status, ch, flg, ignored = 0;
+
+       status = clps_readl(SYSFLG(port));
+       while (!(status & SYSFLG_URXFE)) {
+               ch = clps_readl(UARTDR(port));
+
+               if (tty->flip.count >= TTY_FLIPBUF_SIZE)
+                       goto ignore_char;
+               port->icount.rx++;
+
+               flg = TTY_NORMAL;
+
+               /*
+                * Note that the error handling code is
+                * out of the main execution path
+                */
+               if (ch & UART_ANY_ERR)
+                       goto handle_error;
+
+               if (uart_handle_sysrq_char(port, ch, regs))
+                       goto ignore_char;
+
+       error_return:
+               *tty->flip.flag_buf_ptr++ = flg;
+               *tty->flip.char_buf_ptr++ = ch;
+               tty->flip.count++;
+       ignore_char:
+               status = clps_readl(SYSFLG(port));
+       }
+ out:
+       tty_flip_buffer_push(tty);
+       return;
+
+ handle_error:
+       if (ch & UARTDR_PARERR)
+               port->icount.parity++;
+       else if (ch & UARTDR_FRMERR)
+               port->icount.frame++;
+       if (ch & UARTDR_OVERR)
+               port->icount.overrun++;
+
+       if (ch & port->ignore_status_mask) {
+               if (++ignored > 100)
+                       goto out;
+               goto ignore_char;
+       }
+       ch &= port->read_status_mask;
+
+       if (ch & UARTDR_PARERR)
+               flg = TTY_PARITY;
+       else if (ch & UARTDR_FRMERR)
+               flg = TTY_FRAME;
+
+       if (ch & UARTDR_OVERR) {
+               /*
+                * CHECK: does overrun affect the current character?
+                * ASSUMPTION: it does not.
+                */
+               *tty->flip.flag_buf_ptr++ = flg;
+               *tty->flip.char_buf_ptr++ = ch;
+               tty->flip.count++;
+               if (tty->flip.count >= TTY_FLIPBUF_SIZE)
+                       goto ignore_char;
+               ch = 0;
+               flg = TTY_OVERRUN;
+       }
+#ifdef SUPPORT_SYSRQ
+       port->sysrq = 0;
+#endif
+       goto error_return;
+}
+
+static void clps711xuart_int_tx(int irq, void *dev_id, struct pt_regs *regs)
+{
+       struct uart_port *port = dev_id;
+       struct circ_buf *xmit = &port->info->xmit;
+       int count;
+
+       if (port->x_char) {
+               clps_writel(port->x_char, UARTDR(port));
+               port->icount.tx++;
+               port->x_char = 0;
+               return;
+       }
+       if (uart_circ_empty(xmit) || uart_tx_stopped(port)) {
+               __clps711xuart_stop_tx(port);
+               return;
+       }
+
+       count = port->fifosize >> 1;
+       do {
+               clps_writel(xmit->buf[xmit->tail], UARTDR(port));
+               xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
+               port->icount.tx++;
+               if (uart_circ_empty(xmit))
+                       break;
+       } while (--count > 0);
+
+       if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
+               uart_event(port, EVT_WRITE_WAKEUP);
+
+       if (uart_circ_empty(xmit))
+               __clps711xuart_stop_tx(port);
+}
+
+static unsigned int clps711xuart_tx_empty(struct uart_port *port)
+{
+       unsigned int status = clps_readl(SYSFLG(port));
+       return status & SYSFLG_UBUSY ? 0 : TIOCSER_TEMT;
+}
+
+static unsigned int clps711xuart_get_mctrl(struct uart_port *port)
+{
+       unsigned int port_addr;
+       unsigned int result = 0;
+       unsigned int status;
+
+       port_addr = SYSFLG(port);
+       if (port_addr == SYSFLG1) {
+               status = clps_readl(SYSFLG1);
+               if (status & SYSFLG1_DCD)
+                       result |= TIOCM_CAR;
+               if (status & SYSFLG1_DSR)
+                       result |= TIOCM_DSR;
+               if (status & SYSFLG1_CTS)
+                       result |= TIOCM_CTS;
+       }
+
+       return result;
+}
+
+static void
+clps711xuart_set_mctrl_null(struct uart_port *port, unsigned int mctrl)
+{
+}
+
+static void clps711xuart_break_ctl(struct uart_port *port, int break_state)
+{
+       unsigned long flags;
+       unsigned int ubrlcr;
+
+       spin_lock_irqsave(&port->lock, flags);
+       ubrlcr = clps_readl(UBRLCR(port));
+       if (break_state == -1)
+               ubrlcr |= UBRLCR_BREAK;
+       else
+               ubrlcr &= ~UBRLCR_BREAK;
+       clps_writel(ubrlcr, UBRLCR(port));
+       spin_unlock_irqrestore(&port->lock, flags);
+}
+
+static int clps711xuart_startup(struct uart_port *port)
+{
+       unsigned int syscon;
+       int retval;
+
+       tx_enabled(port) = 1;
+
+       /*
+        * Allocate the IRQs
+        */
+       retval = request_irq(TX_IRQ(port), clps711xuart_int_tx, 0,
+                            "clps711xuart_tx", port);
+       if (retval)
+               return retval;
+
+       retval = request_irq(RX_IRQ(port), clps711xuart_int_rx, 0,
+                            "clps711xuart_rx", port);
+       if (retval) {
+               free_irq(TX_IRQ(port), port);
+               return retval;
+       }
+
+       /*
+        * enable the port
+        */
+       syscon = clps_readl(SYSCON(port));
+       syscon |= SYSCON_UARTEN;
+       clps_writel(syscon, SYSCON(port));
+
+       return 0;
+}
+
+static void clps711xuart_shutdown(struct uart_port *port)
+{
+       unsigned int ubrlcr, syscon;
+
+       /*
+        * Free the interrupt
+        */
+       free_irq(TX_IRQ(port), port);   /* TX interrupt */
+       free_irq(RX_IRQ(port), port);   /* RX interrupt */
+
+       /*
+        * disable the port
+        */
+       syscon = clps_readl(SYSCON(port));
+       syscon &= ~SYSCON_UARTEN;
+       clps_writel(syscon, SYSCON(port));
+
+       /*
+        * disable break condition and fifos
+        */
+       ubrlcr = clps_readl(UBRLCR(port));
+       ubrlcr &= ~(UBRLCR_FIFOEN | UBRLCR_BREAK);
+       clps_writel(ubrlcr, UBRLCR(port));
+}
+
+static void
+clps711xuart_change_speed(struct uart_port *port, unsigned int cflag,
+                         unsigned int iflag, unsigned int quot)
+{
+       unsigned int ubrlcr;
+       unsigned long flags;
+
+       /* byte size and parity */
+       switch (cflag & CSIZE) {
+       case CS5:
+               ubrlcr = UBRLCR_WRDLEN5;
+               break;
+       case CS6:
+               ubrlcr = UBRLCR_WRDLEN6;
+               break;
+       case CS7:
+               ubrlcr = UBRLCR_WRDLEN7;
+               break;
+       default: // CS8
+               ubrlcr = UBRLCR_WRDLEN8;
+               break;
+       }
+       if (cflag & CSTOPB)
+               ubrlcr |= UBRLCR_XSTOP;
+       if (cflag & PARENB) {
+               ubrlcr |= UBRLCR_PRTEN;
+               if (!(cflag & PARODD))
+                       ubrlcr |= UBRLCR_EVENPRT;
+       }
+       if (port->fifosize > 1)
+               ubrlcr |= UBRLCR_FIFOEN;
+
+       port->read_status_mask = UARTDR_OVERR;
+       if (iflag & INPCK)
+               port->read_status_mask |= UARTDR_PARERR | UARTDR_FRMERR;
+
+       /*
+        * Characters to ignore
+        */
+       port->ignore_status_mask = 0;
+       if (iflag & IGNPAR)
+               port->ignore_status_mask |= UARTDR_FRMERR | UARTDR_PARERR;
+       if (iflag & IGNBRK) {
+               /*
+                * If we're ignoring parity and break indicators,
+                * ignore overruns to (for real raw support).
+                */
+               if (iflag & IGNPAR)
+                       port->ignore_status_mask |= UARTDR_OVERR;
+       }
+
+       quot -= 1;
+
+       /* first, disable everything */
+       spin_lock_irqsave(&port->lock, flags);
+
+       clps_writel(ubrlcr | quot, UBRLCR(port));
+
+       spin_unlock_irqrestore(&port->lock, flags);
+}
+
+static const char *clps711xuart_type(struct uart_port *port)
+{
+       return port->type == PORT_CLPS711X ? "CLPS711x" : NULL;
+}
+
+/*
+ * Configure/autoconfigure the port.
+ */
+static void clps711xuart_config_port(struct uart_port *port, int flags)
+{
+       if (flags & UART_CONFIG_TYPE)
+               port->type = PORT_CLPS711X;
+}
+
+static void clps711xuart_release_port(struct uart_port *port)
+{
+}
+
+static int clps711xuart_request_port(struct uart_port *port)
+{
+       return 0;
+}
+
+static struct uart_ops clps711x_pops = {
+       tx_empty:       clps711xuart_tx_empty,
+       set_mctrl:      clps711xuart_set_mctrl_null,
+       get_mctrl:      clps711xuart_get_mctrl,
+       stop_tx:        clps711xuart_stop_tx,
+       start_tx:       clps711xuart_start_tx,
+       stop_rx:        clps711xuart_stop_rx,
+       enable_ms:      clps711xuart_enable_ms,
+       break_ctl:      clps711xuart_break_ctl,
+       startup:        clps711xuart_startup,
+       shutdown:       clps711xuart_shutdown,
+       change_speed:   clps711xuart_change_speed,
+       type:           clps711xuart_type,
+       config_port:    clps711xuart_config_port,
+       release_port:   clps711xuart_release_port,
+       request_port:   clps711xuart_request_port,
+};
+
+static struct uart_port clps711x_ports[UART_NR] = {
+       {
+               iobase:         SYSCON1,
+               irq:            IRQ_UTXINT1, /* IRQ_URXINT1, IRQ_UMSINT */
+               uartclk:        3686400,
+               fifosize:       16,
+               ops:            &clps711x_pops,
+               flags:          ASYNC_BOOT_AUTOCONF,
+       },
+       {
+               iobase:         SYSCON2,
+               irq:            IRQ_UTXINT2, /* IRQ_URXINT2 */
+               uartclk:        3686400,
+               fifosize:       16,
+               ops:            &clps711x_pops,
+               flags:          ASYNC_BOOT_AUTOCONF,
+       }
+};
+
+#ifdef CONFIG_SERIAL_CLPS711X_CONSOLE
+/*
+ *     Print a string to the serial port trying not to disturb
+ *     any possible real use of the port...
+ *
+ *     The console_lock must be held when we get here.
+ *
+ *     Note that this is called with interrupts already disabled
+ */
+static void
+clps711xuart_console_write(struct console *co, const char *s,
+                          unsigned int count)
+{
+       struct uart_port *port = clps711x_ports + co->index;
+       unsigned int status, syscon;
+       int i;
+
+       /*
+        *      Ensure that the port is enabled.
+        */
+       syscon = clps_readl(SYSCON(port));
+       clps_writel(syscon | SYSCON_UARTEN, SYSCON(port));
+
+       /*
+        *      Now, do each character
+        */
+       for (i = 0; i < count; i++) {
+               do {
+                       status = clps_readl(SYSFLG(port));
+               } while (status & SYSFLG_UTXFF);
+               clps_writel(s[i], UARTDR(port));
+               if (s[i] == '\n') {
+                       do {
+                               status = clps_readl(SYSFLG(port));
+                       } while (status & SYSFLG_UTXFF);
+                       clps_writel('\r', UARTDR(port));
+               }
+       }
+
+       /*
+        *      Finally, wait for transmitter to become empty
+        *      and restore the uart state.
+        */
+       do {
+               status = clps_readl(SYSFLG(port));
+       } while (status & SYSFLG_UBUSY);
+
+       clps_writel(syscon, SYSCON(port));
+}
+
+static kdev_t clps711xuart_console_device(struct console *co)
+{
+       return mk_kdev(SERIAL_CLPS711X_MAJOR, SERIAL_CLPS711X_MINOR + co->index);
+}
+
+static void __init
+clps711xuart_console_get_options(struct uart_port *port, int *baud,
+                                int *parity, int *bits)
+{
+       if (clps_readl(SYSCON(port)) & SYSCON_UARTEN) {
+               unsigned int ubrlcr, quot;
+
+               ubrlcr = clps_readl(UBRLCR(port));
+
+               *parity = 'n';
+               if (ubrlcr & UBRLCR_PRTEN) {
+                       if (ubrlcr & UBRLCR_EVENPRT)
+                               *parity = 'e';
+                       else
+                               *parity = 'o';
+               }
+
+               if ((ubrlcr & UBRLCR_WRDLEN_MASK) == UBRLCR_WRDLEN7)
+                       *bits = 7;
+               else
+                       *bits = 8;
+
+               quot = ubrlcr & UBRLCR_BAUD_MASK;
+               *baud = port->uartclk / (16 * (quot + 1));
+       }
+}
+
+static int __init clps711xuart_console_setup(struct console *co, char *options)
+{
+       struct uart_port *port;
+       int baud = 38400;
+       int bits = 8;
+       int parity = 'n';
+       int flow = 'n';
+
+       /*
+        * Check whether an invalid uart number has been specified, and
+        * if so, search for the first available port that does have
+        * console support.
+        */
+       port = uart_get_console(clps711x_ports, UART_NR, co);
+
+       if (options)
+               uart_parse_options(options, &baud, &parity, &bits, &flow);
+       else
+               clps711xuart_console_get_options(port, &baud, &parity, &bits);
+
+       return uart_set_options(port, co, baud, parity, bits, flow);
+}
+
+static struct console clps711x_console = {
+       name:           SERIAL_CLPS711X_NAME,
+       write:          clps711xuart_console_write,
+       device:         clps711xuart_console_device,
+       setup:          clps711xuart_console_setup,
+       flags:          CON_PRINTBUFFER,
+       index:          -1,
+};
+
+void __init clps711xuart_console_init(void)
+{
+       register_console(&clps711x_console);
+}
+
+#define CLPS711X_CONSOLE       &clps711x_console
+#else
+#define CLPS711X_CONSOLE       NULL
+#endif
+
+static struct uart_driver clps711x_reg = {
+       driver_name:            "ttyCL",
+#ifdef CONFIG_DEVFS_FS
+       dev_name:               SERIAL_CLPS711X_NAME,
+#else
+       dev_name:               SERIAL_CLPS711X_NAME,
+#endif
+
+       major:                  SERIAL_CLPS711X_MAJOR,
+       minor:                  SERIAL_CLPS711X_MINOR,
+       nr:                     UART_NR,
+
+       cons:                   CLPS711X_CONSOLE,
+};
+
+static int __init clps711xuart_init(void)
+{
+       int ret, i;
+
+       printk(KERN_INFO "Serial: CLPS711x driver $Revision: 1.38 $\n");
+
+       ret = uart_register_driver(&clps711x_reg);
+       if (ret)
+               return ret;
+
+       for (i = 0; i < UART_NR; i++)
+               uart_add_one_port(&clps711x_reg, &clps711x_ports[i]);
+
+       return 0;
+}
+
+static void __exit clps711xuart_exit(void)
+{
+       int i;
+
+       for (i = 0; i < UART_NR; i++)
+               uart_remove_one_port(&clps711x_reg, &clps711x_ports[i]);
+
+       uart_unregister_driver(&clps711x_reg);
+}
+
+module_init(clps711xuart_init);
+module_exit(clps711xuart_exit);
+
+EXPORT_NO_SYMBOLS;
+
+MODULE_AUTHOR("Deep Blue Solutions Ltd");
+MODULE_DESCRIPTION("CLPS-711x generic serial driver $Revision: 1.38 $");
+MODULE_LICENSE("GPL");
diff --git a/drivers/serial/serial_core.c b/drivers/serial/serial_core.c
new file mode 100644 (file)
index 0000000..c6d3543
--- /dev/null
@@ -0,0 +1,2467 @@
+/*
+ *  linux/drivers/char/serial_core.c
+ *
+ *  Driver core for serial ports
+ *
+ *  Based on drivers/char/serial.c, by Linus Torvalds, Theodore Ts'o.
+ *
+ *  Copyright 1999 ARM Limited
+ *  Copyright (C) 2000-2001 Deep Blue Solutions Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ *  $Id: serial_core.c,v 1.89 2002/07/20 18:07:32 rmk Exp $
+ *
+ */
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <linux/signal.h>
+#include <linux/sched.h>
+#include <linux/interrupt.h>
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
+#include <linux/major.h>
+#include <linux/string.h>
+#include <linux/fcntl.h>
+#include <linux/ptrace.h>
+#include <linux/ioport.h>
+#include <linux/mm.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <linux/circ_buf.h>
+#include <linux/console.h>
+#include <linux/sysrq.h>
+#include <linux/pm.h>
+#include <linux/serial_core.h>
+#include <linux/smp_lock.h>
+#include <linux/serial.h> /* for serial_state and serial_icounter_struct */
+
+#include <asm/system.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/uaccess.h>
+#include <asm/bitops.h>
+
+#undef DEBUG
+#ifdef DEBUG
+#define DPRINTK(x...)  printk(x)
+#else
+#define DPRINTK(x...)  do { } while (0)
+#endif
+
+#ifndef CONFIG_PM
+#define pm_access(pm)          do { } while (0)
+#define pm_unregister(pm)      do { } while (0)
+#endif
+
+/*
+ * This is used to lock changes in serial line configuration.
+ */
+static DECLARE_MUTEX(port_sem);
+
+#define HIGH_BITS_OFFSET       ((sizeof(long)-sizeof(int))*8)
+
+static void uart_change_speed(struct uart_info *info, struct termios *old_termios);
+static void uart_wait_until_sent(struct tty_struct *tty, int timeout);
+
+/*
+ * This routine is used by the interrupt handler to schedule processing in
+ * the software interrupt portion of the driver.
+ */
+void uart_event(struct uart_port *port, int event)
+{
+       struct uart_info *info = port->info;
+
+       set_bit(0, &info->event);
+       tasklet_schedule(&info->tlet);
+}
+
+static void uart_stop(struct tty_struct *tty)
+{
+       struct uart_info *info = tty->driver_data;
+       struct uart_port *port = info->port;
+
+       port->ops->stop_tx(port, 1);
+}
+
+static void __uart_start(struct tty_struct *tty)
+{
+       struct uart_info *info = tty->driver_data;
+       struct uart_port *port = info->port;
+
+       if (!uart_circ_empty(&info->xmit) && info->xmit.buf &&
+           !tty->stopped && !tty->hw_stopped)
+               port->ops->start_tx(port, 1);
+}
+
+static void uart_start(struct tty_struct *tty)
+{
+       struct uart_info *info = tty->driver_data;
+       unsigned long flags;
+
+       pm_access(info->state->pm);
+
+       spin_lock_irqsave(&info->port->lock, flags);
+       __uart_start(tty);
+       spin_unlock_irqrestore(&info->port->lock, flags);
+}
+
+static void uart_tasklet_action(unsigned long data)
+{
+       struct uart_info *info = (struct uart_info *)data;
+       struct tty_struct *tty;
+
+       tty = info->tty;
+       if (!tty || !test_and_clear_bit(EVT_WRITE_WAKEUP, &info->event))
+               return;
+
+       if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
+           tty->ldisc.write_wakeup)
+               (tty->ldisc.write_wakeup)(tty);
+       wake_up_interruptible(&tty->write_wait);
+}
+
+static inline void
+uart_update_mctrl(struct uart_port *port, unsigned int set, unsigned int clear)
+{
+       unsigned long flags;
+       unsigned int old;
+
+       spin_lock_irqsave(&port->lock, flags);
+       old = port->mctrl;
+       port->mctrl = (old & ~clear) | set;
+       if (old != port->mctrl)
+               port->ops->set_mctrl(port, port->mctrl);
+       spin_unlock_irqrestore(&port->lock, flags);
+}
+
+#define uart_set_mctrl(port,set)       uart_update_mctrl(port,set,0)
+#define uart_clear_mctrl(port,clear)   uart_update_mctrl(port,0,clear)
+
+static inline void uart_update_altspeed(struct uart_info *info)
+{
+       unsigned int flags = info->port->flags & UPF_SPD_MASK;
+
+       if (flags == UPF_SPD_HI)
+               info->tty->alt_speed = 57600;
+       if (flags == UPF_SPD_VHI)
+               info->tty->alt_speed = 115200;
+       if (flags == UPF_SPD_SHI)
+               info->tty->alt_speed = 230400;
+       if (flags == UPF_SPD_WARP)
+               info->tty->alt_speed = 460800;
+}
+
+/*
+ * Startup the port.  This will be called once per open.  All calls
+ * will be serialised by the global port semaphore.
+ */
+static int uart_startup(struct uart_info *info, int init_hw)
+{
+       struct uart_port *port = info->port;
+       unsigned long page;
+       int retval = 0;
+
+       if (info->flags & UIF_INITIALIZED)
+               return 0;
+
+       /*
+        * Set the TTY IO error marker - we will only clear this
+        * once we have successfully opened the port.  Also set
+        * up the tty->alt_speed kludge
+        */
+       if (info->tty) {
+               set_bit(TTY_IO_ERROR, &info->tty->flags);
+               uart_update_altspeed(info);
+       }
+
+       if (port->type == PORT_UNKNOWN)
+               return 0;
+
+       /*
+        * Initialise and allocate the transmit and temporary
+        * buffer.
+        */
+       if (!info->xmit.buf) {
+               page = get_zeroed_page(GFP_KERNEL);
+               if (!page)
+                       return -ENOMEM;
+
+               info->xmit.buf = (unsigned char *) page;
+               info->tmpbuf = info->xmit.buf + UART_XMIT_SIZE;
+               init_MUTEX(&info->tmpbuf_sem);
+               uart_circ_clear(&info->xmit);
+       }
+
+       port->mctrl = 0;
+
+       retval = port->ops->startup(port);
+       if (retval == 0) {
+               if (init_hw) {
+                       /*
+                        * Initialise the hardware port settings.
+                        */
+                       uart_change_speed(info, NULL);
+
+                       /*
+                        * Setup the RTS and DTR signals once the
+                        * port is open and ready to respond.
+                        */
+                       if (info->tty->termios->c_cflag & CBAUD)
+                               uart_set_mctrl(port, TIOCM_RTS | TIOCM_DTR);
+               }
+
+               info->flags |= UIF_INITIALIZED;
+
+               if (info->tty)
+                       clear_bit(TTY_IO_ERROR, &info->tty->flags);
+       }
+
+       if (retval && capable(CAP_SYS_ADMIN))
+               retval = 0;
+
+       return retval;
+}
+
+/*
+ * This routine will shutdown a serial port; interrupts are disabled, and
+ * DTR is dropped if the hangup on close termio flag is on.  Calls to
+ * uart_shutdown are serialised by port_sem.
+ */
+static void uart_shutdown(struct uart_info *info)
+{
+       struct uart_port *port = info->port;
+
+       if (!(info->flags & UIF_INITIALIZED))
+               return;
+
+       /*
+        * Turn off DTR and RTS early.
+        */
+       if (!info->tty || (info->tty->termios->c_cflag & HUPCL))
+               uart_clear_mctrl(info->port, TIOCM_DTR | TIOCM_RTS);
+
+       /*
+        * clear delta_msr_wait queue to avoid mem leaks: we may free
+        * the irq here so the queue might never be woken up.  Note
+        * that we won't end up waiting on delta_msr_wait again since
+        * any outstanding file descriptors should be pointing at
+        * hung_up_tty_fops now.
+        */
+       wake_up_interruptible(&info->delta_msr_wait);
+
+       /*
+        * Free the IRQ and disable the port.
+        */
+       port->ops->shutdown(port);
+
+       /*
+        * Ensure that the IRQ handler isn't running on another CPU.
+        */
+       synchronize_irq(port->irq);
+
+       /*
+        * Free the transmit buffer page.
+        */
+       if (info->xmit.buf) {
+               free_page((unsigned long)info->xmit.buf);
+               info->xmit.buf = NULL;
+               info->tmpbuf = NULL;
+       }
+
+       /*
+        * kill off our tasklet
+        */
+       tasklet_kill(&info->tlet);
+       if (info->tty)
+               set_bit(TTY_IO_ERROR, &info->tty->flags);
+
+       info->flags &= ~UIF_INITIALIZED;
+}
+
+static inline
+unsigned int uart_calculate_quot(struct uart_info *info, unsigned int baud)
+{
+       struct uart_port *port = info->port;
+       unsigned int quot;
+
+       /* Special case: B0 rate */
+       if (baud == 0)
+               baud = 9600;
+
+       /* Old HI/VHI/custom speed handling */
+       if (baud == 38400 &&
+           ((port->flags & UPF_SPD_MASK) == UPF_SPD_CUST))
+               quot = info->state->custom_divisor;
+       else
+               quot = port->uartclk / (16 * baud);
+
+       return quot;
+}
+
+static void
+uart_change_speed(struct uart_info *info, struct termios *old_termios)
+{
+       struct uart_port *port = info->port;
+       unsigned int quot, cflag, bits, try;
+
+       /*
+        * If we have no tty, termios, or the port does not exist,
+        * then we can't set the parameters for this port.
+        */
+       if (!info->tty || !info->tty->termios || port->type == PORT_UNKNOWN)
+               return;
+
+       /*
+        * Set flags based on termios cflag
+        */
+       cflag = info->tty->termios->c_cflag;
+
+       /* byte size and parity */
+       switch (cflag & CSIZE) {
+       case CS5:
+               bits = 7;
+               break;
+       case CS6:
+               bits = 8;
+               break;
+       case CS7:
+               bits = 9;
+               break;
+       default:
+               bits = 10;
+               break; // CS8
+       }
+
+       if (cflag & CSTOPB)
+               bits++;
+       if (cflag & PARENB)
+               bits++;
+
+       for (try = 0; try < 3; try ++) {
+               unsigned int baud;
+
+               /* Determine divisor based on baud rate */
+               baud = tty_get_baud_rate(info->tty);
+               quot = uart_calculate_quot(info, baud);
+               if (quot)
+                       break;
+
+               /*
+                * Oops, the quotient was zero.  Try again with
+                * the old baud rate if possible.
+                */
+               info->tty->termios->c_cflag &= ~CBAUD;
+               if (old_termios) {
+                       info->tty->termios->c_cflag |=
+                                (old_termios->c_cflag & CBAUD);
+                       old_termios = NULL;
+                       continue;
+               }
+
+               /*
+                * As a last resort, if the quotient is zero,
+                * default to 9600 bps
+                */
+               info->tty->termios->c_cflag |= B9600;
+       }
+
+       /*
+        * The total number of bits to be transmitted in the fifo.
+        */
+       bits = bits * port->fifosize;
+
+       /*
+        * Figure the timeout to send the above number of bits.
+        * Add .02 seconds of slop
+        */
+       port->timeout = (HZ * bits) / (port->uartclk / (16 * quot)) + HZ/50;
+
+       if (cflag & CRTSCTS)
+               info->flags |= UIF_CTS_FLOW;
+       else
+               info->flags &= ~UIF_CTS_FLOW;
+       if (cflag & CLOCAL)
+               info->flags &= ~UIF_CHECK_CD;
+       else
+               info->flags |= UIF_CHECK_CD;
+
+       port->ops->change_speed(port, cflag, info->tty->termios->c_iflag, quot);
+}
+
+static inline void
+__uart_put_char(struct uart_port *port, struct circ_buf *circ, unsigned char c)
+{
+       unsigned long flags;
+
+       if (!circ->buf)
+               return;
+
+       spin_lock_irqsave(&port->lock, flags);
+       if (uart_circ_chars_free(circ) != 0) {
+               circ->buf[circ->head] = c;
+               circ->head = (circ->head + 1) & (UART_XMIT_SIZE - 1);
+       }
+       spin_unlock_irqrestore(&port->lock, flags);
+}
+
+static inline int
+__uart_user_write(struct uart_port *port, struct circ_buf *circ,
+                 const unsigned char *buf, int count)
+{
+       unsigned long flags;
+       int c, ret = 0;
+
+       if (down_interruptible(&port->info->tmpbuf_sem))
+               return -EINTR;
+
+       while (1) {
+               int c1;
+               c = CIRC_SPACE_TO_END(circ->head, circ->tail, UART_XMIT_SIZE);
+               if (count < c)
+                       c = count;
+               if (c <= 0)
+                       break;
+
+               c -= copy_from_user(port->info->tmpbuf, buf, c);
+               if (!c) {
+                       if (!ret)
+                               ret = -EFAULT;
+                       break;
+               }
+               spin_lock_irqsave(&port->lock, flags);
+               c1 = CIRC_SPACE_TO_END(circ->head, circ->tail, UART_XMIT_SIZE);
+               if (c1 < c)
+                       c = c1;
+               memcpy(circ->buf + circ->head, port->info->tmpbuf, c);
+               circ->head = (circ->head + c) & (UART_XMIT_SIZE - 1);
+               spin_unlock_irqrestore(&port->lock, flags);
+               buf += c;
+               count -= c;
+               ret += c;
+       }
+       up(&port->info->tmpbuf_sem);
+
+       return ret;
+}
+
+static inline int
+__uart_kern_write(struct uart_port *port, struct circ_buf *circ,
+                 const unsigned char *buf, int count)
+{
+       unsigned long flags;
+       int c, ret = 0;
+
+       spin_lock_irqsave(&port->lock, flags);
+       while (1) {
+               c = CIRC_SPACE_TO_END(circ->head, circ->tail, UART_XMIT_SIZE);
+               if (count < c)
+                       c = count;
+               if (c <= 0)
+                       break;
+               memcpy(circ->buf + circ->head, buf, c);
+               circ->head = (circ->head + c) & (UART_XMIT_SIZE - 1);
+               buf += c;
+               count -= c;
+               ret += c;
+       }
+       spin_unlock_irqrestore(&port->lock, flags);
+
+       return ret;
+}
+
+static void uart_put_char(struct tty_struct *tty, unsigned char ch)
+{
+       struct uart_info *info = tty->driver_data;
+
+       if (tty)
+               __uart_put_char(info->port, &info->xmit, ch);
+}
+
+static void uart_flush_chars(struct tty_struct *tty)
+{
+       uart_start(tty);
+}
+
+static int
+uart_write(struct tty_struct *tty, int from_user, const unsigned char * buf,
+          int count)
+{
+       struct uart_info *info = tty->driver_data;
+       int ret;
+
+       if (!tty || !info->xmit.buf)
+               return 0;
+
+       if (from_user)
+               ret = __uart_user_write(info->port, &info->xmit, buf, count);
+       else
+               ret = __uart_kern_write(info->port, &info->xmit, buf, count);
+
+       uart_start(tty);
+       return ret;
+}
+
+static int uart_write_room(struct tty_struct *tty)
+{
+       struct uart_info *info = tty->driver_data;
+
+       return uart_circ_chars_free(&info->xmit);
+}
+
+static int uart_chars_in_buffer(struct tty_struct *tty)
+{
+       struct uart_info *info = tty->driver_data;
+
+       return uart_circ_chars_pending(&info->xmit);
+}
+
+static void uart_flush_buffer(struct tty_struct *tty)
+{
+       struct uart_info *info = tty->driver_data;
+       unsigned long flags;
+
+       DPRINTK("uart_flush_buffer(%d) called\n",
+               MINOR(tty->device) - tty->driver.minor_start);
+
+       spin_lock_irqsave(&info->port->lock, flags);
+       uart_circ_clear(&info->xmit);
+       spin_unlock_irqrestore(&info->port->lock, flags);
+       wake_up_interruptible(&tty->write_wait);
+       if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
+           tty->ldisc.write_wakeup)
+               (tty->ldisc.write_wakeup)(tty);
+}
+
+/*
+ * This function is used to send a high-priority XON/XOFF character to
+ * the device
+ */
+static void uart_send_xchar(struct tty_struct *tty, char ch)
+{
+       struct uart_info *info = tty->driver_data;
+       struct uart_port *port = info->port;
+
+       if (port->ops->send_xchar)
+               port->ops->send_xchar(port, ch);
+       else {
+               port->x_char = ch;
+               if (ch)
+                       port->ops->start_tx(port, 0);
+       }
+}
+
+static void uart_throttle(struct tty_struct *tty)
+{
+       struct uart_info *info = tty->driver_data;
+
+       if (I_IXOFF(tty))
+               uart_send_xchar(tty, STOP_CHAR(tty));
+
+       if (tty->termios->c_cflag & CRTSCTS)
+               uart_clear_mctrl(info->port, TIOCM_RTS);
+}
+
+static void uart_unthrottle(struct tty_struct *tty)
+{
+       struct uart_info *info = tty->driver_data;
+       struct uart_port *port = info->port;
+
+       if (I_IXOFF(tty)) {
+               if (port->x_char)
+                       port->x_char = 0;
+               else
+                       uart_send_xchar(tty, START_CHAR(tty));
+       }
+
+       if (tty->termios->c_cflag & CRTSCTS)
+               uart_set_mctrl(port, TIOCM_RTS);
+}
+
+static int uart_get_info(struct uart_info *info, struct serial_struct *retinfo)
+{
+       struct uart_state *state = info->state;
+       struct uart_port *port = info->port;
+       struct serial_struct tmp;
+
+       memset(&tmp, 0, sizeof(tmp));
+       tmp.type            = port->type;
+       tmp.line            = port->line;
+       tmp.port            = port->iobase;
+       if (HIGH_BITS_OFFSET)
+               tmp.port_high = port->iobase >> HIGH_BITS_OFFSET;
+       tmp.irq             = port->irq;
+       tmp.flags           = port->flags | info->flags;
+       tmp.xmit_fifo_size  = port->fifosize;
+       tmp.baud_base       = port->uartclk / 16;
+       tmp.close_delay     = state->close_delay;
+       tmp.closing_wait    = state->closing_wait;
+       tmp.custom_divisor  = state->custom_divisor;
+       tmp.hub6            = port->hub6;
+       tmp.io_type         = port->iotype;
+       tmp.iomem_reg_shift = port->regshift;
+       tmp.iomem_base      = (void *)port->mapbase;
+
+       if (copy_to_user(retinfo, &tmp, sizeof(*retinfo)))
+               return -EFAULT;
+       return 0;
+}
+
+static int
+uart_set_info(struct uart_info *info, struct serial_struct *newinfo)
+{
+       struct serial_struct new_serial;
+       struct uart_state *state = info->state;
+       struct uart_port *port = info->port;
+       unsigned long new_port;
+       unsigned int change_irq, change_port, old_flags;
+       unsigned int old_custom_divisor;
+       int retval = 0;
+
+       if (copy_from_user(&new_serial, newinfo, sizeof(new_serial)))
+               return -EFAULT;
+
+       new_port = new_serial.port;
+       if (HIGH_BITS_OFFSET)
+               new_port += (unsigned long) new_serial.port_high << HIGH_BITS_OFFSET;
+
+       new_serial.irq = irq_cannonicalize(new_serial.irq);
+
+       /*
+        * This semaphore protects state->count.  It is also
+        * very useful to prevent opens.  Also, take the
+        * port configuration semaphore to make sure that a
+        * module insertion/removal doesn't change anything
+        * under us.
+        */
+       down(&port_sem);
+
+       change_irq  = new_serial.irq != port->irq;
+
+       /*
+        * Since changing the 'type' of the port changes its resource
+        * allocations, we should treat type changes the same as
+        * IO port changes.
+        */
+       change_port = new_port != port->iobase ||
+                     (unsigned long)new_serial.iomem_base != port->mapbase ||
+                     new_serial.hub6 != port->hub6 ||
+                     new_serial.io_type != port->iotype ||
+                     new_serial.iomem_reg_shift != port->regshift ||
+                     new_serial.type != port->type;
+
+       old_flags = port->flags;
+       old_custom_divisor = state->custom_divisor;
+
+       if (!capable(CAP_SYS_ADMIN)) {
+               retval = -EPERM;
+               if (change_irq || change_port ||
+                   (new_serial.baud_base != port->uartclk / 16) ||
+                   (new_serial.close_delay != state->close_delay) ||
+                   (new_serial.closing_wait != state->closing_wait) ||
+                   (new_serial.xmit_fifo_size != port->fifosize) ||
+                   (((new_serial.flags ^ old_flags) & ~UPF_USR_MASK) != 0))
+                       goto exit;
+               port->flags = ((port->flags & ~UPF_USR_MASK) |
+                              (new_serial.flags & UPF_USR_MASK));
+               state->custom_divisor = new_serial.custom_divisor;
+               goto check_and_exit;
+       }
+
+       /*
+        * Ask the low level driver to verify the settings.
+        */
+       if (port->ops->verify_port)
+               retval = port->ops->verify_port(port, &new_serial);
+
+       if ((new_serial.irq >= NR_IRQS) || (new_serial.irq < 0) ||
+           (new_serial.baud_base < 9600))
+               retval = -EINVAL;
+
+       if (retval)
+               goto exit;
+
+       if (change_port || change_irq) {
+               retval = -EBUSY;
+
+               /*
+                * Make sure that we are the sole user of this port.
+                */
+               if (state->count > 1 || info->blocked_open != 0)
+                       goto exit;
+
+               /*
+                * We need to shutdown the serial port at the old
+                * port/type/irq combination.
+                */
+               uart_shutdown(info);
+       }
+
+       if (change_port) {
+               unsigned long old_iobase, old_mapbase;
+               unsigned int old_type, old_iotype, old_hub6, old_shift;
+
+               old_iobase = port->iobase;
+               old_mapbase = port->mapbase;
+               old_type = port->type;
+               old_hub6 = port->hub6;
+               old_iotype = port->iotype;
+               old_shift = port->regshift;
+
+               /*
+                * Free and release old regions
+                */
+               if (old_type != PORT_UNKNOWN)
+                       port->ops->release_port(port);
+
+               port->iobase = new_port;
+               port->type = new_serial.type;
+               port->hub6 = new_serial.hub6;
+               port->iotype = new_serial.io_type;
+               port->regshift = new_serial.iomem_reg_shift;
+               port->mapbase = (unsigned long)new_serial.iomem_base;
+
+               /*
+                * Claim and map the new regions
+                */
+               if (port->type != PORT_UNKNOWN)
+                       retval = port->ops->request_port(port);
+
+               /*
+                * If we fail to request resources for the
+                * new port, try to restore the old settings.
+                */
+               if (retval && old_type != PORT_UNKNOWN) {
+                       port->iobase = old_iobase;
+                       port->type = old_type;
+                       port->hub6 = old_hub6;
+                       port->iotype = old_iotype;
+                       port->regshift = old_shift;
+                       port->mapbase = old_mapbase;
+                       retval = port->ops->request_port(port);
+                       /*
+                        * If we failed to restore the old settings,
+                        * we fail like this.
+                        */
+                       if (retval)
+                               port->type = PORT_UNKNOWN;
+
+                       /*
+                        * We failed anyway.
+                        */
+                       retval = -EBUSY;
+               }
+       }
+
+       port->irq              = new_serial.irq;
+       port->uartclk          = new_serial.baud_base * 16;
+       port->flags            = new_serial.flags & UPF_FLAGS;
+       state->custom_divisor  = new_serial.custom_divisor;
+       state->close_delay     = new_serial.close_delay * HZ / 100;
+       state->closing_wait    = new_serial.closing_wait * HZ / 100;
+       port->fifosize         = new_serial.xmit_fifo_size;
+       info->tty->low_latency = (port->flags & UPF_LOW_LATENCY) ? 1 : 0;
+
+ check_and_exit:
+       retval = 0;
+       if (port->type == PORT_UNKNOWN)
+               goto exit;
+       if (info->flags & UIF_INITIALIZED) {
+               if (((old_flags ^ port->flags) & UPF_SPD_MASK) ||
+                   old_custom_divisor != state->custom_divisor) {
+                       uart_update_altspeed(info);
+                       uart_change_speed(info, NULL);
+               }
+       } else
+               retval = uart_startup(info, 1);
+ exit:
+       up(&port_sem);
+       return retval;
+}
+
+
+/*
+ * uart_get_lsr_info - get line status register info
+ */
+static int uart_get_lsr_info(struct uart_info *info, unsigned int *value)
+{
+       struct uart_port *port = info->port;
+       unsigned int result;
+
+       result = port->ops->tx_empty(port);
+
+       /*
+        * If we're about to load something into the transmit
+        * register, we'll pretend the transmitter isn't empty to
+        * avoid a race condition (depending on when the transmit
+        * interrupt happens).
+        */
+       if (info->port->x_char ||
+           ((uart_circ_chars_pending(&info->xmit) > 0) &&
+            !info->tty->stopped && !info->tty->hw_stopped))
+               result &= ~TIOCSER_TEMT;
+       
+       return put_user(result, value);
+}
+
+static int uart_get_modem_info(struct uart_port *port, unsigned int *value)
+{
+       unsigned int result = port->mctrl;
+
+       result |= port->ops->get_mctrl(port);
+
+       return put_user(result, value);
+}
+
+static int
+uart_set_modem_info(struct uart_port *port, unsigned int cmd,
+                   unsigned int *value)
+{
+       unsigned int arg, set, clear;
+       int ret = 0;
+
+       if (get_user(arg, value))
+               return -EFAULT;
+
+       set = clear = 0;
+       switch (cmd) {
+       case TIOCMBIS:
+               set = arg;
+               break;
+       case TIOCMBIC:
+               clear = arg;
+               break;
+       case TIOCMSET:
+               set = arg;
+               clear = ~arg;
+               break;
+       default:
+               ret = -EINVAL;
+               break;
+       }
+       if (ret == 0)
+               uart_update_mctrl(port, set, clear);
+       return ret;
+}
+
+static void uart_break_ctl(struct tty_struct *tty, int break_state)
+{
+       struct uart_info *info = tty->driver_data;
+       struct uart_port *port = info->port;
+
+       BUG_ON(!kernel_locked());
+
+       if (port->type != PORT_UNKNOWN)
+               port->ops->break_ctl(port, break_state);
+}
+
+static int uart_do_autoconfig(struct uart_info *info)
+{
+       struct uart_port *port = info->port;
+       int flags, ret;
+
+       if (!capable(CAP_SYS_ADMIN))
+               return -EPERM;
+
+       /*
+        * Take the 'count' lock.  This prevents count
+        * from incrementing, and hence any extra opens
+        * of the port while we're auto-configging.
+        */
+       if (down_interruptible(&port_sem))
+               return -ERESTARTSYS;
+
+       ret = -EBUSY;
+       if (info->state->count == 1 && info->blocked_open == 0) {
+               uart_shutdown(info);
+
+               /*
+                * If we already have a port type configured,
+                * we must release its resources.
+                */
+               if (port->type != PORT_UNKNOWN)
+                       port->ops->release_port(port);
+
+               flags = UART_CONFIG_TYPE;
+               if (port->flags & UPF_AUTO_IRQ)
+                       flags |= UART_CONFIG_IRQ;
+
+               /*
+                * This will claim the ports resources if
+                * a port is found.
+                */
+               port->ops->config_port(port, flags);
+
+               ret = uart_startup(info, 1);
+       }
+       up(&port_sem);
+       return ret;
+}
+
+static int
+uart_wait_modem_status(struct uart_info *info, unsigned long arg)
+{
+       struct uart_port *port = info->port;
+       DECLARE_WAITQUEUE(wait, current);
+       struct uart_icount cprev, cnow;
+       int ret;
+
+       /*
+        * note the counters on entry
+        */
+       spin_lock_irq(&port->lock);
+       memcpy(&cprev, &port->icount, sizeof(struct uart_icount));
+       spin_unlock_irq(&port->lock);
+
+       /*
+        * Force modem status interrupts on
+        */
+       port->ops->enable_ms(port);
+
+       add_wait_queue(&info->delta_msr_wait, &wait);
+       for (;;) {
+               spin_lock_irq(&port->lock);
+               memcpy(&cnow, &port->icount, sizeof(struct uart_icount));
+               spin_unlock_irq(&port->lock);
+
+               set_current_state(TASK_INTERRUPTIBLE);
+
+               if (((arg & TIOCM_RNG) && (cnow.rng != cprev.rng)) ||
+                   ((arg & TIOCM_DSR) && (cnow.dsr != cprev.dsr)) ||
+                   ((arg & TIOCM_CD)  && (cnow.dcd != cprev.dcd)) ||
+                   ((arg & TIOCM_CTS) && (cnow.cts != cprev.cts))) {
+                       ret = 0;
+                       break;
+               }
+
+               schedule();
+
+               /* see if a signal did it */
+               if (signal_pending(current)) {
+                       ret = -ERESTARTSYS;
+                       break;
+               }
+
+               cprev = cnow;
+       }
+
+       current->state = TASK_RUNNING;
+       remove_wait_queue(&info->delta_msr_wait, &wait);
+
+       return ret;
+}
+
+/*
+ * Called via sys_ioctl under the BKL.  We can use spin_lock_irq() here.
+ */
+static int
+uart_ioctl(struct tty_struct *tty, struct file *file, unsigned int cmd,
+          unsigned long arg)
+{
+       struct uart_info *info = tty->driver_data;
+       struct serial_icounter_struct icount;
+       struct uart_icount cnow;
+       int ret = -ENOIOCTLCMD;
+
+       BUG_ON(!kernel_locked());
+
+       if ((cmd != TIOCGSERIAL) && (cmd != TIOCSSERIAL) &&
+           (cmd != TIOCSERCONFIG) && (cmd != TIOCSERGSTRUCT) &&
+           (cmd != TIOCMIWAIT) && (cmd != TIOCGICOUNT)) {
+               if (tty->flags & (1 << TTY_IO_ERROR))
+                       return -EIO;
+       }
+
+       switch (cmd) {
+               case TIOCMGET:
+                       ret = uart_get_modem_info(info->port,
+                                                 (unsigned int *)arg);
+                       break;
+
+               case TIOCMBIS:
+               case TIOCMBIC:
+               case TIOCMSET:
+                       ret = uart_set_modem_info(info->port, cmd,
+                                                 (unsigned int *)arg);
+                       break;
+
+               case TIOCGSERIAL:
+                       ret = uart_get_info(info, (struct serial_struct *)arg);
+                       break;
+
+               case TIOCSSERIAL:
+                       ret = uart_set_info(info, (struct serial_struct *)arg);
+                       break;
+
+               case TIOCSERCONFIG:
+                       ret = uart_do_autoconfig(info);
+                       break;
+
+               case TIOCSERGETLSR: /* Get line status register */
+                       ret = uart_get_lsr_info(info, (unsigned int *)arg);
+                       break;
+
+               /*
+                * Wait for any of the 4 modem inputs (DCD,RI,DSR,CTS) to change
+                * - mask passed in arg for lines of interest
+                *   (use |'ed TIOCM_RNG/DSR/CD/CTS for masking)
+                * Caller should use TIOCGICOUNT to see which one it was
+                */
+               case TIOCMIWAIT:
+                       ret = uart_wait_modem_status(info, arg);
+                       break;
+
+               /*
+                * Get counter of input serial line interrupts (DCD,RI,DSR,CTS)
+                * Return: write counters to the user passed counter struct
+                * NB: both 1->0 and 0->1 transitions are counted except for
+                *     RI where only 0->1 is counted.
+                */
+               case TIOCGICOUNT:
+                       spin_lock_irq(&info->port->lock);
+                       memcpy(&cnow, &info->port->icount,
+                              sizeof(struct uart_icount));
+                       spin_unlock_irq(&info->port->lock);
+
+                       icount.cts         = cnow.cts;
+                       icount.dsr         = cnow.dsr;
+                       icount.rng         = cnow.rng;
+                       icount.dcd         = cnow.dcd;
+                       icount.rx          = cnow.rx;
+                       icount.tx          = cnow.tx;
+                       icount.frame       = cnow.frame;
+                       icount.overrun     = cnow.overrun;
+                       icount.parity      = cnow.parity;
+                       icount.brk         = cnow.brk;
+                       icount.buf_overrun = cnow.buf_overrun;
+
+                       ret = copy_to_user((void *)arg, &icount, sizeof(icount))
+                                       ? -EFAULT : 0;
+                       break;
+
+               case TIOCSERGWILD: /* obsolete */
+               case TIOCSERSWILD: /* obsolete */
+                       ret = 0;
+                       break;
+
+               default: {
+                       struct uart_port *port = info->port;
+                       if (port->ops->ioctl)
+                               ret = port->ops->ioctl(port, cmd, arg);
+                       break;
+               }
+       }
+       return ret;
+}
+
+static void uart_set_termios(struct tty_struct *tty, struct termios *old_termios)
+{
+       struct uart_info *info = tty->driver_data;
+       unsigned long flags;
+       unsigned int cflag = tty->termios->c_cflag;
+
+       BUG_ON(!kernel_locked());
+
+       /*
+        * These are the bits that are used to setup various
+        * flags in the low level driver.
+        */
+#define RELEVANT_IFLAG(iflag)  ((iflag) & (IGNBRK|BRKINT|IGNPAR|PARMRK|INPCK))
+
+       if ((cflag ^ old_termios->c_cflag) == 0 &&
+           RELEVANT_IFLAG(tty->termios->c_iflag ^ old_termios->c_iflag) == 0)
+               return;
+
+       uart_change_speed(info, old_termios);
+
+       /* Handle transition to B0 status */
+       if ((old_termios->c_cflag & CBAUD) && !(cflag & CBAUD))
+               uart_clear_mctrl(info->port, TIOCM_RTS | TIOCM_DTR);
+
+       /* Handle transition away from B0 status */
+       if (!(old_termios->c_cflag & CBAUD) && (cflag & CBAUD)) {
+               unsigned int mask = TIOCM_DTR;
+               if (!(cflag & CRTSCTS) ||
+                   !test_bit(TTY_THROTTLED, &tty->flags))
+                       mask |= TIOCM_RTS;
+               uart_set_mctrl(info->port, mask);
+       }
+
+       /* Handle turning off CRTSCTS */
+       if ((old_termios->c_cflag & CRTSCTS) && !(cflag & CRTSCTS)) {
+               spin_lock_irqsave(&info->port->lock, flags);
+               tty->hw_stopped = 0;
+               __uart_start(tty);
+               spin_unlock_irqrestore(&info->port->lock, flags);
+       }
+
+#if 0
+       /*
+        * No need to wake up processes in open wait, since they
+        * sample the CLOCAL flag once, and don't recheck it.
+        * XXX  It's not clear whether the current behavior is correct
+        * or not.  Hence, this may change.....
+        */
+       if (!(old_termios->c_cflag & CLOCAL) &&
+           (tty->termios->c_cflag & CLOCAL))
+               wake_up_interruptible(&info->open_wait);
+#endif
+}
+
+/*
+ * In 2.4.5, calls to this will be serialized via the BKL in
+ *  linux/drivers/char/tty_io.c:tty_release()
+ *  linux/drivers/char/tty_io.c:do_tty_handup()
+ */
+static void uart_close(struct tty_struct *tty, struct file *filp)
+{
+       struct uart_driver *drv = (struct uart_driver *)tty->driver.driver_state;
+       struct uart_info *info = tty->driver_data;
+       struct uart_port *port = info->port;
+       struct uart_state *state;
+       unsigned long flags;
+
+       BUG_ON(!kernel_locked());
+
+       if (!info)
+               return;
+
+       state = info->state;
+
+       DPRINTK("uart_close() called\n");
+
+       /*
+        * This is safe, as long as the BKL exists in
+        * do_tty_hangup(), and we're protected by the BKL.
+        */
+       if (tty_hung_up_p(filp))
+               goto done;
+
+       spin_lock_irqsave(&info->port->lock, flags);
+       if ((tty->count == 1) && (state->count != 1)) {
+               /*
+                * Uh, oh.  tty->count is 1, which means that the tty
+                * structure will be freed.  state->count should always
+                * be one in these conditions.  If it's greater than
+                * one, we've got real problems, since it means the
+                * serial port won't be shutdown.
+                */
+               printk("uart_close: bad serial port count; tty->count is 1, "
+                      "state->count is %d\n", state->count);
+               state->count = 1;
+       }
+       if (--state->count < 0) {
+               printk("rs_close: bad serial port count for %s%d: %d\n",
+                      tty->driver.name, info->port->line, state->count);
+               state->count = 0;
+       }
+       if (state->count) {
+               spin_unlock_irqrestore(&info->port->lock, flags);
+               goto done;
+       }
+
+       /*
+        * The UIF_CLOSING flag protects us against further opens
+        * of this port.
+        */
+       info->flags |= UIF_CLOSING;
+       spin_unlock_irqrestore(&info->port->lock, flags);
+
+       /*
+        * Now we wait for the transmit buffer to clear; and we notify
+        * the line discipline to only process XON/XOFF characters.
+        */
+       tty->closing = 1;
+       if (info->state->closing_wait != USF_CLOSING_WAIT_NONE)
+               tty_wait_until_sent(tty, info->state->closing_wait);
+
+       /*
+        * At this point, we stop accepting input.  To do this, we
+        * disable the receive line status interrupts.
+        */
+       if (info->flags & UIF_INITIALIZED) {
+               port->ops->stop_rx(port);
+               /*
+                * Before we drop DTR, make sure the UART transmitter
+                * has completely drained; this is especially
+                * important if there is a transmit FIFO!
+                */
+               uart_wait_until_sent(tty, port->timeout);
+       }
+       down(&port_sem);
+       uart_shutdown(info);
+       up(&port_sem);
+       uart_flush_buffer(tty);
+       if (tty->ldisc.flush_buffer)
+               tty->ldisc.flush_buffer(tty);
+       tty->closing = 0;
+       info->event = 0;
+       info->tty = NULL;
+       if (info->blocked_open) {
+               if (info->state->close_delay) {
+                       set_current_state(TASK_INTERRUPTIBLE);
+                       schedule_timeout(info->state->close_delay);
+                       set_current_state(TASK_RUNNING);
+               }
+       } else {
+#ifdef CONFIG_PM
+               /*
+                * Put device into D3 state.
+                */
+               pm_send(info->state->pm, PM_SUSPEND, (void *)3);
+#else
+               if (port->ops->pm)
+                       port->ops->pm(port, 3, 0);
+#endif
+       }
+
+       /*
+        * Wake up anyone trying to open this port.
+        */
+       info->flags &= ~(UIF_NORMAL_ACTIVE|UIF_CLOSING);
+       wake_up_interruptible(&info->open_wait);
+
+ done:
+       if (drv->owner)
+               __MOD_DEC_USE_COUNT(drv->owner);
+}
+
+static void uart_wait_until_sent(struct tty_struct *tty, int timeout)
+{
+       struct uart_info *info = tty->driver_data;
+       struct uart_port *port = info->port;
+       unsigned long char_time, expire;
+
+       BUG_ON(!kernel_locked());
+
+       if (port->type == PORT_UNKNOWN || port->fifosize == 0)
+               return;
+
+       /*
+        * Set the check interval to be 1/5 of the estimated time to
+        * send a single character, and make it at least 1.  The check
+        * interval should also be less than the timeout.
+        *
+        * Note: we have to use pretty tight timings here to satisfy
+        * the NIST-PCTS.
+        */
+       char_time = (port->timeout - HZ/50) / port->fifosize;
+       char_time = char_time / 5;
+       if (char_time == 0)
+               char_time = 1;
+       if (timeout && timeout < char_time)
+               char_time = timeout;
+
+       /*
+        * If the transmitter hasn't cleared in twice the approximate
+        * amount of time to send the entire FIFO, it probably won't
+        * ever clear.  This assumes the UART isn't doing flow
+        * control, which is currently the case.  Hence, if it ever
+        * takes longer than port->timeout, this is probably due to a
+        * UART bug of some kind.  So, we clamp the timeout parameter at
+        * 2*port->timeout.
+        */
+       if (timeout == 0 || timeout > 2 * port->timeout)
+               timeout = 2 * port->timeout;
+
+       expire = jiffies + timeout;
+
+       DPRINTK("uart_wait_until_sent(%d), jiffies=%lu, expire=%lu...\n",
+               port->line, jiffies, expire);
+
+       /*
+        * Check whether the transmitter is empty every 'char_time'.
+        * 'timeout' / 'expire' give us the maximum amount of time
+        * we wait.
+        */
+       while (!port->ops->tx_empty(port)) {
+               set_current_state(TASK_INTERRUPTIBLE);
+               schedule_timeout(char_time);
+               if (signal_pending(current))
+                       break;
+               if (time_after(jiffies, expire))
+                       break;
+       }
+       set_current_state(TASK_RUNNING); /* might not be needed */
+}
+
+/*
+ * This is called with the BKL held in
+ *  linux/drivers/char/tty_io.c:do_tty_hangup()
+ * We're called from the eventd thread, so we can sleep for
+ * a _short_ time only.
+ */
+static void uart_hangup(struct tty_struct *tty)
+{
+       struct uart_info *info = tty->driver_data;
+       struct uart_state *state = info->state;
+
+       BUG_ON(!kernel_locked());
+
+       uart_flush_buffer(tty);
+       down(&port_sem);
+       if (info->flags & UIF_CLOSING) {
+               up(&port_sem);
+               return;
+       }
+       uart_shutdown(info);
+       info->event = 0;
+       state->count = 0;
+       info->flags &= ~UIF_NORMAL_ACTIVE;
+       info->tty = NULL;
+       up(&port_sem);
+       wake_up_interruptible(&info->open_wait);
+}
+
+/*
+ * Copy across the serial console cflag setting into the termios settings
+ * for the initial open of the port.  This allows continuity between the
+ * kernel settings, and the settings init adopts when it opens the port
+ * for the first time.
+ */
+static void uart_update_termios(struct uart_info *info)
+{
+       struct tty_struct *tty = info->tty;
+
+#ifdef CONFIG_SERIAL_CORE_CONSOLE
+       struct console *c = info->port->cons;
+
+       if (c && c->cflag && c->index == info->port->line) {
+               tty->termios->c_cflag = c->cflag;
+               c->cflag = 0;
+       }
+#endif
+
+       /*
+        * If the device failed to grab its irq resources,
+        * or some other error occurred, don't try to talk
+        * to the port hardware.
+        */
+       if (!(tty->flags & (1 << TTY_IO_ERROR))) {
+               /*
+                * Make termios settings take effect.
+                */
+               uart_change_speed(info, NULL);
+
+               /*
+                * And finally enable the RTS and DTR signals.
+                */
+               if (tty->termios->c_cflag & CBAUD)
+                       uart_set_mctrl(info->port, TIOCM_DTR | TIOCM_RTS);
+       }
+}
+
+static int
+uart_block_til_ready(struct file *filp, struct uart_info *info)
+{
+       DECLARE_WAITQUEUE(wait, current);
+       struct uart_state *state = info->state;
+       struct uart_port *port = info->port;
+
+       info->blocked_open++;
+       state->count--;
+
+       add_wait_queue(&info->open_wait, &wait);
+       while (1) {
+               set_current_state(TASK_INTERRUPTIBLE);
+
+               /*
+                * If we have been hung up, tell userspace/restart open.
+                */
+               if (tty_hung_up_p(filp))
+                       break;
+
+               /*
+                * If the device is in the middle of being closed, block
+                * until it's done.  We will need to re-initialise the
+                * port.  Hmm, is it legal to block a non-blocking open?
+                */
+               if (info->flags & UIF_CLOSING)
+                       goto wait;
+
+               /*
+                * If the port has been closed, tell userspace/restart open.
+                */
+               if (!(info->flags & UIF_INITIALIZED))
+                       break;
+
+               /*
+                * If non-blocking mode is set, or CLOCAL mode is set,
+                * we don't want to wait for the modem status lines to
+                * indicate that the port is ready.
+                *
+                * Also, if the port is not enabled/configured, we want
+                * to allow the open to succeed here.  Note that we will
+                * have set TTY_IO_ERROR for a non-existant port.
+                */
+               if ((filp->f_flags & O_NONBLOCK) ||
+                   (info->tty->termios->c_cflag & CLOCAL) ||
+                   (info->tty->flags & (1 << TTY_IO_ERROR))) {
+                       break;
+               }
+
+               /*
+                * Set DTR to allow modem to know we're waiting.  Do
+                * not set RTS here - we want to make sure we catch
+                * the data from the modem.
+                */
+               if (info->tty->termios->c_cflag & CBAUD)
+                       uart_set_mctrl(info->port, TIOCM_DTR);
+
+               /*
+                * and wait for the carrier to indicate that the
+                * modem is ready for us.
+                */
+               if (port->ops->get_mctrl(port) & TIOCM_CAR)
+                       break;
+
+        wait:
+               schedule();
+
+               if (signal_pending(current))
+                       break;
+       }
+       set_current_state(TASK_RUNNING);
+       remove_wait_queue(&info->open_wait, &wait);
+
+       state->count++;
+       info->blocked_open--;
+
+       if (signal_pending(current))
+               return -ERESTARTSYS;
+
+       if (tty_hung_up_p(filp) || !(info->flags & UIF_INITIALIZED))
+               return (port->flags & UPF_HUP_NOTIFY) ?
+                       -EAGAIN : -ERESTARTSYS;
+
+       return 0;
+}
+
+static struct uart_info *uart_get(struct uart_driver *drv, int line)
+{
+       struct uart_state *state = drv->state + line;
+       struct uart_info *info = NULL;
+
+       down(&port_sem);
+       if (!state->port)
+               goto out;
+
+       state->count++;
+       info = state->info;
+
+       if (!info) {
+               info = kmalloc(sizeof(struct uart_info), GFP_KERNEL);
+               if (info) {
+                       memset(info, 0, sizeof(struct uart_info));
+                       init_waitqueue_head(&info->open_wait);
+                       init_waitqueue_head(&info->delta_msr_wait);
+
+                       /*
+                        * Link the info into the other structures.
+                        */
+                       info->port  = state->port;
+                       info->state = state;
+                       state->port->info = info;
+
+                       tasklet_init(&info->tlet, uart_tasklet_action,
+                                    (unsigned long)info);
+                       state->info = info;
+               } else
+                       state->count--;
+       }
+
+ out:
+       up(&port_sem);
+       return info;
+}
+
+/*
+ * In 2.4.5, calls to uart_open are serialised by the BKL in
+ *   linux/fs/devices.c:chrdev_open()
+ * Note that if this fails, then uart_close() _will_ be called.
+ *
+ * In time, we want to scrap the "opening nonpresent ports"
+ * behaviour and implement an alternative way for setserial
+ * to set base addresses/ports/types.  This will allow us to
+ * get rid of a certain amount of extra tests.
+ */
+static int uart_open(struct tty_struct *tty, struct file *filp)
+{
+       struct uart_driver *drv = (struct uart_driver *)tty->driver.driver_state;
+       struct uart_info *info;
+       int retval, line = minor(tty->device) - tty->driver.minor_start;
+
+       BUG_ON(!kernel_locked());
+
+       DPRINTK("uart_open(%d) called\n", line);
+
+       /*
+        * tty->driver.num won't change, so we won't fail here with
+        * tty->driver_data set to something non-NULL (and therefore
+        * we won't get caught by uart_close()).
+        */
+       retval = -ENODEV;
+       if (line >= tty->driver.num)
+               goto fail;
+
+       /*
+        * If we fail to increment the module use count, we can't have
+        * any other users of this tty (since this implies that the module
+        * is about to be unloaded).  Therefore, it is safe to set
+        * tty->driver_data to be NULL, so uart_close() doesn't bite us.
+        */
+       if (!try_inc_mod_count(drv->owner)) {
+               tty->driver_data = NULL;
+               goto fail;
+       }
+
+       /*
+        * FIXME: This one isn't fun.  We can't guarantee that the tty isn't
+        * already in open, nor can we guarantee the state of tty->driver_data
+        */
+       info = uart_get(drv, line);
+       retval = -ENOMEM;
+       if (!info) {
+               if (tty->driver_data)
+                       goto fail;
+               else
+                       goto out;
+       }
+
+       /*
+        * Once we set tty->driver_data here, we are guaranteed that
+        * uart_close() will decrement the driver module use count.
+        * Any failures from here onwards should not touch the count.
+        */
+       tty->driver_data = info;
+       info->tty = tty;
+       info->tty->low_latency = (info->port->flags & UPF_LOW_LATENCY) ? 1 : 0;
+
+       /*
+        * If the port is in the middle of closing, bail out now.
+        */
+       if (tty_hung_up_p(filp) || (info->flags & UIF_CLOSING)) {
+               wait_event_interruptible(info->open_wait,
+                                        !(info->flags & UIF_CLOSING));
+               retval = (info->port->flags & UPF_HUP_NOTIFY) ?
+                       -EAGAIN : -ERESTARTSYS;
+               goto fail;
+       }
+
+       /*
+        * Make sure the device is in D0 state.
+        */
+       if (info->state->count == 1) {
+#ifdef CONFIG_PM
+               pm_send(info->state->pm, PM_RESUME, (void *)0);
+#else
+               struct uart_port *port = info->port;
+               if (port->ops->pm)
+                       port->ops->pm(port, 0, 3);
+#endif
+       }
+
+       /*
+        * Start up the serial port.  We have this semaphore here to
+        * prevent uart_startup or uart_shutdown being re-entered if
+        * we sleep while requesting an IRQ.
+        */
+       down(&port_sem);
+       retval = uart_startup(info, 0);
+       up(&port_sem);
+       if (retval)
+               goto fail;
+
+       /*
+        * Wait until the port is ready.
+        */
+       retval = uart_block_til_ready(filp, info);
+
+       /*
+        * If this is the first open to succeed, adjust things to suit.
+        */
+       if (retval == 0 && !(info->flags & UIF_NORMAL_ACTIVE)) {
+               info->flags |= UIF_NORMAL_ACTIVE;
+
+               uart_update_termios(info);
+       }
+
+       return retval;
+
+ out:
+       if (drv->owner)
+               __MOD_DEC_USE_COUNT(drv->owner);
+ fail:
+       return retval;
+}
+
+#ifdef CONFIG_PROC_FS
+
+static const char *uart_type(struct uart_port *port)
+{
+       const char *str = NULL;
+
+       if (port->ops->type)
+               str = port->ops->type(port);
+
+       if (!str)
+               str = "unknown";
+
+       return str;
+}
+
+static int uart_line_info(char *buf, struct uart_driver *drv, int i)
+{
+       struct uart_state *state = drv->state + i;
+       struct uart_port *port = state->port;
+       char stat_buf[32];
+       unsigned int status;
+       int ret;
+
+       if (!port)
+               return 0;
+
+       ret = sprintf(buf, "%d: uart:%s port:%08X irq:%d",
+                       port->line, uart_type(port),
+                       port->iobase, port->irq);
+
+       if (port->type == PORT_UNKNOWN) {
+               strcat(buf, "\n");
+               return ret + 1;
+       }
+
+       status = port->ops->get_mctrl(port);
+
+       ret += sprintf(buf + ret, " tx:%d rx:%d",
+                       port->icount.tx, port->icount.rx);
+       if (port->icount.frame)
+               ret += sprintf(buf + ret, " fe:%d",
+                       port->icount.frame);
+       if (port->icount.parity)
+               ret += sprintf(buf + ret, " pe:%d",
+                       port->icount.parity);
+       if (port->icount.brk)
+               ret += sprintf(buf + ret, " brk:%d",
+                       port->icount.brk);
+       if (port->icount.overrun)
+               ret += sprintf(buf + ret, " oe:%d",
+                       port->icount.overrun);
+
+#define INFOBIT(bit,str) \
+       if (port->mctrl & (bit)) \
+               strncat(stat_buf, (str), sizeof(stat_buf) - \
+                       strlen(stat_buf) - 2)
+#define STATBIT(bit,str) \
+       if (status & (bit)) \
+               strncat(stat_buf, (str), sizeof(stat_buf) - \
+                      strlen(stat_buf) - 2)
+
+       stat_buf[0] = '\0';
+       stat_buf[1] = '\0';
+       INFOBIT(TIOCM_RTS, "|RTS");
+       STATBIT(TIOCM_CTS, "|CTS");
+       INFOBIT(TIOCM_DTR, "|DTR");
+       STATBIT(TIOCM_DSR, "|DSR");
+       STATBIT(TIOCM_CAR, "|CD");
+       STATBIT(TIOCM_RNG, "|RI");
+       if (stat_buf[0])
+               stat_buf[0] = ' ';
+       strcat(stat_buf, "\n");
+
+       ret += sprintf(buf + ret, stat_buf);
+       return ret;
+}
+
+static int uart_read_proc(char *page, char **start, off_t off,
+                         int count, int *eof, void *data)
+{
+       struct tty_driver *ttydrv = data;
+       struct uart_driver *drv = ttydrv->driver_state;
+       int i, len = 0, l;
+       off_t begin = 0;
+
+       len += sprintf(page, "serinfo:1.0 driver%s%s revision:%s\n",
+                       "", "", "");
+       for (i = 0; i < drv->nr && len < PAGE_SIZE - 96; i++) {
+               l = uart_line_info(page + len, drv, i);
+               len += l;
+               if (len + begin > off + count)
+                       goto done;
+               if (len + begin < off) {
+                       begin += len;
+                       len = 0;
+               }
+       }
+       *eof = 1;
+ done:
+       if (off >= len + begin)
+               return 0;
+       *start = page + (off - begin);
+       return (count < begin + len - off) ? count : (begin + len - off);
+}
+#endif
+
+#ifdef CONFIG_SERIAL_CORE_CONSOLE
+/*
+ *     Check whether an invalid uart number has been specified, and
+ *     if so, search for the first available port that does have
+ *     console support.
+ */
+struct uart_port * __init
+uart_get_console(struct uart_port *ports, int nr, struct console *co)
+{
+       int idx = co->index;
+
+       if (idx < 0 || idx >= nr || (ports[idx].iobase == 0 &&
+                                    ports[idx].membase == NULL))
+               for (idx = 0; idx < nr; idx++)
+                       if (ports[idx].iobase != 0 ||
+                           ports[idx].membase != NULL)
+                               break;
+
+       co->index = idx;
+
+       return ports + idx;
+}
+
+/**
+ *     uart_parse_options - Parse serial port baud/parity/bits/flow contro.
+ *     @options: pointer to option string
+ *     @baud: pointer to an 'int' variable for the baud rate.
+ *     @parity: pointer to an 'int' variable for the parity.
+ *     @bits: pointer to an 'int' variable for the number of data bits.
+ *     @flow: pointer to an 'int' variable for the flow control character.
+ *
+ *     uart_parse_options decodes a string containing the serial console
+ *     options.  The format of the string is <baud><parity><bits><flow>,
+ *     eg: 115200n8r
+ */
+void __init
+uart_parse_options(char *options, int *baud, int *parity, int *bits, int *flow)
+{
+       char *s = options;
+
+       *baud = simple_strtoul(s, NULL, 10);
+       while (*s >= '0' && *s <= '9')
+               s++;
+       if (*s)
+               *parity = *s++;
+       if (*s)
+               *bits = *s++ - '0';
+       if (*s)
+               *flow = *s;
+}
+
+struct baud_rates {
+       unsigned int rate;
+       unsigned int cflag;
+};
+
+static struct baud_rates baud_rates[] = {
+       { 921600, B921600 },
+       { 460800, B460800 },
+       { 230400, B230400 },
+       { 115200, B115200 },
+       {  57600, B57600  },
+       {  38400, B38400  },
+       {  19200, B19200  },
+       {   9600, B9600   },
+       {   4800, B4800   },
+       {   2400, B2400   },
+       {   1200, B1200   },
+       {      0, B38400  }
+};
+
+/**
+ *     uart_set_options - setup the serial console parameters
+ *     @port: pointer to the serial ports uart_port structure
+ *     @co: console pointer
+ *     @baud: baud rate
+ *     @parity: parity character - 'n' (none), 'o' (odd), 'e' (even)
+ *     @bits: number of data bits
+ *     @flow: flow control character - 'r' (rts)
+ */
+int __init
+uart_set_options(struct uart_port *port, struct console *co,
+                int baud, int parity, int bits, int flow)
+{
+       unsigned int cflag = CREAD | HUPCL | CLOCAL;
+       unsigned int quot;
+       int i;
+
+       /*
+        * Construct a cflag setting.
+        */
+       for (i = 0; baud_rates[i].rate; i++)
+               if (baud_rates[i].rate <= baud)
+                       break;
+
+       cflag |= baud_rates[i].cflag;
+
+       if (bits == 7)
+               cflag |= CS7;
+       else
+               cflag |= CS8;
+
+       switch (parity) {
+       case 'o': case 'O':
+               cflag |= PARODD;
+               /*fall through*/
+       case 'e': case 'E':
+               cflag |= PARENB;
+               break;
+       }
+
+       if (flow == 'r')
+               cflag |= CRTSCTS;
+
+       co->cflag = cflag;
+       quot = (port->uartclk / (16 * baud));
+       port->ops->change_speed(port, cflag, 0, quot);
+
+       return 0;
+}
+
+extern void ambauart_console_init(void);
+extern void anakin_console_init(void);
+extern void clps711xuart_console_init(void);
+extern void rs285_console_init(void);
+extern void sa1100_rs_console_init(void);
+extern void serial8250_console_init(void);
+extern void uart00_console_init(void);
+
+/*
+ * Central "initialise all serial consoles" container.  Needs to be killed.
+ */
+void __init uart_console_init(void)
+{
+#ifdef CONFIG_SERIAL_AMBA_CONSOLE
+       ambauart_console_init();
+#endif
+#ifdef CONFIG_SERIAL_ANAKIN_CONSOLE
+       anakin_console_init();
+#endif
+#ifdef CONFIG_SERIAL_CLPS711X_CONSOLE
+       clps711xuart_console_init();
+#endif
+#ifdef CONFIG_SERIAL_21285_CONSOLE
+       rs285_console_init();
+#endif
+#ifdef CONFIG_SERIAL_SA1100_CONSOLE
+       sa1100_rs_console_init();
+#endif
+#ifdef CONFIG_SERIAL_8250_CONSOLE
+       serial8250_console_init();
+#endif
+#ifdef CONFIG_SERIAL_UART00_CONSOLE
+       uart00_console_init();
+#endif
+}
+#endif /* CONFIG_SERIAL_CORE_CONSOLE */
+
+#ifdef CONFIG_PM
+/*
+ *  Serial port power management.
+ *
+ * This is pretty coarse at the moment - either all on or all off.  We
+ * should probably some day do finer power management here some day.
+ *
+ * We don't actually save any state; the serial driver already has the
+ * state held internally to re-setup the port when we come out of D3.
+ */
+static int uart_pm_set_state(struct uart_state *state, int pm_state, int oldstate)
+{
+       struct uart_port *port;
+       struct uart_ops *ops;
+       int running = state->info &&
+                     state->info->flags & UIF_INITIALIZED;
+
+       down(&port_sem);
+
+       if (!state->port || state->port->type == PORT_UNKNOWN) {
+               up(&port_sem);
+               return 0;
+       }
+
+       port = state->port;
+       ops = port->ops;
+
+       DPRINTK("pm: %08x: %d -> %d, %srunning\n",
+               port->iobase, dev->state, pm_state, running ? "" : "not ");
+
+       if (pm_state == 0) {
+               if (ops->pm)
+                       ops->pm(port, pm_state, oldstate);
+               if (running) {
+                       /*
+                        * The port lock isn't taken here -
+                        * the port isn't initialised.
+                        */
+                       ops->set_mctrl(port, 0);
+                       ops->startup(port);
+                       uart_change_speed(state->info, NULL);
+                       spin_lock_irq(&port->lock);
+                       ops->set_mctrl(port, port->mctrl);
+                       ops->start_tx(port, 0);
+                       spin_unlock_irq(&port->lock);
+               }
+
+               /*
+                * Re-enable the console device after suspending.
+                */
+               if (port->cons && port->cons->index == port->line)
+                       port->cons->flags |= CON_ENABLED;
+       } else if (pm_state == 1) {
+               if (ops->pm)
+                       ops->pm(port, pm_state, oldstate);
+       } else {
+               /*
+                * Disable the console device before suspending.
+                */
+               if (port->cons && port->cons->index == port->line)
+                       port->cons->flags &= ~CON_ENABLED;
+
+               if (running) {
+                       ops->stop_tx(port, 0);
+                       spin_lock_irq(&port->lock);
+                       ops->set_mctrl(port, 0);
+                       spin_unlock_irq(&port->lock);
+                       ops->stop_rx(port);
+                       ops->shutdown(port);
+               }
+               if (ops->pm)
+                       ops->pm(port, pm_state, oldstate);
+       }
+       up(&port_sem);
+
+       return 0;
+}
+
+/*
+ *  Wakeup support.
+ */
+static int uart_pm_set_wakeup(struct uart_state *state, int data)
+{
+       int err = 0;
+
+       if (state->port->ops->set_wake)
+               err = state->port->ops->set_wake(state->port, data);
+
+       return err;
+}
+
+static int uart_pm(struct pm_dev *dev, pm_request_t rqst, void *data)
+{
+       struct uart_state *state = dev->data;
+       int err = 0;
+
+       switch (rqst) {
+       case PM_SUSPEND:
+       case PM_RESUME:
+               err = uart_pm_set_state(state, (int)data, dev->state);
+               break;
+
+       case PM_SET_WAKEUP:
+               err = uart_pm_set_wakeup(state, (int)data);
+               break;
+       }
+       return err;
+}
+#endif
+
+static inline void
+uart_report_port(struct uart_driver *drv, struct uart_port *port)
+{
+       printk("%s%d at ", drv->dev_name, port->line);
+       switch (port->iotype) {
+       case UPIO_PORT:
+               printk("I/O 0x%x", port->iobase);
+               break;
+       case UPIO_HUB6:
+               printk("I/O 0x%x offset 0x%x", port->iobase, port->hub6);
+               break;
+       case UPIO_MEM:
+               printk("MMIO 0x%lx", port->mapbase);
+               break;
+       }
+       printk(" (irq = %d) is a %s\n", port->irq, uart_type(port));
+}
+
+static void
+__uart_register_port(struct uart_driver *drv, struct uart_state *state,
+                    struct uart_port *port)
+{
+       unsigned int flags;
+
+       state->port = port;
+
+       spin_lock_init(&port->lock);
+       port->type = PORT_UNKNOWN;
+       port->cons = drv->cons;
+       port->info = state->info;
+
+       /*
+        * If there isn't a port here, don't do anything further.
+        */
+       if (!port->iobase && !port->mapbase)
+               return;
+
+       /*
+        * Now do the auto configuration stuff.  Note that config_port
+        * is expected to claim the resources and map the port for us.
+        */
+       flags = UART_CONFIG_TYPE;
+       if (port->flags & UPF_AUTO_IRQ)
+               flags |= UART_CONFIG_IRQ;
+       if (port->flags & UPF_BOOT_AUTOCONF)
+               port->ops->config_port(port, flags);
+
+       /*
+        * Register the port whether it's detected or not.  This allows
+        * setserial to be used to alter this ports parameters.
+        */
+       tty_register_devfs(drv->tty_driver, 0, drv->minor + port->line);
+
+       if (port->type != PORT_UNKNOWN) {
+               unsigned long flags;
+
+               uart_report_port(drv, port);
+
+               /*
+                * Ensure that the modem control lines are de-activated.
+                * We probably don't need a spinlock around this, but
+                */
+               spin_lock_irqsave(&port->lock, flags);
+               port->ops->set_mctrl(port, 0);
+               spin_unlock_irqrestore(&port->lock, flags);
+
+#ifdef CONFIG_PM
+               /*
+                * Power down all ports by default, except the
+                * console if we have one.  We need to drop the
+                * port semaphore here.
+                */
+               if (state->pm && (!drv->cons || port->line != drv->cons->index)) {
+                       up(&port_sem);
+                       pm_send(state->pm, PM_SUSPEND, (void *)3);
+                       down(&port_sem);
+               }
+#endif
+       }
+}
+
+/*
+ *     Hangup the port.  This must be done outside the port_sem
+ *     since uart_hangup() grabs this same semaphore.  Grr.
+ */
+static void
+__uart_hangup_port(struct uart_driver *drv, struct uart_state *state)
+{
+       struct uart_info *info = state->info;
+
+       if (info && info->tty)
+               tty_vhangup(info->tty);
+}
+
+/*
+ * This reverses the affects of __uart_register_port.
+ */
+static void
+__uart_unregister_port(struct uart_driver *drv, struct uart_state *state)
+{
+       struct uart_port *port = state->port;
+       struct uart_info *info = state->info;
+
+       state->info = NULL;
+
+       /*
+        * Remove the devices from devfs
+        */
+       tty_unregister_devfs(drv->tty_driver, drv->minor + port->line);
+
+       /*
+        * Free the port IO and memory resources, if any.
+        */
+       if (port->type != PORT_UNKNOWN)
+               port->ops->release_port(port);
+
+       /*
+        * Indicate that there isn't a port here anymore.
+        */
+       port->type = PORT_UNKNOWN;
+
+       /*
+        * Kill the tasklet, and free resources.
+        */
+       if (info) {
+               tasklet_kill(&info->tlet);
+               kfree(info);
+       }
+}
+
+/**
+ *     uart_register_driver - register a driver with the uart core layer
+ *     @drv: low level driver structure
+ *
+ *     Register a uart driver with the core driver.  We in turn register
+ *     with the tty layer, and initialise the core driver per-port state.
+ *
+ *     We have a proc file in /proc/tty/driver which is named after the
+ *     normal driver.
+ *
+ *     drv->port should be NULL, and the per-port structures should be
+ *     registered using uart_add_one_port after this call has succeeded.
+ */
+int uart_register_driver(struct uart_driver *drv)
+{
+       struct tty_driver *normal = NULL;
+       struct termios **termios = NULL;
+       int i, retval;
+
+       BUG_ON(drv->state);
+
+       /*
+        * Maybe we should be using a slab cache for this, especially if
+        * we have a large number of ports to handle.  Note that we also
+        * allocate space for an integer for reference counting.
+        */
+       drv->state = kmalloc(sizeof(struct uart_state) * drv->nr +
+                            sizeof(int), GFP_KERNEL);
+       retval = -ENOMEM;
+       if (!drv->state)
+               goto out;
+
+       memset(drv->state, 0, sizeof(struct uart_state) * drv->nr +
+                       sizeof(int));
+
+       termios = kmalloc(sizeof(struct termios *) * drv->nr * 2 +
+                         sizeof(struct tty_struct *) * drv->nr, GFP_KERNEL);
+       if (!termios)
+               goto out;
+
+       memset(termios, 0, sizeof(struct termios *) * drv->nr * 2 +
+                          sizeof(struct tty_struct *) * drv->nr);
+
+       normal  = kmalloc(sizeof(struct tty_driver), GFP_KERNEL);
+       if (!normal)
+               goto out;
+
+       memset(normal, 0, sizeof(struct tty_driver));
+
+       drv->tty_driver = normal;
+
+       normal->magic           = TTY_DRIVER_MAGIC;
+       normal->driver_name     = drv->driver_name;
+       normal->name            = drv->dev_name;
+       normal->major           = drv->major;
+       normal->minor_start     = drv->minor;
+       normal->num             = drv->nr;
+       normal->type            = TTY_DRIVER_TYPE_SERIAL;
+       normal->subtype         = SERIAL_TYPE_NORMAL;
+       normal->init_termios    = tty_std_termios;
+       normal->init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL;
+       normal->flags           = TTY_DRIVER_REAL_RAW | TTY_DRIVER_NO_DEVFS;
+       normal->refcount        = (int *)(drv->state + drv->nr);
+       normal->termios         = termios;
+       normal->termios_locked  = termios + drv->nr;
+       normal->table           = (struct tty_struct **)(termios + drv->nr * 2);
+       normal->driver_state    = drv;
+
+       normal->open            = uart_open;
+       normal->close           = uart_close;
+       normal->write           = uart_write;
+       normal->put_char        = uart_put_char;
+       normal->flush_chars     = uart_flush_chars;
+       normal->write_room      = uart_write_room;
+       normal->chars_in_buffer = uart_chars_in_buffer;
+       normal->flush_buffer    = uart_flush_buffer;
+       normal->ioctl           = uart_ioctl;
+       normal->throttle        = uart_throttle;
+       normal->unthrottle      = uart_unthrottle;
+       normal->send_xchar      = uart_send_xchar;
+       normal->set_termios     = uart_set_termios;
+       normal->stop            = uart_stop;
+       normal->start           = uart_start;
+       normal->hangup          = uart_hangup;
+       normal->break_ctl       = uart_break_ctl;
+       normal->wait_until_sent = uart_wait_until_sent;
+#ifdef CONFIG_PROC_FS
+       normal->read_proc       = uart_read_proc;
+#endif
+
+       /*
+        * Initialise the UART state(s).
+        */
+       for (i = 0; i < drv->nr; i++) {
+               struct uart_state *state = drv->state + i;
+
+               state->close_delay     = 5 * HZ / 10;
+               state->closing_wait    = 30 * HZ;
+#ifdef CONFIG_PM
+               state->pm = pm_register(PM_SYS_DEV, PM_SYS_COM, uart_pm);
+               if (state->pm)
+                       state->pm->data = state;
+#endif
+       }
+
+       retval = tty_register_driver(normal);
+ out:
+       if (retval < 0) {
+#ifdef CONFIG_PM
+               for (i = 0; i < drv->nr; i++)
+                       pm_unregister(drv->state[i].pm);
+#endif
+               kfree(normal);
+               kfree(drv->state);
+               kfree(termios);
+       }
+       return retval;
+}
+
+/**
+ *     uart_unregister_driver - remove a driver from the uart core layer
+ *     @drv: low level driver structure
+ *
+ *     Remove all references to a driver from the core driver.  The low
+ *     level driver must have removed all its ports via the
+ *     uart_remove_one_port() if it registered them with uart_add_one_port().
+ *     (ie, drv->port == NULL)
+ */
+void uart_unregister_driver(struct uart_driver *drv)
+{
+       int i;
+
+       for (i = 0; i < drv->nr; i++)
+               pm_unregister(drv->state[i].pm);
+
+       tty_unregister_driver(drv->tty_driver);
+
+       kfree(drv->state);
+       kfree(drv->tty_driver->termios);
+       kfree(drv->tty_driver);
+}
+
+/**
+ *     uart_add_one_port - attach a driver-defined port structure
+ *     @drv: pointer to the uart low level driver structure for this port
+ *     @port: uart port structure to use for this port.
+ *
+ *     This allows the driver to register its own uart_port structure
+ *     with the core driver.  The main purpose is to allow the low
+ *     level uart drivers to expand uart_port, rather than having yet
+ *     more levels of structures.
+ */
+int uart_add_one_port(struct uart_driver *drv, struct uart_port *port)
+{
+       struct uart_state *state;
+
+       BUG_ON(in_interrupt());
+
+       if (port->line >= drv->nr)
+               return -EINVAL;
+
+       state = drv->state + port->line;
+
+       down(&port_sem);
+       __uart_register_port(drv, state, port);
+       up(&port_sem);
+
+       return 0;
+}
+
+/**
+ *     uart_remove_one_port - detach a driver defined port structure
+ *     @drv: pointer to the uart low level driver structure for this port
+ *     @port: uart port structure for this port
+ *
+ *     This unhooks (and hangs up) the specified port structure from the
+ *     core driver.  No further calls will be made to the low-level code
+ *     for this port.
+ */
+int uart_remove_one_port(struct uart_driver *drv, struct uart_port *port)
+{
+       struct uart_state *state = drv->state + port->line;
+
+       BUG_ON(in_interrupt());
+
+       if (state->port != port)
+               printk(KERN_ALERT "Removing wrong port: %p != %p\n",
+                       state->port, port);
+
+       __uart_hangup_port(drv, state);
+
+       down(&port_sem);
+       __uart_unregister_port(drv, state);
+       state->port = NULL;
+       up(&port_sem);
+
+       return 0;
+}
+
+/*
+ *     Are the two ports equivalent?
+ */
+static int uart_match_port(struct uart_port *port1, struct uart_port *port2)
+{
+       if (port1->iotype != port2->iotype)
+               return 0;
+
+       switch (port1->iotype) {
+       case UPIO_PORT:
+               return (port1->iobase == port2->iobase);
+       case UPIO_HUB6:
+               return (port1->iobase == port2->iobase) &&
+                      (port1->hub6   == port2->hub6);
+       case UPIO_MEM:
+               return (port1->membase == port2->membase);
+       }
+       return 0;
+}
+
+/*
+ *     Try to find an unused uart_state slot for a port.
+ */
+static struct uart_state *
+uart_find_match_or_unused(struct uart_driver *drv, struct uart_port *port)
+{
+       int i;
+
+       /*
+        * First, find a port entry which matches.  Note: if we do
+        * find a matching entry, and it has a non-zero use count,
+        * then we can't register the port.
+        */
+       for (i = 0; i < drv->nr; i++)
+               if (uart_match_port(drv->state[i].port, port))
+                       return &drv->state[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 < drv->nr; i++)
+               if (drv->state[i].port->type == PORT_UNKNOWN &&
+                   drv->state[i].port->iobase == 0 &&
+                   drv->state[i].count == 0)
+                       return &drv->state[i];
+
+       /*
+        * That also failed.  Last resort is to find any currently
+        * entry which doesn't have a real port associated with it.
+        */
+       for (i = 0; i < drv->nr; i++)
+               if (drv->state[i].port->type == PORT_UNKNOWN &&
+                   drv->state[i].count == 0)
+                       return &drv->state[i];
+
+       return NULL;
+}
+
+/**
+ *     uart_register_port: register uart settings with a port
+ *     @drv: pointer to the uart low level driver structure for this port
+ *     @port: uart port structure describing the port
+ *
+ *     Register UART settings with the specified low level driver.  Detect
+ *     the type of the port if UPF_BOOT_AUTOCONF is set, and detect the
+ *     IRQ if UPF_AUTO_IRQ is set.
+ *
+ *     We try to pick the same port for the same IO base address, so that
+ *     when a modem is plugged in, unplugged and plugged back in, it gets
+ *     allocated the same port.
+ *
+ *     Returns negative error, or positive line number.
+ */
+int uart_register_port(struct uart_driver *drv, struct uart_port *port)
+{
+       struct uart_state *state;
+       int ret;
+
+       down(&port_sem);
+
+       state = uart_find_match_or_unused(drv, port);
+
+       if (state) {
+               /*
+                * Ok, we've found a line that we can use.
+                *
+                * If we find a port that matches this one, and it appears
+                * to be in-use (even if it doesn't have a type) we shouldn't
+                * alter it underneath itself - the port may be open and
+                * trying to do useful work.
+                */
+               if (state->count != 0 ||
+                   (state->info && state->info->blocked_open != 0)) {
+                       ret = -EBUSY;
+                       goto out;
+               }
+
+               state->port->iobase   = port->iobase;
+               state->port->membase  = port->membase;
+               state->port->irq      = port->irq;
+               state->port->uartclk  = port->uartclk;
+               state->port->fifosize = port->fifosize;
+               state->port->regshift = port->regshift;
+               state->port->iotype   = port->iotype;
+               state->port->flags    = port->flags;
+               state->port->line     = drv->state - state;
+
+               __uart_register_port(drv, state, state->port);
+
+               ret = state->port->line;
+       } else
+               ret = -ENOSPC;
+ out:
+       up(&port_sem);
+       return ret;
+}
+
+/**
+ *     uart_unregister_port - de-allocate a port
+ *     @drv: pointer to the uart low level driver structure for this port
+ *     @line: line index previously returned from uart_register_port()
+ *
+ *     Hang up the specified line associated with the low level driver,
+ *     and mark the port as unused.
+ */
+void uart_unregister_port(struct uart_driver *drv, int line)
+{
+       struct uart_state *state;
+
+       if (line < 0 || line >= drv->nr) {
+               printk(KERN_ERR "Attempt to unregister %s%d\n",
+                       drv->dev_name, line);
+               return;
+       }
+
+       state = drv->state + line;
+
+       __uart_hangup_port(drv, state);
+
+       down(&port_sem);
+       __uart_unregister_port(drv, state);
+       up(&port_sem);
+}
+
+EXPORT_SYMBOL(uart_event);
+EXPORT_SYMBOL(uart_register_driver);
+EXPORT_SYMBOL(uart_unregister_driver);
+EXPORT_SYMBOL(uart_register_port);
+EXPORT_SYMBOL(uart_unregister_port);
+
+MODULE_DESCRIPTION("Serial driver core");
+MODULE_LICENSE("GPL");
diff --git a/drivers/serial/serial_sa1100.c b/drivers/serial/serial_sa1100.c
new file mode 100644 (file)
index 0000000..0f578fc
--- /dev/null
@@ -0,0 +1,899 @@
+/*
+ *  linux/drivers/char/serial_sa1100.c
+ *
+ *  Driver for SA11x0 serial ports
+ *
+ *  Based on drivers/char/serial.c, by Linus Torvalds, Theodore Ts'o.
+ *
+ *  Copyright (C) 2000 Deep Blue Solutions Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ *  $Id: serial_sa1100.c,v 1.41 2002/07/21 08:57:55 rmk Exp $
+ *
+ */
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <linux/signal.h>
+#include <linux/sched.h>
+#include <linux/interrupt.h>
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
+#include <linux/major.h>
+#include <linux/string.h>
+#include <linux/fcntl.h>
+#include <linux/ptrace.h>
+#include <linux/ioport.h>
+#include <linux/mm.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <linux/circ_buf.h>
+#include <linux/serial.h>
+#include <linux/console.h>
+#include <linux/sysrq.h>
+
+#include <asm/system.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/uaccess.h>
+#include <asm/bitops.h>
+#include <asm/hardware.h>
+#include <asm/mach/serial_sa1100.h>
+
+#if defined(CONFIG_SERIAL_SA1100_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ)
+#define SUPPORT_SYSRQ
+#endif
+
+#include <linux/serial_core.h>
+
+/* We've been assigned a range on the "Low-density serial ports" major */
+#define SERIAL_SA1100_MAJOR    204
+#define MINOR_START            5
+
+#define NR_PORTS               3
+
+#define SA1100_ISR_PASS_LIMIT  256
+
+/*
+ * Convert from ignore_status_mask or read_status_mask to UTSR[01]
+ */
+#define SM_TO_UTSR0(x) ((x) & 0xff)
+#define SM_TO_UTSR1(x) ((x) >> 8)
+#define UTSR0_TO_SM(x) ((x))
+#define UTSR1_TO_SM(x) ((x) << 8)
+
+#define UART_GET_UTCR0(sport)  __raw_readl((sport)->port.membase + UTCR0)
+#define UART_GET_UTCR1(sport)  __raw_readl((sport)->port.membase + UTCR1)
+#define UART_GET_UTCR2(sport)  __raw_readl((sport)->port.membase + UTCR2)
+#define UART_GET_UTCR3(sport)  __raw_readl((sport)->port.membase + UTCR3)
+#define UART_GET_UTSR0(sport)  __raw_readl((sport)->port.membase + UTSR0)
+#define UART_GET_UTSR1(sport)  __raw_readl((sport)->port.membase + UTSR1)
+#define UART_GET_CHAR(sport)   __raw_readl((sport)->port.membase + UTDR)
+
+#define UART_PUT_UTCR0(sport,v)        __raw_writel((v),(sport)->port.membase + UTCR0)
+#define UART_PUT_UTCR1(sport,v)        __raw_writel((v),(sport)->port.membase + UTCR1)
+#define UART_PUT_UTCR2(sport,v)        __raw_writel((v),(sport)->port.membase + UTCR2)
+#define UART_PUT_UTCR3(sport,v)        __raw_writel((v),(sport)->port.membase + UTCR3)
+#define UART_PUT_UTSR0(sport,v)        __raw_writel((v),(sport)->port.membase + UTSR0)
+#define UART_PUT_UTSR1(sport,v)        __raw_writel((v),(sport)->port.membase + UTSR1)
+#define UART_PUT_CHAR(sport,v) __raw_writel((v),(sport)->port.membase + UTDR)
+
+/*
+ * This is the size of our serial port register set.
+ */
+#define UART_PORT_SIZE 0x24
+
+/*
+ * This determines how often we check the modem status signals
+ * for any change.  They generally aren't connected to an IRQ
+ * so we have to poll them.  We also check immediately before
+ * filling the TX fifo incase CTS has been dropped.
+ */
+#define MCTRL_TIMEOUT  (250*HZ/1000)
+
+struct sa1100_port {
+       struct uart_port        port;
+       struct timer_list       timer;
+       unsigned int            old_status;
+};
+
+/*
+ * Handle any change of modem status signal since we were last called.
+ */
+static void sa1100_mctrl_check(struct sa1100_port *sport)
+{
+       unsigned int status, changed;
+
+       status = sport->port.ops->get_mctrl(&sport->port);
+       changed = status ^ sport->old_status;
+
+       if (changed == 0)
+               return;
+
+       sport->old_status = status;
+
+       if (changed & TIOCM_RI)
+               sport->port.icount.rng++;
+       if (changed & TIOCM_DSR)
+               sport->port.icount.dsr++;
+       if (changed & TIOCM_CAR)
+               uart_handle_dcd_change(&sport->port, status & TIOCM_CAR);
+       if (changed & TIOCM_CTS)
+               uart_handle_cts_change(&sport->port, status & TIOCM_CTS);
+
+       wake_up_interruptible(&sport->port.info->delta_msr_wait);
+}
+
+/*
+ * This is our per-port timeout handler, for checking the
+ * modem status signals.
+ */
+static void sa1100_timeout(unsigned long data)
+{
+       struct sa1100_port *sport = (struct sa1100_port *)data;
+       unsigned long flags;
+
+       if (sport->port.info) {
+               spin_lock_irqsave(&sport->port.lock, flags);
+               sa1100_mctrl_check(sport);
+               spin_unlock_irqrestore(&sport->port.lock, flags);
+
+               mod_timer(&sport->timer, jiffies + MCTRL_TIMEOUT);
+       }
+}
+
+static void __sa1100_stop_tx(struct sa1100_port *sport)
+{
+       u32 utcr3;
+
+       utcr3 = UART_GET_UTCR3(sport);
+       UART_PUT_UTCR3(sport, utcr3 & ~UTCR3_TIE);
+       sport->port.read_status_mask &= ~UTSR0_TO_SM(UTSR0_TFS);
+}
+
+/*
+ * interrupts disabled on entry
+ */
+static void sa1100_stop_tx(struct uart_port *port, unsigned int tty_stop)
+{
+       struct sa1100_port *sport = (struct sa1100_port *)port;
+       unsigned long flags;
+
+       spin_lock_irqsave(&sport->port.lock, flags);
+       __sa1100_stop_tx(sport);
+       spin_unlock_irqrestore(&sport->port.lock, flags);
+}
+
+/*
+ * interrupts may not be disabled on entry
+ */
+static void sa1100_start_tx(struct uart_port *port, unsigned int tty_start)
+{
+       struct sa1100_port *sport = (struct sa1100_port *)port;
+       unsigned long flags;
+       u32 utcr3;
+
+       spin_lock_irqsave(&sport->port.lock, flags);
+       utcr3 = UART_GET_UTCR3(sport);
+       sport->port.read_status_mask |= UTSR0_TO_SM(UTSR0_TFS);
+       UART_PUT_UTCR3(sport, utcr3 | UTCR3_TIE);
+       spin_unlock_irqrestore(&sport->port.lock, flags);
+}
+
+/*
+ * Interrupts enabled
+ */
+static void sa1100_stop_rx(struct uart_port *port)
+{
+       struct sa1100_port *sport = (struct sa1100_port *)port;
+       unsigned long flags;
+       u32 utcr3;
+
+       spin_lock_irqsave(&sport->port.lock, flags);
+       utcr3 = UART_GET_UTCR3(sport);
+       UART_PUT_UTCR3(sport, utcr3 & ~UTCR3_RIE);
+       spin_unlock_irqrestore(&sport->port.lock, flags);
+}
+
+/*
+ * Set the modem control timer to fire immediately.
+ */
+static void sa1100_enable_ms(struct uart_port *port)
+{
+       struct sa1100_port *sport = (struct sa1100_port *)port;
+
+       mod_timer(&sport->timer, jiffies);
+}
+
+static void
+sa1100_rx_chars(struct sa1100_port *sport, struct pt_regs *regs)
+{
+       struct tty_struct *tty = sport->port.info->tty;
+       unsigned int status, ch, flg, ignored = 0;
+
+       status = UTSR1_TO_SM(UART_GET_UTSR1(sport)) |
+                UTSR0_TO_SM(UART_GET_UTSR0(sport));
+       while (status & UTSR1_TO_SM(UTSR1_RNE)) {
+               ch = UART_GET_CHAR(sport);
+
+               if (tty->flip.count >= TTY_FLIPBUF_SIZE)
+                       goto ignore_char;
+               sport->port.icount.rx++;
+
+               flg = TTY_NORMAL;
+
+               /*
+                * note that the error handling code is
+                * out of the main execution path
+                */
+               if (status & UTSR1_TO_SM(UTSR1_PRE | UTSR1_FRE | UTSR1_ROR))
+                       goto handle_error;
+
+               if (uart_handle_sysrq_char(&sport->port, ch, regs))
+                       goto ignore_char;
+
+       error_return:
+               *tty->flip.flag_buf_ptr++ = flg;
+               *tty->flip.char_buf_ptr++ = ch;
+               tty->flip.count++;
+       ignore_char:
+               status = UTSR1_TO_SM(UART_GET_UTSR1(sport)) |
+                        UTSR0_TO_SM(UART_GET_UTSR0(sport));
+       }
+ out:
+       tty_flip_buffer_push(tty);
+       return;
+
+ handle_error:
+       if (status & UTSR1_TO_SM(UTSR1_PRE))
+               sport->port.icount.parity++;
+       else if (status & UTSR1_TO_SM(UTSR1_FRE))
+               sport->port.icount.frame++;
+       if (status & UTSR1_TO_SM(UTSR1_ROR))
+               sport->port.icount.overrun++;
+
+       if (status & sport->port.ignore_status_mask) {
+               if (++ignored > 100)
+                       goto out;
+               goto ignore_char;
+       }
+
+       status &= sport->port.read_status_mask;
+
+       if (status & UTSR1_TO_SM(UTSR1_PRE))
+               flg = TTY_PARITY;
+       else if (status & UTSR1_TO_SM(UTSR1_FRE))
+               flg = TTY_FRAME;
+
+       if (status & UTSR1_TO_SM(UTSR1_ROR)) {
+               /*
+                * overrun does *not* affect the character
+                * we read from the FIFO
+                */
+               *tty->flip.flag_buf_ptr++ = flg;
+               *tty->flip.char_buf_ptr++ = ch;
+               tty->flip.count++;
+               if (tty->flip.count >= TTY_FLIPBUF_SIZE)
+                       goto ignore_char;
+               ch = 0;
+               flg = TTY_OVERRUN;
+       }
+#ifdef SUPPORT_SYSRQ
+       sport->port.sysrq = 0;
+#endif
+       goto error_return;
+}
+
+static void sa1100_tx_chars(struct sa1100_port *sport)
+{
+       struct circ_buf *xmit = &sport->port.info->xmit;
+
+       if (sport->port.x_char) {
+               UART_PUT_CHAR(sport, sport->port.x_char);
+               sport->port.icount.tx++;
+               sport->port.x_char = 0;
+               return;
+       }
+
+       /*
+        * Check the modem control lines before
+        * transmitting anything.
+        */
+       sa1100_mctrl_check(sport);
+
+       if (uart_circ_empty(xmit) || uart_tx_stopped(&sport->port)) {
+               __sa1100_stop_tx(sport);
+               return;
+       }
+
+       /*
+        * Tried using FIFO (not checking TNF) for fifo fill:
+        * still had the '4 bytes repeated' problem.
+        */
+       while (UART_GET_UTSR1(sport) & UTSR1_TNF) {
+               UART_PUT_CHAR(sport, xmit->buf[xmit->tail]);
+               xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
+               sport->port.icount.tx++;
+               if (uart_circ_empty(xmit))
+                       break;
+       }
+
+       if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
+               uart_event(&sport->port, EVT_WRITE_WAKEUP);
+
+       if (uart_circ_empty(xmit))
+               __sa1100_stop_tx(sport);
+}
+
+static void sa1100_int(int irq, void *dev_id, struct pt_regs *regs)
+{
+       struct sa1100_port *sport = dev_id;
+       unsigned int status, pass_counter = 0;
+
+       spin_lock(&sport->port.lock);
+       status = UART_GET_UTSR0(sport);
+       status &= SM_TO_UTSR0(sport->port.read_status_mask) | ~UTSR0_TFS;
+       do {
+               if (status & (UTSR0_RFS | UTSR0_RID)) {
+                       /* Clear the receiver idle bit, if set */
+                       if (status & UTSR0_RID)
+                               UART_PUT_UTSR0(sport, UTSR0_RID);
+                       sa1100_rx_chars(sport, regs);
+               }
+
+               /* Clear the relevent break bits */
+               if (status & (UTSR0_RBB | UTSR0_REB))
+                       UART_PUT_UTSR0(sport, status & (UTSR0_RBB | UTSR0_REB));
+
+               if (status & UTSR0_RBB)
+                       sport->port.icount.brk++;
+
+               if (status & UTSR0_REB)
+                       uart_handle_break(&sport->port);
+
+               if (status & UTSR0_TFS)
+                       sa1100_tx_chars(sport);
+               if (pass_counter++ > SA1100_ISR_PASS_LIMIT)
+                       break;
+               status = UART_GET_UTSR0(sport);
+               status &= SM_TO_UTSR0(sport->port.read_status_mask) |
+                         ~UTSR0_TFS;
+       } while (status & (UTSR0_TFS | UTSR0_RFS | UTSR0_RID));
+       spin_unlock(&sport->port.lock);
+}
+
+/*
+ * Return TIOCSER_TEMT when transmitter is not busy.
+ */
+static unsigned int sa1100_tx_empty(struct uart_port *port)
+{
+       struct sa1100_port *sport = (struct sa1100_port *)port;
+
+       return UART_GET_UTSR1(sport) & UTSR1_TBY ? 0 : TIOCSER_TEMT;
+}
+
+static unsigned int sa1100_get_mctrl(struct uart_port *port)
+{
+       return TIOCM_CTS | TIOCM_DSR | TIOCM_CAR;
+}
+
+static void sa1100_set_mctrl(struct uart_port *port, unsigned int mctrl)
+{
+}
+
+/*
+ * Interrupts always disabled.
+ */
+static void sa1100_break_ctl(struct uart_port *port, int break_state)
+{
+       struct sa1100_port *sport = (struct sa1100_port *)port;
+       unsigned long flags;
+       unsigned int utcr3;
+
+       spin_lock_irqsave(&sport->port.lock, flags);
+       utcr3 = UART_GET_UTCR3(sport);
+       if (break_state == -1)
+               utcr3 |= UTCR3_BRK;
+       else
+               utcr3 &= ~UTCR3_BRK;
+       UART_PUT_UTCR3(sport, utcr3);
+       spin_unlock_irqrestore(&sport->port.lock, flags);
+}
+
+static int sa1100_startup(struct uart_port *port)
+{
+       struct sa1100_port *sport = (struct sa1100_port *)port;
+       int retval;
+
+       /*
+        * Allocate the IRQ
+        */
+       retval = request_irq(sport->port.irq, sa1100_int, 0,
+                            "serial_sa1100", sport);
+       if (retval)
+               return retval;
+
+       /*
+        * Finally, clear and enable interrupts
+        */
+       UART_PUT_UTSR0(sport, -1);
+       UART_PUT_UTCR3(sport, UTCR3_RXE | UTCR3_TXE | UTCR3_RIE);
+
+       /*
+        * Enable modem status interrupts
+        */
+       sa1100_enable_ms(&sport->port);
+
+       return 0;
+}
+
+static void sa1100_shutdown(struct uart_port *port)
+{
+       struct sa1100_port *sport = (struct sa1100_port *)port;
+
+       /*
+        * Stop our timer.
+        */
+       del_timer_sync(&sport->timer);
+
+       /*
+        * Free the interrupt
+        */
+       free_irq(sport->port.irq, sport);
+
+       /*
+        * Disable all interrupts, port and break condition.
+        */
+       UART_PUT_UTCR3(sport, 0);
+}
+
+static void
+sa1100_change_speed(struct uart_port *port, unsigned int cflag,
+                   unsigned int iflag, unsigned int quot)
+{
+       struct sa1100_port *sport = (struct sa1100_port *)port;
+       unsigned long flags;
+       unsigned int utcr0, old_utcr3;
+
+       /* byte size and parity */
+       switch (cflag & CSIZE) {
+       case CS7:
+               utcr0 = 0;
+               break;
+       default:
+               utcr0 = UTCR0_DSS;
+               break;
+       }
+       if (cflag & CSTOPB)
+               utcr0 |= UTCR0_SBS;
+       if (cflag & PARENB) {
+               utcr0 |= UTCR0_PE;
+               if (!(cflag & PARODD))
+                       utcr0 |= UTCR0_OES;
+       }
+
+       sport->port.read_status_mask &= UTSR0_TO_SM(UTSR0_TFS);
+       sport->port.read_status_mask |= UTSR1_TO_SM(UTSR1_ROR);
+       if (iflag & INPCK)
+               sport->port.read_status_mask |=
+                               UTSR1_TO_SM(UTSR1_FRE | UTSR1_PRE);
+       if (iflag & (BRKINT | PARMRK))
+               sport->port.read_status_mask |=
+                               UTSR0_TO_SM(UTSR0_RBB | UTSR0_REB);
+
+       /*
+        * Characters to ignore
+        */
+       sport->port.ignore_status_mask = 0;
+       if (iflag & IGNPAR)
+               sport->port.ignore_status_mask |=
+                               UTSR1_TO_SM(UTSR1_FRE | UTSR1_PRE);
+       if (iflag & IGNBRK) {
+               sport->port.ignore_status_mask |=
+                               UTSR0_TO_SM(UTSR0_RBB | UTSR0_REB);
+               /*
+                * If we're ignoring parity and break indicators,
+                * ignore overruns too (for real raw support).
+                */
+               if (iflag & IGNPAR)
+                       sport->port.ignore_status_mask |=
+                               UTSR1_TO_SM(UTSR1_ROR);
+       }
+
+       del_timer_sync(&sport->timer);
+
+       /* first, disable interrupts and drain transmitter */
+       spin_lock_irqsave(&sport->port.lock, flags);
+       old_utcr3 = UART_GET_UTCR3(sport);
+       UART_PUT_UTCR3(sport, old_utcr3 & ~(UTCR3_RIE | UTCR3_TIE));
+
+       while (UART_GET_UTSR1(sport) & UTSR1_TBY);
+
+       /* then, disable everything */
+       UART_PUT_UTCR3(sport, 0);
+
+       /* set the parity, stop bits and data size */
+       UART_PUT_UTCR0(sport, utcr0);
+
+       /* set the baud rate */
+       quot -= 1;
+       UART_PUT_UTCR1(sport, ((quot & 0xf00) >> 8));
+       UART_PUT_UTCR2(sport, (quot & 0xff));
+
+       UART_PUT_UTSR0(sport, -1);
+
+       UART_PUT_UTCR3(sport, old_utcr3);
+       spin_unlock_irqrestore(&sport->port.lock, flags);
+
+       if (UART_ENABLE_MS(&sport->port, cflag))
+               sa1100_enable_ms(&sport->port);
+}
+
+static const char *sa1100_type(struct uart_port *port)
+{
+       struct sa1100_port *sport = (struct sa1100_port *)port;
+
+       return sport->port.type == PORT_SA1100 ? "SA1100" : NULL;
+}
+
+/*
+ * Release the memory region(s) being used by 'port'.
+ */
+static void sa1100_release_port(struct uart_port *port)
+{
+       struct sa1100_port *sport = (struct sa1100_port *)port;
+
+       release_mem_region(sport->port.mapbase, UART_PORT_SIZE);
+}
+
+/*
+ * Request the memory region(s) being used by 'port'.
+ */
+static int sa1100_request_port(struct uart_port *port)
+{
+       struct sa1100_port *sport = (struct sa1100_port *)port;
+
+       return request_mem_region(sport->port.mapbase, UART_PORT_SIZE,
+                       "serial_sa1100") != NULL ? 0 : -EBUSY;
+}
+
+/*
+ * Configure/autoconfigure the port.
+ */
+static void sa1100_config_port(struct uart_port *port, int flags)
+{
+       struct sa1100_port *sport = (struct sa1100_port *)port;
+
+       if (flags & UART_CONFIG_TYPE &&
+           sa1100_request_port(&sport->port) == 0)
+               sport->port.type = PORT_SA1100;
+}
+
+/*
+ * Verify the new serial_struct (for TIOCSSERIAL).
+ * The only change we allow are to the flags and type, and
+ * even then only between PORT_SA1100 and PORT_UNKNOWN
+ */
+static int
+sa1100_verify_port(struct uart_port *port, struct serial_struct *ser)
+{
+       struct sa1100_port *sport = (struct sa1100_port *)port;
+       int ret = 0;
+
+       if (ser->type != PORT_UNKNOWN && ser->type != PORT_SA1100)
+               ret = -EINVAL;
+       if (sport->port.irq != ser->irq)
+               ret = -EINVAL;
+       if (ser->io_type != SERIAL_IO_MEM)
+               ret = -EINVAL;
+       if (sport->port.uartclk / 16 != ser->baud_base)
+               ret = -EINVAL;
+       if ((void *)sport->port.mapbase != ser->iomem_base)
+               ret = -EINVAL;
+       if (sport->port.iobase != ser->port)
+               ret = -EINVAL;
+       if (ser->hub6 != 0)
+               ret = -EINVAL;
+       return ret;
+}
+
+static struct uart_ops sa1100_pops = {
+       tx_empty:       sa1100_tx_empty,
+       set_mctrl:      sa1100_set_mctrl,
+       get_mctrl:      sa1100_get_mctrl,
+       stop_tx:        sa1100_stop_tx,
+       start_tx:       sa1100_start_tx,
+       stop_rx:        sa1100_stop_rx,
+       enable_ms:      sa1100_enable_ms,
+       break_ctl:      sa1100_break_ctl,
+       startup:        sa1100_startup,
+       shutdown:       sa1100_shutdown,
+       change_speed:   sa1100_change_speed,
+       type:           sa1100_type,
+       release_port:   sa1100_release_port,
+       request_port:   sa1100_request_port,
+       config_port:    sa1100_config_port,
+       verify_port:    sa1100_verify_port,
+};
+
+static struct sa1100_port sa1100_ports[NR_PORTS];
+
+/*
+ * Setup the SA1100 serial ports.  Note that we don't include the IrDA
+ * port here since we have our own SIR/FIR driver (see drivers/net/irda)
+ *
+ * Note also that we support "console=ttySAx" where "x" is either 0 or 1.
+ * Which serial port this ends up being depends on the machine you're
+ * running this kernel on.  I'm not convinced that this is a good idea,
+ * but that's the way it traditionally works.
+ *
+ * Note that NanoEngine UART3 becomes UART2, and UART2 is no longer
+ * used here.
+ */
+static void __init sa1100_init_ports(void)
+{
+       static int first = 1;
+       int i;
+
+       if (!first)
+               return;
+       first = 0;
+
+       for (i = 0; i < NR_PORTS; i++) {
+               sa1100_ports[i].port.uartclk   = 3686400;
+               sa1100_ports[i].port.ops       = &sa1100_pops;
+               sa1100_ports[i].port.fifosize  = 8;
+               sa1100_ports[i].port.line      = i;
+               sa1100_ports[i].port.iotype    = SERIAL_IO_MEM;
+               init_timer(&sa1100_ports[i].timer);
+               sa1100_ports[i].timer.function = sa1100_timeout;
+               sa1100_ports[i].timer.data     = (unsigned long)&sa1100_ports[i];
+       }
+
+       /*
+        * make transmit lines outputs, so that when the port
+        * is closed, the output is in the MARK state.
+        */
+       PPDR |= PPC_TXD1 | PPC_TXD3;
+       PPSR |= PPC_TXD1 | PPC_TXD3;
+}
+
+void __init sa1100_register_uart_fns(struct sa1100_port_fns *fns)
+{
+       if (fns->get_mctrl)
+               sa1100_pops.get_mctrl = fns->get_mctrl;
+       if (fns->set_mctrl)
+               sa1100_pops.set_mctrl = fns->set_mctrl;
+
+       sa1100_pops.pm       = fns->pm;
+       sa1100_pops.set_wake = fns->set_wake;
+}
+
+void __init sa1100_register_uart(int idx, int port)
+{
+       if (idx >= NR_PORTS) {
+               printk(KERN_ERR __FUNCTION__ ": bad index number %d\n", idx);
+               return;
+       }
+
+       switch (port) {
+       case 1:
+               sa1100_ports[idx].port.membase = (void *)&Ser1UTCR0;
+               sa1100_ports[idx].port.mapbase = _Ser1UTCR0;
+               sa1100_ports[idx].port.irq     = IRQ_Ser1UART;
+               sa1100_ports[idx].port.flags   = ASYNC_BOOT_AUTOCONF;
+               break;
+
+       case 2:
+               sa1100_ports[idx].port.membase = (void *)&Ser2UTCR0;
+               sa1100_ports[idx].port.mapbase = _Ser2UTCR0;
+               sa1100_ports[idx].port.irq     = IRQ_Ser2ICP;
+               sa1100_ports[idx].port.flags   = ASYNC_BOOT_AUTOCONF;
+               break;
+
+       case 3:
+               sa1100_ports[idx].port.membase = (void *)&Ser3UTCR0;
+               sa1100_ports[idx].port.mapbase = _Ser3UTCR0;
+               sa1100_ports[idx].port.irq     = IRQ_Ser3UART;
+               sa1100_ports[idx].port.flags   = ASYNC_BOOT_AUTOCONF;
+               break;
+
+       default:
+               printk(KERN_ERR __FUNCTION__ ": bad port number %d\n", port);
+       }
+}
+
+
+#ifdef CONFIG_SERIAL_SA1100_CONSOLE
+
+/*
+ * Interrupts are disabled on entering
+ */
+static void
+sa1100_console_write(struct console *co, const char *s, unsigned int count)
+{
+       struct sa1100_port *sport = &sa1100_ports[co->index];
+       unsigned int old_utcr3, status, i;
+
+       /*
+        *      First, save UTCR3 and then disable interrupts
+        */
+       old_utcr3 = UART_GET_UTCR3(sport);
+       UART_PUT_UTCR3(sport, (old_utcr3 & ~(UTCR3_RIE | UTCR3_TIE)) |
+                               UTCR3_TXE);
+
+       /*
+        *      Now, do each character
+        */
+       for (i = 0; i < count; i++) {
+               do {
+                       status = UART_GET_UTSR1(sport);
+               } while (!(status & UTSR1_TNF));
+               UART_PUT_CHAR(sport, s[i]);
+               if (s[i] == '\n') {
+                       do {
+                               status = UART_GET_UTSR1(sport);
+                       } while (!(status & UTSR1_TNF));
+                       UART_PUT_CHAR(sport, '\r');
+               }
+       }
+
+       /*
+        *      Finally, wait for transmitter to become empty
+        *      and restore UTCR3
+        */
+       do {
+               status = UART_GET_UTSR1(sport);
+       } while (status & UTSR1_TBY);
+       UART_PUT_UTCR3(sport, old_utcr3);
+}
+
+static kdev_t sa1100_console_device(struct console *co)
+{
+       return mk_kdev(SERIAL_SA1100_MAJOR, MINOR_START + co->index);
+}
+
+/*
+ * If the port was already initialised (eg, by a boot loader),
+ * try to determine the current setup.
+ */
+static void __init
+sa1100_console_get_options(struct sa1100_port *sport, int *baud,
+                          int *parity, int *bits)
+{
+       unsigned int utcr3;
+
+       utcr3 = UART_GET_UTCR3(sport) & (UTCR3_RXE | UTCR3_TXE);
+       if (utcr3 == (UTCR3_RXE | UTCR3_TXE)) {
+               /* ok, the port was enabled */
+               unsigned int utcr0, quot;
+
+               utcr0 = UART_GET_UTCR0(sport);
+
+               *parity = 'n';
+               if (utcr0 & UTCR0_PE) {
+                       if (utcr0 & UTCR0_OES)
+                               *parity = 'e';
+                       else
+                               *parity = 'o';
+               }
+
+               if (utcr0 & UTCR0_DSS)
+                       *bits = 8;
+               else
+                       *bits = 7;
+
+               quot = UART_GET_UTCR2(sport) | UART_GET_UTCR1(sport) << 8;
+               quot &= 0xfff;
+               *baud = sport->port.uartclk / (16 * (quot + 1));
+       }
+}
+
+static int __init
+sa1100_console_setup(struct console *co, char *options)
+{
+       struct sa1100_port *sport;
+       int baud = 9600;
+       int bits = 8;
+       int parity = 'n';
+       int flow = 'n';
+
+       /*
+        * Check whether an invalid uart number has been specified, and
+        * if so, search for the first available port that does have
+        * console support.
+        */
+       if (co->index == -1 || co->index >= NR_PORTS)
+               co->index = 0;
+       sport = &sa1100_ports[co->index];
+
+       if (options)
+               uart_parse_options(options, &baud, &parity, &bits, &flow);
+       else
+               sa1100_console_get_options(sport, &baud, &parity, &bits);
+
+       return uart_set_options(&sport->port, co, baud, parity, bits, flow);
+}
+
+static struct console sa1100_console = {
+       name:           "ttySA",
+       write:          sa1100_console_write,
+       device:         sa1100_console_device,
+       setup:          sa1100_console_setup,
+       flags:          CON_PRINTBUFFER,
+       index:          -1,
+};
+
+void __init sa1100_rs_console_init(void)
+{
+       sa1100_init_ports();
+       register_console(&sa1100_console);
+}
+
+#define SA1100_CONSOLE &sa1100_console
+#else
+#define SA1100_CONSOLE NULL
+#endif
+
+static struct uart_driver sa1100_reg = {
+       owner:                  THIS_MODULE,
+       driver_name:            "ttySA",
+#ifdef CONFIG_DEVFS_FS
+       dev_name:               "ttySA%d",
+#else
+       dev_name:               "ttySA",
+#endif
+       major:                  SERIAL_SA1100_MAJOR,
+       minor:                  MINOR_START,
+       nr:                     NR_PORTS,
+       cons:                   SA1100_CONSOLE,
+};
+
+static int __init sa1100_serial_init(void)
+{
+       int ret;
+
+       printk(KERN_INFO "Serial: SA11x0 driver $Revision: 1.41 $\n");
+
+       sa1100_init_ports();
+       ret = uart_register_driver(&sa1100_reg);
+       if (ret == 0) {
+               int i;
+
+               for (i = 0; i < NR_PORTS; i++)
+                       uart_add_one_port(&sa1100_reg, &sa1100_ports[i].port);
+       }
+       return ret;
+}
+
+static void __exit sa1100_serial_exit(void)
+{
+       int i;
+
+       for (i = 0; i < NR_PORTS; i++)
+               uart_remove_one_port(&sa1100_reg, &sa1100_ports[i].port);
+
+       uart_unregister_driver(&sa1100_reg);
+}
+
+module_init(sa1100_serial_init);
+module_exit(sa1100_serial_exit);
+
+EXPORT_NO_SYMBOLS;
+
+MODULE_AUTHOR("Deep Blue Solutions Ltd");
+MODULE_DESCRIPTION("SA1100 generic serial port driver $Revision: 1.41 $");
+MODULE_LICENSE("GPL");
diff --git a/drivers/serial/serial_uart00.c b/drivers/serial/serial_uart00.c
new file mode 100644 (file)
index 0000000..e90c36f
--- /dev/null
@@ -0,0 +1,778 @@
+/*
+ *  linux/drivers/char/serial_uart00.c
+ *
+ *  Driver for UART00 serial ports
+ *
+ *  Based on drivers/char/serial_amba.c, by ARM Limited & 
+ *                                          Deep Blue Solutions Ltd.
+ *  Copyright 2001 Altera Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ *  $Id: serial_uart00.c,v 1.32 2002/07/20 17:10:04 rmk Exp $
+ *
+ */
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <linux/signal.h>
+#include <linux/sched.h>
+#include <linux/interrupt.h>
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
+#include <linux/major.h>
+#include <linux/string.h>
+#include <linux/fcntl.h>
+#include <linux/ptrace.h>
+#include <linux/ioport.h>
+#include <linux/mm.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <linux/circ_buf.h>
+#include <linux/serial.h>
+#include <linux/console.h>
+#include <linux/sysrq.h>
+#include <linux/pld/pld_hotswap.h>
+
+#include <asm/system.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/uaccess.h>
+#include <asm/bitops.h>
+#include <asm/sizes.h>
+
+#if defined(CONFIG_SERIAL_UART00_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ)
+#define SUPPORT_SYSRQ
+#endif
+
+#include <linux/serial_core.h>
+#include <asm/arch/excalibur.h>
+#define UART00_TYPE (volatile unsigned int*)
+#include <asm/arch/uart00.h>
+#include <asm/arch/int_ctrl00.h>
+
+#define UART_NR                2
+
+#define SERIAL_UART00_NAME     "ttyUA"
+#define SERIAL_UART00_MAJOR    204
+#define SERIAL_UART00_MINOR    16      /* Temporary - will change in future */
+#define SERIAL_UART00_NR       UART_NR
+#define UART_PORT_SIZE 0x50
+
+#define UART00_ISR_PASS_LIMIT  256
+
+/*
+ * Access macros for the UART00 UARTs
+ */
+#define UART_GET_INT_STATUS(p) inl(UART_ISR((p)->membase))
+#define UART_PUT_IES(p, c)      outl(c,UART_IES((p)->membase))
+#define UART_GET_IES(p)         inl(UART_IES((p)->membase))
+#define UART_PUT_IEC(p, c)      outl(c,UART_IEC((p)->membase))
+#define UART_GET_IEC(p)         inl(UART_IEC((p)->membase))
+#define UART_PUT_CHAR(p, c)     outl(c,UART_TD((p)->membase))
+#define UART_GET_CHAR(p)        inl(UART_RD((p)->membase))
+#define UART_GET_RSR(p)         inl(UART_RSR((p)->membase))
+#define UART_GET_RDS(p)         inl(UART_RDS((p)->membase))
+#define UART_GET_MSR(p)         inl(UART_MSR((p)->membase))
+#define UART_GET_MCR(p)         inl(UART_MCR((p)->membase))
+#define UART_PUT_MCR(p, c)      outl(c,UART_MCR((p)->membase))
+#define UART_GET_MC(p)          inl(UART_MC((p)->membase))
+#define UART_PUT_MC(p, c)       outl(c,UART_MC((p)->membase))
+#define UART_GET_TSR(p)         inl(UART_TSR((p)->membase))
+#define UART_GET_DIV_HI(p)     inl(UART_DIV_HI((p)->membase))
+#define UART_PUT_DIV_HI(p,c)   outl(c,UART_DIV_HI((p)->membase))
+#define UART_GET_DIV_LO(p)     inl(UART_DIV_LO((p)->membase))
+#define UART_PUT_DIV_LO(p,c)   outl(c,UART_DIV_LO((p)->membase))
+#define UART_RX_DATA(s)                ((s) & UART_RSR_RX_LEVEL_MSK)
+#define UART_TX_READY(s)       (((s) & UART_TSR_TX_LEVEL_MSK) < 15)
+//#define UART_TX_EMPTY(p)     ((UART_GET_FR(p) & UART00_UARTFR_TMSK) == 0)
+
+static void uart00_stop_tx(struct uart_port *port, unsigned int tty_stop)
+{
+       UART_PUT_IEC(port, UART_IEC_TIE_MSK);
+}
+
+static void uart00_stop_rx(struct uart_port *port)
+{
+       UART_PUT_IEC(port, UART_IEC_RE_MSK);
+}
+
+static void uart00_enable_ms(struct uart_port *port)
+{
+       UART_PUT_IES(port, UART_IES_ME_MSK);
+}
+
+static void
+uart00_rx_chars(struct uart_port *port, struct pt_regs *regs)
+{
+       struct tty_struct *tty = port->info->tty;
+       unsigned int status, ch, rds, flg, ignored = 0;
+
+       status = UART_GET_RSR(port);
+       while (UART_RX_DATA(status)) {
+               /* 
+                * We need to read rds before reading the 
+                * character from the fifo
+                */
+               rds = UART_GET_RDS(port);
+               ch = UART_GET_CHAR(port);
+               port->icount.rx++;
+
+               if (tty->flip.count >= TTY_FLIPBUF_SIZE)
+                       goto ignore_char;
+
+               flg = TTY_NORMAL;
+
+               /*
+                * Note that the error handling code is
+                * out of the main execution path
+                */
+               if (rds & (UART_RDS_BI_MSK |UART_RDS_FE_MSK|
+                          UART_RDS_PE_MSK |UART_RDS_PE_MSK))
+                       goto handle_error;
+               if (uart_handle_sysrq_char(port, ch, regs))
+                       goto ignore_char;
+
+       error_return:
+               *tty->flip.flag_buf_ptr++ = flg;
+               *tty->flip.char_buf_ptr++ = ch;
+               tty->flip.count++;
+       ignore_char:
+               status = UART_GET_RSR(port);
+       }
+ out:
+       tty_flip_buffer_push(tty);
+       return;
+
+ handle_error:
+       if (rds & UART_RDS_BI_MSK) {
+               status &= ~(UART_RDS_FE_MSK | UART_RDS_PE_MSK);
+               port->icount.brk++;
+               if (uart_handle_break(port))
+                       goto ignore_char;
+       } else if (rds & UART_RDS_PE_MSK)
+               port->icount.parity++;
+       else if (rds & UART_RDS_PE_MSK)
+               port->icount.frame++;
+       if (rds & UART_RDS_OE_MSK)
+               port->icount.overrun++;
+
+       if (rds & port->ignore_status_mask) {
+               if (++ignored > 100)
+                       goto out;
+               goto ignore_char;
+       }
+       rds &= port->read_status_mask;
+
+       if (rds & UART_RDS_BI_MSK)
+               flg = TTY_BREAK;
+       else if (rds & UART_RDS_PE_MSK)
+               flg = TTY_PARITY;
+       else if (rds & UART_RDS_FE_MSK)
+               flg = TTY_FRAME;
+
+       if (status & UART_RDS_OE_MSK) {
+               /*
+                * CHECK: does overrun affect the current character?
+                * ASSUMPTION: it does not.
+                */
+               *tty->flip.flag_buf_ptr++ = flg;
+               *tty->flip.char_buf_ptr++ = ch;
+               tty->flip.count++;
+               if (tty->flip.count >= TTY_FLIPBUF_SIZE)
+                       goto ignore_char;
+               ch = 0;
+               flg = TTY_OVERRUN;
+       }
+#ifdef SUPPORT_SYSRQ
+       port->sysrq = 0;
+#endif
+       goto error_return;
+}
+
+static void uart00_tx_chars(struct uart_port *port)
+{
+       struct circ_buf *xmit = &port->info->xmit;
+       int count;
+
+       if (port->x_char) {
+               while ((UART_GET_TSR(port) & UART_TSR_TX_LEVEL_MSK) == 15);
+               UART_PUT_CHAR(port, port->x_char);
+               port->icount.tx++;
+               port->x_char = 0;
+               return;
+       }
+       if (uart_circ_empty(xmit) || uart_tx_stopped(port)) {
+               uart00_stop_tx(port, 0);
+               return;
+       }
+
+       count = port->fifosize >> 1;
+       do {
+               while ((UART_GET_TSR(port) & UART_TSR_TX_LEVEL_MSK) == 15);
+               UART_PUT_CHAR(port, xmit->buf[xmit->tail]);
+               xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
+               port->icount.tx++;
+               if (uart_circ_empty(xmit))
+                       break;
+       } while (--count > 0);
+
+       if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
+               uart_event(port, EVT_WRITE_WAKEUP);
+
+       if (uart_circ_empty(xmit))
+               uart00_stop_tx(port, 0);
+}
+
+static void uart00_start_tx(struct uart_port *port, unsigned int tty_start)
+{
+       UART_PUT_IES(port, UART_IES_TIE_MSK);
+       uart00_tx_chars(port);
+}
+
+static void uart00_modem_status(struct uart_port *port)
+{
+       unsigned int status;
+
+       status = UART_GET_MSR(port);
+
+       if (!status & (UART_MSR_DCTS_MSK | UART_MSR_DDSR_MSK | 
+                      UART_MSR_TERI_MSK | UART_MSR_DDCD_MSK))
+               return;
+
+       if (status & UART_MSR_DDCD_MSK)
+               uart_handle_dcd_change(port, status & UART_MSR_DCD_MSK);
+
+       if (status & UART_MSR_DDSR_MSK)
+               port->icount.dsr++;
+
+       if (status & UART_MSR_DCTS_MSK)
+               uart_handle_cts_change(port, status & UART_MSR_CTS_MSK);
+
+       wake_up_interruptible(&port->info->delta_msr_wait);
+}
+
+static void uart00_int(int irq, void *dev_id, struct pt_regs *regs)
+{
+       struct uart_port *port = dev_id;
+       unsigned int status, pass_counter = 0;
+
+       status = UART_GET_INT_STATUS(port);
+       do {
+               if (status & UART_ISR_RI_MSK)
+                       uart00_rx_chars(port, regs);
+               if (status & UART_ISR_MI_MSK)
+                       uart00_modem_status(port);
+               if (status & (UART_ISR_TI_MSK | UART_ISR_TII_MSK))
+                       uart00_tx_chars(port);
+               if (pass_counter++ > UART00_ISR_PASS_LIMIT)
+                       break;
+
+               status = UART_GET_INT_STATUS(port);
+       } while (status);
+}
+
+static unsigned int uart00_tx_empty(struct uart_port *port)
+{
+       return UART_GET_TSR(port) & UART_TSR_TX_LEVEL_MSK? 0 : TIOCSER_TEMT;
+}
+
+static unsigned int uart00_get_mctrl(struct uart_port *port)
+{
+       unsigned int result = 0;
+       unsigned int status;
+
+       status = UART_GET_MSR(port);
+       if (status & UART_MSR_DCD_MSK)
+               result |= TIOCM_CAR;
+       if (status & UART_MSR_DSR_MSK)
+               result |= TIOCM_DSR;
+       if (status & UART_MSR_CTS_MSK)
+               result |= TIOCM_CTS;
+       if (status & UART_MSR_RI_MSK)
+               result |= TIOCM_RI;
+
+       return result;
+}
+
+static void uart00_set_mctrl_null(struct uart_port *port, unsigned int mctrl)
+{
+}
+
+static void uart00_break_ctl(struct uart_port *port, int break_state)
+{
+       unsigned long flags;
+       unsigned int mcr;
+
+       spin_lock_irqsave(&port->lock, flags);
+       mcr = UART_GET_MCR(port);
+       if (break_state == -1)
+               mcr |= UART_MCR_BR_MSK;
+       else
+               mcr &= ~UART_MCR_BR_MSK;
+       UART_PUT_MCR(port, mcr);
+       spin_unlock_irqrestore(&port->lock, flags);
+}
+
+static void
+uart00_change_speed(struct uart_port *port, unsigned int cflag,
+                   unsigned int iflag, unsigned int quot)
+{
+       unsigned int uart_mc, old_ies;
+       unsigned long flags;
+
+       /* byte size and parity */
+       switch (cflag & CSIZE) {
+       case CS5:
+               uart_mc = UART_MC_CLS_CHARLEN_5;
+               break;
+       case CS6:
+               uart_mc = UART_MC_CLS_CHARLEN_6;
+               break;
+       case CS7:
+               uart_mc = UART_MC_CLS_CHARLEN_7;
+               break;
+       default: // CS8
+               uart_mc = UART_MC_CLS_CHARLEN_8;
+               break;
+       }
+       if (cflag & CSTOPB)
+               uart_mc|= UART_MC_ST_TWO;
+       if (cflag & PARENB) {
+               uart_mc |= UART_MC_PE_MSK;
+               if (!(cflag & PARODD))
+                       uart_mc |= UART_MC_EP_MSK;
+       }
+
+       port->read_status_mask = UART_RDS_OE_MSK;
+       if (iflag & INPCK)
+               port->read_status_mask |= UART_RDS_FE_MSK | UART_RDS_PE_MSK;
+       if (iflag & (BRKINT | PARMRK))
+               port->read_status_mask |= UART_RDS_BI_MSK;
+
+       /*
+        * Characters to ignore
+        */
+       port->ignore_status_mask = 0;
+       if (iflag & IGNPAR)
+               port->ignore_status_mask |= UART_RDS_FE_MSK | UART_RDS_PE_MSK;
+       if (iflag & IGNBRK) {
+               port->ignore_status_mask |= UART_RDS_BI_MSK;
+               /*
+                * If we're ignoring parity and break indicators,
+                * ignore overruns to (for real raw support).
+                */
+               if (iflag & IGNPAR)
+                       port->ignore_status_mask |= UART_RDS_OE_MSK;
+       }
+
+       /* first, disable everything */
+       spin_lock_irqsave(&port->lock, flags);
+       old_ies = UART_GET_IES(port); 
+
+       if (UART_ENABLE_MS(port, cflag))
+               old_ies |= UART_IES_ME_MSK;
+
+       /* Set baud rate */
+       UART_PUT_DIV_LO(port, (quot & 0xff));
+       UART_PUT_DIV_HI(port, ((quot & 0xf00) >> 8));
+
+       UART_PUT_MC(port, uart_mc);
+       UART_PUT_IES(port, old_ies);
+
+       spin_unlock_irqrestore(&port->lock, flags);
+}
+
+static int uart00_startup(struct uart_port *port)
+{
+       int result;
+
+       /*
+        * Allocate the IRQ
+        */
+       result = request_irq(port->irq, uart00_int, 0, "uart00", port);
+       if (result) {
+               printk(KERN_ERR "Request of irq %d failed\n", port->irq);
+               return result;
+       }
+
+       /*
+        * Finally, enable interrupts. Use the TII interrupt to minimise 
+        * the number of interrupts generated. If higher performance is 
+        * needed, consider using the TI interrupt with a suitable FIFO
+        * threshold
+        */
+       UART_PUT_IES(port, UART_IES_RE_MSK | UART_IES_TIE_MSK);
+
+       return 0;
+}
+
+static void uart00_shutdown(struct uart_port *port)
+{
+       /*
+        * disable all interrupts, disable the port
+        */
+       UART_PUT_IEC(port, 0xff);
+
+       /* disable break condition and fifos */
+       UART_PUT_MCR(port, UART_GET_MCR(port) &~UART_MCR_BR_MSK);
+
+        /*
+        * Free the interrupt
+        */
+       free_irq(port->irq, port);
+}
+
+static const char *uart00_type(struct uart_port *port)
+{
+       return port->type == PORT_UART00 ? "Altera UART00" : NULL;
+}
+
+/*
+ * Release the memory region(s) being used by 'port'
+ */
+static void uart00_release_port(struct uart_port *port)
+{
+       release_mem_region(port->mapbase, UART_PORT_SIZE);
+
+#ifdef CONFIG_ARCH_CAMELOT
+       if (port->membase != (void*)IO_ADDRESS(EXC_UART00_BASE)) {
+               iounmap(port->membase);
+       }
+#endif
+}
+
+/*
+ * Request the memory region(s) being used by 'port'
+ */
+static int uart00_request_port(struct uart_port *port)
+{
+       return request_mem_region(port->mapbase, UART_PORT_SIZE, "serial_uart00")
+                       != NULL ? 0 : -EBUSY;
+}
+
+/*
+ * Configure/autoconfigure the port.
+ */
+static void uart00_config_port(struct uart_port *port, int flags)
+{
+
+       /*
+        * Map the io memory if this is a soft uart
+        */
+       if (!port->membase)
+               port->membase = ioremap_nocache(port->mapbase,SZ_4K);
+
+       if (!port->membase)
+               printk(KERN_ERR "serial00: cannot map io memory\n");
+       else
+               port->type = PORT_UART00;
+
+}
+
+/*
+ * verify the new serial_struct (for TIOCSSERIAL).
+ */
+static int uart00_verify_port(struct uart_port *port, struct serial_struct *ser)
+{
+       int ret = 0;
+       if (ser->type != PORT_UNKNOWN && ser->type != PORT_UART00)
+               ret = -EINVAL;
+       if (ser->irq < 0 || ser->irq >= NR_IRQS)
+               ret = -EINVAL;
+       if (ser->baud_base < 9600)
+               ret = -EINVAL;
+       return ret;
+}
+
+static struct uart_ops uart00_pops = {
+       tx_empty:       uart00_tx_empty,
+       set_mctrl:      uart00_set_mctrl_null,
+       get_mctrl:      uart00_get_mctrl,
+       stop_tx:        uart00_stop_tx,
+       start_tx:       uart00_start_tx,
+       stop_rx:        uart00_stop_rx,
+       enable_ms:      uart00_enable_ms,
+       break_ctl:      uart00_break_ctl,
+       startup:        uart00_startup,
+       shutdown:       uart00_shutdown,
+       change_speed:   uart00_change_speed,
+       type:           uart00_type,
+       release_port:   uart00_release_port,
+       request_port:   uart00_request_port,
+       config_port:    uart00_config_port,
+       verify_port:    uart00_verify_port,
+};
+
+
+#ifdef CONFIG_ARCH_CAMELOT
+static struct uart_port epxa10db_port = {
+       membase:        (void*)IO_ADDRESS(EXC_UART00_BASE),
+       mapbase:         EXC_UART00_BASE,
+       iotype:         SERIAL_IO_MEM,
+       irq:            IRQ_UART,
+       uartclk:        EXC_AHB2_CLK_FREQUENCY,
+       fifosize:       16,
+       ops:            &uart00_pops,
+       flags:          ASYNC_BOOT_AUTOCONF,
+};
+#endif
+
+
+#ifdef CONFIG_SERIAL_UART00_CONSOLE
+static void uart00_console_write(struct console *co, const char *s, unsigned count)
+{
+#ifdef CONFIG_ARCH_CAMELOT
+       struct uart_port *port = &epxa10db_port;
+       unsigned int status, old_ies;
+       int i;
+
+       /*
+        *      First save the CR then disable the interrupts
+        */
+       old_ies = UART_GET_IES(port);
+       UART_PUT_IEC(port,0xff);
+
+       /*
+        *      Now, do each character
+        */
+       for (i = 0; i < count; i++) {
+               do {
+                       status = UART_GET_TSR(port);
+               } while (!UART_TX_READY(status));
+               UART_PUT_CHAR(port, s[i]);
+               if (s[i] == '\n') {
+                       do {
+                               status = UART_GET_TSR(port);
+                       } while (!UART_TX_READY(status));
+                       UART_PUT_CHAR(port, '\r');
+               }
+       }
+
+       /*
+        *      Finally, wait for transmitter to become empty
+        *      and restore the IES
+        */
+       do {
+               status = UART_GET_TSR(port);
+       } while (status & UART_TSR_TX_LEVEL_MSK);
+       UART_PUT_IES(port, old_ies);
+#endif
+}
+
+static kdev_t uart00_console_device(struct console *co)
+{
+       return mk_kdev(SERIAL_UART00_MAJOR, SERIAL_UART00_MINOR + co->index);
+}
+
+static void __init
+uart00_console_get_options(struct uart_port *port, int *baud,
+                          int *parity, int *bits)
+{
+       unsigned int uart_mc, quot;
+
+       uart_mc = UART_GET_MC(port);
+
+       *parity = 'n';
+       if (uart_mc & UART_MC_PE_MSK) {
+               if (uart_mc & UART_MC_EP_MSK)
+                       *parity = 'e';
+               else
+                       *parity = 'o';
+       }
+
+       switch (uart_mc & UART_MC_CLS_MSK) {
+       case UART_MC_CLS_CHARLEN_5:
+               *bits = 5;
+               break;
+       case UART_MC_CLS_CHARLEN_6:
+               *bits = 6;
+               break;
+       case UART_MC_CLS_CHARLEN_7:
+               *bits = 7;
+               break;
+       case UART_MC_CLS_CHARLEN_8:
+               *bits = 8;
+               break;
+       }
+       quot = UART_GET_DIV_LO(port) | (UART_GET_DIV_HI(port) << 8);
+       *baud = port->uartclk / (16 *quot );
+}
+
+static int __init uart00_console_setup(struct console *co, char *options)
+{
+       struct uart_port *port;
+       int baud = 38400;
+       int bits = 8;
+       int parity = 'n';
+       int flow = 'n';
+
+#ifdef CONFIG_ARCH_CAMELOT
+       port = &epxa10db_port;             ;
+#else
+       return -ENODEV;
+#endif
+       if (options)
+               uart_parse_options(options, &baud, &parity, &bits, &flow);
+       else
+               uart00_console_get_options(port, &baud, &parity, &bits);
+
+       return uart_set_options(port, co, baud, parity, bits, flow);
+}
+
+static struct console uart00_console = {
+       name:           SERIAL_UART00_NAME,
+       write:          uart00_console_write,
+       device:         uart00_console_device,
+       setup:          uart00_console_setup,
+       flags:          CON_PRINTBUFFER,
+       index:          0,
+};
+
+void __init uart00_console_init(void)
+{
+       register_console(&uart00_console);
+}
+
+#define UART00_CONSOLE &uart00_console
+#else
+#define UART00_CONSOLE NULL
+#endif
+
+static struct uart_driver uart00_reg = {
+       owner:                  NULL,
+       driver_name:            SERIAL_UART00_NAME,
+       dev_name:               SERIAL_UART00_NAME,
+       major:                  SERIAL_UART00_MAJOR,
+       minor:                  SERIAL_UART00_MINOR,
+       nr:                     UART_NR,
+       cons:                   UART00_CONSOLE,
+};
+
+struct dev_port_entry{
+       unsigned int base_addr;
+       struct uart_port *port;
+};
+
+static struct dev_port_entry dev_port_map[UART_NR];
+
+#ifdef CONFIG_PLD_HOTSWAP
+/*
+ * Keep a mapping of dev_info addresses -> port lines to use when
+ * removing ports dev==NULL indicates unused entry
+ */
+
+struct uart00_ps_data{
+       unsigned int clk;
+       unsigned int fifosize;
+};
+
+int uart00_add_device(struct pldhs_dev_info* dev_info, void* dev_ps_data)
+{
+       struct uart00_ps_data* dev_ps=dev_ps_data;
+       struct uart_port * port;
+       int i,result;
+
+       i=0;
+       while(dev_port_map[i].port)
+               i++;
+
+       if(i==UART_NR){
+               printk(KERN_WARNING "uart00: Maximum number of ports reached\n");
+               return 0;
+       }
+
+       port=kmalloc(sizeof(struct uart_port),GFP_KERNEL);
+       if(!port)
+               return -ENOMEM;
+
+       printk("clk=%d fifo=%d\n",dev_ps->clk,dev_ps->fifosize);
+       port->membase=0;
+       port->mapbase=dev_info->base_addr;
+       port->iotype=SERIAL_IO_MEM;
+       port->irq=dev_info->irq;
+       port->uartclk=dev_ps->clk;
+       port->fifosize=dev_ps->fifosize;
+       port->ops=&uart00_pops;
+       port->line=i;
+       port->flags=ASYNC_BOOT_AUTOCONF;
+
+       result=uart_add_one_port(&uart00_reg, port);
+       if(result){
+               printk("uart_add_one_port returned %d\n",result);
+               return result;
+       }
+       dev_port_map[i].base_addr=dev_info->base_addr;
+       dev_port_map[i].port=port;
+       printk("uart00: added device at %x as ttyUA%d\n",dev_port_map[i].base_addr,i);
+       return 0;
+
+}
+
+int uart00_remove_devices(void)
+{
+       int i,result;
+
+
+       result=0;
+       for(i=1;i<UART_NR;i++){
+               if(dev_port_map[i].base_addr){
+                       result=uart_remove_one_port(&uart00_reg, dev_port_map[i].port);
+                       if(result)
+                               return result;
+
+                       /* port removed sucessfully, so now tidy up */
+                       kfree(dev_port_map[i].port);
+                       dev_port_map[i].base_addr=0;
+                       dev_port_map[i].port=NULL;
+               }
+       }
+       return 0;
+
+}
+
+struct pld_hotswap_ops uart00_pldhs_ops={
+       name: "uart00",
+       add_device: uart00_add_device,
+       remove_devices:uart00_remove_devices,
+};
+
+#endif
+
+static int __init uart00_init(void)
+{
+       int result;
+
+       printk(KERN_INFO "Serial: UART00 driver $Revision: 1.32 $\n");
+
+       printk(KERN_WARNING "serial_uart00:Using temporary major/minor pairs"
+               " - these WILL change in the future\n");
+
+       result = uart_register_driver(&uart00_reg);
+       if (result)
+               return result;
+#ifdef CONFIG_ARCH_CAMELOT
+       result = uart_add_one_port(&uart00_reg,&epxa10db_port);
+#endif
+       if (result)
+               uart_unregister_driver(&uart00_reg);
+
+#ifdef  CONFIG_PLD_HOTSWAP
+       pldhs_register_driver(&uart00_pldhs_ops);
+#endif
+       return result;
+}
+
+__initcall(uart00_init);
diff --git a/include/linux/serial_core.h b/include/linux/serial_core.h
new file mode 100644 (file)
index 0000000..19ab829
--- /dev/null
@@ -0,0 +1,395 @@
+/*
+ *  linux/drivers/char/serial_core.h
+ *
+ *  Copyright (C) 2000 Deep Blue Solutions Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ *  $Id: serial_core.h,v 1.49 2002/07/20 18:06:32 rmk Exp $
+ */
+
+/*
+ * The type definitions.  These are from Ted Ts'o's serial.h
+ */
+#define PORT_UNKNOWN   0
+#define PORT_8250      1
+#define PORT_16450     2
+#define PORT_16550     3
+#define PORT_16550A    4
+#define PORT_CIRRUS    5
+#define PORT_16650     6
+#define PORT_16650V2   7
+#define PORT_16750     8
+#define PORT_STARTECH  9
+#define PORT_16C950    10
+#define PORT_16654     11
+#define PORT_16850     12
+#define PORT_RSA       13
+#define PORT_MAX_8250  13      /* max port ID */
+
+/*
+ * ARM specific type numbers.  These are not currently guaranteed
+ * to be implemented, and will change in the future.  These are
+ * separate so any additions to the old serial.c that occur before
+ * we are merged can be easily merged here.
+ */
+#define PORT_AMBA      32
+#define PORT_CLPS711X  33
+#define PORT_SA1100    34
+#define PORT_UART00    35
+#define PORT_21285     37
+
+#ifdef __KERNEL__
+
+#include <linux/config.h>
+#include <linux/interrupt.h>
+#include <linux/circ_buf.h>
+#include <linux/spinlock.h>
+
+struct uart_port;
+struct uart_info;
+struct serial_struct;
+
+/*
+ * This structure describes all the operations that can be
+ * done on the physical hardware.
+ */
+struct uart_ops {
+       unsigned int    (*tx_empty)(struct uart_port *);
+       void            (*set_mctrl)(struct uart_port *, unsigned int mctrl);
+       unsigned int    (*get_mctrl)(struct uart_port *);
+       void            (*stop_tx)(struct uart_port *, unsigned int tty_stop);
+       void            (*start_tx)(struct uart_port *, unsigned int tty_start);
+       void            (*send_xchar)(struct uart_port *, char ch);
+       void            (*stop_rx)(struct uart_port *);
+       void            (*enable_ms)(struct uart_port *);
+       void            (*break_ctl)(struct uart_port *, int ctl);
+       int             (*startup)(struct uart_port *);
+       void            (*shutdown)(struct uart_port *);
+       void            (*change_speed)(struct uart_port *, unsigned int cflag,
+                                       unsigned int iflag, unsigned int quot);
+       void            (*pm)(struct uart_port *, unsigned int state,
+                             unsigned int oldstate);
+       int             (*set_wake)(struct uart_port *, unsigned int state);
+
+       /*
+        * Return a string describing the type of the port
+        */
+       const char *(*type)(struct uart_port *);
+
+       /*
+        * Release IO and memory resources used by the port.
+        * This includes iounmap if necessary.
+        */
+       void            (*release_port)(struct uart_port *);
+
+       /*
+        * Request IO and memory resources used by the port.
+        * This includes iomapping the port if necessary.
+        */
+       int             (*request_port)(struct uart_port *);
+       void            (*config_port)(struct uart_port *, int);
+       int             (*verify_port)(struct uart_port *, struct serial_struct *);
+       int             (*ioctl)(struct uart_port *, unsigned int, unsigned long);
+};
+
+#define UART_CONFIG_TYPE       (1 << 0)
+#define UART_CONFIG_IRQ                (1 << 1)
+
+struct uart_icount {
+       __u32   cts;
+       __u32   dsr;
+       __u32   rng;
+       __u32   dcd;
+       __u32   rx;
+       __u32   tx;
+       __u32   frame;
+       __u32   overrun;
+       __u32   parity;
+       __u32   brk;
+       __u32   buf_overrun;
+};
+
+struct uart_port {
+       spinlock_t              lock;                   /* port lock */
+       unsigned int            iobase;                 /* in/out[bwl] */
+       char                    *membase;               /* read/write[bwl] */
+       unsigned int            irq;                    /* irq number */
+       unsigned int            uartclk;                /* base uart clock */
+       unsigned char           fifosize;               /* tx fifo size */
+       unsigned char           x_char;                 /* xon/xoff char */
+       unsigned char           regshift;               /* reg offset shift */
+       unsigned char           iotype;                 /* io access style */
+
+#define UPIO_PORT              (0)
+#define UPIO_HUB6              (1)
+#define UPIO_MEM               (2)
+
+       unsigned int            read_status_mask;       /* driver specific */
+       unsigned int            ignore_status_mask;     /* driver specific */
+       struct uart_info        *info;                  /* pointer to parent info */
+       struct uart_icount      icount;                 /* statistics */
+
+       struct console          *cons;                  /* struct console, if any */
+#ifdef CONFIG_SERIAL_CORE_CONSOLE
+       unsigned long           sysrq;                  /* sysrq timeout */
+#endif
+
+       unsigned int            flags;
+
+#define UPF_HUP_NOTIFY         (1 << 0)
+#define UPF_SAK                        (1 << 2)
+#define UPF_SPD_MASK           (0x1030)
+#define UPF_SPD_HI             (0x0010)
+#define UPF_SPD_VHI            (0x0020)
+#define UPF_SPD_CUST           (0x0030)
+#define UPF_SPD_SHI            (0x1000)
+#define UPF_SPD_WARP           (0x1010)
+#define UPF_SKIP_TEST          (1 << 6)
+#define UPF_AUTO_IRQ           (1 << 7)
+#define UPF_HARDPPS_CD         (1 << 11)
+#define UPF_LOW_LATENCY                (1 << 13)
+#define UPF_BUGGY_UART         (1 << 14)
+#define UPF_AUTOPROBE          (1 << 15)
+#define UPF_BOOT_AUTOCONF      (1 << 28)
+
+#define UPF_FLAGS              (0x7fff)
+#define UPF_USR_MASK           (UPF_SPD_MASK|UPF_LOW_LATENCY)
+
+       unsigned int            mctrl;                  /* current modem ctrl settings */
+       unsigned int            timeout;                /* character-based timeout */
+       unsigned int            type;                   /* port type */
+       struct uart_ops         *ops;
+       unsigned int            line;                   /* port index */
+       unsigned long           mapbase;                /* for ioremap */
+       unsigned char           hub6;                   /* this should be in the 8250 driver */
+       unsigned char           unused[3];
+};
+
+/*
+ * This is the state information which is persistent across opens.
+ * The low level driver must not to touch any elements contained
+ * within.
+ */
+struct uart_state {
+       unsigned int            close_delay;
+       unsigned int            closing_wait;
+
+#define USF_CLOSING_WAIT_INF   (0)
+#define USF_CLOSING_WAIT_NONE  (65535)
+
+       unsigned int            custom_divisor;
+
+       int                     count;
+       struct uart_info        *info;
+       struct uart_port        *port;
+
+#ifdef CONFIG_PM
+       struct pm_dev           *pm;
+#endif
+};
+
+#define UART_XMIT_SIZE 1024
+/*
+ * This is the state information which is only valid when the port
+ * is open; it may be freed by the core driver once the device has
+ * been closed.  Either the low level driver or the core can modify
+ * stuff here.
+ */
+struct uart_info {
+       struct uart_port        *port;
+       struct uart_state       *state;
+       struct tty_struct       *tty;
+       struct circ_buf         xmit;
+       unsigned int            flags;
+
+/*
+ * These are the flags that specific to info->flags, and reflect our
+ * internal state.  They can not be accessed via port->flags.  Low
+ * level drivers must not change these, but may query them instead.
+ */
+#define UIF_CHECK_CD           (1 << 25)
+#define UIF_CTS_FLOW           (1 << 26)
+#define UIF_CLOSING            (1 << 27)
+#define UIF_NORMAL_ACTIVE      (1 << 29)
+#define UIF_INITIALIZED                (1 << 31)
+
+       unsigned char           *tmpbuf;
+       struct semaphore        tmpbuf_sem;
+
+       unsigned long           event;
+       int                     blocked_open;
+
+       struct tasklet_struct   tlet;
+
+       wait_queue_head_t       open_wait;
+       wait_queue_head_t       delta_msr_wait;
+};
+
+/* number of characters left in xmit buffer before we ask for more */
+#define WAKEUP_CHARS           256
+
+#define EVT_WRITE_WAKEUP       0
+
+struct module;
+struct tty_driver;
+
+struct uart_driver {
+       struct module           *owner;
+       const char              *driver_name;
+       const char              *dev_name;
+       int                      major;
+       int                      minor;
+       int                      nr;
+       struct console          *cons;
+
+       /*
+        * these are private; the low level driver should not
+        * touch these; they should be initialised to NULL
+        */
+       struct uart_state       *state;
+       struct tty_driver       *tty_driver;
+};
+
+void uart_event(struct uart_port *port, int event);
+struct uart_port *uart_get_console(struct uart_port *ports, int nr,
+                                  struct console *c);
+void uart_parse_options(char *options, int *baud, int *parity, int *bits,
+                       int *flow);
+int uart_set_options(struct uart_port *port, struct console *co, int baud,
+                    int parity, int bits, int flow);
+int uart_register_driver(struct uart_driver *uart);
+void uart_unregister_driver(struct uart_driver *uart);
+void uart_unregister_port(struct uart_driver *reg, int line);
+int uart_register_port(struct uart_driver *reg, struct uart_port *port);
+int uart_add_one_port(struct uart_driver *reg, struct uart_port *port);
+int uart_remove_one_port(struct uart_driver *reg, struct uart_port *port);
+
+#define uart_circ_empty(circ)          ((circ)->head == (circ)->tail)
+#define uart_circ_clear(circ)          ((circ)->head = (circ)->tail = 0)
+
+#define uart_circ_chars_pending(circ)  \
+       (CIRC_CNT((circ)->head, (circ)->tail, UART_XMIT_SIZE))
+
+#define uart_circ_chars_free(circ)     \
+       (CIRC_SPACE((circ)->head, (circ)->tail, UART_XMIT_SIZE))
+
+#define uart_tx_stopped(port)          \
+       ((port)->info->tty->stopped || (port)->info->tty->hw_stopped)
+
+/*
+ * The following are helper functions for the low level drivers.
+ */
+#ifdef SUPPORT_SYSRQ
+static inline int
+uart_handle_sysrq_char(struct uart_port *port, unsigned int ch,
+                      struct pt_regs *regs)
+{
+       if (port->sysrq) {
+               if (ch && time_before(jiffies, port->sysrq)) {
+                       handle_sysrq(ch, regs, NULL);
+                       port->sysrq = 0;
+                       return 1;
+               }
+               port->sysrq = 0;
+       }
+       return 0;
+}
+#else
+#define uart_handle_sysrq_char(port,ch,regs)   (0)
+#endif
+
+/*
+ * We do the SysRQ and SAK checking like this...
+ */
+static inline int uart_handle_break(struct uart_port *port)
+{
+       struct uart_info *info = port->info;
+#ifdef SUPPORT_SYSRQ
+       if (port->cons && port->cons->index == port->line) {
+               if (!port->sysrq) {
+                       port->sysrq = jiffies + HZ*5;
+                       return 1;
+               }
+               port->sysrq = 0;
+       }
+#endif
+       if (info->flags & UPF_SAK)
+               do_SAK(info->tty);
+       return 0;
+}
+
+/**
+ *     uart_handle_dcd_change - handle a change of carrier detect state
+ *     @port: uart_port structure for the open port
+ *     @status: new carrier detect status, nonzero if active
+ */
+static inline void
+uart_handle_dcd_change(struct uart_port *port, unsigned int status)
+{
+       struct uart_info *info = port->info;
+
+       port->icount.dcd++;
+
+#ifdef CONFIG_HARD_PPS
+       if ((port->flags & UPF_HARDPPS_CD) && status)
+               hardpps();
+#endif
+
+       if (info->flags & UIF_CHECK_CD) {
+               if (status)
+                       wake_up_interruptible(&info->open_wait);
+               else if (info->tty)
+                       tty_hangup(info->tty);
+       }
+}
+
+/**
+ *     uart_handle_cts_change - handle a change of clear-to-send state
+ *     @port: uart_port structure for the open port
+ *     @status: new clear to send status, nonzero if active
+ */
+static inline void
+uart_handle_cts_change(struct uart_port *port, unsigned int status)
+{
+       struct uart_info *info = port->info;
+       struct tty_struct *tty = info->tty;
+
+       port->icount.cts++;
+
+       if (info->flags & UIF_CTS_FLOW) {
+               if (tty->hw_stopped) {
+                       if (status) {
+                               tty->hw_stopped = 0;
+                               port->ops->start_tx(port, 0);
+                               uart_event(port, EVT_WRITE_WAKEUP);
+                       }
+               } else {
+                       if (!status) {
+                               tty->hw_stopped = 1;
+                               port->ops->stop_tx(port, 0);
+                       }
+               }
+       }
+}
+
+/*
+ *     UART_ENABLE_MS - determine if port should enable modem status irqs
+ */
+#define UART_ENABLE_MS(port,cflag)     ((port)->flags & UPF_HARDPPS_CD || \
+                                        (cflag) & CRTSCTS || \
+                                        !(cflag) & CLOCAL)
+
+#endif