Skip site navigation (1)Skip section navigation (2)
Date:      Tue, 24 Dec 2002 22:22:56 -0800 (PST)
From:      Marcel Moolenaar <marcel@FreeBSD.org>
To:        Perforce Change Reviews <perforce@freebsd.org>
Subject:   PERFORCE change 22732 for review
Message-ID:  <200212250622.gBP6MuMo074496@repoman.freebsd.org>

next in thread | raw e-mail | index | archive | help
http://perforce.freebsd.org/chv.cgi?CH=22732

Change 22732 by marcel@marcel_nfs on 2002/12/24 22:22:25

	Auto-detect the FIFO size. Keep a debug printf in
	there to keep an eye on it for a while.
	
	Rename two LCR bits to match the names used in the
	datasheet.

Affected files ...

.. //depot/projects/ia64/sys/dev/sio/sio.c#30 edit
.. //depot/projects/ia64/sys/dev/sio/sio_cons.c#9 edit
.. //depot/projects/ia64/sys/dev/sio/sioreg.h#6 edit

Differences ...

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

@@ -264,43 +264,158 @@
 /* TUNABLE_INT("machdep.conspeed", &comdefaultrate); */
 
 /*
- * Initialize FIFOs. We cannot simple enable the FIFOs because that may cause
- * garbage to appear on the wire (non-empty transmit FIFO) or garbage to be
- * received (non-empty receive FIFO). The former is prevented by enabling
- * loopback prior to enabling the FIFOs. The latter is prevented by ignoring
- * any data in the RHR after we have reset the FIFOs.
+ * Set a FIFO mode. We try not to be disruptive, but leave some of the
+ * responsibility for the caller to avoid unconditional high overhead.
+ * For best results, set loopback mode and clear all interrupt sources
+ * before calling this function. The loopback mode prevents garbage on
+ * the line and clearing IER avoids possible stray interrupts.
  */
