Skip site navigation (1)Skip section navigation (2)
Date:      Sun, 23 Nov 1997 18:39:28 +1100
From:      Bruce Evans <bde@zeta.org.au>
To:        bde@zeta.org.au, mouth@ibm.net
Cc:        hackers@freebsd.org
Subject:   Re: Status of 650 UART support
Message-ID:  <199711230739.SAA19794@godzilla.zeta.org.au>

next in thread | raw e-mail | index | archive | help
>>I think you can actually ready bit 7 to see if there is a full fifo
>>(with no parity/framing/overrun errors) and, if it is set, read the
>>entire fifo before reading the LSR again.  This gives close to one I/O
>>per input byte.  It is a bit tricky to handle error cases and quitting
>>properly - we don't want to fall back to two I/O's per input byte.
>
>The Startech databook makes no mention of LSR bit 7 indicating a full
>=46IFO, only a dirty one.  Same for all the literature I have on the NS
>16550.

Oops.  It actually gives a summary of the errors in the fifo.  This is
necessary for avoiding reads of the LSR when the fifo is read all at
once.  The LSR must still be read before each byte if LSR bit 7 indicates
an error.

>Apparently you can use the FIFO trigger level to read a block of bytes
>without checking LSR bit 0 every time -- the Startech data book even
>suggests it.  To handle all cases, especially multiport shared
>interrupt cards where you don't know which UART generated the receiver
>interrupt, you must first check the IIR (Startech calls it the ISR --
>interrupt Status register) to see if the UART generated a received
>data interrupt.

sio attempts to minimise reads of the IIR, so fitting this in cleanly
is tricky.  OTOH, the read of the MSR can be avoided if the IIR can be
trusted (the current code is designed to avoid IIR bugs in 8250s and
16450s without much cost if the IIR actually works).  I was thinking of
an extra read of the IIR when I mentioned tricky error handling.  The
simplest approach leads to 3 i/o's per byte of input instead of 2
(IIR, LSR, RXDATA) instrad of (LSR, RXDATA).

>After emptying the block, you could leave any remaining characters in
>the FIFO, and simply get them on the next interrupt, avoiding the need
>of checking LSR bit 0 for bytes beyond the block size.

Here is some (low quality, old and unfinished) code for doing this.
It has some interface changes (void *vcom, and siointr1() called
directly from XintrN()) that won't work in -current.  The behaviour is
controlled by global variables sio_iir and sio_stream.  Setting sio_iir
gives more use of the IIR and setting sio_stream gives bulk processing
of the input fifo.  sio_iir is probably required if sio_stream is used
and is probably a pessimization otherwise.

Bruce

diff -c2 sio.c~ sio.c
*** sio.c~	Fri Nov 21 19:36:47 1997
--- sio.c	Fri Nov 21 19:36:49 1997
***************
*** 1407,1421 ****
  }
  
! void
! siointr(unit)
! 	int	unit;
  {
! #ifndef COM_MULTIPORT
! 	COM_LOCK();
! 	siointr1(com_addr(unit));
! 	COM_UNLOCK();
! #else /* COM_MULTIPORT */
! 	struct com_s    *com;
  	bool_t		possibly_more_intrs;
  
  	/*
--- 1423,1436 ----
  }
  
! #ifdef COM_MULTIPORT
! static void
! siointr(vcom)
! 	void	*vcom;
  {
! 	struct com_s	*com;
  	bool_t		possibly_more_intrs;
+ 	int		unit;
+ 
+ 	com = vcom;
  
  	/*
***************
*** 1440,1444 ****
  			       != IIR_NOPEND) {
  				siointr1(com);
! 				possibly_more_intrs = TRUE;
  			}
  			/* XXX COM_UNLOCK(); */
--- 1455,1460 ----
  			       != IIR_NOPEND) {
  				siointr1(com);
! 				if (!siointr1_looping)
! 					possibly_more_intrs = TRUE;
  			}
  			/* XXX COM_UNLOCK(); */
***************
*** 1446,1470 ****
  	} while (possibly_more_intrs);
  	COM_UNLOCK();
- #endif /* COM_MULTIPORT */
  }
  
  static void
