linux 2.6.16.38 w/ vs2.0.3-rc1
[linux-2.6.git] / drivers / serial / serial_txx9.c
index b361669..ee98a86 100644 (file)
  *     1.02    Cleanup. (import 8250.c changes)
  *     1.03    Fix low-latency mode. (import 8250.c changes)
  *     1.04    Remove usage of deprecated functions, cleanup.
- *     1.05    More strict check in verify_port.  Cleanup.
- *     1.06    Do not insert a char caused previous overrun.
- *             Fix some spin_locks.
- *             Do not call uart_add_one_port for absent ports.
  */
+#include <linux/config.h>
 
 #if defined(CONFIG_SERIAL_TXX9_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ)
 #define SUPPORT_SYSRQ
@@ -60,7 +57,7 @@
 #include <asm/io.h>
 #include <asm/irq.h>
 
-static char *serial_version = "1.06";
+static char *serial_version = "1.04";
 static char *serial_name = "TX39/49 Serial driver";
 
 #define PASS_LIMIT     256
@@ -68,10 +65,12 @@ static char *serial_name = "TX39/49 Serial driver";
 #if !defined(CONFIG_SERIAL_TXX9_STDSERIAL)
 /* "ttyS" is used for standard serial driver */
 #define TXX9_TTY_NAME "ttyTX"
+#define TXX9_TTY_DEVFS_NAME "tttx/"
 #define TXX9_TTY_MINOR_START   (64 + 64)       /* ttyTX0(128), ttyTX1(129) */
 #else
 /* acts like standard serial driver */
 #define TXX9_TTY_NAME "ttyS"
+#define TXX9_TTY_DEVFS_NAME "tts/"
 #define TXX9_TTY_MINOR_START   64
 #endif
 #define TXX9_TTY_MAJOR TTY_MAJOR
@@ -95,8 +94,6 @@ static char *serial_name = "TX39/49 Serial driver";
 #define UART_NR  4
 #endif
 
