Subject: support for AST-style multi-port serial cards with nice config
To: None <current-users@sun-lamp.cs.berkeley.edu>
From: Roland McGrath <roland@frob.com>
List: current-users
Date: 03/21/1994 18:42:00
Inspired by Bill Sommerfeld's hacks to support the multi-port serial
cards (really, his code was better documentation than the poor
translation that came with my hardware, so I could finally figure out
what to do), I hacked this further to support them with nice
configuration.
For example, here is the configuration I am using:
master ast0 at isa? port 0x1a0 tty irq 5 vector astintr
device com2 at ast0 flags 2 slave 0
device com3 at ast0 flags 2 slave 1
device com4 at ast0 flags 2 slave 2
device com5 at ast0 flags 2 slave 3
This says that com2 uses ports 0x1a0-0x1a7, com3 uses ports
0x1a8-0x1af, etc.; com2 uses the low-order bit in the interrupt
demultiplexing byte, com3 uses the 2 bit, com4 uses the 4 bit, and
com5 uses the 8 bit.
You can also put `port' specifiers on the individual device lines
(according to its documentation, my card supports some modes where all
the ports are not all at consecutive io addresses--I haven't used
them). You must still put in the slave ID, so it knows which
interrupt bit to check. For each device that has no io port
specified, it will use BASE+(8*SLAVE), where BASE is the io port given
for the master device and SLAVE is the slave ID number.
An unrelated kludge you can omit, `flags 2' sets CLOCAL for those ports.
Here comes the code. Firstly you want this hack to config(8). You
can do fine without it, you just have to use "drive" in place of
"slave" in the configuration, which sounds a little silly for a serial port.
diff -c /usr/src/usr.sbin/config/mkioconf.c /usr/src/usr.sbin/config/my-mkioconf.c
*** /usr/src/usr.sbin/config/mkioconf.c Sat Mar 12 06:10:41 1994
--- /usr/src/usr.sbin/config/my-mkioconf.c Sat Mar 19 04:09:04 1994
***************
*** 730,736 ****
fprintf(fp, " %5s, %2d, C 0x%05x, %5d, %5s, %2d, 0x%04x, %3d,",
sirq(dp->d_irq), dp->d_drq, dp->d_maddr, dp->d_msize,
shandler(dp), dp->d_unit, dp->d_flags,
! eq(mp->d_name, "isa") ? 0 : dp->d_drive);
if (eq(mp->d_name, "isa"))
fprintf(fp, " NULL,");
else
--- 730,737 ----
fprintf(fp, " %5s, %2d, C 0x%05x, %5d, %5s, %2d, 0x%04x, %3d,",
sirq(dp->d_irq), dp->d_drq, dp->d_maddr, dp->d_msize,
shandler(dp), dp->d_unit, dp->d_flags,
! eq(mp->d_name, "isa") ? 0 :
! dp->d_drive == UNKNOWN ? dp->d_slave : dp->d_drive);
if (eq(mp->d_name, "isa"))
fprintf(fp, " NULL,");
else
This tells config about the new `ast' device.
diff -c /sys/arch/i386/conf/files.i386.\~1\~ /sys/arch/i386/conf/files.i386
*** /sys/arch/i386/conf/files.i386.~1~ Sat Mar 12 06:04:48 1994
--- /sys/arch/i386/conf/files.i386 Mon Mar 21 16:34:45 1994
***************
*** 23,28 ****
--- 23,29 ----
arch/i386/isa/bt742a.c optional bt device-driver requires isa scsi not aha
arch/i386/isa/clock.c optional isa
arch/i386/isa/com.c optional com device-driver requires isa
+ arch/i386/isa/ast.c optional ast device-driver requires com isa
arch/i386/isa/cy.c optional cy device-driver requires isa
arch/i386/isa/elink.c optional ep or ie
arch/i386/isa/fd.c optional fd device-driver requires isa
These are the necessary changes to com.c. The ability to set CLOCAL
with `flags 2' is in here as well, and not really related to the
multiport support (but we all need it, so what the hell).
diff -c /sys/arch/i386/isa/com.c.\~1\~ /sys/arch/i386/isa/com.c
*** /sys/arch/i386/isa/com.c.~1~ Fri Mar 18 05:56:36 1994
--- /sys/arch/i386/isa/com.c Mon Mar 21 17:54:39 1994
***************
*** 40,45 ****
--- 40,46 ----
* uses National Semiconductor NS16450/NS16550AF UART
*/
#include "com.h"
+ #include "ast.h"
#include <sys/param.h>
#include <sys/systm.h>
***************
*** 69,75 ****
u_short sc_iobase;
u_char sc_hwflags;
#define COM_HW_MULTI 0x01
- #define COM_HW_CONFIGBITS COM_HW_MULTI
#define COM_HW_FIFO 0x02
#define COM_HW_CONSOLE 0x40
u_char sc_swflags;
--- 70,75 ----
***************
*** 77,82 ****
--- 77,83 ----
#define COM_SW_CLOCAL 0x02
#define COM_SW_CRTSCTS 0x04
#define COM_SW_MDMBUF 0x08
+ #define COM_SW_CONFIGBITS COM_SW_CLOCAL
u_char sc_msr, sc_mcr;
} com_softc[NCOM];
/* XXXX should be in com_softc, but not ready for that yet */
***************
*** 163,168 ****
--- 164,182 ----
struct com_softc *sc = &com_softc[isa_dev->id_unit];
u_short iobase = isa_dev->id_iobase;
+ if (isa_dev->id_parent) {
+ if (iobase == 0) {
+ /* For multiport cards, the iobase may be left
+ unspecified (zero) for slave ports. In
+ that case we calculate it from the master
+ (parent) iobase setting and the slave port
+ number (physid). */
+ iobase = isa_dev->id_iobase
+ = isa_dev->id_parent->id_iobase +
+ (8 * isa_dev->id_physid);
+ }
+ }
+
/* 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;
***************
*** 186,195 ****
delay(1000);
sc->sc_iobase = iobase;
! sc->sc_hwflags = isa_dev->id_flags & COM_HW_CONFIGBITS;
! sc->sc_swflags = 0;
! printf("%s: ", sc->sc_dev.dv_xname);
/* look for a NS 16550AF UART with FIFOs */
outb(iobase + com_fifo,
--- 200,221 ----
delay(1000);
sc->sc_iobase = iobase;
! sc->sc_hwflags = 0;
! sc->sc_swflags = isa_dev->id_flags & COM_SW_CONFIGBITS;
! printf("%s", sc->sc_dev.dv_xname);
! #if NAST > 0
! if (isa_dev->id_parent) {
! printf(" at 0x%x %s%d slave %d",
! isa_dev->id_iobase,
! isa_dev->id_parent->id_driver->name,
! isa_dev->id_parent->id_unit,
! isa_dev->id_physid);
! astslave(isa_dev, unit);
! sc->sc_hwflags |= COM_HW_MULTI;
! }
! #endif
! printf(": ");
/* look for a NS 16550AF UART with FIFOs */
outb(iobase + com_fifo,
***************
*** 295,302 ****
(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);
--- 321,330 ----
(void) inb(iobase + com_lsr);
(void) inb(iobase + com_data);
/* you turn me on, baby */
! sc->sc_mcr = MCR_DTR | MCR_RTS;
! if (!(sc->sc_hwflags & COM_HW_MULTI))
! sc->sc_mcr |= MCR_IENABLE;
! outb(iobase + com_mcr, sc->sc_mcr);
outb(iobase + com_ier,
IER_ERXRDY | IER_ETXRDY | IER_ERLS | IER_EMSC);
And, finally, here is sys/arch/i386/isa/ast.c:
/* Multi-port serial card interrupt demuxing support.
Roland McGrath 3/20/94 */
#include "ast.h"
#include <sys/types.h>
#include <machine/pio.h>
#include <i386/isa/isa_device.h>
int astprobe __P((struct isa_device *));
int astattach __P((struct isa_device *));
struct isa_driver astdriver = {
astprobe, astattach, "ast"
};
struct astunit
{
u_short iobase;
int alive; /* Mask of slave units attached. */
int slaveunits[8]; /* com device unit numbers. */
} astunits[NAST];
int
astprobe(struct isa_device *isa_dev)
{
/* Do the normal com probe for the first UART and assume
its presence means there is a multiport board there. */
return comprobe1(isa_dev->id_iobase);
}
int
astattach(struct isa_device *isa_dev)
{
int unit = isa_dev->id_unit;
u_short iobase = isa_dev->id_iobase;
unsigned int x;
astunits[unit].iobase = iobase;
outb (iobase | 0x1f, 0x80);
/* XXX Is this needed? Ted's driver for Linux does it. */
x = inb (iobase | 0x1f);
/* My guess is this bitmask tells you how many ports are there.
I only have a 4-port board to try (returns 0xf). --roland */
printf ("ast%d: 0x%x\n", unit, x);
}
void
astslave(struct isa_device *slave, int comunit)
{
struct astunit *a = &astunits[slave->id_parent->id_unit];
a->slaveunits[slave->id_physid] = comunit;
a->alive |= 1 << slave->id_physid;
}
int
astintr(int unit)
{
struct astunit *a = &astunits[unit];
u_short iobase = a->iobase;
int alive = a->alive;
int bits;
do {
bits = inb (iobase | 0x1f) & alive;
#define TRY(I) ((bits & (1 << (I))) ? 0 : comintr (a->slaveunits[I]))
TRY (0), TRY (1), TRY (2), TRY (3);
TRY (4), TRY (5), TRY (6), TRY (7);
#undef TRY
} while (bits != alive);
return 1;
}
Enjoy,
Roland
------------------------------------------------------------------------------