Subject: changes to com.c to support AST 4-port cards and soft carrier.
To: None <current-users@sun-lamp.cs.berkeley.edu>
From: Bill Sommerfeld <sommerfeld@orchard.medford.ma.us>
List: current-users
Date: 03/09/1994 22:04:00
The enclosed diffs implent two changes, and are relative to

$Id: com.c,v 1.21 1994/03/08 08:12:56 mycroft Exp $

I'll start off by saying that Charles's recent rewrite work on this
driver -- especially the new handling of sc->sc_mcr -- made this
change much cleaner to implement.  Thanks for doing this.

	1) AST 4-port card support.

	These cards have four 16450's or 16550's mapped in at four
consecutive blocks of I/O ports, all sharing an interrupt.  I got the
one I currently have for about $20 at a computer show; it came with
four socketed 16450's, which I replaced with 16550's before I ever
plugged it in to my system.

	The card can optionally be set to have the first two ports at
IO_COM1 and IO_COM2 and the remaining two ports sharing a third
interrupt; I have not tested this configuration and it might not work.

	The interrupt lines from the individual UARTs are connected up
to the "scratch register" of the UART at the highest address of the
four; the (active low) individual interrupt signals show up in the low
order four bits of the scratch register.

	This is handled in the driver by calling the "regular" comintr
routine once for each cleared bit; because ISA interrupts are
edge-triggered, the handler must loop until all four interrupt lines
are clear before returning.  This code has been tested with two of the
four ports running at once doing file transfers without any lockups.

	The individual UARTs must have MCR_IENABLE turned *off*, not
on, otherwise they don't interrupt.

	2) soft carrier: since I needed to set the COM_MULTI bit in
sc->sc_flags based on isa_dev->id_flags (and I have a few devices
which either need soft carrier or new RS232 cables..), I also let you
set COM_SOFTCAR (but not COM_FIFO) the same way as you set COM_MULTI.

Here's a config file fragment showing how to set it up; the four UARTs
*must* appear as four consecutive devices in the config file (I could
have removed this restriction by adding more fields to each entry in
com_softc, but it didn't seem to be worth it..).

device		com2	at isa? port 0x1a0 tty irq 5 flags 4 vector commintr 
device		com3	at isa? port 0x1a8 tty flags 4
device		com4	at isa? port 0x1b0 tty flags 4 
device		com5	at isa? port 0x1b8 tty flags 4 

"flags 4" should be changed to "flags 5" if you want soft carrier
turned on.


				- Bill

diff -c -r1.1.1.4 com.c
*** 1.1.1.4	1994/03/08 16:00:02
--- com.c	1994/03/10 02:33:19
***************
*** 70,76 ****
--- 70,78 ----
  	u_char sc_flags;
  #define	COM_SOFTCAR	0x01
  #define	COM_FIFO	0x02
+ #define COM_MULTI	0x04	/* multi-port card sharing interrupt */
  	u_char sc_msr, sc_mcr;
+ 	u_char sc_master;	/* minor device number of "master" device. */
  } com_softc[NCOM];
  /* XXXX should be in com_softc, but not ready for that yet */
  struct	tty *com_tty[NCOM];
***************
*** 156,161 ****
--- 158,169 ----
  	struct com_softc *sc = &com_softc[isa_dev->id_unit];
  	u_short iobase = isa_dev->id_iobase;
  
+ 	if (isa_dev->id_flags & COM_MULTI) {
+ 		outb (iobase | 0x1f, 0x80);
+ 		/* XXX Is this needed?  Ted's driver for Linux does it.  */
+ 		(void) inb (iobase | 0x1f);
+ 	}
+ 
  	/* XXX HACK */
  	sprintf(sc->sc_dev.dv_xname, "%s%d", comdriver.name, isa_dev->id_unit);
  	sc->sc_dev.dv_unit = isa_dev->id_unit;
***************
*** 179,188 ****
  		delay(1000);
  
  	sc->sc_iobase = iobase;
