Subject: DHU driver fixups
To: None <port-vax@netbsd.org>
From: Hugh Graham <hugh@openbsd.org>
List: port-vax
Date: 04/05/2003 20:16:16
I recently purchased a CXA16 card, found the dhu.c driver to be in
pretty bad condition, and figured you all might be interested in my
efforts to fix it up somewhat.

This diff is against OpenBSD's tree, so it will likely require some
minor tweaking to apply on NetBSD, but the basic effects are:

	Comment at line 58 is disproved by a CXY08 in DHU mode,
	so use the state of the "modem low" bit to make a better
	guess about how many lines we have. Will still be incorrect
	for a CXA16 in DHV mode, but without a "real" DHV11 to
	compare with I'll leave it alone.

	Transmit interrupt handler at line 300 was servicing at
	most one "action report" per interrupt and could get behind
	when under load. Eventually the device would stop generating
	transmit interrupts and deadlock, so take care to service
	all reports as soon as we can.

	Clause at line 381 enables the "recieve interrupt timer"
	available on true DHU models and sets it to 10ms. This allows
	roughly 50 characters to accumulate in the fifo per interrupt
	at 38400 baud, rather than 1:1, reducing the resource usage
	considerably.

	The check for DHV11 at line 630 seems to fix the biggest
	problem with the existing driver. It appears that the DHU11
	device doesn't have a TXCHAR register (it's the receive
	interrupt timer on this model), and the attempt to write there
	would deadlock the driver because no interrupt would result.
	The check just avoids doing single character output on DHU11.

With these fixes, I'm able to use my CXA16 in DHU mode and run looped
back transfers at 38400 baud on 8 lines (all the MMJ cables I have)
without data loss or performance degredation.

I hope it works as well for you.

/Hugh

PS: I'm interested in looking at the onboard "DHW" serial IO adaptors
for the MicroVAX class machines. If you have a spare card, back panel
connector, cables and any necessary peripherals for a VAX 4000 100
machine you can donate to me (in Canada), please get in touch.

Index: dhu.c
===================================================================
RCS file: /cvs/src/sys/arch/vax/qbus/dhu.c,v
retrieving revision 1.5
retrieving revision 1.9
diff -u -r1.5 -r1.9
--- dhu.c	2002/10/19 22:40:44	1.5
+++ dhu.c	2003/04/06 01:34:56	1.9
@@ -1,6 +1,7 @@
-/*	$OpenBSD: dhu.c,v 1.5 2002/10/19 22:40:44 hugh Exp $	*/
+/*	$OpenBSD: dhu.c,v 1.9 2003/04/06 01:34:56 hugh Exp $	*/
 /*	$NetBSD: dhu.c,v 1.19 2000/06/04 06:17:01 matt Exp $	*/
 /*
+ * Copyright (c) 2003, Hugh Graham.
  * Copyright (c) 1996  Ken C. Wellsch.  All rights reserved.
  * Copyright (c) 1992, 1993
  *	The Regents of the University of California.  All rights reserved.
@@ -56,8 +57,6 @@
 #include <arch/vax/qbus/ubavar.h>
 #include <arch/vax/qbus/dhureg.h>
 
-/* A DHU-11 has 16 ports while a DHV-11 has only 8. We use 16 by default */
-
 #define	NDHULINE 	16
 
 #define DHU_M2U(c)	((c)>>4)	/* convert minor(dev) to unit # */
@@ -68,6 +67,7 @@
 	struct	evcnt	sc_rintrcnt;	/* Interrupt statistics */
 	struct	evcnt	sc_tintrcnt;	/* Interrupt statistics */
 	int		sc_type;	/* controller type, DHU or DHV */
+	int		sc_lines;	/* number of lines */
 	bus_space_tag_t	sc_iot;
 	bus_space_handle_t sc_ioh;
 	bus_dma_tag_t	sc_dmat;
@@ -230,11 +230,21 @@
 	}
 
 	c = DHU_READ_WORD(DHU_UBA_STAT);
