PERFORCE change 30345 for review

Marcel Moolenaar marcel at FreeBSD.org
Thu May 1 13:59:34 PDT 2003


http://perforce.freebsd.org/chv.cgi?CH=30345

Change 30345 by marcel at marcel_nfs on 2003/05/01 13:59:30

	This got dropped from the previous change (30343) due
	to merge conflicts. Files have been copied from the
	ia64 brach after accepting the conflicting merge.
	This should be ok...

Affected files ...

.. //depot/projects/sio/sys/dev/sio/sio.c#2 edit
.. //depot/projects/sio/sys/dev/sio/sio_pccard.c#2 edit
.. //depot/projects/sio/sys/dev/sio/sio_pci.c#2 edit
.. //depot/projects/sio/sys/dev/sio/sioreg.h#2 edit

Differences ...

==== //depot/projects/sio/sys/dev/sio/sio.c#2 (text+ko) ====

@@ -36,7 +36,6 @@
  */
 
 #include "opt_comconsole.h"
-#include "opt_compat.h"
 #include "opt_ddb.h"
 #include "opt_sio.h"
 
@@ -66,24 +65,18 @@
 #include <sys/sysctl.h>
 #include <sys/syslog.h>
 #include <sys/tty.h>
-#include <machine/bus_pio.h>
 #include <machine/bus.h>
 #include <sys/rman.h>
 #include <sys/timepps.h>
 #include <sys/uio.h>
-
-#include <isa/isavar.h>
+#include <sys/cons.h>
 
 #include <machine/resource.h>
+#include <machine/stdarg.h>
 
 #include <dev/sio/sioreg.h>
 #include <dev/sio/siovar.h>
 
-#ifdef COM_ESP
-#include <dev/ic/esp.h>
-#endif
-#include <dev/ic/ns16550.h>
-
 #define	LOTS_OF_EVENTS	64	/* helps separate urgent events from input */
 
 #define	CALLOUT_MASK		0x80
@@ -96,19 +89,6 @@
 #define	UNIT_TO_MINOR(unit)	((((unit) & ~0x1fU) << (8 + 3)) \
 				 | ((unit) & 0x1f))
 
-#ifdef COM_MULTIPORT
-/* checks in flags for multiport and which is multiport "master chip"
- * for a given card
- */
-#define	COM_ISMULTIPORT(flags)	((flags) & 0x01)
-#define	COM_MPMASTER(flags)	(((flags) >> 8) & 0x0ff)
-#define	COM_NOTAST4(flags)	((flags) & 0x04)
-#else
-#define	COM_ISMULTIPORT(flags)	(0)
-#endif /* COM_MULTIPORT */
-
-#define	COM_CONSOLE(flags)	((flags) & 0x10)
-#define	COM_FORCECONSOLE(flags)	((flags) & 0x20)
 #define	COM_LLCONSOLE(flags)	((flags) & 0x40)
 #define	COM_DEBUGGER(flags)	((flags) & 0x80)
 #define	COM_LOSESOUTINTS(flags)	((flags) & 0x08)
@@ -123,11 +103,6 @@
 #define	COM_TI16754(flags)	((flags) & 0x200000)
 #define	COM_FIFOSIZE(flags)	(((flags) & 0xff000000) >> 24)
 
