]> git.hungrycats.org Git - linux/commitdiff
[SUNZILOG]: Get serial console et al. working once more.
authorPete Zaitcev <zaitcev@redhat.com>
Sun, 12 Jan 2003 16:22:10 +0000 (08:22 -0800)
committerDavid S. Miller <davem@nuts.ninka.net>
Sun, 12 Jan 2003 16:22:10 +0000 (08:22 -0800)
- Make sure R9 is really loaded
- Make sure IS_CONS flag is handled properly.

drivers/serial/sunzilog.c

index 12e48d961d5d36b9a4da222b2d12ccad91c9383a..cb602f2aa5cd12687f9ff7ceddfe241937fbafdb 100644 (file)
@@ -112,10 +112,7 @@ struct uart_sunzilog_port {
 
 #define ZILOG_CHANNEL_FROM_PORT(PORT)  ((struct zilog_channel *)((PORT)->membase))
 #define UART_ZILOG(PORT)               ((struct uart_sunzilog_port *)(PORT))
-#define SUNZILOG_GET_CURR_REG(PORT, REGNUM)            \
-       (UART_ZILOG(PORT)->curregs[REGNUM])
-#define SUNZILOG_SET_CURR_REG(PORT, REGNUM, REGVAL)    \
-       ((UART_ZILOG(PORT)->curregs[REGNUM]) = (REGVAL))
+
 #define ZS_IS_KEYB(UP) ((UP)->flags & SUNZILOG_FLAG_CONS_KEYB)
 #define ZS_IS_MOUSE(UP)        ((UP)->flags & SUNZILOG_FLAG_CONS_MOUSE)
 #define ZS_IS_CONS(UP) ((UP)->flags & SUNZILOG_FLAG_IS_CONS)
@@ -278,8 +275,7 @@ static void sunzilog_change_mouse_baud(struct uart_sunzilog_port *up)
        up->cflag &= ~CBAUD;
        up->cflag |= suncore_mouse_baud_cflag_next(cur_cflag, &new_baud);
 
-       brg = BPS_TO_BRG(new_baud,
-                        (ZS_CLOCK / ZS_CLOCK_DIVISOR));
+       brg = BPS_TO_BRG(new_baud, ZS_CLOCK / ZS_CLOCK_DIVISOR);
        up->curregs[R12] = (brg & 0xff);
        up->curregs[R13] = (brg >> 8) & 0xff;
        sunzilog_maybe_update_regs(up, ZILOG_CHANNEL_FROM_PORT(&up->port));
@@ -332,7 +328,7 @@ static void sunzilog_receive_chars(struct uart_sunzilog_port *up,
                                   struct zilog_channel *channel,
                                   struct pt_regs *regs)
 {
-       struct tty_struct *tty = up->port.info->tty;
+       struct tty_struct *tty = up->port.info->tty;    /* XXX info==NULL? */
 
        while (1) {
                unsigned char ch, r1;
@@ -340,7 +336,7 @@ static void sunzilog_receive_chars(struct uart_sunzilog_port *up,
                if (unlikely(tty->flip.count >= TTY_FLIPBUF_SIZE)) {
                        tty->flip.work.func((void *)tty);
                        if (tty->flip.count >= TTY_FLIPBUF_SIZE)
-                               return;
+                               return;         /* XXX Ignores SysRq when we need it most. Fix. */
                }
 
                r1 = read_zsreg(channel, R1);
@@ -474,7 +470,7 @@ static void sunzilog_status_handle(struct uart_sunzilog_port *up,
 static void sunzilog_transmit_chars(struct uart_sunzilog_port *up,
                                    struct zilog_channel *channel)
 {
-       struct circ_buf *xmit = &up->port.info->xmit;
+       struct circ_buf *xmit;
 
        if (ZS_IS_CONS(up)) {
                unsigned char status = sbus_readb(&channel->control);
@@ -499,7 +495,12 @@ static void sunzilog_transmit_chars(struct uart_sunzilog_port *up,
 
        if (ZS_TX_STOPPED(up)) {
                up->flags &= ~SUNZILOG_FLAG_TX_STOPPED;
-               goto disable_tx_int;
+
+               sbus_writeb(RES_Tx_P, &channel->control);
+               ZSDELAY();
+               ZS_WSYNC(channel);
+
+               return;
        }
 
        if (up->port.x_char) {
@@ -512,8 +513,11 @@ static void sunzilog_transmit_chars(struct uart_sunzilog_port *up,
                return;
        }
 
+       if (up->port.info == NULL)
+               goto ack_tx_int;
+       xmit = &up->port.info->xmit;
        if (uart_circ_empty(xmit) || uart_tx_stopped(&up->port))
-               goto disable_tx_int;
+               goto ack_tx_int;
 
        sbus_writeb(xmit->buf[xmit->tail], &channel->data);
        ZSDELAY();
@@ -525,12 +529,15 @@ static void sunzilog_transmit_chars(struct uart_sunzilog_port *up,
        if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
                uart_write_wakeup(&up->port);
 
-       if (!uart_circ_empty(xmit))
-               return;
+       if (uart_circ_empty(xmit))
+               goto ack_tx_int;
 
-disable_tx_int:
-       up->curregs[R5] &= ~TxENAB;
-       write_zsreg(ZILOG_CHANNEL_FROM_PORT(&up->port), R5, up->curregs[R5]);
+       return;
+
+ack_tx_int:
+       sbus_writeb(RES_Tx_P, &channel->control);
+       ZSDELAY();
+       ZS_WSYNC(channel);
 }
 
 static void sunzilog_interrupt(int irq, void *dev_id, struct pt_regs *regs)
@@ -543,7 +550,7 @@ static void sunzilog_interrupt(int irq, void *dev_id, struct pt_regs *regs)
                unsigned char r3;
 
                spin_lock(&up->port.lock);
-               r3 = read_zsreg(channel, 3);
+               r3 = read_zsreg(channel, R3);
 
                /* Channel A */
                if (r3 & (CHAEXT | CHATxIP | CHARxIP)) {
@@ -679,13 +686,6 @@ static void sunzilog_start_tx(struct uart_port *port, unsigned int tty_start)
        up->flags |= SUNZILOG_FLAG_TX_ACTIVE;
        up->flags &= ~SUNZILOG_FLAG_TX_STOPPED;
 
-       /* Enable the transmitter.  */
-       if (!(up->curregs[R5] & TxENAB)) {
-               /* NOTE: Not subject to 'transmitter active' rule.  */ 
-               up->curregs[R5] |= TxENAB;
-               write_zsreg(channel, R5, up->curregs[R5]);
-       }
-
        status = sbus_readb(&channel->control);
        ZSDELAY();
 
@@ -785,39 +785,69 @@ static void sunzilog_break_ctl(struct uart_port *port, int break_state)
        spin_unlock_irqrestore(&port->lock, flags);
 }
 
-static int sunzilog_startup(struct uart_port *port)
+static void __sunzilog_startup(struct uart_sunzilog_port *up)
 {
-       struct uart_sunzilog_port *up = UART_ZILOG(port);
        struct zilog_channel *channel;
-       unsigned long flags;
 
-       spin_lock_irqsave(&port->lock, flags);
-
-       channel = ZILOG_CHANNEL_FROM_PORT(port);
+       channel = ZILOG_CHANNEL_FROM_PORT(&up->port);
        up->prev_status = sbus_readb(&channel->control);
 
        /* Enable receiver and transmitter.  */
        up->curregs[R3] |= RxENAB;
        up->curregs[R5] |= TxENAB;
 
-       /* Enable RX and status interrupts.  TX interrupts are enabled
-        * as needed.
-        */
-       up->curregs[R1] |= EXT_INT_ENAB | INT_ALL_Rx;
-       up->curregs[R9] |= MIE;
+       up->curregs[R1] |= EXT_INT_ENAB | INT_ALL_Rx | TxINT_ENAB;
        sunzilog_maybe_update_regs(up, channel);
+}
 
-       spin_unlock_irqrestore(&port->lock, flags);
+static int sunzilog_startup(struct uart_port *port)
+{
+       struct uart_sunzilog_port *up = UART_ZILOG(port);
+       unsigned long flags;
 
+       if (ZS_IS_CONS(up))
+               return 0;
+
+       spin_lock_irqsave(&port->lock, flags);
+       __sunzilog_startup(up);
+       spin_unlock_irqrestore(&port->lock, flags);
        return 0;
 }
 
+/*
+ * The test for ZS_IS_CONS is explained by the following e-mail:
+ *****
+ * From: Russell King <rmk@arm.linux.org.uk>
+ * Date: Sun, 8 Dec 2002 10:18:38 +0000
+ *
+ * On Sun, Dec 08, 2002 at 02:43:36AM -0500, Pete Zaitcev wrote:
+ * > I boot my 2.5 boxes using "console=ttyS0,9600" argument,
+ * > and I noticed that something is not right with reference
+ * > counting in this case. It seems that when the console
+ * > is open by kernel initially, this is not accounted
+ * > as an open, and uart_startup is not called.
+ *
+ * That is correct.  We are unable to call uart_startup when the serial
+ * console is initialised because it may need to allocate memory (as
+ * request_irq does) and the memory allocators may not have been
+ * initialised.
+ *
+ * 1. initialise the port into a state where it can send characters in the
+ *    console write method.
+ *
+ * 2. don't do the actual hardware shutdown in your shutdown() method (but
+ *    do the normal software shutdown - ie, free irqs etc)
+ *****
+ */
 static void sunzilog_shutdown(struct uart_port *port)
 {
        struct uart_sunzilog_port *up = UART_ZILOG(port);
        struct zilog_channel *channel;
        unsigned long flags;
 
+       if (ZS_IS_CONS(up))
+               return;
+
        spin_lock_irqsave(&port->lock, flags);
 
        channel = ZILOG_CHANNEL_FROM_PORT(port);
@@ -829,7 +859,6 @@ static void sunzilog_shutdown(struct uart_port *port)
        /* Disable all interrupts and BRK assertion.  */
        up->curregs[R1] &= ~(EXT_INT_ENAB | TxINT_ENAB | RxINT_MASK);
        up->curregs[R5] &= ~SND_BRK;
-       up->curregs[R9] &= ~MIE;
        sunzilog_maybe_update_regs(up, channel);
 
        spin_unlock_irqrestore(&port->lock, flags);
@@ -843,18 +872,15 @@ sunzilog_convert_to_zs(struct uart_sunzilog_port *up, unsigned int cflag,
                       unsigned int iflag, int brg)
 {
 
-       /* Don't modify MIE. */
-       up->curregs[R9] |= NV;
-
        up->curregs[R10] = NRZ;
-       up->curregs[11] = TCBR | RCBR;
+       up->curregs[R11] = TCBR | RCBR;
 
        /* Program BAUD and clock source. */
-       up->curregs[4] &= ~XCLK_MASK;
-       up->curregs[4] |= X16CLK;
-       up->curregs[12] = brg & 0xff;
-       up->curregs[13] = (brg >> 8) & 0xff;
-       up->curregs[14] = BRSRC | BRENAB;
+       up->curregs[R4] &= ~XCLK_MASK;
+       up->curregs[R4] |= X16CLK;
+       up->curregs[R12] = brg & 0xff;
+       up->curregs[R13] = (brg >> 8) & 0xff;
+       up->curregs[R14] = BRSRC | BRENAB;
 
        /* Character size, stop bits, and parity. */
        up->curregs[3] &= ~RxN_MASK;
@@ -1027,7 +1053,7 @@ static void __init sunzilog_alloc_tables(void)
                alloc_one_table(NUM_SUNZILOG * sizeof(struct zilog_layout *));
 
        if (sunzilog_port_table == NULL || sunzilog_chip_regs == NULL) {
-               prom_printf("sunzilog_init: Cannot alloc SunZilog tables.\n");
+               prom_printf("SunZilog: Cannot allocate tables.\n");
                prom_halt();
        }
 }
@@ -1333,7 +1359,7 @@ static int __init sunzilog_console_setup(struct console *con, char *options)
        unsigned long flags;
        int baud, brg;
 
-       printk("Console: ttyS%d (Zilog8530)\n",
+       printk("Console: ttyS%d (SunZilog)\n",
               (sunzilog_reg.minor - 64) + con->index);
 
        /* Get firmware console settings.  */
@@ -1356,21 +1382,15 @@ static int __init sunzilog_console_setup(struct console *con, char *options)
 
        brg = BPS_TO_BRG(baud, ZS_CLOCK / ZS_CLOCK_DIVISOR);
 
-       /*
-        * Temporary fix.
-        */
-       spin_lock_init(&up->port.lock);
-
        spin_lock_irqsave(&up->port.lock, flags);
 
        up->curregs[R15] = BRKIE;
-
        sunzilog_convert_to_zs(up, con->cflag, 0, brg);
 
-       spin_unlock_irqrestore(&up->port.lock, flags);
-
        sunzilog_set_mctrl(&up->port, TIOCM_DTR | TIOCM_RTS);
-       sunzilog_startup(&up->port);
+       __sunzilog_startup(up);
+
+       spin_unlock_irqrestore(&up->port.lock, flags);
 
        return 0;
 }
@@ -1401,6 +1421,7 @@ static int __init sunzilog_console_init(void)
                return 0;
 
        sunzilog_console.index = i;
+       sunzilog_port_table[i].flags |= SUNZILOG_FLAG_IS_CONS;
        register_console(&sunzilog_console);
        return 0;
 }
@@ -1457,6 +1478,12 @@ static void __init sunzilog_prepare(void)
        struct zilog_layout *rp;
        int channel, chip;
 
+       /*
+        * Temporary fix.
+        */
+       for (channel = 0; channel < NUM_CHANNELS - 1; channel++)
+               spin_lock_init(sunzilog_port_table[channel].port.lock);
+
        sunzilog_irq_chain = up = &sunzilog_port_table[0];
        for (channel = 0; channel < NUM_CHANNELS - 1; channel++)
                up[channel].next = &up[channel + 1];
@@ -1504,7 +1531,7 @@ static void __init sunzilog_init_kbdms(struct uart_sunzilog_port *up, int channe
                up->cflag = B4800 | CS8 | CLOCAL | CREAD;
                baud = 4800;
        }
-       printk(KERN_INFO "zs%d at 0x%p (irq = %s) is a Zilog8530\n",
+       printk(KERN_INFO "zs%d at 0x%p (irq = %s) is a SunZilog\n",
               channel, up->port.membase, __irq_itoa(zilog_irq));
 
        up->curregs[R15] = BRKIE;
@@ -1534,10 +1561,8 @@ static void __init sunzilog_init_kbdms(struct uart_sunzilog_port *up, int channe
        serio_register_port(&up->serio);
 #endif
 
-       spin_unlock(&up->port.lock);
        sunzilog_set_mctrl(&up->port, TIOCM_DTR | TIOCM_RTS);
-       sunzilog_startup(&up->port);
-       spin_lock(&up->port.lock);
+       __sunzilog_startup(up);
 }
 
 static void __init sunzilog_init_hw(void)
@@ -1560,8 +1585,6 @@ static void __init sunzilog_init_hw(void)
 
                if (i == KEYBOARD_LINE || i == MOUSE_LINE) {
                        sunzilog_init_kbdms(up, i);
-               } else if (ZS_IS_CONS(up)) {
-                       /* sunzilog_console_setup takes care of this */
                } else {
                        /* Normal serial TTY. */
                        up->parity_mask = 0xff;
@@ -1572,11 +1595,12 @@ static void __init sunzilog_init_hw(void)
                        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(baud, ZS_CLOCK / ZS_CLOCK_DIVISOR);
                        up->curregs[R12] = (brg & 0xff);
                        up->curregs[R13] = (brg >> 8) & 0xff;
                        up->curregs[R14] = BRSRC | BRENAB;
-                       sunzilog_maybe_update_regs(up, channel);
+                       __load_zsregs(channel, up->curregs);
+                       write_zsreg(channel, R9, up->curregs[R9]);
                }
 
                spin_unlock_irqrestore(&up->port.lock, flags);