patch-2.1.79 linux/drivers/sbus/char/zs.c

Next file: linux/drivers/sbus/sbus.c
Previous file: linux/drivers/sbus/char/sunserial.h
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v2.1.78/linux/drivers/sbus/char/zs.c linux/drivers/sbus/char/zs.c
@@ -1,4 +1,4 @@
-/* $Id: zs.c,v 1.3 1997/09/04 14:57:34 jj Exp $
+/* $Id: zs.c,v 1.15 1997/12/22 16:09:34 jj Exp $
  * zs.c: Zilog serial port driver for the Sparc.
  *
  * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
@@ -22,8 +22,8 @@
 #include <linux/keyboard.h>
 #include <linux/console.h>
 #include <linux/delay.h>
-#include <linux/version.h>
 #include <linux/init.h>
+#include <linux/sysrq.h>
 
 #include <asm/io.h>
 #include <asm/irq.h>
@@ -36,6 +36,9 @@
 #include <asm/sbus.h>
 #ifdef __sparc_v9__
 #include <asm/fhc.h>
+#ifdef CONFIG_PCI
+#include <linux/bios32.h>
+#endif
 #endif
 
 #include "sunserial.h"
@@ -52,7 +55,6 @@
 
 struct sun_zslayout **zs_chips;
 struct sun_zschannel **zs_channels;
-struct sun_zschannel *zs_conschan;
 struct sun_zschannel *zs_mousechan;
 struct sun_zschannel *zs_kbdchan;
 struct sun_zschannel *zs_kgdbchan;
@@ -63,12 +65,12 @@
 int zilog_irq;
 
 struct tty_struct *zs_ttys;
-/** struct tty_struct *zs_constty; **/
 
 /* Console hooks... */
-static int zs_cons_chanout = 0;
-static int zs_cons_chanin = 0;
-struct sun_serial *zs_consinfo = 0;
+#ifdef CONFIG_SERIAL_CONSOLE
+static struct console zs_console;
+static int zs_console_init(void);
+#endif
 
 static unsigned char kgdb_regs[16] = {
 	0, 0, 0,                     /* write 0, 1, 2 */
@@ -114,6 +116,8 @@
 /* number of characters left in xmit buffer before we ask for more */
 #define WAKEUP_CHARS 256
 
+#define SERIAL_DO_RESTART
+
 /* Debugging... DEBUG_INTR is bad to use when one of the zs
  * lines is your console ;(
  */
@@ -127,8 +131,7 @@
 #define _INLINE_ inline
 
 int zs_init(void);
-void zs_cons_hook(int, int, int);
-void zs_kgdb_hook(int);
+static void zs_kgdb_hook(int);
 
 static void change_speed(struct sun_serial *info);
 
@@ -443,20 +446,29 @@
 			return;
 		}
 		if(info->is_cons) {
+#ifdef CONFIG_MAGIC_SYSRQ
+			static int serial_sysrq;
+
+			if (!ch) {
+				serial_sysrq = 1;
+				return;
+			} else if (serial_sysrq) {
+				if (ch == 'a' || ch == 'A')
+					/* whee, break-A received */
+					batten_down_hatches();
+				else
+					handle_sysrq(ch, regs, NULL, NULL);
+				serial_sysrq = 0;
+				return;
+			}
+#else
 			if(ch==0) {
 				/* whee, break received */
 				batten_down_hatches();
 				/* Continue execution... */
 				return;
-#if 0
-			} else if (ch == 1) {
-				show_state();
-				return;
-			} else if (ch == 2) {
-				show_buffers();
-				return;
-#endif
 			}
+#endif
 			/* It is a 'keyboard interrupt' ;-) */
 			wake_up(&keypress_wait);
 		}
@@ -996,21 +1008,6 @@
 	restore_flags(flags);
 }
 
-
-/* This is for console output over ttya/ttyb */
-static void zs_cons_put_char(char ch)
-{
-	struct sun_zschannel *chan = zs_conschan;
-	unsigned long flags;
-
-	if(!chan)
-		return;
-
-	save_flags(flags); cli();
-	zs_put_char(chan, ch);
-	restore_flags(flags);
-}
-
 /* These are for receiving and sending characters under the kgdb
  * source level kernel debugger.
  */
@@ -1032,70 +1029,6 @@
 	return chan->data;
 }
 
