[SUNSAB]: Fix several bugs.
authorAl Viro <viro@zeniv.linux.org.uk>
Mon, 17 Sep 2007 23:47:07 +0000 (16:47 -0700)
committerDavid S. Miller <davem@davemloft.net>
Mon, 17 Sep 2007 23:47:07 +0000 (16:47 -0700)
* don't register irq until ->startup() (and release in ->shutdown()).
That avoids oopsen with the current tree when interrupt comes before we'd
set up the data structures for ttyb.
* handle console=ttyS... even when OBP talks to screen/keyboard
* register irq handler for each port, let kernel/irq/handle.c
call it for both if needed.  Kills code duplication in sunsab_interrupt().
BTW, there'd been bitrot in it - ttya handling had stopped calling
check_status() on BRK (correctly), ttyb copy of that code had kept the
bogus call in that case.

Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
Signed-off-by: David S. Miller <davem@davemloft.net>
drivers/serial/sunsab.c

index bca57bb949393275c902f5f31e35e1abe9527d1a..e348ba684050eca227fa0e8a5ed8f148a0521ccc 100644 (file)
@@ -58,6 +58,7 @@ struct uart_sunsab_port {
        unsigned char                   interrupt_mask1;/* ISR1 masking         */
        unsigned char                   pvr_dtr_bit;    /* Which PVR bit is DTR */
        unsigned char                   pvr_dsr_bit;    /* Which PVR bit is DSR */
+       unsigned int                    gis_shift;
        int                             type;           /* SAB82532 version     */
 
        /* Setting configuration bits while the transmitter is active
@@ -305,13 +306,15 @@ static irqreturn_t sunsab_interrupt(int irq, void *dev_id)
        struct tty_struct *tty;
        union sab82532_irq_status status;
        unsigned long flags;
+       unsigned char gis;
 
        spin_lock_irqsave(&up->port.lock, flags);
 
        status.stat = 0;
-       if (readb(&up->regs->r.gis) & SAB82532_GIS_ISA0)
+       gis = readb(&up->regs->r.gis) >> up->gis_shift;
+       if (gis & 1)
                status.sreg.isr0 = readb(&up->regs->r.isr0);
-       if (readb(&up->regs->r.gis) & SAB82532_GIS_ISA1)
+       if (gis & 2)
                status.sreg.isr1 = readb(&up->regs->r.isr1);
 
        tty = NULL;
@@ -327,35 +330,6 @@ static irqreturn_t sunsab_interrupt(int irq, void *dev_id)
                        transmit_chars(up, &status);
        }
 
-       spin_unlock(&up->port.lock);
-
-       if (tty)
-               tty_flip_buffer_push(tty);
-
-       up++;
-
-       spin_lock(&up->port.lock);
-
-       status.stat = 0;
-       if (readb(&up->regs->r.gis) & SAB82532_GIS_ISB0)
-               status.sreg.isr0 = readb(&up->regs->r.isr0);
-       if (readb(&up->regs->r.gis) & SAB82532_GIS_ISB1)
-               status.sreg.isr1 = readb(&up->regs->r.isr1);
-
-       tty = NULL;
-       if (status.stat) {
-               if ((status.sreg.isr0 & (SAB82532_ISR0_TCD | SAB82532_ISR0_TIME |
-                                        SAB82532_ISR0_RFO | SAB82532_ISR0_RPF)) ||
-                   (status.sreg.isr1 & SAB82532_ISR1_BRK))
-
-                       tty = receive_chars(up, &status);
-               if ((status.sreg.isr0 & SAB82532_ISR0_CDSC) ||
-                   (status.sreg.isr1 & (SAB82532_ISR1_BRK | SAB82532_ISR1_CSC)))
-                       check_status(up, &status);
-               if (status.sreg.isr1 & (SAB82532_ISR1_ALLS | SAB82532_ISR1_XPR))
-                       transmit_chars(up, &status);
-       }
-
        spin_unlock_irqrestore(&up->port.lock, flags);
 
        if (tty)
@@ -539,6 +513,10 @@ static int sunsab_startup(struct uart_port *port)
        struct uart_sunsab_port *up = (struct uart_sunsab_port *) port;
        unsigned long flags;
        unsigned char tmp;
+       int err = request_irq(up->port.irq, sunsab_interrupt,
+                             IRQF_SHARED, "sab", up);
+       if (err)
+               return err;
 
        spin_lock_irqsave(&up->port.lock, flags);
 
@@ -641,6 +619,7 @@ static void sunsab_shutdown(struct uart_port *port)
 #endif
 
        spin_unlock_irqrestore(&up->port.lock, flags);
+       free_irq(up->port.irq, up);
 }
 
 /*
@@ -1008,9 +987,11 @@ static int __devinit sunsab_init_one(struct uart_sunsab_port *up,
        if ((up->port.line & 0x1) == 0) {
                up->pvr_dsr_bit = (1 << 0);
                up->pvr_dtr_bit = (1 << 1);
+               up->gis_shift = 2;
        } else {
                up->pvr_dsr_bit = (1 << 3);
                up->pvr_dtr_bit = (1 << 2);
+               up->gis_shift = 0;
        }
        up->cached_pvr = (1 << 1) | (1 << 2) | (1 << 4);
        writeb(up->cached_pvr, &up->regs->w.pvr);
@@ -1023,19 +1004,6 @@ static int __devinit sunsab_init_one(struct uart_sunsab_port *up,
        up->tec_timeout = SAB82532_MAX_TEC_TIMEOUT;
        up->cec_timeout = SAB82532_MAX_CEC_TIMEOUT;
 
-       if (!(up->port.line & 0x01)) {
-               int err;
-
-               err = request_irq(up->port.irq, sunsab_interrupt,
-                                 IRQF_SHARED, "sab", up);
-               if (err) {
-                       of_iounmap(&op->resource[0],
-                                  up->port.membase,
-                                  sizeof(union sab82532_async_regs));
-                       return err;
-               }
-       }
-
        return 0;
 }
 
@@ -1051,52 +1019,60 @@ static int __devinit sab_probe(struct of_device *op, const struct of_device_id *
                              0,
                              (inst * 2) + 0);
        if (err)
-               return err;
+               goto out;
 
        err = sunsab_init_one(&up[1], op,
                              sizeof(union sab82532_async_regs),
                              (inst * 2) + 1);
-       if (err) {
-               of_iounmap(&op->resource[0],
-                          up[0].port.membase,
-                          sizeof(union sab82532_async_regs));
-               free_irq(up[0].port.irq, &up[0]);
-               return err;
-       }
+       if (err)
+               goto out1;
 
        sunserial_console_match(SUNSAB_CONSOLE(), op->node,
                                &sunsab_reg, up[0].port.line);
-       uart_add_one_port(&sunsab_reg, &up[0].port);
 
        sunserial_console_match(SUNSAB_CONSOLE(), op->node,
                                &sunsab_reg, up[1].port.line);
-       uart_add_one_port(&sunsab_reg, &up[1].port);
+
+       err = uart_add_one_port(&sunsab_reg, &up[0].port);
+       if (err)
+               goto out2;
+
+       err = uart_add_one_port(&sunsab_reg, &up[1].port);
+       if (err)
+               goto out3;
 
        dev_set_drvdata(&op->dev, &up[0]);
 
        inst++;
 
        return 0;
-}
-
-static void __devexit sab_remove_one(struct uart_sunsab_port *up)
-{
-       struct of_device *op = to_of_device(up->port.dev);
 
-       uart_remove_one_port(&sunsab_reg, &up->port);
-       if (!(up->port.line & 1))
-               free_irq(up->port.irq, up);
+out3:
+       uart_remove_one_port(&sunsab_reg, &up[0].port);
+out2:
        of_iounmap(&op->resource[0],
-                  up->port.membase,
+                  up[1].port.membase,
                   sizeof(union sab82532_async_regs));
+out1:
+       of_iounmap(&op->resource[0],
+                  up[0].port.membase,
+                  sizeof(union sab82532_async_regs));
+out:
+       return err;
 }
 
 static int __devexit sab_remove(struct of_device *op)
 {
        struct uart_sunsab_port *up = dev_get_drvdata(&op->dev);
 
-       sab_remove_one(&up[0]);
-       sab_remove_one(&up[1]);
+       uart_remove_one_port(&sunsab_reg, &up[1].port);
+       uart_remove_one_port(&sunsab_reg, &up[0].port);
+       of_iounmap(&op->resource[0],
+                  up[1].port.membase,
+                  sizeof(union sab82532_async_regs));
+       of_iounmap(&op->resource[0],
+                  up[0].port.membase,
+                  sizeof(union sab82532_async_regs));
 
        dev_set_drvdata(&op->dev, NULL);
 
@@ -1143,6 +1119,7 @@ static int __init sunsab_init(void)
 
                sunsab_reg.minor = sunserial_current_minor;
                sunsab_reg.nr = num_channels;
+               sunsab_reg.cons = SUNSAB_CONSOLE();
 
                err = uart_register_driver(&sunsab_reg);
                if (err) {