Subject: kern/3844: Support for automatic hardware flow control on 16650 UARTs
To: None <gnats-bugs@gnats.netbsd.org>
From: Dave Huang <khym@bga.com>
List: netbsd-bugs
Date: 07/08/1997 18:07:14
>Number:         3844
>Category:       kern
>Synopsis:       Support for automatic hardware flow control on 16650 UARTs
>Confidential:   no
>Severity:       non-critical
>Priority:       low
>Responsible:    kern-bug-people (Kernel Bug People)
>State:          open
>Class:          change-request
>Submitter-Id:   net
>Arrival-Date:   Tue Jul  8 16:20:01 1997
>Last-Modified:
>Originator:     Dave Huang
>Organization:
Name: Dave Huang     |   Mammal, mammal / their names are called /
INet: khym@bga.com   |   they raise a paw / the bat, the cat /
FurryMUCK: Dahan     |   dolphin and dog / koala bear and hog -- TMBG
Dahan: Hani G Y+C 21 Y++ L+++ W- C++ T++ A+ E+ S++ V++ F- Q+++ P+ B+ PA+ PL++
>Release:        NetBSD-current as of July 4, 1997
>Environment:
	
System: NetBSD dahan.metonymy.com 1.2G NetBSD 1.2G (SPIFF) #58: Sat Jul 5 05:00:30 CDT 1997 khym@dahan.metonymy.com:/usr/src.local/sys/arch/i386/compile/SPIFF i386


>Description:
I recently bought one of these Turbo 4COM boards from
www.byterunner.com, a 4-port card with 16650 UARTs on it. It's got a
32-byte FIFO, vs. the 16-byte one on the 16550, so I was able to run
at 115200 baud with about the same amount of overruns as I had been
getting at 57600 baud with my 16550 card.

Not bad, but a nifty feature of the 16650 is that it has automatic
flow control; if the FIFO gets too full, it can automatically lower
RTS until the FIFO empties out some. And if the other end lowers CTS,
it can automatically stop sending.

So, here's a quick patch to detect a 16650 and enable automatic
hardware flow control if CRTSCTS is set (the 16650 can also do
automatic XON/XOFF flow control, but I didn't bother with that).

The copyright/license stuff on st16650reg.h probably needs to be fixed
up some. The file's just a copy of ns16550reg.h with a couple of lines
added, so I copied ns16550reg.h's license.

With this patch, I can run two simultaneous 115200 baud null modem
PPP connections, with data flowing both ways, and not get any overruns
at all. And this is on my wimpy 4MB 25MHZ 80386. I can even flood ping
it with big packets and not get overruns :) (the throughput does drop
a lot, but it's much better than if it were randomly losing
characters). I'm sure it could handle even more ports without losing
characters, but I don't have enough computers around to test it out...

>How-To-Repeat:

>Fix:
diff -uP /usr/src/sys/dev/ic/st16650reg.h ./ic/st16650reg.h
--- /usr/src/sys/dev/ic/st16650reg.h	Wed Dec 31 18:00:00 1969
+++ ./ic/st16650reg.h	Tue Jul  8 04:43:00 1997
@@ -0,0 +1,56 @@
+/*-
+ * Copyright (c) 1991 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *	This product includes software developed by the University of
+ *	California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *	@(#)ns16550.h	7.1 (Berkeley) 5/9/91
+ */
+
+/*
+ * ST16650A UART registers
+ */
+
+#define	com_data	0	/* data register (R/W) */
+#define	com_dlbl	0	/* divisor latch low (W) */
+#define	com_dlbh	1	/* divisor latch high (W) */
+#define	com_ier		1	/* interrupt enable (W) */
+#define	com_iir		2	/* interrupt identification (R) */
+#define	com_fifo	2	/* FIFO control (W) */
+#define	com_efr		2	/* Enhanced feature register (R/W) */
+#define	com_lctl	3	/* line control register (R/W) */
+#define	com_cfcr	3	/* line control register (R/W) */
+#define	com_mcr		4	/* modem control register (R/W) */
+#define	com_xon1	4	/* XON 1 character (R/W) */
+#define	com_lsr		5	/* line status register (R/W) */
+#define	com_xon2	5	/* XON 2 character (R/W) */
+#define	com_msr		6	/* modem status register (R/W) */
+#define	com_xoff1	6	/* XOFF 1 character (R/W) */
+#define	com_scratch	7	/* scratch register (R/W) */
+#define	com_xoff2	7	/* XOFF 2 character (R/W) */
diff -uP /usr/src/sys/dev/isa/com.c ./isa/com.c
--- /usr/src/sys/dev/isa/com.c	Sun Jul  6 06:20:58 1997
+++ ./isa/com.c	Tue Jul  8 04:51:59 1997
@@ -70,6 +70,7 @@
 
 /*
  * COM driver, uses National Semiconductor NS16450/NS16550AF UART
+ * Supports automatic hardware flow control on StarTech ST16C650A UART
  */
 #include <sys/param.h>
 #include <sys/systm.h>