-#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)))
-
 /*
  * com state bits.
  * (CS_BUSY | CS_TTGO) and (CS_BUSY | CS_TTGO | CS_ODEVREADY) must be higher
@@ -163,125 +138,9 @@
 	"tty-level buffer overflow",
 };
 
-#define	CE_NTYPES			3
 #define	CE_RECORD(com, errnum)		(++(com)->delta_error_counts[errnum])
 
-/* types.  XXX - should be elsewhere */
-typedef u_int	Port_t;		/* hardware port */
-typedef u_char	bool_t;		/* boolean */
-
-/* queue of linear buffers */
-struct lbq {
-	u_char	*l_head;	/* next char to process */
-	u_char	*l_tail;	/* one past the last char to process */
-	struct lbq *l_next;	/* next in queue */
-	bool_t	l_queued;	/* nonzero if queued */
-};
-
-/* com device structure */
-struct com_s {
-	u_int	flags;		/* Copy isa device flags */
-	u_char	state;		/* miscellaneous flag bits */
-	bool_t  active_out;	/* nonzero if the callout device is open */
-	u_char	cfcr_image;	/* copy of value written to CFCR */
-#ifdef COM_ESP
-	bool_t	esp;		/* is this unit a hayes esp board? */
-#endif
-	u_char	extra_state;	/* more flag bits, separate for order trick */
-	u_char	fifo_image;	/* copy of value written to FIFO */
-	bool_t	hasfifo;	/* nonzero for 16550 UARTs */
-	bool_t	st16650a;	/* Is a Startech 16650A or RTS/CTS compat */
-	bool_t	loses_outints;	/* nonzero if device loses output interrupts */
-	u_char	mcr_image;	/* copy of value written to MCR */
-#ifdef COM_MULTIPORT
-	bool_t	multiport;	/* is this unit part of a multiport device? */
-#endif /* COM_MULTIPORT */
-	bool_t	no_irq;		/* nonzero if irq is not attached */
-	bool_t  gone;		/* hardware disappeared */
-	bool_t	poll;		/* nonzero if polling is required */
-	bool_t	poll_output;	/* nonzero if polling for output is required */
-	int	unit;		/* unit	number */
-	int	dtr_wait;	/* time to hold DTR down on close (* 1/hz) */
-	u_int	tx_fifo_size;
-	u_int	wopeners;	/* # processes waiting for DCD in open() */
-
-	/*
-	 * The high level of the driver never reads status registers directly
-	 * because there would be too many side effects to handle conveniently.
-	 * Instead, it reads copies of the registers stored here by the
-	 * interrupt handler.
-	 */
-	u_char	last_modem_status;	/* last MSR read by intr handler */
-	u_char	prev_modem_status;	/* last MSR handled by high level */
-
-	u_char	hotchar;	/* ldisc-specific char to be handled ASAP */
-	u_char	*ibuf;		/* start of input buffer */
-	u_char	*ibufend;	/* end of input buffer */
-	u_char	*ibufold;	/* old input buffer, to be freed */
-	u_char	*ihighwater;	/* threshold in input buffer */
-	u_char	*iptr;		/* next free spot in input buffer */
-	int	ibufsize;	/* size of ibuf (not include error bytes) */
-	int	ierroff;	/* offset of error bytes in ibuf */
-
-	struct lbq	obufq;	/* head of queue of output buffers */
-	struct lbq	obufs[2];	/* output buffers */
-
-	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 */
-
-	/* Initial state. */
-	struct termios	it_in;	/* should be in struct tty */
-	struct termios	it_out;
-
-	/* Lock state. */
-	struct termios	lt_in;	/* should be in struct tty */
-	struct termios	lt_out;
-
-	bool_t	do_timestamp;
-	bool_t	do_dcd_timestamp;
-	struct timeval	timestamp;
-	struct timeval	dcd_timestamp;
-	struct	pps_state pps;
-	int	pps_bit;
-
-	u_long	bytes_in;	/* statistics */
-	u_long	bytes_out;
-	u_int	delta_error_counts[CE_NTYPES];
-	u_long	error_counts[CE_NTYPES];
-
-	u_long	rclk;
-
-	struct resource *irqres;
-	struct resource *ioportres;
-	void *cookie;
-	dev_t devs[6];
-
-	/*
-	 * Data area for output buffers.  Someday we should build the output
-	 * buffer queue without copying data.
-	 */
-	u_char	obuf1[256];
-	u_char	obuf2[256];
-};
-
-#ifdef COM_ESP
-static	int	espattach(struct com_s *com, Port_t esp_port);
-#endif
-
 static	timeout_t siobusycheck;
-static	u_int	siodivisor(u_long rclk, speed_t speed);
 static	timeout_t siodtrwakeup;
 static	void	comhardclose(struct com_s *com);
 static	void	sioinput(struct com_s *com);
@@ -327,18 +186,16 @@
 	.d_kqfilter =	ttykqfilter,
 };
 
-static	int			comconsole = -1;
-static	volatile speed_t	comdefaultrate = CONSPEED;
-static	u_long			comdefaultrclk = DEFAULT_RCLK;
+speed_t comdefaultrate = CONSPEED;
+
+u_long comdefaultrclk = DEFAULT_RCLK;
 SYSCTL_ULONG(_machdep, OID_AUTO, conrclk, CTLFLAG_RW, &comdefaultrclk, 0, "");
-static	speed_t			gdbdefaultrate = GDBSPEED;
-SYSCTL_UINT(_machdep, OID_AUTO, gdbspeed, CTLFLAG_RW,
-	    &gdbdefaultrate, GDBSPEED, "");
+
+static speed_t gdbdefaultrate = GDBSPEED;
+SYSCTL_ULONG(_machdep, OID_AUTO, gdbspeed, CTLFLAG_RW, &gdbdefaultrate,
+    GDBSPEED, "");
+
 static	u_int	com_events;	/* input chars + weighted output completions */
-static	Port_t	siocniobase;
-static	int	siocnunit = -1;
-static	Port_t	siogdbiobase;
-static	int	siogdbunit = -1;
 static	void	*sio_slow_ih;
 static	void	*sio_fast_ih;
 static	int	sio_timeout;
@@ -347,13 +204,6 @@
     = CALLOUT_HANDLE_INITIALIZER(&sio_timeout_handle);
 static	int	sio_numunits;
 
-#ifdef COM_ESP
-/* XXX configure this properly. */
-/* XXX quite broken for new-bus. */
-static	Port_t	likely_com_ports[] = { 0x3f8, 0x2f8, 0x3e8, 0x2e8, };
-static	Port_t	likely_esp_ports[] = { 0x140, 0x180, 0x280, 0 };
-#endif
-
 /*
  * handle sysctl read/write requests for console speed
  * 
@@ -380,13 +230,10 @@
 
 	comdefaultrate = newspeed;
 
-	if (comconsole < 0)		/* serial console not selected? */
+	com = &sio_console;
+	if (com->consdev == NULL)
 		return (0);
 