-static void
-sioinitfifo(struct com_s *com)
+static int
+siosetfifo(struct com_s *com, int fcr)
 {
+	int delay, limit;
+
+	/* 1/10th the time to transmit 1 character (estimate). */
+	delay = 16000000 * com->reg_dl / com->rclk;
 
-	sio_setreg(com, com_mcr, com->reg_mcr | MCR_LOOPBACK);
+	com->reg_fcr = 0;
+	sio_setreg(com, com_fcr, 0);
 	/* XXX barrier */
-	sio_setreg(com, com_fcr, FCR_ENABLE);
+
+	/*
+	 * 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)
+		return (EIO);
+
+	com->reg_fcr = fcr;
+	sio_setreg(com, com_fcr, fcr | FCR_XMT_RST | FCR_RCV_RST);
 	/* XXX barrier */
-	sio_setreg(com, com_fcr, FCR_ENABLE | FCR_RCV_RST | FCR_XMT_RST);
-	/* XXX barrier */
-	DELAY(100);
-	com->reg_fcr = 0;
+
+	/*
+	 * Set the hasfifo field when the device has FIFOs. In that case
+	 * we also flush any possible garbage from the transmitter and
+	 * receiver. Flush the transmitter before flushing the receiver
+	 * to allow having the UART in loopback mode. Since we always
+	 * enable FIFO mode after disabling it first, we won't have more
+	 * than two characters the flush.
+	 */
 	com->hasfifo = (sio_getreg(com, com_iir) & IIR_FIFO_MASK) ? 1 : 0;
 	if (com->hasfifo) {
-		while (sio_getreg(com, com_lsr) & LSR_RXRDY) {
+		limit = 30;
+		while ((sio_getreg(com, com_lsr) & LSR_TEMT) == 0 && --limit) {
+			if (delay)
+				DELAY(delay);
+		}
+		if (limit == 0)
+			return (EIO);
+
+		limit = 30;
+		while ((sio_getreg(com, com_lsr) & LSR_RXRDY) && --limit) {
 			(void)sio_getreg(com, com_data);
 			/* XXX barrier */
 		}
-		com->reg_fcr = FCR_ENABLE | FCR_RX_HIGH;
-		sio_setreg(com, com_mcr, com->reg_fcr);
+		if (limit == 0)
+			return (EIO);
+	}
+
+	return (0);
+}
+
+/*
+ * 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.
+ */
+static int
+sioinitfifo(struct com_s *com)
+{
+	int count, delay, error, limit;
+
+	com->fifosize = 0;
+
+	sio_setreg(com, com_mcr, MCR_LOOPBACK | MCR_DTR | MCR_RTS);
+	/* XXX barrier */
+
+	error = siosetfifo(com, FCR_ENABLE | FCR_DMA_MODE | FCR_RX_HIGH);
+	if (error || !com->hasfifo) {
+		sio_setreg(com, com_mcr, com->reg_mcr);
+		/* XXX barrier */
+		return (error);
+	}
+
+	/* 1/10th the time to transmit 1 character (estimate). */
+	delay = 16000000 * com->reg_dl / com->rclk;
+
+	sio_setreg(com, com_ier, IER_ERXRDY);
+	/* XXX barrier */
+
+	/*
+	 * We should have a sufficiently clean "pipe" to determine
+	 * the size of the FIFOs. If there was a character lurking
+	 * in the receiver, then it really cannot do any harm. We
+	 * send as much characters as is reasonable and wait for the
+	 * the TX interrupt to be asserted, counting the characters
+	 * as we send them. Based on that count we know the FIFO size.
+	 */
+	count = 0;
+	while ((sio_getreg(com, com_iir) & IIR_RXRDY) == 0 && count < 1030) {
+		sio_setreg(com, com_data, 0);
 		/* XXX barrier */
+		count++;
+
+		limit = 30;
+		while ((sio_getreg(com, com_lsr) & LSR_TEMT) == 0 && --limit) {
+			if (delay)
+				DELAY(delay);
+		}
+		if (limit == 0) {
+			sio_setreg(com, com_ier, 0);
+			/* XXX barrier */
+			sio_setreg(com, com_mcr, com->reg_mcr);
+			/* XXX barrier */
+			return (EIO);
+		}
+	}
 
-		com->fifosize = 16;		/* Minimal FIFO size. */
-		/*
-		 * XXX count the number of characters we can send before
-		 * we have a receive interrupt. This tells us how large
-		 * the FIFOs are.
-		 */
+	sio_setreg(com, com_ier, 0);
+	/* XXX barrier */
+
+	error = siosetfifo(com, FCR_ENABLE | FCR_RX_MEDH);
+	if (error) {
+		sio_setreg(com, com_mcr, com->reg_mcr);
+		/* XXX barrier */
+		return (error);
 	}
+
+	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 >= 120 && count < 128)
+		com->fifosize = 128;		/* 16950 */
+	else
+		com->fifosize = 1;		/* XXX */
+
 	sio_setreg(com, com_mcr, com->reg_mcr);
 	/* XXX barrier */
+
+	device_printf(com->dev, "count=%d, FIFO size=%d\n", count,
+	    com->fifosize);
+	return (0);
 }
 
 static void
@@ -324,23 +439,21 @@
 			device_set_desc(com->dev, "16550 or compatible");
 			break;
 		case 32:
-			/*
-			 * XXX Should we check that features like automatic
-			 * flow control exist?
-			 */
+			/* XXX Should we check features? */
 			device_set_desc(com->dev, "16650 or compatible");
 			break;
 		case 64:
-			/*
-			 * XXX Should we check that features like automatic
-			 * flow control exist?
-			 */
+			/* 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,
-			    "16550 with non-standard FIFO");
+			    "Non-standard or broken UART with FIFO");
 			break;
 		}
 	} else {
@@ -467,6 +580,7 @@
 sioprobe(device_t dev)
 {
 	struct com_s	*com;
+	int		error;
 	u_int		flags;
 
 	while (sio_inited != 2)
@@ -505,18 +619,35 @@
 	 * are set correctly for the console.
 	 */
 	if (com->consdev == NULL) {
-		if (sioprobe1(com)) {
+		error = sioprobe1(com);
+		if (error) {
 			bus_release_resource(dev, com->addr_type,
 			    com->addr_rid, com->addr_res);
-			return (ENXIO);
+			return (error);
 		}
 
 		if (com->rclk == 0)
 			com->rclk = DEFAULT_RCLK;
+
+		sio_setreg(com, com_lcr, LCR_8BITS | LCR_DLAB);
+		/* XXX barrier */
+		com->reg_dl = siodivisor(com->rclk, comdefaultrate);
+		sio_setdreg(com, com_dl, com->reg_dl);
+		/* XXX barrier */
+		sio_setreg(com, com_lcr, LCR_8BITS);
+		/* XXX barrier */
+		com->reg_mcr = MCR_IENABLE;
+		sio_setreg(com, com_mcr, com->reg_mcr);
+		/* XXX barrier */
 	}
 
 	/* Initialize the FIFOs. */
-	sioinitfifo(com);
+	error = sioinitfifo(com);
+	if (error) {
+		bus_release_resource(dev, com->addr_type, com->addr_rid,
+		    com->addr_res);
+		return (error);
+	}
 
 	if (device_get_desc(dev) == NULL)
 		siodescribe(com);
@@ -532,7 +663,8 @@
 		return (ENXIO);
 	}
 
