sio(4) driver

Thomas Moestl t.moestl at tu-bs.de
Fri Aug 1 10:03:54 PDT 2003


On Fri, 2003/08/01 at 16:22:40 +0300, Maxim Mazurok wrote:
> I need to add 2 async serial port to my sparc.
> 
> 5.1-RELEASE
> 
> I add to kernel 
> 
> device	puc
> options	PUC_FASTINTR
> 
> but i forgot add
> 
> device	sio
> 
> after reboot i have in dmesg:
> 
> puc0: <NetMos NM9835 Dual UART and 1284 Printer port> port 0x1030-0x103f,0x1020-0x1027,0x1018-0x101f,0x1010-0x1017,0x1008-0x100f,0x1000-0x1007 irq 4 at device 3.0 on pci1
> 
> card detected, but no have ports (no sio driver in kernel).
> I rebuild kernel width sio driver and after reboot have kernel trap.
> sio(4) diver are ported to sparc?

It works only for ISA sio(4)s currently, due to some ISA specific code
that happens to work with PCI on other platforms. I have attached a
quick hack to fix this which I use on one of my machines, which should
get you around the problems (it also cointains things like console
support).
The rework which marcel@ is doing in the perforce repository will
fix this the right way.

	- Thomas

-- 
Thomas Moestl <t.moestl at tu-bs.de>	http://www.tu-bs.de/~y0015675/
              <tmm at FreeBSD.org>		http://people.FreeBSD.org/~tmm/
PGP fingerprint: 1C97 A604 2BD0 E492 51D0  9C0F 1FE6 4F1D 419C 776C
-------------- next part --------------
Index: sio.c
===================================================================
RCS file: /vol/ncvs/src/sys/dev/sio/sio.c,v
retrieving revision 1.400
diff -u -r1.400 sio.c
--- sio.c	9 Jun 2003 21:25:14 -0000	1.400
+++ sio.c	12 Jun 2003 14:00:19 -0000
@@ -126,7 +126,7 @@
 #define	sio_getreg(com, off) \
 	(bus_space_read_1((com)->bst, (com)->bsh, (off)))
 #define	sio_setreg(com, off, value) \
-	(bus_space_write_1((com)->bst, (com)->bsh, (off), (value)))
+	bus_space_write_1((com)->bst, (com)->bsh, (off), (value))
 
 /*
  * com state bits.
@@ -229,15 +229,9 @@
 	bus_space_tag_t		bst;
 	bus_space_handle_t	bsh;
 
-	Port_t	data_port;	/* i/o ports */
 #ifdef COM_ESP
 	Port_t	esp_port;
 #endif
-	Port_t	int_id_port;
-	Port_t	modem_ctl_port;
-	Port_t	line_status_port;
-	Port_t	modem_status_port;
-	Port_t	intr_ctl_port;	/* Ports of IIR register */
 
 	struct tty	*tp;	/* cross reference */
 
@@ -337,9 +331,20 @@
 	    &gdbdefaultrate, GDBSPEED, "");
 static	u_int	com_events;	/* input chars + weighted output completions */
 static	Port_t	siocniobase;
-static	int	siocnunit = -1;
+#ifdef __sparc64__
+#define	SIOP_CONS	0
+#define	SIOP_LLCONS	1
+static	struct bus_space_tag siocntag[2];
+static	bus_space_handle_t siocnhandle[2];
+static	bus_addr_t siocnports[2];
+#endif
+#if defined(__i386__) || defined(__ia64__) || defined(__sparc64__)
+static	int	siocnunit;
+#endif
 static	Port_t	siogdbiobase;
+#ifndef __sparc64__
 static	int	siogdbunit = -1;
+#endif
 static	void	*sio_slow_ih;
 static	void	*sio_fast_ih;
 static	int	sio_timeout;
@@ -348,6 +353,18 @@
     = CALLOUT_HANDLE_INITIALIZER(&sio_timeout_handle);
 static	int	sio_numunits;
 
+#ifdef __sparc64__
+#define	SIOCNIOBASE(b)	siocnports[(b)]
+#define	SIOCNOUTB(p, o, v) \
+	bus_space_write_1(&siocntag[p], siocnhandle[p], o, v)
+#define	SIOCNINB(p, o) \
+	bus_space_read_1(&siocntag[p], siocnhandle[p], o)
+#else
+#define	SIOCNIOBASE(b)	(b)
+#define	SIOCNOUTB(p, o, v)	outb(p + o, v)
+#define	SIOCNINB(p, o)		inb(p + o)
+#endif
+
 #ifdef COM_ESP
 /* XXX configure this properly. */
 /* XXX quite broken for new-bus. */
