Merge to Fedora kernel-2.6.18-1.2224_FC5 patched with stable patch-2.6.18.1-vs2.0...
[linux-2.6.git] / drivers / serial / 8250.c
index fbb53ad..fd8b94e 100644 (file)
@@ -7,6 +7,9 @@
  *
  *  Copyright (C) 2001 Russell King.
  *
+ *  2005/09/16: Enabled higher baud rates for 16C95x.
+ *             (Mathias Adam <a2@adamis.de>)
+ *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
  * the Free Software Foundation; either version 2 of the License, or
@@ -19,7 +22,6 @@
  *  mapbase is the physical address of the IO port.
  *  membase is an 'ioremapped' cookie.
  */
-#include <linux/config.h>
 
 #if defined(CONFIG_SERIAL_8250_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ)
 #define SUPPORT_SYSRQ
@@ -49,7 +51,7 @@
 
 /*
  * Configuration:
- *   share_irqs - whether we pass SA_SHIRQ to request_irq().  This option
+ *   share_irqs - whether we pass IRQF_SHARED to request_irq().  This option
  *                is unsafe when used on edge-triggered interrupts.
  */
 static unsigned int share_irqs = SERIAL8250_SHARE_IRQS;
@@ -300,6 +302,7 @@ static inline int map_8250_out_reg(struct uart_8250_port *up, int offset)
 
 static unsigned int serial_in(struct uart_8250_port *up, int offset)
 {
+       unsigned int tmp;
        offset = map_8250_in_reg(up, offset) << up->port.regshift;
 
        switch (up->port.iotype) {
@@ -318,6 +321,13 @@ static unsigned int serial_in(struct uart_8250_port *up, int offset)
                return __raw_readl(up->port.membase + offset);
 #endif
 
+       case UPIO_TSI:
+               if (offset == UART_IIR) {
+                       tmp = readl((u32 *)(up->port.membase + UART_RX));
+                       return (cpu_to_le32(tmp) >> 8) & 0xff;
+               } else
+                       return readb(up->port.membase + offset);
+
        default:
                return inb(up->port.iobase + offset);
        }
@@ -347,6 +357,10 @@ serial_out(struct uart_8250_port *up, int offset, int value)
                __raw_writel(value, up->port.membase + offset);
                break;
 #endif
+       case UPIO_TSI:
+               if (!((offset == UART_IER) && (value & UART_IER_UUE)))
+                       writeb(value, up->port.membase + offset);
+               break;
 
        default:
                outb(value, up->port.iobase + offset);
@@ -1401,7 +1415,7 @@ static void serial_do_unlink(struct irq_info *i, struct uart_8250_port *up)
 static int serial_link_irq_chain(struct uart_8250_port *up)
 {
        struct irq_info *i = irq_lists + up->port.irq;
-       int ret, irq_flags = up->port.flags & UPF_SHARE_IRQ ? SA_SHIRQ : 0;
+       int ret, irq_flags = up->port.flags & UPF_SHARE_IRQ ? IRQF_SHARED : 0;
 
        spin_lock_irq(&i->lock);
 
@@ -1745,6 +1759,14 @@ static unsigned int serial8250_get_divisor(struct uart_port *port, unsigned int
        else if ((port->flags & UPF_MAGIC_MULTIPLIER) &&
                 baud == (port->uartclk/8))
                quot = 0x8002;
+       /*
+        * For 16C950s UART_TCR is used in combination with divisor==1
+        * to achieve baud rates up to baud_base*4.
+        */
+       else if ((port->type == PORT_16C950) &&
+                baud > (port->uartclk/16))
+               quot = 1;
+
        else
                quot = uart_get_divisor(port, baud);
 
@@ -1758,7 +1780,7 @@ serial8250_set_termios(struct uart_port *port, struct termios *termios,
        struct uart_8250_port *up = (struct uart_8250_port *)port;
        unsigned char cval, fcr = 0;
        unsigned long flags;
-       unsigned int baud, quot;
+       unsigned int baud, quot, max_baud;
 
        switch (termios->c_cflag & CSIZE) {
        case CS5:
@@ -1790,7 +1812,8 @@ serial8250_set_termios(struct uart_port *port, struct termios *termios,
        /*
         * Ask the core to calculate the divisor for us.
         */
-       baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk/16); 
+       max_baud = (up->port.type == PORT_16C950 ? port->uartclk/4 : port->uartclk/16);
+       baud = uart_get_baud_rate(port, termios, old, 0, max_baud); 
        quot = serial8250_get_divisor(port, baud);
 
        /*
@@ -1826,6 +1849,19 @@ serial8250_set_termios(struct uart_port *port, struct termios *termios,
         */
        spin_lock_irqsave(&up->port.lock, flags);
 
+       /* 
+        * 16C950 supports additional prescaler ratios between 1:16 and 1:4
+        * thus increasing max baud rate to uartclk/4.
+        */
+       if (up->port.type == PORT_16C950) {
+               if (baud == port->uartclk/4)
+                       serial_icr_write(up, UART_TCR, 0x4);
+               else if (baud == port->uartclk/8)
+                       serial_icr_write(up, UART_TCR, 0x8);
+               else
+                       serial_icr_write(up, UART_TCR, 0);
+       }
+       
        /*
         * Update the per-port timeout.
         */
@@ -2243,10 +2279,14 @@ serial8250_console_write(struct console *co, const char *s, unsigned int count)
 
        touch_nmi_watchdog();
 
-       if (oops_in_progress) {
-               locked = spin_trylock_irqsave(&up->port.lock, flags);
+       local_irq_save(flags);
+       if (up->port.sysrq) {
+               /* serial8250_handle_port() already took the lock */
+               locked = 0;
+       } else if (oops_in_progress) {
+               locked = spin_trylock(&up->port.lock);
        } else
-               spin_lock_irqsave(&up->port.lock, flags);
+               spin_lock(&up->port.lock);
 
        /*
         *      First save the IER then disable the interrupts
@@ -2268,7 +2308,8 @@ serial8250_console_write(struct console *co, const char *s, unsigned int count)
        serial_out(up, UART_IER, ier);
 
        if (locked)
-               spin_unlock_irqrestore(&up->port.lock, flags);
+               spin_unlock(&up->port.lock);
+       local_irq_restore(flags);
 }
 
 static int serial8250_console_setup(struct console *co, char *options)
@@ -2356,7 +2397,6 @@ int __init serial8250_start_console(struct uart_port *port, char *options)
 static struct uart_driver serial8250_reg = {
        .owner                  = THIS_MODULE,
        .driver_name            = "serial",
-       .devfs_name             = "tts/",
        .dev_name               = "ttyS",
        .major                  = TTY_MAJOR,
        .minor                  = 64,