! siointr1(com)
! 	struct com_s	*com;
  {
  	u_char	line_status;
  	u_char	modem_status;
  	u_char	*ioptr;
  	u_char	recv_data;
  
  	while (TRUE) {
  		line_status = inb(com->line_status_port);
  
  		/* input event? (check first to help avoid overruns) */
  		while (line_status & LSR_RCV_MASK) {
  			/* break/unnattached error bits or real input? */
  			if (!(line_status & LSR_RXRDY))
  				recv_data = 0;
  			else
  				recv_data = inb(com->data_port);
  			if (line_status & (LSR_BI | LSR_FE | LSR_PE)) {
--- 1462,1527 ----
  	} while (possibly_more_intrs);
  	COM_UNLOCK();
  }
+ #endif /* COM_MULTIPORT */
  
  static void
! siointr1(vcom)
! 	void	*vcom;
  {
+ 	struct com_s	*com;
+ 	u_char	iir;
  	u_char	line_status;
  	u_char	modem_status;
  	u_char	*ioptr;
  	u_char	recv_data;
+ 	int	streaming;
+ 
+ 	com = vcom;
  
+ 	/*
+ 	 * siointr1_looping is used to determine if siointr1() is looping
+ 	 * without doing anything.  If COM_MULTIPORT then assume we
+ 	 * would loop if nothing is done since we only run through
+ 	 * once and can not detect it ourselves.
+ 	 */
+ #ifdef COM_MULTIPORT
+ 	siointr1_looping = TRUE;
+ #else
+ 	siointr1_looping = FALSE;
+ #endif
+ 	if (sio_iir) {
+ 		iir = inb(com->int_id_port) & IIR_IMASK;
+ 		if (iir == IIR_RXTOUT)
+ 			goto rxrdy;
+ 		if (iir == IIR_RXRDY)
+ 			goto rxrdy;
+ 		if (iir == IIR_RLS)
+ 			goto rxrdy;
+ 		goto mlsc;
+ 	}
  	while (TRUE) {
+ rxrdy:
  		line_status = inb(com->line_status_port);
+ 		streaming = 0;
  
  		/* input event? (check first to help avoid overruns) */
  		while (line_status & LSR_RCV_MASK) {
+ 			int i;
+ 
+ 			for (i = 0; i < fifo_delay; ++i)
+ 				inb(com->data_port + 7);
+ 			siointr1_looping = FALSE;
+ 			if (sio_stream && streaming == 0 && com->hasfifo
+ 			    && !(line_status & LSR_RCV_FIFO)) {
+ 				if (!sio_iir)
+ 					iir = inb(com->int_id_port) & IIR_IMASK;
+ 				if (iir == IIR_RXRDY)
+ 					streaming = 14;
+ 			}
  			/* break/unnattached error bits or real input? */
  			if (!(line_status & LSR_RXRDY))
  				recv_data = 0;
  			else
+ more_rx:
  				recv_data = inb(com->data_port);
  			if (line_status & (LSR_BI | LSR_FE | LSR_PE)) {
***************
*** 1527,1530 ****
--- 1584,1591 ----
  			}
  cont:
+ 			if (streaming != 0 && --streaming != 0)
+ 				goto more_rx;
+ 			if (sio_iir)
+ 				goto loop;
  			/*
  			 * "& 0x7F" is to avoid the gcc-1.40 generating a slow
***************
*** 1534,1540 ****
--- 1595,1603 ----
  		}
  
+ mlsc:
  		/* modem status change? (always check before doing output) */
  		modem_status = inb(com->modem_status_port);
  		if (modem_status != com->last_modem_status) {
+ 			siointr1_looping = FALSE;
  			if (com->do_dcd_timestamp
  			    && !(com->last_modem_status & MSR_DCD)
***************
*** 1564,1570 ****
--- 1627,1635 ----
  		}
  
+ txrdy:
  		/* output queued and everything ready? */
  		if (line_status & LSR_TXRDY
  		    && com->state >= (CS_BUSY | CS_TTGO | CS_ODEVREADY)) {
+ 			siointr1_looping = FALSE;
  			ioptr = com->obufq.l_head;
  			if (com->tx_fifo_size > 1) {
***************
*** 1600,1613 ****
  					com_events += LOTS_OF_EVENTS;
  					com->state |= CS_ODONE;
! 					setsofttty();	/* handle at high level ASAP */
  				}
  			}
  		}
  
  		/* finished? */
  #ifndef COM_MULTIPORT
  		if ((inb(com->int_id_port) & IIR_IMASK) == IIR_NOPEND)
- #endif /* COM_MULTIPORT */
  			return;
  	}
  }
--- 1665,1706 ----
  					com_events += LOTS_OF_EVENTS;
  					com->state |= CS_ODONE;
! 
! 					/* handle at high level ASAP */
! 					setsofttty();
  				}
  			}
  		}
  
+ loop:
+ 		if (sio_iir) {
+ 			iir = inb(com->int_id_port) & IIR_IMASK;
+ 			if (iir == IIR_NOPEND)
+ 				return;
+ 			if (iir == IIR_RXTOUT)
+ 				goto rxrdy;
+ 			if (iir == IIR_RXRDY)
+ 				goto rxrdy;
+ 			if (iir == IIR_RLS)
+ 				goto rxrdy;
+ 			if (iir == IIR_MLSC)
+ 				goto mlsc;
+ 			if (iir == IIR_TXRDY)
+ 				goto txrdy;
+ 		}
+ 
  		/* finished? */
  #ifndef COM_MULTIPORT
  		if ((inb(com->int_id_port) & IIR_IMASK) == IIR_NOPEND)
  			return;
+ 		/*
+ 		 * If the device says we should be doing something but we
+ 		 * aren't, exit or else we may never return.
+ 		 */
+ 		if (sio_loop_test && siointr1_looping)
+ 			return;
+ 		siointr1_looping = TRUE;
+ #else
+ 		return;
+ #endif
  	}
  }



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