@@ -640,7 +657,7 @@
 	 * XXX what about the UART bug avoided by waiting in comparam()?
 	 * We don't want to to wait long enough to drain at 2 bps.
 	 */
-	if (iobase == siocniobase)
+	if (iobase == SIOCNIOBASE(siocniobase))
 		DELAY((16 + 1) * 1000000 / (comdefaultrate / 10));
 	else {
 		sio_setreg(com, com_cfcr, CFCR_DLAB | CFCR_8BITS);
@@ -660,7 +677,8 @@
 	sio_setreg(com, com_mcr, mcr_image);
 	sio_setreg(com, com_ier, 0);
 	DELAY(1000);		/* XXX */
-	irqmap[0] = isa_irq_pending();
+	if (!noprobe)
+		irqmap[0] = isa_irq_pending();
 
 	/*
 	 * Attempt to set loopback mode so that we can send a null byte
@@ -736,7 +754,7 @@
 		sio_setreg(com, com_cfcr, CFCR_8BITS);
 		mtx_unlock_spin(&sio_lock);
 		bus_release_resource(dev, SYS_RES_IOPORT, rid, port);
-		if (iobase == siocniobase)
+		if (iobase == SIOCNIOBASE(siocniobase))
 			result = 0;
 		if (result != 0) {
 			device_set_softc(dev, NULL);
@@ -813,7 +831,7 @@
 			break;
 		}
 	bus_release_resource(dev, SYS_RES_IOPORT, rid, port);
-	if (iobase == siocniobase)
+	if (iobase == SIOCNIOBASE(siocniobase))
 		result = 0;
 	if (result != 0) {
 		device_set_softc(dev, NULL);
@@ -944,13 +962,7 @@
 	com->obufs[0].l_head = com->obuf1;
 	com->obufs[1].l_head = com->obuf2;
 
-	com->data_port = iobase + com_data;
-	com->int_id_port = iobase + com_iir;
-	com->modem_ctl_port = iobase + com_mcr;
-	com->mcr_image = inb(com->modem_ctl_port);
-	com->line_status_port = iobase + com_lsr;
-	com->modem_status_port = iobase + com_msr;
-	com->intr_ctl_port = iobase + com_ier;
+	com->mcr_image = sio_getreg(com, com_mcr);
 
 	if (rclk == 0)
 		rclk = DEFAULT_RCLK;
@@ -983,7 +995,7 @@
 		 * Leave i/o resources allocated if this is a `cn'-level
 		 * console, so that other devices can't snarf them.
 		 */
-		if (iobase != siocniobase)
+		if (iobase != SIOCNIOBASE(siocniobase))
 			bus_release_resource(dev, SYS_RES_IOPORT, rid, port);
 		return (ENOMEM);
 	}
@@ -1015,7 +1027,7 @@
 	sio_setreg(com, com_fifo, FIFO_ENABLE | FIFO_RX_HIGH);
 	DELAY(100);
 	com->st16650a = 0;
