patch-2.2.11 linux/drivers/sgi/char/sgiserial.c

Next file: linux/drivers/sgi/char/sgiserial.h
Previous file: linux/drivers/sgi/char/sgicons.c
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v2.2.10/linux/drivers/sgi/char/sgiserial.c linux/drivers/sgi/char/sgiserial.c
@@ -1,6 +1,7 @@
 /* sgiserial.c: Serial port driver for SGI machines.
  *
  * Copyright (C) 1996 David S. Miller (dm@engr.sgi.com)
+ *
  */
 
 #include <linux/config.h> /* for CONFIG_REMOTE_DEBUG */
@@ -18,6 +19,7 @@
 #include <linux/kernel.h>
 #include <linux/delay.h>
 #include <linux/console.h>
+#include <linux/init.h>
 
 #include <asm/io.h>
 #include <asm/irq.h>
@@ -39,33 +41,16 @@
 struct sgi_zschannel *zs_channels[NUM_CHANNELS] = { 0, 0, };
 struct sgi_zschannel *zs_conschan;
 struct sgi_zschannel *zs_kgdbchan;
-int zs_nodes[NUM_SERIAL] = { 0, };
 
 struct sgi_serial zs_soft[NUM_CHANNELS];
 struct sgi_serial *zs_chain;  /* IRQ servicing chain */
 static int zilog_irq = 21;
 
-struct tty_struct zs_ttys[NUM_CHANNELS];
-/** struct tty_struct *zs_constty; **/
-
 /* Console hooks... */
 static int zs_cons_chanout = 0;
 static int zs_cons_chanin = 0;
 struct sgi_serial *zs_consinfo = 0;
 
-static struct console sgi_console_driver = {
-        "debug",
-        NULL,                   /* write */
-        NULL,                   /* read */
-        NULL,                   /* device */
-        NULL,                   /* wait_key */
-        NULL,                   /* unblank */
-        NULL,                   /* setup */
-        CON_PRINTBUFFER,
-        -1,
-        0,
-        NULL
-};
 static unsigned char kgdb_regs[16] = {
 	0, 0, 0,                     /* write 0, 1, 2 */
 	(Rx8 | RxENABLE),            /* write 3 */
@@ -80,6 +65,22 @@
 	(DCDIE)                      /* write 15 */
 };
 
+static unsigned char zscons_regs[16] = {
+	0,                           /* write 0 */
+	(EXT_INT_ENAB | INT_ALL_Rx), /* write 1 */
+	0,                           /* write 2 */
+	(Rx8 | RxENABLE),            /* write 3 */
+	(X16CLK),                    /* write 4 */
+	(DTR | Tx8 | TxENAB),        /* write 5 */
+	0, 0, 0,                     /* write 6, 7, 8 */
+	(NV | MIE),                  /* write 9 */
+	(NRZ),                       /* write 10 */
+	(TCBR | RCBR),               /* write 11 */
+	0, 0,                        /* BRG time constant, write 12 + 13 */
+	(BRENABL),                   /* write 14 */
+	(DCDIE | CTSIE | TxUIE | BRKIE) /* write 15 */
+};
+
 #define ZS_CLOCK         3672000   /* Zilog input clock rate */
 
 DECLARE_TASK_QUEUE(tq_serial);
@@ -161,6 +162,10 @@
  * driver work on the Sun4 which needs a settling delay after each chip
  * register access, other machines handle this in hardware via auxiliary
  * flip-flops which implement the settle time we do in software.
+ *
+ * read_zsreg() and write_zsreg() may get called from rs_kgdb_hook() before
+ * interrupts are enabled. Therefore we have to check ioc_iocontrol before we
+ * access it.
  */
 static inline unsigned char read_zsreg(struct sgi_zschannel *channel, unsigned char reg)
 {
@@ -169,7 +174,8 @@
 
 	udelay(2);
 	channel->control = reg;
-	junk = ioc_icontrol->istat0;
+	if (ioc_icontrol)
+		junk = ioc_icontrol->istat0;
 	udelay(1);
 	retval = channel->control;
 	return retval;
@@ -181,10 +187,12 @@
 
 	udelay(2);
 	channel->control = reg;
-	junk = ioc_icontrol->istat0;
+	if (ioc_icontrol)
+		junk = ioc_icontrol->istat0;
 	udelay(1);
 	channel->control = value;
-	junk = ioc_icontrol->istat0;
+	if (ioc_icontrol)
+		junk = ioc_icontrol->istat0;
 	return;
 }
 