-#define HIGH_BITS_OFFSET       ((sizeof(long)-sizeof(int))*8)
-
 struct uart_txx9_port {
        struct uart_port        port;
 
@@ -213,7 +210,7 @@ static inline unsigned int sio_in(struct uart_txx9_port *up, int offset)
 {
        switch (up->port.iotype) {
        default:
-               return __raw_readl(up->port.membase + offset);
+               return *(volatile u32 *)(up->port.membase + offset);
        case UPIO_PORT:
                return inl(up->port.iobase + offset);
        }
@@ -224,7 +221,7 @@ sio_out(struct uart_txx9_port *up, int offset, int value)
 {
        switch (up->port.iotype) {
        default:
-               __raw_writel(value, up->port.membase + offset);
+               *(volatile u32 *)(up->port.membase + offset) = value;
                break;
        case UPIO_PORT:
                outl(value, up->port.iobase + offset);
@@ -262,19 +259,34 @@ sio_quot_set(struct uart_txx9_port *up, int quot)
 static void serial_txx9_stop_tx(struct uart_port *port)
 {
        struct uart_txx9_port *up = (struct uart_txx9_port *)port;
+       unsigned long flags;
+
+       spin_lock_irqsave(&up->port.lock, flags);
        sio_mask(up, TXX9_SIDICR, TXX9_SIDICR_TIE);
+       spin_unlock_irqrestore(&up->port.lock, flags);
 }
 
 static void serial_txx9_start_tx(struct uart_port *port)
 {
        struct uart_txx9_port *up = (struct uart_txx9_port *)port;
+       unsigned long flags;
+
+       spin_lock_irqsave(&up->port.lock, flags);
        sio_set(up, TXX9_SIDICR, TXX9_SIDICR_TIE);
+       spin_unlock_irqrestore(&up->port.lock, flags);
 }
 
 static void serial_txx9_stop_rx(struct uart_port *port)
 {
        struct uart_txx9_port *up = (struct uart_txx9_port *)port;
+       unsigned long flags;
+
+       spin_lock_irqsave(&up->port.lock, flags);
        up->port.read_status_mask &= ~TXX9_SIDISR_RDIS;
+#if 0
+       sio_mask(up, TXX9_SIDICR, TXX9_SIDICR_RIE);
+#endif
+       spin_unlock_irqrestore(&up->port.lock, flags);
 }
 
 static void serial_txx9_enable_ms(struct uart_port *port)
@@ -290,16 +302,12 @@ receive_chars(struct uart_txx9_port *up, unsigned int *status, struct pt_regs *r
        unsigned int disr = *status;
        int max_count = 256;
        char flag;
-       unsigned int next_ignore_status_mask;
 
        do {
                ch = sio_in(up, TXX9_SIRFIFO);
                flag = TTY_NORMAL;
                up->port.icount.rx++;
 
-               /* mask out RFDN_MASK bit added by previous overrun */
-               next_ignore_status_mask =
-                       up->port.ignore_status_mask & ~TXX9_SIDISR_RFDN_MASK;
                if (unlikely(disr & (TXX9_SIDISR_UBRK | TXX9_SIDISR_UPER |
                                     TXX9_SIDISR_UFER | TXX9_SIDISR_UOER))) {
                        /*
@@ -320,17 +328,8 @@ receive_chars(struct uart_txx9_port *up, unsigned int *status, struct pt_regs *r
                                up->port.icount.parity++;
                        else if (disr & TXX9_SIDISR_UFER)
                                up->port.icount.frame++;
-                       if (disr & TXX9_SIDISR_UOER) {
+                       if (disr & TXX9_SIDISR_UOER)
                                up->port.icount.overrun++;
-                               /*
-                                * The receiver read buffer still hold
-                                * a char which caused overrun.
-                                * Ignore next char by adding RFDN_MASK
-                                * to ignore_status_mask temporarily.
-                                */
-                               next_ignore_status_mask |=
-                                       TXX9_SIDISR_RFDN_MASK;
-                       }
 
                        /*
                         * Mask off conditions which should be ingored.
@@ -350,7 +349,6 @@ receive_chars(struct uart_txx9_port *up, unsigned int *status, struct pt_regs *r
                uart_insert_char(&up->port, disr, TXX9_SIDISR_UOER, ch, flag);
 
        ignore_char:
-               up->port.ignore_status_mask = next_ignore_status_mask;
                disr = sio_in(up, TXX9_SIDISR);
        } while (!(disr & TXX9_SIDISR_UVALID) && (max_count-- > 0));
        spin_unlock(&up->port.lock);
@@ -452,11 +450,14 @@ static unsigned int serial_txx9_get_mctrl(struct uart_port *port)
 static void serial_txx9_set_mctrl(struct uart_port *port, unsigned int mctrl)
 {
        struct uart_txx9_port *up = (struct uart_txx9_port *)port;
+       unsigned long flags;
 
+       spin_lock_irqsave(&up->port.lock, flags);
        if (mctrl & TIOCM_RTS)
                sio_mask(up, TXX9_SIFLCR, TXX9_SIFLCR_RTSSC);
        else
                sio_set(up, TXX9_SIFLCR, TXX9_SIFLCR_RTSSC);
+       spin_unlock_irqrestore(&up->port.lock, flags);
 }
 
 static void serial_txx9_break_ctl(struct uart_port *port, int break_state)
@@ -480,7 +481,7 @@ static int serial_txx9_startup(struct uart_port *port)
 
        /*
         * Clear the FIFO buffers and disable them.
-        * (they will be reenabled in set_termios())
+        * (they will be reeanbled in set_termios())
         */
        sio_set(up, TXX9_SIFCR,
                TXX9_SIFCR_TFRST | TXX9_SIFCR_RFRST | TXX9_SIFCR_FRSTE);
@@ -495,7 +496,7 @@ static int serial_txx9_startup(struct uart_port *port)
        sio_out(up, TXX9_SIDISR, 0);
 
        retval = request_irq(up->port.irq, serial_txx9_interrupt,
-                            IRQF_SHARED, "serial_txx9", up);
+                            SA_SHIRQ, "serial_txx9", up);
        if (retval)
                return retval;
 
@@ -783,14 +784,8 @@ static void serial_txx9_config_port(struct uart_port *port, int uflags)
 static int
 serial_txx9_verify_port(struct uart_port *port, struct serial_struct *ser)
 {
-       unsigned long new_port = ser->port;
-       if (HIGH_BITS_OFFSET)
-               new_port += (unsigned long)ser->port_high << HIGH_BITS_OFFSET;
-       if (ser->type != port->type ||
-           ser->irq != port->irq ||
-           ser->io_type != port->iotype ||
-           new_port != port->iobase ||
-           (unsigned long)ser->iomem_base != port->mapbase)
+       if (ser->irq < 0 ||
+           ser->baud_base < 9600 || ser->type != PORT_TXX9)
                return -EINVAL;
        return 0;
 }
@@ -832,8 +827,7 @@ static void __init serial_txx9_register_ports(struct uart_driver *drv)
 
                up->port.line = i;
                up->port.ops = &serial_txx9_pops;
-               if (up->port.iobase || up->port.mapbase)
-                       uart_add_one_port(drv, &up->port);
+               uart_add_one_port(drv, &up->port);
        }
 }
 
@@ -860,14 +854,6 @@ static inline void wait_for_xmitr(struct uart_txx9_port *up)
        }
 }
 
-static void serial_txx9_console_putchar(struct uart_port *port, int ch)
-{
-       struct uart_txx9_port *up = (struct uart_txx9_port *)port;
-
-       wait_for_xmitr(up);
-       sio_out(up, TXX9_SITFIFO, ch);
-}
-
 /*
  *     Print a string to the serial port trying not to disturb
  *     any possible real use of the port...
@@ -879,6 +865,7 @@ serial_txx9_console_write(struct console *co, const char *s, unsigned int count)
 {
        struct uart_txx9_port *up = &serial_txx9_ports[co->index];
        unsigned int ier, flcr;
+       int i;
 
        /*
         *      First save the UER then disable the interrupts
@@ -892,7 +879,22 @@ serial_txx9_console_write(struct console *co, const char *s, unsigned int count)
        if (!(up->port.flags & UPF_CONS_FLOW) && (flcr & TXX9_SIFLCR_TES))
                sio_out(up, TXX9_SIFLCR, flcr & ~TXX9_SIFLCR_TES);
 
-       uart_console_write(&up->port, s, count, serial_txx9_console_putchar);
+       /*
+        *      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...
+                */
+               sio_out(up, TXX9_SITFIFO, *s);
+               if (*s == 10) {
+                       wait_for_xmitr(up);
+                       sio_out(up, TXX9_SITFIFO, 13);
+               }
+       }
 
        /*
         *      Finally, wait for transmitter to become empty
@@ -924,6 +926,11 @@ static int serial_txx9_console_setup(struct console *co, char *options)
        if (!port->ops)
                return -ENODEV;
 
+       /*
+        * Temporary fix.
+        */
+       spin_lock_init(&port->lock);
+
        /*
         *      Disable UART interrupts, set DTR and RTS high
         *      and set speed.
@@ -968,6 +975,7 @@ console_initcall(serial_txx9_console_init);
 static struct uart_driver serial_txx9_reg = {
        .owner                  = THIS_MODULE,
        .driver_name            = "serial_txx9",
+       .devfs_name             = TXX9_TTY_DEVFS_NAME,
        .dev_name               = TXX9_TTY_NAME,
        .major                  = TXX9_TTY_MAJOR,
        .minor                  = TXX9_TTY_MINOR_START,
@@ -1033,10 +1041,11 @@ static int __devinit serial_txx9_register_port(struct uart_port *port)
        mutex_lock(&serial_txx9_mutex);
        for (i = 0; i < UART_NR; i++) {
                uart = &serial_txx9_ports[i];
-               if (!(uart->port.iobase || uart->port.mapbase))
+               if (uart->port.type == PORT_UNKNOWN)
                        break;
        }
        if (i < UART_NR) {
+               uart_remove_one_port(&serial_txx9_reg, &uart->port);
                uart->port.iobase = port->iobase;
                uart->port.membase = port->membase;
                uart->port.irq      = port->irq;
@@ -1071,8 +1080,9 @@ static void __devexit serial_txx9_unregister_port(int line)
        uart->port.type = PORT_UNKNOWN;
        uart->port.iobase = 0;
        uart->port.mapbase = 0;
-       uart->port.membase = NULL;
+       uart->port.membase = 0;
        uart->port.dev = NULL;
+       uart_add_one_port(&serial_txx9_reg, &uart->port);
        mutex_unlock(&serial_txx9_mutex);
 }
 
@@ -1188,11 +1198,8 @@ static void __exit serial_txx9_exit(void)
 #ifdef ENABLE_SERIAL_TXX9_PCI
        pci_unregister_driver(&serial_txx9_pci_driver);
 #endif
-       for (i = 0; i < UART_NR; i++) {
-               struct uart_txx9_port *up = &serial_txx9_ports[i];
-               if (up->port.iobase || up->port.mapbase)
-                       uart_remove_one_port(&serial_txx9_reg, &up->port);
-       }
+       for (i = 0; i < UART_NR; i++)
+               uart_remove_one_port(&serial_txx9_reg, &serial_txx9_ports[i].port);
 
        uart_unregister_driver(&serial_txx9_reg);
 }