]> git.neil.brown.name Git - history.git/commitdiff
SPARC: More Sun serial driver updates.
authorDavid S. Miller <davem@nuts.ninka.net>
Sat, 3 Aug 2002 00:42:29 +0000 (17:42 -0700)
committerDavid S. Miller <davem@nuts.ninka.net>
Sat, 3 Aug 2002 00:42:29 +0000 (17:42 -0700)
- Fully convert SUNSAB driver.
- Make Sun serial console selection more sensible.
- Misc bug fixes to all drivers.

drivers/serial/sunsab.c
drivers/serial/sunsab.h [new file with mode: 0644]
drivers/serial/sunsu.c
drivers/serial/sunzilog.c
include/asm-sparc64/sab82532.h [deleted file]
include/linux/serial_core.h

index 0bb00e0b2e7afe3af94001cd0ae4fe910a32947f..b88c56fee41ef2b47ca2e84176f10cec52514999 100644 (file)
@@ -1,7 +1,7 @@
-/* $Id: sab82532.c,v 1.66 2002/01/08 16:00:16 davem Exp $
- * sab82532.c: ASYNC Driver for the SIEMENS SAB82532 DUSCC.
+/* sunsab.c: ASYNC Driver for the SIEMENS SAB82532 DUSCC.
  *
  * Copyright (C) 1997  Eddie C. Dost  (ecd@skynet.be)
+ * Copyright (C) 2002  David S. Miller (davem@redhat.com)
  *
  * Rewrote buffer handling to use CIRC(Circular Buffer) macros.
  *   Maxim Krasnyanskiy <maxk@qualcomm.com>
  * rates to be programmed into the UART.  Also eliminated a lot of
  * duplicated code in the console setup.
  *   Theodore Ts'o <tytso@mit.edu>, 2001-Oct-12
+ *
+ * Ported to new 2.5.x UART layer.
+ *   David S. Miller <davem@redhat.com>
  */
 
 #include <linux/config.h>
 #include <linux/module.h>
-#include <linux/errno.h>
-#include <linux/signal.h>
+#include <linux/kernel.h>
 #include <linux/sched.h>
-#include <linux/timer.h>
-#include <linux/interrupt.h>
+#include <linux/errno.h>
 #include <linux/tty.h>
 #include <linux/tty_flip.h>
-#include <linux/serial.h>
-#include <linux/serialP.h>
-#include <linux/serial_reg.h>
-#include <linux/console.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/circ_buf.h>
+#include <linux/serial.h>
+#include <linux/console.h>
+#include <linux/spinlock.h>
 #include <linux/slab.h>
 #include <linux/init.h>
-#include <linux/delay.h>
 
-#include <asm/sab82532.h>
-#include <asm/uaccess.h>
-#include <asm/ebus.h>
+#include <asm/io.h>
 #include <asm/irq.h>
+#include <asm/oplib.h>
+#include <asm/ebus.h>
 
-#include "sunserial.h"
-
-static DECLARE_TASK_QUEUE(tq_serial);
-
-/* This is (one of many) a special gross hack to allow SU and
- * SAB serials to co-exist on the same machine. -DaveM
- */
-#undef SERIAL_BH
-#define SERIAL_BH      AURORA_BH
-
-static struct tty_driver serial_driver, callout_driver;
-static int sab82532_refcount;
-
-#undef SERIAL_PARANOIA_CHECK
-#define SERIAL_DO_RESTART
-
-/* Set of debugging defines */
-#undef SERIAL_DEBUG_OPEN
-#undef SERIAL_DEBUG_FLOW
-#undef SERIAL_DEBUG_MODEM
-#undef SERIAL_DEBUG_WAIT_UNTIL_SENT
-#undef SERIAL_DEBUG_SEND_BREAK
-#undef SERIAL_DEBUG_INTR
-#undef SERIAL_DEBUG_FIFO
-#define SERIAL_DEBUG_OVERFLOW 1
-
-/* Trace things on serial device, useful for console debugging: */
-#undef SERIAL_LOG_DEVICE
-
-#ifdef SERIAL_LOG_DEVICE
-static void dprint_init(int tty);
-#endif
-
-static void change_speed(struct sab82532 *info);
-static void sab82532_wait_until_sent(struct tty_struct *tty, int timeout);
+#include <linux/serial_core.h>
+
+#include "suncore.h"
+#include "sunsab.h"
+
+struct uart_sunsab_port {
+       struct uart_port                port;           /* Generic UART port    */
+       union sab82532_async_regs       *regs;          /* Chip registers       */
+       unsigned long                   irqflags;       /* IRQ state flags      */
+       int                             xmit_fifo_size; /* TX fifo size         */
+       int                             recv_fifo_size; /* RX fifo size         */
+       int                             dsr;            /* Current DSR state    */
+       unsigned int                    cec_timeout;    /* Chip poll timeout... */
+       unsigned int                    tec_timeout;    /* likewise             */
+       unsigned char                   interrupt_mask0;/* ISR0 masking         */
+       unsigned char                   interrupt_mask1;/* ISR1 masking         */
+       unsigned char                   pvr_dtr_bit;    /* Which PVR bit is DTR */
+       unsigned char                   pvr_dsr_bit;    /* Which PVR bit is DSR */
+       int                             type;           /* SAB82532 version     */
+       int                             sab_line;       /* Internal numbering   */
+       unsigned int irq;                               /* Device interrupt     */
+};
 
 /*
  * This assumes you have a 29.4912 MHz clock for your UART.
  */