! 	sc->sc_flags = 0;
  
  	printf("%s: ", sc->sc_dev.dv_xname);
  
  	/* look for a NS 16550AF UART with FIFOs */
  	outb(iobase + com_fifo,
  	    FIFO_ENABLE | FIFO_RCV_RST | FIFO_XMT_RST | FIFO_TRIGGER_14);
--- 187,207 ----
  		delay(1000);
  
  	sc->sc_iobase = iobase;
! 	sc->sc_flags = isa_dev->id_flags & (COM_SOFTCAR|COM_MULTI);
  
  	printf("%s: ", sc->sc_dev.dv_xname);
  
+ 	if (sc->sc_flags & COM_MULTI) {
+ 		int miobase = iobase & ~0x1f;
+ 		int i;
+ 		printf("multiport ");
+ 		for (i=0; i<=unit; i++) {
+ 			if (com_softc[i].sc_iobase == miobase) {
+ 				sc->sc_master = i;
+ 				printf("0x%x ", miobase);
+ 			}
+ 		}
+ 	}
  	/* look for a NS 16550AF UART with FIFOs */
  	outb(iobase + com_fifo,
  	    FIFO_ENABLE | FIFO_RCV_RST | FIFO_XMT_RST | FIFO_TRIGGER_14);
***************
*** 243,248 ****
--- 262,268 ----
  	struct tty *tp;
  	int s;
  	int error = 0;
+ 	int enable = MCR_IENABLE;
   
  	if (unit > NCOM)
  		return ENXIO;
***************
*** 279,287 ****
  			    FIFO_TRIGGER_8);
  		(void) inb(iobase + com_lsr);
  		(void) inb(iobase + com_data);
  		/* you turn me on, baby */
  		outb(iobase + com_mcr,
! 		    sc->sc_mcr = MCR_DTR | MCR_RTS | MCR_IENABLE);
  		outb(iobase + com_ier,
  		    IER_ERXRDY | IER_ETXRDY | IER_ERLS | IER_EMSC);
  
--- 299,308 ----
  			    FIFO_TRIGGER_8);
  		(void) inb(iobase + com_lsr);
  		(void) inb(iobase + com_data);
+ 		enable = (sc->sc_flags & COM_MULTI) ? 0 : MCR_IENABLE;
  		/* you turn me on, baby */
  		outb(iobase + com_mcr,
! 		    sc->sc_mcr = MCR_DTR | MCR_RTS | enable);
  		outb(iobase + com_ier,
  		    IER_ERXRDY | IER_ETXRDY | IER_ERLS | IER_EMSC);
  
***************
*** 649,654 ****
--- 670,676 ----
  	}
  }
  
+ 
  int
  comintr(unit)
  	int unit;
***************
*** 694,699 ****
--- 716,723 ----
  		} else if (code == IIR_MLSC) {
  			commint(sc);
  		} else {
+ 			printf("%s: weird interrupt: iir=0x%02x\n",
+ 			    sc->sc_dev.dv_xname, code);
  			log(LOG_WARNING, "%s: weird interrupt: iir=0x%02x\n",
  			    sc->sc_dev.dv_xname, code);
  		}
***************
*** 703,708 ****
--- 727,756 ----
  	}
  }
  
+ /*
+  * AST multi-port card interrupt multiplexor.
+  */
+ 
+ int commintr (unit)
+ {
+ 	int flags;
+ 	struct com_softc *sc = &com_softc[unit];
+ 	u_short iobase = sc->sc_iobase;
+ 	if (!(sc->sc_flags & COM_MULTI)) {
+ 		/* oops, we missed */
+ 		comintr(unit);
+ 	} else {
+ 		unit = sc->sc_master;
+ 		sc = &com_softc[unit];
+ 		iobase = sc->sc_iobase;
+ 		do {
+ 			flags = inb (iobase | 0x1f) & 0xf;
+ #define CHECK(I)  ((flags & (1 << (I))) ? 0 : comintr (unit + (I)))
+ 			CHECK (0), CHECK (1), CHECK (2), CHECK (3);
+ #undef CHECK
+ 		} while (flags != 0xf);
+ 	}
+ }
  /*
   * Following are all routines needed for COM to act as console
   */

------------------------------------------------------------------------------