@@ -235,7 +243,7 @@
 		kgdb_regs[R1] = 0;
 		kgdb_regs[R9] &= ~MIE;
 	}
-	brg = BPS_TO_BRG(bps, ZS_CLOCK/16);
+	brg = BPS_TO_BRG(bps, ZS_CLOCK/ss->clk_divisor);
 	kgdb_regs[R12] = (brg & 255);
 	kgdb_regs[R13] = ((brg >> 8) & 255);
 	load_zsregs(ss->zs_channel, kgdb_regs);
@@ -552,7 +560,7 @@
 	struct sgi_serial * info = (struct sgi_serial *) dev_id;
 	unsigned char zs_intreg;
 
-	zs_intreg = read_zsreg(info->zs_channel, 3);
+	zs_intreg = read_zsreg(info->zs_next->zs_channel, 3);
 
 	/* NOTE: The read register 3, which holds the irq status,
 	 *       does so for both channels on each chip.  Although
@@ -564,25 +572,25 @@
 #define CHAN_B_IRQMASK (CHBRxIP | CHBTxIP | CHBEXT)
 
 	/* *** Chip 1 *** */
-	/* Channel A -- /dev/ttya, could be the console */
-	if(zs_intreg & CHAN_A_IRQMASK) {
-		if (zs_intreg & CHARxIP)
+	/* Channel B -- /dev/ttyb, could be the console */
+	if(zs_intreg & CHAN_B_IRQMASK) {
+		if (zs_intreg & CHBRxIP)
 			receive_chars(info, regs);
-		if (zs_intreg & CHATxIP)
+		if (zs_intreg & CHBTxIP)
 			transmit_chars(info);
-		if (zs_intreg & CHAEXT)
+		if (zs_intreg & CHBEXT)
 			status_handle(info);
 	}
 
 	info=info->zs_next;
 
-	/* Channel B -- /dev/ttyb, could be the console */
-	if(zs_intreg & CHAN_B_IRQMASK) {
-		if (zs_intreg & CHBRxIP)
+	/* Channel A -- /dev/ttya, could be the console */
+	if(zs_intreg & CHAN_A_IRQMASK) {
+		if (zs_intreg & CHARxIP)
 			receive_chars(info, regs);
-		if (zs_intreg & CHBTxIP)
+		if (zs_intreg & CHATxIP)
 			transmit_chars(info);
-		if (zs_intreg & CHBEXT)
+		if (zs_intreg & CHAEXT)
 			status_handle(info);
 	}
 }
@@ -904,7 +912,7 @@
 /* These are for receiving and sending characters under the kgdb
  * source level kernel debugger.
  */
-void putDebugChar(char kgdb_char)
+int putDebugChar(char kgdb_char)
 {
 	struct sgi_zschannel *chan = zs_kgdbchan;
 	volatile unsigned char junk;
@@ -919,6 +927,8 @@
 	chan->data = kgdb_char;
 	junk = ioc_icontrol->istat0;
 	restore_flags(flags);
+
+	return 1;
 }
 
 char getDebugChar(void)
@@ -971,63 +981,6 @@
 	return;
 }
 
-/*
- * zs_console_print is registered for printk.
- */
-
-static void zs_console_print(struct console *co, const char *str, unsigned int count)
-{
-
-	while(count--) {
-		if(*str == '\n')
-			rs_put_char('\r');
-		rs_put_char(*str++);
-	}
-
-	/* Comment this if you want to have a strict interrupt-driven output */
-	rs_fair_output();
-}
-
-static void rs_flush_chars(struct tty_struct *tty)
-{
-	struct sgi_serial *info = (struct sgi_serial *)tty->driver_data;
-	unsigned long flags;
-
-	if (serial_paranoia_check(info, tty->device, "rs_flush_chars"))
-		return;
-
-	if (info->xmit_cnt <= 0 || tty->stopped || tty->hw_stopped ||
-	    !info->xmit_buf)
-		return;
-
-	/* Enable transmitter */
-	save_flags(flags); cli();
-	info->curregs[1] |= TxINT_ENAB|EXT_INT_ENAB;
-	info->pendregs[1] |= TxINT_ENAB|EXT_INT_ENAB;
-	write_zsreg(info->zs_channel, 1, info->curregs[1]);
-	info->curregs[5] |= TxENAB;
-	info->pendregs[5] |= TxENAB;
-	write_zsreg(info->zs_channel, 5, info->curregs[5]);
-
-	/*
-	 * Send a first (bootstrapping) character. A best solution is
-	 * to call transmit_chars() here which handles output in a
-	 * generic way. Current transmit_chars() not only transmits,
-	 * but resets interrupts also what we do not desire here.
-	 * XXX Discuss with David.
-	 */
-	if (info->zs_channel->control & Tx_BUF_EMP) {
-		volatile unsigned char junk;
-
-		/* Send char */
-		udelay(2);
-		info->zs_channel->data = info->xmit_buf[info->xmit_tail++];
-		junk = ioc_icontrol->istat0;
-		info->xmit_tail = info->xmit_tail & (SERIAL_XMIT_SIZE-1);
-		info->xmit_cnt--;
-	}
-	restore_flags(flags);
-}
 
 static int rs_write(struct tty_struct * tty, int from_user,
 		    const unsigned char *buf, int count)