-	bus_release_resource(dev, com->addr_type, com->addr_rid, com->addr_res);
+	bus_release_resource(dev, com->addr_type, com->addr_rid,
+	    com->addr_res);
 	return (0);
 }
 
@@ -557,22 +689,7 @@
 	if (unit >= sio_numunits)
 		sio_numunits = unit + 1;
 
-	/*
-	 * Preset the UART to something reasonable. Only do this when the
-	 * UART is not used as the console. We assume the console works
-	 * and whatever the setting, it cannot be unreasonable.
-	 */
-	if (com->consdev == NULL) {
-		sio_setreg(com, com_lcr, LCR_8BITS | LCR_DLAB);
-		/* XXX barrier */
-		com->reg_dl = siodivisor(com->rclk, comdefaultrate);
-		sio_setdreg(com, com_dl, com->reg_dl);
-		/* XXX barrier */
-		sio_setreg(com, com_lcr, LCR_8BITS);
-		/* XXX barrier */
-		sio_setreg(com, com_mcr, MCR_IENABLE);
-		/* XXX barrier */
-	} else
+	if (com->consdev != NULL)
 		com->consdev->cn_dev = makedev(CDEV_MAJOR, unit);
 
 	com->unit = unit;
@@ -1039,8 +1156,7 @@
 	s = spltty();
 	if (com->state & CS_BUSY)
 		com->extra_state &= ~CSE_BUSYCHECK;	/* False alarm. */
-	else if ((sio_getreg(com, com_lsr) & (LSR_TSRE | LSR_TXRDY))
-	    == (LSR_TSRE | LSR_TXRDY)) {
+	else if (sio_getreg(com, com_lsr) & LSR_TEMT) {
 		com->tp->t_state &= ~TS_BUSY;
 		ttwwakeup(com->tp);
 		com->extra_state &= ~CSE_BUSYCHECK;
@@ -1364,7 +1480,7 @@
 		}
 
 		/* output queued and everything ready? */
-		if (line_status & LSR_TXRDY
+		if (line_status & LSR_THRE
 		    && com->state >= (CS_BUSY | CS_TTGO | CS_ODEVREADY)) {
 			ioptr = com->obufq.l_head;
 			if (com->hasfifo && com->unit != siotsunit) {

==== //depot/projects/ia64/sys/dev/sio/sio_cons.c#9 (text+ko) ====

@@ -210,7 +210,7 @@
 
 	s = spltty();
 	to = 100000;
-	while ((sio_getreg(&sio_console, com_lsr) & LSR_TSRE) == 0 && --to)
+	while ((sio_getreg(&sio_console, com_lsr) & LSR_THRE) == 0 && --to)
 		;
 	sio_setreg(&sio_console, com_data, c);
 	splx(s);

==== //depot/projects/ia64/sys/dev/sio/sioreg.h#6 (text+ko) ====

@@ -79,7 +79,7 @@
 #define	FCR_RX_MEDH	0x80
 #define	FCR_RX_HIGH	0xc0
 
-/* character format control register */
+/* line control register */
 #define	LCR_DLAB	0x80
 #define	LCR_SBREAK	0x40
 #define	LCR_PZERO	0x30
@@ -102,8 +102,8 @@
 
 /* line status register */
 #define	LSR_RCV_FIFO	0x80
-#define	LSR_TSRE	0x40
-#define	LSR_TXRDY	0x20
+#define	LSR_TEMT	0x40	/* Transmitter Empty. */
+#define	LSR_THRE	0x20	/* Transmitter Holding Register Empty. */
 #define	LSR_BI		0x10
 #define	LSR_FE		0x08
 #define	LSR_PE		0x04

To Unsubscribe: send mail to majordomo@FreeBSD.org
with "unsubscribe p4-projects" in the body of the message




Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?200212250622.gBP6MuMo074496>