aboutsummaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
authorThomas Bogendoerfer <tsbogend@alpha.franken.de>2007-11-28 19:21:44 -0500
committerLinus Torvalds <torvalds@woody.linux-foundation.org>2007-11-29 12:24:53 -0500
commit68576cf122bc5195c758ed295e78b5858472378a (patch)
tree5b6b7cb9608bf757bf3368c808f14bf206eddbe8 /drivers
parent6d4f5879b6f4da50bde94e1cae73755978ed048f (diff)
IP22ZILOG: fix lockup and sysrq
- fix lockup when switching from early console to real console - make sysrq reliable - fix panic, if sysrq is issued before console is opened Signed-off-by: Thomas Bogendoerfer <tsbogend@alpha.franken.de> Acked-by: Ralf Baechle <ralf@linux-mips.org> Cc: Alan Cox <alan@lxorguk.ukuu.org.uk> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'drivers')
-rw-r--r--drivers/serial/ip22zilog.c247
1 files changed, 106 insertions, 141 deletions
diff --git a/drivers/serial/ip22zilog.c b/drivers/serial/ip22zilog.c
index f3257f708ef9..9c95bc0398ad 100644
--- a/drivers/serial/ip22zilog.c
+++ b/drivers/serial/ip22zilog.c
@@ -45,8 +45,6 @@
45 45
46#include "ip22zilog.h" 46#include "ip22zilog.h"
47 47
48void ip22_do_break(void);
49
50/* 48/*
51 * On IP22 we need to delay after register accesses but we do not need to 49 * On IP22 we need to delay after register accesses but we do not need to
52 * flush writes. 50 * flush writes.
@@ -81,12 +79,9 @@ struct uart_ip22zilog_port {
81#define IP22ZILOG_FLAG_REGS_HELD 0x00000040 79#define IP22ZILOG_FLAG_REGS_HELD 0x00000040
82#define IP22ZILOG_FLAG_TX_STOPPED 0x00000080 80#define IP22ZILOG_FLAG_TX_STOPPED 0x00000080
83#define IP22ZILOG_FLAG_TX_ACTIVE 0x00000100 81#define IP22ZILOG_FLAG_TX_ACTIVE 0x00000100
82#define IP22ZILOG_FLAG_RESET_DONE 0x00000200
84 83
85 unsigned int cflag; 84 unsigned int tty_break;
86
87 /* L1-A keyboard break state. */
88 int kbd_id;
89 int l1_down;
90 85
91 unsigned char parity_mask; 86 unsigned char parity_mask;
92 unsigned char prev_status; 87 unsigned char prev_status;
@@ -250,13 +245,26 @@ static void ip22zilog_maybe_update_regs(struct uart_ip22zilog_port *up,
250 } 245 }
251} 246}
252 247
253static void ip22zilog_receive_chars(struct uart_ip22zilog_port *up, 248#define Rx_BRK 0x0100 /* BREAK event software flag. */
254 struct zilog_channel *channel) 249#define Rx_SYS 0x0200 /* SysRq event software flag. */
250
251static struct tty_struct *ip22zilog_receive_chars(struct uart_ip22zilog_port *up,
252 struct zilog_channel *channel)
255{ 253{
256 struct tty_struct *tty = up->port.info->tty; /* XXX info==NULL? */ 254 struct tty_struct *tty;
255 unsigned char ch, flag;
256 unsigned int r1;
257
258 tty = NULL;
259 if (up->port.info != NULL &&
260 up->port.info->tty != NULL)
261 tty = up->port.info->tty;
257 262
258 while (1) { 263 for (;;) {
259 unsigned char ch, r1, flag; 264 ch = readb(&channel->control);
265 ZSDELAY();
266 if (!(ch & Rx_CH_AV))
267 break;
260 268
261 r1 = read_zsreg(channel, R1); 269 r1 = read_zsreg(channel, R1);
262 if (r1 & (PAR_ERR | Rx_OVR | CRC_ERR)) { 270 if (r1 & (PAR_ERR | Rx_OVR | CRC_ERR)) {
@@ -265,43 +273,26 @@ static void ip22zilog_receive_chars(struct uart_ip22zilog_port *up,
265 ZS_WSYNC(channel); 273 ZS_WSYNC(channel);
266 } 274 }
267 275
268 ch = readb(&channel->control);
269 ZSDELAY();
270
271 /* This funny hack depends upon BRK_ABRT not interfering
272 * with the other bits we care about in R1.
273 */
274 if (ch & BRK_ABRT)
275 r1 |= BRK_ABRT;
276
277 ch = readb(&channel->data); 276 ch = readb(&channel->data);
278 ZSDELAY(); 277 ZSDELAY();
279 278
280 ch &= up->parity_mask; 279 ch &= up->parity_mask;
281 280
282 if (ZS_IS_CONS(up) && (r1 & BRK_ABRT)) { 281 /* Handle the null char got when BREAK is removed. */
283 /* Wait for BREAK to deassert to avoid potentially 282 if (!ch)
284 * confusing the PROM. 283 r1 |= up->tty_break;
285 */
286 while (1) {
287 ch = readb(&channel->control);
288 ZSDELAY();
289 if (!(ch & BRK_ABRT))
290 break;
291 }
292 ip22_do_break();
293 return;
294 }
295 284
296 /* A real serial line, record the character and status. */ 285 /* A real serial line, record the character and status. */
297 flag = TTY_NORMAL; 286 flag = TTY_NORMAL;
298 up->port.icount.rx++; 287 up->port.icount.rx++;
299 if (r1 & (BRK_ABRT | PAR_ERR | Rx_OVR | CRC_ERR)) { 288 if (r1 & (PAR_ERR | Rx_OVR | CRC_ERR | Rx_SYS | Rx_BRK)) {
300 if (r1 & BRK_ABRT) { 289 up->tty_break = 0;
301 r1 &= ~(PAR_ERR | CRC_ERR); 290
291 if (r1 & (Rx_SYS | Rx_BRK)) {
302 up->port.icount.brk++; 292 up->port.icount.brk++;
303 if (uart_handle_break(&up->port)) 293 if (r1 & Rx_SYS)
304 goto next_char; 294 continue;
295 r1 &= ~(PAR_ERR | CRC_ERR);
305 } 296 }
306 else if (r1 & PAR_ERR) 297 else if (r1 & PAR_ERR)
307 up->port.icount.parity++; 298 up->port.icount.parity++;
@@ -310,30 +301,21 @@ static void ip22zilog_receive_chars(struct uart_ip22zilog_port *up,
310 if (r1 & Rx_OVR) 301 if (r1 & Rx_OVR)
311 up->port.icount.overrun++; 302 up->port.icount.overrun++;
312 r1 &= up->port.read_status_mask; 303 r1 &= up->port.read_status_mask;
313 if (r1 & BRK_ABRT) 304 if (r1 & Rx_BRK)
314 flag = TTY_BREAK; 305 flag = TTY_BREAK;
315 else if (r1 & PAR_ERR) 306 else if (r1 & PAR_ERR)
316 flag = TTY_PARITY; 307 flag = TTY_PARITY;
317 else if (r1 & CRC_ERR) 308 else if (r1 & CRC_ERR)
318 flag = TTY_FRAME; 309 flag = TTY_FRAME;
319 } 310 }
320 if (uart_handle_sysrq_char(&up->port, ch))
321 goto next_char;
322 311
323 if (up->port.ignore_status_mask == 0xff || 312 if (uart_handle_sysrq_char(&up->port, ch))
324 (r1 & up->port.ignore_status_mask) == 0) 313 continue;
325 tty_insert_flip_char(tty, ch, flag);
326 314
327 if (r1 & Rx_OVR) 315 if (tty)
328 tty_insert_flip_char(tty, 0, TTY_OVERRUN); 316 uart_insert_char(&up->port, r1, Rx_OVR, ch, flag);
329 next_char:
330 ch = readb(&channel->control);
331 ZSDELAY();
332 if (!(ch & Rx_CH_AV))
333 break;
334 } 317 }
335 318 return tty;
336 tty_flip_buffer_push(tty);
337} 319}
338 320
339static void ip22zilog_status_handle(struct uart_ip22zilog_port *up, 321static void ip22zilog_status_handle(struct uart_ip22zilog_port *up,
@@ -348,6 +330,15 @@ static void ip22zilog_status_handle(struct uart_ip22zilog_port *up,
348 ZSDELAY(); 330 ZSDELAY();
349 ZS_WSYNC(channel); 331 ZS_WSYNC(channel);
350 332
333 if (up->curregs[R15] & BRKIE) {
334 if ((status & BRK_ABRT) && !(up->prev_status & BRK_ABRT)) {
335 if (uart_handle_break(&up->port))
336 up->tty_break = Rx_SYS;
337 else
338 up->tty_break = Rx_BRK;
339 }
340 }
341
351 if (ZS_WANTS_MODEM_STATUS(up)) { 342 if (ZS_WANTS_MODEM_STATUS(up)) {
352 if (status & SYNC) 343 if (status & SYNC)
353 up->port.icount.dsr++; 344 up->port.icount.dsr++;
@@ -356,10 +347,10 @@ static void ip22zilog_status_handle(struct uart_ip22zilog_port *up,
356 * But it does not tell us which bit has changed, we have to keep 347 * But it does not tell us which bit has changed, we have to keep
357 * track of this ourselves. 348 * track of this ourselves.
358 */ 349 */
359 if ((status & DCD) ^ up->prev_status) 350 if ((status ^ up->prev_status) ^ DCD)
360 uart_handle_dcd_change(&up->port, 351 uart_handle_dcd_change(&up->port,
361 (status & DCD)); 352 (status & DCD));
362 if ((status & CTS) ^ up->prev_status) 353 if ((status ^ up->prev_status) ^ CTS)
363 uart_handle_cts_change(&up->port, 354 uart_handle_cts_change(&up->port,
364 (status & CTS)); 355 (status & CTS));
365 356
@@ -447,19 +438,21 @@ static irqreturn_t ip22zilog_interrupt(int irq, void *dev_id)
447 while (up) { 438 while (up) {
448 struct zilog_channel *channel 439 struct zilog_channel *channel
449 = ZILOG_CHANNEL_FROM_PORT(&up->port); 440 = ZILOG_CHANNEL_FROM_PORT(&up->port);
441 struct tty_struct *tty;
450 unsigned char r3; 442 unsigned char r3;
451 443
452 spin_lock(&up->port.lock); 444 spin_lock(&up->port.lock);
453 r3 = read_zsreg(channel, R3); 445 r3 = read_zsreg(channel, R3);
454 446
455 /* Channel A */ 447 /* Channel A */
448 tty = NULL;
456 if (r3 & (CHAEXT | CHATxIP | CHARxIP)) { 449 if (r3 & (CHAEXT | CHATxIP | CHARxIP)) {
457 writeb(RES_H_IUS, &channel->control); 450 writeb(RES_H_IUS, &channel->control);
458 ZSDELAY(); 451 ZSDELAY();
459 ZS_WSYNC(channel); 452 ZS_WSYNC(channel);
460 453
461 if (r3 & CHARxIP) 454 if (r3 & CHARxIP)
462 ip22zilog_receive_chars(up, channel); 455 tty = ip22zilog_receive_chars(up, channel);
463 if (r3 & CHAEXT) 456 if (r3 & CHAEXT)
464 ip22zilog_status_handle(up, channel); 457 ip22zilog_status_handle(up, channel);
465 if (r3 & CHATxIP) 458 if (r3 & CHATxIP)
@@ -467,18 +460,22 @@ static irqreturn_t ip22zilog_interrupt(int irq, void *dev_id)
467 } 460 }
468 spin_unlock(&up->port.lock); 461 spin_unlock(&up->port.lock);
469 462
463 if (tty)
464 tty_flip_buffer_push(tty);
465
470 /* Channel B */ 466 /* Channel B */
471 up = up->next; 467 up = up->next;
472 channel = ZILOG_CHANNEL_FROM_PORT(&up->port); 468 channel = ZILOG_CHANNEL_FROM_PORT(&up->port);
473 469
474 spin_lock(&up->port.lock); 470 spin_lock(&up->port.lock);
471 tty = NULL;
475 if (r3 & (CHBEXT | CHBTxIP | CHBRxIP)) { 472 if (r3 & (CHBEXT | CHBTxIP | CHBRxIP)) {
476 writeb(RES_H_IUS, &channel->control); 473 writeb(RES_H_IUS, &channel->control);
477 ZSDELAY(); 474 ZSDELAY();
478 ZS_WSYNC(channel); 475 ZS_WSYNC(channel);
479 476
480 if (r3 & CHBRxIP) 477 if (r3 & CHBRxIP)
481 ip22zilog_receive_chars(up, channel); 478 tty = ip22zilog_receive_chars(up, channel);
482 if (r3 & CHBEXT) 479 if (r3 & CHBEXT)
483 ip22zilog_status_handle(up, channel); 480 ip22zilog_status_handle(up, channel);
484 if (r3 & CHBTxIP) 481 if (r3 & CHBTxIP)
@@ -486,6 +483,9 @@ static irqreturn_t ip22zilog_interrupt(int irq, void *dev_id)
486 } 483 }
487 spin_unlock(&up->port.lock); 484 spin_unlock(&up->port.lock);
488 485
486 if (tty)
487 tty_flip_buffer_push(tty);
488
489 up = up->next; 489 up = up->next;
490 } 490 }
491 491
@@ -681,11 +681,46 @@ static void ip22zilog_break_ctl(struct uart_port *port, int break_state)
681 spin_unlock_irqrestore(&port->lock, flags); 681 spin_unlock_irqrestore(&port->lock, flags);
682} 682}
683 683
684static void __ip22zilog_reset(struct uart_ip22zilog_port *up)
685{
686 struct zilog_channel *channel;
687 int i;
688
689 if (up->flags & IP22ZILOG_FLAG_RESET_DONE)
690 return;
691
692 /* Let pending transmits finish. */
693 channel = ZILOG_CHANNEL_FROM_PORT(&up->port);
694 for (i = 0; i < 1000; i++) {
695 unsigned char stat = read_zsreg(channel, R1);
696 if (stat & ALL_SNT)
697 break;
698 udelay(100);
699 }
700
701 if (!ZS_IS_CHANNEL_A(up)) {
702 up++;
703 channel = ZILOG_CHANNEL_FROM_PORT(&up->port);
704 }
705 write_zsreg(channel, R9, FHWRES);
706 ZSDELAY_LONG();
707 (void) read_zsreg(channel, R0);
708
709 up->flags |= IP22ZILOG_FLAG_RESET_DONE;
710 up->next->flags |= IP22ZILOG_FLAG_RESET_DONE;
711}
712
684static void __ip22zilog_startup(struct uart_ip22zilog_port *up) 713static void __ip22zilog_startup(struct uart_ip22zilog_port *up)
685{ 714{
686 struct zilog_channel *channel; 715 struct zilog_channel *channel;
687 716
688 channel = ZILOG_CHANNEL_FROM_PORT(&up->port); 717 channel = ZILOG_CHANNEL_FROM_PORT(&up->port);
718
719 __ip22zilog_reset(up);
720
721 __load_zsregs(channel, up->curregs);
722 /* set master interrupt enable */
723 write_zsreg(channel, R9, up->curregs[R9]);
689 up->prev_status = readb(&channel->control); 724 up->prev_status = readb(&channel->control);
690 725
691 /* Enable receiver and transmitter. */ 726 /* Enable receiver and transmitter. */
@@ -859,8 +894,6 @@ ip22zilog_set_termios(struct uart_port *port, struct ktermios *termios,
859 else 894 else
860 up->flags &= ~IP22ZILOG_FLAG_MODEM_STATUS; 895 up->flags &= ~IP22ZILOG_FLAG_MODEM_STATUS;
861 896
862 up->cflag = termios->c_cflag;
863
864 ip22zilog_maybe_update_regs(up, ZILOG_CHANNEL_FROM_PORT(port)); 897 ip22zilog_maybe_update_regs(up, ZILOG_CHANNEL_FROM_PORT(port));
865 uart_update_timeout(port, termios->c_cflag, baud); 898 uart_update_timeout(port, termios->c_cflag, baud);
866 899
@@ -992,74 +1025,29 @@ ip22zilog_console_write(struct console *con, const char *s, unsigned int count)
992 spin_unlock_irqrestore(&up->port.lock, flags); 1025 spin_unlock_irqrestore(&up->port.lock, flags);
993} 1026}
994 1027
995void
996ip22serial_console_termios(struct console *con, char *options)
997{
998 int baud = 9600, bits = 8, cflag;
999 int parity = 'n';
1000 int flow = 'n';
1001
1002 if (options)
1003 uart_parse_options(options, &baud, &parity, &bits, &flow);
1004
1005 cflag = CREAD | HUPCL | CLOCAL;
1006
1007 switch (baud) {
1008 case 150: cflag |= B150; break;
1009 case 300: cflag |= B300; break;
1010 case 600: cflag |= B600; break;
1011 case 1200: cflag |= B1200; break;
1012 case 2400: cflag |= B2400; break;
1013 case 4800: cflag |= B4800; break;
1014 case 9600: cflag |= B9600; break;
1015 case 19200: cflag |= B19200; break;
1016 case 38400: cflag |= B38400; break;
1017 default: baud = 9600; cflag |= B9600; break;
1018 }
1019
1020 con->cflag = cflag | CS8; /* 8N1 */
1021
1022 uart_update_timeout(&ip22zilog_port_table[con->index].port, cflag, baud);
1023}
1024
1025static int __init ip22zilog_console_setup(struct console *con, char *options) 1028static int __init ip22zilog_console_setup(struct console *con, char *options)
1026{ 1029{
1027 struct uart_ip22zilog_port *up = &ip22zilog_port_table[con->index]; 1030 struct uart_ip22zilog_port *up = &ip22zilog_port_table[con->index];
1028 unsigned long flags; 1031 unsigned long flags;
1029 int baud, brg; 1032 int baud = 9600, bits = 8;
1030 1033 int parity = 'n';
1031 printk("Console: ttyS%d (IP22-Zilog)\n", con->index); 1034 int flow = 'n';
1032 1035
1033 /* Get firmware console settings. */ 1036 up->flags |= IP22ZILOG_FLAG_IS_CONS;
1034 ip22serial_console_termios(con, options);
1035 1037
1036 /* Firmware console speed is limited to 150-->38400 baud so 1038 printk(KERN_INFO "Console: ttyS%d (IP22-Zilog)\n", con->index);
1037 * this hackish cflag thing is OK.
1038 */
1039 switch (con->cflag & CBAUD) {
1040 case B150: baud = 150; break;
1041 case B300: baud = 300; break;
1042 case B600: baud = 600; break;
1043 case B1200: baud = 1200; break;
1044 case B2400: baud = 2400; break;
1045 case B4800: baud = 4800; break;
1046 default: case B9600: baud = 9600; break;
1047 case B19200: baud = 19200; break;
1048 case B38400: baud = 38400; break;
1049 };
1050
1051 brg = BPS_TO_BRG(baud, ZS_CLOCK / ZS_CLOCK_DIVISOR);
1052 1039
1053 spin_lock_irqsave(&up->port.lock, flags); 1040 spin_lock_irqsave(&up->port.lock, flags);
1054 1041
1055 up->curregs[R15] = BRKIE; 1042 up->curregs[R15] |= BRKIE;
1056 ip22zilog_convert_to_zs(up, con->cflag, 0, brg);
1057 1043
1058 __ip22zilog_startup(up); 1044 __ip22zilog_startup(up);
1059 1045
1060 spin_unlock_irqrestore(&up->port.lock, flags); 1046 spin_unlock_irqrestore(&up->port.lock, flags);
1061 1047
1062 return 0; 1048 if (options)
1049 uart_parse_options(options, &baud, &parity, &bits, &flow);
1050 return uart_set_options(&up->port, con, baud, parity, bits, flow);
1063} 1051}
1064 1052
1065static struct uart_driver ip22zilog_reg; 1053static struct uart_driver ip22zilog_reg;
@@ -1140,25 +1128,10 @@ static void __init ip22zilog_prepare(void)
1140 up[(chip * 2) + 1].port.line = (chip * 2) + 1; 1128 up[(chip * 2) + 1].port.line = (chip * 2) + 1;
1141 up[(chip * 2) + 1].flags |= IP22ZILOG_FLAG_IS_CHANNEL_A; 1129 up[(chip * 2) + 1].flags |= IP22ZILOG_FLAG_IS_CHANNEL_A;
1142 } 1130 }
1143}
1144
1145static void __init ip22zilog_init_hw(void)
1146{
1147 int i;
1148
1149 for (i = 0; i < NUM_CHANNELS; i++) {
1150 struct uart_ip22zilog_port *up = &ip22zilog_port_table[i];
1151 struct zilog_channel *channel = ZILOG_CHANNEL_FROM_PORT(&up->port);
1152 unsigned long flags;
1153 int baud, brg;
1154 1131
1155 spin_lock_irqsave(&up->port.lock, flags); 1132 for (channel = 0; channel < NUM_CHANNELS; channel++) {
1156 1133 struct uart_ip22zilog_port *up = &ip22zilog_port_table[channel];
1157 if (ZS_IS_CHANNEL_A(up)) { 1134 int brg;
1158 write_zsreg(channel, R9, FHWRES);
1159 ZSDELAY_LONG();
1160 (void) read_zsreg(channel, R0);
1161 }
1162 1135
1163 /* Normal serial TTY. */ 1136 /* Normal serial TTY. */
1164 up->parity_mask = 0xff; 1137 up->parity_mask = 0xff;
@@ -1169,16 +1142,10 @@ static void __init ip22zilog_init_hw(void)
1169 up->curregs[R9] = NV | MIE; 1142 up->curregs[R9] = NV | MIE;
1170 up->curregs[R10] = NRZ; 1143 up->curregs[R10] = NRZ;
1171 up->curregs[R11] = TCBR | RCBR; 1144 up->curregs[R11] = TCBR | RCBR;
1172 baud = 9600; 1145 brg = BPS_TO_BRG(9600, ZS_CLOCK / ZS_CLOCK_DIVISOR);
1173 brg = BPS_TO_BRG(baud, ZS_CLOCK / ZS_CLOCK_DIVISOR);
1174 up->curregs[R12] = (brg & 0xff); 1146 up->curregs[R12] = (brg & 0xff);
1175 up->curregs[R13] = (brg >> 8) & 0xff; 1147 up->curregs[R13] = (brg >> 8) & 0xff;
1176 up->curregs[R14] = BRENAB; 1148 up->curregs[R14] = BRENAB;
1177 __load_zsregs(channel, up->curregs);
1178 /* set master interrupt enable */
1179 write_zsreg(channel, R9, up->curregs[R9]);
1180
1181 spin_unlock_irqrestore(&up->port.lock, flags);
1182 } 1149 }
1183} 1150}
1184 1151
@@ -1195,8 +1162,6 @@ static int __init ip22zilog_ports_init(void)
1195 panic("IP22-Zilog: Unable to register zs interrupt handler.\n"); 1162 panic("IP22-Zilog: Unable to register zs interrupt handler.\n");
1196 } 1163 }
1197 1164
1198 ip22zilog_init_hw();
1199
1200 ret = uart_register_driver(&ip22zilog_reg); 1165 ret = uart_register_driver(&ip22zilog_reg);
1201 if (ret == 0) { 1166 if (ret == 0) {
1202 int i; 1167 int i;