@@ -1117,6 +1070,47 @@
 		(tty->ldisc.write_wakeup)(tty);
 }
 
+static void rs_flush_chars(struct tty_struct *tty)
+{
+	struct sgi_serial *info = (struct sgi_serial *)tty->driver_data;
+	unsigned long flags;
+
+	if (serial_paranoia_check(info, tty->device, "rs_flush_chars"))
+		return;
+
+	if (info->xmit_cnt <= 0 || tty->stopped || tty->hw_stopped ||
+	    !info->xmit_buf)
+		return;
+
+	/* Enable transmitter */
+	save_flags(flags); cli();
+	info->curregs[1] |= TxINT_ENAB|EXT_INT_ENAB;
+	info->pendregs[1] |= TxINT_ENAB|EXT_INT_ENAB;
+	write_zsreg(info->zs_channel, 1, info->curregs[1]);
+	info->curregs[5] |= TxENAB;
+	info->pendregs[5] |= TxENAB;
+	write_zsreg(info->zs_channel, 5, info->curregs[5]);
+
+	/*
+	 * Send a first (bootstrapping) character. A best solution is
+	 * to call transmit_chars() here which handles output in a
+	 * generic way. Current transmit_chars() not only transmits,
+	 * but resets interrupts also what we do not desire here.
+	 * XXX Discuss with David.
+	 */
+	if (info->zs_channel->control & Tx_BUF_EMP) {
+		volatile unsigned char junk;
+
+		/* Send char */
+		udelay(2);
+		info->zs_channel->data = info->xmit_buf[info->xmit_tail++];
+		junk = ioc_icontrol->istat0;
+		info->xmit_tail = info->xmit_tail & (SERIAL_XMIT_SIZE-1);
+		info->xmit_cnt--;
+	}
+	restore_flags(flags);
+}
+
 /*
  * ------------------------------------------------------------
  * rs_throttle()
@@ -1614,7 +1608,7 @@
 		if (!(info->flags & ZILOG_CALLOUT_ACTIVE) &&
 		    !(info->flags & ZILOG_CLOSING) && do_clocal)
 			break;
-		if (current->signal & ~current->blocked) {
+		if (signal_pending(current)) {
 			retval = -ERESTARTSYS;
 			break;
 		}
@@ -1725,17 +1719,14 @@
 }
 
 
-
 static inline void
 rs_cons_check(struct sgi_serial *ss, int channel)
 {
 	int i, o, io;
-	static consout_registered = 0;
-	static msg_printed = 0;
+	static int msg_printed = 0;
 
 	i = o = io = 0;
 
-
 	/* Is this one of the serial console lines? */
 	if((zs_cons_chanout != channel) &&
 	   (zs_cons_chanin != channel))
@@ -1744,18 +1735,6 @@
 	zs_consinfo = ss;
 
 