-	switch (inb(com->int_id_port) & IIR_FIFO_MASK) {
+	switch (sio_getreg(com, com_iir) & IIR_FIFO_MASK) {
 	case FIFO_RX_LOW:
 		printf(" 16450");
 		break;
@@ -1164,7 +1176,7 @@
 		 * on the console.
 		 */
 		if (ret == 0 && unit == comconsole)
-			outb(siocniobase + com_ier, IER_ERXRDY | IER_ERLS |
+			SIOCNOUTB(siocniobase, com_ier, IER_ERXRDY | IER_ERLS |
 			    IER_EMSC);
 #endif
 	}
@@ -1292,11 +1304,11 @@
 				 * for about 85 usec instead of 100.
 				 */
 				DELAY(50);
-				if (!(inb(com->line_status_port) & LSR_RXRDY))
+				if (!(sio_getreg(com, com_lsr) & LSR_RXRDY))
 					break;
 				sio_setreg(com, com_fifo, 0);
 				DELAY(50);
-				(void) inb(com->data_port);
+				(void) sio_getreg(com, com_data);
 			}
 			if (i == 500) {
 				error = EIO;
@@ -1305,15 +1317,15 @@
 		}
 
 		mtx_lock_spin(&sio_lock);
-		(void) inb(com->line_status_port);
-		(void) inb(com->data_port);
+		(void) sio_getreg(com, com_lsr);
+		(void) sio_getreg(com, com_data);
 		com->prev_modem_status = com->last_modem_status
-		    = inb(com->modem_status_port);
+		    = sio_getreg(com, com_msr);
 		if (COM_IIR_TXRDYBUG(com->flags)) {
-			outb(com->intr_ctl_port, IER_ERXRDY | IER_ERLS
+			sio_setreg(com, com_ier, IER_ERXRDY | IER_ERLS
 						| IER_EMSC);
 		} else {
-			outb(com->intr_ctl_port, IER_ERXRDY | IER_ETXRDY
+			sio_setreg(com, com_ier, IER_ERXRDY | IER_ETXRDY
 						| IER_ERLS | IER_EMSC);
 		}
 		mtx_unlock_spin(&sio_lock);
@@ -1524,7 +1536,7 @@
 	s = spltty();
 	if (com->state & CS_BUSY)
 		com->extra_state &= ~CSE_BUSYCHECK;	/* False alarm. */
-	else if ((inb(com->line_status_port) & (LSR_TSRE | LSR_TXRDY))
+	else if ((sio_getreg(com, com_lsr) & (LSR_TSRE | LSR_TXRDY))
 	    == (LSR_TSRE | LSR_TXRDY)) {
 		com->tp->t_state &= ~TS_BUSY;
 		ttwwakeup(com->tp);
@@ -1668,7 +1680,7 @@
 	 */
 	if ((com->state & CS_RTS_IFLOW) && !(com->mcr_image & MCR_RTS) &&
 	    !(tp->t_state & TS_TBLOCK))
-		outb(com->modem_ctl_port, com->mcr_image |= MCR_RTS);
+		sio_setreg(com, com_mcr,  com->mcr_image |= MCR_RTS);
 }
 
 static void
@@ -1705,7 +1717,7 @@
 			 */
 			if (com != NULL 
 			    && !com->gone
-			    && (inb(com->int_id_port) & IIR_IMASK)
+			    && (sio_getreg(com, com_iir) & IIR_IMASK)
 			       != IIR_NOPEND) {
 				siointr1(com);
 				possibly_more_intrs = TRUE;
@@ -1761,12 +1773,12 @@
 	u_char	int_ctl;
 	u_char	int_ctl_new;
 
-	int_ctl = inb(com->intr_ctl_port);
+	int_ctl = sio_getreg(com, com_ier);
 	int_ctl_new = int_ctl;
 
 	while (!com->gone) {
 		if (com->pps.ppsparam.mode & PPS_CAPTUREBOTH) {
-			modem_status = inb(com->modem_status_port);
+			modem_status = sio_getreg(com, com_msr);
 		        if ((modem_status ^ com->last_modem_status) &
 			    com->pps_bit) {
 				pps_capture(&com->pps);
@@ -1775,7 +1787,7 @@
 				    PPS_CAPTUREASSERT : PPS_CAPTURECLEAR);
 			}
 		}
-		line_status = inb(com->line_status_port);
+		line_status = sio_getreg(com, com_lsr);
 
 		/* input event? (check first to help avoid overruns) */
 		while (line_status & LSR_RCV_MASK) {
@@ -1783,7 +1795,7 @@
 			if (!(line_status & LSR_RXRDY))
 				recv_data = 0;
 			else
-				recv_data = inb(com->data_port);
+				recv_data = sio_getreg(com, com_data);
 #if defined(DDB) && defined(ALT_BREAK_TO_DEBUGGER)
 			/*
 			 * Solaris implements a new BREAK which is initiated
@@ -1870,7 +1882,7 @@
 				com->iptr = ++ioptr;
 				if (ioptr == com->ihighwater
 				    && com->state & CS_RTS_IFLOW)
-					outb(com->modem_ctl_port,
+					sio_setreg(com, com_mcr,
 					     com->mcr_image &= ~MCR_RTS);
 				if (line_status & LSR_OE)
 					CE_RECORD(com, CE_OVERRUN);
@@ -1880,11 +1892,11 @@
 			 * "& 0x7F" is to avoid the gcc-1.40 generating a slow
 			 * jump from the top of the loop to here
 			 */
-			line_status = inb(com->line_status_port) & 0x7F;
+			line_status = sio_getreg(com, com_lsr) & 0x7F;
 		}
 
 		/* modem status change? (always check before doing output) */
-		modem_status = inb(com->modem_status_port);
+		modem_status = sio_getreg(com, com_msr);
 		if (modem_status != com->last_modem_status) {
 			if (com->do_dcd_timestamp
 			    && !(com->last_modem_status & MSR_DCD)
@@ -1925,10 +1937,10 @@
 					ocount = com->tx_fifo_size;
 				com->bytes_out += ocount;
 				do
-					outb(com->data_port, *ioptr++);
+					sio_setreg(com, com_data, *ioptr++);
 				while (--ocount != 0);
 			} else {
-				outb(com->data_port, *ioptr++);
+				sio_setreg(com, com_data, *ioptr++);
 				++com->bytes_out;
 				if (com->unit == siotsunit) {
 					nanouptime(&siots[siotso]);
@@ -1965,13 +1977,13 @@
 				}
 			}
 			if (COM_IIR_TXRDYBUG(com->flags) && (int_ctl != int_ctl_new)) {
-				outb(com->intr_ctl_port, int_ctl_new);
+				sio_setreg(com, com_ier, int_ctl_new);
 			}
 		}
 
 		/* finished? */
 #ifndef COM_MULTIPORT
-		if ((inb(com->int_id_port) & IIR_IMASK) == IIR_NOPEND)
+		if ((sio_getreg(com, com_iir) & IIR_IMASK) == IIR_NOPEND)
 #endif /* COM_MULTIPORT */
 			return;
 	}
@@ -2345,7 +2357,7 @@
 		 * CS_RTS_IFLOW just changed from on to off.  Force MCR_RTS
 		 * on here, since comstart() won't do it later.
 		 */
-		outb(com->modem_ctl_port, com->mcr_image |= MCR_RTS);
+		sio_setreg(com, com_mcr, com->mcr_image |= MCR_RTS);
 		if (com->st16650a) {
 			sio_setreg(com, com_cfcr, 0xbf);
 			sio_setreg(com, com_fifo,
@@ -2494,11 +2506,11 @@
 		com->state |= CS_TTGO;
 	if (tp->t_state & TS_TBLOCK) {
 		if (com->mcr_image & MCR_RTS && com->state & CS_RTS_IFLOW)
-			outb(com->modem_ctl_port, com->mcr_image &= ~MCR_RTS);
+			sio_setreg(com, com_mcr, com->mcr_image &= ~MCR_RTS);
 	} else {
 		if (!(com->mcr_image & MCR_RTS) && com->iptr < com->ihighwater
 		    && com->state & CS_RTS_IFLOW)
-			outb(com->modem_ctl_port, com->mcr_image |= MCR_RTS);
+			sio_setreg(com, com_mcr, com->mcr_image |= MCR_RTS);
 	}
 	mtx_unlock_spin(&sio_lock);
 	if (tp->t_state & (TS_TIMEOUT | TS_TTSTOP)) {
@@ -2643,14 +2655,14 @@
 	mtx_lock_spin(&sio_lock);
 	switch (how) {
 	case DMSET:
-		outb(com->modem_ctl_port,
+		sio_setreg(com, com_mcr,
 		     com->mcr_image = mcr | (com->mcr_image & MCR_IENABLE));
 		break;
 	case DMBIS:
-		outb(com->modem_ctl_port, com->mcr_image |= mcr);
+		sio_setreg(com, com_mcr, com->mcr_image |= mcr);
 		break;
 	case DMBIC:
-		outb(com->modem_ctl_port, com->mcr_image &= ~mcr);
+		sio_setreg(com, com_mcr, com->mcr_image &= ~mcr);
 		break;
 	}
 	mtx_unlock_spin(&sio_lock);
@@ -2785,6 +2797,7 @@
 static speed_t siocngetspeed(Port_t, u_long rclk);
 #endif
 static void siocnclose(struct siocnstate *sp, Port_t iobase);
+static void siocninitdl(Port_t iobase, speed_t rate);
 static void siocnopen(struct siocnstate *sp, Port_t iobase, int speed);
 static void siocntxwait(Port_t iobase);
 
@@ -2810,7 +2823,9 @@
 /* To get the GDB related variables */
 #if DDB > 0
 #include <ddb/ddb.h>
+#ifndef __sparc64__
 static struct consdev gdbconsdev;
+#endif
 
 #endif
 
@@ -2826,7 +2841,7 @@
 	 * transmits.
 	 */
 	timo = 100000;
-	while ((inb(iobase + com_lsr) & (LSR_TSRE | LSR_TXRDY))
+	while ((SIOCNINB(iobase, com_lsr) & (LSR_TSRE | LSR_TXRDY))
 	       != (LSR_TSRE | LSR_TXRDY) && --timo != 0)
 		;
 }
@@ -2852,13 +2867,13 @@
 	u_char	dlbl;
 	u_char  cfcr;
 
-	cfcr = inb(iobase + com_cfcr);
-	outb(iobase + com_cfcr, CFCR_DLAB | cfcr);
+	cfcr = SIOCNINB(iobase, com_cfcr);
+	SIOCNOUTB(iobase, com_cfcr, CFCR_DLAB | cfcr);
 
-	dlbl = inb(iobase + com_dlbl);
-	dlbh = inb(iobase + com_dlbh);
+	dlbl = SIOCNINB(iobase, com_dlbl);
+	dlbh = SIOCNINB(iobase, com_dlbh);
 
-	outb(iobase + com_cfcr, cfcr);
+	SIOCNOUTB(iobase, com_cfcr, cfcr);
 
 	divisor = dlbh << 8 | dlbl;
 
@@ -2885,13 +2900,13 @@
 	 * and set our default ones (cs8 -parenb speed=comdefaultrate).
 	 * We can't save the fifo register since it is read-only.
 	 */
-	sp->ier = inb(iobase + com_ier);
-	outb(iobase + com_ier, 0);	/* spltty() doesn't stop siointr() */
+	sp->ier = SIOCNINB(iobase, com_ier);
+	SIOCNOUTB(iobase, com_ier, 0);	/* spltty() doesn't stop siointr() */
 	siocntxwait(iobase);
-	sp->cfcr = inb(iobase + com_cfcr);
-	outb(iobase + com_cfcr, CFCR_DLAB | CFCR_8BITS);
-	sp->dlbl = inb(iobase + com_dlbl);
-	sp->dlbh = inb(iobase + com_dlbh);
+	sp->cfcr = SIOCNINB(iobase, com_cfcr);
+	SIOCNOUTB(iobase, com_cfcr, CFCR_DLAB | CFCR_8BITS);
+	sp->dlbl = SIOCNINB(iobase, com_dlbl);
+	sp->dlbh = SIOCNINB(iobase, com_dlbh);
 	/*
 	 * Only set the divisor registers if they would change, since on
 	 * some 16550 incompatibles (Startech), setting them clears the
@@ -2901,18 +2916,18 @@
 	divisor = siodivisor(comdefaultrclk, speed);
 	dlbl = divisor & 0xFF;
 	if (sp->dlbl != dlbl)
-		outb(iobase + com_dlbl, dlbl);
+		SIOCNOUTB(iobase, com_dlbl, dlbl);
 	dlbh = divisor >> 8;
 	if (sp->dlbh != dlbh)
-		outb(iobase + com_dlbh, dlbh);
-	outb(iobase + com_cfcr, CFCR_8BITS);
-	sp->mcr = inb(iobase + com_mcr);
+		SIOCNOUTB(iobase, com_dlbh, dlbh);
+	SIOCNOUTB(iobase, com_cfcr, CFCR_8BITS);
+	sp->mcr = SIOCNINB(iobase, com_mcr);
 	/*
 	 * We don't want interrupts, but must be careful not to "disable"
 	 * them by clearing the MCR_IENABLE bit, since that might cause
 	 * an interrupt by floating the IRQ line.
 	 */
-	outb(iobase + com_mcr, (sp->mcr & MCR_IENABLE) | MCR_DTR | MCR_RTS);
+	SIOCNOUTB(iobase, com_mcr, (sp->mcr & MCR_IENABLE) | MCR_DTR | MCR_RTS);
 }
 
 static void
@@ -2924,20 +2939,45 @@
 	 * Restore the device control registers.
 	 */
 	siocntxwait(iobase);
-	outb(iobase + com_cfcr, CFCR_DLAB | CFCR_8BITS);
-	if (sp->dlbl != inb(iobase + com_dlbl))
-		outb(iobase + com_dlbl, sp->dlbl);
-	if (sp->dlbh != inb(iobase + com_dlbh))
-		outb(iobase + com_dlbh, sp->dlbh);
-	outb(iobase + com_cfcr, sp->cfcr);
+	SIOCNOUTB(iobase, com_cfcr, CFCR_DLAB | CFCR_8BITS);
+	if (sp->dlbl != SIOCNINB(iobase, com_dlbl))
+		SIOCNOUTB(iobase, com_dlbl, sp->dlbl);
+	if (sp->dlbh != SIOCNINB(iobase, com_dlbh))
+		SIOCNOUTB(iobase, com_dlbh, sp->dlbh);
+	SIOCNOUTB(iobase, com_cfcr, sp->cfcr);
 	/*
 	 * XXX damp oscillations of MCR_DTR and MCR_RTS by not restoring them.
 	 */
-	outb(iobase + com_mcr, sp->mcr | MCR_DTR | MCR_RTS);
-	outb(iobase + com_ier, sp->ier);
+	SIOCNOUTB(iobase, com_mcr, sp->mcr | MCR_DTR | MCR_RTS);
+	SIOCNOUTB(iobase, com_ier, sp->ier);
 }
 
-#ifndef __alpha__
+static void
+siocninitdl(iobase, rate)
+	Port_t			iobase;
+	speed_t			rate;
+{
+	u_int			divisor;
+	u_char			cfcr;
+
+	/*
+	 * Initialize the divisor latch.  We can't rely on
+	 * siocnopen() to do this the first time, since it
+	 * avoids writing to the latch if the latch appears
+	 * to have the correct value.  Also, if we didn't
+	 * just read the speed from the hardware, then we
+	 * need to set the speed in hardware so that
+	 * switching it later is null.
+	 */
+	cfcr = SIOCNINB(iobase, com_cfcr);
+	SIOCNOUTB(iobase, com_cfcr, CFCR_DLAB | cfcr);
+	divisor = siodivisor(comdefaultrclk, rate);
+	SIOCNOUTB(iobase, com_dlbl, divisor & 0xff);
+	SIOCNOUTB(iobase, com_dlbh, divisor >> 8);
+	SIOCNOUTB(iobase, com_cfcr, cfcr);
+}
+
+#if defined(__i386__) || defined(__ia64__)
 
 static void
 siocnprobe(cp)
@@ -2945,7 +2985,6 @@
 {
 	speed_t			boot_speed;
 	u_char			cfcr;
-	u_int			divisor;
 	int			s, unit;
 	struct siocnstate	sp;
 
@@ -2989,22 +3028,7 @@
 					comdefaultrate = boot_speed;
 			}
 
-			/*
-			 * Initialize the divisor latch.  We can't rely on
-			 * siocnopen() to do this the first time, since it 
-			 * avoids writing to the latch if the latch appears
-			 * to have the correct value.  Also, if we didn't
-			 * just read the speed from the hardware, then we
-			 * need to set the speed in hardware so that
-			 * switching it later is null.
-			 */
-			cfcr = inb(iobase + com_cfcr);
-			outb(iobase + com_cfcr, CFCR_DLAB | cfcr);
-			divisor = siodivisor(comdefaultrclk, comdefaultrate);
-			outb(iobase + com_dlbl, divisor & 0xff);
-			outb(iobase + com_dlbh, divisor >> 8);
-			outb(iobase + com_cfcr, cfcr);
-
+			siocninitdl(iobase, comdefaultrate);
 			siocnopen(&sp, iobase, comdefaultrate);
 
 			splx(s);
@@ -3052,6 +3076,10 @@
 #endif
 }
 
+#endif
+
+#ifndef __alpha__
+
 static void
 siocninit(cp)
 	struct consdev	*cp;
@@ -3079,34 +3107,16 @@
 {
 	int			s;
 	u_char			cfcr;
-	u_int			divisor;
 	struct siocnstate	sp;
-	int			unit = 0;	/* XXX random value! */
 
 	siocniobase = port;
-	siocnunit = unit;
 	comdefaultrate = speed;
 	sio_consdev.cn_pri = CN_NORMAL;
-	sio_consdev.cn_dev = makedev(CDEV_MAJOR, unit);
+	sio_consdev.cn_dev = makedev(CDEV_MAJOR, 0);
 
 	s = spltty();
 
-	/*
-	 * Initialize the divisor latch.  We can't rely on
-	 * siocnopen() to do this the first time, since it 
-	 * avoids writing to the latch if the latch appears
-	 * to have the correct value.  Also, if we didn't
-	 * just read the speed from the hardware, then we
-	 * need to set the speed in hardware so that
-	 * switching it later is null.
-	 */
-	cfcr = inb(siocniobase + com_cfcr);
-	outb(siocniobase + com_cfcr, CFCR_DLAB | cfcr);
-	divisor = siodivisor(comdefaultrclk, comdefaultrate);
-	outb(siocniobase + com_dlbl, divisor & 0xff);
-	outb(siocniobase + com_dlbh, divisor >> 8);
-	outb(siocniobase + com_cfcr, cfcr);
-
+	siocninitdl(siocniobase, comdefaultrate);
 	siocnopen(&sp, siocniobase, comdefaultrate);
 	splx(s);
 
@@ -3121,9 +3131,8 @@
 {
 	int			s;
 	u_char			cfcr;
-	u_int			divisor;
 	struct siocnstate	sp;
-	int			unit = 1;	/* XXX random value! */
+	int			unit = 1;	/* XXX !!! */
 
 	siogdbiobase = port;
 	gdbdefaultrate = speed;
@@ -3139,22 +3148,7 @@
 
 	s = spltty();
 
-	/*
-	 * Initialize the divisor latch.  We can't rely on
-	 * siocnopen() to do this the first time, since it 
-	 * avoids writing to the latch if the latch appears
-	 * to have the correct value.  Also, if we didn't
-	 * just read the speed from the hardware, then we
-	 * need to set the speed in hardware so that
-	 * switching it later is null.
-	 */
-	cfcr = inb(siogdbiobase + com_cfcr);
-	outb(siogdbiobase + com_cfcr, CFCR_DLAB | cfcr);
-	divisor = siodivisor(comdefaultrclk, gdbdefaultrate);
-	outb(siogdbiobase + com_dlbl, divisor & 0xff);
-	outb(siogdbiobase + com_dlbh, divisor >> 8);
-	outb(siogdbiobase + com_cfcr, cfcr);
-
+	siocninitdl(siogdbiobase, gdbdefaultrate);
 	siocnopen(&sp, siogdbiobase, gdbdefaultrate);
 	splx(s);
 
@@ -3183,8 +3177,8 @@
 	}
 	s = spltty();
 	siocnopen(&sp, iobase, speed);
-	if (inb(iobase + com_lsr) & LSR_RXRDY)
-		c = inb(iobase + com_data);
+	if (SIOCNINB(iobase, com_lsr) & LSR_RXRDY)
+		c = SIOCNINB(iobase, com_data);
 	else
 		c = -1;
 	siocnclose(&sp, iobase);
@@ -3213,9 +3207,9 @@
 	}
 	s = spltty();
 	siocnopen(&sp, iobase, speed);
-	while (!(inb(iobase + com_lsr) & LSR_RXRDY))
+	while (!(SIOCNINB(iobase, com_lsr) & LSR_RXRDY))
 		;
-	c = inb(iobase + com_data);
+	c = SIOCNINB(iobase, com_data);
 	siocnclose(&sp, iobase);
 	splx(s);
 	return (c);
@@ -3247,7 +3241,7 @@
 	}
 	siocnopen(&sp, iobase, speed);
 	siocntxwait(iobase);
-	outb(iobase + com_data, c);
+	SIOCNOUTB(iobase, com_data, c);
 	siocnclose(&sp, iobase);
 	if (need_unlock)
 		mtx_unlock_spin(&sio_lock);
@@ -3304,6 +3298,208 @@
 	siocntxwait(siogdbiobase);
 	outb(siogdbiobase + com_data, c);
 	siocnclose(&sp, siogdbiobase);
+	splx(s);
+}
+#endif
+
+#ifdef __sparc64__
+
+#include <ofw/openfirm.h>
+#include <ofw/ofw_pci.h>
+#include <machine/ofw_upa.h>
+#include <sparc64/pci/ofw_pci.h>
+#include <sparc64/isa/ofw_isa.h>
+#include <sparc64/ebus/ebusvar.h>
+
+/* This requires EBus support for now. */
+extern char *sio_ofw_names[];
+extern char *sio_ofw_compat[];
+int sio_ofw_inlist(char *name, char *list[]);
+
+static phandle_t siocnfind(struct consdev *cp, phandle_t root,
+    int *unit, int *flags);
+
+static char sio_ofw_name[32];
+
+/*
+ * Sparc64 console support is a bit complicated; the console needs to
+ * initialize before any bus drivers are registered. Therefore the attach
+ * routine needs to walk the ofw device tree, extract nodes that look like
+ * sio's, look at the parent bus nodes, map the registers, and map the
+ * resulting PCI adresses to physical addresses using the PCI host bridge node.
+ */
+
+/* Find the first sio eligible for console use and return. */
+static phandle_t
+siocnfind(cp, root, unit, flags)
+	struct consdev	*cp;
+	phandle_t	root;
+	int		*unit;
+	int		*flags;
+{
+	int		disabled;
+	int		found;
+	phandle_t	node;
+	phandle_t	rv;
+
+	node = OF_child(root);
+	while (node != 0) {
+		found = 0;
+		if (OF_getprop(node, "name", sio_ofw_name,
+		    sizeof(sio_ofw_name)) != -1) {
+			sio_ofw_name[sizeof(sio_ofw_name) - 1] = '\0';
+			if (sio_ofw_inlist(sio_ofw_name, sio_ofw_names))
+				found = 1;
+		}
+		if (OF_getprop(node, "compat", sio_ofw_name,
+		    sizeof(sio_ofw_name)) != -1) {
+			sio_ofw_name[sizeof(sio_ofw_name) - 1] = '\0';
+			if (sio_ofw_inlist(sio_ofw_name, sio_ofw_compat))
+				found = 1;
+		}
+		if (found) {
+			if (resource_int_value("sio", *unit, "disabled",
+			    &disabled) != 0)
+				disabled = 0;
+			if (resource_int_value("sio", *unit, "flags",
+			    flags) == 0) {
+				if (!disabled && COM_CONSOLE(*flags))
+					return (node);
+			}
+			(*unit)++;
+		}
+		/*
+		 * This recursion should be at most only about 5 levels deep,
+		 * so this should not take up too much stack space.
+		 * It is also done very early, before interrupts can kick in
+		 * or the like.
+		 */
+		if ((rv = siocnfind(cp, node, unit, flags)) != 0)
+			return (rv);
+		node = OF_peer(node);
+	}
+	return (0);
+}
+
+static int siocnmap(phandle_t node, phandle_t parent);
+
+/*
+ * ISA and EBus ranges and regs are identical, so we can use a single function
+ * here.
+ */
+static int
+siocnmap(node, parent)
+	phandle_t	node;
+	phandle_t	parent;
+{
+	int			bs;
+	phandle_t		bus;
+	u_long			child;
+	int			cs;
+	u_long			dummy;
+	int			error;
+	int			i;
+	struct isa_ranges	ir[4];
+	char			name[32];
+	phandle_t		pbus;
+	u_long			phys;
+	struct isa_regs		reg;
+	int			rsz;
+	int			type;
+	struct upa_ranges	ur[4];
+
+	if (OF_getprop(node, "reg", &reg, sizeof(reg)) == -1 ||
+	    (rsz = OF_getprop(parent, "ranges", ir, sizeof(ir))) == -1)
+		return (ENXIO);
+	phys = ISA_REG_PHYS(&reg);
+	dummy = phys + 8;
+	type = ofw_isa_map_iorange(ir, rsz / sizeof(*ir), &phys, &dummy);
+	if (type == SYS_RES_MEMORY) {
+		cs = PCI_CS_MEM32;
+		bs = PCI_MEMORY_BUS_SPACE;
+	} else {
+		cs = PCI_CS_IO;
+		bs = PCI_IO_BUS_SPACE;
+	}
+	bus = OF_parent(parent);
+	if (OF_getprop(bus, "name", name, sizeof(name)) == -1)
+		return (ENXIO);
+	name[sizeof(name) - 1] = '\0';
+	if (strcmp(name, "pci") != 0)
+		return (ENXIO);
+	/* Find the topmost PCI node (the host bridge) */
+	while ((pbus = OF_parent(bus)) != 0) {
+		if (OF_getprop(pbus, "name", name, sizeof(name)) != -1) {
+			name[sizeof(name) - 1] = '\0';
+			if (strcmp(name, "pci") != 0)
+				break;
+		}
+		bus = pbus;
+	}
+	if (pbus == 0)
+		return (ENXIO);
+	if ((rsz = OF_getprop(bus, "ranges", ur, sizeof(ur))) == -1)
+		return (ENXIO);
+	error = ENXIO;
+	siocniobase = SIOP_CONS;
+	for (i = 0; i < (rsz / sizeof(ur[0])); i++) {
+		child = UPA_RANGE_CHILD(&ur[i]);
+		if (UPA_RANGE_CS(&ur[i]) == cs && phys >= child &&
+		    phys - child < UPA_RANGE_SIZE(&ur[i])) {
+			siocnports[SIOP_CONS] = phys;
+			siocnhandle[SIOP_CONS] = sparc64_fake_bustag(bs,
+			    UPA_RANGE_PHYS(&ur[i]) + phys,
+			    &siocntag[SIOP_CONS]);
+			error = 0;
+			break;
+		}
+	}
+	return (error);
+}
+
+static void
+siocnprobe(cp)
+	struct consdev	*cp;
+{
+	char			bname[32];
+	speed_t			boot_speed;
+	int			error;
+	int			flags;
+	phandle_t		node;
+	phandle_t		parent;
+	int			s;
+	struct siocnstate	sp;
+	int			unit;
+
+	cp->cn_pri = CN_DEAD;
+	node = OF_peer(0);
+	if (node <= 0)
+		return;
+	unit = 0;
+	if ((node = siocnfind(cp, node, &unit, &flags)) == 0)
+		return;
+	if ((parent = OF_parent(node)) == 0)
+		return;
+	if ((OF_getprop(parent, "name", bname, sizeof(bname))) <= 0)
+		return;
+	bname[sizeof(bname) - 1] = '\0';
+	error = ENXIO;
+	if (strcmp(bname, "isa") == 0 ||
+	    strcmp(bname, "ebus") == 0)
+		error = siocnmap(node, parent);
+	if (error != 0)
+		return;
+	s = spltty();
+	if (boothowto & RB_SERIAL) {
+		boot_speed = siocngetspeed(SIOP_CONS, comdefaultrclk);
+		if (boot_speed)
+			comdefaultrate = boot_speed;
+	}
+	cp->cn_dev = makedev(CDEV_MAJOR, unit);
+	cp->cn_pri = COM_FORCECONSOLE(flags) || boothowto & RB_SERIAL ?
+	    CN_REMOTE : CN_NORMAL;
+	siocninitdl(SIOP_CONS, comdefaultrate);
+	siocnopen(&sp, SIOP_CONS, comdefaultrate);
 	splx(s);
 }
 #endif


More information about the freebsd-sparc64 mailing list