Add Station and AdHoc mode support to libertas_tf
[safe/jmp/linux-2.6] / drivers / serial / ip22zilog.c
index 193722d..ebff4a1 100644 (file)
  *  Copyright (C) 2002 Ralf Baechle (ralf@linux-mips.org)
  *  Copyright (C) 2002 David S. Miller (davem@redhat.com)
  */
-#include <linux/config.h>
 #include <linux/module.h>
 #include <linux/kernel.h>
-#include <linux/sched.h>
 #include <linux/errno.h>
 #include <linux/delay.h>
 #include <linux/tty.h>
@@ -47,8 +45,6 @@
 
 #include "ip22zilog.h"
 
-void ip22_do_break(void);
-
 /*
  * On IP22 we need to delay after register accesses but we do not need to
  * flush writes.
@@ -83,12 +79,9 @@ struct uart_ip22zilog_port {
 #define IP22ZILOG_FLAG_REGS_HELD       0x00000040
 #define IP22ZILOG_FLAG_TX_STOPPED      0x00000080
 #define IP22ZILOG_FLAG_TX_ACTIVE       0x00000100
+#define IP22ZILOG_FLAG_RESET_DONE      0x00000200
 
-       unsigned int                    cflag;
-
-       /* L1-A keyboard break state.  */
-       int                             kbd_id;
-       int                             l1_down;
+       unsigned int                    tty_break;
 
        unsigned char                   parity_mask;
        unsigned char                   prev_status;
@@ -252,14 +245,26 @@ static void ip22zilog_maybe_update_regs(struct uart_ip22zilog_port *up,
        }
 }
 