-	com = com_addr(comconsole);
-	if (com == NULL)
-		return (ENXIO);
-
 	/*
 	 * set the initial and lock rates for /dev/ttydXX and /dev/cuaXX
 	 * (note, the lock rates really are boolean -- if non-zero, disallow
@@ -415,544 +262,498 @@
 	    0, 0, sysctl_machdep_comdefaultrate, "I", "");
 /* TUNABLE_INT("machdep.conspeed", &comdefaultrate); */
 
-#define SET_FLAG(dev, bit) device_set_flags(dev, device_get_flags(dev) | (bit))
-#define CLR_FLAG(dev, bit) device_set_flags(dev, device_get_flags(dev) & ~(bit))
+#ifdef SIO_DEBUG
+static void
+siodebug(struct com_s *com, const char *fmt, ...)
+{
+	va_list ap;
+
+	va_start(ap, fmt);
+	if (com != NULL)
+		device_print_prettyname(com->dev);
+	vprintf(fmt, ap);
+	va_end(ap);
+}
+#else
+static __inline void
+siodebug(struct com_s *com, const char *fmt, ...)
+{
+}
+#endif
 
 /*
- *	Unload the driver and clear the table.
- *	XXX this is mostly wrong.
- *	XXX TODO:
- *	This is usually called when the card is ejected, but
- *	can be caused by a kldunload of a controller driver.
- *	The idea is to reset the driver's view of the device
- *	and ensure that any driver entry points such as
- *	read and write do not hang.
+ * Flush the UART. Flush the transmitter FIFO and shift register first, then
+ * flush the receiver FIFO. In this order flushing works correctly even when
+ * the UART is in loopback mode. Bad timing may cause at most one character
+ * to remain in the receiver FIFO/buffer after we're done flushing. This
+ * would be the character that is (partly) in the receiver shift register.
  */