+
+	sc->sc_type = (c & DHU_STAT_DHU) ? IS_DHU : IS_DHV;
 
-	sc->sc_type = (c & DHU_STAT_DHU)? IS_DHU: IS_DHV;
-	printf("\n%s: DH%s-11\n", self->dv_xname, (c & DHU_STAT_DHU)?"U":"V");
+	if (sc->sc_type == IS_DHU) {
+		if (c & DHU_STAT_MDL)
+			sc->sc_lines = 16;	/* "Modem Low" */
+		else
+			sc->sc_lines = 8;	/* Has modem support */
+	} else
+		sc->sc_lines = 8;
 
-	for (i = 0; i < sc->sc_type; i++) {
+	printf("\n%s: DH%s-11 %d lines\n", self->dv_xname,
+	    (sc->sc_type == IS_DHU) ? "U" : "V", sc->sc_lines);
+
+	for (i = 0; i < sc->sc_lines; i++) {
 		struct tty *tp;
 		tp = sc->sc_dhu[i].dhu_tty = ttymalloc();
 		sc->sc_dhu[i].dhu_state = STATE_IDLE;
@@ -289,8 +299,8 @@
 				    (void)(*linesw[tp->t_line].l_modem)(tp, 1);
 			}
 			else if ((tp->t_state & TS_CARR_ON) &&
-				(*linesw[tp->t_line].l_modem)(tp, 0) == 0)
-					(void) dhumctl(sc, line, 0, DMSET);
+			    (*linesw[tp->t_line].l_modem)(tp, 0) == 0)
+				(void) dhumctl(sc, line, 0, DMSET);
 
 			/* Do CRTSCTS flow control */
 			delta = c ^ sc->sc_dhu[line].dhu_modem;
@@ -337,29 +347,41 @@
 {
 	struct	dhu_softc *sc = arg;
 	struct tty *tp;
-	int line;
+	int line, i;
 
-	line = DHU_LINE(DHU_READ_BYTE(DHU_UBA_CSR_HI));
+	while ((i = DHU_READ_BYTE(DHU_UBA_CSR_HI)) & (DHU_CSR_TX_ACTION >> 8)) {
 
-	tp = sc->sc_dhu[line].dhu_tty;
+		line = DHU_LINE(i);
 
-	tp->t_state &= ~TS_BUSY;
-	if (tp->t_state & TS_FLUSH)
-		tp->t_state &= ~TS_FLUSH;
-	else {
-		if (sc->sc_dhu[line].dhu_state == STATE_DMA_STOPPED)
-			sc->sc_dhu[line].dhu_cc -= 
-			DHU_READ_WORD(DHU_UBA_TBUFCNT);
-		ndflush(&tp->t_outq, sc->sc_dhu[line].dhu_cc);
-		sc->sc_dhu[line].dhu_cc = 0;
-	}
+		tp = sc->sc_dhu[line].dhu_tty;
 
-	sc->sc_dhu[line].dhu_state = STATE_IDLE;
+		if (i & (DHU_CSR_TX_DMA_ERROR >> 8))
+			printf("dhu%d: DMA ERROR on line: %d\n",
+			    DHU_M2U(minor(tp->t_dev)), line);
+
+		if (i & (DHU_CSR_DIAG_FAIL >> 8))
+			printf("dhu%d: DIAG FAIL on line: %d\n",
+			    DHU_M2U(minor(tp->t_dev)), line);
+
+		tp->t_state &= ~TS_BUSY;
+
+		if (tp->t_state & TS_FLUSH)
+			tp->t_state &= ~TS_FLUSH;
+		else {
+			if (sc->sc_dhu[line].dhu_state == STATE_DMA_STOPPED)
+				sc->sc_dhu[line].dhu_cc -= 
+				DHU_READ_WORD(DHU_UBA_TBUFCNT);
+			ndflush(&tp->t_outq, sc->sc_dhu[line].dhu_cc);
+			sc->sc_dhu[line].dhu_cc = 0;
+		}
 
-	if (tp->t_line)
-		(*linesw[tp->t_line].l_start)(tp);
-	else
-		dhustart(tp);
+		sc->sc_dhu[line].dhu_state = STATE_IDLE;
+
+		if (tp->t_line)
+			(*linesw[tp->t_line].l_start)(tp);
+		else
+			dhustart(tp);
+	}
 }
 
 int
