diff options
author | Al Viro <viro@zeniv.linux.org.uk> | 2007-09-17 19:47:07 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2007-09-17 19:47:07 -0400 |
commit | 9c5b34806c28195e4d0f2deaa41d8158ca5874e1 (patch) | |
tree | d12e521ee1adcf5130c4c2ae4cc360cf50db07a7 /drivers/serial | |
parent | c2f828977ba5d17c13debba374ea252d18e5ccfb (diff) |
[SUNSAB]: Fix several bugs.
* 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>
Diffstat (limited to 'drivers/serial')
-rw-r--r-- | drivers/serial/sunsab.c | 107 |
1 files changed, 42 insertions, 65 deletions
diff --git a/drivers/serial/sunsab.c b/drivers/serial/sunsab.c index bca57bb94939..e348ba684050 100644 --- a/drivers/serial/sunsab.c +++ b/drivers/serial/sunsab.c | |||
@@ -58,6 +58,7 @@ struct uart_sunsab_port { | |||
58 | unsigned char interrupt_mask1;/* ISR1 masking */ | 58 | unsigned char interrupt_mask1;/* ISR1 masking */ |
59 | unsigned char pvr_dtr_bit; /* Which PVR bit is DTR */ | 59 | unsigned char pvr_dtr_bit; /* Which PVR bit is DTR */ |
60 | unsigned char pvr_dsr_bit; /* Which PVR bit is DSR */ | 60 | unsigned char pvr_dsr_bit; /* Which PVR bit is DSR */ |
61 | unsigned int gis_shift; | ||
61 | int type; /* SAB82532 version */ | 62 | int type; /* SAB82532 version */ |
62 | 63 | ||
63 | /* Setting configuration bits while the transmitter is active | 64 | /* Setting configuration bits while the transmitter is active |
@@ -305,13 +306,15 @@ static irqreturn_t sunsab_interrupt(int irq, void *dev_id) | |||
305 | struct tty_struct *tty; | 306 | struct tty_struct *tty; |
306 | union sab82532_irq_status status; | 307 | union sab82532_irq_status status; |
307 | unsigned long flags; | 308 | unsigned long flags; |
309 | unsigned char gis; | ||
308 | 310 | ||
309 | spin_lock_irqsave(&up->port.lock, flags); | 311 | spin_lock_irqsave(&up->port.lock, flags); |
310 | 312 | ||
311 | status.stat = 0; | 313 | status.stat = 0; |
312 | if (readb(&up->regs->r.gis) & SAB82532_GIS_ISA0) | 314 | gis = readb(&up->regs->r.gis) >> up->gis_shift; |
315 | if (gis & 1) | ||
313 | status.sreg.isr0 = readb(&up->regs->r.isr0); | 316 | status.sreg.isr0 = readb(&up->regs->r.isr0); |
314 | if (readb(&up->regs->r.gis) & SAB82532_GIS_ISA1) | 317 | if (gis & 2) |
315 | status.sreg.isr1 = readb(&up->regs->r.isr1); | 318 | status.sreg.isr1 = readb(&up->regs->r.isr1); |
316 | 319 | ||
317 | tty = NULL; | 320 | tty = NULL; |
@@ -327,35 +330,6 @@ static irqreturn_t sunsab_interrupt(int irq, void *dev_id) | |||
327 | transmit_chars(up, &status); | 330 | transmit_chars(up, &status); |
328 | } | 331 | } |
329 | 332 | ||
330 | spin_unlock(&up->port.lock); | ||
331 | |||
332 | if (tty) | ||
333 | tty_flip_buffer_push(tty); | ||
334 | |||
335 | up++; | ||
336 | |||
337 | spin_lock(&up->port.lock); | ||
338 | |||
339 | status.stat = 0; | ||
340 | if (readb(&up->regs->r.gis) & SAB82532_GIS_ISB0) | ||
341 | status.sreg.isr0 = readb(&up->regs->r.isr0); | ||
342 | if (readb(&up->regs->r.gis) & SAB82532_GIS_ISB1) | ||
343 | status.sreg.isr1 = readb(&up->regs->r.isr1); | ||
344 | |||
345 | tty = NULL; | ||
346 | if (status.stat) { | ||
347 | if ((status.sreg.isr0 & (SAB82532_ISR0_TCD | SAB82532_ISR0_TIME | | ||
348 | SAB82532_ISR0_RFO | SAB82532_ISR0_RPF)) || | ||
349 | (status.sreg.isr1 & SAB82532_ISR1_BRK)) | ||
350 | |||
351 | tty = receive_chars(up, &status); | ||
352 | if ((status.sreg.isr0 & SAB82532_ISR0_CDSC) || | ||
353 | (status.sreg.isr1 & (SAB82532_ISR1_BRK | SAB82532_ISR1_CSC))) | ||
354 | check_status(up, &status); | ||
355 | if (status.sreg.isr1 & (SAB82532_ISR1_ALLS | SAB82532_ISR1_XPR)) | ||
356 | transmit_chars(up, &status); | ||
357 | } | ||
358 | |||
359 | spin_unlock_irqrestore(&up->port.lock, flags); | 333 | spin_unlock_irqrestore(&up->port.lock, flags); |
360 | 334 | ||
361 | if (tty) | 335 | if (tty) |
@@ -539,6 +513,10 @@ static int sunsab_startup(struct uart_port *port) | |||
539 | struct uart_sunsab_port *up = (struct uart_sunsab_port *) port; | 513 | struct uart_sunsab_port *up = (struct uart_sunsab_port *) port; |
540 | unsigned long flags; | 514 | unsigned long flags; |
541 | unsigned char tmp; | 515 | unsigned char tmp; |
516 | int err = request_irq(up->port.irq, sunsab_interrupt, | ||
517 | IRQF_SHARED, "sab", up); | ||
518 | if (err) | ||
519 | return err; | ||
542 | 520 | ||
543 | spin_lock_irqsave(&up->port.lock, flags); | 521 | spin_lock_irqsave(&up->port.lock, flags); |
544 | 522 | ||
@@ -641,6 +619,7 @@ static void sunsab_shutdown(struct uart_port *port) | |||
641 | #endif | 619 | #endif |
642 | 620 | ||
643 | spin_unlock_irqrestore(&up->port.lock, flags); | 621 | spin_unlock_irqrestore(&up->port.lock, flags); |
622 | free_irq(up->port.irq, up); | ||
644 | } | 623 | } |
645 | 624 | ||
646 | /* | 625 | /* |
@@ -1008,9 +987,11 @@ static int __devinit sunsab_init_one(struct uart_sunsab_port *up, | |||
1008 | if ((up->port.line & 0x1) == 0) { | 987 | if ((up->port.line & 0x1) == 0) { |
1009 | up->pvr_dsr_bit = (1 << 0); | 988 | up->pvr_dsr_bit = (1 << 0); |
1010 | up->pvr_dtr_bit = (1 << 1); | 989 | up->pvr_dtr_bit = (1 << 1); |
990 | up->gis_shift = 2; | ||
1011 | } else { | 991 | } else { |
1012 | up->pvr_dsr_bit = (1 << 3); | 992 | up->pvr_dsr_bit = (1 << 3); |
1013 | up->pvr_dtr_bit = (1 << 2); | 993 | up->pvr_dtr_bit = (1 << 2); |
994 | up->gis_shift = 0; | ||
1014 | } | 995 | } |
1015 | up->cached_pvr = (1 << 1) | (1 << 2) | (1 << 4); | 996 | up->cached_pvr = (1 << 1) | (1 << 2) | (1 << 4); |
1016 | writeb(up->cached_pvr, &up->regs->w.pvr); | 997 | writeb(up->cached_pvr, &up->regs->w.pvr); |
@@ -1023,19 +1004,6 @@ static int __devinit sunsab_init_one(struct uart_sunsab_port *up, | |||
1023 | up->tec_timeout = SAB82532_MAX_TEC_TIMEOUT; | 1004 | up->tec_timeout = SAB82532_MAX_TEC_TIMEOUT; |
1024 | up->cec_timeout = SAB82532_MAX_CEC_TIMEOUT; | 1005 | up->cec_timeout = SAB82532_MAX_CEC_TIMEOUT; |
1025 | 1006 | ||
1026 | if (!(up->port.line & 0x01)) { | ||
1027 | int err; | ||
1028 | |||
1029 | err = request_irq(up->port.irq, sunsab_interrupt, | ||
1030 | IRQF_SHARED, "sab", up); | ||
1031 | if (err) { | ||
1032 | of_iounmap(&op->resource[0], | ||
1033 | up->port.membase, | ||
1034 | sizeof(union sab82532_async_regs)); | ||
1035 | return err; | ||
1036 | } | ||
1037 | } | ||
1038 | |||
1039 | return 0; | 1007 | return 0; |
1040 | } | 1008 | } |
1041 | 1009 | ||
@@ -1051,52 +1019,60 @@ static int __devinit sab_probe(struct of_device *op, const struct of_device_id * | |||
1051 | 0, | 1019 | 0, |
1052 | (inst * 2) + 0); | 1020 | (inst * 2) + 0); |
1053 | if (err) | 1021 | if (err) |
1054 | return err; | 1022 | goto out; |
1055 | 1023 | ||
1056 | err = sunsab_init_one(&up[1], op, | 1024 | err = sunsab_init_one(&up[1], op, |
1057 | sizeof(union sab82532_async_regs), | 1025 | sizeof(union sab82532_async_regs), |
1058 | (inst * 2) + 1); | 1026 | (inst * 2) + 1); |
1059 | if (err) { | 1027 | if (err) |
1060 | of_iounmap(&op->resource[0], | 1028 | goto out1; |
1061 | up[0].port.membase, | ||
1062 | sizeof(union sab82532_async_regs)); | ||
1063 | free_irq(up[0].port.irq, &up[0]); | ||
1064 | return err; | ||
1065 | } | ||
1066 | 1029 | ||
1067 | sunserial_console_match(SUNSAB_CONSOLE(), op->node, | 1030 | sunserial_console_match(SUNSAB_CONSOLE(), op->node, |
1068 | &sunsab_reg, up[0].port.line); | 1031 | &sunsab_reg, up[0].port.line); |
1069 | uart_add_one_port(&sunsab_reg, &up[0].port); | ||
1070 | 1032 | ||
1071 | sunserial_console_match(SUNSAB_CONSOLE(), op->node, | 1033 | sunserial_console_match(SUNSAB_CONSOLE(), op->node, |
1072 | &sunsab_reg, up[1].port.line); | 1034 | &sunsab_reg, up[1].port.line); |
1073 | uart_add_one_port(&sunsab_reg, &up[1].port); | 1035 | |
1036 | err = uart_add_one_port(&sunsab_reg, &up[0].port); | ||
1037 | if (err) | ||
1038 | goto out2; | ||
1039 | |||
1040 | err = uart_add_one_port(&sunsab_reg, &up[1].port); | ||
1041 | if (err) | ||
1042 | goto out3; | ||
1074 | 1043 | ||
1075 | dev_set_drvdata(&op->dev, &up[0]); | 1044 | dev_set_drvdata(&op->dev, &up[0]); |
1076 | 1045 | ||
1077 | inst++; | 1046 | inst++; |
1078 | 1047 | ||
1079 | return 0; | 1048 | return 0; |
1080 | } | ||
1081 | |||
1082 | static void __devexit sab_remove_one(struct uart_sunsab_port *up) | ||
1083 | { | ||
1084 | struct of_device *op = to_of_device(up->port.dev); | ||
1085 | 1049 | ||
1086 | uart_remove_one_port(&sunsab_reg, &up->port); | 1050 | out3: |
1087 | if (!(up->port.line & 1)) | 1051 | uart_remove_one_port(&sunsab_reg, &up[0].port); |
1088 | free_irq(up->port.irq, up); | 1052 | out2: |
1089 | of_iounmap(&op->resource[0], | 1053 | of_iounmap(&op->resource[0], |
1090 | up->port.membase, | 1054 | up[1].port.membase, |
1091 | sizeof(union sab82532_async_regs)); | 1055 | sizeof(union sab82532_async_regs)); |
1056 | out1: | ||
1057 | of_iounmap(&op->resource[0], | ||
1058 | up[0].port.membase, | ||
1059 | sizeof(union sab82532_async_regs)); | ||
1060 | out: | ||
1061 | return err; | ||
1092 | } | 1062 | } |
1093 | 1063 | ||
1094 | static int __devexit sab_remove(struct of_device *op) | 1064 | static int __devexit sab_remove(struct of_device *op) |
1095 | { | 1065 | { |
1096 | struct uart_sunsab_port *up = dev_get_drvdata(&op->dev); | 1066 | struct uart_sunsab_port *up = dev_get_drvdata(&op->dev); |
1097 | 1067 | ||
1098 | sab_remove_one(&up[0]); | 1068 | uart_remove_one_port(&sunsab_reg, &up[1].port); |
1099 | sab_remove_one(&up[1]); | 1069 | uart_remove_one_port(&sunsab_reg, &up[0].port); |
1070 | of_iounmap(&op->resource[0], | ||
1071 | up[1].port.membase, | ||
1072 | sizeof(union sab82532_async_regs)); | ||
1073 | of_iounmap(&op->resource[0], | ||
1074 | up[0].port.membase, | ||
1075 | sizeof(union sab82532_async_regs)); | ||
1100 | 1076 | ||
1101 | dev_set_drvdata(&op->dev, NULL); | 1077 | dev_set_drvdata(&op->dev, NULL); |
1102 | 1078 | ||
@@ -1143,6 +1119,7 @@ static int __init sunsab_init(void) | |||
1143 | 1119 | ||
1144 | sunsab_reg.minor = sunserial_current_minor; | 1120 | sunsab_reg.minor = sunserial_current_minor; |
1145 | sunsab_reg.nr = num_channels; | 1121 | sunsab_reg.nr = num_channels; |
1122 | sunsab_reg.cons = SUNSAB_CONSOLE(); | ||
1146 | 1123 | ||
1147 | err = uart_register_driver(&sunsab_reg); | 1124 | err = uart_register_driver(&sunsab_reg); |
1148 | if (err) { | 1125 | if (err) { |