fedora core 6 1.2949 + vserver 2.2.0
[linux-2.6.git] / drivers / serial / sunsu.c
index dffd7b6..3ec3df2 100644 (file)
  *   Theodore Ts'o <tytso@mit.edu>, 2001-Oct-12
  *
  * Converted to new 2.5.x UART layer.
- *   David S. Miller (davem@redhat.com), 2002-Jul-29
+ *   David S. Miller (davem@davemloft.net), 2002-Jul-29
  */
 
-#include <linux/config.h>
 #include <linux/module.h>
 #include <linux/kernel.h>
 #include <linux/sched.h>
 
 #include <asm/io.h>
 #include <asm/irq.h>
-#include <asm/oplib.h>
-#include <asm/ebus.h>
-#ifdef CONFIG_SPARC64
-#include <asm/isa.h>
-#endif
+#include <asm/prom.h>
+#include <asm/of_device.h>
 
 #if defined(CONFIG_SERIAL_SUNSU_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ)
 #define SUPPORT_SYSRQ
@@ -94,8 +90,7 @@ struct uart_sunsu_port {
        /* Probing information.  */
        enum su_type            su_type;
        unsigned int            type_probed;    /* XXX Stupid */
-       int                     port_node;
-       unsigned int            irq;
+       unsigned long           reg_size;
 
 #ifdef CONFIG_SERIO
        struct serio            serio;
@@ -103,18 +98,16 @@ struct uart_sunsu_port {
 #endif
 };
 
-#define _INLINE_
-
-static _INLINE_ unsigned int serial_in(struct uart_sunsu_port *up, int offset)
+static unsigned int serial_in(struct uart_sunsu_port *up, int offset)
 {
        offset <<= up->port.regshift;
 
        switch (up->port.iotype) {
-       case SERIAL_IO_HUB6:
+       case UPIO_HUB6:
                outb(up->port.hub6 - 1 + offset, up->port.iobase);
                return inb(up->port.iobase + 1);
 
-       case SERIAL_IO_MEM:
+       case UPIO_MEM:
                return readb(up->port.membase + offset);
 
        default:
@@ -122,8 +115,7 @@ static _INLINE_ unsigned int serial_in(struct uart_sunsu_port *up, int offset)
        }
 }
 
-static _INLINE_ void
-serial_out(struct uart_sunsu_port *up, int offset, int value)
+static void serial_out(struct uart_sunsu_port *up, int offset, int value)
 {
 #ifndef CONFIG_SPARC64
        /*
@@ -140,12 +132,12 @@ serial_out(struct uart_sunsu_port *up, int offset, int value)
        offset <<= up->port.regshift;
 
        switch (up->port.iotype) {
-       case SERIAL_IO_HUB6:
+       case UPIO_HUB6:
                outb(up->port.hub6 - 1 + offset, up->port.iobase);
                outb(value, up->port.iobase + 1);
                break;
 
-       case SERIAL_IO_MEM:
+       case UPIO_MEM:
                writeb(value, up->port.membase + offset);
                break;
 
@@ -256,21 +248,30 @@ static void disable_rsa(struct uart_sunsu_port *up)
 }
 #endif /* CONFIG_SERIAL_8250_RSA */
 
-static void sunsu_stop_tx(struct uart_port *port, unsigned int tty_stop)
+static inline void __stop_tx(struct uart_sunsu_port *p)
+{
+       if (p->ier & UART_IER_THRI) {
+               p->ier &= ~UART_IER_THRI;
+               serial_out(p, UART_IER, p->ier);
+       }
+}
+
+static void sunsu_stop_tx(struct uart_port *port)
 {
        struct uart_sunsu_port *up = (struct uart_sunsu_port *) port;
 
-       if (up->ier & UART_IER_THRI) {
-               up->ier &= ~UART_IER_THRI;
-               serial_out(up, UART_IER, up->ier);
-       }
-       if (up->port.type == PORT_16C950 && tty_stop) {
+       __stop_tx(up);
+
+       /*
+        * We really want to stop the transmitter from sending.
+        */
+       if (up->port.type == PORT_16C950) {
                up->acr |= UART_ACR_TXDIS;
                serial_icr_write(up, UART_ACR, up->acr);
        }
 }
 
-static void sunsu_start_tx(struct uart_port *port, unsigned int tty_start)
+static void sunsu_start_tx(struct uart_port *port)
 {
        struct uart_sunsu_port *up = (struct uart_sunsu_port *) port;
 
@@ -278,10 +279,11 @@ static void sunsu_start_tx(struct uart_port *port, unsigned int tty_start)
                up->ier |= UART_IER_THRI;
                serial_out(up, UART_IER, up->ier);
        }
+
        /*
-        * We only do this from uart_start
+        * Re-enable the transmitter if we disabled it.
         */
-       if (tty_start && up->port.type == PORT_16C950) {
+       if (up->port.type == PORT_16C950 && up->acr & UART_ACR_TXDIS) {
                up->acr &= ~UART_ACR_TXDIS;
                serial_icr_write(up, UART_ACR, up->acr);
        }
@@ -290,13 +292,10 @@ static void sunsu_start_tx(struct uart_port *port, unsigned int tty_start)
 static void sunsu_stop_rx(struct uart_port *port)
 {
        struct uart_sunsu_port *up = (struct uart_sunsu_port *) port;
-       unsigned long flags;
 
-       spin_lock_irqsave(&up->port.lock, flags);
        up->ier &= ~UART_IER_RLSI;
        up->port.read_status_mask &= ~UART_LSR_DR;
        serial_out(up, UART_IER, up->ier);
-       spin_unlock_irqrestore(&up->port.lock, flags);
 }
 
 static void sunsu_enable_ms(struct uart_port *port)
@@ -310,23 +309,17 @@ static void sunsu_enable_ms(struct uart_port *port)
        spin_unlock_irqrestore(&up->port.lock, flags);
 }
 
-static _INLINE_ void
-receive_chars(struct uart_sunsu_port *up, unsigned char *status, struct pt_regs *regs)
+static struct tty_struct *
+receive_chars(struct uart_sunsu_port *up, unsigned char *status)
 {
        struct tty_struct *tty = up->port.info->tty;
-       unsigned char ch;
+       unsigned char ch, flag;
        int max_count = 256;
        int saw_console_brk = 0;
 
        do {
-               if (unlikely(tty->flip.count >= TTY_FLIPBUF_SIZE)) {
-                       tty->flip.work.func((void *)tty);
-                       if (tty->flip.count >= TTY_FLIPBUF_SIZE)
-                               return; // if TTY_DONT_FLIP is set
-               }
                ch = serial_inp(up, UART_RX);
-               *tty->flip.char_buf_ptr = ch;
-               *tty->flip.flag_buf_ptr = TTY_NORMAL;
+               flag = TTY_NORMAL;
                up->port.icount.rx++;
 
                if (unlikely(*status & (UART_LSR_BI | UART_LSR_PE |
@@ -368,41 +361,34 @@ receive_chars(struct uart_sunsu_port *up, unsigned char *status, struct pt_regs
                        }
 
                        if (*status & UART_LSR_BI) {
-                               *tty->flip.flag_buf_ptr = TTY_BREAK;
+                               flag = TTY_BREAK;
                        } else if (*status & UART_LSR_PE)
-                               *tty->flip.flag_buf_ptr = TTY_PARITY;
+                               flag = TTY_PARITY;
                        else if (*status & UART_LSR_FE)
-                               *tty->flip.flag_buf_ptr = TTY_FRAME;
+                               flag = TTY_FRAME;
                }
-               if (uart_handle_sysrq_char(&up->port, ch, regs))
+               if (uart_handle_sysrq_char(&up->port, ch))
                        goto ignore_char;
-               if ((*status & up->port.ignore_status_mask) == 0) {
-                       tty->flip.flag_buf_ptr++;
-                       tty->flip.char_buf_ptr++;
-                       tty->flip.count++;
-               }
-               if ((*status & UART_LSR_OE) &&
-                   tty->flip.count < TTY_FLIPBUF_SIZE) {
+               if ((*status & up->port.ignore_status_mask) == 0)
+                       tty_insert_flip_char(tty, ch, flag);
+               if (*status & UART_LSR_OE)
                        /*
                         * 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++;
-               }
+                        tty_insert_flip_char(tty, 0, TTY_OVERRUN);
        ignore_char:
                *status = serial_inp(up, UART_LSR);
        } while ((*status & UART_LSR_DR) && (max_count-- > 0));
-       tty_flip_buffer_push(tty);
 
        if (saw_console_brk)
                sun_do_break();
+
+       return tty;
 }
 
-static _INLINE_ void transmit_chars(struct uart_sunsu_port *up)
+static void transmit_chars(struct uart_sunsu_port *up)
 {
        struct circ_buf *xmit = &up->port.info->xmit;
        int count;
@@ -413,8 +399,12 @@ static _INLINE_ void transmit_chars(struct uart_sunsu_port *up)
                up->port.x_char = 0;
                return;
        }
-       if (uart_circ_empty(xmit) || uart_tx_stopped(&up->port)) {
-               sunsu_stop_tx(&up->port, 0);
+       if (uart_tx_stopped(&up->port)) {
+               sunsu_stop_tx(&up->port);
+               return;
+       }
+       if (uart_circ_empty(xmit)) {
+               __stop_tx(up);
                return;
        }
 
@@ -431,10 +421,10 @@ static _INLINE_ void transmit_chars(struct uart_sunsu_port *up)
                uart_write_wakeup(&up->port);
 
        if (uart_circ_empty(xmit))
-               sunsu_stop_tx(&up->port, 0);
+               __stop_tx(up);
 }
 
-static _INLINE_ void check_modem_status(struct uart_sunsu_port *up)
+static void check_modem_status(struct uart_sunsu_port *up)
 {
        int status;
 
@@ -455,7 +445,7 @@ static _INLINE_ void check_modem_status(struct uart_sunsu_port *up)
        wake_up_interruptible(&up->port.info->delta_msr_wait);
 }
 
-static irqreturn_t sunsu_serial_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+static irqreturn_t sunsu_serial_interrupt(int irq, void *dev_id)
 {
        struct uart_sunsu_port *up = dev_id;
        unsigned long flags;
@@ -464,12 +454,23 @@ static irqreturn_t sunsu_serial_interrupt(int irq, void *dev_id, struct pt_regs
        spin_lock_irqsave(&up->port.lock, flags);
 
        do {
+               struct tty_struct *tty;
+
                status = serial_inp(up, UART_LSR);
+               tty = NULL;
                if (status & UART_LSR_DR)
-                       receive_chars(up, &status, regs);
+                       tty = receive_chars(up, &status);
                check_modem_status(up);
                if (status & UART_LSR_THRE)
                        transmit_chars(up);
+
+               spin_unlock_irqrestore(&up->port.lock, flags);
+
+               if (tty)
+                       tty_flip_buffer_push(tty);
+
+               spin_lock_irqsave(&up->port.lock, flags);
+
        } while (!(serial_in(up, UART_IIR) & UART_IIR_NO_INT));
 
        spin_unlock_irqrestore(&up->port.lock, flags);
@@ -493,14 +494,10 @@ static void sunsu_change_mouse_baud(struct uart_sunsu_port *up)
 
        quot = up->port.uartclk / (16 * new_baud);
 
-       spin_unlock(&up->port.lock);
-
        sunsu_change_speed(&up->port, up->cflag, 0, quot);
-
-       spin_lock(&up->port.lock);
 }
 
-static void receive_kbd_ms_chars(struct uart_sunsu_port *up, struct pt_regs *regs, int is_break)
+static void receive_kbd_ms_chars(struct uart_sunsu_port *up, int is_break)
 {
        do {
                unsigned char ch = serial_inp(up, UART_RX);
@@ -508,7 +505,7 @@ static void receive_kbd_ms_chars(struct uart_sunsu_port *up, struct pt_regs *reg
                /* Stop-A is handled by drivers/char/keyboard.c now. */
                if (up->su_type == SU_PORT_KBD) {
 #ifdef CONFIG_SERIO
-                       serio_interrupt(&up->serio, ch, 0, regs);
+                       serio_interrupt(&up->serio, ch, 0);
 #endif
                } else if (up->su_type == SU_PORT_MS) {
                        int ret = suncore_mouse_baud_detection(ch, is_break);
@@ -522,7 +519,7 @@ static void receive_kbd_ms_chars(struct uart_sunsu_port *up, struct pt_regs *reg
 
                        case 0:
 #ifdef CONFIG_SERIO
-                               serio_interrupt(&up->serio, ch, 0, regs);
+                               serio_interrupt(&up->serio, ch, 0);
 #endif
                                break;
                        };
@@ -530,7 +527,7 @@ static void receive_kbd_ms_chars(struct uart_sunsu_port *up, struct pt_regs *reg
        } while (serial_in(up, UART_LSR) & UART_LSR_DR);
 }
 
-static irqreturn_t sunsu_kbd_ms_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+static irqreturn_t sunsu_kbd_ms_interrupt(int irq, void *dev_id)
 {
        struct uart_sunsu_port *up = dev_id;
 
@@ -538,8 +535,7 @@ static irqreturn_t sunsu_kbd_ms_interrupt(int irq, void *dev_id, struct pt_regs
                unsigned char status = serial_inp(up, UART_LSR);
 
                if ((status & UART_LSR_DR) || (status & UART_LSR_BI))
-                       receive_kbd_ms_chars(up, regs,
-                                            (status & UART_LSR_BI) != 0);
+                       receive_kbd_ms_chars(up, (status & UART_LSR_BI) != 0);
        }
 
        return IRQ_HANDLED;
@@ -561,13 +557,10 @@ static unsigned int sunsu_tx_empty(struct uart_port *port)
 static unsigned int sunsu_get_mctrl(struct uart_port *port)
 {
        struct uart_sunsu_port *up = (struct uart_sunsu_port *) port;
-       unsigned long flags;
        unsigned char status;
        unsigned int ret;
 
-       spin_lock_irqsave(&up->port.lock, flags);
        status = serial_in(up, UART_MSR);
-       spin_unlock_irqrestore(&up->port.lock, flags);
 
        ret = 0;
        if (status & UART_MSR_DCD)
@@ -643,7 +636,7 @@ static int sunsu_startup(struct uart_port *port)
 
        /*
         * Clear the FIFO buffers and disable them.
-        * (they will be reeanbled in set_termios())
+        * (they will be reenabled in set_termios())
         */
        if (uart_config[up->port.type].flags & UART_CLEAR_FIFO) {
                serial_outp(up, UART_FCR, UART_FCR_ENABLE_FIFO);
@@ -665,21 +658,21 @@ static int sunsu_startup(struct uart_port *port)
         * if it is, then bail out, because there's likely no UART
         * here.
         */
-       if (!(up->port.flags & ASYNC_BUGGY_UART) &&
+       if (!(up->port.flags & UPF_BUGGY_UART) &&
            (serial_inp(up, UART_LSR) == 0xff)) {
                printk("ttyS%d: LSR safety check engaged!\n", up->port.line);
                return -ENODEV;
        }
 
        if (up->su_type != SU_PORT_PORT) {
-               retval = request_irq(up->irq, sunsu_kbd_ms_interrupt,
-                                    SA_SHIRQ, su_typev[up->su_type], up);
+               retval = request_irq(up->port.irq, sunsu_kbd_ms_interrupt,
+                                    IRQF_SHARED, su_typev[up->su_type], up);
        } else {
-               retval = request_irq(up->irq, sunsu_serial_interrupt,
-                                    SA_SHIRQ, su_typev[up->su_type], up);
+               retval = request_irq(up->port.irq, sunsu_serial_interrupt,
+                                    IRQF_SHARED, su_typev[up->su_type], up);
        }
        if (retval) {
-               printk("su: Cannot register IRQ %d\n", up->irq);
+               printk("su: Cannot register IRQ %d\n", up->port.irq);
                return retval;
        }
 
@@ -703,7 +696,7 @@ static int sunsu_startup(struct uart_port *port)
        up->ier = UART_IER_RLSI | UART_IER_RDI;
        serial_outp(up, UART_IER, up->ier);
 
-       if (up->port.flags & ASYNC_FOURPORT) {
+       if (up->port.flags & UPF_FOURPORT) {
                unsigned int icp;
                /*
                 * Enable interrupts on the AST Fourport board
@@ -736,7 +729,7 @@ static void sunsu_shutdown(struct uart_port *port)
        serial_outp(up, UART_IER, 0);
 
        spin_lock_irqsave(&up->port.lock, flags);
-       if (up->port.flags & ASYNC_FOURPORT) {
+       if (up->port.flags & UPF_FOURPORT) {
                /* reset interrupts on the AST Fourport board */
                inb((up->port.iobase & 0xfe0) | 0x1f);
                up->port.mctrl |= TIOCM_OUT1;
@@ -767,7 +760,7 @@ static void sunsu_shutdown(struct uart_port *port)
         */
        (void) serial_in(up, UART_RX);
 
-       free_irq(up->irq, up);
+       free_irq(up->port.irq, up);
 }
 
 static void
@@ -900,8 +893,8 @@ sunsu_change_speed(struct uart_port *port, unsigned int cflag,
 }
 
 static void
-sunsu_set_termios(struct uart_port *port, struct termios *termios,
-                 struct termios *old)
+sunsu_set_termios(struct uart_port *port, struct ktermios *termios,
+                 struct ktermios *old)
 {
        unsigned int baud, quot;
 
@@ -978,11 +971,11 @@ static struct uart_sunsu_port sunsu_ports[UART_NR];
 
 #ifdef CONFIG_SERIO
 
-static spinlock_t sunsu_serio_lock = SPIN_LOCK_UNLOCKED;
+static DEFINE_SPINLOCK(sunsu_serio_lock);
 
 static int sunsu_serio_write(struct serio *serio, unsigned char ch)
 {
-       struct uart_sunsu_port *up = serio->driver;
+       struct uart_sunsu_port *up = serio->port_data;
        unsigned long flags;
        int lsr;
 
@@ -1002,7 +995,7 @@ static int sunsu_serio_write(struct serio *serio, unsigned char ch)
 
 static int sunsu_serio_open(struct serio *serio)
 {
-       struct uart_sunsu_port *up = serio->driver;
+       struct uart_sunsu_port *up = serio->port_data;
        unsigned long flags;
        int ret;
 
@@ -1019,7 +1012,7 @@ static int sunsu_serio_open(struct serio *serio)
 
 static void sunsu_serio_close(struct serio *serio)
 {
-       struct uart_sunsu_port *up = serio->driver;
+       struct uart_sunsu_port *up = serio->port_data;
        unsigned long flags;
 
        spin_lock_irqsave(&sunsu_serio_lock, flags);
@@ -1033,102 +1026,17 @@ static void sunsu_autoconfig(struct uart_sunsu_port *up)
 {
        unsigned char status1, status2, scratch, scratch2, scratch3;
        unsigned char save_lcr, save_mcr;
-       struct linux_ebus_device *dev = 0;
-       struct linux_ebus *ebus;
-#ifdef CONFIG_SPARC64
-       struct sparc_isa_bridge *isa_br;
-       struct sparc_isa_device *isa_dev;
-#endif
-#ifndef CONFIG_SPARC64
-       struct linux_prom_registers reg0;
-#endif
        unsigned long flags;
 
-       if (!up->port_node || !up->su_type)
+       if (up->su_type == SU_PORT_NONE)
                return;
 
        up->type_probed = PORT_UNKNOWN;
-       up->port.iotype = SERIAL_IO_MEM;
-
-       /*
-        * First we look for Ebus-bases su's
-        */
-       for_each_ebus(ebus) {
-               for_each_ebusdev(dev, ebus) {
-                       if (dev->prom_node == up->port_node) {
-                               /*
-                                * The EBus is broken on sparc; it delivers
-                                * virtual addresses in resources. Oh well...
-                                * This is correct on sparc64, though.
-                                */
-                               up->port.membase = (char *) dev->resource[0].start;
-                               /*
-                                * This is correct on both architectures.
-                                */
-                               up->port.mapbase = dev->resource[0].start;
-                               up->irq = dev->irqs[0];
-                               goto ebus_done;
-                       }
-               }
-       }
-
-#ifdef CONFIG_SPARC64
-       for_each_isa(isa_br) {
-               for_each_isadev(isa_dev, isa_br) {
-                       if (isa_dev->prom_node == up->port_node) {
-                               /* Same on sparc64. Cool architecure... */
-                               up->port.membase = (char *) isa_dev->resource.start;
-                               up->port.mapbase = isa_dev->resource.start;
-                               up->irq = isa_dev->irq;
-                               goto ebus_done;
-                       }
-               }
-       }
-#endif
-
-#ifdef CONFIG_SPARC64
-       /*
-        * Not on Ebus, bailing.
-        */
-       return;
-#else
-       /*
-        * Not on Ebus, must be OBIO.
-        */
-       if (prom_getproperty(up->port_node, "reg",
-                            (char *)&reg0, sizeof(reg0)) == -1) {
-               prom_printf("sunsu: no \"reg\" property\n");
-               return;
-       }
-       prom_apply_obio_ranges(&reg0, 1);
-       if (reg0.which_io != 0) {       /* Just in case... */
-               prom_printf("sunsu: bus number nonzero: 0x%x:%x\n",
-                   reg0.which_io, reg0.phys_addr);
-               return;
-       }
-       up->port.mapbase = reg0.phys_addr;
-       if ((up->port.membase = ioremap(reg0.phys_addr, reg0.reg_size)) == 0) {
-               prom_printf("sunsu: Cannot map registers.\n");
-               return;
-       }
-
-       /*
-        * 0x20 is sun4m thing, Dave Redman heritage.
-        * See arch/sparc/kernel/irq.c.
-        */
-#define IRQ_4M(n)      ((n)|0x20)
-
-       /*
-        * There is no intr property on MrCoffee, so hardwire it.
-        */
-       up->irq = IRQ_4M(13);
-#endif
-
-ebus_done:
+       up->port.iotype = UPIO_MEM;
 
        spin_lock_irqsave(&up->port.lock, flags);
 
-       if (!(up->port.flags & ASYNC_BUGGY_UART)) {
+       if (!(up->port.flags & UPF_BUGGY_UART)) {
                /*
                 * Do a simple existence test first; if we fail this, there's
                 * no point trying anything else.
@@ -1166,7 +1074,7 @@ ebus_done:
         * manufacturer would be stupid enough to design a board
         * that conflicts with COM 1-4 --- we hope!
         */
-       if (!(up->port.flags & ASYNC_SKIP_TEST)) {
+       if (!(up->port.flags & UPF_SKIP_TEST)) {
                serial_outp(up, UART_MCR, UART_MCR_LOOP | 0x0A);
                status1 = serial_inp(up, UART_MSR) & 0xF0;
                serial_outp(up, UART_MCR, save_mcr);
@@ -1267,59 +1175,63 @@ out:
 static struct uart_driver sunsu_reg = {
        .owner                  = THIS_MODULE,
        .driver_name            = "serial",
-       .devfs_name             = "tts/",
        .dev_name               = "ttyS",
        .major                  = TTY_MAJOR,
 };
 
-static int __init sunsu_kbd_ms_init(void)
+static int __init sunsu_kbd_ms_init(struct uart_sunsu_port *up)
 {
-       struct uart_sunsu_port *up;
-       int i;
-
-       for (i = 0, up = sunsu_ports; i < 2; i++, up++) {
-               up->port.line = i;
-               up->port.type = PORT_UNKNOWN;
-               up->port.uartclk = (SU_BASE_BAUD * 16);
+       int quot, baud;
+#ifdef CONFIG_SERIO
+       struct serio *serio;
+#endif
 
-               if (up->su_type == SU_PORT_KBD)
-                       up->cflag = B1200 | CS8 | CLOCAL | CREAD;
-               else
-                       up->cflag = B4800 | CS8 | CLOCAL | CREAD;
+       if (up->su_type == SU_PORT_KBD) {
+               up->cflag = B1200 | CS8 | CLOCAL | CREAD;
+               baud = 1200;
+       } else {
+               up->cflag = B4800 | CS8 | CLOCAL | CREAD;
+               baud = 4800;
+       }
+       quot = up->port.uartclk / (16 * baud);
 
-               sunsu_autoconfig(up);
-               if (up->port.type == PORT_UNKNOWN)
-                       continue;
+       sunsu_autoconfig(up);
+       if (up->port.type == PORT_UNKNOWN)
+               return -ENODEV;
 
-               printk(KERN_INFO "su%d at 0x%p (irq = %s) is a %s\n",
-                      i,
-                      up->port.membase, __irq_itoa(up->irq),
-                      sunsu_type(&up->port));
+       printk("%s: %s port at %lx, irq %u\n",
+              to_of_device(up->port.dev)->node->full_name,
+              (up->su_type == SU_PORT_KBD) ? "Keyboard" : "Mouse",
+              up->port.mapbase, up->port.irq);
 
 #ifdef CONFIG_SERIO
-               memset(&up->serio, 0, sizeof(up->serio));
+       serio = &up->serio;
+       serio->port_data = up;
 
-               up->serio.driver = up;
-
-               up->serio.type = SERIO_RS232;
-               if (up->su_type == SU_PORT_KBD) {
-                       up->serio.type |= SERIO_SUNKBD;
-                       up->serio.name = "sukbd";
-               } else {
-                       up->serio.type |= (SERIO_SUN | (1 << 16));
-                       up->serio.name = "sums";
-               }
-               up->serio.phys = (i == 0 ? "su/serio0" : "su/serio1");
+       serio->id.type = SERIO_RS232;
+       if (up->su_type == SU_PORT_KBD) {
+               serio->id.proto = SERIO_SUNKBD;
+               strlcpy(serio->name, "sukbd", sizeof(serio->name));
+       } else {
+               serio->id.proto = SERIO_SUN;
+               serio->id.extra = 1;
+               strlcpy(serio->name, "sums", sizeof(serio->name));
+       }
+       strlcpy(serio->phys,
+               (!(up->port.line & 1) ? "su/serio0" : "su/serio1"),
+               sizeof(serio->phys));
 
-               up->serio.write = sunsu_serio_write;
-               up->serio.open = sunsu_serio_open;
-               up->serio.close = sunsu_serio_close;
+       serio->write = sunsu_serio_write;
+       serio->open = sunsu_serio_open;
+       serio->close = sunsu_serio_close;
+       serio->dev.parent = up->port.dev;
 
-               serio_register_port(&up->serio);
+       serio_register_port(serio);
 #endif
 
-               sunsu_startup(&up->port);
-       }
+       sunsu_change_speed(&up->port, up->cflag, 0, quot);
+
+       sunsu_startup(&up->port);
        return 0;
 }
 
@@ -1353,7 +1265,7 @@ static __inline__ void wait_for_xmitr(struct uart_sunsu_port *up)
        } while ((status & BOTH_EMPTY) != BOTH_EMPTY);
 
        /* Wait up to 1s for flow control if necessary */
-       if (up->port.flags & ASYNC_CONS_FLOW) {
+       if (up->port.flags & UPF_CONS_FLOW) {
                tmout = 1000000;
                while (--tmout &&
                       ((serial_in(up, UART_MSR) & UART_MSR_CTS) == 0))
@@ -1361,6 +1273,14 @@ static __inline__ void wait_for_xmitr(struct uart_sunsu_port *up)
        }
 }
 
+static void sunsu_console_putchar(struct uart_port *port, int ch)
+{
+       struct uart_sunsu_port *up = (struct uart_sunsu_port *)port;
+
+       wait_for_xmitr(up);
+       serial_out(up, UART_TX, ch);
+}
+
 /*
  *     Print a string to the serial port trying not to disturb
  *     any possible real use of the port...
@@ -1370,7 +1290,6 @@ static void sunsu_console_write(struct console *co, const char *s,
 {
        struct uart_sunsu_port *up = &sunsu_ports[co->index];
        unsigned int ier;
-       int i;
 
        /*
         *      First save the UER then disable the interrupts
@@ -1378,22 +1297,7 @@ static void sunsu_console_write(struct console *co, const char *s,
        ier = serial_in(up, UART_IER);
        serial_out(up, UART_IER, 0);
 
-       /*
-        *      Now, do each character
-        */
-       for (i = 0; i < count; i++, s++) {
-               wait_for_xmitr(up);
-
-               /*
-                *      Send the character out.
-                *      If a LF, also do CR...
-                */
-               serial_out(up, UART_TX, *s);
-               if (*s == 10) {
-                       wait_for_xmitr(up);
-                       serial_out(up, UART_TX, 13);
-               }
-       }
+       uart_console_write(&up->port, s, count, sunsu_console_putchar);
 
        /*
         *      Finally, wait for transmitter to become empty
@@ -1409,7 +1313,7 @@ static void sunsu_console_write(struct console *co, const char *s,
  *     - initialize the serial port
  *     Return non-zero if we didn't find a serial port.
  */
-static int __init sunsu_console_setup(struct console *co, char *options)
+static int sunsu_console_setup(struct console *co, char *options)
 {
        struct uart_port *port;
        int baud = 9600;
@@ -1449,271 +1353,232 @@ static struct console sunsu_cons = {
        .index  =       -1,
        .data   =       &sunsu_reg,
 };
-#define SUNSU_CONSOLE  (&sunsu_cons)
 
 /*
  *     Register console.
  */
 
-static int __init sunsu_serial_console_init(void)
+static inline struct console *SUNSU_CONSOLE(int num_uart)
 {
        int i;
 
        if (con_is_present())
-               return 0;
+               return NULL;
 
-       for (i = 0; i < UART_NR; i++) {
+       for (i = 0; i < num_uart; i++) {
                int this_minor = sunsu_reg.minor + i;
 
                if ((this_minor - 64) == (serial_console - 1))
                        break;
        }
-       if (i == UART_NR)
-               return 0;
-       if (sunsu_ports[i].port_node == 0)
-               return 0;
+       if (i == num_uart)
+               return NULL;
 
        sunsu_cons.index = i;
-       register_console(&sunsu_cons);
-       return 0;
+
+       return &sunsu_cons;
 }
 #else
-#define SUNSU_CONSOLE                  (NULL)
+#define SUNSU_CONSOLE(num_uart)                (NULL)
 #define sunsu_serial_console_init()    do { } while (0)
 #endif
 
-static int __init sunsu_serial_init(void)
+static enum su_type __devinit su_get_type(struct device_node *dp)
 {
-       int instance, ret, i;
-
-       /* How many instances do we need?  */
-       instance = 0;
-       for (i = 0; i < UART_NR; i++) {
-               struct uart_sunsu_port *up = &sunsu_ports[i];
+       struct device_node *ap = of_find_node_by_path("/aliases");
 
-               if (up->su_type == SU_PORT_MS ||
-                   up->su_type == SU_PORT_KBD)
-                       continue;
+       if (ap) {
+               char *keyb = of_get_property(ap, "keyboard", NULL);
+               char *ms = of_get_property(ap, "mouse", NULL);
 
-               up->port.flags |= ASYNC_BOOT_AUTOCONF;
-               up->port.type = PORT_UNKNOWN;
-               up->port.uartclk = (SU_BASE_BAUD * 16);
+               if (keyb) {
+                       if (dp == of_find_node_by_path(keyb))
+                               return SU_PORT_KBD;
+               }
+               if (ms) {
+                       if (dp == of_find_node_by_path(ms))
+                               return SU_PORT_MS;
+               }
+       }
 
-               sunsu_autoconfig(up);
-               if (up->port.type == PORT_UNKNOWN)
-                       continue;
+       return SU_PORT_PORT;
+}
 
-               up->port.line = instance++;
-               up->port.ops = &sunsu_pops;
+static int __devinit su_probe(struct of_device *op, const struct of_device_id *match)
+{
+       static int inst;
+       struct device_node *dp = op->node;
+       struct uart_sunsu_port *up;
+       struct resource *rp;
+       enum su_type type;
+       int err;
+
+       type = su_get_type(dp);
+       if (type == SU_PORT_PORT) {
+               if (inst >= UART_NR)
+                       return -EINVAL;
+               up = &sunsu_ports[inst];
+       } else {
+               up = kzalloc(sizeof(*up), GFP_KERNEL);
+               if (!up)
+                       return -ENOMEM;
        }
 
-       sunsu_reg.minor = sunserial_current_minor;
-       sunserial_current_minor += instance;
-
-       sunsu_reg.nr = instance;
-       sunsu_reg.cons = SUNSU_CONSOLE;
+       up->port.line = inst;
 
-       ret = uart_register_driver(&sunsu_reg);
-       if (ret < 0)
-               return ret;
+       spin_lock_init(&up->port.lock);
 
-       for (i = 0; i < UART_NR; i++) {
-               struct uart_sunsu_port *up = &sunsu_ports[i];
+       up->su_type = type;
 
-               /* Do not register Keyboard/Mouse lines with UART
-                * layer.
-                */
-               if (up->su_type == SU_PORT_MS ||
-                   up->su_type == SU_PORT_KBD)
-                       continue;
+       rp = &op->resource[0];
+       up->port.mapbase = rp->start;
+       up->reg_size = (rp->end - rp->start) + 1;
+       up->port.membase = of_ioremap(rp, 0, up->reg_size, "su");
+       if (!up->port.membase) {
+               if (type != SU_PORT_PORT)
+                       kfree(up);
+               return -ENOMEM;
+       }
 
-               if (up->port.type == PORT_UNKNOWN)
-                       continue;
+       up->port.irq = op->irqs[0];
 
-               uart_add_one_port(&sunsu_reg, &up->port);
-       }
+       up->port.dev = &op->dev;
 
-       return 0;
-}
+       up->port.type = PORT_UNKNOWN;
+       up->port.uartclk = (SU_BASE_BAUD * 16);
 
-static int su_node_ok(int node, char *name, int namelen)
-{
-       if (strncmp(name, "su", namelen) == 0 ||
-           strncmp(name, "su_pnp", namelen) == 0)
-               return 1;
-
-       if (strncmp(name, "serial", namelen) == 0) {
-               char compat[32];
-               int clen;
-
-               /* Is it _really_ a 'su' device? */
-               clen = prom_getproperty(node, "compatible", compat, sizeof(compat));
-               if (clen > 0) {
-                       if (strncmp(compat, "sab82532", 8) == 0) {
-                               /* Nope, Siemens serial, not for us. */
-                               return 0;
-                       }
+       err = 0;
+       if (up->su_type == SU_PORT_KBD || up->su_type == SU_PORT_MS) {
+               err = sunsu_kbd_ms_init(up);
+               if (err) {
+                       kfree(up);
+                       goto out_unmap;
                }
-               return 1;
+               dev_set_drvdata(&op->dev, up);
+
+               return 0;
        }
 
-       return 0;
-}
+       up->port.flags |= UPF_BOOT_AUTOCONF;
 
-#define SU_PROPSIZE    128
+       sunsu_autoconfig(up);
 
-/*
- * Scan status structure.
- * "prop" is a local variable but it eats stack to keep it in each
- * stack frame of a recursive procedure.
- */
-struct su_probe_scan {
-       int msnode, kbnode;     /* PROM nodes for mouse and keyboard */
-       int msx, kbx;           /* minors for mouse and keyboard */
-       int devices;            /* scan index */
-       char prop[SU_PROPSIZE];
-};
+       err = -ENODEV;
+       if (up->port.type == PORT_UNKNOWN)
+               goto out_unmap;
 
-/*
- * We have several platforms which present 'su' in different parts
- * of the device tree. 'su' may be found under obio, ebus, isa and pci.
- * We walk over the tree and find them wherever PROM hides them.
- */
-static void __init su_probe_any(struct su_probe_scan *t, int sunode)
-{
-       struct uart_sunsu_port *up;
-       int len;
+       up->port.ops = &sunsu_pops;
 
-       if (t->devices >= UART_NR)
-               return;
+       err = uart_add_one_port(&sunsu_reg, &up->port);
+       if (err)
+               goto out_unmap;
 
-       for (; sunode != 0; sunode = prom_getsibling(sunode)) {
-               len = prom_getproperty(sunode, "name", t->prop, SU_PROPSIZE);
-               if (len <= 1)
-                       continue;               /* Broken PROM node */
-
-               if (su_node_ok(sunode, t->prop, len)) {
-                       up = &sunsu_ports[t->devices];
-                       if (t->kbnode != 0 && sunode == t->kbnode) {
-                               t->kbx = t->devices;
-                               up->su_type = SU_PORT_KBD;
-                       } else if (t->msnode != 0 && sunode == t->msnode) {
-                               t->msx = t->devices;
-                               up->su_type = SU_PORT_MS;
-                       } else {
-#ifdef CONFIG_SPARC64
-                               /*
-                                * Do not attempt to use the truncated
-                                * keyboard/mouse ports as serial ports
-                                * on Ultras with PC keyboard attached.
-                                */
-                               if (prom_getbool(sunode, "mouse"))
-                                       continue;
-                               if (prom_getbool(sunode, "keyboard"))
-                                       continue;
-#endif
-                               up->su_type = SU_PORT_PORT;
-                       }
-                       up->port_node = sunode;
-                       ++t->devices;
-               } else {
-                       su_probe_any(t, prom_getchild(sunode));
-               }
-       }
+       dev_set_drvdata(&op->dev, up);
+
+       inst++;
+
+       return 0;
+
+out_unmap:
+       of_iounmap(&op->resource[0], up->port.membase, up->reg_size);
+       return err;
 }
 
-static int __init sunsu_probe(void)
+static int __devexit su_remove(struct of_device *op)
 {
-       int node;
-       int len;
-       struct su_probe_scan scan;
+       struct uart_sunsu_port *up = dev_get_drvdata(&op->dev);
 
-       /*
-        * First, we scan the tree.
-        */
-       scan.devices = 0;
-       scan.msx = -1;
-       scan.kbx = -1;
-       scan.kbnode = 0;
-       scan.msnode = 0;
+       if (up->su_type == SU_PORT_MS ||
+           up->su_type == SU_PORT_KBD) {
+#ifdef CONFIG_SERIO
+               serio_unregister_port(&up->serio);
+#endif
+               kfree(up);
+       } else if (up->port.type != PORT_UNKNOWN) {
+               uart_remove_one_port(&sunsu_reg, &up->port);
+       }
 
-       /*
-        * Get the nodes for keyboard and mouse from 'aliases'...
-        */
-        node = prom_getchild(prom_root_node);
-       node = prom_searchsiblings(node, "aliases");
-       if (node != 0) {
-               len = prom_getproperty(node, "keyboard", scan.prop, SU_PROPSIZE);
-               if (len > 0) {
-                       scan.prop[len] = 0;
-                       scan.kbnode = prom_finddevice(scan.prop);
-               }
+       if (up->port.membase)
+               of_iounmap(&op->resource[0], up->port.membase, up->reg_size);
 
-               len = prom_getproperty(node, "mouse", scan.prop, SU_PROPSIZE);
-               if (len > 0) {
-                       scan.prop[len] = 0;
-                       scan.msnode = prom_finddevice(scan.prop);
-               }
-       }
+       dev_set_drvdata(&op->dev, NULL);
 
-       su_probe_any(&scan, prom_getchild(prom_root_node));
+       return 0;
+}
 
-       /*
-        * Second, we process the special case of keyboard and mouse.
-        *
-        * Currently if we got keyboard and mouse hooked to "su" ports
-        * we do not use any possible remaining "su" as a serial port.
-        * Thus, we ignore values of .msx and .kbx, then compact ports.
-        */
-       if (scan.msx != -1 && scan.kbx != -1) {
-               sunsu_ports[0].su_type = SU_PORT_MS;
-               sunsu_ports[0].port_node = scan.msnode;
-               sunsu_ports[1].su_type = SU_PORT_KBD;
-               sunsu_ports[1].port_node = scan.kbnode;
+static struct of_device_id su_match[] = {
+       {
+               .name = "su",
+       },
+       {
+               .name = "su_pnp",
+       },
+       {
+               .name = "serial",
+               .compatible = "su",
+       },
+       {},
+};
+MODULE_DEVICE_TABLE(of, su_match);
 
-               sunsu_kbd_ms_init();
-               return 0;
-       }
+static struct of_platform_driver su_driver = {
+       .name           = "su",
+       .match_table    = su_match,
+       .probe          = su_probe,
+       .remove         = __devexit_p(su_remove),
+};
 
-       if (scan.msx != -1 || scan.kbx != -1) {
-               printk("sunsu_probe: cannot match keyboard and mouse, confused\n");
-               return -ENODEV;
+static int num_uart;
+
+static int __init sunsu_init(void)
+{
+       struct device_node *dp;
+       int err;
+
+       num_uart = 0;
+       for_each_node_by_name(dp, "su") {
+               if (su_get_type(dp) == SU_PORT_PORT)
+                       num_uart++;
+       }
+       for_each_node_by_name(dp, "su_pnp") {
+               if (su_get_type(dp) == SU_PORT_PORT)
+                       num_uart++;
+       }
+       for_each_node_by_name(dp, "serial") {
+               if (of_device_is_compatible(dp, "su")) {
+                       if (su_get_type(dp) == SU_PORT_PORT)
+                               num_uart++;
+               }
        }
 
-       if (scan.devices == 0)
-               return -ENODEV;
+       if (num_uart) {
+               sunsu_reg.minor = sunserial_current_minor;
+               sunsu_reg.nr = num_uart;
+               err = uart_register_driver(&sunsu_reg);
+               if (err)
+                       return err;
+               sunsu_reg.tty_driver->name_base = sunsu_reg.minor - 64;
+               sunserial_current_minor += num_uart;
+               sunsu_reg.cons = SUNSU_CONSOLE(num_uart);
+       }
 
-       /*
-        * Console must be initiated after the generic initialization.
-        */
-               sunsu_serial_init();
-       sunsu_serial_console_init();
+       err = of_register_driver(&su_driver, &of_bus_type);
+       if (err && num_uart)
+               uart_unregister_driver(&sunsu_reg);
 
-       return 0;
+       return err;
 }
 
 static void __exit sunsu_exit(void)
 {
-       int i, saw_uart;
-
-       saw_uart = 0;
-       for (i = 0; i < UART_NR; i++) {
-               struct uart_sunsu_port *up = &sunsu_ports[i];
-
-               if (up->su_type == SU_PORT_MS ||
-                   up->su_type == SU_PORT_KBD) {
-#ifdef CONFIG_SERIO
-                       serio_unregister_port(&up->serio);
-#endif
-               } else if (up->port.type != PORT_UNKNOWN) {
-                       uart_remove_one_port(&sunsu_reg, &up->port);
-                       saw_uart++;
-               }
-       }
-
-       if (saw_uart)
+       if (num_uart)
                uart_unregister_driver(&sunsu_reg);
 }
 
-module_init(sunsu_probe);
+module_init(sunsu_init);
 module_exit(sunsu_exit);
+
+MODULE_AUTHOR("Eddie C. Dost, Peter Zaitcev, and David S. Miller");
+MODULE_DESCRIPTION("Sun SU serial port driver");
+MODULE_VERSION("2.0");
+MODULE_LICENSE("GPL");