@@ -381,9 +403,16 @@
 
 	sc = dhu_cd.cd_devs[unit];
 
-	if (line >= sc->sc_type)
+	if (line >= sc->sc_lines)
 		return ENXIO;
 
+	if (sc->sc_type == IS_DHU) {
+		s = spltty();		/* CSR 3:0 must be 0 */
+		DHU_WRITE_BYTE(DHU_UBA_CSR, DHU_CSR_RXIE);
+		DHU_WRITE_BYTE(DHU_UBA_RXTIME, 10);
+		splx(s);		/* RX int delay 10ms */
+	}
+
 	s = spltty();
 	DHU_WRITE_BYTE(DHU_UBA_CSR, DHU_CSR_RXIE | line);
 	sc->sc_dhu[line].dhu_modem = DHU_READ_WORD(DHU_UBA_STAT);
@@ -605,6 +634,7 @@
 	int s;
 
 	s = spltty();
+
 	if (tp->t_state & (TS_TIMEOUT|TS_BUSY|TS_TTSTOP))
 		goto out;
 	if (tp->t_outq.c_cc <= tp->t_lowat) {
@@ -630,15 +660,13 @@
 
 	sc->sc_dhu[line].dhu_cc = cc;
 
-	if (cc == 1) {
+	if (cc == 1 && sc->sc_type == IS_DHV) {
 
 		sc->sc_dhu[line].dhu_state = STATE_TX_ONE_CHAR;
 		
 		DHU_WRITE_WORD(DHU_UBA_TXCHAR, 
 		    DHU_TXCHAR_DATA_VALID | *tp->t_outq.c_cf);
-
 	} else {
-
 		sc->sc_dhu[line].dhu_state = STATE_DMA_RUNNING;
 
 		addr = sc->sc_dhu[line].dhu_dmah->dm_segs[0].ds_addr +
Index: dhureg.h
===================================================================
RCS file: /cvs/src/sys/arch/vax/qbus/dhureg.h,v
retrieving revision 1.1
retrieving revision 1.4
diff -u -r1.1 -r1.4
--- dhureg.h	2000/04/27 03:14:47	1.1
+++ dhureg.h	2003/04/06 01:33:32	1.4
@@ -1,4 +1,4 @@
-/*	$OpenBSD: dhureg.h,v 1.1 2000/04/27 03:14:47 bjc Exp $	*/
+/*	$OpenBSD: dhureg.h,v 1.4 2003/04/06 01:33:32 hugh Exp $	*/
 /*	$NetBSD: dhureg.h,v 1.4 1999/05/28 20:17:29 ragge Exp $	*/
 /*
  * Copyright (c) 1996  Ken C. Wellsch.  All rights reserved.
@@ -66,8 +66,10 @@
 #define	DHU_UBA_CSR_HI	1
 #define	DHU_UBA_RBUF	2
 #define	DHU_UBA_TXCHAR	2
+#define	DHU_UBA_RXTIME	DHU_UBA_TXCHAR	/* on a real dhu only */
 #define	DHU_UBA_LPR	4
 #define	DHU_UBA_STAT	6
+#define	DHU_UBA_FIFO	DHU_UBA_STAT	/* on a real dhu only */
 #define	DHU_UBA_LNCTRL	8
 #define	DHU_UBA_TBUFAD1	10
 #define	DHU_UBA_TBUFAD2	12
@@ -134,6 +136,7 @@
 #define DHU_STAT_RI		0020000
 #define DHU_STAT_DCD		0010000
 #define DHU_STAT_CTS		0004000
+#define DHU_STAT_MDL		0001000
 #define DHU_STAT_DHU		0000400
 
 /* LNCTRL bits */