-static void ip22zilog_receive_chars(struct uart_ip22zilog_port *up,
-                                  struct zilog_channel *channel,
-                                  struct pt_regs *regs)
+#define Rx_BRK 0x0100                   /* BREAK event software flag.  */
+#define Rx_SYS 0x0200                   /* SysRq event software flag.  */
+
+static struct tty_struct *ip22zilog_receive_chars(struct uart_ip22zilog_port *up,
+                                                 struct zilog_channel *channel)
 {
-       struct tty_struct *tty = up->port.info->tty;    /* XXX info==NULL? */
+       struct tty_struct *tty;
+       unsigned char ch, flag;
+       unsigned int r1;
 
-       while (1) {
-               unsigned char ch, r1, flag;
+       tty = NULL;
+       if (up->port.state != NULL &&
+           up->port.state->port.tty != NULL)
+               tty = up->port.state->port.tty;
+
+       for (;;) {
+               ch = readb(&channel->control);
+               ZSDELAY();
+               if (!(ch & Rx_CH_AV))
+                       break;
 
                r1 = read_zsreg(channel, R1);
                if (r1 & (PAR_ERR | Rx_OVR | CRC_ERR)) {
@@ -268,43 +273,26 @@ static void ip22zilog_receive_chars(struct uart_ip22zilog_port *up,
                        ZS_WSYNC(channel);
                }
 
-               ch = readb(&channel->control);
-               ZSDELAY();
-
-               /* This funny hack depends upon BRK_ABRT not interfering
-                * with the other bits we care about in R1.
-                */
-               if (ch & BRK_ABRT)
-                       r1 |= BRK_ABRT;
-
                ch = readb(&channel->data);
                ZSDELAY();
 
                ch &= up->parity_mask;
 
-               if (ZS_IS_CONS(up) && (r1 & BRK_ABRT)) {
-                       /* Wait for BREAK to deassert to avoid potentially
-                        * confusing the PROM.
-                        */
-                       while (1) {
-                               ch = readb(&channel->control);
-                               ZSDELAY();
-                               if (!(ch & BRK_ABRT))
-                                       break;
-                       }
-                       ip22_do_break();
-                       return;
-               }
+               /* Handle the null char got when BREAK is removed.  */
+               if (!ch)
+                       r1 |= up->tty_break;
 
                /* A real serial line, record the character and status.  */
                flag = TTY_NORMAL;
                up->port.icount.rx++;
-               if (r1 & (BRK_ABRT | PAR_ERR | Rx_OVR | CRC_ERR)) {
-                       if (r1 & BRK_ABRT) {
-                               r1 &= ~(PAR_ERR | CRC_ERR);
+               if (r1 & (PAR_ERR | Rx_OVR | CRC_ERR | Rx_SYS | Rx_BRK)) {
+                       up->tty_break = 0;
+
+                       if (r1 & (Rx_SYS | Rx_BRK)) {
                                up->port.icount.brk++;
-                               if (uart_handle_break(&up->port))
-                                       goto next_char;
+                               if (r1 & Rx_SYS)
+                                       continue;
+                               r1 &= ~(PAR_ERR | CRC_ERR);
                        }
                        else if (r1 & PAR_ERR)
                                up->port.icount.parity++;
@@ -313,35 +301,25 @@ static void ip22zilog_receive_chars(struct uart_ip22zilog_port *up,
                        if (r1 & Rx_OVR)
                                up->port.icount.overrun++;
                        r1 &= up->port.read_status_mask;
-                       if (r1 & BRK_ABRT)
+                       if (r1 & Rx_BRK)
                                flag = TTY_BREAK;
                        else if (r1 & PAR_ERR)
                                flag = TTY_PARITY;
                        else if (r1 & CRC_ERR)
                                flag = TTY_FRAME;
                }
-               if (uart_handle_sysrq_char(&up->port, ch, regs))
-                       goto next_char;
 
-               if (up->port.ignore_status_mask == 0xff ||
-                   (r1 & up->port.ignore_status_mask) == 0)
-                       tty_insert_flip_char(tty, ch, flag);
+               if (uart_handle_sysrq_char(&up->port, ch))
+                       continue;
 
-               if (r1 & Rx_OVR)
-                       tty_insert_flip_char(tty, 0, TTY_OVERRUN);
-       next_char:
-               ch = readb(&channel->control);
-               ZSDELAY();
-               if (!(ch & Rx_CH_AV))
-                       break;
+               if (tty)
+                       uart_insert_char(&up->port, r1, Rx_OVR, ch, flag);
        }
-
-       tty_flip_buffer_push(tty);
+       return tty;
 }
 
 static void ip22zilog_status_handle(struct uart_ip22zilog_port *up,
-                                  struct zilog_channel *channel,
-                                  struct pt_regs *regs)
+                                  struct zilog_channel *channel)
 {
        unsigned char status;
 
@@ -352,6 +330,15 @@ static void ip22zilog_status_handle(struct uart_ip22zilog_port *up,
        ZSDELAY();
        ZS_WSYNC(channel);
 
+       if (up->curregs[R15] & BRKIE) {
+               if ((status & BRK_ABRT) && !(up->prev_status & BRK_ABRT)) {
+                       if (uart_handle_break(&up->port))
+                               up->tty_break = Rx_SYS;
+                       else
+                               up->tty_break = Rx_BRK;
+               }
+       }
+
        if (ZS_WANTS_MODEM_STATUS(up)) {
                if (status & SYNC)
                        up->port.icount.dsr++;
@@ -360,14 +347,14 @@ static void ip22zilog_status_handle(struct uart_ip22zilog_port *up,
                 * But it does not tell us which bit has changed, we have to keep
                 * track of this ourselves.
                 */
-               if ((status & DCD) ^ up->prev_status)
+               if ((status ^ up->prev_status) ^ DCD)
                        uart_handle_dcd_change(&up->port,
                                               (status & DCD));
-               if ((status & CTS) ^ up->prev_status)
+               if ((status ^ up->prev_status) ^ CTS)
                        uart_handle_cts_change(&up->port,
                                               (status & CTS));
 
-               wake_up_interruptible(&up->port.info->delta_msr_wait);
+               wake_up_interruptible(&up->port.state->port.delta_msr_wait);
        }
 
        up->prev_status = status;
@@ -417,9 +404,9 @@ static void ip22zilog_transmit_chars(struct uart_ip22zilog_port *up,
                return;
        }
 
-       if (up->port.info == NULL)
+       if (up->port.state == NULL)
                goto ack_tx_int;
-       xmit = &up->port.info->xmit;
+       xmit = &up->port.state->xmit;
        if (uart_circ_empty(xmit))
                goto ack_tx_int;
        if (uart_tx_stopped(&up->port))
@@ -444,52 +431,61 @@ ack_tx_int:
        ZS_WSYNC(channel);
 }
 
-static irqreturn_t ip22zilog_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+static irqreturn_t ip22zilog_interrupt(int irq, void *dev_id)
 {
        struct uart_ip22zilog_port *up = dev_id;
 
        while (up) {
                struct zilog_channel *channel
                        = ZILOG_CHANNEL_FROM_PORT(&up->port);
+               struct tty_struct *tty;
                unsigned char r3;
 
                spin_lock(&up->port.lock);
                r3 = read_zsreg(channel, R3);
 
                /* Channel A */
+               tty = NULL;
                if (r3 & (CHAEXT | CHATxIP | CHARxIP)) {
                        writeb(RES_H_IUS, &channel->control);
                        ZSDELAY();
                        ZS_WSYNC(channel);
 
                        if (r3 & CHARxIP)
-                               ip22zilog_receive_chars(up, channel, regs);
+                               tty = ip22zilog_receive_chars(up, channel);
                        if (r3 & CHAEXT)
-                               ip22zilog_status_handle(up, channel, regs);
+                               ip22zilog_status_handle(up, channel);
                        if (r3 & CHATxIP)
                                ip22zilog_transmit_chars(up, channel);
                }
                spin_unlock(&up->port.lock);
 
+               if (tty)
+                       tty_flip_buffer_push(tty);
+
                /* Channel B */
                up = up->next;
                channel = ZILOG_CHANNEL_FROM_PORT(&up->port);
 
                spin_lock(&up->port.lock);
+               tty = NULL;
                if (r3 & (CHBEXT | CHBTxIP | CHBRxIP)) {
                        writeb(RES_H_IUS, &channel->control);
                        ZSDELAY();
                        ZS_WSYNC(channel);
 
                        if (r3 & CHBRxIP)
-                               ip22zilog_receive_chars(up, channel, regs);
+                               tty = ip22zilog_receive_chars(up, channel);
                        if (r3 & CHBEXT)
-                               ip22zilog_status_handle(up, channel, regs);
+                               ip22zilog_status_handle(up, channel);
                        if (r3 & CHBTxIP)
                                ip22zilog_transmit_chars(up, channel);
                }
                spin_unlock(&up->port.lock);
 
+               if (tty)
+                       tty_flip_buffer_push(tty);
+
                up = up->next;
        }
 
@@ -611,7 +607,7 @@ static void ip22zilog_start_tx(struct uart_port *port)
                port->icount.tx++;
                port->x_char = 0;
        } else {
-               struct circ_buf *xmit = &port->info->xmit;
+               struct circ_buf *xmit = &port->state->xmit;
 
                writeb(xmit->buf[xmit->tail], &channel->data);
                ZSDELAY();
@@ -685,11 +681,46 @@ static void ip22zilog_break_ctl(struct uart_port *port, int break_state)
        spin_unlock_irqrestore(&port->lock, flags);
 }
 
+static void __ip22zilog_reset(struct uart_ip22zilog_port *up)
+{
+       struct zilog_channel *channel;
+       int i;
+
+       if (up->flags & IP22ZILOG_FLAG_RESET_DONE)
+               return;
+
+       /* Let pending transmits finish.  */
+       channel = ZILOG_CHANNEL_FROM_PORT(&up->port);
+       for (i = 0; i < 1000; i++) {
+               unsigned char stat = read_zsreg(channel, R1);
+               if (stat & ALL_SNT)
+                       break;
+               udelay(100);
+       }
+
+       if (!ZS_IS_CHANNEL_A(up)) {
+               up++;
+               channel = ZILOG_CHANNEL_FROM_PORT(&up->port);
+       }
+       write_zsreg(channel, R9, FHWRES);
+       ZSDELAY_LONG();
+       (void) read_zsreg(channel, R0);
+
+       up->flags |= IP22ZILOG_FLAG_RESET_DONE;
+       up->next->flags |= IP22ZILOG_FLAG_RESET_DONE;
+}
+
 static void __ip22zilog_startup(struct uart_ip22zilog_port *up)
 {
        struct zilog_channel *channel;
 
        channel = ZILOG_CHANNEL_FROM_PORT(&up->port);
+
+       __ip22zilog_reset(up);
+
+       __load_zsregs(channel, up->curregs);
+       /* set master interrupt enable */
+       write_zsreg(channel, R9, up->curregs[R9]);
        up->prev_status = readb(&channel->control);
 
        /* Enable receiver and transmitter.  */
@@ -843,8 +874,8 @@ ip22zilog_convert_to_zs(struct uart_ip22zilog_port *up, unsigned int cflag,
 
 /* The port lock is not held.  */
 static void
-ip22zilog_set_termios(struct uart_port *port, struct termios *termios,
-                     struct termios *old)
+ip22zilog_set_termios(struct uart_port *port, struct ktermios *termios,
+                     struct ktermios *old)
 {
        struct uart_ip22zilog_port *up = (struct uart_ip22zilog_port *) port;
        unsigned long flags;
@@ -863,9 +894,8 @@ ip22zilog_set_termios(struct uart_port *port, struct termios *termios,
        else
                up->flags &= ~IP22ZILOG_FLAG_MODEM_STATUS;
 
-       up->cflag = termios->c_cflag;
-
        ip22zilog_maybe_update_regs(up, ZILOG_CHANNEL_FROM_PORT(port));
+       uart_update_timeout(port, termios->c_cflag, baud);
 
        spin_unlock_irqrestore(&up->port.lock, flags);
 }
@@ -925,13 +955,7 @@ static int zilog_irq = -1;
 
 static void * __init alloc_one_table(unsigned long size)
 {
-       void *ret;
-
-       ret = kmalloc(size, GFP_KERNEL);
-       if (ret != NULL)
-               memset(ret, 0, size);
-
-       return ret;
+       return kzalloc(size, GFP_KERNEL);
 }
 
 static void __init ip22zilog_alloc_tables(void)
@@ -967,8 +991,9 @@ static struct zilog_layout * __init get_zs(int chip)
 #define ZS_PUT_CHAR_MAX_DELAY  2000    /* 10 ms */
 
 #ifdef CONFIG_SERIAL_IP22_ZILOG_CONSOLE
-static void ip22zilog_put_char(struct zilog_channel *channel, unsigned char ch)
+static void ip22zilog_put_char(struct uart_port *port, int ch)
 {
+       struct zilog_channel *channel = ZILOG_CHANNEL_FROM_PORT(port);
        int loops = ZS_PUT_CHAR_MAX_DELAY;
 
        /* This is a timed polling loop so do not switch the explicit
@@ -992,86 +1017,37 @@ static void
 ip22zilog_console_write(struct console *con, const char *s, unsigned int count)
 {
        struct uart_ip22zilog_port *up = &ip22zilog_port_table[con->index];
-       struct zilog_channel *channel = ZILOG_CHANNEL_FROM_PORT(&up->port);
        unsigned long flags;
-       int i;
 
        spin_lock_irqsave(&up->port.lock, flags);
-       for (i = 0; i < count; i++, s++) {
-               ip22zilog_put_char(channel, *s);
-               if (*s == 10)
-                       ip22zilog_put_char(channel, 13);
-       }
+       uart_console_write(&up->port, s, count, ip22zilog_put_char);
        udelay(2);
        spin_unlock_irqrestore(&up->port.lock, flags);
 }
 
-void
-ip22serial_console_termios(struct console *con, char *options)
-{
-       int baud = 9600, bits = 8, cflag;
-       int parity = 'n';
-       int flow = 'n';
-
-       if (options)
-               uart_parse_options(options, &baud, &parity, &bits, &flow);
-
-       cflag = CREAD | HUPCL | CLOCAL;
-
-       switch (baud) {
-               case 150: cflag |= B150; break;
-               case 300: cflag |= B300; break;
-               case 600: cflag |= B600; break;
-               case 1200: cflag |= B1200; break;
-               case 2400: cflag |= B2400; break;
-               case 4800: cflag |= B4800; break;
-               case 9600: cflag |= B9600; break;
-               case 19200: cflag |= B19200; break;
-               case 38400: cflag |= B38400; break;
-               default: baud = 9600; cflag |= B9600; break;
-       }
-
-       con->cflag = cflag | CS8;                       /* 8N1 */
-}
-
 static int __init ip22zilog_console_setup(struct console *con, char *options)
 {
        struct uart_ip22zilog_port *up = &ip22zilog_port_table[con->index];
        unsigned long flags;
-       int baud, brg;
-
-       printk("Console: ttyS%d (IP22-Zilog)\n", con->index);
-
-       /* Get firmware console settings.  */
-       ip22serial_console_termios(con, options);
+       int baud = 9600, bits = 8;
+       int parity = 'n';
+       int flow = 'n';
 
-       /* Firmware console speed is limited to 150-->38400 baud so
-        * this hackish cflag thing is OK.
-        */
-       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;
-       };
+       up->flags |= IP22ZILOG_FLAG_IS_CONS;
 
-       brg = BPS_TO_BRG(baud, ZS_CLOCK / ZS_CLOCK_DIVISOR);
+       printk(KERN_INFO "Console: ttyS%d (IP22-Zilog)\n", con->index);
 
        spin_lock_irqsave(&up->port.lock, flags);
 
-       up->curregs[R15] = BRKIE;
-       ip22zilog_convert_to_zs(up, con->cflag, 0, brg);
+       up->curregs[R15] |= BRKIE;
 
        __ip22zilog_startup(up);
 
        spin_unlock_irqrestore(&up->port.lock, flags);
 
-       return 0;
+       if (options)
+               uart_parse_options(options, &baud, &parity, &bits, &flow);
+       return uart_set_options(&up->port, con, baud, parity, bits, flow);
 }
 
 static struct uart_driver ip22zilog_reg;
@@ -1090,7 +1066,6 @@ static struct console ip22zilog_console = {
 static struct uart_driver ip22zilog_reg = {
        .owner          = THIS_MODULE,
        .driver_name    = "serial",
-       .devfs_name     = "tts/",
        .dev_name       = "ttyS",
        .major          = TTY_MAJOR,
        .minor          = 64,
@@ -1150,29 +1125,13 @@ static void __init ip22zilog_prepare(void)
                up[(chip * 2) + 1].port.fifosize = 1;
                up[(chip * 2) + 1].port.ops = &ip22zilog_pops;
                up[(chip * 2) + 1].port.type = PORT_IP22ZILOG;
-               up[(chip * 2) + 1].port.flags |= IP22ZILOG_FLAG_IS_CHANNEL_A;
                up[(chip * 2) + 1].port.line = (chip * 2) + 1;
-               up[(chip * 2) + 1].flags = 0;
+               up[(chip * 2) + 1].flags |= IP22ZILOG_FLAG_IS_CHANNEL_A;
        }
-}
-
-static void __init ip22zilog_init_hw(void)
-{
-       int i;
 
-       for (i = 0; i < NUM_CHANNELS; i++) {
-               struct uart_ip22zilog_port *up = &ip22zilog_port_table[i];
-               struct zilog_channel *channel = ZILOG_CHANNEL_FROM_PORT(&up->port);
-               unsigned long flags;
-               int baud, brg;
-
-               spin_lock_irqsave(&up->port.lock, flags);
-
-               if (ZS_IS_CHANNEL_A(up)) {
-                       write_zsreg(channel, R9, FHWRES);
-                       ZSDELAY_LONG();
-                       (void) read_zsreg(channel, R0);
-               }
+       for (channel = 0; channel < NUM_CHANNELS; channel++) {
+               struct uart_ip22zilog_port *up = &ip22zilog_port_table[channel];
+               int brg;
 
                /* Normal serial TTY. */
                up->parity_mask = 0xff;
@@ -1183,16 +1142,10 @@ static void __init ip22zilog_init_hw(void)
                up->curregs[R9] = NV | MIE;
                up->curregs[R10] = NRZ;
                up->curregs[R11] = TCBR | RCBR;
-               baud = 9600;
-               brg = BPS_TO_BRG(baud, ZS_CLOCK / ZS_CLOCK_DIVISOR);
+               brg = BPS_TO_BRG(9600, ZS_CLOCK / ZS_CLOCK_DIVISOR);
                up->curregs[R12] = (brg & 0xff);
                up->curregs[R13] = (brg >> 8) & 0xff;
                up->curregs[R14] = BRENAB;
-               __load_zsregs(channel, up->curregs);
-               /* set master interrupt enable */
-               write_zsreg(channel, R9, up->curregs[R9]);
-
-               spin_unlock_irqrestore(&up->port.lock, flags);
        }
 }
 
@@ -1209,8 +1162,6 @@ static int __init ip22zilog_ports_init(void)
                panic("IP22-Zilog: Unable to register zs interrupt handler.\n");
        }
 
-       ip22zilog_init_hw();
-
        ret = uart_register_driver(&ip22zilog_reg);
        if (ret == 0) {
                int i;
@@ -1237,13 +1188,27 @@ static int __init ip22zilog_init(void)
 static void __exit ip22zilog_exit(void)
 {
        int i;
+       struct uart_ip22zilog_port *up;
 
        for (i = 0; i < NUM_CHANNELS; i++) {
-               struct uart_ip22zilog_port *up = &ip22zilog_port_table[i];
+               up = &ip22zilog_port_table[i];
 
                uart_remove_one_port(&ip22zilog_reg, &up->port);
        }
 
+       /* Free IO mem */
+       up = &ip22zilog_port_table[0];
+       for (i = 0; i < NUM_IP22ZILOG; i++) {
+               if (up[(i * 2) + 0].port.mapbase) {
+                  iounmap((void*)up[(i * 2) + 0].port.mapbase);
+                  up[(i * 2) + 0].port.mapbase = 0;
+               }
+               if (up[(i * 2) + 1].port.mapbase) {
+                       iounmap((void*)up[(i * 2) + 1].port.mapbase);
+                       up[(i * 2) + 1].port.mapbase = 0;
+               }
+       }
+
        uart_unregister_driver(&ip22zilog_reg);
 }