@@ -91,7 +92,7 @@
 
 #include <dev/isa/comreg.h>
 #include <dev/isa/comvar.h>
-#include <dev/ic/ns16550reg.h>
+#include <dev/ic/st16650reg.h>
 #ifdef COM_HAYESP
 #include <dev/ic/hayespreg.h>
 #endif
@@ -393,6 +394,7 @@
 	int	hayesp_ports[] = { 0x140, 0x180, 0x280, 0x300, 0 };
 	int	*hayespp;
 #endif
+	u_char  iir;
 
 	if (iobase == comconsaddr) {
 		comconsattached = 1;
@@ -455,8 +457,25 @@
 		if (ISSET(bus_space_read_1(iot, ioh, com_fifo), FIFO_TRIGGER_14)
 		    == FIFO_TRIGGER_14) {
 			SET(sc->sc_hwflags, COM_HW_FIFO);
-			printf(": ns16550a, working fifo\n");
-			sc->sc_fifolen = 16;
+
+			/*
+			 * Okay, we have at least a 16550, check to
+			 * see if it's a 16650. Save the IIR, then
+			 * write a magic value into LCR and see if the
+			 * IIR changes into the EFR.
+			 */
+			iir = bus_space_read_1(iot, ioh, com_iir);
+			bus_space_write_1(iot, ioh, com_lcr, LCR_EERS);
+			if (bus_space_read_1(iot, ioh, com_iir) != iir) {
+				SET(sc->sc_hwflags, COM_HW_FLOW);
+				printf(": st16650a, working fifo\n");
+				sc->sc_fifolen = 32;
+			} else {
+				printf(": ns16550a, working fifo\n");
+				sc->sc_fifolen = 16;
+			}
+
+			bus_space_write_1(iot, ioh, com_lcr, LCR_8BITS);
 		} else
 			printf(": ns16550, broken fifo\n");
 	else
@@ -910,6 +929,7 @@
 		sc->sc_msr_cts = MSR_CTS;
 		sc->sc_r_hiwat = RXHIWAT;
 		sc->sc_r_lowat = RXLOWAT;
+		sc->sc_efr = EFR_AUTORTS | EFR_AUTOCTS;
 	} else if (ISSET(t->c_cflag, MDMBUF)) {
 		/*
 		 * For DTR/DCD flow control, make sure we don't toggle DTR for
@@ -920,6 +940,7 @@
 		sc->sc_msr_cts = MSR_DCD;
 		sc->sc_r_hiwat = RXHIWAT;
 		sc->sc_r_lowat = RXLOWAT;
+		sc->sc_efr = 0;
 	} else {
 		/*
 		 * If no flow control, then always set RTS.  This will make
@@ -931,6 +952,7 @@
 		sc->sc_msr_cts = 0;
 		sc->sc_r_hiwat = 0;
 		sc->sc_r_lowat = 0;
+		sc->sc_efr = 0;
 		if (ISSET(sc->sc_mcr, MCR_DTR))
 			SET(sc->sc_mcr, MCR_RTS);
 		else
@@ -1042,6 +1064,10 @@
 
 	bus_space_write_1(iot, ioh, com_ier, 0);
 
+	if (ISSET(sc->sc_hwflags, COM_HW_FLOW)) {
+		bus_space_write_1(iot, ioh, com_lcr, LCR_EERS);
+		bus_space_write_1(iot, ioh, com_efr, sc->sc_efr);
+	}
 	bus_space_write_1(iot, ioh, com_lcr, sc->sc_lcr | LCR_DLAB);
 	bus_space_write_1(iot, ioh, com_dlbl, sc->sc_dlbl);
 	bus_space_write_1(iot, ioh, com_dlbh, sc->sc_dlbh);
@@ -1694,6 +1720,8 @@
 	int rate;
 {
 
+	bus_space_write_1(iot, ioh, com_lcr, LCR_EERS);
+	bus_space_write_1(iot, ioh, com_efr, 0);
 	bus_space_write_1(iot, ioh, com_lcr, LCR_DLAB);
 	rate = comspeed(rate);
 	bus_space_write_1(iot, ioh, com_dlbl, rate);
diff -uP /usr/src/sys/dev/isa/comreg.h ./isa/comreg.h
--- /usr/src/sys/dev/isa/comreg.h	Mon Oct 14 11:35:11 1996
+++ ./isa/comreg.h	Tue Jul  8 04:45:26 1997
@@ -45,6 +45,8 @@
 #define	IER_ETXRDY	0x2	/* Enable transmitter empty interrupt */
 #define	IER_ERLS	0x4	/* Enable line status interrupt */
 #define	IER_EMSC	0x8	/* Enable modem status interrupt */
+#define	IER_ERTS	0x40	/* Enable RTS interrupt */
+#define	IER_ECTS	0x80	/* Enable CTS interrupt */
 
 /* interrupt identification register */
 #define	IIR_IMASK	0xf
@@ -66,7 +68,22 @@
 #define	FIFO_TRIGGER_8	0x80	/* ibid 8 */
 #define	FIFO_TRIGGER_14	0xc0	/* ibid 14 */
 
+/* enhanced feature register */
+#define	EFR_AUTOCTS	0x80	/* Automatic CTS flow control */
+#define	EFR_AUTORTS	0x40	/* Automatic RTS flow control */
+#define	EFR_SPECIAL	0x20	/* Special char detect */
+#define	EFR_EFCR	0x10	/* Enhanced function control bit */
+#define	EFR_TXFLOWBOTH	0x0c	/* Automatic transmit XON/XOFF 1 and 2 */
+#define	EFR_TXFLOW1	0x08	/* Automatic transmit XON/XOFF 1 */
+#define	EFR_TXFLOW2	0x04	/* Automatic transmit XON/XOFF 2 */
+#define	EFR_TXFLOWNONE	0x00	/* No automatic XON/XOFF transmit */
+#define	EFR_RXFLOWBOTH	0x03	/* Automatic receive XON/XOFF 1 and 2 */
+#define	EFR_RXFLOW1	0x02	/* Automatic receive XON/XOFF 1 */
+#define	EFR_RXFLOW2	0x01	/* Automatic receive XON/XOFF 2 */
+#define	EFR_RXFLOWNONE	0x00	/* No automatic XON/XOFF receive */
+
 /* line control register */
+#define	LCR_EERS	0xBF	/* Enable access to Enhanced Register Set */
 #define	LCR_DLAB	0x80	/* Divisor latch access enable */
 #define	LCR_SBREAK	0x40	/* Break Control */
 #define	LCR_PZERO	0x38	/* Space parity */
diff -uP /usr/src/sys/dev/isa/comvar.h ./isa/comvar.h
--- /usr/src/sys/dev/isa/comvar.h	Sun Jul  6 06:20:59 1997
+++ ./isa/comvar.h	Tue Jul  8 04:47:16 1997
@@ -40,6 +40,7 @@
 #define	COM_HW_NOIEN	0x01
 #define	COM_HW_FIFO	0x02
 #define	COM_HW_HAYESP	0x04
+#define	COM_HW_FLOW	0x08
 #define	COM_HW_CONSOLE	0x40
 #define	COM_HW_KGDB	0x80
 
@@ -70,7 +71,7 @@
 	int sc_fifolen;
 
 	u_char sc_msr, sc_msr_delta, sc_msr_mask, sc_mcr, sc_mcr_active, sc_lcr,
-	       sc_ier, sc_fifo, sc_dlbl, sc_dlbh;
+	       sc_ier, sc_fifo, sc_dlbl, sc_dlbh, sc_efr;
 	u_char sc_mcr_dtr, sc_mcr_rts, sc_msr_cts, sc_msr_dcd;
 
 	int sc_r_hiwat;

>Audit-Trail:
>Unformatted: