Subject: Re: COM driver and the ST16C1550
To: None <tech-kern@netbsd.org>
From: Allen Briggs <briggs@wasabisystems.com>
List: tech-kern
Date: 08/29/2001 22:28:12
I sent this a month and a half back or so...

> Hi.  I've been working with the ST16C1550 UART from EXAR.  It is
> supposedly 16550-compatible, but it doesn't quite seem to be.  When
> operating in interrupt mode, it gets stuck in the receive interrupt
> loop because the only interrupt condition is TXRDY.  The following
> patch seems to work for me, but I don't know if it will break existing
> drivers.  Unfortunately, I don't know any definite way to distinguish
> this from a genuine ns16550a...

I've gotten a bit further on the diff, and I exchanged some email with
Onno van der Linden back then.  Here's one of his private responses
(with his permission):

========================================================================
[ I wrote ]
> > I was trying to preserve the current behavior as much as
> > possible--handling everything else, then reloading the xmit.
> > This seems to make the most sense for the heldchange processing.

[ Onno replied ]
> You need the tx intrhandling inside the loop, but it should be
> called once and it should be readable code. What I would do is
> - add an extra txintronce variable initialized with 0
> - create an inline txintr function (or a huge define) that contains
>   everything handled by and including the if (ISSET(lsr, LSR_TXRDY))
>   statement
> - at the end of the loop add (right before the while)
>   if (!txintronce) {
>        txtintronce = 1;
>        txintr();
>   }
> 
> If we don't need the txintronce variable we could
> - create an inline txintr function (or a huge define) that contains
>   everything inside of the if (ISSET(lsr, LSR_TXRDY)) statement
> - at the the end of the loop add (right before the while)
>   if (ISSET(lsr, LSR_TXRDY))
>      txintr();
> 
> I don't think we really need the txintronce construction.
> 
> And while we're there, why not create a couple more inline functions
> (rxintr, msrintr) that are called from within comintr ?
========================================================================

I didn't see anything different in the chip setup to explain the
behavior that I was seeing.  The following diff does work for me...
Does anyone have any thoughts?

-allen

Index: com.c
===================================================================
RCS file: /cvsroot/syssrc/sys/dev/ic/com.c,v
retrieving revision 1.188
diff -u -r1.188 com.c
--- com.c	2001/08/27 14:27:01	1.188
+++ com.c	2001/08/30 02:28:34
@@ -2036,7 +2036,7 @@
 	put = sc->sc_rbput;
 	cc = sc->sc_rbavail;
 
-	do {
+again:	do {
 		u_char	msr, delta;
 
 		lsr = bus_space_read_1(iot, ioh, com_lsr);
@@ -2114,7 +2114,6 @@
 				bus_space_write_1(iot, ioh, com_ier, 0);
 				delay(10);
 				bus_space_write_1(iot, ioh, com_ier,sc->sc_ier);
-				iir = IIR_NOPEND;
 				continue;
 			}
 		}
@@ -2187,7 +2186,8 @@
 
 			sc->sc_st_check = 1;
 		}
-	} while (!ISSET((iir = bus_space_read_1(iot, ioh, com_iir)), IIR_NOPEND));
+	} while (ISSET((iir = bus_space_read_1(iot, ioh, com_iir)), IIR_RXRDY)
+	    || ((iir & IIR_IMASK) == 0));
 
 	/*
 	 * Done handling any receive interrupts. See if data can be
@@ -2228,6 +2228,10 @@
 			}
 		}
 	}
+
+	if (!ISSET((iir = bus_space_read_1(iot, ioh, com_iir)), IIR_NOPEND))
+		goto again;
+
 	COM_UNLOCK(sc);
 
 	/* Wake up the poller. */