-	/* Register the console output putchar, if necessary */
-	if((zs_cons_chanout == channel)) {
-		o = 1;
-		/* double whee.. */
-
-		if(!consout_registered) {
-		  sgi_console_driver.write = zs_console_print;
-			register_console(&sgi_console_driver);
-			consout_registered = 1;
-		}
-	}
-
 
 	/* If this is console input, we handle the break received
 	 * status interrupt on this line to mean prom_halt().
@@ -1873,8 +1852,8 @@
 		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_channels[(chip*2)] = &zs_chips[chip]->channelB;
+			zs_channels[(chip*2)+1] = &zs_chips[chip]->channelA;
 			zs_soft[(chip*2)].kgdb_channel = 0;
 			zs_soft[(chip*2)+1].kgdb_channel = 0;
 		}
@@ -1990,14 +1969,14 @@
 	
 	if(chip)
 		panic("rs_cons_hook called with chip not zero");
-	if(line != 1 && line != 2)
+	if(line != 0 && line != 1)
 		panic("rs_cons_hook called with line not ttya or ttyb");
-	channel = line - 1;
+	channel = line;
 	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_channels[(chip*2)] = &zs_chips[chip]->channelB;
+		zs_channels[(chip*2)+1] = &zs_chips[chip]->channelA;
 	}
 	zs_soft[channel].zs_channel = zs_channels[channel];
 	zs_soft[channel].change_needed = 0;
@@ -2041,3 +2020,176 @@
 	udelay(5);
 	ZS_CLEARFIFO(zs_kgdbchan);
 }
+
+static void zs_console_write(struct console *co, const char *str, unsigned int count)
+{
+
+	while(count--) {
+		if(*str == '\n')
+			rs_put_char('\r');
+		rs_put_char(*str++);
+	}
+
+	/* Comment this if you want to have a strict interrupt-driven output */
+	rs_fair_output();
+}
+
+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 sgi_serial *info;
+	int	baud = 9600;
+	int	bits = 8;
+	int	parity = 'n';
+	int	cflag = CREAD | HUPCL | CLOCAL;
+	char	*s;
+	int     i, brg;
+    
+	if (options) {
+		baud = simple_strtoul(options, NULL, 10);
+		s = options;
+		while(*s >= '0' && *s <= '9')
+			s++;
+		if (*s) parity = *s++;
+		if (*s) bits   = *s - '0';
+	}
+
+	/*
+	 *	Now construct a cflag setting.
+	 */
+	switch(baud) {
+		case 1200:
+			cflag |= B1200;
+			break;
+		case 2400:
+			cflag |= B2400;
+			break;
+		case 4800:
+			cflag |= B4800;
+			break;
+		case 19200:
+			cflag |= B19200;
+			break;
+		case 38400:
+			cflag |= B38400;
+			break;
+		case 57600:
+			cflag |= B57600;
+			break;
+		case 115200:
+			cflag |= B115200;
+			break;
+		case 9600:
+		default:
+			cflag |= B9600;
+			break;
+	}
+	switch(bits) {
+		case 7:
+			cflag |= CS7;
+			break;
+		default:
+		case 8:
+			cflag |= CS8;
+			break;
+	}
+	switch(parity) {
+		case 'o': case 'O':
+			cflag |= PARODD;
+			break;
+		case 'e': case 'E':
+			cflag |= PARENB;
+			break;
+	}
+	con->cflag = cflag;
+
+        rs_cons_hook(0, 0, con->index);
+	info = zs_soft + con->index;
+	info->is_cons = 1;
+    
+	printk("Console: ttyS%d (Zilog8530)\n", info->line);
+
+	i = con->cflag & CBAUD;
+	if (con->cflag & CBAUDEX) {
+		i &= ~CBAUDEX;
+		con->cflag &= ~CBAUDEX;
+	}
+	info->zs_baud = baud;
+
+	switch (con->cflag & CSIZE) {
+		case CS5:
+			zscons_regs[3] = Rx5 | RxENABLE;
+			zscons_regs[5] = Tx5 | TxENAB;
+			break;
+		case CS6:
+			zscons_regs[3] = Rx6 | RxENABLE;
+			zscons_regs[5] = Tx6 | TxENAB;
+			break;
+		case CS7:
+			zscons_regs[3] = Rx7 | RxENABLE;
+			zscons_regs[5] = Tx7 | TxENAB;
+			break;
+		default:
+		case CS8:
+			zscons_regs[3] = Rx8 | RxENABLE;
+			zscons_regs[5] = Tx8 | TxENAB;
+			break;
+	}
+	zscons_regs[5] |= DTR;
+
+	if (con->cflag & PARENB)
+		zscons_regs[4] |= PAR_ENA;
+	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));
+	memcpy(info->pendregs, zscons_regs, sizeof(zscons_regs));    
+	load_zsregs(info->zs_channel, zscons_regs);
+	ZS_CLEARERR(info->zs_channel);
+	ZS_CLEARFIFO(info->zs_channel);
+	return 0;
+}
+
+static struct console sgi_console_driver = {
+        "ttyS",
+        zs_console_write,       /* write */
+        NULL,                   /* read */
+        zs_console_device,      /* device */
+        zs_console_wait_key,    /* wait_key */
+        NULL,                   /* unblank */
+        zs_console_setup,       /* setup */
+        CON_PRINTBUFFER,
+        -1,
+        0,
+        NULL
+};
+
+/*
+ *	Register console.
+ */
+__initfunc (long serial_console_init(long kmem_start, long kmem_end))
+{
+	register_console(&sgi_console_driver);
+	return kmem_start;
+}
+
+

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)