-#define BASE_BAUD ( 29491200 / 16 )
-
-static struct sab82532 *sab82532_chain = 0;
-static struct tty_struct *sab82532_table[NR_PORTS];
-static struct termios *sab82532_termios[NR_PORTS];
-static struct termios *sab82532_termios_locked[NR_PORTS];
-
-#ifdef CONFIG_SERIAL_CONSOLE
-extern int serial_console;
-static struct console sab82532_console;
-static int sab82532_console_init(void);
-static void batten_down_hatches(struct sab82532 *info);
-#endif
-
-#ifndef MIN
-#define MIN(a,b)       ((a) < (b) ? (a) : (b))
-#endif
+#define SAB_BASE_BAUD ( 29491200 / 16 )
 
 static char *sab82532_version[16] = {
        "V1.0", "V2.0", "V3.2", "V(0x03)",
@@ -103,840 +72,600 @@ static char *sab82532_version[16] = {
        "V(0x08)", "V(0x09)", "V(0x0a)", "V(0x0b)",
        "V(0x0c)", "V(0x0d)", "V(0x0e)", "V(0x0f)"
 };
-static char serial_version[16];
-
-/*
- * tmp_buf is used as a temporary buffer by sab82532_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 = 0;
-static DECLARE_MUTEX(tmp_buf_sem);
-
-static inline int serial_paranoia_check(struct sab82532 *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 sab82532 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;
-}
-
-/*
- * This is used to figure out the divisor speeds.
- *
- * The formula is:    Baud = BASE_BAUD / ((N + 1) * (1 << M)),
- *
- * with               0 <= N < 64 and 0 <= M < 16
- * 
- * 12-Oct-2001 - Replaced table driven approach with code written by
- * Theodore Ts'o <tytso@alum.mit.edu> which exactly replicates the
- * table.  (Modulo bugs for the 307200 and 61440 baud rates, which
- * were clearly incorrectly calculated in the original table.  This is
- * why tables filled with magic constants are evil.)
- */
-
-static void calc_ebrg(int baud, int *n_ret, int *m_ret)
-{
-       int     n, m;
-
-       if (baud == 0) {
-               *n_ret = 0;
-               *m_ret = 0;
-               return;
-       }
-     
-       /*
-        * We scale numbers by 10 so that we get better accuracy
-        * without having to use floating point.  Here we increment m
-        * until n is within the valid range.
-        */
-       n = (BASE_BAUD*10) / baud;
-       m = 0;
-       while (n >= 640) {
-               n = n / 2;
-               m++;
-       }
-       n = (n+5) / 10;
-       /*
-        * We try very hard to avoid speeds with M == 0 since they may
-        * not work correctly for XTAL frequences above 10 MHz.
-        */
-       if ((m == 0) && ((n & 1) == 0)) {
-               n = n / 2;
-               m++;
-       }
-       *n_ret = n - 1;
-       *m_ret = m;
-}
 
 #define SAB82532_MAX_TEC_TIMEOUT 200000        /* 1 character time (at 50 baud) */
 #define SAB82532_MAX_CEC_TIMEOUT  50000        /* 2.5 TX CLKs (at 50 baud) */
 
-static __inline__ void sab82532_tec_wait(struct sab82532 *info)
+static __inline__ void sunsab_tec_wait(struct uart_sunsab_port *up)
 {
-       int timeout = info->tec_timeout;
+       int timeout = up->tec_timeout;
 
-       while ((readb(&info->regs->r.star) & SAB82532_STAR_TEC) && --timeout)
+       while ((readb(&up->regs->r.star) & SAB82532_STAR_TEC) && --timeout)
                udelay(1);
 }
 
-static __inline__ void sab82532_cec_wait(struct sab82532 *info)
+static __inline__ void sunsab_cec_wait(struct uart_sunsab_port *up)
 {
-       int timeout = info->cec_timeout;
+       int timeout = up->cec_timeout;
 
-       while ((readb(&info->regs->r.star) & SAB82532_STAR_CEC) && --timeout)
+       while ((readb(&up->regs->r.star) & SAB82532_STAR_CEC) && --timeout)
                udelay(1);
 }
 
-static __inline__ void sab82532_start_tx(struct sab82532 *info)
-{
-       unsigned long flags;
-       int i;
-
-       save_flags(flags); cli();
-
-       if (info->xmit.head == info->xmit.tail)
-               goto out;
-
-       if (!test_bit(SAB82532_XPR, &info->irqflags))
-               goto out;
-
-       info->interrupt_mask1 &= ~(SAB82532_IMR1_ALLS);
-       writeb(info->interrupt_mask1, &info->regs->w.imr1);
-       clear_bit(SAB82532_ALLS, &info->irqflags);
-
-       clear_bit(SAB82532_XPR, &info->irqflags);
-       for (i = 0; i < info->xmit_fifo_size; i++) {
-               writeb(info->xmit.buf[info->xmit.tail],
-                      &info->regs->w.xfifo[i]);
-               info->xmit.tail = (info->xmit.tail + 1) & (SERIAL_XMIT_SIZE-1);
-               info->icount.tx++;
-               if (info->xmit.head == info->xmit.tail)
-                       break;
-       }
-
-       /* Issue a Transmit Frame command. */
-       sab82532_cec_wait(info);
-       writeb(SAB82532_CMDR_XF, &info->regs->w.cmdr);
-
-out:
-       restore_flags(flags);
-}
-
-
-/*
- * ------------------------------------------------------------
- * sab82532_stop() and sab82532_start()
- *
- * This routines are called before setting or resetting tty->stopped.
- * They enable or disable transmitter interrupts, as necessary.
- * ------------------------------------------------------------
- */
-static void sab82532_stop(struct tty_struct *tty)
-{
-       struct sab82532 *info = (struct sab82532 *)tty->driver_data;
-       unsigned long flags;
-
-       if (serial_paranoia_check(info, tty->device, "sab82532_stop"))
-               return;
-
-       save_flags(flags); cli();
-       info->interrupt_mask1 |= SAB82532_IMR1_XPR;
-       writeb(info->interrupt_mask1, &info->regs->w.imr1);
-       restore_flags(flags);
-}
-
-static void sab82532_start(struct tty_struct *tty)
-{
-       struct sab82532 *info = (struct sab82532 *)tty->driver_data;
-       unsigned long flags;
-       
-       if (serial_paranoia_check(info, tty->device, "sab82532_start"))
-               return;
-
-       save_flags(flags); cli();
-       info->interrupt_mask1 &= ~(SAB82532_IMR1_XPR);
-       writeb(info->interrupt_mask1, &info->regs->w.imr1);
-       sab82532_start_tx(info);
-       restore_flags(flags);
-}
-
-/*
- * ----------------------------------------------------------------------
- *
- * Here starts the interrupt handling routines.  All of the following
- * subroutines are declared as inline and are folded into
- * sab82532_interrupt().  They were separated out for readability's sake.
- *
- * Note: sab82532_interrupt() is a "fast" interrupt, which means that it
- * runs with interrupts turned off.  People who may want to modify
- * sab82532_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 void sab82532_sched_event(struct sab82532 *info, int event)
-{
-       info->event |= 1 << event;
-       queue_task(&info->tqueue, &tq_serial);
-       mark_bh(SERIAL_BH);
-}
-
-static void receive_chars(struct sab82532 *info,
-                         union sab82532_irq_status *stat)
+static void receive_chars(struct uart_sunsab_port *up,
+                         union sab82532_irq_status *stat,
+                         struct pt_regs *regs)
 {
-       struct tty_struct *tty = info->tty;
+       struct tty_struct *tty = up->port.info->tty;
        unsigned char buf[32];
-       unsigned char status;
+       int saw_console_brk = 0;
        int free_fifo = 0;
-       int i, count = 0;
+       int count = 0;
+       int i;
 
        /* Read number of BYTES (Character + Status) available. */
        if (stat->sreg.isr0 & SAB82532_ISR0_RPF) {
-               count = info->recv_fifo_size;
+               count = up->recv_fifo_size;
                free_fifo++;
        }
 
        if (stat->sreg.isr0 & SAB82532_ISR0_TCD) {
-               count = readb(&info->regs->r.rbcl) & (info->recv_fifo_size - 1);
+               count = readb(&up->regs->r.rbcl) & (up->recv_fifo_size - 1);
                free_fifo++;
        }
 
        /* Issue a FIFO read command in case we where idle. */
        if (stat->sreg.isr0 & SAB82532_ISR0_TIME) {
-               sab82532_cec_wait(info);
-               writeb(SAB82532_CMDR_RFRD, &info->regs->w.cmdr);
+               sunsab_cec_wait(up);
+               writeb(SAB82532_CMDR_RFRD, &up->regs->w.cmdr);
                return;
        }
 
-       if (stat->sreg.isr0 & SAB82532_ISR0_RFO) {
-#ifdef SERIAL_DEBUG_OVERFLOW
-               printk("sab82532: receive_chars: RFO");
-#endif
+       if (stat->sreg.isr0 & SAB82532_ISR0_RFO)
                free_fifo++;
-       }
 
        /* Read the FIFO. */
        for (i = 0; i < count; i++)
-               buf[i] = readb(&info->regs->r.rfifo[i]);
+               buf[i] = readb(&up->regs->r.rfifo[i]);
 
        /* Issue Receive Message Complete command. */
        if (free_fifo) {
-               sab82532_cec_wait(info);
-               writeb(SAB82532_CMDR_RMC, &info->regs->w.cmdr);
+               sunsab_cec_wait(up);
+               writeb(SAB82532_CMDR_RMC, &up->regs->w.cmdr);
        }
 
-       if (!tty)
-               return;
+       for (i = 0; i < count; i++) {
+               unsigned char ch = buf[i];
 
-       for (i = 0; i < count; ) {
-               if (tty->flip.count >= TTY_FLIPBUF_SIZE) {
-#ifdef SERIAL_DEBUG_OVERFLOW
-                       printk("sab82532: receive_chars: tty overrun\n");
-#endif
-                       info->icount.buf_overrun++;
-                       break;
+               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
                }
 
-               tty->flip.count++;
-               *tty->flip.char_buf_ptr++ = buf[i++];
-               status = buf[i++];
-               info->icount.rx++;
+               *tty->flip.char_buf_ptr = ch;
+               *tty->flip.flag_buf_ptr = TTY_NORMAL;
+               up->port.icount.rx++;
+
+               if (unlikely(stat->sreg.isr0 & (SAB82532_ISR0_PERR |
+                                               SAB82532_ISR0_FERR |
+                                               SAB82532_ISR0_RFO)) ||
+                   unlikely(stat->sreg.isr1 & SAB82532_ISR1_BRK)) {
+                       /*
+                        * For statistics only
+                        */
+                       if (stat->sreg.isr1 & SAB82532_ISR1_BRK) {
+                               stat->sreg.isr0 &= ~(SAB82532_ISR0_PERR |
+                                                    SAB82532_ISR0_FERR);
+                               up->port.icount.brk++;
+                               if (up->port.line == up->port.cons->index)
+                                       saw_console_brk = 1;
+                               /*
+                                * 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))
+                                       continue;
+                       } else if (stat->sreg.isr0 & SAB82532_ISR0_PERR)
+                               up->port.icount.parity++;
+                       else if (stat->sreg.isr0 & SAB82532_ISR0_FERR)
+                               up->port.icount.frame++;
+                       if (stat->sreg.isr0 & SAB82532_ISR0_RFO)
+                               up->port.icount.overrun++;
+
+                       /*
+                        * Mask off conditions which should be ingored.
+                        */
+                       stat->sreg.isr0 &= (up->port.read_status_mask & 0xff);
+                       stat->sreg.isr1 &= ((up->port.read_status_mask >> 8) & 0xff);
+
+                       if (stat->sreg.isr1 & SAB82532_ISR1_BRK) {
+                               *tty->flip.flag_buf_ptr = TTY_BREAK;
+                       } else if (stat->sreg.isr0 & SAB82532_ISR0_PERR)
+                               *tty->flip.flag_buf_ptr = TTY_PARITY;
+                       else if (stat->sreg.isr0 & SAB82532_ISR0_FERR)
+                               *tty->flip.flag_buf_ptr = TTY_FRAME;
+               }
 
-#ifdef SERIAL_DEBUG_INTR
-                printk("DR%02x:%02x...", (unsigned char)*(tty->flip.char_buf_ptr - 1), status);
-#endif
+               if (uart_handle_sysrq_char(&up->port, ch, regs))
+                       continue;
 
-               if (status & SAB82532_RSTAT_PE) {
-                       *tty->flip.flag_buf_ptr++ = TTY_PARITY;
-                       info->icount.parity++;
-               } else if (status & SAB82532_RSTAT_FE) {
-                       *tty->flip.flag_buf_ptr++ = TTY_FRAME;
-                       info->icount.frame++;
+               if ((stat->sreg.isr0 & (up->port.ignore_status_mask & 0xff)) == 0 &&
+                   (stat->sreg.isr1 & ((up->port.ignore_status_mask >> 8) & 0xff)) == 0){
+                       tty->flip.flag_buf_ptr++;
+                       tty->flip.char_buf_ptr++;
+                       tty->flip.count++;
+               }
+               if ((stat->sreg.isr0 & SAB82532_ISR0_RFO) &&
+                   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++;
                }
-               else
-                       *tty->flip.flag_buf_ptr++ = TTY_NORMAL;
        }
 
-       queue_task(&tty->flip.tqueue, &tq_timer);
+       tty_flip_buffer_push(tty);
+
+       if (saw_console_brk)
+               sun_do_break();
 }
 
-static void transmit_chars(struct sab82532 *info,
+static void sunsab_stop_tx(struct uart_port *, unsigned int);
+
+static void transmit_chars(struct uart_sunsab_port *up,
                           union sab82532_irq_status *stat)
 {
+       struct circ_buf *xmit = &up->port.info->xmit;
        int i;
 
        if (stat->sreg.isr1 & SAB82532_ISR1_ALLS) {
-               info->interrupt_mask1 |= SAB82532_IMR1_ALLS;
-               writeb(info->interrupt_mask1, &info->regs->w.imr1);
-               set_bit(SAB82532_ALLS, &info->irqflags);
+               up->interrupt_mask1 |= SAB82532_IMR1_ALLS;
+               writeb(up->interrupt_mask1, &up->regs->w.imr1);
+               set_bit(SAB82532_ALLS, &up->irqflags);
        }
 
        if (!(stat->sreg.isr1 & SAB82532_ISR1_XPR))
                return;
 
-       if (!(readb(&info->regs->r.star) & SAB82532_STAR_XFW)) {
-#ifdef SERIAL_DEBUG_FIFO
-               printk("%s: XPR, but no XFW (?)\n", __FUNCTION__);
-#endif
+       if (!(readb(&up->regs->r.star) & SAB82532_STAR_XFW))
                return;
-       }
-
-       set_bit(SAB82532_XPR, &info->irqflags);
 
-       if (!info->tty) {
-               info->interrupt_mask1 |= SAB82532_IMR1_XPR;
-               writeb(info->interrupt_mask1, &info->regs->w.imr1);
-               return;
-       }
+       set_bit(SAB82532_XPR, &up->irqflags);
 
-       if ((info->xmit.head == info->xmit.tail) ||
-           info->tty->stopped || info->tty->hw_stopped) {
-               info->interrupt_mask1 |= SAB82532_IMR1_XPR;
-               writeb(info->interrupt_mask1, &info->regs->w.imr1);
+       if (uart_circ_empty(xmit) || uart_tx_stopped(&up->port)) {
+               up->interrupt_mask1 |= SAB82532_IMR1_XPR;
+               writeb(up->interrupt_mask1, &up->regs->w.imr1);
                return;
        }
 
-       info->interrupt_mask1 &= ~(SAB82532_IMR1_ALLS);
-       writeb(info->interrupt_mask1, &info->regs->w.imr1);
-       clear_bit(SAB82532_ALLS, &info->irqflags);
+       up->interrupt_mask1 &= ~(SAB82532_IMR1_ALLS);
+       writeb(up->interrupt_mask1, &up->regs->w.imr1);
+       clear_bit(SAB82532_ALLS, &up->irqflags);
 
        /* Stuff 32 bytes into Transmit FIFO. */
-       clear_bit(SAB82532_XPR, &info->irqflags);
-       for (i = 0; i < info->xmit_fifo_size; i++) {
-               writeb(info->xmit.buf[info->xmit.tail],
-                      &info->regs->w.xfifo[i]);
-               info->xmit.tail = (info->xmit.tail + 1) & (SERIAL_XMIT_SIZE-1);
-               info->icount.tx++;
-               if (info->xmit.head == info->xmit.tail)
+       clear_bit(SAB82532_XPR, &up->irqflags);
+       for (i = 0; i < up->xmit_fifo_size; i++) {
+               writeb(xmit->buf[xmit->tail],
+                      &up->regs->w.xfifo[i]);
+               xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
+               up->port.icount.tx++;
+               if (uart_circ_empty(xmit))
                        break;
        }
 
        /* Issue a Transmit Frame command. */
-       sab82532_cec_wait(info);
-       writeb(SAB82532_CMDR_XF, &info->regs->w.cmdr);
+       sunsab_cec_wait(up);
+       writeb(SAB82532_CMDR_XF, &up->regs->w.cmdr);
 
-       if (CIRC_CNT(info->xmit.head, info->xmit.tail, SERIAL_XMIT_SIZE) < WAKEUP_CHARS)
-               sab82532_sched_event(info, RS_EVENT_WRITE_WAKEUP);
+       if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
+               uart_event(&up->port, EVT_WRITE_WAKEUP);
 
-#ifdef SERIAL_DEBUG_INTR
-       printk("THRE...");
-#endif
+       if (uart_circ_empty(xmit))
+               sunsab_stop_tx(&up->port, 0);
 }
 
-static void check_status(struct sab82532 *info,
+static void check_status(struct uart_sunsab_port *up,
                         union sab82532_irq_status *stat)
 {
-       struct tty_struct *tty = info->tty;
-       int modem_change = 0;
-
-       if (stat->sreg.isr1 & SAB82532_ISR1_BRK) {
-#ifdef CONFIG_SERIAL_CONSOLE
-               if (info->is_console) {
-                       sun_do_break(info);
-                       return;
-               }
-#endif
-               if (tty->flip.count >= TTY_FLIPBUF_SIZE) {
-                       info->icount.buf_overrun++;
-                       goto check_modem;
-               }
-               tty->flip.count++;
-               *tty->flip.flag_buf_ptr++ = TTY_PARITY;
-               *tty->flip.char_buf_ptr++ = 0;
-               info->icount.brk++;
-       }
+       if (stat->sreg.isr0 & SAB82532_ISR0_CDSC)
+               uart_handle_dcd_change(&up->port,
+                                      !(readb(&up->regs->r.vstr) & SAB82532_VSTR_CD));
 
-       if (!tty)
-               return;
+       if (stat->sreg.isr1 & SAB82532_ISR1_CSC)
+               uart_handle_cts_change(&up->port,
+                                      (readb(&up->regs->r.star) & SAB82532_STAR_CTS));
 
-       if (stat->sreg.isr0 & SAB82532_ISR0_RFO) {
-               if (tty->flip.count >= TTY_FLIPBUF_SIZE) {
-                       info->icount.buf_overrun++;
-                       goto check_modem;
-               }
-               tty->flip.count++;
-               *tty->flip.flag_buf_ptr++ = TTY_PARITY;
-               *tty->flip.char_buf_ptr++ = 0;
-               info->icount.overrun++;
+       if ((readb(&up->regs->r.pvr) & up->pvr_dsr_bit) ^ up->dsr) {
+               up->dsr = (readb(&up->regs->r.pvr) & up->pvr_dsr_bit) ? 0 : 1;
+               up->port.icount.dsr++;
        }
 
-check_modem:
-       if (stat->sreg.isr0 & SAB82532_ISR0_CDSC) {
-               info->dcd = (readb(&info->regs->r.vstr) & SAB82532_VSTR_CD) ? 0 : 1;
-               info->icount.dcd++;
-               modem_change++;
-#ifdef SERIAL_DEBUG_MODEM
-               printk("DCD change: %d\n", info->icount.dcd);
-#endif
-       }
-       if (stat->sreg.isr1 & SAB82532_ISR1_CSC) {
-               info->cts = readb(&info->regs->r.star) & SAB82532_STAR_CTS;
-               info->icount.cts++;
-               modem_change++;
-#ifdef SERIAL_DEBUG_MODEM
-               printk("CTS change: %d, CTS %s\n", info->icount.cts, info->cts ? "on" : "off");
-#endif
-       }
-       if ((readb(&info->regs->r.pvr) & info->pvr_dsr_bit) ^ info->dsr) {
-               info->dsr = (readb(&info->regs->r.pvr) & info->pvr_dsr_bit) ? 0 : 1;
-               info->icount.dsr++;
-               modem_change++;
-#ifdef SERIAL_DEBUG_MODEM
-               printk("DSR change: %d\n", info->icount.dsr);
-#endif
-       }
-       if (modem_change)
-               wake_up_interruptible(&info->delta_msr_wait);
+       wake_up_interruptible(&up->port.info->delta_msr_wait);
+}
 
-       if ((info->flags & ASYNC_CHECK_CD) &&
-           (stat->sreg.isr0 & SAB82532_ISR0_CDSC)) {
+static void sunsab_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+       struct uart_sunsab_port *up = dev_id;
+       union sab82532_irq_status status;
+       unsigned long flags;
 
-#if (defined(SERIAL_DEBUG_OPEN) || defined(SERIAL_DEBUG_INTR))
-               printk("ttys%d CD now %s...", info->line,
-                      (info->dcd) ? "on" : "off");
-#endif         
+       spin_lock_irqsave(&up->port.lock, flags);
 
-               if (info->dcd)
-                       wake_up_interruptible(&info->open_wait);
-               else if (!((info->flags & ASYNC_CALLOUT_ACTIVE) &&
-                          (info->flags & ASYNC_CALLOUT_NOHUP))) {
+       status.stat = 0;
+       if (readb(&up->regs->r.gis) & SAB82532_GIS_ISA0)
+               status.sreg.isr0 = readb(&up->regs->r.isr0);
+       if (readb(&up->regs->r.gis) & SAB82532_GIS_ISA1)
+               status.sreg.isr1 = readb(&up->regs->r.isr1);
 
-#ifdef SERIAL_DEBUG_OPEN
-                       printk("scheduling hangup...");
-#endif
-                       MOD_INC_USE_COUNT;
-                       if (schedule_task(&info->tqueue_hangup) == 0)
-                               MOD_DEC_USE_COUNT;
-               }
+       if (status.stat) {
+               if (status.sreg.isr0 & (SAB82532_ISR0_TCD | SAB82532_ISR0_TIME |
+                                       SAB82532_ISR0_RFO | SAB82532_ISR0_RPF))
+                       receive_chars(up, &status, regs);
+               if ((status.sreg.isr0 & SAB82532_ISR0_CDSC) ||
+                   (status.sreg.isr1 & SAB82532_ISR1_CSC))
+                       check_status(up, &status);
+               if (status.sreg.isr1 & (SAB82532_ISR1_ALLS | SAB82532_ISR1_XPR))
+                       transmit_chars(up, &status);
        }
 
-       if (info->flags & ASYNC_CTS_FLOW) {
-               if (info->tty->hw_stopped) {
-                       if (info->cts) {
+       spin_unlock(&up->port.lock);
 
-#if (defined(SERIAL_DEBUG_INTR) || defined(SERIAL_DEBUG_FLOW))
-                               printk("CTS tx start...");
-#endif
-                               info->tty->hw_stopped = 0;
-                               sab82532_sched_event(info,
-                                                    RS_EVENT_WRITE_WAKEUP);
-                               info->interrupt_mask1 &= ~(SAB82532_IMR1_XPR);
-                               writeb(info->interrupt_mask1, &info->regs->w.imr1);
-                               sab82532_start_tx(info);
-                       }
-               } else {
-                       if (!(info->cts)) {
+       up++;
 
-#if (defined(SERIAL_DEBUG_INTR) || defined(SERIAL_DEBUG_FLOW))
-                               printk("CTS tx stop...");
-#endif
-                               info->tty->hw_stopped = 1;
-                       }
-               }
+       spin_lock(&up->port.lock);
+
+       status.stat = 0;
+       if (readb(&up->regs->r.gis) & SAB82532_GIS_ISB0)
+               status.sreg.isr0 = readb(&up->regs->r.isr0);
+       if (readb(&up->regs->r.gis) & SAB82532_GIS_ISB1)
+               status.sreg.isr1 = readb(&up->regs->r.isr1);
+
+       if (status.stat) {
+               if (status.sreg.isr0 & (SAB82532_ISR0_TCD | SAB82532_ISR0_TIME |
+                                       SAB82532_ISR0_RFO | SAB82532_ISR0_RPF))
+                       receive_chars(up, &status, regs);
+               if ((status.sreg.isr0 & SAB82532_ISR0_CDSC) ||
+                   (status.sreg.isr1 & (SAB82532_ISR1_BRK | SAB82532_ISR1_CSC)))
+                       check_status(up, &status);
+               if (status.sreg.isr1 & (SAB82532_ISR1_ALLS | SAB82532_ISR1_XPR))
+                       transmit_chars(up, &status);
+       }
+
+       spin_unlock_irqrestore(&up->port.lock, flags);
+}
+
+/* port->lock is not held.  */
+static unsigned int sunsab_tx_empty(struct uart_port *port)
+{
+       struct uart_sunsab_port *up = (struct uart_sunsab_port *) port;
+       int ret;
+
+       /* Do not need a lock for a state test like this.  */
+       if (test_bit(SAB82532_ALLS, &up->irqflags))
+               ret = TIOCSER_TEMT;
+       else
+               ret = 0;
+
+       return ret;
+}
+
+/* port->lock held by caller.  */
+static void sunsab_set_mctrl(struct uart_port *port, unsigned int mctrl)
+{
+       struct uart_sunsab_port *up = (struct uart_sunsab_port *) port;
+
+       if (mctrl & TIOCM_RTS) {
+               writeb(readb(&up->regs->rw.mode) & ~SAB82532_MODE_FRTS,
+                      &up->regs->rw.mode);
+               writeb(readb(&up->regs->rw.mode) | SAB82532_MODE_RTS,
+                      &up->regs->rw.mode);
+       } else {
+               writeb(readb(&up->regs->rw.mode) | SAB82532_MODE_FRTS,
+                      &up->regs->rw.mode);
+               writeb(readb(&up->regs->rw.mode) | SAB82532_MODE_RTS,
+                      &up->regs->rw.mode);
+       }
+       if (mctrl & TIOCM_DTR) {
+               writeb(readb(&up->regs->rw.pvr) & ~(up->pvr_dtr_bit), &up->regs->rw.pvr);
+       } else {
+               writeb(readb(&up->regs->rw.pvr) | up->pvr_dtr_bit, &up->regs->rw.pvr);
        }
 }
 
-/*
- * This is the serial driver's generic interrupt routine
- */
-static void sab82532_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+/* port->lock is not held.  */
+static unsigned int sunsab_get_mctrl(struct uart_port *port)
 {
-       struct sab82532 *info = dev_id;
-       union sab82532_irq_status status;
+       struct uart_sunsab_port *up = (struct uart_sunsab_port *) port;
+       unsigned long flags;
+       unsigned char val;
+       unsigned int result;
 
-#ifdef SERIAL_DEBUG_INTR
-       printk("sab82532_interrupt(%d)...", irq);
-#endif
+       result = 0;
 
-       status.stat = 0;
-       if (readb(&info->regs->r.gis) & SAB82532_GIS_ISA0)
-               status.sreg.isr0 = readb(&info->regs->r.isr0);
-       if (readb(&info->regs->r.gis) & SAB82532_GIS_ISA1)
-               status.sreg.isr1 = readb(&info->regs->r.isr1);
-
-#ifdef SERIAL_DEBUG_INTR
-       printk("%d<%02x.%02x>", info->line,
-              status.sreg.isr0, status.sreg.isr1);
-#endif
+       spin_lock_irqsave(&up->port.lock, flags);
 
-       if (!status.stat)
-               goto next;
+       val = readb(&up->regs->r.pvr);
+       result |= (val & up->pvr_dsr_bit) ? 0 : TIOCM_DSR;
 
-       if (status.sreg.isr0 & (SAB82532_ISR0_TCD | SAB82532_ISR0_TIME |
-                               SAB82532_ISR0_RFO | SAB82532_ISR0_RPF))
-               receive_chars(info, &status);
-       if ((status.sreg.isr0 & SAB82532_ISR0_CDSC) ||
-           (status.sreg.isr1 & (SAB82532_ISR1_BRK | SAB82532_ISR1_CSC)))
-               check_status(info, &status);
-       if (status.sreg.isr1 & (SAB82532_ISR1_ALLS | SAB82532_ISR1_XPR))
-               transmit_chars(info, &status);
+       val = readb(&up->regs->r.vstr);
+       result |= (val & SAB82532_VSTR_CD) ? 0 : TIOCM_CAR;
 
-next:
-       info = info->next;
-       status.stat = 0;
-       if (readb(&info->regs->r.gis) & SAB82532_GIS_ISB0)
-               status.sreg.isr0 = readb(&info->regs->r.isr0);
-       if (readb(&info->regs->r.gis) & SAB82532_GIS_ISB1)
-               status.sreg.isr1 = readb(&info->regs->r.isr1);
-
-#ifdef SERIAL_DEBUG_INTR
-       printk("%d<%02x.%02x>", info->line,
-              status.sreg.isr0, status.sreg.isr1);
-#endif
+       val = readb(&up->regs->r.star);
+       result |= (val & SAB82532_STAR_CTS) ? TIOCM_CTS : 0;
 
-       if (!status.stat)
-               goto done;
-
-       if (status.sreg.isr0 & (SAB82532_ISR0_TCD | SAB82532_ISR0_TIME |
-                               SAB82532_ISR0_RFO | SAB82532_ISR0_RPF))
-               receive_chars(info, &status);
-       if ((status.sreg.isr0 & SAB82532_ISR0_CDSC) ||
-           (status.sreg.isr1 & (SAB82532_ISR1_BRK | SAB82532_ISR1_CSC)))
-               check_status(info, &status);
-       if (status.sreg.isr1 & (SAB82532_ISR1_ALLS | SAB82532_ISR1_XPR))
-               transmit_chars(info, &status);
-
-done:
-       ;
-#ifdef SERIAL_DEBUG_INTR
-       printk("end.\n");
-#endif
-}
+       spin_unlock_irqrestore(&up->port.lock, flags);
 
-/*
- * -------------------------------------------------------------------
- * Here ends the serial interrupt routines.
- * -------------------------------------------------------------------
- */
+       return result;
+}
 
-/*
- * 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
- * sab82532_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 sab82532_sched_event(), and they get done here.
- */
-static void do_serial_bh(void)
+/* port->lock held by caller.  */
+static void sunsab_stop_tx(struct uart_port *port, unsigned int tty_stop)
 {
-       run_task_queue(&tq_serial);
+       struct uart_sunsab_port *up = (struct uart_sunsab_port *) port;
+
+       up->interrupt_mask1 |= SAB82532_IMR1_XPR;
+       writeb(up->interrupt_mask1, &up->regs->w.imr1);
 }
 
-static void do_softint(void *private_)
+/* port->lock held by caller.  */
+static void sunsab_start_tx(struct uart_port *port, unsigned int tty_start)
 {
-       struct sab82532 *info = (struct sab82532 *)private_;
-       struct tty_struct *tty;
+       struct uart_sunsab_port *up = (struct uart_sunsab_port *) port;
+       struct circ_buf *xmit = &up->port.info->xmit;
+       int i;
 
-       tty = info->tty;
-       if (!tty)
+       if (!test_bit(SAB82532_XPR, &up->irqflags))
                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);
+       up->interrupt_mask1 &= ~SAB82532_IMR1_XPR;
+       writeb(up->interrupt_mask1, &up->regs->w.imr1);
+       clear_bit(SAB82532_ALLS, &up->irqflags);
+       clear_bit(SAB82532_XPR, &up->irqflags);
+
+       for (i = 0; i < up->xmit_fifo_size; i++) {
+               writeb(xmit->buf[xmit->tail],
+                      &up->regs->w.xfifo[i]);
+               xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
+               up->port.icount.tx++;
+               if (uart_circ_empty(xmit))
+                       break;
        }
+
+       /* Issue a Transmit Frame command.  */
+       sunsab_cec_wait(up);
+       writeb(SAB82532_CMDR_XF, &up->regs->w.cmdr);
 }
 
-/*
- * This routine is called from the scheduler tqueue when the interrupt
- * routine has signalled that a hangup has occurred.  The path of
- * hangup processing is:
- *
- *     serial interrupt routine -> (scheduler tqueue) ->
- *     do_serial_hangup() -> tty->hangup() -> sab82532_hangup()
- * 
- */
-static void do_serial_hangup(void *private_)
+/* port->lock is not held.  */
+static void sunsab_send_xchar(struct uart_port *port, char ch)
+{
+       struct uart_sunsab_port *up = (struct uart_sunsab_port *) port;
+       unsigned long flags;
+
+       spin_lock_irqsave(&up->port.lock, flags);
+
+       sunsab_tec_wait(up);
+       writeb(ch, &up->regs->w.tic);
+
+       spin_unlock_irqrestore(&up->port.lock, flags);
+}
+
+/* port->lock held by caller.  */
+static void sunsab_stop_rx(struct uart_port *port)
+{
+       struct uart_sunsab_port *up = (struct uart_sunsab_port *) port;
+
+       up->interrupt_mask0 |= SAB82532_ISR0_TCD;
+       writeb(up->interrupt_mask1, &up->regs->w.imr0);
+}
+
+/* port->lock held by caller.  */
+static void sunsab_enable_ms(struct uart_port *port)
 {
-       struct sab82532 *info = (struct sab82532 *) private_;
-       struct tty_struct *tty;
+       /* For now we always receive these interrupts.  */
+}
+
+/* port->lock is not held.  */
+static void sunsab_break_ctl(struct uart_port *port, int break_state)
+{
+       struct uart_sunsab_port *up = (struct uart_sunsab_port *) port;
+       unsigned long flags;
+       unsigned char val;
 
-       tty = info->tty;
-       if (tty)
-               tty_hangup(tty);
-       MOD_DEC_USE_COUNT;
+       spin_lock_irqsave(&up->port.lock, flags);
+
+       val = readb(&up->regs->rw.dafo);
+       if (break_state)
+               val |= SAB82532_DAFO_XBRK;
+       else
+               val &= ~SAB82532_DAFO_XBRK;
+       writeb(val, &up->regs->rw.dafo);
+
+       spin_unlock_irqrestore(&up->port.lock, flags);
 }
 
-static void
-sab82532_init_line(struct sab82532 *info)
+/* port->lock is not held.  */
+static int sunsab_startup(struct uart_port *port)
 {
-       unsigned char stat, tmp;
+       struct uart_sunsab_port *up = (struct uart_sunsab_port *) port;
+       unsigned long flags;
+       unsigned char tmp;
+
+       spin_lock_irqsave(&up->port.lock, flags);
 
        /*
         * Wait for any commands or immediate characters
         */
-       sab82532_cec_wait(info);
-       sab82532_tec_wait(info);
+       sunsab_cec_wait(up);
+       sunsab_tec_wait(up);
 
        /*
         * Clear the FIFO buffers.
         */
-       writeb(SAB82532_CMDR_RRES, &info->regs->w.cmdr);
-       sab82532_cec_wait(info);
-       writeb(SAB82532_CMDR_XRES, &info->regs->w.cmdr);
+       writeb(SAB82532_CMDR_RRES, &up->regs->w.cmdr);
+       sunsab_cec_wait(up);
+       writeb(SAB82532_CMDR_XRES, &up->regs->w.cmdr);
 
        /*
         * Clear the interrupt registers.
         */
-       stat = readb(&info->regs->r.isr0);
-       stat = readb(&info->regs->r.isr1);
+       (void) readb(&up->regs->r.isr0);
+       (void) readb(&up->regs->r.isr1);
 
        /*
         * Now, initialize the UART 
         */
-       writeb(0, &info->regs->w.ccr0);                         /* power-down */
+       writeb(0, &up->regs->w.ccr0);                           /* power-down */
        writeb(SAB82532_CCR0_MCE | SAB82532_CCR0_SC_NRZ |
-              SAB82532_CCR0_SM_ASYNC, &info->regs->w.ccr0);
-       writeb(SAB82532_CCR1_ODS | SAB82532_CCR1_BCR | 7, &info->regs->w.ccr1);
+              SAB82532_CCR0_SM_ASYNC, &up->regs->w.ccr0);
+       writeb(SAB82532_CCR1_ODS | SAB82532_CCR1_BCR | 7, &up->regs->w.ccr1);
        writeb(SAB82532_CCR2_BDF | SAB82532_CCR2_SSEL |
-              SAB82532_CCR2_TOE, &info->regs->w.ccr2);
-       writeb(0, &info->regs->w.ccr3);
-       writeb(SAB82532_CCR4_MCK4 | SAB82532_CCR4_EBRG, &info->regs->w.ccr4);
+              SAB82532_CCR2_TOE, &up->regs->w.ccr2);
+       writeb(0, &up->regs->w.ccr3);
+       writeb(SAB82532_CCR4_MCK4 | SAB82532_CCR4_EBRG, &up->regs->w.ccr4);
        writeb(SAB82532_MODE_RTS | SAB82532_MODE_FCTS |
-              SAB82532_MODE_RAC, &info->regs->w.mode);
-       writeb(SAB82532_RFC_DPS | SAB82532_RFC_RFDF, &info->regs->w.rfc);
-       switch (info->recv_fifo_size) {
+              SAB82532_MODE_RAC, &up->regs->w.mode);
+       writeb(SAB82532_RFC_DPS | SAB82532_RFC_RFDF, &up->regs->w.rfc);
+
+       switch (up->recv_fifo_size) {
                case 1:
-                       tmp = readb(&info->regs->w.rfc);
+                       tmp = readb(&up->regs->w.rfc);
                        tmp |= SAB82532_RFC_RFTH_1;
-                       writeb(tmp, &info->regs->w.rfc);
+                       writeb(tmp, &up->regs->w.rfc);
                        break;
                case 4:
-                       tmp = readb(&info->regs->w.rfc);
+                       tmp = readb(&up->regs->w.rfc);
                        tmp |= SAB82532_RFC_RFTH_4;
-                       writeb(tmp, &info->regs->w.rfc);
+                       writeb(tmp, &up->regs->w.rfc);
                        break;
                case 16:
-                       tmp = readb(&info->regs->w.rfc);
+                       tmp = readb(&up->regs->w.rfc);
                        tmp |= SAB82532_RFC_RFTH_16;
-                       writeb(tmp, &info->regs->w.rfc);
+                       writeb(tmp, &up->regs->w.rfc);
                        break;
                default:
-                       info->recv_fifo_size = 32;
+                       up->recv_fifo_size = 32;
                        /* fall through */
                case 32:
-                       tmp = readb(&info->regs->w.rfc);
+                       tmp = readb(&up->regs->w.rfc);
                        tmp |= SAB82532_RFC_RFTH_32;
-                       writeb(tmp, &info->regs->w.rfc);
+                       writeb(tmp, &up->regs->w.rfc);
                        break;
-       }
-       tmp = readb(&info->regs->rw.ccr0);
+       };
+
+       tmp = readb(&up->regs->rw.ccr0);
        tmp |= SAB82532_CCR0_PU;        /* power-up */
-       writeb(tmp, &info->regs->rw.ccr0);
+       writeb(tmp, &up->regs->rw.ccr0);
+
+       /*
+        * Finally, enable interrupts
+        */
+       up->interrupt_mask0 = (SAB82532_IMR0_PERR | SAB82532_IMR0_FERR |
+                              SAB82532_IMR0_PLLA);
+       writeb(up->interrupt_mask0, &up->regs->w.imr0);
+       up->interrupt_mask1 = (SAB82532_IMR1_BRKT | SAB82532_IMR1_ALLS |
+                              SAB82532_IMR1_XOFF | SAB82532_IMR1_TIN |
+                              SAB82532_IMR1_CSC | SAB82532_IMR1_XON |
+                              SAB82532_IMR1_XPR);
+       writeb(up->interrupt_mask1, &up->regs->w.imr1);
+       set_bit(SAB82532_ALLS, &up->irqflags);
+       set_bit(SAB82532_XPR, &up->irqflags);
+
+       spin_unlock_irqrestore(&up->port.lock, flags);
+
+       return 0;
 }
 
-static int startup(struct sab82532 *info)
+/* port->lock is not held.  */
+static void sunsab_shutdown(struct uart_port *port)
 {
+       struct uart_sunsab_port *up = (struct uart_sunsab_port *) port;
        unsigned long flags;
-       unsigned long page;
-       int retval = 0;
+       unsigned char tmp;
 
-       page = get_free_page(GFP_KERNEL);
-       if (!page)
-               return -ENOMEM;
+       spin_lock_irqsave(&up->port.lock, flags);
 
-       save_flags(flags); cli();
+       /* Disable Interrupts */
+       up->interrupt_mask0 = 0xff;
+       writeb(up->interrupt_mask0, &up->regs->w.imr0);
+       up->interrupt_mask1 = 0xff;
+       writeb(up->interrupt_mask1, &up->regs->w.imr1);
 
-       if (info->flags & ASYNC_INITIALIZED) {
-               free_page(page);
-               goto errout;
-       }
+       /* Disable break condition */
+       tmp = readb(&up->regs->rw.dafo);
+       tmp &= ~SAB82532_DAFO_XBRK;
+       writeb(tmp, &up->regs->rw.dafo);
 
-       if (!info->regs) {
-               if (info->tty)
-                       set_bit(TTY_IO_ERROR, &info->tty->flags);
-               free_page(page);
-               retval = -ENODEV;
-               goto errout;
-       }
-       if (info->xmit.buf)
-               free_page(page);
-       else
-               info->xmit.buf = (unsigned char *)page;
+       /* Disable Receiver */  
+       tmp = readb(&up->regs->rw.mode);
+       tmp &= ~SAB82532_MODE_RAC;
+       writeb(tmp, &up->regs->rw.mode);
 
-#ifdef SERIAL_DEBUG_OPEN
-       printk("starting up serial port %d...", info->line);
-#endif
+       /* Power Down */        
+       tmp = readb(&up->regs->rw.ccr0);
+       tmp &= ~SAB82532_CCR0_PU;
+       writeb(tmp, &up->regs->rw.ccr0);
 
-       /*
-        * Initialize the Hardware
-        */
-       sab82532_init_line(info);
+       spin_unlock_irqrestore(&up->port.lock, flags);
+}
 
-       if (info->tty->termios->c_cflag & CBAUD) {
-               u8 tmp;
+/*
+ * This is used to figure out the divisor speeds.
+ *
+ * The formula is:    Baud = SAB_BASE_BAUD / ((N + 1) * (1 << M)),
+ *
+ * with               0 <= N < 64 and 0 <= M < 16
+ */
 
-               tmp = readb(&info->regs->rw.mode);
-               tmp &= ~(SAB82532_MODE_FRTS);
-               tmp |= SAB82532_MODE_RTS;
-               writeb(tmp, &info->regs->rw.mode);
+static void calc_ebrg(int baud, int *n_ret, int *m_ret)
+{
+       int     n, m;
 
-               tmp = readb(&info->regs->rw.pvr);
-               tmp &= ~(info->pvr_dtr_bit);
-               writeb(tmp, &info->regs->rw.pvr);
+       if (baud == 0) {
+               *n_ret = 0;
+               *m_ret = 0;
+               return;
        }
-
+     
        /*
-        * Finally, enable interrupts
-        */
-       info->interrupt_mask0 = SAB82532_IMR0_PERR | SAB82532_IMR0_FERR |
-                               SAB82532_IMR0_PLLA;
-       writeb(info->interrupt_mask0, &info->regs->w.imr0);
-       info->interrupt_mask1 = SAB82532_IMR1_BRKT | SAB82532_IMR1_ALLS |
-                               SAB82532_IMR1_XOFF | SAB82532_IMR1_TIN |
-                               SAB82532_IMR1_CSC | SAB82532_IMR1_XON |
-                               SAB82532_IMR1_XPR;
-       writeb(info->interrupt_mask1, &info->regs->w.imr1);
-       set_bit(SAB82532_ALLS, &info->irqflags);
-
-       if (info->tty)
-               clear_bit(TTY_IO_ERROR, &info->tty->flags);
-       info->xmit.head = info->xmit.tail = 0;
-
-       set_bit(SAB82532_XPR, &info->irqflags);
-
-       /*
-        * and set the speed of the serial port
+        * We scale numbers by 10 so that we get better accuracy
+        * without having to use floating point.  Here we increment m
+        * until n is within the valid range.
         */
-       change_speed(info);
-
-       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 sab82532 *info)
-{
-       unsigned long flags;
-       u8 tmp;
-
-       if (!(info->flags & ASYNC_INITIALIZED))
-               return;
-
-#ifdef SERIAL_DEBUG_OPEN
-       printk("Shutting down serial port %d...", info->line);
-#endif
-
-       save_flags(flags); cli(); /* Disable interrupts */
-
+       n = (SAB_BASE_BAUD * 10) / baud;
+       m = 0;
+       while (n >= 640) {
+               n = n / 2;
+               m++;
+       }
+       n = (n+5) / 10;
        /*
-        * clear delta_msr_wait queue to avoid mem leaks: we may free the irq
-        * here so the queue might never be waken up
+        * We try very hard to avoid speeds with M == 0 since they may
+        * not work correctly for XTAL frequences above 10 MHz.
         */
-       wake_up_interruptible(&info->delta_msr_wait);
-
-       if (info->xmit.buf) {
-               free_page((unsigned long)info->xmit.buf);
-               info->xmit.buf = 0;
-       }
-
-       if (info->is_console) {
-               info->interrupt_mask0 = SAB82532_IMR0_PERR | SAB82532_IMR0_FERR |
-                                       SAB82532_IMR0_PLLA | SAB82532_IMR0_CDSC;
-               writeb(info->interrupt_mask0, &info->regs->w.imr0);
-               info->interrupt_mask1 = SAB82532_IMR1_BRKT | SAB82532_IMR1_ALLS |
-                                       SAB82532_IMR1_XOFF | SAB82532_IMR1_TIN |
-                                       SAB82532_IMR1_CSC | SAB82532_IMR1_XON |
-                                       SAB82532_IMR1_XPR;
-               writeb(info->interrupt_mask1, &info->regs->w.imr1);
-               if (info->tty)
-                       set_bit(TTY_IO_ERROR, &info->tty->flags);
-               info->flags &= ~ASYNC_INITIALIZED;
-               restore_flags(flags);
-               return;
-       }
-
-       /* Disable Interrupts */
-       info->interrupt_mask0 = 0xff;
-       writeb(info->interrupt_mask0, &info->regs->w.imr0);
-       info->interrupt_mask1 = 0xff;
-       writeb(info->interrupt_mask1, &info->regs->w.imr1);
-
-       if (!info->tty || (info->tty->termios->c_cflag & HUPCL)) {
-               tmp = readb(&info->regs->r.mode);
-               tmp |= (SAB82532_MODE_FRTS | SAB82532_MODE_RTS);
-               writeb(tmp, &info->regs->rw.mode);
-               writeb(readb(&info->regs->rw.pvr) | info->pvr_dtr_bit,
-                      &info->regs->rw.pvr);
+       if ((m == 0) && ((n & 1) == 0)) {
+               n = n / 2;
+               m++;
        }
-
-       /* Disable break condition */
-       tmp = readb(&info->regs->rw.dafo);
-       tmp &= ~(SAB82532_DAFO_XBRK);
-       writeb(tmp, &info->regs->rw.dafo);
-
-       /* Disable Receiver */  
-       tmp = readb(&info->regs->rw.mode);
-       tmp &= ~(SAB82532_MODE_RAC);
-       writeb(tmp, &info->regs->rw.mode);
-
-       /* Power Down */        
-       tmp = readb(&info->regs->rw.ccr0);
-       tmp &= ~(SAB82532_CCR0_PU);
-       writeb(tmp, &info->regs->rw.ccr0);
-
-       if (info->tty)
-               set_bit(TTY_IO_ERROR, &info->tty->flags);
-
-       info->flags &= ~ASYNC_INITIALIZED;
-       restore_flags(flags);
+       *n_ret = n - 1;
+       *m_ret = m;
 }
 
-/*
- * 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 sab82532 *info)
+/* Internal routine, port->lock is held and local interrupts are disabled.  */
+static void sunsab_convert_to_sab(struct uart_sunsab_port *up, unsigned int cflag,
+                                 unsigned int iflag, int baud)
 {
-       unsigned long   flags;
-       unsigned int    ebrg;
-       tcflag_t        cflag;
-       unsigned char   dafo;
-       int             bits, n, m;
-
-       if (!info->tty || !info->tty->termios)
-               return;
-       cflag = info->tty->termios->c_cflag;
+       unsigned int ebrg;
+       unsigned char dafo;
+       int bits, n, m;
 
        /* Byte size and parity */
        switch (cflag & CSIZE) {
@@ -959,1173 +688,296 @@ static void change_speed(struct sab82532 *info)
        }
 
        if (cflag & PARODD) {
-#ifdef CMSPAR
-               if (cflag & CMSPAR)
-                       dafo |= SAB82532_DAFO_PAR_MARK;
-               else
-#endif
-                       dafo |= SAB82532_DAFO_PAR_ODD;
+               dafo |= SAB82532_DAFO_PAR_ODD;
        } else {
-#ifdef CMSPAR
-               if (cflag & CMSPAR)
-                       dafo |= SAB82532_DAFO_PAR_SPACE;
-               else
-#endif
-                       dafo |= SAB82532_DAFO_PAR_EVEN;
+               dafo |= SAB82532_DAFO_PAR_EVEN;
        }
 
-       /* Determine EBRG values based on baud rate */
-       info->baud = tty_get_baud_rate(info->tty);
-       calc_ebrg(info->baud, &n, &m);
-       
+       calc_ebrg(baud, &n, &m);
+
        ebrg = n | (m << 6);
 
-       if (info->baud) {
-               info->timeout = (info->xmit_fifo_size * HZ * bits) / info->baud;
-               info->tec_timeout = (10 * 1000000) / info->baud;
-               info->cec_timeout = info->tec_timeout >> 2;
-       } else {
-               info->timeout = 0;
-               info->tec_timeout = SAB82532_MAX_TEC_TIMEOUT;
-               info->cec_timeout = SAB82532_MAX_CEC_TIMEOUT;
-       }
-       info->timeout += HZ / 50;               /* Add .02 seconds of slop */
+       up->tec_timeout = (10 * 1000000) / baud;
+       up->cec_timeout = up->tec_timeout >> 2;
 
        /* CTS flow control flags */
-       if (cflag & CRTSCTS)
-               info->flags |= ASYNC_CTS_FLOW;
-       else
-               info->flags &= ~(ASYNC_CTS_FLOW);
-
-       if (cflag & CLOCAL)
-               info->flags &= ~(ASYNC_CHECK_CD);
-       else
-               info->flags |= ASYNC_CHECK_CD;
-       if (info->tty)
-               info->tty->hw_stopped = 0;
-
-       /*
-        * Set up parity check flag
-        * XXX: not implemented, yet.
+       /* We encode read_status_mask and ignore_status_mask like so:
+        *
+        * ---------------------
+        * | ... | ISR1 | ISR0 |
+        * ---------------------
+        *  ..    15   8 7    0
         */
-#define RELEVANT_IFLAG(iflag) (iflag & (IGNBRK|BRKINT|IGNPAR|PARMRK|INPCK))
+
+       up->port.read_status_mask = (SAB82532_ISR0_TCD | SAB82532_ISR0_TIME |
+                                    SAB82532_ISR0_RFO | SAB82532_ISR0_RPF |
+                                    SAB82532_ISR0_CDSC);
+       up->port.read_status_mask |= (SAB82532_ISR1_CSC |
+                                     SAB82532_ISR1_ALLS |
+                                     SAB82532_ISR1_XPR) << 8;
+       if (iflag & INPCK)
+               up->port.read_status_mask |= (SAB82532_ISR0_PERR |
+                                             SAB82532_ISR0_FERR);
+       if (iflag & (BRKINT | PARMRK))
+               up->port.read_status_mask |= (SAB82532_ISR1_BRK << 8);
 
        /*
-        * Characters to ignore
-        * XXX: not implemented, yet.
+        * Characteres to ignore
         */
+       up->port.ignore_status_mask = 0;
+       if (iflag & IGNPAR)
+               up->port.ignore_status_mask |= (SAB82532_ISR0_PERR |
+                                               SAB82532_ISR0_FERR);
+       if (iflag & IGNBRK) {
+               up->port.ignore_status_mask |= (SAB82532_ISR1_BRK << 8);
+               /*
+                * If we're ignoring parity and break indicators,
+                * ignore overruns too (for real raw support).
+                */
+               if (iflag & IGNPAR)
+                       up->port.ignore_status_mask |= SAB82532_ISR0_RFO;
+       }
 
        /*
-        * !!! ignore all characters if CREAD is not set
-        * XXX: not implemented, yet.
+        * ignore all characters if CREAD is not set
         */
        if ((cflag & CREAD) == 0)
-               info->ignore_status_mask |= SAB82532_ISR0_RPF |
-                                           SAB82532_ISR0_TCD |
-                                           SAB82532_ISR0_TIME;
-
-       save_flags(flags); cli();
-       sab82532_cec_wait(info);
-       sab82532_tec_wait(info);
-       writeb(dafo, &info->regs->w.dafo);
-       writeb(ebrg & 0xff, &info->regs->w.bgr);
-       writeb(readb(&info->regs->rw.ccr2) & ~(0xc0), &info->regs->rw.ccr2);
-       writeb(readb(&info->regs->rw.ccr2) | ((ebrg >> 2) & 0xc0), &info->regs->rw.ccr2);
-       if (info->flags & ASYNC_CTS_FLOW) {
-               writeb(readb(&info->regs->rw.mode) & ~(SAB82532_MODE_RTS), &info->regs->rw.mode);
-               writeb(readb(&info->regs->rw.mode) | SAB82532_MODE_FRTS, &info->regs->rw.mode);
-               writeb(readb(&info->regs->rw.mode) & ~(SAB82532_MODE_FCTS), &info->regs->rw.mode);
-               info->interrupt_mask1 &= ~(SAB82532_IMR1_CSC);
-               writeb(info->interrupt_mask1, &info->regs->w.imr1);
-       } else {
-               writeb(readb(&info->regs->rw.mode) | SAB82532_MODE_RTS, &info->regs->rw.mode);
-               writeb(readb(&info->regs->rw.mode) & ~(SAB82532_MODE_FRTS), &info->regs->rw.mode);
-               writeb(readb(&info->regs->rw.mode) | SAB82532_MODE_FCTS, &info->regs->rw.mode);
-               info->interrupt_mask1 |= SAB82532_IMR1_CSC;
-               writeb(info->interrupt_mask1, &info->regs->w.imr1);
-       }
-       writeb(readb(&info->regs->rw.mode) | SAB82532_MODE_RAC, &info->regs->rw.mode);
-       restore_flags(flags);
-}
-
-static void sab82532_put_char(struct tty_struct *tty, unsigned char ch)
-{
-       struct sab82532 *info = (struct sab82532 *)tty->driver_data;
-       unsigned long flags;
-
-       if (serial_paranoia_check(info, tty->device, "sab82532_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)) {
-               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 sab82532_flush_chars(struct tty_struct *tty)
-{
-       struct sab82532 *info = (struct sab82532 *)tty->driver_data;
-       unsigned long flags;
-
-       if (serial_paranoia_check(info, tty->device, "sab82532_flush_chars"))
-               return;
-
-       if ((info->xmit.head == info->xmit.tail) ||
-           tty->stopped || tty->hw_stopped || !info->xmit.buf)
-               return;
-
-       save_flags(flags); cli();
-       info->interrupt_mask1 &= ~(SAB82532_IMR1_XPR);
-       writeb(info->interrupt_mask1, &info->regs->w.imr1);
-       sab82532_start_tx(info);
-       restore_flags(flags);
-}
-
-static int sab82532_write(struct tty_struct * tty, int from_user,
-                         const unsigned char *buf, int count)
-{
-       int c, ret = 0;
-       struct sab82532 *info = (struct sab82532 *)tty->driver_data;
-       unsigned long flags;
-
-       if (serial_paranoia_check(info, tty->device, "sab82532_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);
+               up->port.ignore_status_mask |= (SAB82532_ISR0_RPF |
+                                               SAB82532_ISR0_TCD);
+
+       /* Now bang the new settings into the chip.  */
+       sunsab_cec_wait(up);
+       sunsab_tec_wait(up);
+       writeb(dafo, &up->regs->w.dafo);
+       writeb(ebrg & 0xff, &up->regs->w.bgr);
+       writeb((readb(&up->regs->rw.ccr2) & ~0xc0) | ((ebrg >> 2) & 0xc0),
+              &up->regs->rw.ccr2);
+
+       if (cflag & CRTSCTS) {
+               writeb(readb(&up->regs->rw.mode) & ~SAB82532_MODE_RTS,
+                      &up->regs->rw.mode);
+               writeb(readb(&up->regs->rw.mode) | SAB82532_MODE_FRTS,
+                      &up->regs->rw.mode);
+               writeb(readb(&up->regs->rw.mode) & ~SAB82532_MODE_FCTS,
+                      &up->regs->rw.mode);
+               up->interrupt_mask1 &= ~SAB82532_IMR1_CSC;
+               writeb(up->interrupt_mask1, &up->regs->w.imr1);
        } 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->interrupt_mask1 &= ~(SAB82532_IMR1_XPR);
-               writeb(info->interrupt_mask1, &info->regs->w.imr1);
-               sab82532_start_tx(info);
+               writeb(readb(&up->regs->rw.mode) | SAB82532_MODE_RTS,
+                      &up->regs->rw.mode);
+               writeb(readb(&up->regs->rw.mode) & ~SAB82532_MODE_FRTS,
+                      &up->regs->rw.mode);
+               writeb(readb(&up->regs->rw.mode) | SAB82532_MODE_FCTS,
+                      &up->regs->rw.mode);
+               up->interrupt_mask1 |= SAB82532_IMR1_CSC;
+               writeb(up->interrupt_mask1, &up->regs->w.imr1);
        }
+       writeb(readb(&up->regs->rw.mode) | SAB82532_MODE_RAC, &up->regs->rw.mode);
 
-       restore_flags(flags);
-       return ret;
 }
 
-static int sab82532_write_room(struct tty_struct *tty)
+/* port->lock is not held.  */
+static void sunsab_change_speed(struct uart_port *port, unsigned int cflag,
+                               unsigned int iflag, unsigned int quot)
 {
-       struct sab82532 *info = (struct sab82532 *)tty->driver_data;
-
-       if (serial_paranoia_check(info, tty->device, "sab82532_write_room"))
-               return 0;
-
-       return CIRC_SPACE(info->xmit.head, info->xmit.tail, SERIAL_XMIT_SIZE);
-}
-
-static int sab82532_chars_in_buffer(struct tty_struct *tty)
-{
-       struct sab82532 *info = (struct sab82532 *)tty->driver_data;
-                               
-       if (serial_paranoia_check(info, tty->device, "sab82532_chars_in_buffer"))
-               return 0;
-
-       return CIRC_CNT(info->xmit.head, info->xmit.tail, SERIAL_XMIT_SIZE);
-}
-
-static void sab82532_flush_buffer(struct tty_struct *tty)
-{
-       struct sab82532 *info = (struct sab82532 *)tty->driver_data;
+       struct uart_sunsab_port *up = (struct uart_sunsab_port *) port;
        unsigned long flags;
+       int baud;
 
-       if (serial_paranoia_check(info, tty->device, "sab82532_flush_buffer"))
-               return;
+       spin_lock_irqsave(&up->port.lock, flags);
 
-       save_flags(flags); cli();
-       info->xmit.head = info->xmit.tail = 0;
-       restore_flags(flags);
+       /* Undo what generic UART core did.  */
+       baud = (SAB_BASE_BAUD / (quot * 16));
 
-       wake_up_interruptible(&tty->write_wait);
-       if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
-           tty->ldisc.write_wakeup)
-               (tty->ldisc.write_wakeup)(tty);
-}
+       sunsab_convert_to_sab(up, cflag, iflag, baud);
 
-/*
- * This function is used to send a high-priority XON/XOFF character to
- * the device
- */
-static void sab82532_send_xchar(struct tty_struct *tty, char ch)
-{
-       struct sab82532 *info = (struct sab82532 *)tty->driver_data;
-       unsigned long flags;
-
-       if (serial_paranoia_check(info, tty->device, "sab82532_send_xchar"))
-               return;
-
-       save_flags(flags); cli();
-       sab82532_tec_wait(info);
-       writeb(ch, &info->regs->w.tic);
-       restore_flags(flags);
+       spin_unlock_irqrestore(&up->port.lock, flags);
 }
 
-/*
- * ------------------------------------------------------------
- * sab82532_throttle()
- * 
- * This routine is called by the upper-layer tty layer to signal that
- * incoming characters should be throttled.
- * ------------------------------------------------------------
- */
-static void sab82532_throttle(struct tty_struct * tty)
+static const char *sunsab_type(struct uart_port *port)
 {
-       struct sab82532 *info = (struct sab82532 *)tty->driver_data;
-#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, "sab82532_throttle"))
-               return;
-       
-       if (I_IXOFF(tty))
-               sab82532_send_xchar(tty, STOP_CHAR(tty));
-
-       if (tty->termios->c_cflag & CRTSCTS) {
-               u8 mode = readb(&info->regs->r.mode);
-               mode &= ~(SAB82532_MODE_FRTS | SAB82532_MODE_RTS);
-               writeb(mode, &info->regs->w.mode);
-       }
+       return "SunSAB";
 }
 
-static void sab82532_unthrottle(struct tty_struct * tty)
+static void sunsab_release_port(struct uart_port *port)
 {
-       struct sab82532 *info = (struct sab82532 *)tty->driver_data;
-#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, "sab82532_unthrottle"))
-               return;
-       
-       if (I_IXOFF(tty)) {
-               if (info->x_char)
-                       info->x_char = 0;
-               else
-                       sab82532_send_xchar(tty, START_CHAR(tty));
-       }
-
-       if (tty->termios->c_cflag & CRTSCTS) {
-               u8 mode = readb(&info->regs->r.mode);
-               mode &= ~(SAB82532_MODE_RTS);
-               mode |= SAB82532_MODE_FRTS;
-               writeb(mode, &info->regs->w.mode);
-       }
 }
 
-/*
- * ------------------------------------------------------------
- * sab82532_ioctl() and friends
- * ------------------------------------------------------------
- */
-
-static int get_serial_info(struct sab82532 *info,
-                          struct serial_struct *retinfo)
+static int sunsab_request_port(struct uart_port *port)
 {
-       struct serial_struct tmp;
-   
-       if (!retinfo)
-               return -EFAULT;
-       memset(&tmp, 0, sizeof(tmp));
-       tmp.type = info->type;
-       tmp.line = info->line;
-       tmp.port = (unsigned long)info->regs;
-       tmp.irq = info->irq;
-       tmp.flags = info->flags;
-       tmp.xmit_fifo_size = info->xmit_fifo_size;
-       tmp.baud_base = info->baud_base;
-       tmp.close_delay = info->close_delay;
-       tmp.closing_wait = info->closing_wait;
-       tmp.custom_divisor = info->custom_divisor;
-       tmp.hub6 = 0;
-       if (copy_to_user(retinfo, &tmp, sizeof(*retinfo)))
-               return -EFAULT;
        return 0;
 }
 
-static int set_serial_info(struct sab82532 *info,
-                          struct serial_struct *new_info)
+static void sunsab_config_port(struct uart_port *port, int flags)
 {
-       return 0;
 }
 
-
-/*
- * 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 sab82532 * info, unsigned int *value)
+static int sunsab_verify_port(struct uart_port *port, struct serial_struct *ser)
 {
-       unsigned int result;
-
-       result = (!info->xmit.buf && test_bit(SAB82532_ALLS, &info->irqflags))
-                                                       ? TIOCSER_TEMT : 0;
-       return put_user(result, value);
+       return -EINVAL;
 }
 
+static struct uart_ops sunsab_pops = {
+       .tx_empty       = sunsab_tx_empty,
+       .set_mctrl      = sunsab_set_mctrl,
+       .get_mctrl      = sunsab_get_mctrl,
+       .stop_tx        = sunsab_stop_tx,
+       .start_tx       = sunsab_start_tx,
+       .send_xchar     = sunsab_send_xchar,
+       .stop_rx        = sunsab_stop_rx,
+       .enable_ms      = sunsab_enable_ms,
+       .break_ctl      = sunsab_break_ctl,
+       .startup        = sunsab_startup,
+       .shutdown       = sunsab_shutdown,
+       .change_speed   = sunsab_change_speed,
+       .type           = sunsab_type,
+       .release_port   = sunsab_release_port,
+       .request_port   = sunsab_request_port,
+       .config_port    = sunsab_config_port,
+       .verify_port    = sunsab_verify_port,
+};
 
-static int get_modem_info(struct sab82532 * info, unsigned int *value)
-{
-       unsigned int result;
-
-       result =  ((readb(&info->regs->r.mode) & SAB82532_MODE_RTS) ? 
-                   ((readb(&info->regs->r.mode) & SAB82532_MODE_FRTS) ? 0 : TIOCM_RTS)
-                                                           : TIOCM_RTS)
-               | ((readb(&info->regs->r.pvr) & info->pvr_dtr_bit) ? 0 : TIOCM_DTR)
-               | ((readb(&info->regs->r.vstr) & SAB82532_VSTR_CD) ? 0 : TIOCM_CAR)
-               | ((readb(&info->regs->r.pvr) & info->pvr_dsr_bit) ? 0 : TIOCM_DSR)
-               | ((readb(&info->regs->r.star) & SAB82532_STAR_CTS) ? TIOCM_CTS : 0);
-       return put_user(result,value);
-}
+static struct uart_driver sunsab_reg = {
+       .owner                  = THIS_MODULE,
+       .driver_name            = "serial",
+#ifdef CONFIG_DEVFS_FS
+       .dev_name               = "tts/%d",
+#else
+       .dev_name               = "ttyS",
+#endif
+       .major                  = TTY_MAJOR,
+};
 
-static int set_modem_info(struct sab82532 * info, unsigned int cmd,
-                         unsigned int *value)
-{
-       unsigned int arg;
-
-       if (get_user(arg, value))
-               return -EFAULT;
-       switch (cmd) {
-       case TIOCMBIS: 
-               if (arg & TIOCM_RTS) {
-                       writeb(readb(&info->regs->rw.mode) & ~(SAB82532_MODE_FRTS), &info->regs->rw.mode);
-                       writeb(readb(&info->regs->rw.mode) | SAB82532_MODE_RTS, &info->regs->rw.mode);
-               }
-               if (arg & TIOCM_DTR) {
-                       writeb(readb(&info->regs->rw.pvr) & ~(info->pvr_dtr_bit), &info->regs->rw.pvr);
-               }
-               break;
-       case TIOCMBIC:
-               if (arg & TIOCM_RTS) {
-                       writeb(readb(&info->regs->rw.mode) | SAB82532_MODE_FRTS, &info->regs->rw.mode);
-                       writeb(readb(&info->regs->rw.mode) | SAB82532_MODE_RTS, &info->regs->rw.mode);
-               }
-               if (arg & TIOCM_DTR) {
-                       writeb(readb(&info->regs->rw.pvr) | info->pvr_dtr_bit, &info->regs->rw.pvr);
-               }
-               break;
-       case TIOCMSET:
-               if (arg & TIOCM_RTS) {
-                       writeb(readb(&info->regs->rw.mode) & ~(SAB82532_MODE_FRTS), &info->regs->rw.mode);
-                       writeb(readb(&info->regs->rw.mode) | SAB82532_MODE_RTS, &info->regs->rw.mode);
-               } else {
-                       writeb(readb(&info->regs->rw.mode) | SAB82532_MODE_FRTS, &info->regs->rw.mode);
-                       writeb(readb(&info->regs->rw.mode) | SAB82532_MODE_RTS, &info->regs->rw.mode);
-               }
-               if (arg & TIOCM_DTR) {
-                       writeb(readb(&info->regs->rw.pvr) & ~(info->pvr_dtr_bit), &info->regs->rw.pvr);
-               } else {
-                       writeb(readb(&info->regs->rw.pvr) | info->pvr_dtr_bit, &info->regs->rw.pvr);
-               }
-               break;
-       default:
-               return -EINVAL;
-       }
-       return 0;
-}
+static struct uart_sunsab_port *sunsab_ports;
+static int num_channels;
 
-/*
- * This routine sends a break character out the serial port.
- */
-static void sab82532_break(struct tty_struct *tty, int break_state)
+static __inline__ void sunsab_console_putchar(struct uart_sunsab_port *up, char c)
 {
-       struct sab82532 * info = (struct sab82532 *)tty->driver_data;
        unsigned long flags;
 
-       if (serial_paranoia_check(info, tty->device, "sab82532_break"))
-               return;
+       spin_lock_irqsave(&up->port.lock, flags);
 
-       if (!info->regs)
-               return;
+       sunsab_tec_wait(up);
+       writeb(c, &up->regs->w.tic);
 
-#ifdef SERIAL_DEBUG_SEND_BREAK
-       printk("sab82532_break(%d) jiff=%lu...", break_state, jiffies);
-#endif
-       save_flags(flags); cli();
-       if (break_state == -1)
-               writeb(readb(&info->regs->rw.dafo) | SAB82532_DAFO_XBRK, &info->regs->rw.dafo);
-       else
-               writeb(readb(&info->regs->rw.dafo) & ~(SAB82532_DAFO_XBRK), &info->regs->rw.dafo);
-       restore_flags(flags);
+       spin_unlock_irqrestore(&up->port.lock, flags);
 }
 
-
-static int sab82532_ioctl(struct tty_struct *tty, struct file * file,
-                   unsigned int cmd, unsigned long arg)
+static void sunsab_console_write(struct console *con, const char *s, unsigned n)
 {
-       struct sab82532 * info = (struct sab82532 *)tty->driver_data;
-       struct async_icount cprev, cnow;        /* kernel counter temps */
-       struct serial_icounter_struct *p_cuser; /* user space */
-
-       if (serial_paranoia_check(info, tty->device, "sab82532_ioctl"))
-               return -ENODEV;
+       struct uart_sunsab_port *up = &sunsab_ports[con->index];
+       int i;
 
-       if ((cmd != TIOCGSERIAL) && (cmd != TIOCSSERIAL) &&
-           (cmd != TIOCSERCONFIG) && (cmd != TIOCSERGWILD)  &&
-           (cmd != TIOCSERSWILD) && (cmd != TIOCSERGSTRUCT) &&
-           (cmd != TIOCMIWAIT) && (cmd != TIOCGICOUNT)) {
-               if (tty->flags & (1 << TTY_IO_ERROR))
-                   return -EIO;
+       for (i = 0; i < n; i++) {
+               if (*s == '\n')
+                       sunsab_console_putchar(up, '\r');
+               sunsab_console_putchar(up, *s++);
        }
-       
-       switch (cmd) {
-               case TIOCGSOFTCAR:
-                       return put_user(C_CLOCAL(tty) ? 1 : 0, (int *) arg);
-               case TIOCSSOFTCAR:
-                       if (get_user(arg, (unsigned int *) arg))
-                               return -EFAULT;
-                       tty->termios->c_cflag =
-                               ((tty->termios->c_cflag & ~CLOCAL) |
-                                (arg ? CLOCAL : 0));
-                       return 0;
-               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 TIOCSERGETLSR: /* Get line status register */
-                       return get_lsr_info(info, (unsigned int *) arg);
-
-               case TIOCSERGSTRUCT:
-                       if (copy_to_user((struct sab82532 *) arg,
-                                        info, sizeof(struct sab82532)))
-                               return -EFAULT;
-                       return 0;
-                               
-               /*
-                * 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:
-                       cli();
-                       /* note the counters on entry */
-                       cprev = info->icount;
-                       sti();
-                       while (1) {
-                               interruptible_sleep_on(&info->delta_msr_wait);
-                               /* see if a signal did it */
-                               if (signal_pending(current))
-                                       return -ERESTARTSYS;
-                               cli();
-                               cnow = info->icount; /* atomic copy */
-                               sti();
-                               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:
-                       cli();
-                       cnow = info->icount;
-                       sti();
-                       p_cuser = (struct serial_icounter_struct *) arg;
-                       if (put_user(cnow.cts, &p_cuser->cts) ||
-                           put_user(cnow.dsr, &p_cuser->dsr) ||
-                           put_user(cnow.rng, &p_cuser->rng) ||
-                           put_user(cnow.dcd, &p_cuser->dcd))
-                               return -EFAULT;
-                       return 0;
-
-               default:
-                       return -ENOIOCTLCMD;
-               }
-       return 0;
+       sunsab_tec_wait(up);
 }
 
-static void sab82532_set_termios(struct tty_struct *tty,
-                                struct termios *old_termios)
+static kdev_t sunsab_console_device(struct console *con)
 {
-       struct sab82532 *info = (struct sab82532 *)tty->driver_data;
-
-       if (   (tty->termios->c_cflag == old_termios->c_cflag)
-           && (   RELEVANT_IFLAG(tty->termios->c_iflag) 
-               == RELEVANT_IFLAG(old_termios->c_iflag)))
-         return;
-
-       change_speed(info);
-
-       /* Handle transition to B0 status */
-       if ((old_termios->c_cflag & CBAUD) &&
-           !(tty->termios->c_cflag & CBAUD)) {
-               writeb(readb(&info->regs->w.mode) | SAB82532_MODE_FRTS, &info->regs->w.mode);
-               writeb(readb(&info->regs->w.mode) | SAB82532_MODE_RTS, &info->regs->w.mode);
-               writeb(readb(&info->regs->w.pvr) | info->pvr_dtr_bit, &info->regs->w.pvr);
-       }
-       
-       /* Handle transition away from B0 status */
-       if (!(old_termios->c_cflag & CBAUD) &&
-           (tty->termios->c_cflag & CBAUD)) {
-               writeb(readb(&info->regs->w.pvr) & ~(info->pvr_dtr_bit), &info->regs->w.pvr);
-               if (tty->termios->c_cflag & CRTSCTS) {
-                       writeb(readb(&info->regs->w.mode) & ~(SAB82532_MODE_RTS), &info->regs->w.mode);
-                       writeb(readb(&info->regs->w.mode) | SAB82532_MODE_FRTS, &info->regs->w.mode);
-               } else if (test_bit(TTY_THROTTLED, &tty->flags)) {
-                       writeb(readb(&info->regs->w.mode) & ~(SAB82532_MODE_FRTS | SAB82532_MODE_RTS), &info->regs->w.mode);
-               } else {
-                       writeb(readb(&info->regs->w.mode) & ~(SAB82532_MODE_FRTS), &info->regs->w.mode);
-                       writeb(readb(&info->regs->w.mode) | SAB82532_MODE_RTS, &info->regs->w.mode);
-               }
-       }
-       
-       /* Handle turning off CRTSCTS */
-       if ((old_termios->c_cflag & CRTSCTS) &&
-           !(tty->termios->c_cflag & CRTSCTS)) {
-               tty->hw_stopped = 0;
-               sab82532_start(tty);
-       }
+       return mk_kdev(sunsab_reg.major, sunsab_reg.minor + con->index);
 }
 
-/*
- * ------------------------------------------------------------
- * sab82532_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 sab82532_close(struct tty_struct *tty, struct file * filp)
+static int sunsab_console_setup(struct console *con, char *options)
 {
-       struct sab82532 *info = (struct sab82532 *)tty->driver_data;
+       struct uart_sunsab_port *up = &sunsab_ports[con->index];
        unsigned long flags;
+       int baud;
 
-       if (!info || serial_paranoia_check(info, tty->device, "sab82532_close"))
-               return;
-
-       save_flags(flags); cli();
+       printk("Console: ttyS%d (SAB82532)\n",
+              (sunsab_reg.minor - 64) + con->index);
 
-       if (tty_hung_up_p(filp)) {
-               MOD_DEC_USE_COUNT;
-               restore_flags(flags);
-               return;
-       }
+       sunserial_console_termios(con);
 
-#ifdef SERIAL_DEBUG_OPEN
-       printk("sab82532_close ttys%d, count = %d\n", info->line, info->count);
-#endif
-       if ((tty->count == 1) && (info->count != 1)) {
-               /*
-                * Uh, oh.  tty->count is 1, which means that the tty
-                * structure will be freed.  info->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("sab82532_close: bad serial port count; tty->count is 1,"
-                      " info->count is %d\n", info->count);
-               info->count = 1;
-       }
-       if (--info->count < 0) {
-               printk("sab82532_close: bad serial port count for ttys%d: %d\n",
-                      info->line, info->count);
-               info->count = 0;
-       }
-       if (info->count) {
-               MOD_DEC_USE_COUNT;
-               restore_flags(flags);
-               return;
-       }
-       info->flags |= ASYNC_CLOSING;
-       /*
-        * Save the termios structure, since this port may have
-        * separate termios for callout and dialin.
-        */
-       if (info->flags & ASYNC_NORMAL_ACTIVE)
-               info->normal_termios = *tty->termios;
-       if (info->flags & ASYNC_CALLOUT_ACTIVE)
-               info->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.
+       /* Firmware console speed is limited to 150-->38400 baud so
+        * this hackish cflag thing is OK.
         */
-       tty->closing = 1;
-       if (info->closing_wait != ASYNC_CLOSING_WAIT_NONE)
-               tty_wait_until_sent(tty, info->closing_wait);
+       switch (con->cflag & CBAUD) {
+       case B150: baud = 150; break;
+       case B300: baud = 300; break;
+       case B600: baud = 600; break;
+       case B1200: baud = 1200; break;
+       case B2400: baud = 2400; break;
+       case B4800: baud = 4800; break;
+       default: case B9600: baud = 9600; break;
+       case B19200: baud = 19200; break;
+       case B38400: baud = 38400; break;
+       };
 
        /*
-        * At this point we stop accepting input.  To do this, we
-        * disable the receive line status interrupts, and turn off
-        * the receiver.
+        * Temporary fix.
         */
-       info->interrupt_mask0 |= SAB82532_IMR0_TCD;
-       writeb(info->interrupt_mask0, &info->regs->w.imr0);
-       if (info->flags & ASYNC_INITIALIZED) {
-               /*
-                * Before we drop DTR, make sure the UART transmitter
-                * has completely drained; this is especially
-                * important if there is a transmit FIFO!
-                */
-               sab82532_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) {
-                       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;
-       restore_flags(flags);
-}
-
-/*
- * sab82532_wait_until_sent() --- wait until the transmitter is empty
- */
-static void sab82532_wait_until_sent(struct tty_struct *tty, int timeout)
-{
-       struct sab82532 *info = (struct sab82532 *)tty->driver_data;
-       unsigned long orig_jiffies, char_time;
-
-       if (serial_paranoia_check(info,tty->device,"sab82532_wait_until_sent"))
-               return;
+       spin_lock_init(&up->port.lock);
 
        /*
-        * 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.
+        * Initialize the hardware
         */
-       char_time = (info->timeout - HZ/50) / info->xmit_fifo_size;
-       char_time = char_time / 5;
-       if (char_time == 0)
-               char_time = 1;
-       if (timeout)
-         char_time = MIN(char_time, timeout);
-#ifdef SERIAL_DEBUG_WAIT_UNTIL_SENT
-       printk("In sab82532_wait_until_sent(%d) check=%lu "
-              "xmit_cnt = %ld, alls = %d (jiff=%lu)...\n",
-              timeout, char_time,
-              CIRC_CNT(info->xmit.head, info->xmit.tail, SERIAL_XMIT_SIZE),
-              test_bit(SAB82532_ALLS, &info->irqflags), jiffies);
-#endif
-       orig_jiffies = jiffies;
-       while ((info->xmit.head != info->xmit.tail) ||
-              !test_bit(SAB82532_ALLS, &info->irqflags)) {
-               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_WAIT_UNTIL_SENT
-       printk("xmit_cnt = %ld, alls = %d (jiff=%lu)...done\n",
-              CIRC_CNT(info->xmit.head, info->xmit.tail, SERIAL_XMIT_SIZE),
-              test_bit(SAB82532_ALLS, &info->irqflags), jiffies);
-#endif
-}
-
-/*
- * sab82532_hangup() --- called by tty_hangup() when a hangup is signaled.
- */
-static void sab82532_hangup(struct tty_struct *tty)
-{
-       struct sab82532 * info = (struct sab82532 *)tty->driver_data;
-
-       if (serial_paranoia_check(info, tty->device, "sab82532_hangup"))
-               return;
-
-#ifdef CONFIG_SERIAL_CONSOLE
-       if (info->is_console)
-               return;
-#endif
-
-       sab82532_flush_buffer(tty);
-       shutdown(info);
-       info->event = 0;
-       info->count = 0;
-       info->flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CALLOUT_ACTIVE);
-       info->tty = 0;
-       wake_up_interruptible(&info->open_wait);
-}
+       sunsab_startup(&up->port);
 
-/*
- * ------------------------------------------------------------
- * sab82532_open() and friends
- * ------------------------------------------------------------
- */
-static int block_til_ready(struct tty_struct *tty, struct file * filp,
-                          struct sab82532 *info)
-{
-       DECLARE_WAITQUEUE(wait, current);
-       int retval;
-       int do_clocal = 0;
+       spin_lock_irqsave(&up->port.lock, flags);
 
        /*
-        * If the device is in the middle of being closed, then block
-        * until it's done, and then try again.
+        * Finally, enable interrupts
         */
-       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
-               if (info->flags & ASYNC_HUP_NOTIFY)
-                       return -EAGAIN;
-               else
-                       return -ERESTARTSYS;
-#else
-               return -EAGAIN;
-#endif
-       }
+       up->interrupt_mask0 = SAB82532_IMR0_PERR | SAB82532_IMR0_FERR |
+                               SAB82532_IMR0_PLLA | SAB82532_IMR0_CDSC;
+       writeb(up->interrupt_mask0, &up->regs->w.imr0);
+       up->interrupt_mask1 = SAB82532_IMR1_BRKT | SAB82532_IMR1_ALLS |
+                               SAB82532_IMR1_XOFF | SAB82532_IMR1_TIN |
+                               SAB82532_IMR1_CSC | SAB82532_IMR1_XON |
+                               SAB82532_IMR1_XPR;
+       writeb(up->interrupt_mask1, &up->regs->w.imr1);
 
-       /*
-        * 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;
-       }
+       sunsab_convert_to_sab(up, con->cflag, 0, baud);
 
-       if (info->flags & ASYNC_CALLOUT_ACTIVE) {
-               if (info->normal_termios.c_cflag & CLOCAL)
-                       do_clocal = 1;
-       } else {
-               if (tty->termios->c_cflag & CLOCAL)
-                       do_clocal = 1;
-       }
+       spin_unlock_irqrestore(&up->port.lock, flags);
        
-       /*
-        * 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, info->count is dropped by one, so that
-        * sab82532_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",
-              info->line, info->count);
-#endif
-       cli();
-       if (!tty_hung_up_p(filp)) 
-               info->count--;
-       sti();
-       info->blocked_open++;
-       while (1) {
-               cli();
-               if (!(info->flags & ASYNC_CALLOUT_ACTIVE) &&
-                   (tty->termios->c_cflag & CBAUD)) {
-                       writeb(readb(&info->regs->rw.pvr) & ~(info->pvr_dtr_bit), &info->regs->rw.pvr);
-                       writeb(readb(&info->regs->rw.mode) | SAB82532_MODE_FRTS, &info->regs->rw.mode);
-                       writeb(readb(&info->regs->rw.mode) & ~(SAB82532_MODE_RTS), &info->regs->rw.mode);
-               }
-               sti();
-               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 || !(readb(&info->regs->r.vstr) & SAB82532_VSTR_CD)))
-                       break;
-               if (signal_pending(current)) {
-                       retval = -ERESTARTSYS;
-                       break;
-               }
-#ifdef SERIAL_DEBUG_OPEN
-               printk("block_til_ready blocking: ttyS%d, count = %d, flags = %x, clocal = %d, vstr = %02x\n",
-                      info->line, info->count, info->flags, do_clocal, readb(&info->regs->r.vstr));
-#endif
-               schedule();
-       }
-       current->state = TASK_RUNNING;
-       remove_wait_queue(&info->open_wait, &wait);
-       if (!tty_hung_up_p(filp))
-               info->count++;
-       info->blocked_open--;
-#ifdef SERIAL_DEBUG_OPEN
-       printk("block_til_ready after blocking: ttys%d, count = %d\n",
-              info->line, info->count);
-#endif
-       if (retval)
-               return retval;
-       info->flags |= ASYNC_NORMAL_ACTIVE;
-       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 sab82532_open(struct tty_struct *tty, struct file * filp)
-{
-       struct sab82532 *info = sab82532_chain;
-       int retval, line;
-       unsigned long page;
-
-#ifdef SERIAL_DEBUG_OPEN
-       printk("sab82532_open: count = %d\n", info->count);
-#endif
-
-       line = minor(tty->device) - tty->driver.minor_start;
-       if ((line < 0) || (line >= NR_PORTS))
-               return -ENODEV;
-
-       while (info) {
-               if (info->line == line)
-                       break;
-               info = info->next;
-       }
-       if (!info) {
-               printk("sab82532_open: can't find info for line %d\n",
-                      line);
-               return -ENODEV;
-       }
-
-       if (serial_paranoia_check(info, tty->device, "sab82532_open"))
-               return -ENODEV;
-
-#ifdef SERIAL_DEBUG_OPEN
-       printk("sab82532_open %s%d, count = %d\n", tty->driver.name, info->line,
-              info->count);
-#endif
-
-       if (!tmp_buf) {
-               page = get_free_page(GFP_KERNEL);
-               if (!page)
-                       return -ENOMEM;
-               if (tmp_buf)
-                       free_page(page);
-               else
-                       tmp_buf = (unsigned char *) page;
-       }
-
-       info->count++;
-       tty->driver_data = info;
-       info->tty = tty;
-
-       /*
-        * If the port is in 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);
-#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)
-               return retval;
-
-       MOD_INC_USE_COUNT;
-       retval = block_til_ready(tty, filp, info);
-       if (retval) {
-#ifdef SERIAL_DEBUG_OPEN
-               printk("sab82532_open returning after block_til_ready with %d\n",
-                      retval);
-#endif
-               return retval;
-       }
-
-       if ((info->count == 1) &&
-           (info->flags & ASYNC_SPLIT_TERMIOS)) {
-               if (tty->driver.subtype == SERIAL_TYPE_NORMAL)
-                       *tty->termios = info->normal_termios;
-               else 
-                       *tty->termios = info->callout_termios;
-               change_speed(info);
-       }
-
-#ifdef CONFIG_SERIAL_CONSOLE
-       if (sab82532_console.cflag && sab82532_console.index == line) {
-               tty->termios->c_cflag = sab82532_console.cflag;
-               sab82532_console.cflag = 0;
-               change_speed(info);
-       }
-#endif
-
-       info->session = current->session;
-       info->pgrp = current->pgrp;
-
-#ifdef SERIAL_DEBUG_OPEN
-       printk("sab82532_open ttys%d successful... count %d", info->line, info->count);
-#endif
        return 0;
 }
 
-/*
- * /proc fs routines....
- */
+static struct console sunsab_console = {
+       .name   =       "ttyS",
+       .write  =       sunsab_console_write,
+       .device =       sunsab_console_device,
+       .setup  =       sunsab_console_setup,
+       .flags  =       CON_PRINTBUFFER,
+       .index  =       -1,
+};
 
-static __inline__ int
-line_info(char *buf, struct sab82532 *info)
+static void __init sunsab_console_init(void)
 {
-       unsigned long flags;
-       char stat_buf[30];
-       int ret;
+       int i;
 
-       ret = sprintf(buf, "%u: uart:SAB82532 ", info->line);
-       switch (info->type) {
-               case 0:
-                       ret += sprintf(buf+ret, "V1.0 ");
-                       break;
-               case 1:
-                       ret += sprintf(buf+ret, "V2.0 ");
-                       break;
-               case 2:
-                       ret += sprintf(buf+ret, "V3.2 ");
-                       break;
-               default:
-                       ret += sprintf(buf+ret, "V?.? ");
-                       break;
-       }
-       ret += sprintf(buf+ret, "port:%lX irq:%s",
-                      (unsigned long)info->regs, __irq_itoa(info->irq));
+       if (con_is_present())
+               return;
 
-       if (!info->regs) {
-               ret += sprintf(buf+ret, "\n");
-               return ret;
-       }
+       for (i = 0; i < num_channels; i++) {
+               int this_minor = sunsab_reg.minor + i;
 
-       /*
-        * Figure out the current RS-232 lines
-        */
-       stat_buf[0] = 0;
-       stat_buf[1] = 0;
-       save_flags(flags); cli();
-       if (readb(&info->regs->r.mode) & SAB82532_MODE_RTS) {
-               if (!(readb(&info->regs->r.mode) & SAB82532_MODE_FRTS))
-                       strcat(stat_buf, "|RTS");
-       } else {
-               strcat(stat_buf, "|RTS");
+               if ((this_minor - 64) == (serial_console - 1))
+                       break;
        }
-       if (readb(&info->regs->r.star) & SAB82532_STAR_CTS)
-               strcat(stat_buf, "|CTS");
-       if (!(readb(&info->regs->r.pvr) & info->pvr_dtr_bit))
-               strcat(stat_buf, "|DTR");
-       if (!(readb(&info->regs->r.pvr) & info->pvr_dsr_bit))
-               strcat(stat_buf, "|DSR");
-       if (!(readb(&info->regs->r.vstr) & SAB82532_VSTR_CD))
-               strcat(stat_buf, "|CD");
-       restore_flags(flags);
-
-       if (info->baud)
-               ret += sprintf(buf+ret, " baud:%u", info->baud);
-
-       ret += sprintf(buf+ret, " tx:%u rx:%u",
-                      info->icount.tx, info->icount.rx);
-
-       if (info->icount.frame)
-               ret += sprintf(buf+ret, " fe:%u", info->icount.frame);
-
-       if (info->icount.parity)
-               ret += sprintf(buf+ret, " pe:%u", info->icount.parity);
-
-       if (info->icount.brk)
-               ret += sprintf(buf+ret, " brk:%u", info->icount.brk);
-
-       if (info->icount.overrun)
-               ret += sprintf(buf+ret, " oe:%u", info->icount.overrun);
-
-       /*
-        * Last thing is the RS-232 status lines.
-        */
-       ret += sprintf(buf+ret, " %s\n", stat_buf + 1);
-       return ret;
-}
+       if (i == num_channels)
+               return;
 
-int sab82532_read_proc(char *page, char **start, off_t off, int count,
-                      int *eof, void *data)
-{
-       struct sab82532 *info = sab82532_chain;
-       off_t begin = 0;
-       int len = 0;
-
-       len += sprintf(page, "serinfo:1.0 driver:%s\n", serial_version);
-       for (info = sab82532_chain; info && len < 4000; info = info->next) {
-               len += line_info(page + len, info);
-               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);
+       sunsab_console.index = i;
+       register_console(&sunsab_console);
 }
 
-/*
- * ---------------------------------------------------------------------
- * sab82532_init() and friends
- *
- * sab82532_init() is called at boot-time to initialize the serial driver.
- * ---------------------------------------------------------------------
- */
-static int __init get_sab82532(unsigned long *memory_start)
+static void __init for_each_sab_edev(void (*callback)(struct linux_ebus_device *, void *), void *arg)
 {
        struct linux_ebus *ebus;
-       struct linux_ebus_device *edev = 0;
-       struct sab82532 *sab;
-       unsigned long regs, offset;
-       int i;
+       struct linux_ebus_device *edev = NULL;
 
        for_each_ebus(ebus) {
                for_each_ebusdev(edev, ebus) {
-                       if (!strcmp(edev->prom_name, "se"))
-                               goto ebus_done;
-
-                       if (!strcmp(edev->prom_name, "serial")) {
+                       if (!strcmp(edev->prom_name, "se")) {
+                               callback(edev, arg);
+                               continue;
+                       } else if (!strcmp(edev->prom_name, "serial")) {
                                char compat[32];
                                int clen;
 
@@ -2136,481 +988,183 @@ static int __init get_sab82532(unsigned long *memory_start)
                                                        compat, sizeof(compat));
                                if (clen > 0) {
                                        if (strncmp(compat, "sab82532", 8) == 0) {
-                                               /* Yep. */
-                                               goto ebus_done;
+                                               callback(edev, arg);
+                                               continue;
                                        }
                                }
                        }
                }
        }
-ebus_done:
-       if (!edev)
-               return -ENODEV;
+}
+
+static void __init sab_count_callback(struct linux_ebus_device *edev, void *arg)
+{
+       int *count_p = arg;
+
+       (*count_p)++;
+}
+
+static void __init sab_attach_callback(struct linux_ebus_device *edev, void *arg)
+{
+       int *instance_p = arg;
+       struct uart_sunsab_port *up;
+       unsigned long regs, offset;
+       int i;
 
        regs = edev->resource[0].start;
        offset = sizeof(union sab82532_async_regs);
-
        for (i = 0; i < 2; i++) {
-               if (memory_start) {
-                       *memory_start = (*memory_start + 7) & ~(7);
-                       sab = (struct sab82532 *)*memory_start;
-                       *memory_start += sizeof(struct sab82532);
-               } else {
-                       sab = (struct sab82532 *)kmalloc(sizeof(struct sab82532),
-                                                        GFP_KERNEL);
-                       if (!sab) {
-                               printk("sab82532: can't alloc sab struct\n");
-                               break;
-                       }
-               }
-               memset(sab, 0, sizeof(struct sab82532));
-
-               sab->regs = ioremap(regs + offset, sizeof(union sab82532_async_regs));
-               sab->irq = edev->irqs[0];
-               sab->line = 1 - i;
-               sab->xmit_fifo_size = 32;
-               sab->recv_fifo_size = 32;
+               up = &sunsab_ports[(*instance_p * 2) + i];
 
-               writeb(SAB82532_IPC_IC_ACT_LOW, &sab->regs->w.ipc);
+               memset(up, 0, sizeof(*up));
+               up->regs = ioremap(regs + offset, sizeof(union sab82532_async_regs));
+               up->irq = edev->irqs[0];
+               up->sab_line = 1 - i;
+               up->xmit_fifo_size = 32;
+               up->recv_fifo_size = 32;
 
-               sab->next = sab82532_chain;
-               sab82532_chain = sab;
+               writeb(SAB82532_IPC_IC_ACT_LOW, &up->regs->w.ipc);
 
                offset -= sizeof(union sab82532_async_regs);
        }
-       return 0;
 }
 
-#ifndef MODULE
-static void __init sab82532_kgdb_hook(int line)
+static int __init probe_for_sabs(void)
 {
-       prom_printf("sab82532: kgdb support is not implemented, yet\n");
-       prom_halt();
-}
-#endif
+       int this_sab = 0;
 
-static inline void __init show_serial_version(void)
-{
-       char *revision = "$Revision: 1.66 $";
-       char *version, *p;
-
-       version = strchr(revision, ' ');
-       strcpy(serial_version, ++version);
-       p = strchr(serial_version, ' ');
-       *p = '\0';
-       printk("SAB82532 serial driver version %s\n", serial_version);
-}
+       /* Find device instances.  */
+       for_each_sab_edev(&sab_count_callback, &this_sab);
+       if (!this_sab)
+               return -ENODEV;
 
-extern int su_num_ports;
+       /* Allocate tables.  */
+       sunsab_ports = kmalloc(sizeof(struct uart_sunsab_port) * this_sab * 2,
+                              GFP_KERNEL);
+       if (!sunsab_ports)
+               return -ENOMEM;
 
-/*
- * The serial driver boot-time initialization code!
- */
-int __init sab82532_init(void)
-{
-       struct sab82532 *info;
-       int i;
+       num_channels = this_sab * 2;
 
-       if (!sab82532_chain)
-               get_sab82532(0);
-       if (!sab82532_chain)
-               return -ENODEV;
+       this_sab = 0;
+       for_each_sab_edev(&sab_attach_callback, &this_sab);
+       return 0;
+}
 
-       init_bh(SERIAL_BH, do_serial_bh);
+static void __init sunsab_init_hw(void)
+{
+       int i;
 
-       show_serial_version();
+       for (i = 0; i < num_channels; i++) {
+               struct uart_sunsab_port *up = &sunsab_ports[i];
 
-       /* Initialize the tty_driver structure */
-       memset(&serial_driver, 0, sizeof(struct tty_driver));
-       serial_driver.magic = TTY_DRIVER_MAGIC;
-       serial_driver.driver_name = "serial";
-#ifdef CONFIG_DEVFS_FS
-       serial_driver.name = "tts/%d";
-#else
-       serial_driver.name = "ttyS";
-#endif
-       serial_driver.major = TTY_MAJOR;
-       serial_driver.minor_start = 64 + su_num_ports;
-       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;
-       serial_driver.refcount = &sab82532_refcount;
-       serial_driver.table = sab82532_table;
-       serial_driver.termios = sab82532_termios;
-       serial_driver.termios_locked = sab82532_termios_locked;
-
-       serial_driver.open = sab82532_open;
-       serial_driver.close = sab82532_close;
-       serial_driver.write = sab82532_write;
-       serial_driver.put_char = sab82532_put_char;
-       serial_driver.flush_chars = sab82532_flush_chars;
-       serial_driver.write_room = sab82532_write_room;
-       serial_driver.chars_in_buffer = sab82532_chars_in_buffer;
-       serial_driver.flush_buffer = sab82532_flush_buffer;
-       serial_driver.ioctl = sab82532_ioctl;
-       serial_driver.throttle = sab82532_throttle;
-       serial_driver.unthrottle = sab82532_unthrottle;
-       serial_driver.send_xchar = sab82532_send_xchar;
-       serial_driver.set_termios = sab82532_set_termios;
-       serial_driver.stop = sab82532_stop;
-       serial_driver.start = sab82532_start;
-       serial_driver.hangup = sab82532_hangup;
-       serial_driver.break_ctl = sab82532_break;
-       serial_driver.wait_until_sent = sab82532_wait_until_sent;
-       serial_driver.read_proc = sab82532_read_proc;
+               up->port.line = i;
+               up->port.ops = &sunsab_pops;
+               up->port.type = PORT_SUNSAB;
+               up->port.uartclk = SAB_BASE_BAUD;
 
-       /*
-        * The callout device is just like normal device except for
-        * major number and the subtype code.
-        */
-       callout_driver = serial_driver;
-#ifdef 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;
-       callout_driver.read_proc = 0;
-       callout_driver.proc_entry = 0;
-
-       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 (info = sab82532_chain, i = 0; info; info = info->next, i++) {
-               info->magic = SERIAL_MAGIC;
-
-               info->type = readb(&info->regs->r.vstr) & 0x0f;
-               writeb(~((1 << 1) | (1 << 2) | (1 << 4)), &info->regs->w.pcr);
-               writeb(0xff, &info->regs->w.pim);
-               if (info->line == 0) {
-                       info->pvr_dsr_bit = (1 << 0);
-                       info->pvr_dtr_bit = (1 << 1);
+               up->type = readb(&up->regs->r.vstr) & 0x0f;
+               writeb(~((1 << 1) | (1 << 2) | (1 << 4)), &up->regs->w.pcr);
+               writeb(0xff, &up->regs->w.pim);
+               if (up->sab_line == 0) {
+                       up->pvr_dsr_bit = (1 << 0);
+                       up->pvr_dtr_bit = (1 << 1);
                } else {
-                       info->pvr_dsr_bit = (1 << 3);
-                       info->pvr_dtr_bit = (1 << 2);
+                       up->pvr_dsr_bit = (1 << 3);
+                       up->pvr_dtr_bit = (1 << 2);
                }
-               writeb((1 << 1) | (1 << 2) | (1 << 4), &info->regs->w.pvr);
-               writeb(readb(&info->regs->rw.mode) | SAB82532_MODE_FRTS, &info->regs->rw.mode);
-               writeb(readb(&info->regs->rw.mode) | SAB82532_MODE_RTS, &info->regs->rw.mode);
-
-               info->custom_divisor = 16;
-               info->close_delay = 5*HZ/10;
-               info->closing_wait = 30*HZ;
-               info->tec_timeout = SAB82532_MAX_TEC_TIMEOUT;
-               info->cec_timeout = SAB82532_MAX_CEC_TIMEOUT;
-               info->x_char = 0;
-               info->event = 0;        
-               info->blocked_open = 0;
-               info->tqueue.routine = do_softint;
-               info->tqueue.data = info;
-               info->tqueue_hangup.routine = do_serial_hangup;
-               info->tqueue_hangup.data = info;
-               info->callout_termios = callout_driver.init_termios;
-               info->normal_termios = serial_driver.init_termios;
-               init_waitqueue_head(&info->open_wait);
-               init_waitqueue_head(&info->close_wait);
-               init_waitqueue_head(&info->delta_msr_wait);
-               info->icount.cts = info->icount.dsr = 
-                       info->icount.rng = info->icount.dcd = 0;
-               info->icount.rx = info->icount.tx = 0;
-               info->icount.frame = info->icount.parity = 0;
-               info->icount.overrun = info->icount.brk = 0;
-
-               if (!(info->line & 0x01)) {
-                       if (request_irq(info->irq, sab82532_interrupt, SA_SHIRQ,
-                                       "serial(sab82532)", info)) {
-                               printk("sab82532: can't get IRQ %x\n",
-                                      info->irq);
-                               panic("sab82532 initialization failed");
+               writeb((1 << 1) | (1 << 2) | (1 << 4), &up->regs->w.pvr);
+               writeb(readb(&up->regs->rw.mode) | SAB82532_MODE_FRTS,
+                      &up->regs->rw.mode);
+               writeb(readb(&up->regs->rw.mode) | SAB82532_MODE_RTS,
+                      &up->regs->rw.mode);
+
+               up->tec_timeout = SAB82532_MAX_TEC_TIMEOUT;
+               up->cec_timeout = SAB82532_MAX_CEC_TIMEOUT;
+
+               if (!(up->sab_line & 0x01)) {
+                       if (request_irq(up->irq, sunsab_interrupt, SA_SHIRQ,
+                                       "serial(sab82532)", up)) {
+                               printk("sunsab%d: can't get IRQ %x\n",
+                                      i, up->irq);
+                               continue;
                        }
                }
        
                printk(KERN_INFO
-                      "ttyS%02d at 0x%lx (irq = %s) is a SAB82532 %s\n",
-                      info->line + su_num_ports, (unsigned long)info->regs,
-                      __irq_itoa(info->irq), sab82532_version[info->type]);
+                      "sunsab%d at 0x%lx (irq = %s) is a SAB82532 %s\n",
+                      i, (unsigned long)up->regs,
+                      __irq_itoa(up->irq), sab82532_version[up->type]);
        }
-
-#ifdef SERIAL_LOG_DEVICE
-       dprint_init(SERIAL_LOG_DEVICE);
-#endif
-       return 0;
 }
 
-int __init sab82532_probe(void)
+static int __init sunsab_init(void)
 {
-       int node, enode, snode;
-       char model[32];
-       int len;
+       int ret = probe_for_sabs();
+       int i;
 
-        node = prom_getchild(prom_root_node);
-       node = prom_searchsiblings(node, "pci");
+       if (ret < 0)
+               return ret;
 
-       /*
-        * Check for SUNW,sabre on Ultra 5/10/AXi.
-        */
-       len = prom_getproperty(node, "model", model, sizeof(model));
-       if ((len > 0) && !strncmp(model, "SUNW,sabre", len)) {
-               node = prom_getchild(node);
-               node = prom_searchsiblings(node, "pci");
-       }
+       sunsab_init_hw();
 
-       /*
-        * For each PCI bus...
-        */
-       while (node) {
-               enode = prom_getchild(node);
-               enode = prom_searchsiblings(enode, "ebus");
+       sunsab_reg.minor = sunserial_current_minor;
+       sunserial_current_minor += num_channels;
 
-               /*
-                * For each EBus on this PCI...
-                */
-               while (enode) {
-                       int child;
+       sunsab_reg.nr = num_channels;
+       sunsab_reg.cons = &sunsab_console;
 
-                       child = prom_getchild(enode);
-                       snode = prom_searchsiblings(child, "se");
-                       if (snode)
-                               goto found;
+       ret = uart_register_driver(&sunsab_reg);
+       if (ret < 0) {
+               int i;
 
-                       snode = prom_searchsiblings(child, "serial");
-                       if (snode) {
-                               char compat[32];
-                               int clen;
-
-                               clen = prom_getproperty(snode, "compatible",
-                                                       compat, sizeof(compat));
-                               if (clen > 0) {
-                                       if (strncmp(compat, "sab82532", 8) == 0)
-                                               goto found;
-                               }
-                       }
+               for (i = 0; i < num_channels; i++) {
+                       struct uart_sunsab_port *up = &sunsab_ports[i];
 
-                       enode = prom_getsibling(enode);
-                       enode = prom_searchsiblings(enode, "ebus");
+                       if (!(up->sab_line & 0x01))
+                               free_irq(up->irq, up);
+                       iounmap(up->regs);
                }
-               node = prom_getsibling(node);
-               node = prom_searchsiblings(node, "pci");
-       }
-       return -ENODEV;
-
-found:
-#ifdef CONFIG_SERIAL_CONSOLE
-       sunserial_setinitfunc(sab82532_console_init);
-#endif
-#ifndef MODULE
-       sunserial_setinitfunc(sab82532_init);
-       rs_ops.rs_kgdb_hook = sab82532_kgdb_hook;
-#endif
-       return 0;
-}
-
-#ifdef MODULE
-MODULE_LICENSE("GPL");
-
-int init_module(void)
-{
-       if (get_sab82532(0))
-               return -ENODEV;
-
-       return sab82532_init();
-}
-
-void cleanup_module(void) 
-{
-       struct sab82532 *sab;
-       unsigned long flags;
-       int e1, e2;
-
-       /* printk("Unloading %s: version %s\n", serial_name, serial_version); */
-       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);
-
-       if (tmp_buf) {
-               free_page((unsigned long) tmp_buf);
-               tmp_buf = NULL;
-       }
-       for (sab = sab82532_chain; sab; sab = sab->next) {
-               if (!(sab->line & 0x01))
-                       free_irq(sab->irq, sab);
-               iounmap(sab->regs);
-       }
-}
-#endif /* MODULE */
-
-#ifdef CONFIG_SERIAL_CONSOLE
-static __inline__ void
-sab82532_console_putchar(struct sab82532 *info, char c)
-{
-       unsigned long flags;
-
-       save_flags(flags); cli();
-       sab82532_tec_wait(info);
-       writeb(c, &info->regs->w.tic);
-       restore_flags(flags);
-}
+               kfree(sunsab_ports);
+               sunsab_ports = NULL;
 
-static void
-sab82532_console_write(struct console *con, const char *s, unsigned n)
-{
-       struct sab82532 *info;
-       int i;
-
-       info = sab82532_chain;
-       for (i = con->index; i; i--) {
-               info = info->next;
-               if (!info)
-                       return;
-       }
-
-       for (i = 0; i < n; i++) {
-               if (*s == '\n')
-                       sab82532_console_putchar(info, '\r');
-               sab82532_console_putchar(info, *s++);
-       }
-       sab82532_tec_wait(info);
-}
-
-static kdev_t
-sab82532_console_device(struct console *con)
-{
-       return mk_kdev(TTY_MAJOR, 64 + con->index);
-}
-
-static int
-sab82532_console_setup(struct console *con, char *options)
-{
-       static struct tty_struct c_tty;
-       static struct termios c_termios;
-       struct sab82532 *info;
-       tcflag_t        cflag;
-       int             i;
-
-       info = sab82532_chain;
-       for (i = con->index; i; i--) {
-               info = info->next;
-               if (!info)
-                       return -ENODEV;
+               return ret;
        }
-       info->is_console = 1;
-
-       /*
-        * Initialize the hardware
-        */
-       sab82532_init_line(info);
-
-       /*
-        * Finally, enable interrupts
-        */
-       info->interrupt_mask0 = SAB82532_IMR0_PERR | SAB82532_IMR0_FERR |
-                               SAB82532_IMR0_PLLA | SAB82532_IMR0_CDSC;
-       writeb(info->interrupt_mask0, &info->regs->w.imr0);
-       info->interrupt_mask1 = SAB82532_IMR1_BRKT | SAB82532_IMR1_ALLS |
-                               SAB82532_IMR1_XOFF | SAB82532_IMR1_TIN |
-                               SAB82532_IMR1_CSC | SAB82532_IMR1_XON |
-                               SAB82532_IMR1_XPR;
-       writeb(info->interrupt_mask1, &info->regs->w.imr1);
-
-       printk("Console: ttyS%d (SAB82532)\n", info->line);
 
-       sunserial_console_termios(con);
-       cflag = con->cflag;
+       for (i = 0; i < num_channels; i++) {
+               struct uart_sunsab_port *up = &sunsab_ports[i];
 
-       /*
-        * Fake up the tty and tty->termios structures so we can use
-        * change_speed (and eliminate a lot of duplicate code).
-        */
-       if (!info->tty) {
-               memset(&c_tty, 0, sizeof(c_tty));
-               info->tty = &c_tty;
-       }
-       if (!info->tty->termios) {
-               memset(&c_termios, 0, sizeof(c_termios));
-               info->tty->termios = &c_termios;
+               uart_add_one_port(&sunsab_reg, &up->port);
        }
-       info->tty->termios->c_cflag = con->cflag;
 
-       change_speed(info);
+       sunsab_console_init();
 
-       /* Now take out the pointers to static structures if necessary */
-       if (info->tty->termios == &c_termios)
-               info->tty->termios = 0;
-       if (info->tty == &c_tty)
-               info->tty = 0;
-       
        return 0;
 }
 
-static struct console sab82532_console = {
-       .name =         "ttyS",
-       .write =        sab82532_console_write,
-       .device =       sab82532_console_device,
-       .setup =        sab82532_console_setup,
-       .flags =        CON_PRINTBUFFER,
-       .index =        -1,
-};
-
-int __init sab82532_console_init(void)
+static void __exit sunsab_exit(void)
 {
-       extern int su_console_registered;
-
-       if (con_is_present() || su_console_registered)
-               return 0;
+       int i;
 
-       if (!sab82532_chain) {
-               prom_printf("sab82532_console_setup: can't get SAB82532 chain");
-               prom_halt();
-       }
+       for (i = 0; i < num_channels; i++) {
+               struct uart_sunsab_port *up = &sunsab_ports[i];
 
-       sab82532_console.index = serial_console - 1;
-       register_console(&sab82532_console);
-       return 0;
-}
+               uart_remove_one_port(&sunsab_reg, &up->port);
 
-#ifdef SERIAL_LOG_DEVICE
+               if (!(up->sab_line & 0x01))
+                       free_irq(up->irq, up);
+               iounmap(up->regs);
+       }
 
-static int serial_log_device = 0;
+       uart_unregister_driver(&sunsab_reg);
 
-static void
-dprint_init(int tty)
-{
-       serial_console = tty + 1;
-       sab82532_console.index = tty;
-       sab82532_console_setup(&sab82532_console, "");
-       serial_console = 0;
-       serial_log_device = tty + 1;
+       kfree(sunsab_ports);
+       sunsab_ports = NULL;
 }
 
-int
-dprintf(const char *fmt, ...)
-{
-       static char buffer[4096];
-       va_list args;
-       int i;
+module_init(sunsab_init);
+module_exit(sunsab_exit);
 
-       if (!serial_log_device)
-               return 0;
-
-       va_start(args, fmt);
-       i = vsprintf(buffer, fmt, args);
-       va_end(args);
-       sab82532_console.write(&sab82532_console, buffer, i);
-       return i;
-}
-#endif /* SERIAL_LOG_DEVICE */
-#endif /* CONFIG_SERIAL_CONSOLE */
+MODULE_AUTHOR("Eddie C. Dost and David S. Miller");
+MODULE_DESCRIPTION("Sun SAB82532 serial port driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/serial/sunsab.h b/drivers/serial/sunsab.h
new file mode 100644 (file)
index 0000000..686086f
--- /dev/null
@@ -0,0 +1,321 @@
+/* sunsab.h: Register Definitions for the Siemens SAB82532 DUSCC
+ *
+ * Copyright (C) 1997  Eddie C. Dost  (ecd@skynet.be)
+ */
+
+#ifndef _SUNSAB_H
+#define _SUNSAB_H
+
+struct sab82532_async_rd_regs {
+       u8      rfifo[0x20];    /* Receive FIFO                         */
+       u8      star;           /* Status Register                      */
+       u8      __pad1;
+       u8      mode;           /* Mode Register                        */
+       u8      timr;           /* Timer Register                       */
+       u8      xon;            /* XON Character                        */
+       u8      xoff;           /* XOFF Character                       */
+       u8      tcr;            /* Termination Character Register       */
+       u8      dafo;           /* Data Format                          */
+       u8      rfc;            /* RFIFO Control Register               */
+       u8      __pad2;
+       u8      rbcl;           /* Receive Byte Count Low               */
+       u8      rbch;           /* Receive Byte Count High              */
+       u8      ccr0;           /* Channel Configuration Register 0     */
+       u8      ccr1;           /* Channel Configuration Register 1     */
+       u8      ccr2;           /* Channel Configuration Register 2     */
+       u8      ccr3;           /* Channel Configuration Register 3     */
+       u8      __pad3[4];
+       u8      vstr;           /* Version Status Register              */
+       u8      __pad4[3];
+       u8      gis;            /* Global Interrupt Status              */
+       u8      ipc;            /* Interrupt Port Configuration         */
+       u8      isr0;           /* Interrupt Status 0                   */
+       u8      isr1;           /* Interrupt Status 1                   */
+       u8      pvr;            /* Port Value Register                  */
+       u8      pis;            /* Port Interrupt Status                */
+       u8      pcr;            /* Port Configuration Register          */
+       u8      ccr4;           /* Channel Configuration Register 4     */
+};
+
+struct sab82532_async_wr_regs {
+       u8      xfifo[0x20];    /* Transmit FIFO                        */
+       u8      cmdr;           /* Command Register                     */
+       u8      __pad1;
+       u8      mode;
+       u8      timr;
+       u8      xon;
+       u8      xoff;
+       u8      tcr;
+       u8      dafo;
+       u8      rfc;
+       u8      __pad2;
+       u8      xbcl;           /* Transmit Byte Count Low              */
+       u8      xbch;           /* Transmit Byte Count High             */
+       u8      ccr0;
+       u8      ccr1;
+       u8      ccr2;
+       u8      ccr3;
+       u8      tsax;           /* Time-Slot Assignment Reg. Transmit   */
+       u8      tsar;           /* Time-Slot Assignment Reg. Receive    */
+       u8      xccr;           /* Transmit Channel Capacity Register   */
+       u8      rccr;           /* Receive Channel Capacity Register    */
+       u8      bgr;            /* Baud Rate Generator Register         */
+       u8      tic;            /* Transmit Immediate Character         */
+       u8      mxn;            /* Mask XON Character                   */
+       u8      mxf;            /* Mask XOFF Character                  */
+       u8      iva;            /* Interrupt Vector Address             */
+       u8      ipc;
+       u8      imr0;           /* Interrupt Mask Register 0            */
+       u8      imr1;           /* Interrupt Mask Register 1            */
+       u8      pvr;
+       u8      pim;            /* Port Interrupt Mask                  */
+       u8      pcr;
+       u8      ccr4;
+};
+
+struct sab82532_async_rw_regs {        /* Read/Write registers                 */
+       u8      __pad1[0x20];
+       u8      __pad2;
+       u8      __pad3;
+       u8      mode;
+       u8      timr;
+       u8      xon;
+       u8      xoff;
+       u8      tcr;
+       u8      dafo;
+       u8      rfc;
+       u8      __pad4;
+       u8      __pad5;
+       u8      __pad6;
+       u8      ccr0;
+       u8      ccr1;
+       u8      ccr2;
+       u8      ccr3;
+       u8      __pad7;
+       u8      __pad8;
+       u8      __pad9;
+       u8      __pad10;
+       u8      __pad11;
+       u8      __pad12;
+       u8      __pad13;
+       u8      __pad14;
+       u8      __pad15;
+       u8      ipc;
+       u8      __pad16;
+       u8      __pad17;
+       u8      pvr;
+       u8      __pad18;
+       u8      pcr;
+       u8      ccr4;
+};
+
+union sab82532_async_regs {
+       __volatile__ struct sab82532_async_rd_regs      r;
+       __volatile__ struct sab82532_async_wr_regs      w;
+       __volatile__ struct sab82532_async_rw_regs      rw;
+};
+
+union sab82532_irq_status {
+       unsigned short                   stat;
+       struct {
+               unsigned char            isr0;
+               unsigned char            isr1;
+       } sreg;
+};
+
+/* irqflags bits */
+#define SAB82532_ALLS                  0x00000001
+#define SAB82532_XPR                   0x00000002
+
+/* RFIFO Status Byte */
+#define SAB82532_RSTAT_PE              0x80
+#define SAB82532_RSTAT_FE              0x40
+#define SAB82532_RSTAT_PARITY          0x01
+
+/* Status Register (STAR) */
+#define SAB82532_STAR_XDOV             0x80
+#define SAB82532_STAR_XFW              0x40
+#define SAB82532_STAR_RFNE             0x20
+#define SAB82532_STAR_FCS              0x10
+#define SAB82532_STAR_TEC              0x08
+#define SAB82532_STAR_CEC              0x04
+#define SAB82532_STAR_CTS              0x02
+
+/* Command Register (CMDR) */
+#define SAB82532_CMDR_RMC              0x80
+#define SAB82532_CMDR_RRES             0x40
+#define SAB82532_CMDR_RFRD             0x20
+#define SAB82532_CMDR_STI              0x10
+#define SAB82532_CMDR_XF               0x08
+#define SAB82532_CMDR_XRES             0x01
+
+/* Mode Register (MODE) */
+#define SAB82532_MODE_FRTS             0x40
+#define SAB82532_MODE_FCTS             0x20
+#define SAB82532_MODE_FLON             0x10
+#define SAB82532_MODE_RAC              0x08
+#define SAB82532_MODE_RTS              0x04
+#define SAB82532_MODE_TRS              0x02
+#define SAB82532_MODE_TLP              0x01
+
+/* Timer Register (TIMR) */
+#define SAB82532_TIMR_CNT_MASK         0xe0
+#define SAB82532_TIMR_VALUE_MASK       0x1f
+
+/* Data Format (DAFO) */
+#define SAB82532_DAFO_XBRK             0x40
+#define SAB82532_DAFO_STOP             0x20
+#define SAB82532_DAFO_PAR_SPACE                0x00
+#define SAB82532_DAFO_PAR_ODD          0x08
+#define SAB82532_DAFO_PAR_EVEN         0x10
+#define SAB82532_DAFO_PAR_MARK         0x18
+#define SAB82532_DAFO_PARE             0x04
+#define SAB82532_DAFO_CHL8             0x00
+#define SAB82532_DAFO_CHL7             0x01
+#define SAB82532_DAFO_CHL6             0x02
+#define SAB82532_DAFO_CHL5             0x03
+
+/* RFIFO Control Register (RFC) */
+#define SAB82532_RFC_DPS               0x40
+#define SAB82532_RFC_DXS               0x20
+#define SAB82532_RFC_RFDF              0x10
+#define SAB82532_RFC_RFTH_1            0x00
+#define SAB82532_RFC_RFTH_4            0x04
+#define SAB82532_RFC_RFTH_16           0x08
+#define SAB82532_RFC_RFTH_32           0x0c
+#define SAB82532_RFC_TCDE              0x01
+
+/* Received Byte Count High (RBCH) */
+#define SAB82532_RBCH_DMA              0x80
+#define SAB82532_RBCH_CAS              0x20
+
+/* Transmit Byte Count High (XBCH) */
+#define SAB82532_XBCH_DMA              0x80
+#define SAB82532_XBCH_CAS              0x20
+#define SAB82532_XBCH_XC               0x10
+
+/* Channel Configuration Register 0 (CCR0) */
+#define SAB82532_CCR0_PU               0x80
+#define SAB82532_CCR0_MCE              0x40
+#define SAB82532_CCR0_SC_NRZ           0x00
+#define SAB82532_CCR0_SC_NRZI          0x08
+#define SAB82532_CCR0_SC_FM0           0x10
+#define SAB82532_CCR0_SC_FM1           0x14
+#define SAB82532_CCR0_SC_MANCH         0x18
+#define SAB82532_CCR0_SM_HDLC          0x00
+#define SAB82532_CCR0_SM_SDLC_LOOP     0x01
+#define SAB82532_CCR0_SM_BISYNC                0x02
+#define SAB82532_CCR0_SM_ASYNC         0x03
+
+/* Channel Configuration Register 1 (CCR1) */
+#define SAB82532_CCR1_ODS              0x10
+#define SAB82532_CCR1_BCR              0x08
+#define SAB82532_CCR1_CM_MASK          0x07
+
+/* Channel Configuration Register 2 (CCR2) */
+#define SAB82532_CCR2_SOC1             0x80
+#define SAB82532_CCR2_SOC0             0x40
+#define SAB82532_CCR2_BR9              0x80
+#define SAB82532_CCR2_BR8              0x40
+#define SAB82532_CCR2_BDF              0x20
+#define SAB82532_CCR2_SSEL             0x10
+#define SAB82532_CCR2_XCS0             0x20
+#define SAB82532_CCR2_RCS0             0x10
+#define SAB82532_CCR2_TOE              0x08
+#define SAB82532_CCR2_RWX              0x04
+#define SAB82532_CCR2_DIV              0x01
+
+/* Channel Configuration Register 3 (CCR3) */
+#define SAB82532_CCR3_PSD              0x01
+
+/* Time Slot Assignment Register Transmit (TSAX) */
+#define SAB82532_TSAX_TSNX_MASK                0xfc
+#define SAB82532_TSAX_XCS2             0x02    /* see also CCR2 */
+#define SAB82532_TSAX_XCS1             0x01
+
+/* Time Slot Assignment Register Receive (TSAR) */
+#define SAB82532_TSAR_TSNR_MASK                0xfc
+#define SAB82532_TSAR_RCS2             0x02    /* see also CCR2 */
+#define SAB82532_TSAR_RCS1             0x01
+
+/* Version Status Register (VSTR) */
+#define SAB82532_VSTR_CD               0x80
+#define SAB82532_VSTR_DPLA             0x40
+#define SAB82532_VSTR_VN_MASK          0x0f
+#define SAB82532_VSTR_VN_1             0x00
+#define SAB82532_VSTR_VN_2             0x01
+#define SAB82532_VSTR_VN_3_2           0x02
+
+/* Global Interrupt Status Register (GIS) */
+#define SAB82532_GIS_PI                        0x80
+#define SAB82532_GIS_ISA1              0x08
+#define SAB82532_GIS_ISA0              0x04
+#define SAB82532_GIS_ISB1              0x02
+#define SAB82532_GIS_ISB0              0x01
+
+/* Interrupt Vector Address (IVA) */
+#define SAB82532_IVA_MASK              0xf1
+
+/* Interrupt Port Configuration (IPC) */
+#define SAB82532_IPC_VIS               0x80
+#define SAB82532_IPC_SLA1              0x10
+#define SAB82532_IPC_SLA0              0x08
+#define SAB82532_IPC_CASM              0x04
+#define SAB82532_IPC_IC_OPEN_DRAIN     0x00
+#define SAB82532_IPC_IC_ACT_LOW                0x01
+#define SAB82532_IPC_IC_ACT_HIGH       0x03
+
+/* Interrupt Status Register 0 (ISR0) */
+#define SAB82532_ISR0_TCD              0x80
+#define SAB82532_ISR0_TIME             0x40
+#define SAB82532_ISR0_PERR             0x20
+#define SAB82532_ISR0_FERR             0x10
+#define SAB82532_ISR0_PLLA             0x08
+#define SAB82532_ISR0_CDSC             0x04
+#define SAB82532_ISR0_RFO              0x02
+#define SAB82532_ISR0_RPF              0x01
+
+/* Interrupt Status Register 1 (ISR1) */
+#define SAB82532_ISR1_BRK              0x80
+#define SAB82532_ISR1_BRKT             0x40
+#define SAB82532_ISR1_ALLS             0x20
+#define SAB82532_ISR1_XOFF             0x10
+#define SAB82532_ISR1_TIN              0x08
+#define SAB82532_ISR1_CSC              0x04
+#define SAB82532_ISR1_XON              0x02
+#define SAB82532_ISR1_XPR              0x01
+
+/* Interrupt Mask Register 0 (IMR0) */
+#define SAB82532_IMR0_TCD              0x80
+#define SAB82532_IMR0_TIME             0x40
+#define SAB82532_IMR0_PERR             0x20
+#define SAB82532_IMR0_FERR             0x10
+#define SAB82532_IMR0_PLLA             0x08
+#define SAB82532_IMR0_CDSC             0x04
+#define SAB82532_IMR0_RFO              0x02
+#define SAB82532_IMR0_RPF              0x01
+
+/* Interrupt Mask Register 1 (IMR1) */
+#define SAB82532_IMR1_BRK              0x80
+#define SAB82532_IMR1_BRKT             0x40
+#define SAB82532_IMR1_ALLS             0x20
+#define SAB82532_IMR1_XOFF             0x10
+#define SAB82532_IMR1_TIN              0x08
+#define SAB82532_IMR1_CSC              0x04
+#define SAB82532_IMR1_XON              0x02
+#define SAB82532_IMR1_XPR              0x01
+
+/* Port Interrupt Status Register (PIS) */
+#define SAB82532_PIS_SYNC_B            0x08
+#define SAB82532_PIS_DTR_B             0x04
+#define SAB82532_PIS_DTR_A             0x02
+#define SAB82532_PIS_SYNC_A            0x01
+
+/* Channel Configuration Register 4 (CCR4) */
+#define SAB82532_CCR4_MCK4             0x80
+#define SAB82532_CCR4_EBRG             0x40
+#define SAB82532_CCR4_TST1             0x20
+#define SAB82532_CCR4_ICD              0x10
+
+
+#endif /* !(_SUNSAB_H) */
index 7f252737444157ad953103067506f485f59ca3ef..b7c46004d06b4a7d2d6b5afd2ffdd40cb4c8a9e1 100644 (file)
@@ -1362,7 +1362,7 @@ static void sunsu_console_write(struct console *co, const char *s,
 
 static kdev_t sunsu_console_device(struct console *co)
 {
-       return mk_kdev(TTY_MAJOR, 64 + co->index);
+       return mk_kdev(sunsu_reg.major, sunsu_reg.minor + co->index);
 }
 
 /*
@@ -1379,6 +1379,9 @@ static int __init sunsu_console_setup(struct console *co, char *options)
        int parity = 'n';
        int flow = 'n';
 
+       printk("Console: ttyS%d (SU)\n",
+              (sunsu_reg.minor - 64) + co->index);
+
        /*
         * Check whether an invalid uart number has been specified, and
         * if so, search for the first available port that does have
@@ -1414,15 +1417,23 @@ static struct console sunsu_cons = {
 
 static int __init sunsu_serial_console_init(void)
 {
-       int index;
+       int i;
 
        if (con_is_present())
                return 0;
 
-       index = serial_console - 1;
-       if (sunsu_ports[index].port_node == 0)
+       for (i = 0; i < UART_NR; i++) {
+               int this_minor = sunsu_reg.minor + i;
+
+               if ((this_minor - 64) == (serial_console - 1))
+                       break;
+       }
+       if (i == UART_NR)
                return 0;
-       sunsu_cons.index = index;
+       if (sunsu_ports[i].port_node == 0)
+               return 0;
+
+       sunsu_cons.index = i;
        register_console(&sunsu_cons);
        return 0;
 }
index 19133aaea388b1f076ed0805cabf00778a97a2ae..f2c96c6768426744a778ad80479a5764a4c095b9 100644 (file)
@@ -1013,7 +1013,6 @@ static struct uart_driver sunzilog_reg = {
        .dev_name       =       "ttyS",
 #endif
        .major          =       TTY_MAJOR,
-       .minor          =       64,
 };
 
 static void * __init alloc_one_table(unsigned long size)
@@ -1386,7 +1385,7 @@ sunzilog_console_write(struct console *con, const char *s, unsigned int count)
 
 static kdev_t sunzilog_console_device(struct console *con)
 {
-       return mk_kdev(TTY_MAJOR, 64 + con->index);
+       return mk_kdev(sunzilog_reg.major, sunzilog_reg.minor + con->index);
 }
 
 static int __init sunzilog_console_setup(struct console *con, char *options)
@@ -1395,7 +1394,8 @@ static int __init sunzilog_console_setup(struct console *con, char *options)
        unsigned long flags;
        int baud, brg;
 
-       printk("Console: ttyS%d (Zilog8530)\n", con->index / 2);
+       printk("Console: ttyS%d (Zilog8530)\n",
+              (sunzilog_reg.minor - 64) + con->index);
 
        /* Get firmware console settings.  */
        sunserial_console_termios(con);
@@ -1447,10 +1447,21 @@ static struct console sunzilog_console = {
 
 static int __init sunzilog_console_init(void)
 {
+       int i;
+
        if (con_is_present())
                return 0;
 
-       sunzilog_console.index = serial_console - 1;
+       for (i = 0; i < NUM_CHANNELS; i++) {
+               int this_minor = sunzilog_reg.minor + i;
+
+               if ((this_minor - 64) == (serial_console - 1))
+                       break;
+       }
+       if (i == NUM_CHANNELS)
+               return 0;
+
+       sunzilog_console.index = i;
        register_console(&sunzilog_console);
        return 0;
 }
@@ -1480,6 +1491,7 @@ static void __init sunzilog_prepare(void)
                up[(chip * 2) + 0].port.uartclk = ZS_CLOCK;
                up[(chip * 2) + 0].port.fifosize = 1;
                up[(chip * 2) + 0].port.ops = &sunzilog_pops;
+               up[(chip * 2) + 0].port.type = PORT_SUNZILOG;
                up[(chip * 2) + 0].port.flags = 0;
                up[(chip * 2) + 0].port.line = (chip * 2) + 0;
                up[(chip * 2) + 0].flags |= SUNZILOG_FLAG_IS_CHANNEL_A;
@@ -1490,6 +1502,7 @@ static void __init sunzilog_prepare(void)
                up[(chip * 2) + 1].port.uartclk = ZS_CLOCK;
                up[(chip * 2) + 1].port.fifosize = 1;
                up[(chip * 2) + 1].port.ops = &sunzilog_pops;
+               up[(chip * 2) + 1].port.type = PORT_SUNZILOG;
                up[(chip * 2) + 1].port.flags = 0;
                up[(chip * 2) + 1].port.line = (chip * 2) + 1;
                up[(chip * 2) + 1].flags |= 0;
@@ -1607,12 +1620,10 @@ static int __init sunzilog_ports_init(void)
         * in the system.
         */
        sunzilog_reg.nr = NUM_CHANNELS;
-
-#ifdef CONFIG_SERIAL_CONSOLE
        sunzilog_reg.cons = &sunzilog_console;
-#else
-       sunzilog_reg.cons = NULL;
-#endif
+
+       sunzilog_reg.minor = sunserial_current_minor;
+       sunserial_current_minor += NUM_CHANNELS;
 
        ret = uart_register_driver(&sunzilog_reg);
        if (ret == 0) {
diff --git a/include/asm-sparc64/sab82532.h b/include/asm-sparc64/sab82532.h
deleted file mode 100644 (file)
index 01fb958..0000000
+++ /dev/null
@@ -1,380 +0,0 @@
-/* $Id: sab82532.h,v 1.7 2001/05/23 23:09:10 ecd Exp $
- * sab82532.h: Register Definitions for the Siemens SAB82532 DUSCC
- *
- * Copyright (C) 1997  Eddie C. Dost  (ecd@skynet.be)
- */
-
-#ifndef _SPARC64_SAB82532_H
-#define _SPARC64_SAB82532_H
-
-#include <linux/types.h>
-#include <linux/serial.h>
-#include <linux/circ_buf.h>
-
-struct sab82532_async_rd_regs {
-       u8      rfifo[0x20];    /* Receive FIFO                         */
-       u8      star;           /* Status Register                      */
-       u8      __pad1;
-       u8      mode;           /* Mode Register                        */
-       u8      timr;           /* Timer Register                       */
-       u8      xon;            /* XON Character                        */
-       u8      xoff;           /* XOFF Character                       */
-       u8      tcr;            /* Termination Character Register       */
-       u8      dafo;           /* Data Format                          */
-       u8      rfc;            /* RFIFO Control Register               */
-       u8      __pad2;
-       u8      rbcl;           /* Receive Byte Count Low               */
-       u8      rbch;           /* Receive Byte Count High              */
-       u8      ccr0;           /* Channel Configuration Register 0     */
-       u8      ccr1;           /* Channel Configuration Register 1     */
-       u8      ccr2;           /* Channel Configuration Register 2     */
-       u8      ccr3;           /* Channel Configuration Register 3     */
-       u8      __pad3[4];
-       u8      vstr;           /* Version Status Register              */
-       u8      __pad4[3];
-       u8      gis;            /* Global Interrupt Status              */
-       u8      ipc;            /* Interrupt Port Configuration         */
-       u8      isr0;           /* Interrupt Status 0                   */
-       u8      isr1;           /* Interrupt Status 1                   */
-       u8      pvr;            /* Port Value Register                  */
-       u8      pis;            /* Port Interrupt Status                */
-       u8      pcr;            /* Port Configuration Register          */
-       u8      ccr4;           /* Channel Configuration Register 4     */
-};
-
-struct sab82532_async_wr_regs {
-       u8      xfifo[0x20];    /* Transmit FIFO                        */
-       u8      cmdr;           /* Command Register                     */
-       u8      __pad1;
-       u8      mode;
-       u8      timr;
-       u8      xon;
-       u8      xoff;
-       u8      tcr;
-       u8      dafo;
-       u8      rfc;
-       u8      __pad2;
-       u8      xbcl;           /* Transmit Byte Count Low              */
-       u8      xbch;           /* Transmit Byte Count High             */
-       u8      ccr0;
-       u8      ccr1;
-       u8      ccr2;
-       u8      ccr3;
-       u8      tsax;           /* Time-Slot Assignment Reg. Transmit   */
-       u8      tsar;           /* Time-Slot Assignment Reg. Receive    */
-       u8      xccr;           /* Transmit Channel Capacity Register   */
-       u8      rccr;           /* Receive Channel Capacity Register    */
-       u8      bgr;            /* Baud Rate Generator Register         */
-       u8      tic;            /* Transmit Immediate Character         */
-       u8      mxn;            /* Mask XON Character                   */
-       u8      mxf;            /* Mask XOFF Character                  */
-       u8      iva;            /* Interrupt Vector Address             */
-       u8      ipc;
-       u8      imr0;           /* Interrupt Mask Register 0            */
-       u8      imr1;           /* Interrupt Mask Register 1            */
-       u8      pvr;
-       u8      pim;            /* Port Interrupt Mask                  */
-       u8      pcr;
-       u8      ccr4;
-};
-
-struct sab82532_async_rw_regs {        /* Read/Write registers                 */
-       u8      __pad1[0x20];
-       u8      __pad2;
-       u8      __pad3;
-       u8      mode;
-       u8      timr;
-       u8      xon;
-       u8      xoff;
-       u8      tcr;
-       u8      dafo;
-       u8      rfc;
-       u8      __pad4;
-       u8      __pad5;
-       u8      __pad6;
-       u8      ccr0;
-       u8      ccr1;
-       u8      ccr2;
-       u8      ccr3;
-       u8      __pad7;
-       u8      __pad8;
-       u8      __pad9;
-       u8      __pad10;
-       u8      __pad11;
-       u8      __pad12;
-       u8      __pad13;
-       u8      __pad14;
-       u8      __pad15;
-       u8      ipc;
-       u8      __pad16;
-       u8      __pad17;
-       u8      pvr;
-       u8      __pad18;
-       u8      pcr;
-       u8      ccr4;
-};
-
-union sab82532_async_regs {
-       __volatile__ struct sab82532_async_rd_regs      r;
-       __volatile__ struct sab82532_async_wr_regs      w;
-       __volatile__ struct sab82532_async_rw_regs      rw;
-};
-
-#define NR_PORTS                        2
-
-union sab82532_irq_status {
-       unsigned short                   stat;
-       struct {
-               unsigned char            isr0;
-               unsigned char            isr1;
-       } sreg;
-};
-
-struct sab82532 {
-       int                              magic;
-       int                              baud_base;
-       union sab82532_async_regs       *regs;
-       int                              irq;
-       int                              flags;         /* defined in tty.h */
-       int                              type;          /* SAB82532 version */
-       struct tty_struct               *tty;
-       int                              read_status_mask;
-       int                              ignore_status_mask;
-       int                              timeout;
-       int                              xmit_fifo_size;
-       int                              recv_fifo_size;
-       int                              custom_divisor;
-       int                              baud;
-       unsigned int                     cec_timeout;
-       unsigned int                     tec_timeout;
-       int                              x_char;
-       int                              close_delay;
-       unsigned short                   closing_wait;
-       unsigned short                   closing_wait2;
-       unsigned long                    irqflags;
-       int                              is_console;
-       unsigned char                    interrupt_mask0;
-       unsigned char                    interrupt_mask1;
-       unsigned char                    pvr_dtr_bit;
-       unsigned char                    pvr_dsr_bit;
-       unsigned char                    dcd;
-       unsigned char                    cts;
-       unsigned char                    dsr;
-       unsigned long                    event;
-       unsigned long                    last_active;
-       int                              line;
-       int                              count;
-       int                              blocked_open;
-       long                             session;
-       long                             pgrp;
-       struct circ_buf                  xmit;
-       struct tq_struct                 tqueue;
-       struct tq_struct                 tqueue_hangup;
-       struct async_icount              icount;
-       struct termios                   normal_termios;
-       struct termios                   callout_termios;
-       wait_queue_head_t                open_wait;
-       wait_queue_head_t                close_wait;
-       wait_queue_head_t                delta_msr_wait;
-       struct sab82532                 *next;
-       struct sab82532                 *prev;
-};
-
-
-/* irqflags bits */
-#define SAB82532_ALLS                  0x00000001
-#define SAB82532_XPR                   0x00000002
-
-
-/* RFIFO Status Byte */
-#define SAB82532_RSTAT_PE              0x80
-#define SAB82532_RSTAT_FE              0x40
-#define SAB82532_RSTAT_PARITY          0x01
-
-/* Status Register (STAR) */
-#define SAB82532_STAR_XDOV             0x80
-#define SAB82532_STAR_XFW              0x40
-#define SAB82532_STAR_RFNE             0x20
-#define SAB82532_STAR_FCS              0x10
-#define SAB82532_STAR_TEC              0x08
-#define SAB82532_STAR_CEC              0x04
-#define SAB82532_STAR_CTS              0x02
-
-/* Command Register (CMDR) */
-#define SAB82532_CMDR_RMC              0x80
-#define SAB82532_CMDR_RRES             0x40
-#define SAB82532_CMDR_RFRD             0x20
-#define SAB82532_CMDR_STI              0x10
-#define SAB82532_CMDR_XF               0x08
-#define SAB82532_CMDR_XRES             0x01
-
-/* Mode Register (MODE) */
-#define SAB82532_MODE_FRTS             0x40
-#define SAB82532_MODE_FCTS             0x20
-#define SAB82532_MODE_FLON             0x10
-#define SAB82532_MODE_RAC              0x08
-#define SAB82532_MODE_RTS              0x04
-#define SAB82532_MODE_TRS              0x02
-#define SAB82532_MODE_TLP              0x01
-
-/* Timer Register (TIMR) */
-#define SAB82532_TIMR_CNT_MASK         0xe0
-#define SAB82532_TIMR_VALUE_MASK       0x1f
-
-/* Data Format (DAFO) */
-#define SAB82532_DAFO_XBRK             0x40
-#define SAB82532_DAFO_STOP             0x20
-#define SAB82532_DAFO_PAR_SPACE                0x00
-#define SAB82532_DAFO_PAR_ODD          0x08
-#define SAB82532_DAFO_PAR_EVEN         0x10
-#define SAB82532_DAFO_PAR_MARK         0x18
-#define SAB82532_DAFO_PARE             0x04
-#define SAB82532_DAFO_CHL8             0x00
-#define SAB82532_DAFO_CHL7             0x01
-#define SAB82532_DAFO_CHL6             0x02
-#define SAB82532_DAFO_CHL5             0x03
-
-/* RFIFO Control Register (RFC) */
-#define SAB82532_RFC_DPS               0x40
-#define SAB82532_RFC_DXS               0x20
-#define SAB82532_RFC_RFDF              0x10
-#define SAB82532_RFC_RFTH_1            0x00
-#define SAB82532_RFC_RFTH_4            0x04
-#define SAB82532_RFC_RFTH_16           0x08
-#define SAB82532_RFC_RFTH_32           0x0c
-#define SAB82532_RFC_TCDE              0x01
-
-/* Received Byte Count High (RBCH) */
-#define SAB82532_RBCH_DMA              0x80
-#define SAB82532_RBCH_CAS              0x20
-
-/* Transmit Byte Count High (XBCH) */
-#define SAB82532_XBCH_DMA              0x80
-#define SAB82532_XBCH_CAS              0x20
-#define SAB82532_XBCH_XC               0x10
-
-/* Channel Configuration Register 0 (CCR0) */
-#define SAB82532_CCR0_PU               0x80
-#define SAB82532_CCR0_MCE              0x40
-#define SAB82532_CCR0_SC_NRZ           0x00
-#define SAB82532_CCR0_SC_NRZI          0x08
-#define SAB82532_CCR0_SC_FM0           0x10
-#define SAB82532_CCR0_SC_FM1           0x14
-#define SAB82532_CCR0_SC_MANCH         0x18
-#define SAB82532_CCR0_SM_HDLC          0x00
-#define SAB82532_CCR0_SM_SDLC_LOOP     0x01
-#define SAB82532_CCR0_SM_BISYNC                0x02
-#define SAB82532_CCR0_SM_ASYNC         0x03
-
-/* Channel Configuration Register 1 (CCR1) */
-#define SAB82532_CCR1_ODS              0x10
-#define SAB82532_CCR1_BCR              0x08
-#define SAB82532_CCR1_CM_MASK          0x07
-
-/* Channel Configuration Register 2 (CCR2) */
-#define SAB82532_CCR2_SOC1             0x80
-#define SAB82532_CCR2_SOC0             0x40
-#define SAB82532_CCR2_BR9              0x80
-#define SAB82532_CCR2_BR8              0x40
-#define SAB82532_CCR2_BDF              0x20
-#define SAB82532_CCR2_SSEL             0x10
-#define SAB82532_CCR2_XCS0             0x20
-#define SAB82532_CCR2_RCS0             0x10
-#define SAB82532_CCR2_TOE              0x08
-#define SAB82532_CCR2_RWX              0x04
-#define SAB82532_CCR2_DIV              0x01
-
-/* Channel Configuration Register 3 (CCR3) */
-#define SAB82532_CCR3_PSD              0x01
-
-/* Time Slot Assignment Register Transmit (TSAX) */
-#define SAB82532_TSAX_TSNX_MASK                0xfc
-#define SAB82532_TSAX_XCS2             0x02    /* see also CCR2 */
-#define SAB82532_TSAX_XCS1             0x01
-
-/* Time Slot Assignment Register Receive (TSAR) */
-#define SAB82532_TSAR_TSNR_MASK                0xfc
-#define SAB82532_TSAR_RCS2             0x02    /* see also CCR2 */
-#define SAB82532_TSAR_RCS1             0x01
-
-/* Version Status Register (VSTR) */
-#define SAB82532_VSTR_CD               0x80
-#define SAB82532_VSTR_DPLA             0x40
-#define SAB82532_VSTR_VN_MASK          0x0f
-#define SAB82532_VSTR_VN_1             0x00
-#define SAB82532_VSTR_VN_2             0x01
-#define SAB82532_VSTR_VN_3_2           0x02
-
-/* Global Interrupt Status Register (GIS) */
-#define SAB82532_GIS_PI                        0x80
-#define SAB82532_GIS_ISA1              0x08
-#define SAB82532_GIS_ISA0              0x04
-#define SAB82532_GIS_ISB1              0x02
-#define SAB82532_GIS_ISB0              0x01
-
-/* Interrupt Vector Address (IVA) */
-#define SAB82532_IVA_MASK              0xf1
-
-/* Interrupt Port Configuration (IPC) */
-#define SAB82532_IPC_VIS               0x80
-#define SAB82532_IPC_SLA1              0x10
-#define SAB82532_IPC_SLA0              0x08
-#define SAB82532_IPC_CASM              0x04
-#define SAB82532_IPC_IC_OPEN_DRAIN     0x00
-#define SAB82532_IPC_IC_ACT_LOW                0x01
-#define SAB82532_IPC_IC_ACT_HIGH       0x03
-
-/* Interrupt Status Register 0 (ISR0) */
-#define SAB82532_ISR0_TCD              0x80
-#define SAB82532_ISR0_TIME             0x40
-#define SAB82532_ISR0_PERR             0x20
-#define SAB82532_ISR0_FERR             0x10
-#define SAB82532_ISR0_PLLA             0x08
-#define SAB82532_ISR0_CDSC             0x04
-#define SAB82532_ISR0_RFO              0x02
-#define SAB82532_ISR0_RPF              0x01
-
-/* Interrupt Status Register 1 (ISR1) */
-#define SAB82532_ISR1_BRK              0x80
-#define SAB82532_ISR1_BRKT             0x40
-#define SAB82532_ISR1_ALLS             0x20
-#define SAB82532_ISR1_XOFF             0x10
-#define SAB82532_ISR1_TIN              0x08
-#define SAB82532_ISR1_CSC              0x04
-#define SAB82532_ISR1_XON              0x02
-#define SAB82532_ISR1_XPR              0x01
-
-/* Interrupt Mask Register 0 (IMR0) */
-#define SAB82532_IMR0_TCD              0x80
-#define SAB82532_IMR0_TIME             0x40
-#define SAB82532_IMR0_PERR             0x20
-#define SAB82532_IMR0_FERR             0x10
-#define SAB82532_IMR0_PLLA             0x08
-#define SAB82532_IMR0_CDSC             0x04
-#define SAB82532_IMR0_RFO              0x02
-#define SAB82532_IMR0_RPF              0x01
-
-/* Interrupt Mask Register 1 (IMR1) */
-#define SAB82532_IMR1_BRK              0x80
-#define SAB82532_IMR1_BRKT             0x40
-#define SAB82532_IMR1_ALLS             0x20
-#define SAB82532_IMR1_XOFF             0x10
-#define SAB82532_IMR1_TIN              0x08
-#define SAB82532_IMR1_CSC              0x04
-#define SAB82532_IMR1_XON              0x02
-#define SAB82532_IMR1_XPR              0x01
-
-/* Port Interrupt Status Register (PIS) */
-#define SAB82532_PIS_SYNC_B            0x08
-#define SAB82532_PIS_DTR_B             0x04
-#define SAB82532_PIS_DTR_A             0x02
-#define SAB82532_PIS_SYNC_A            0x01
-
-/* Channel Configuration Register 4 (CCR4) */
-#define SAB82532_CCR4_MCK4             0x80
-#define SAB82532_CCR4_EBRG             0x40
-#define SAB82532_CCR4_TST1             0x20
-#define SAB82532_CCR4_ICD              0x10
-
-
-#endif /* !(_SPARC64_SAB82532_H) */
index 19ab829b67e36c9854fe858b39896cfe08bbade1..8cf9eccab7c1b70b828e8db94f1fdc438e430bee 100644 (file)
 #define PORT_UART00    35
 #define PORT_21285     37
 
+/* Sparc type numbers.  */
+#define PORT_SUNZILOG  38
+#define PORT_SUNSAB    39
+
 #ifdef __KERNEL__
 
 #include <linux/config.h>