-int
-siodetach(dev)
-	device_t	dev;
+static int
+sioflush(struct com_s *com)
 {
-	struct com_s	*com;
-	int i;
+	int delay, limit;
+
+	/* 1/10th the time to transmit 1 character (estimate). */
+	delay = 16000000 * com->reg_dl / com->rclk;
 
-	com = (struct com_s *) device_get_softc(dev);
-	if (com == NULL) {
-		device_printf(dev, "NULL com in siounload\n");
-		return (0);
+	/* Flush the transmitter. */
+	limit = (com->fifosize) ? com->fifosize : 1;
+	limit = (limit + 2) * 10;
+	while ((sio_getreg(com, com_lsr) & LSR_TEMT) == 0 && --limit)
+		DELAY(delay);
+	if (limit == 0) {
+		siodebug(NULL, "transmitter appears stuck... ");
+		return (EIO);
 	}
-	com->gone = 1;
-	for (i = 0 ; i < 6; i++)
-		destroy_dev(com->devs[i]);
-	if (com->irqres) {
-		bus_teardown_intr(dev, com->irqres, com->cookie);
-		bus_release_resource(dev, SYS_RES_IRQ, 0, com->irqres);
+
+	DELAY(10*delay);
+
+	/*
+	 * Flush the receiver. Pick an arbitrary high limit to avoid
+	 * getting stuck in an infinite loop when the hardware is
+	 * broken.
+	 */
+	limit=32768;
+	while ((sio_getreg(com, com_lsr) & LSR_RXRDY) && --limit) {
+		(void)sio_getreg(com, com_data);
+		/* XXX barrier */
+		DELAY(5*delay);
 	}
-	if (com->ioportres)
-		bus_release_resource(dev, SYS_RES_IOPORT, 0, com->ioportres);
-	if (com->tp && (com->tp->t_state & TS_ISOPEN)) {
-		device_printf(dev, "still open, forcing close\n");
-		(*linesw[com->tp->t_line].l_close)(com->tp, 0);
-		com->tp->t_gen++;
-		ttyclose(com->tp);
-		ttwakeup(com->tp);
-		ttwwakeup(com->tp);
-	} else {
-		if (com->ibuf != NULL)
-			free(com->ibuf, M_DEVBUF);
-		device_set_softc(dev, NULL);
-		free(com, M_DEVBUF);
+	if (limit == 0) {
+		siodebug(NULL, "receiver appears broken... ");
+		return (EIO);
 	}
+
 	return (0);
 }
 
-int
-sioprobe(dev, xrid, rclk, noprobe)
-	device_t	dev;
-	int		xrid;
-	u_long		rclk;
-	int		noprobe;
+/*
+ * Initialize the FIFOs and determine the size of the receiver FIFO. We
+ * assume that the transmitter FIFO has the same size. First we set DMA
+ * mode (mode 1) with the highest trigger level. In combination with
+ * loopback this allows us to determine the size of the receiver FIFO.
+ * After that we re-initialize with a medium high trigger level. This
+ * function is expected to be called with FIFOs disabled.
+ */
+static int
+sioinitfifo(struct com_s *com)
 {
-#if 0
-	static bool_t	already_init;
-	device_t	xdev;
-#endif
-	struct com_s	*com;
-	u_int		divisor;
-	bool_t		failures[10];
-	int		fn;
-	device_t	idev;
-	Port_t		iobase;
-	intrmask_t	irqmap[4];
-	intrmask_t	irqs;
-	u_char		mcr_image;
-	int		result;
-	u_long		xirq;
-	u_int		flags = device_get_flags(dev);
-	int		rid;
-	struct resource *port;
+	int count, delay, limit;
+
+	com->hasfifo = 0;
+	com->fifosize = 0;
 
-	rid = xrid;
-	port = bus_alloc_resource(dev, SYS_RES_IOPORT, &rid,
-				  0, ~0, IO_COMSIZE, RF_ACTIVE);
-	if (!port)
-		return (ENXIO);
+	/* 1/10th the time to transmit 1 character (estimate). */
+	delay = 16000000 * com->reg_dl / com->rclk;
 
-	com = malloc(sizeof(*com), M_DEVBUF, M_NOWAIT | M_ZERO);
-	if (com == NULL)
-		return (ENOMEM);
-	device_set_softc(dev, com);
-	com->bst = rman_get_bustag(port);
-	com->bsh = rman_get_bushandle(port);
-	if (rclk == 0)
-		rclk = DEFAULT_RCLK;
-	com->rclk = rclk;
+	/*
+	 * Make sure the transmitter is idle before we play with the
+	 * FIFOs. We don't wait more than roughly three times as long
+	 * as needed to send a single character. Given that we may need
+	 * to wait for both the THR and the TSR to clear, this leaves
+	 * us roughly 1 character time slack.
+	 */
+	limit = 30;
+	while ((sio_getreg(com, com_lsr) & LSR_TEMT) == 0 && --limit) {
+		if (delay)
+			DELAY(delay);
+	}
+	if (limit == 0) {
+		siodebug(NULL, "transmitter appears stuck");
+		return (EIO);
+	}
 
-	while (sio_inited != 2)
-		if (atomic_cmpset_int(&sio_inited, 0, 1)) {
-			mtx_init(&sio_lock, sio_driver_name, NULL,
-			    (comconsole != -1) ?
-			    MTX_SPIN | MTX_QUIET : MTX_SPIN);
-			atomic_store_rel_int(&sio_inited, 2);
-		}
+	/*
+	 * Set loopback mode. This avoids having garbage on the wire and
+	 * also allows us send and receive data. We set DTR and RTS to
+	 * avoid the possibility that automatic flow-control prevents
+	 * any data from being sent.
+	 */
+	sio_setreg(com, com_mcr, com->reg_mcr | MCR_LOOPBACK|MCR_DTR|MCR_RTS);
+	/* XXX barrier */
 
-#if 0
 	/*
-	 * XXX this is broken - when we are first called, there are no
-	 * previously configured IO ports.  We could hard code
-	 * 0x3f8, 0x2f8, 0x3e8, 0x2e8 etc but that's probably worse.
-	 * This code has been doing nothing since the conversion since
-	 * "count" is zero the first time around.
+	 * Enable FIFOs. Set DMA mode with the highest trigger level so
+	 * that we can determine the FIFO size. Since this is the first
+	 * time we enable the FIFOs, reset them.
 	 */
-	if (!already_init) {
-		/*
-		 * Turn off MCR_IENABLE for all likely serial ports.  An unused
-		 * port with its MCR_IENABLE gate open will inhibit interrupts
-		 * from any used port that shares the interrupt vector.
-		 * XXX the gate enable is elsewhere for some multiports.
-		 */
-		device_t *devs;
-		int count, i, xioport;
+	com->reg_fcr = FCR_ENABLE | FCR_DMA_MODE | FCR_RX_HIGH;
+	sio_setreg(com, com_fcr, com->reg_fcr | FCR_XMT_RST | FCR_RCV_RST);
+	/* XXX barrier */
 
-		devclass_get_devices(sio_devclass, &devs, &count);
-		for (i = 0; i < count; i++) {
-			xdev = devs[i];
-			if (device_is_enabled(xdev) &&
-			    bus_get_resource(xdev, SYS_RES_IOPORT, 0, &xioport,
-					     NULL) == 0)
-				outb(xioport + com_mcr, 0);
-		}
-		free(devs, M_TEMP);
-		already_init = TRUE;
+	/* Check if the UART has FIFOs. If not, we're done. */
+	com->hasfifo = (sio_getreg(com, com_iir) & IIR_FIFO_MASK) ? 1 : 0;
+	if (!com->hasfifo) {
+		sio_setreg(com, com_mcr, com->reg_mcr);
+		/* XXX barrier */
+		siodebug(NULL, "no FIFOs... ");
+		return (0);
 	}
-#endif
 
-	if (COM_LLCONSOLE(flags)) {
-		printf("sio%d: reserved for low-level i/o\n",
-		       device_get_unit(dev));
-		bus_release_resource(dev, SYS_RES_IOPORT, rid, port);
-		device_set_softc(dev, NULL);
-		free(com, M_DEVBUF);
-		return (ENXIO);
+	/* We have FIFOs. Flush the transmitter and receiver. */
+	if (sioflush(com)) {
+		sio_setreg(com, com_mcr, com->reg_mcr);
+		/* XXX barrier */
+		goto fallback;
 	}
 
+	sio_setreg(com, com_ier, IER_ERXRDY);
+	/* XXX barrier */
+
 	/*
-	 * If the device is on a multiport card and has an AST/4
-	 * compatible interrupt control register, initialize this
-	 * register and prepare to leave MCR_IENABLE clear in the mcr.
-	 * Otherwise, prepare to set MCR_IENABLE in the mcr.
-	 * Point idev to the device struct giving the correct id_irq.
-	 * This is the struct for the master device if there is one.
+	 * We should have a sufficiently clean "pipe" to determine the
+	 * size of the FIFOs. We send as much characters as is reasonable
+	 * and wait for the the RX interrupt to be asserted, counting the
+	 * characters as we send them. Based on that count we know the
+	 * FIFO size.
 	 */
-	idev = dev;
-	mcr_image = MCR_IENABLE;
-#ifdef COM_MULTIPORT
-	if (COM_ISMULTIPORT(flags)) {
-		Port_t xiobase;
-		u_long io;
+	count = 0;
+	while ((sio_getreg(com, com_iir) & IIR_RXRDY) == 0 && count < 1030) {
+		sio_setreg(com, com_data, 0);
+		/* XXX barrier */
+		count++;
 
-		idev = devclass_get_device(sio_devclass, COM_MPMASTER(flags));
-		if (idev == NULL) {
-			printf("sio%d: master device %d not configured\n",
-			       device_get_unit(dev), COM_MPMASTER(flags));
-			idev = dev;
+		limit = 30;
+		while ((sio_getreg(com, com_lsr) & LSR_TEMT) == 0 && --limit) {
+			if (delay)
+				DELAY(delay);
 		}
-		if (!COM_NOTAST4(flags)) {
-			if (bus_get_resource(idev, SYS_RES_IOPORT, 0, &io,
-					     NULL) == 0) {
-				xiobase = io;
-				if (bus_get_resource(idev, SYS_RES_IRQ, 0,
-				    NULL, NULL) == 0)
-					outb(xiobase + com_scr, 0x80);
-				else
-					outb(xiobase + com_scr, 0);
-			}
-			mcr_image = 0;
+		if (limit == 0) {
+			sio_setreg(com, com_ier, 0);
+			/* XXX barrier */
+			sio_setreg(com, com_mcr, com->reg_mcr);
+			/* XXX barrier */
+			siodebug(NULL, "can't determine FIFO size... ");
+			goto fallback;
 		}
 	}
-#endif /* COM_MULTIPORT */
-	if (bus_get_resource(idev, SYS_RES_IRQ, 0, NULL, NULL) != 0)
-		mcr_image = 0;
+
+	sio_setreg(com, com_ier, 0);
+	/* XXX barrier */
+
+	/* Reset FIFOs. */
+	com->reg_fcr = FCR_ENABLE;
+	sio_setreg(com, com_fcr, com->reg_fcr | FCR_XMT_RST | FCR_RCV_RST);
+	/* XXX barrier */
+
+	sio_setreg(com, com_mcr, com->reg_mcr);
+	/* XXX barrier */
+
+	if (count >= 14 && count < 16)
+		com->fifosize = 16;		/* 16550 */
+	else if (count >= 28 && count < 32)
+		com->fifosize = 32;		/* 16650 */
+	else if (count >= 56 && count < 64)
+		com->fifosize = 64;		/* 16750 */
+	else if (count >= 112 && count < 128)
+		com->fifosize = 128;		/* 16950 */
+	else
+		com->fifosize = 1;		/* XXX */
 
-	bzero(failures, sizeof failures);
-	iobase = rman_get_start(port);
+	siodebug(NULL, "count=%d; FIFO=%d... ", count, com->fifosize);
+	return (0);
 
-	/*
-	 * We don't want to get actual interrupts, just masked ones.
-	 * Interrupts from this line should already be masked in the ICU,
-	 * but mask them in the processor as well in case there are some
-	 * (misconfigured) shared interrupts.
-	 */
-	mtx_lock_spin(&sio_lock);
-/* EXTRA DELAY? */
+fallback:
+	siodebug(NULL, "disabling FIFOs... ");
+	com->hasfifo = 0;
+	return (0);
+}
 
-	/*
-	 * For the TI16754 chips, set prescaler to 1 (4 is often the
-	 * default after-reset value) as otherwise it's impossible to
-	 * get highest baudrates.
-	 */
-	if (COM_TI16754(flags)) {
-		u_char cfcr, efr;
+static void
+siodescribe(struct com_s *com)
+{
+	int has_spr;
+	u_char spr;
 
-		cfcr = sio_getreg(com, com_cfcr);
-		sio_setreg(com, com_cfcr, CFCR_EFR_ENABLE);
-		efr = sio_getreg(com, com_efr);
-		/* Unlock extended features to turn off prescaler. */
-		sio_setreg(com, com_efr, efr | EFR_EFE);
-		/* Disable EFR. */
-		sio_setreg(com, com_cfcr, (cfcr != CFCR_EFR_ENABLE) ? cfcr : 0);
-		/* Turn off prescaler. */
-		sio_setreg(com, com_mcr,
-			   sio_getreg(com, com_mcr) & ~MCR_PRESCALE);
-		sio_setreg(com, com_cfcr, CFCR_EFR_ENABLE);
-		sio_setreg(com, com_efr, efr);
-		sio_setreg(com, com_cfcr, cfcr);
+	if (com->hasfifo) {
+		/*
+		 * NS16550 or higher. The minimum FIFO size is 16 bytes for
+		 * the NS16550. The ST16C650 has 32 bytes FIFOs and the
+		 * NS16750 has 64 bytes FIFOs.
+		 */
+		switch (com->fifosize) {
+		case 16:
+			/*
+			 * XXX Should we try to see if we have a broken
+			 * 16550 or a fixed 16550A?
+			 */
+			device_set_desc(com->dev, "16550 or compatible");
+			break;
+		case 32:
+			/* XXX Should we check features? */
+			device_set_desc(com->dev, "16650 or compatible");
+			break;
+		case 64:
+			/* XXX Should we check features? */
+			device_set_desc(com->dev, "16750 or compatible");
+			break;
+		case 128:
+			/* XXX Should we check features? */
+			device_set_desc(com->dev, "16950 or compatible");
+			break;
+		default:
+			/* XXX Probably not right. */
+			device_set_desc(com->dev,
+			    "Non-standard or broken UART with FIFO");
+			break;
+		}
+	} else {
+		/*
+		 * NS16450 or lower. Use the Scratch Pad Register (SPR) to
+		 * differentiate the NS16450 from the INS8250.
+		 */
+		spr = sio_getreg(com, com_scr);
+		sio_setreg(com, com_scr, ~spr);
+		/* XXX barrier */
+		has_spr = (sio_getreg(com, com_scr) == ~spr) ? 1 : 0;
+		/* XXX barrier */
+		sio_setreg(com, com_scr, spr);
+		/* XXX barrier */
+		if (!has_spr)
+			device_set_desc(com->dev, "8250 or compatible");
+		else
+			device_set_desc(com->dev, "16450 or compatible");
 	}
+}
+
+u_int
+siodivisor(u_long rclk, u_long speed)
+{
+	long	actual_speed;
+	u_int	divisor;
+	int	error;
+
+	if (speed == 0)
+		return (0);
+#if UINT_MAX > (ULONG_MAX - 1) / 8
+	if (speed > (ULONG_MAX - 1) / 8)
+		return (0);
+#endif
+	divisor = (rclk / (8UL * speed) + 1) / 2;
+	if (divisor == 0 || divisor >= 65536)
+		return (0);
+	actual_speed = rclk / (16UL * divisor);
 
-	/*
-	 * Initialize the speed and the word size and wait long enough to
-	 * drain the maximum of 16 bytes of junk in device output queues.
-	 * The speed is undefined after a master reset and must be set
-	 * before relying on anything related to output.  There may be
-	 * junk after a (very fast) soft reboot and (apparently) after
-	 * master reset.
-	 * 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)
-		DELAY((16 + 1) * 1000000 / (comdefaultrate / 10));
-	else {
-		sio_setreg(com, com_cfcr, CFCR_DLAB | CFCR_8BITS);
-		divisor = siodivisor(rclk, SIO_TEST_SPEED);
-		sio_setreg(com, com_dlbl, divisor & 0xff);
-		sio_setreg(com, com_dlbh, divisor >> 8);
-		sio_setreg(com, com_cfcr, CFCR_8BITS);
-		DELAY((16 + 1) * 1000000 / (SIO_TEST_SPEED / 10));
-	}
+	/* 10 times error in percent: */
+	error = ((actual_speed - (long)speed) * 2000 / (long)speed + 1) / 2;
 
-	/*
-	 * Enable the interrupt gate and disable device interupts.  This
-	 * should leave the device driving the interrupt line low and
-	 * guarantee an edge trigger if an interrupt can be generated.
-	 */
-/* EXTRA DELAY? */
-	sio_setreg(com, com_mcr, mcr_image);
-	sio_setreg(com, com_ier, 0);
-	DELAY(1000);		/* XXX */
-	irqmap[0] = isa_irq_pending();
+	/* 3.0% maximum error tolerance: */
+	if (error < -30 || error > 30)
+		return (0);
 
-	/*
-	 * Attempt to set loopback mode so that we can send a null byte
-	 * without annoying any external device.
-	 */
-/* EXTRA DELAY? */
-	sio_setreg(com, com_mcr, mcr_image | MCR_LOOPBACK);
+	return (divisor);
+}
 
-	/*
-	 * Attempt to generate an output interrupt.  On 8250's, setting
-	 * IER_ETXRDY generates an interrupt independent of the current
-	 * setting and independent of whether the THR is empty.  On 16450's,
-	 * setting IER_ETXRDY generates an interrupt independent of the
-	 * current setting.  On 16550A's, setting IER_ETXRDY only
-	 * generates an interrupt when IER_ETXRDY is not already set.
-	 */
-	sio_setreg(com, com_ier, IER_ETXRDY);
+/*
+ * Do some non-destructive (for this device that is) tests
+ * to make sure we have something that looks like an UART.
+ */
+int sioprobe1(struct com_s *com)
+{
+	u_char lcr, val;
 
-	/*
-	 * On some 16x50 incompatibles, setting IER_ETXRDY doesn't generate
-	 * an interrupt.  They'd better generate one for actually doing
-	 * output.  Loopback may be broken on the same incompatibles but
-	 * it's unlikely to do more than allow the null byte out.
-	 */
-	sio_setreg(com, com_data, 0);
-	DELAY((1 + 2) * 1000000 / (SIO_TEST_SPEED / 10));
+	lcr = sio_getreg(com, com_lcr);
+	sio_setreg(com, com_lcr, lcr & ~LCR_DLAB);
 
-	/*
-	 * Turn off loopback mode so that the interrupt gate works again
-	 * (MCR_IENABLE was hidden).  This should leave the device driving
-	 * an interrupt line high.  It doesn't matter if the interrupt
-	 * line oscillates while we are not looking at it, since interrupts
-	 * are disabled.
-	 */
-/* EXTRA DELAY? */
-	sio_setreg(com, com_mcr, mcr_image);
- 
-	/*
-	 * It seems my Xircom CBEM56G Cardbus modem wants to be reset
-	 * to 8 bits *again*, or else probe test 0 will fail.
-	 * gwk at sgi.com, 4/19/2001
-	 */
-	sio_setreg(com, com_cfcr, CFCR_8BITS);
+	/* Check known 0 bits that don't depend on DLAB. */
+	val = sio_getreg(com, com_iir);
+	if (val & 0x30)
+		goto fail;
+	val = sio_getreg(com, com_mcr);
+	if (val & 0xe0)
+		goto fail;
 
-	/*
-	 * Some pcmcia cards have the "TXRDY bug", so we check everyone
-	 * for IIR_TXRDY implementation ( Palido 321s, DC-1S... )
-	 */
-	if (noprobe) {
-		/* Reading IIR register twice */
-		for (fn = 0; fn < 2; fn ++) {
-			DELAY(10000);
-			failures[6] = sio_getreg(com, com_iir);
-		}
-		/* Check IIR_TXRDY clear ? */
-		result = 0;
-		if (failures[6] & IIR_TXRDY) {
-			/* No, Double check with clearing IER */
-			sio_setreg(com, com_ier, 0);
-			if (sio_getreg(com, com_iir) & IIR_NOPEND) {
-				/* Ok. We discovered TXRDY bug! */
-				SET_FLAG(dev, COM_C_IIR_TXRDYBUG);
-			} else {
-				/* Unknown, Just omit this chip.. XXX */
-				result = ENXIO;
-				sio_setreg(com, com_mcr, 0);
-			}
-		} else {
-			/* OK. this is well-known guys */
-			CLR_FLAG(dev, COM_C_IIR_TXRDYBUG);
-		}
-		sio_setreg(com, com_ier, 0);
-		sio_setreg(com, com_cfcr, CFCR_8BITS);
-		mtx_unlock_spin(&sio_lock);
-		bus_release_resource(dev, SYS_RES_IOPORT, rid, port);
-		if (iobase == siocniobase)
-			result = 0;
-		if (result != 0) {
-			device_set_softc(dev, NULL);
-			free(com, M_DEVBUF);
-		}
-		return (result);
-	}
+	/* Check known 0 bits that depend on !DLAB. */
+	val = sio_getreg(com, com_ier);
+	if (val & 0xf0)
+		goto fail;
 
-	/*
-	 * Check that
-	 *	o the CFCR, IER and MCR in UART hold the values written to them
-	 *	  (the values happen to be all distinct - this is good for
-	 *	  avoiding false positive tests from bus echoes).
-	 *	o an output interrupt is generated and its vector is correct.
-	 *	o the interrupt goes away when the IIR in the UART is read.
-	 */
-/* EXTRA DELAY? */
-	failures[0] = sio_getreg(com, com_cfcr) - CFCR_8BITS;
-	failures[1] = sio_getreg(com, com_ier) - IER_ETXRDY;
-	failures[2] = sio_getreg(com, com_mcr) - mcr_image;
-	DELAY(10000);		/* Some internal modems need this time */
-	irqmap[1] = isa_irq_pending();
-	failures[4] = (sio_getreg(com, com_iir) & IIR_IMASK) - IIR_TXRDY;
-	DELAY(1000);		/* XXX */
-	irqmap[2] = isa_irq_pending();
-	failures[6] = (sio_getreg(com, com_iir) & IIR_IMASK) - IIR_NOPEND;
+	/* XXX we can do more. */
 
-	/*
-	 * Turn off all device interrupts and check that they go off properly.
-	 * Leave MCR_IENABLE alone.  For ports without a master port, it gates
-	 * the OUT2 output of the UART to
-	 * the ICU input.  Closing the gate would give a floating ICU input
-	 * (unless there is another device driving it) and spurious interrupts.
-	 * (On the system that this was first tested on, the input floats high
-	 * and gives a (masked) interrupt as soon as the gate is closed.)
-	 */
 	sio_setreg(com, com_ier, 0);
-	sio_setreg(com, com_cfcr, CFCR_8BITS);	/* dummy to avoid bus echo */
-	failures[7] = sio_getreg(com, com_ier);
-	DELAY(1000);		/* XXX */
-	irqmap[3] = isa_irq_pending();
-	failures[9] = (sio_getreg(com, com_iir) & IIR_IMASK) - IIR_NOPEND;
+	/* XXX barrier */
+	sio_setreg(com, com_lcr, lcr);
+	/* XXX barrier */
+	return (0);
+
+ fail:
+	sio_setreg(com, com_lcr, lcr);
+	/* XXX barrier */
+	return (ENXIO);
+}
+
+/*
+ *	Unload the driver and clear the table.
+ *	XXX this is mostly wrong.
+ *	XXX TODO:
+ *	This is usually called when the card is ejected, but
+ *	can be caused by a kldunload of a controller driver.
+ *	The idea is to reset the driver's view of the device
+ *	and ensure that any driver entry points such as
+ *	read and write do not hang.
+ */
+int
+siodetach(device_t dev)
+{
+	struct com_s *com;
+	int i;
 
-	mtx_unlock_spin(&sio_lock);
+	com = (struct com_s *)device_get_softc(dev);
+	com->gone = 1;
 
-	irqs = irqmap[1] & ~irqmap[0];
-	if (bus_get_resource(idev, SYS_RES_IRQ, 0, &xirq, NULL) == 0 &&
-	    ((1 << xirq) & irqs) == 0) {
-		printf(
-		"sio%d: configured irq %ld not in bitmap of probed irqs %#x\n",
-		    device_get_unit(dev), xirq, irqs);
-		printf(
-		"sio%d: port may not be enabled\n",
-		    device_get_unit(dev));
+	if (com->tp && (com->tp->t_state & TS_ISOPEN)) {
+		device_printf(dev, "still open, forcing close\n");
+		(*linesw[com->tp->t_line].l_close)(com->tp, 0);
+		com->tp->t_gen++;
+		ttyclose(com->tp);
+		ttwakeup(com->tp);
+		ttwwakeup(com->tp);
 	}
-	if (bootverbose)
-		printf("sio%d: irq maps: %#x %#x %#x %#x\n",
-		    device_get_unit(dev),
-		    irqmap[0], irqmap[1], irqmap[2], irqmap[3]);
-
-	result = 0;
-	for (fn = 0; fn < sizeof failures; ++fn)
-		if (failures[fn]) {
-			sio_setreg(com, com_mcr, 0);
-			result = ENXIO;
-			if (bootverbose) {
-				printf("sio%d: probe failed test(s):",
-				    device_get_unit(dev));
-				for (fn = 0; fn < sizeof failures; ++fn)
-					if (failures[fn])
-						printf(" %d", fn);
-				printf("\n");
-			}
-			break;
-		}
-	bus_release_resource(dev, SYS_RES_IOPORT, rid, port);
-	if (iobase == siocniobase)
-		result = 0;
-	if (result != 0) {
-		device_set_softc(dev, NULL);
-		free(com, M_DEVBUF);
+	if (com->irq_res) {
+		bus_teardown_intr(dev, com->irq_res, com->irq_cookie);
+		bus_release_resource(dev, SYS_RES_IRQ, 0, com->irq_res);
 	}
-	return (result);
+	bus_release_resource(dev, com->addr_type, com->addr_rid, com->addr_res);
+	for (i = 0 ; i < 6; i++)
+		destroy_dev(com->devs[i]);
+	if (com->ibuf != NULL)
+		free(com->ibuf, M_DEVBUF);
+	return (0);
 }
 
-#ifdef COM_ESP
-static int
-espattach(com, esp_port)
-	struct com_s		*com;
-	Port_t			esp_port;
+int
+sioprobe(device_t dev)
 {
-	u_char	dips;
-	u_char	val;
+	struct com_s	*com;
+	int		error;
+	u_int		flags;
+

>>> TRUNCATED FOR MAIL (1000 lines) <<<


More information about the p4-projects mailing list