-/*
- * Fair output driver allows a process to speak.
- */
-static void zs_fair_output(void)
-{
-	int left;		/* Output no more than that */
-	unsigned long flags;
-	struct sun_serial *info = zs_consinfo;
-	char c;
-
-	if (info == 0) return;
-	if (info->xmit_buf == 0) return;
-
-	save_flags(flags);  cli();
-	left = info->xmit_cnt;
-	while (left != 0) {
-		c = info->xmit_buf[info->xmit_tail];
-		info->xmit_tail = (info->xmit_tail+1) & (SERIAL_XMIT_SIZE-1);
-		info->xmit_cnt--;
-		restore_flags(flags);
-
-		zs_cons_put_char(c);
-
-		cli();
-		left = MIN(info->xmit_cnt, left-1);
-	}
-
-	/* Last character is being transmitted now (hopefully). */
-	zs_conschan->control = RES_Tx_P;
-	udelay(5);
-
-	restore_flags(flags);
-	return;
-}
-
-/*
- * zs_console_print is registered for printk.
- */
-static void zs_console_print(const char *s, unsigned count)
-{
-	int i;
-
-	for (i = 0; i < count; i++, s++) {
-		if(*s == '\n')
-			zs_cons_put_char('\r');
-		zs_cons_put_char(*s);
-	}
-
-	/* Comment this if you want to have a strict interrupt-driven output */
-	zs_fair_output();
-}
-
-static void zs_console_wait_key(void)
-{
-	sleep_on(&keypress_wait);
-}
-
-static int zs_console_device(void)
-{
-	extern int serial_console;
-	
-	return MKDEV(TTYAUX_MAJOR, 64 + serial_console - 1);
-}
-
 static void zs_flush_chars(struct tty_struct *tty)
 {
 	struct sun_serial *info = (struct sun_serial *)tty->driver_data;
@@ -1190,7 +1123,7 @@
 {
 	struct sun_serial *info = (struct sun_serial *)tty->driver_data;
 	int	ret;
-				
+
 	if (serial_paranoia_check(info, tty->device, "zs_write_room"))
 		return 0;
 	ret = SERIAL_XMIT_SIZE - info->xmit_cnt - 1;
@@ -1202,7 +1135,7 @@
 static int zs_chars_in_buffer(struct tty_struct *tty)
 {
 	struct sun_serial *info = (struct sun_serial *)tty->driver_data;
-				
+
 	if (serial_paranoia_check(info, tty->device, "zs_chars_in_buffer"))
 		return 0;
 	return info->xmit_cnt;
@@ -1211,7 +1144,7 @@
 static void zs_flush_buffer(struct tty_struct *tty)
 {
 	struct sun_serial *info = (struct sun_serial *)tty->driver_data;
-				
+
 	if (serial_paranoia_check(info, tty->device, "zs_flush_buffer"))
 		return;
 	cli();
@@ -1641,10 +1574,13 @@
 void zs_hangup(struct tty_struct *tty)
 {
 	struct sun_serial * info = (struct sun_serial *)tty->driver_data;
-	
+
 	if (serial_paranoia_check(info, tty->device, "zs_hangup"))
 		return;
-	
+
+	if (info->is_cons)
+		return;
+
 #ifdef SERIAL_DEBUG_OPEN
 	printk("zs_hangup<%p: tty-%d, count = %d bye\n",
 		__builtin_return_address(0), info->line, info->count);
@@ -1862,6 +1798,14 @@
 		change_speed(info);
 	}
 
+#ifdef CONFIG_SERIAL_CONSOLE
+	if (zs_console.cflag && zs_console.index == line) {
+		tty->termios->c_cflag = zs_console.cflag;
+		zs_console.cflag = 0;
+		change_speed(info);
+	}
+#endif
+
 	info->session = current->session;
 	info->pgrp = current->pgrp;
 
@@ -1875,7 +1819,7 @@
 
 static void show_serial_version(void)
 {
-	char *revision = "$Revision: 1.3 $";
+	char *revision = "$Revision: 1.15 $";
 	char *version, *p;
 
 	version = strchr(revision, ' ');
@@ -1895,7 +1839,8 @@
 #ifdef __sparc_v9__
 static struct devid_cookie zs_dcookie;
 static unsigned long zs_irq_flags;
-static struct sun_zslayout *get_zs(int chip)
+__initfunc(static struct sun_zslayout *
+get_zs(int chip))
 {
 	unsigned int vaddr[2] = { 0, 0 };
 	int busnode, seen, zsnode, sun4u_ino;
@@ -1968,13 +1913,16 @@
 	return (struct sun_zslayout *)(unsigned long) vaddr[0];
 }
 #else /* !(__sparc_v9__) */
-static struct sun_zslayout *get_zs(int chip)
+__initfunc(static struct sun_zslayout *
+get_zs(int chip))
 {
 	struct linux_prom_irqs tmp_irq[2];
 	unsigned int paddr = 0;
 	unsigned int vaddr[2] = { 0, 0 };
-	int zsnode, tmpnode, iospace, slave, len, seen, sun4u_irq;
+	int zsnode, tmpnode, iospace, slave, len;
+	int cpunode = 0, bbnode = 0;
 	static int irq = 0;
+	int chipid = chip;
 
 #if CONFIG_AP1000
         printk("No zs chip\n");
@@ -2014,7 +1962,10 @@
 				if (board == (chip >> 1)) {
 					node = prom_getchild(tmpnode);
 					if (node && (node = prom_searchsiblings(node, "bootbus"))) {
-						zsnode = node;
+						cpunode = tmpnode;
+						bbnode = node;
+						zsnode = prom_getchild(node);
+						chipid = (chip & 1);
 						break;
 					}
 				}
@@ -2022,10 +1973,6 @@
 			}
 			if (!tmpnode)
 				panic ("get_zs: couldn't find board%d's bootbus\n", chip >> 1);
-		} else if (sparc_cpu_model == sun4u) {
-			tmpnode = prom_searchsiblings(zsnode, "sbus");
-			if(tmpnode)
-				zsnode = prom_getchild(tmpnode);
 		} else {
 			tmpnode = prom_searchsiblings(zsnode, "obio");
 			if(tmpnode)
@@ -2033,41 +1980,46 @@
 		}
 		if(!zsnode)
 			panic("get_zs no zs serial prom node");
-		seen = 0;
 		while(zsnode) {
 			zsnode = prom_searchsiblings(zsnode, "zs");
 			slave = prom_getintdefault(zsnode, "slave", -1);
-			if((slave == chip) ||
-			   (sparc_cpu_model == sun4u && seen == chip)) {
+			if(slave == chipid) {
 				/* The one we want */
-				len = prom_getproperty(zsnode, "address",
-						       (void *) vaddr,
-						       sizeof(vaddr));
-        			if (len % sizeof(unsigned int)) {
-					prom_printf("WHOOPS:  proplen for %s "
-						"was %d, need multiple of "
-						"%d\n", "address", len,
-						sizeof(unsigned int));
-					panic("zilog: address property");
-				}
-				zs_nodes[chip] = zsnode;
-				if(sparc_cpu_model == sun4u) {
-					len = prom_getproperty(zsnode, "interrupts",
-							       (char *) &sun4u_irq,
-							       sizeof(tmp_irq));
-					tmp_irq[0].pri = sun4u_irq;
-				} else {
-					len = prom_getproperty(zsnode, "intr",
-							       (char *) tmp_irq,
-							       sizeof(tmp_irq));
-					if (len % sizeof(struct linux_prom_irqs)) {
-						prom_printf(
-						      "WHOOPS:  proplen for %s "
-						      "was %d, need multiple of "
-						      "%d\n", "address", len,
-						      sizeof(struct linux_prom_irqs));
+				if (sparc_cpu_model != sun4d) {
+					len = prom_getproperty(zsnode, "address",
+							       (void *) vaddr,
+							       sizeof(vaddr));
+        				if (len % sizeof(unsigned int)) {
+						prom_printf("WHOOPS:  proplen for %s "
+							"was %d, need multiple of "
+							"%d\n", "address", len,
+							sizeof(unsigned int));
 						panic("zilog: address property");
 					}
+				} else {
+					/* On sun4d don't have address property :( */
+					struct linux_prom_registers zsreg[4];
+					
+					if (prom_getproperty(zsnode, "reg", (char *)zsreg, sizeof(zsreg)) == -1) {
+						prom_printf ("Cannot map zs regs\n");
+						prom_halt();
+					}
+					prom_apply_generic_ranges(bbnode, cpunode, zsreg, 1);
+					vaddr[0] = (unsigned long)
+							sparc_alloc_io(zsreg[0].phys_addr, 0, 8,
+								       "Zilog Serial", zsreg[0].which_io, 0);
+				}
+				zs_nodes[chip] = zsnode;
+				len = prom_getproperty(zsnode, "intr",
+						       (char *) tmp_irq,
+						       sizeof(tmp_irq));
+				if (len % sizeof(struct linux_prom_irqs)) {
+					prom_printf(
+					      "WHOOPS:  proplen for %s "
+					      "was %d, need multiple of "
+					      "%d\n", "address", len,
+					      sizeof(struct linux_prom_irqs));
+					panic("zilog: address property");
 				}
 				if(!irq) {
 					irq = zilog_irq = tmp_irq[0].pri;
@@ -2078,7 +2030,6 @@
 				break;
 			}
 			zsnode = prom_getsibling(zsnode);
-			seen++;
 		}
 		if(!zsnode)
 			panic("get_zs whee chip not found");
@@ -2089,242 +2040,6 @@
 	return (struct sun_zslayout *)(unsigned long) vaddr[0];
 }
 #endif
-
-static inline void
-init_zscons_termios(struct termios *termios)
-{
-	char mode[16], buf[16];
-	char *mode_prop = "ttyX-mode";
-	char *cd_prop = "ttyX-ignore-cd";
-	char *dtr_prop = "ttyX-rts-dtr-off";
-	char *s;
-	int baud, bits, cflag;
-	char parity;
-	int topnd, nd;
-	int channel, stop;
-	int carrier = 0;
-	int rtsdtr = 1;
-	extern int serial_console;
-
-	if (!serial_console)
-		return;
-
-	if (serial_console == 1) {
-		mode_prop[3] = 'a';
-		cd_prop[3] = 'a';
-		dtr_prop[3] = 'a';
-	} else {
-		mode_prop[3] = 'b';
-		cd_prop[3] = 'b';
-		dtr_prop[3] = 'b';
-	}
-
-	topnd = prom_getchild(prom_root_node);
-	nd = prom_searchsiblings(topnd, "options");
-	if (!nd) {
-		strcpy(mode, "9600,8,n,1,-");
-		goto no_options;
-	}
-
-	if (!prom_node_has_property(nd, mode_prop)) {
-		strcpy(mode, "9600,8,n,1,-");
-		goto no_options;
-	}
-
-	memset(mode, 0, sizeof(mode));
-	prom_getstring(nd, mode_prop, mode, sizeof(mode));
-
-	if (prom_node_has_property(nd, cd_prop)) {
-		memset(buf, 0, sizeof(buf));
-		prom_getstring(nd, cd_prop, buf, sizeof(buf));
-		if (!strcmp(buf, "false"))
-			carrier = 1;
-
-		/* XXX this is unused below. */
-	}
-
-	if (prom_node_has_property(nd, cd_prop)) {
-		memset(buf, 0, sizeof(buf));
-		prom_getstring(nd, cd_prop, buf, sizeof(buf));
-		if (!strcmp(buf, "false"))
-			rtsdtr = 0;
-
-		/* XXX this is unused below. */
-	}
-
-no_options:
-	cflag = CREAD | HUPCL | CLOCAL;
-
-	s = mode;
-	baud = simple_strtoul(s, 0, 0);
-	s = strchr(s, ',');
-	bits = simple_strtoul(++s, 0, 0);
-	s = strchr(s, ',');
-	parity = *(++s);
-	s = strchr(s, ',');
-	stop = simple_strtoul(++s, 0, 0);
-	s = strchr(s, ',');
-	/* XXX handshake is not handled here. */
-
-	for (channel = 0; channel < NUM_CHANNELS; channel++)
-		if (zs_soft[channel].is_cons)
-			break;
-
-	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;
-		default:
-			baud = 9600;
-		case 9600:
-			cflag |= B9600;
-			break;
-		case 19200:
-			cflag |= B19200;
-			break;
-		case 38400:
-			cflag |= B38400;
-			break;
-	}
-	zs_soft[channel].zs_baud = baud;
-
-	switch (bits) {
-		case 5:
-			zscons_regs[3] = Rx5 | RxENAB;
-			zscons_regs[5] = Tx5 | TxENAB;
-			zs_soft[channel].parity_mask = 0x1f;
-			cflag |= CS5;
-			break;
-		case 6:
-			zscons_regs[3] = Rx6 | RxENAB;
-			zscons_regs[5] = Tx6 | TxENAB;
-			zs_soft[channel].parity_mask = 0x3f;
-			cflag |= CS6;
-			break;
-		case 7:
-			zscons_regs[3] = Rx7 | RxENAB;
-			zscons_regs[5] = Tx7 | TxENAB;
-			zs_soft[channel].parity_mask = 0x7f;
-			cflag |= CS7;
-			break;
-		default:
-		case 8:
-			zscons_regs[3] = Rx8 | RxENAB;
-			zscons_regs[5] = Tx8 | TxENAB;
-			zs_soft[channel].parity_mask = 0xff;
-			cflag |= CS8;
-			break;
-	}
-	zscons_regs[5] |= DTR;
-
-	switch (parity) {
-		case 'o':
-			zscons_regs[4] |= PAR_ENAB;
-			cflag |= (PARENB | PARODD);
-			break;
-		case 'e':
-			zscons_regs[4] |= (PAR_ENAB | PAR_EVEN);
-			cflag |= PARENB;
-			break;
-		default:
-		case 'n':
-			break;
-	}
-
-	switch (stop) {
-		default:
-		case 1:
-			zscons_regs[4] |= SB1;
-			break;
-		case 2:
-			cflag |= CSTOPB;
-			zscons_regs[4] |= SB2;
-			break;
-	}
-
-	termios->c_cflag = cflag;
-}
-
-__initfunc(static void serial_finish_init(void (*printfunc)(const char *, unsigned)))
-{
-	extern unsigned char *linux_serial_image;
-	char buffer[2048];
-	
-	sprintf (buffer, linux_serial_image, UTS_RELEASE);
-	(*printfunc)(buffer, strlen(buffer));
-}
-
-static inline void
-zs_cons_check(struct sun_serial *ss, int channel)
-{
-	int i, o, io;
-	static int consout_registered = 0;
-	static int msg_printed = 0;
-	static struct console console = {
-		zs_console_print, 0,
-		zs_console_wait_key, zs_console_device };
-
-	i = o = io = 0;
-
-	/* Is this one of the serial console lines? */
-	if((zs_cons_chanout != channel) &&
-	   (zs_cons_chanin != channel))
-		return;
-	zs_conschan = ss->zs_channel;
-	zs_consinfo = ss;
-
-	/* Register the console output putchar, if necessary */
-	if((zs_cons_chanout == channel)) {
-		o = 1;
-		/* double whee.. */
-		if(!consout_registered) {
-			serial_finish_init (zs_console_print);
-			register_console(&console);
-			consout_registered = 1;
-		}
-	}
-
-	/* If this is console input, we handle the break received
-	 * status interrupt on this line to mean prom_halt().
-	 */
-	if(zs_cons_chanin == channel) {
-		ss->break_abort = 1;
-		i = 1;
-	}
-	if(o && i)
-		io = 1;
-
-	/* Set flag variable for this port so that it cannot be
-	 * opened for other uses by accident.
-	 */
-	ss->is_cons = 1;
-
-	if(io) {
-		if(!msg_printed) {
-			printk("zs%d: console I/O\n", ((channel>>1)&1));
-			msg_printed = 1;
-		}
-	} else {
-		printk("zs%d: console %s\n", ((channel>>1)&1),
-		       (i==1 ? "input" : (o==1 ? "output" : "WEIRD")));
-	}
-}
-
 /* This is for the auto baud rate detection in the mouse driver. */
 void zs_change_mouse_baud(int newbaud)
 {
@@ -2347,10 +2062,11 @@
 	if(sparc_cpu_model == sun4)
 		goto no_probe;
 
+	NUM_SERIAL = 0;
+	
 	node = prom_getchild(prom_root_node);
 	if (sparc_cpu_model == sun4d) {
 		node = prom_searchsiblings(node, "boards");
-		NUM_SERIAL = 0;
 		if (!node)
 			panic ("Cannot find out count of boards");
 		else
@@ -2360,22 +2076,38 @@
 			node = prom_getsibling(node);
 		}
 		goto no_probe;
-	} else if (sparc_cpu_model == sun4u) {
-		node = prom_searchsiblings(node, "sbus");
-		if(node)
+	}
+#ifdef __sparc_v9__
+	else if (sparc_cpu_model == sun4u) {
+		int central_node;
+
+		/* Central bus zilogs must be checked for first,
+		 * since Enterprise boxes have SBUS as well.
+		 */
+		central_node = prom_finddevice("/central");
+		if(central_node != 0 && central_node != -1)
+			node = prom_searchsiblings(prom_getchild(central_node), "fhc");
+		else
+			node = prom_searchsiblings(node, "sbus");
+		if(node != 0 && node != -1)
 			node = prom_getchild(node);
-		if(!node)
+		if(node == 0 || node == -1)
 			return -ENODEV;
-	} else {
+	}
+#endif /* __sparc_v9__ */
+	else {
 		node = prom_searchsiblings(node, "obio");
 		if(node)
 			node = prom_getchild(node);
+		NUM_SERIAL = 2;
 		goto no_probe;
 	}
 
 	node = prom_searchsiblings(node, "zs");
 	if (!node)
 		return -ENODEV;
+		
+	NUM_SERIAL = 2;
 
 no_probe:
 	p = (char *)((*memory_start + 7) & ~7);
@@ -2399,17 +2131,85 @@
 	*memory_start = (((unsigned long)p) + i + 7) & ~7;
 
 	/* Fill in rs_ops struct... */
+#ifdef CONFIG_SERIAL_CONSOLE
+	sunserial_setinitfunc(memory_start, zs_console_init);
+#endif
 	sunserial_setinitfunc(memory_start, zs_init);
-	rs_ops.rs_cons_hook = zs_cons_hook;
 	rs_ops.rs_kgdb_hook = zs_kgdb_hook;
 	rs_ops.rs_change_mouse_baud = zs_change_mouse_baud;
 
+	sunkbd_setinitfunc(memory_start, sun_kbd_init);
+	kbd_ops.compute_shiftstate = sun_compute_shiftstate;
+	kbd_ops.setledstate = sun_setledstate;
+	kbd_ops.getledstate = sun_getledstate;
+	kbd_ops.setkeycode = sun_setkeycode;
+	kbd_ops.getkeycode = sun_getkeycode;
+#ifdef CONFIG_PCI
+	sunkbd_install_keymaps(memory_start, sun_key_maps, sun_keymap_count,
+			       sun_func_buf, sun_func_table,
+			       sun_funcbufsize, sun_funcbufleft,
+			       sun_accent_table, sun_accent_table_size);
+#endif
 	return 0;
 }
 
+static inline void zs_prepare(void)
+{
+	int channel, chip;
+	unsigned long flags;
+
+	if (!NUM_SERIAL) return;
+	
+	save_and_cli(flags);
+	
+	/* Set up our interrupt linked list */
+	zs_chain = &zs_soft[0];
+	for(channel = 0; channel < NUM_CHANNELS - 1; channel++) {
+		zs_soft[channel].zs_next = &zs_soft[channel + 1];
+		zs_soft[channel].line = channel;
+	}
+	zs_soft[channel].zs_next = 0;
+
+	/* Initialize Softinfo */
+	for(chip = 0; chip < NUM_SERIAL; chip++) {
+		/* If we are doing kgdb over one of the channels on
+		 * chip zero, kgdb_channel will be set to 1 by the
+		 * zs_kgdb_hook() routine below.
+		 */
+		if(!zs_chips[chip]) {
+			zs_chips[chip] = get_zs(chip);
+			/* Two channels per chip */
+			zs_channels[(chip*2)] = &zs_chips[chip]->channelA;
+			zs_channels[(chip*2)+1] = &zs_chips[chip]->channelB;
+			zs_soft[(chip*2)].kgdb_channel = 0;
+			zs_soft[(chip*2)+1].kgdb_channel = 0;
+		}
+
+		/* First, set up channel A on this chip. */
+		channel = chip * 2;
+		zs_soft[channel].zs_channel = zs_channels[channel];
+		zs_soft[channel].change_needed = 0;
+		zs_soft[channel].clk_divisor = 16;
+		zs_soft[channel].cons_keyb = 0;
+		zs_soft[channel].cons_mouse = 0;
+		zs_soft[channel].channelA = 1;
+
+		/* Now, channel B */
+		channel++;
+		zs_soft[channel].zs_channel = zs_channels[channel];
+		zs_soft[channel].change_needed = 0;
+		zs_soft[channel].clk_divisor = 16;
+		zs_soft[channel].cons_keyb = 0;
+		zs_soft[channel].cons_mouse = 0;
+		zs_soft[channel].channelA = 0;
+	}
+	
+	restore_flags(flags);
+}
+
 __initfunc(int zs_init(void))
 {
-	int chip, channel, brg, i;
+	int channel, brg, i;
 	unsigned long flags;
 	struct sun_serial *info;
 	char dummy;
@@ -2420,7 +2220,7 @@
 #endif
 
 #ifdef CONFIG_PCI
-	if (prom_searchsiblings(prom_getchild(prom_root_node), "pci"))
+	if (pcibios_present())
 		return 0;
 #endif
 
@@ -2444,7 +2244,6 @@
 	serial_driver.type = TTY_DRIVER_TYPE_SERIAL;
 	serial_driver.subtype = SERIAL_TYPE_NORMAL;
 	serial_driver.init_termios = tty_std_termios;
-
 	serial_driver.init_termios.c_cflag =
 		B9600 | CS8 | CREAD | HUPCL | CLOCAL;
 	serial_driver.flags = TTY_DRIVER_REAL_RAW;
@@ -2472,8 +2271,6 @@
 	serial_driver.read_proc = 0;
 	serial_driver.proc_entry = 0;
 
-	init_zscons_termios(&serial_driver.init_termios);
-
 	/*
 	 * The callout device is just like normal device except for
 	 * major number and the subtype code.
@@ -2487,48 +2284,11 @@
 		panic("Couldn't register serial driver\n");
 	if (tty_register_driver(&callout_driver))
 		panic("Couldn't register callout driver\n");
-	
-	save_flags(flags); cli();
 
-	/* Set up our interrupt linked list */
-	zs_chain = &zs_soft[0];
-	for(channel = 0; channel < NUM_CHANNELS - 1; channel++)
-		zs_soft[channel].zs_next = &zs_soft[channel + 1];
-	zs_soft[channel + 1].zs_next = 0;
+	save_flags(flags); cli();
 
 	/* Initialize Softinfo */
-	for(chip = 0; chip < NUM_SERIAL; chip++) {
-		/* If we are doing kgdb over one of the channels on
-		 * chip zero, kgdb_channel will be set to 1 by the
-		 * zs_kgdb_hook() routine below.
-		 */
-		if(!zs_chips[chip]) {
-			zs_chips[chip] = get_zs(chip);
-			/* Two channels per chip */
-			zs_channels[(chip*2)] = &zs_chips[chip]->channelA;
-			zs_channels[(chip*2)+1] = &zs_chips[chip]->channelB;
-			zs_soft[(chip*2)].kgdb_channel = 0;
-			zs_soft[(chip*2)+1].kgdb_channel = 0;
-		}
-
-		/* First, set up channel A on this chip. */
-		channel = chip * 2;
-		zs_soft[channel].zs_channel = zs_channels[channel];
-		zs_soft[channel].change_needed = 0;
-		zs_soft[channel].clk_divisor = 16;
-		zs_soft[channel].cons_keyb = 0;
-		zs_soft[channel].cons_mouse = 0;
-		zs_soft[channel].channelA = 1;
-
-		/* Now, channel B */
-		channel++;
-		zs_soft[channel].zs_channel = zs_channels[channel];
-		zs_soft[channel].change_needed = 0;
-		zs_soft[channel].clk_divisor = 16;
-		zs_soft[channel].cons_keyb = 0;
-		zs_soft[channel].cons_mouse = 0;
-		zs_soft[channel].channelA = 0;
-	}
+	zs_prepare();
 
 	/* Initialize Hardware */
 	for(channel = 0; channel < NUM_CHANNELS; channel++) {
@@ -2719,50 +2479,13 @@
 	return 0;
 }
 
-/* Hooks for running a serial console.  con_init() calls this if the
- * console is being run over one of the ttya/ttyb serial ports.
- * 'chip' should be zero, as chip 1 drives the mouse/keyboard.
- * 'channel' is decoded as 0=TTYA 1=TTYB, note that the channels
- * are addressed backwards, channel B is first, then channel A.
- */
-void
-zs_cons_hook(int chip, int out, int line)
-{
-	int channel;
-
-#ifdef CONFIG_PCI
-	if (prom_searchsiblings(prom_getchild(prom_root_node), "pci"))
-		return;
-#endif
-
-	if(chip)
-		panic("zs_cons_hook called with chip not zero");
-	if(line != 1 && line != 2)
-		panic("zs_cons_hook called with line not ttya or ttyb");
-	channel = line - 1;
-	if(!zs_chips[chip]) {
-		zs_chips[chip] = get_zs(chip);
-		/* Two channels per chip */
-		zs_channels[(chip*2)] = &zs_chips[chip]->channelA;
-		zs_channels[(chip*2)+1] = &zs_chips[chip]->channelB;
-	}
-	zs_soft[channel].zs_channel = zs_channels[channel];
-	zs_soft[channel].change_needed = 0;
-	zs_soft[channel].clk_divisor = 16;
-	if(out)
-		zs_cons_chanout = ((chip * 2) + channel);
-	else
-		zs_cons_chanin = ((chip * 2) + channel);
-	zs_cons_check(&zs_soft[channel], channel);
-}
-
 /* This is called at boot time to prime the kgdb serial debugging
  * serial line.  The 'tty_num' argument is 0 for /dev/ttya and 1
  * for /dev/ttyb which is determined in setup_arch() from the
  * boot command line flags.
  */
-void
-zs_kgdb_hook(int tty_num)
+__initfunc(static void
+zs_kgdb_hook(int tty_num))
 {
 	int chip = 0;
 
@@ -2784,3 +2507,183 @@
         ZS_CLEARERR(zs_kgdbchan);
         ZS_CLEARFIFO(zs_kgdbchan);
 }
+
+#ifdef CONFIG_SERIAL_CONSOLE
+
+/* This is for console output over ttya/ttyb */
+static void
+zs_console_putchar(struct sun_serial *info, char ch)
+{
+	unsigned long flags;
+
+	if(!info->zs_channel)
+		return;
+
+	save_flags(flags); cli();
+	zs_put_char(info->zs_channel, ch);
+	restore_flags(flags);
+}
+
+/*
+ * Fair output driver allows a process to speak.
+ */
+static void zs_fair_output(struct sun_serial *info)
+{
+	int left;		/* Output no more than that */
+	unsigned long flags;
+	char c;
+
+	if (info == 0) return;
+	if (info->xmit_buf == 0) return;
+
+	save_flags(flags);  cli();
+	left = info->xmit_cnt;
+	while (left != 0) {
+		c = info->xmit_buf[info->xmit_tail];
+		info->xmit_tail = (info->xmit_tail+1) & (SERIAL_XMIT_SIZE-1);
+		info->xmit_cnt--;
+		restore_flags(flags);
+
+		zs_console_putchar(info, c);
+
+		cli();
+		left = MIN(info->xmit_cnt, left-1);
+	}
+
+	/* Last character is being transmitted now (hopefully). */
+	info->zs_channel->control = RES_Tx_P;
+	udelay(5);
+
+	restore_flags(flags);
+	return;
+}
+
+/*
+ * zs_console_write is registered for printk.
+ */
+static void
+zs_console_write(struct console *con, const char *s, unsigned count)
+{
+	struct sun_serial *info;
+	int i;
+
+	info = zs_soft + con->index;
+
+	for (i = 0; i < count; i++, s++) {
+		if(*s == '\n')
+			zs_console_putchar(info, '\r');
+		zs_console_putchar(info, *s);
+	}
+
+	/* Comment this if you want to have a strict interrupt-driven output */
+	zs_fair_output(info);
+}
+
+static int
+zs_console_wait_key(struct console *con)
+{
+	sleep_on(&keypress_wait);
+	return 0;
+}
+
+static kdev_t zs_console_device(struct console *con)
+{
+	return MKDEV(TTY_MAJOR, 64 + con->index);
+}
+
+__initfunc(static int
+zs_console_setup(struct console *con, char *options))
+{
+	struct sun_serial *info;
+	int i, brg, baud;
+
+	info = zs_soft + con->index;
+	info->is_cons = 1;
+
+	printk("Console: ttyS%d (Zilog8530)\n", info->line);
+
+	sunserial_console_termios(con);
+
+	i = con->cflag & CBAUD;
+	if (con->cflag & CBAUDEX) {
+		i &= ~CBAUDEX;
+		con->cflag &= ~CBAUDEX;
+	}
+	baud = baud_table[i];
+	info->zs_baud = baud;
+
+	switch (con->cflag & CSIZE) {
+		case CS5:
+			zscons_regs[3] = Rx5 | RxENAB;
+			zscons_regs[5] = Tx5 | TxENAB;
+			info->parity_mask = 0x1f;
+			break;
+		case CS6:
+			zscons_regs[3] = Rx6 | RxENAB;
+			zscons_regs[5] = Tx6 | TxENAB;
+			info->parity_mask = 0x3f;
+			break;
+		case CS7:
+			zscons_regs[3] = Rx7 | RxENAB;
+			zscons_regs[5] = Tx7 | TxENAB;
+			info->parity_mask = 0x7f;
+			break;
+		default:
+		case CS8:
+			zscons_regs[3] = Rx8 | RxENAB;
+			zscons_regs[5] = Tx8 | TxENAB;
+			info->parity_mask = 0xff;
+			break;
+	}
+	zscons_regs[5] |= DTR;
+
+	if (con->cflag & PARENB)
+		zscons_regs[4] |= PAR_ENAB;
+	if (!(con->cflag & PARODD))
+		zscons_regs[4] |= PAR_EVEN;
+
+	if (con->cflag & CSTOPB)
+		zscons_regs[4] |= SB2;
+	else
+		zscons_regs[4] |= SB1;
+
+	brg = BPS_TO_BRG(baud, ZS_CLOCK / info->clk_divisor);
+	zscons_regs[12] = brg & 0xff;
+	zscons_regs[13] = (brg >> 8) & 0xff;
+
+	memcpy(info->curregs, zscons_regs, sizeof(zscons_regs));
+	load_zsregs(info, zscons_regs);
+
+	ZS_CLEARERR(info->zs_channel);
+	ZS_CLEARFIFO(info->zs_channel);
+	return 0;
+}
+
+static struct console zs_console = {
+	"ttyS",
+	zs_console_write,
+	NULL,
+	zs_console_device,
+	zs_console_wait_key,
+	NULL,
+	zs_console_setup,
+	CON_PRINTBUFFER,
+	-1,
+	0,
+	NULL
+};
+
+__initfunc(static int
+zs_console_init(void))
+{
+	extern int con_is_present(void);
+
+	if (con_is_present())
+		return 0;
+
+	zs_console.index = serial_console - 1;
+	register_console(&zs_console);
+	return 0;
+}
+
+#endif /* CONFIG_SERIAL_CONSOLE */

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen, slshen@lbl.gov