diff options
author | bde <bde@FreeBSD.org> | 1994-10-12 19:49:11 +0000 |
---|---|---|
committer | bde <bde@FreeBSD.org> | 1994-10-12 19:49:11 +0000 |
commit | e42ffcf4bb6b8f00024f8055149b90cb31ae067d (patch) | |
tree | 43e219de40bc14c3ea655fa060f9a4e681e69ece | |
parent | 0c7d9bd3651934d2eafda618212920dbf8e4c46b (diff) | |
download | FreeBSD-src-e42ffcf4bb6b8f00024f8055149b90cb31ae067d.zip FreeBSD-src-e42ffcf4bb6b8f00024f8055149b90cb31ae067d.tar.gz |
Work around FIFO bug in SMC UARTS.
Fix endless loop in siopoll() for an event on a tty with no tty struct.
Don't generate unwanted interrupts in the serial console driver. These
bugs probably don't matter unless the tty struct is dynamically allocated.
Support polled mode. To use it, leave out the irq and the vector in
the config file. It only causes extra overhead for open polled ports.
The maximum usable speed is approximately 1000 bps for a 16450 and
15000 bps for a 16550.
Other cosmetic changes.
-rw-r--r-- | sys/dev/sio/sio.c | 135 | ||||
-rw-r--r-- | sys/i386/isa/sio.c | 135 | ||||
-rw-r--r-- | sys/isa/sio.c | 135 |
3 files changed, 258 insertions, 147 deletions
diff --git a/sys/dev/sio/sio.c b/sys/dev/sio/sio.c index c39a739..1df3260 100644 --- a/sys/dev/sio/sio.c +++ b/sys/dev/sio/sio.c @@ -31,7 +31,7 @@ * SUCH DAMAGE. * * from: @(#)com.c 7.5 (Berkeley) 5/16/91 - * $Id: sio.c,v 1.52 1994/09/13 03:30:31 phk Exp $ + * $Id: sio.c,v 1.53 1994/09/21 19:39:25 davidg Exp $ */ #include "sio.h" @@ -58,6 +58,8 @@ #include <sys/malloc.h> #include <sys/syslog.h> +#include <machine/clock.h> + #include <i386/isa/icu.h> /* XXX just to get at `imen' */ #include <i386/isa/isa.h> #include <i386/isa/isa_device.h> @@ -102,8 +104,8 @@ termioschars(t) #define COM_NOTAST4(dev) ((dev)->id_flags & 0x04) #endif /* COM_MULTIPORT */ -#define COM_NOFIFO(dev) ((dev)->id_flags & 0x02) -#define COM_VERBOSE(dev) ((dev)->id_flags & 0x80) +#define COM_NOFIFO(dev) ((dev)->id_flags & 0x02) +#define COM_VERBOSE(dev) ((dev)->id_flags & 0x80) #define com_scr 7 /* scratch register for 16450-16550 (R/W) */ @@ -172,6 +174,8 @@ struct com_s { #ifdef COM_MULTIPORT bool_t multiport; /* is this unit part of a multiport device? */ #endif /* COM_MULTIPORT */ + bool_t no_irq; /* nonzero if irq is not attached */ + bool_t poll; /* nonzero if polling is required */ int dtr_wait; /* time to hold DTR down on close (* 1/hz) */ u_int tx_fifo_size; u_int wopeners; /* # processes waiting for DCD in open() */ @@ -211,10 +215,8 @@ struct com_s { struct termios lt_in; /* should be in struct tty */ struct termios lt_out; -#ifdef TIOCTIMESTAMP bool_t do_timestamp; struct timeval timestamp; -#endif u_long bytes_in; /* statistics */ u_long bytes_out; @@ -286,9 +288,7 @@ static int LoadSoftModem __P((int unit,int base_io, u_long size, u_char *ptr static struct com_s *p_com_addr[NSIO]; #define com_addr(unit) (p_com_addr[unit]) -#ifdef TIOCTIMESTAMP static struct timeval intr_timestamp; -#endif struct isa_driver siodriver = { sioprobe, sioattach, "sio" @@ -371,9 +371,9 @@ sioprobe(dev) } /* - * If the port is on a multiport card and has a master port, - * initialize the common interrupt control register in the - * master and prepare to leave MCR_IENABLE clear in the mcr. + * If the device is on a multiport card and has an AST/4 + * compatible interrupt control register, initialize this + * register and prepare to leave MCR_IENABLE clear in the mcr. * Otherwise, prepare to set MCR_IENABLE in the mcr. * Point idev to the device struct giving the correct id_irq. * This is the struct for the master device if there is one. @@ -385,26 +385,19 @@ sioprobe(dev) idev = find_isadev(isa_devtab_tty, &siodriver, COM_MPMASTER(dev)); if (idev == NULL) { - printf("sio%d: master device %d not found\n", - dev->id_unit, COM_MPMASTER(dev)); - return (0); - } - if (idev->id_irq == 0) { - printf("sio%d: master device %d irq not configured\n", + printf("sio%d: master device %d not configured\n", dev->id_unit, COM_MPMASTER(dev)); return (0); } if (!COM_NOTAST4(dev)) { - outb(idev->id_iobase + com_scr, 0x80); + outb(idev->id_iobase + com_scr, + idev->id_irq ? 0x80 : 0); mcr_image = 0; } } - else #endif /* COM_MULTIPORT */ - if (idev->id_irq == 0) { - printf("sio%d: irq not configured\n", dev->id_unit); - return (0); - } + if (idev->id_irq == 0) + mcr_image = 0; bzero(failures, sizeof failures); iobase = dev->id_iobase; @@ -472,7 +465,7 @@ sioprobe(dev) * it's unlikely to do more than allow the null byte out. */ outb(iobase + com_data, 0); - DELAY((2 + 1) * 9600 / 10); + DELAY((1 + 2) * 9600 / 10); /* * Turn off loopback mode so that the interrupt gate works again @@ -497,7 +490,8 @@ sioprobe(dev) if (idev->id_irq != 0) failures[3] = isa_irq_pending(idev) ? 0 : 1; failures[4] = (inb(iobase + com_iir) & IIR_IMASK) - IIR_TXRDY; - failures[5] = isa_irq_pending(idev) ? 1 : 0; + if (idev->id_irq != 0) + failures[5] = isa_irq_pending(idev) ? 1 : 0; failures[6] = (inb(iobase + com_iir) & IIR_IMASK) - IIR_NOPEND; /* @@ -512,7 +506,8 @@ sioprobe(dev) outb(iobase + com_ier, 0); outb(iobase + com_cfcr, CFCR_8BITS); /* dummy to avoid bus echo */ failures[7] = inb(iobase + com_ier); - failures[8] = isa_irq_pending(idev) ? 1 : 0; + if (idev->id_irq != 0) + failures[8] = isa_irq_pending(idev) ? 1 : 0; failures[9] = (inb(iobase + com_iir) & IIR_IMASK) - IIR_NOPEND; outb(IO_ICU1 + 1, imen); /* XXX */ @@ -554,13 +549,15 @@ sioattach(isdp) * data port is not hidden when we enable interrupts. * o ier = 0. * Interrupts are only enabled when the line is open. - * o mcr = MCR_IENABLE, or 0 if the port has a master port. + * o mcr = MCR_IENABLE, or 0 if the port has AST/4 compatible + * interrupt control register or the config specifies no irq. * Keeping MCR_DTR and MCR_RTS off might stop the external * device from sending before we are ready. */ bzero(com, sizeof *com); com->cfcr_image = CFCR_8BITS; com->dtr_wait = 3 * hz; + com->no_irq = isdp->id_irq == 0; com->tx_fifo_size = 1; com->iptr = com->ibuf = com->ibuf1; com->ibufend = com->ibuf1 + RS_IBUFSIZE; @@ -656,6 +653,8 @@ determined_type: ; if (unit == COM_MPMASTER(isdp)) printf(" master"); printf(")"); + com->no_irq = find_isadev(isa_devtab_tty, &siodriver, + COM_MPMASTER(isdp))->id_irq == 0; } #endif /* COM_MULTIPORT */ printf("\n"); @@ -665,7 +664,7 @@ determined_type: ; if (unit == comconsole) kgdb_dev = -1; /* can't debug over console port */ else { - int divisor; + int divisor; /* * XXX now unfinished and broken. Need to do @@ -783,6 +782,7 @@ open_top: ? com->it_out : com->it_in; commctl(com, MCR_DTR | MCR_RTS, DMSET); com->ftl_max = com->ftl_init; + com->poll = com->no_irq; ++com->wopeners; error = comparam(tp, &tp->t_termios); --com->wopeners; @@ -794,12 +794,30 @@ open_top: ttsetwater(tp); iobase = com->iobase; if (com->hasfifo) { - /* Drain fifo. */ - outb(iobase + com_fifo, - FIFO_ENABLE | FIFO_RCV_RST | FIFO_XMT_RST - | com->ftl); - DELAY(100); + /* + * (Re)enable and drain fifos. + * + * Certain SMC chips cause problems if the fifos + * are enabled while input is ready. Turn off the + * fifo if necessary to clear the input. We test + * the input ready bit after enabling the fifos + * since we've already enabled them in comparam() + * and to handle races between enabling and fresh + * input. + */ + while (TRUE) { + outb(iobase + com_fifo, + FIFO_RCV_RST | FIFO_XMT_RST + | FIFO_ENABLE | com->ftl); + DELAY(100); + if (!(inb(com->line_status_port) & LSR_RXRDY)) + break; + outb(iobase + com_fifo, 0); + DELAY(100); + (void) inb(com->data_port); + } } + disable_intr(); (void) inb(com->line_status_port); (void) inb(com->data_port); @@ -878,9 +896,8 @@ comhardclose(com) unit = DEV_TO_UNIT(com->tp->t_dev); iobase = com->iobase; s = spltty(); -#ifdef TIOCTIMESTAMP + com->poll = FALSE; com->do_timestamp = 0; -#endif outb(iobase + com_cfcr, com->cfcr_image &= ~CFCR_SBREAK); #ifdef KGDB /* do not disable interrupts or hang up if debugging */ @@ -968,16 +985,14 @@ siodtrwakeup(chan) wakeup(&com->dtr_wait); } -#ifdef TIOCTIMESTAMP /* Interrupt routine for timekeeping purposes */ void siointrts(unit) - int unit; + int unit; { microtime(&intr_timestamp); siointr(unit); } -#endif void siointr(unit) @@ -1020,11 +1035,9 @@ siointr1(com) u_char *ioptr; u_char recv_data; -#ifdef TIOCTIMESTAMP if (com->do_timestamp) /* XXX a little bloat here... */ com->timestamp = intr_timestamp; -#endif while (TRUE) { line_status = inb(com->line_status_port); @@ -1233,7 +1246,7 @@ sioioctl(dev, cmd, data, flag, p) } tp = com->tp; if (cmd == TIOCSETA || cmd == TIOCSETAW || cmd == TIOCSETAF) { - int cc; + int cc; struct termios *dt = (struct termios *)data; struct termios *lt = mynor & CALLOUT_MASK ? &com->lt_out : &com->lt_in; @@ -1318,12 +1331,10 @@ sioioctl(dev, cmd, data, flag, p) case TIOCMGDTRWAIT: *(int *)data = com->dtr_wait; break; -#ifdef TIOCTIMESTAMP case TIOCTIMESTAMP: com->do_timestamp = TRUE; *(struct timeval *)data = com->timestamp; break; -#endif default: splx(s); return (ENOTTY); @@ -1364,8 +1375,27 @@ repeat: if (com == NULL) continue; tp = com->tp; - if (tp == NULL) + if (tp == NULL) { + /* + * XXX forget any events related to closed devices + * (actually never opened devices) so that we don't + * loop. + */ + disable_intr(); + incc = com->iptr - com->ibuf; + com->iptr = com->ibuf; + if (com->state & CS_CHECKMSR) { + incc += LOTS_OF_EVENTS; + com->state &= ~CS_CHECKMSR; + } + com_events -= incc; + enable_intr(); + if (incc != 0) + log(LOG_DEBUG, + "sio%d: %d events for device with no tp\n", + unit, incc); continue; + } /* switch the role of the low-level input buffers */ if (com->iptr == (ibuf = com->ibuf)) { @@ -1445,7 +1475,7 @@ repeat: if (delta == 0 || !(tp->t_state & TS_ISOPEN)) continue; total = com->error_counts[errnum] += delta; - log(LOG_WARNING, + log(LOG_ERR, "sio%d: %u more %s%s (total %lu)\n", unit, delta, error_desc[errnum], delta == 1 ? "" : "s", total); @@ -1461,7 +1491,7 @@ repeat: com->ftl -= FIFO_TRIGGER_DELTA; outb(com->iobase + com_fifo, FIFO_ENABLE | com->ftl); - log(LOG_WARNING, + log(LOG_DEBUG, "sio%d: reduced fifo trigger level to %d\n", unit, ftl_in_bytes[com->ftl @@ -1815,7 +1845,7 @@ comwakeup(chan) { int unit; - timeout(comwakeup, (void *)NULL, hz / 100); + timeout(comwakeup, (caddr_t)NULL, hz > 200 ? hz / 200 : 1); if (com_events != 0) { int s; @@ -1826,11 +1856,13 @@ comwakeup(chan) } /* recover from lost output interrupts */ + /* poll any lines that don't use interrupts */ for (unit = 0; unit < NSIO; ++unit) { struct com_s *com; com = com_addr(unit); - if (com != NULL && com->state >= (CS_BUSY | CS_TTGO)) { + if (com != NULL + && (com->state >= (CS_BUSY | CS_TTGO) || com->poll)) { disable_intr(); siointr1(com); enable_intr(); @@ -1898,7 +1930,12 @@ siocnopen(sp) outb(iobase + com_dlbh, (u_int) divisor >> 8); outb(iobase + com_cfcr, CFCR_8BITS); sp->mcr = inb(iobase + com_mcr); - outb(iobase + com_mcr, MCR_DTR | MCR_RTS); + /* + * We don't want interrupts, but must be careful not to "disable" + * them by clearing the MCR_IENABLE bit, since that might cause + * an interrupt by floating the IRQ line. + */ + outb(iobase + com_mcr, (sp->mcr & MCR_IENABLE) | MCR_DTR | MCR_RTS); } static void @@ -1917,7 +1954,7 @@ siocnclose(sp) outb(iobase + com_dlbh, sp->dlbh); outb(iobase + com_cfcr, sp->cfcr); /* - * XXX damp oscllations of MCR_DTR and MCR_RTS by not restoring them. + * XXX damp oscillations of MCR_DTR and MCR_RTS by not restoring them. */ outb(iobase + com_mcr, sp->mcr | MCR_DTR | MCR_RTS); outb(iobase + com_ier, sp->ier); diff --git a/sys/i386/isa/sio.c b/sys/i386/isa/sio.c index c39a739..1df3260 100644 --- a/sys/i386/isa/sio.c +++ b/sys/i386/isa/sio.c @@ -31,7 +31,7 @@ * SUCH DAMAGE. * * from: @(#)com.c 7.5 (Berkeley) 5/16/91 - * $Id: sio.c,v 1.52 1994/09/13 03:30:31 phk Exp $ + * $Id: sio.c,v 1.53 1994/09/21 19:39:25 davidg Exp $ */ #include "sio.h" @@ -58,6 +58,8 @@ #include <sys/malloc.h> #include <sys/syslog.h> +#include <machine/clock.h> + #include <i386/isa/icu.h> /* XXX just to get at `imen' */ #include <i386/isa/isa.h> #include <i386/isa/isa_device.h> @@ -102,8 +104,8 @@ termioschars(t) #define COM_NOTAST4(dev) ((dev)->id_flags & 0x04) #endif /* COM_MULTIPORT */ -#define COM_NOFIFO(dev) ((dev)->id_flags & 0x02) -#define COM_VERBOSE(dev) ((dev)->id_flags & 0x80) +#define COM_NOFIFO(dev) ((dev)->id_flags & 0x02) +#define COM_VERBOSE(dev) ((dev)->id_flags & 0x80) #define com_scr 7 /* scratch register for 16450-16550 (R/W) */ @@ -172,6 +174,8 @@ struct com_s { #ifdef COM_MULTIPORT bool_t multiport; /* is this unit part of a multiport device? */ #endif /* COM_MULTIPORT */ + bool_t no_irq; /* nonzero if irq is not attached */ + bool_t poll; /* nonzero if polling is required */ int dtr_wait; /* time to hold DTR down on close (* 1/hz) */ u_int tx_fifo_size; u_int wopeners; /* # processes waiting for DCD in open() */ @@ -211,10 +215,8 @@ struct com_s { struct termios lt_in; /* should be in struct tty */ struct termios lt_out; -#ifdef TIOCTIMESTAMP bool_t do_timestamp; struct timeval timestamp; -#endif u_long bytes_in; /* statistics */ u_long bytes_out; @@ -286,9 +288,7 @@ static int LoadSoftModem __P((int unit,int base_io, u_long size, u_char *ptr static struct com_s *p_com_addr[NSIO]; #define com_addr(unit) (p_com_addr[unit]) -#ifdef TIOCTIMESTAMP static struct timeval intr_timestamp; -#endif struct isa_driver siodriver = { sioprobe, sioattach, "sio" @@ -371,9 +371,9 @@ sioprobe(dev) } /* - * If the port is on a multiport card and has a master port, - * initialize the common interrupt control register in the - * master and prepare to leave MCR_IENABLE clear in the mcr. + * If the device is on a multiport card and has an AST/4 + * compatible interrupt control register, initialize this + * register and prepare to leave MCR_IENABLE clear in the mcr. * Otherwise, prepare to set MCR_IENABLE in the mcr. * Point idev to the device struct giving the correct id_irq. * This is the struct for the master device if there is one. @@ -385,26 +385,19 @@ sioprobe(dev) idev = find_isadev(isa_devtab_tty, &siodriver, COM_MPMASTER(dev)); if (idev == NULL) { - printf("sio%d: master device %d not found\n", - dev->id_unit, COM_MPMASTER(dev)); - return (0); - } - if (idev->id_irq == 0) { - printf("sio%d: master device %d irq not configured\n", + printf("sio%d: master device %d not configured\n", dev->id_unit, COM_MPMASTER(dev)); return (0); } if (!COM_NOTAST4(dev)) { - outb(idev->id_iobase + com_scr, 0x80); + outb(idev->id_iobase + com_scr, + idev->id_irq ? 0x80 : 0); mcr_image = 0; } } - else #endif /* COM_MULTIPORT */ - if (idev->id_irq == 0) { - printf("sio%d: irq not configured\n", dev->id_unit); - return (0); - } + if (idev->id_irq == 0) + mcr_image = 0; bzero(failures, sizeof failures); iobase = dev->id_iobase; @@ -472,7 +465,7 @@ sioprobe(dev) * it's unlikely to do more than allow the null byte out. */ outb(iobase + com_data, 0); - DELAY((2 + 1) * 9600 / 10); + DELAY((1 + 2) * 9600 / 10); /* * Turn off loopback mode so that the interrupt gate works again @@ -497,7 +490,8 @@ sioprobe(dev) if (idev->id_irq != 0) failures[3] = isa_irq_pending(idev) ? 0 : 1; failures[4] = (inb(iobase + com_iir) & IIR_IMASK) - IIR_TXRDY; - failures[5] = isa_irq_pending(idev) ? 1 : 0; + if (idev->id_irq != 0) + failures[5] = isa_irq_pending(idev) ? 1 : 0; failures[6] = (inb(iobase + com_iir) & IIR_IMASK) - IIR_NOPEND; /* @@ -512,7 +506,8 @@ sioprobe(dev) outb(iobase + com_ier, 0); outb(iobase + com_cfcr, CFCR_8BITS); /* dummy to avoid bus echo */ failures[7] = inb(iobase + com_ier); - failures[8] = isa_irq_pending(idev) ? 1 : 0; + if (idev->id_irq != 0) + failures[8] = isa_irq_pending(idev) ? 1 : 0; failures[9] = (inb(iobase + com_iir) & IIR_IMASK) - IIR_NOPEND; outb(IO_ICU1 + 1, imen); /* XXX */ @@ -554,13 +549,15 @@ sioattach(isdp) * data port is not hidden when we enable interrupts. * o ier = 0. * Interrupts are only enabled when the line is open. - * o mcr = MCR_IENABLE, or 0 if the port has a master port. + * o mcr = MCR_IENABLE, or 0 if the port has AST/4 compatible + * interrupt control register or the config specifies no irq. * Keeping MCR_DTR and MCR_RTS off might stop the external * device from sending before we are ready. */ bzero(com, sizeof *com); com->cfcr_image = CFCR_8BITS; com->dtr_wait = 3 * hz; + com->no_irq = isdp->id_irq == 0; com->tx_fifo_size = 1; com->iptr = com->ibuf = com->ibuf1; com->ibufend = com->ibuf1 + RS_IBUFSIZE; @@ -656,6 +653,8 @@ determined_type: ; if (unit == COM_MPMASTER(isdp)) printf(" master"); printf(")"); + com->no_irq = find_isadev(isa_devtab_tty, &siodriver, + COM_MPMASTER(isdp))->id_irq == 0; } #endif /* COM_MULTIPORT */ printf("\n"); @@ -665,7 +664,7 @@ determined_type: ; if (unit == comconsole) kgdb_dev = -1; /* can't debug over console port */ else { - int divisor; + int divisor; /* * XXX now unfinished and broken. Need to do @@ -783,6 +782,7 @@ open_top: ? com->it_out : com->it_in; commctl(com, MCR_DTR | MCR_RTS, DMSET); com->ftl_max = com->ftl_init; + com->poll = com->no_irq; ++com->wopeners; error = comparam(tp, &tp->t_termios); --com->wopeners; @@ -794,12 +794,30 @@ open_top: ttsetwater(tp); iobase = com->iobase; if (com->hasfifo) { - /* Drain fifo. */ - outb(iobase + com_fifo, - FIFO_ENABLE | FIFO_RCV_RST | FIFO_XMT_RST - | com->ftl); - DELAY(100); + /* + * (Re)enable and drain fifos. + * + * Certain SMC chips cause problems if the fifos + * are enabled while input is ready. Turn off the + * fifo if necessary to clear the input. We test + * the input ready bit after enabling the fifos + * since we've already enabled them in comparam() + * and to handle races between enabling and fresh + * input. + */ + while (TRUE) { + outb(iobase + com_fifo, + FIFO_RCV_RST | FIFO_XMT_RST + | FIFO_ENABLE | com->ftl); + DELAY(100); + if (!(inb(com->line_status_port) & LSR_RXRDY)) + break; + outb(iobase + com_fifo, 0); + DELAY(100); + (void) inb(com->data_port); + } } + disable_intr(); (void) inb(com->line_status_port); (void) inb(com->data_port); @@ -878,9 +896,8 @@ comhardclose(com) unit = DEV_TO_UNIT(com->tp->t_dev); iobase = com->iobase; s = spltty(); -#ifdef TIOCTIMESTAMP + com->poll = FALSE; com->do_timestamp = 0; -#endif outb(iobase + com_cfcr, com->cfcr_image &= ~CFCR_SBREAK); #ifdef KGDB /* do not disable interrupts or hang up if debugging */ @@ -968,16 +985,14 @@ siodtrwakeup(chan) wakeup(&com->dtr_wait); } -#ifdef TIOCTIMESTAMP /* Interrupt routine for timekeeping purposes */ void siointrts(unit) - int unit; + int unit; { microtime(&intr_timestamp); siointr(unit); } -#endif void siointr(unit) @@ -1020,11 +1035,9 @@ siointr1(com) u_char *ioptr; u_char recv_data; -#ifdef TIOCTIMESTAMP if (com->do_timestamp) /* XXX a little bloat here... */ com->timestamp = intr_timestamp; -#endif while (TRUE) { line_status = inb(com->line_status_port); @@ -1233,7 +1246,7 @@ sioioctl(dev, cmd, data, flag, p) } tp = com->tp; if (cmd == TIOCSETA || cmd == TIOCSETAW || cmd == TIOCSETAF) { - int cc; + int cc; struct termios *dt = (struct termios *)data; struct termios *lt = mynor & CALLOUT_MASK ? &com->lt_out : &com->lt_in; @@ -1318,12 +1331,10 @@ sioioctl(dev, cmd, data, flag, p) case TIOCMGDTRWAIT: *(int *)data = com->dtr_wait; break; -#ifdef TIOCTIMESTAMP case TIOCTIMESTAMP: com->do_timestamp = TRUE; *(struct timeval *)data = com->timestamp; break; -#endif default: splx(s); return (ENOTTY); @@ -1364,8 +1375,27 @@ repeat: if (com == NULL) continue; tp = com->tp; - if (tp == NULL) + if (tp == NULL) { + /* + * XXX forget any events related to closed devices + * (actually never opened devices) so that we don't + * loop. + */ + disable_intr(); + incc = com->iptr - com->ibuf; + com->iptr = com->ibuf; + if (com->state & CS_CHECKMSR) { + incc += LOTS_OF_EVENTS; + com->state &= ~CS_CHECKMSR; + } + com_events -= incc; + enable_intr(); + if (incc != 0) + log(LOG_DEBUG, + "sio%d: %d events for device with no tp\n", + unit, incc); continue; + } /* switch the role of the low-level input buffers */ if (com->iptr == (ibuf = com->ibuf)) { @@ -1445,7 +1475,7 @@ repeat: if (delta == 0 || !(tp->t_state & TS_ISOPEN)) continue; total = com->error_counts[errnum] += delta; - log(LOG_WARNING, + log(LOG_ERR, "sio%d: %u more %s%s (total %lu)\n", unit, delta, error_desc[errnum], delta == 1 ? "" : "s", total); @@ -1461,7 +1491,7 @@ repeat: com->ftl -= FIFO_TRIGGER_DELTA; outb(com->iobase + com_fifo, FIFO_ENABLE | com->ftl); - log(LOG_WARNING, + log(LOG_DEBUG, "sio%d: reduced fifo trigger level to %d\n", unit, ftl_in_bytes[com->ftl @@ -1815,7 +1845,7 @@ comwakeup(chan) { int unit; - timeout(comwakeup, (void *)NULL, hz / 100); + timeout(comwakeup, (caddr_t)NULL, hz > 200 ? hz / 200 : 1); if (com_events != 0) { int s; @@ -1826,11 +1856,13 @@ comwakeup(chan) } /* recover from lost output interrupts */ + /* poll any lines that don't use interrupts */ for (unit = 0; unit < NSIO; ++unit) { struct com_s *com; com = com_addr(unit); - if (com != NULL && com->state >= (CS_BUSY | CS_TTGO)) { + if (com != NULL + && (com->state >= (CS_BUSY | CS_TTGO) || com->poll)) { disable_intr(); siointr1(com); enable_intr(); @@ -1898,7 +1930,12 @@ siocnopen(sp) outb(iobase + com_dlbh, (u_int) divisor >> 8); outb(iobase + com_cfcr, CFCR_8BITS); sp->mcr = inb(iobase + com_mcr); - outb(iobase + com_mcr, MCR_DTR | MCR_RTS); + /* + * We don't want interrupts, but must be careful not to "disable" + * them by clearing the MCR_IENABLE bit, since that might cause + * an interrupt by floating the IRQ line. + */ + outb(iobase + com_mcr, (sp->mcr & MCR_IENABLE) | MCR_DTR | MCR_RTS); } static void @@ -1917,7 +1954,7 @@ siocnclose(sp) outb(iobase + com_dlbh, sp->dlbh); outb(iobase + com_cfcr, sp->cfcr); /* - * XXX damp oscllations of MCR_DTR and MCR_RTS by not restoring them. + * XXX damp oscillations of MCR_DTR and MCR_RTS by not restoring them. */ outb(iobase + com_mcr, sp->mcr | MCR_DTR | MCR_RTS); outb(iobase + com_ier, sp->ier); diff --git a/sys/isa/sio.c b/sys/isa/sio.c index c39a739..1df3260 100644 --- a/sys/isa/sio.c +++ b/sys/isa/sio.c @@ -31,7 +31,7 @@ * SUCH DAMAGE. * * from: @(#)com.c 7.5 (Berkeley) 5/16/91 - * $Id: sio.c,v 1.52 1994/09/13 03:30:31 phk Exp $ + * $Id: sio.c,v 1.53 1994/09/21 19:39:25 davidg Exp $ */ #include "sio.h" @@ -58,6 +58,8 @@ #include <sys/malloc.h> #include <sys/syslog.h> +#include <machine/clock.h> + #include <i386/isa/icu.h> /* XXX just to get at `imen' */ #include <i386/isa/isa.h> #include <i386/isa/isa_device.h> @@ -102,8 +104,8 @@ termioschars(t) #define COM_NOTAST4(dev) ((dev)->id_flags & 0x04) #endif /* COM_MULTIPORT */ -#define COM_NOFIFO(dev) ((dev)->id_flags & 0x02) -#define COM_VERBOSE(dev) ((dev)->id_flags & 0x80) +#define COM_NOFIFO(dev) ((dev)->id_flags & 0x02) +#define COM_VERBOSE(dev) ((dev)->id_flags & 0x80) #define com_scr 7 /* scratch register for 16450-16550 (R/W) */ @@ -172,6 +174,8 @@ struct com_s { #ifdef COM_MULTIPORT bool_t multiport; /* is this unit part of a multiport device? */ #endif /* COM_MULTIPORT */ + bool_t no_irq; /* nonzero if irq is not attached */ + bool_t poll; /* nonzero if polling is required */ int dtr_wait; /* time to hold DTR down on close (* 1/hz) */ u_int tx_fifo_size; u_int wopeners; /* # processes waiting for DCD in open() */ @@ -211,10 +215,8 @@ struct com_s { struct termios lt_in; /* should be in struct tty */ struct termios lt_out; -#ifdef TIOCTIMESTAMP bool_t do_timestamp; struct timeval timestamp; -#endif u_long bytes_in; /* statistics */ u_long bytes_out; @@ -286,9 +288,7 @@ static int LoadSoftModem __P((int unit,int base_io, u_long size, u_char *ptr static struct com_s *p_com_addr[NSIO]; #define com_addr(unit) (p_com_addr[unit]) -#ifdef TIOCTIMESTAMP static struct timeval intr_timestamp; -#endif struct isa_driver siodriver = { sioprobe, sioattach, "sio" @@ -371,9 +371,9 @@ sioprobe(dev) } /* - * If the port is on a multiport card and has a master port, - * initialize the common interrupt control register in the - * master and prepare to leave MCR_IENABLE clear in the mcr. + * If the device is on a multiport card and has an AST/4 + * compatible interrupt control register, initialize this + * register and prepare to leave MCR_IENABLE clear in the mcr. * Otherwise, prepare to set MCR_IENABLE in the mcr. * Point idev to the device struct giving the correct id_irq. * This is the struct for the master device if there is one. @@ -385,26 +385,19 @@ sioprobe(dev) idev = find_isadev(isa_devtab_tty, &siodriver, COM_MPMASTER(dev)); if (idev == NULL) { - printf("sio%d: master device %d not found\n", - dev->id_unit, COM_MPMASTER(dev)); - return (0); - } - if (idev->id_irq == 0) { - printf("sio%d: master device %d irq not configured\n", + printf("sio%d: master device %d not configured\n", dev->id_unit, COM_MPMASTER(dev)); return (0); } if (!COM_NOTAST4(dev)) { - outb(idev->id_iobase + com_scr, 0x80); + outb(idev->id_iobase + com_scr, + idev->id_irq ? 0x80 : 0); mcr_image = 0; } } - else #endif /* COM_MULTIPORT */ - if (idev->id_irq == 0) { - printf("sio%d: irq not configured\n", dev->id_unit); - return (0); - } + if (idev->id_irq == 0) + mcr_image = 0; bzero(failures, sizeof failures); iobase = dev->id_iobase; @@ -472,7 +465,7 @@ sioprobe(dev) * it's unlikely to do more than allow the null byte out. */ outb(iobase + com_data, 0); - DELAY((2 + 1) * 9600 / 10); + DELAY((1 + 2) * 9600 / 10); /* * Turn off loopback mode so that the interrupt gate works again @@ -497,7 +490,8 @@ sioprobe(dev) if (idev->id_irq != 0) failures[3] = isa_irq_pending(idev) ? 0 : 1; failures[4] = (inb(iobase + com_iir) & IIR_IMASK) - IIR_TXRDY; - failures[5] = isa_irq_pending(idev) ? 1 : 0; + if (idev->id_irq != 0) + failures[5] = isa_irq_pending(idev) ? 1 : 0; failures[6] = (inb(iobase + com_iir) & IIR_IMASK) - IIR_NOPEND; /* @@ -512,7 +506,8 @@ sioprobe(dev) outb(iobase + com_ier, 0); outb(iobase + com_cfcr, CFCR_8BITS); /* dummy to avoid bus echo */ failures[7] = inb(iobase + com_ier); - failures[8] = isa_irq_pending(idev) ? 1 : 0; + if (idev->id_irq != 0) + failures[8] = isa_irq_pending(idev) ? 1 : 0; failures[9] = (inb(iobase + com_iir) & IIR_IMASK) - IIR_NOPEND; outb(IO_ICU1 + 1, imen); /* XXX */ @@ -554,13 +549,15 @@ sioattach(isdp) * data port is not hidden when we enable interrupts. * o ier = 0. * Interrupts are only enabled when the line is open. - * o mcr = MCR_IENABLE, or 0 if the port has a master port. + * o mcr = MCR_IENABLE, or 0 if the port has AST/4 compatible + * interrupt control register or the config specifies no irq. * Keeping MCR_DTR and MCR_RTS off might stop the external * device from sending before we are ready. */ bzero(com, sizeof *com); com->cfcr_image = CFCR_8BITS; com->dtr_wait = 3 * hz; + com->no_irq = isdp->id_irq == 0; com->tx_fifo_size = 1; com->iptr = com->ibuf = com->ibuf1; com->ibufend = com->ibuf1 + RS_IBUFSIZE; @@ -656,6 +653,8 @@ determined_type: ; if (unit == COM_MPMASTER(isdp)) printf(" master"); printf(")"); + com->no_irq = find_isadev(isa_devtab_tty, &siodriver, + COM_MPMASTER(isdp))->id_irq == 0; } #endif /* COM_MULTIPORT */ printf("\n"); @@ -665,7 +664,7 @@ determined_type: ; if (unit == comconsole) kgdb_dev = -1; /* can't debug over console port */ else { - int divisor; + int divisor; /* * XXX now unfinished and broken. Need to do @@ -783,6 +782,7 @@ open_top: ? com->it_out : com->it_in; commctl(com, MCR_DTR | MCR_RTS, DMSET); com->ftl_max = com->ftl_init; + com->poll = com->no_irq; ++com->wopeners; error = comparam(tp, &tp->t_termios); --com->wopeners; @@ -794,12 +794,30 @@ open_top: ttsetwater(tp); iobase = com->iobase; if (com->hasfifo) { - /* Drain fifo. */ - outb(iobase + com_fifo, - FIFO_ENABLE | FIFO_RCV_RST | FIFO_XMT_RST - | com->ftl); - DELAY(100); + /* + * (Re)enable and drain fifos. + * + * Certain SMC chips cause problems if the fifos + * are enabled while input is ready. Turn off the + * fifo if necessary to clear the input. We test + * the input ready bit after enabling the fifos + * since we've already enabled them in comparam() + * and to handle races between enabling and fresh + * input. + */ + while (TRUE) { + outb(iobase + com_fifo, + FIFO_RCV_RST | FIFO_XMT_RST + | FIFO_ENABLE | com->ftl); + DELAY(100); + if (!(inb(com->line_status_port) & LSR_RXRDY)) + break; + outb(iobase + com_fifo, 0); + DELAY(100); + (void) inb(com->data_port); + } } + disable_intr(); (void) inb(com->line_status_port); (void) inb(com->data_port); @@ -878,9 +896,8 @@ comhardclose(com) unit = DEV_TO_UNIT(com->tp->t_dev); iobase = com->iobase; s = spltty(); -#ifdef TIOCTIMESTAMP + com->poll = FALSE; com->do_timestamp = 0; -#endif outb(iobase + com_cfcr, com->cfcr_image &= ~CFCR_SBREAK); #ifdef KGDB /* do not disable interrupts or hang up if debugging */ @@ -968,16 +985,14 @@ siodtrwakeup(chan) wakeup(&com->dtr_wait); } -#ifdef TIOCTIMESTAMP /* Interrupt routine for timekeeping purposes */ void siointrts(unit) - int unit; + int unit; { microtime(&intr_timestamp); siointr(unit); } -#endif void siointr(unit) @@ -1020,11 +1035,9 @@ siointr1(com) u_char *ioptr; u_char recv_data; -#ifdef TIOCTIMESTAMP if (com->do_timestamp) /* XXX a little bloat here... */ com->timestamp = intr_timestamp; -#endif while (TRUE) { line_status = inb(com->line_status_port); @@ -1233,7 +1246,7 @@ sioioctl(dev, cmd, data, flag, p) } tp = com->tp; if (cmd == TIOCSETA || cmd == TIOCSETAW || cmd == TIOCSETAF) { - int cc; + int cc; struct termios *dt = (struct termios *)data; struct termios *lt = mynor & CALLOUT_MASK ? &com->lt_out : &com->lt_in; @@ -1318,12 +1331,10 @@ sioioctl(dev, cmd, data, flag, p) case TIOCMGDTRWAIT: *(int *)data = com->dtr_wait; break; -#ifdef TIOCTIMESTAMP case TIOCTIMESTAMP: com->do_timestamp = TRUE; *(struct timeval *)data = com->timestamp; break; -#endif default: splx(s); return (ENOTTY); @@ -1364,8 +1375,27 @@ repeat: if (com == NULL) continue; tp = com->tp; - if (tp == NULL) + if (tp == NULL) { + /* + * XXX forget any events related to closed devices + * (actually never opened devices) so that we don't + * loop. + */ + disable_intr(); + incc = com->iptr - com->ibuf; + com->iptr = com->ibuf; + if (com->state & CS_CHECKMSR) { + incc += LOTS_OF_EVENTS; + com->state &= ~CS_CHECKMSR; + } + com_events -= incc; + enable_intr(); + if (incc != 0) + log(LOG_DEBUG, + "sio%d: %d events for device with no tp\n", + unit, incc); continue; + } /* switch the role of the low-level input buffers */ if (com->iptr == (ibuf = com->ibuf)) { @@ -1445,7 +1475,7 @@ repeat: if (delta == 0 || !(tp->t_state & TS_ISOPEN)) continue; total = com->error_counts[errnum] += delta; - log(LOG_WARNING, + log(LOG_ERR, "sio%d: %u more %s%s (total %lu)\n", unit, delta, error_desc[errnum], delta == 1 ? "" : "s", total); @@ -1461,7 +1491,7 @@ repeat: com->ftl -= FIFO_TRIGGER_DELTA; outb(com->iobase + com_fifo, FIFO_ENABLE | com->ftl); - log(LOG_WARNING, + log(LOG_DEBUG, "sio%d: reduced fifo trigger level to %d\n", unit, ftl_in_bytes[com->ftl @@ -1815,7 +1845,7 @@ comwakeup(chan) { int unit; - timeout(comwakeup, (void *)NULL, hz / 100); + timeout(comwakeup, (caddr_t)NULL, hz > 200 ? hz / 200 : 1); if (com_events != 0) { int s; @@ -1826,11 +1856,13 @@ comwakeup(chan) } /* recover from lost output interrupts */ + /* poll any lines that don't use interrupts */ for (unit = 0; unit < NSIO; ++unit) { struct com_s *com; com = com_addr(unit); - if (com != NULL && com->state >= (CS_BUSY | CS_TTGO)) { + if (com != NULL + && (com->state >= (CS_BUSY | CS_TTGO) || com->poll)) { disable_intr(); siointr1(com); enable_intr(); @@ -1898,7 +1930,12 @@ siocnopen(sp) outb(iobase + com_dlbh, (u_int) divisor >> 8); outb(iobase + com_cfcr, CFCR_8BITS); sp->mcr = inb(iobase + com_mcr); - outb(iobase + com_mcr, MCR_DTR | MCR_RTS); + /* + * We don't want interrupts, but must be careful not to "disable" + * them by clearing the MCR_IENABLE bit, since that might cause + * an interrupt by floating the IRQ line. + */ + outb(iobase + com_mcr, (sp->mcr & MCR_IENABLE) | MCR_DTR | MCR_RTS); } static void @@ -1917,7 +1954,7 @@ siocnclose(sp) outb(iobase + com_dlbh, sp->dlbh); outb(iobase + com_cfcr, sp->cfcr); /* - * XXX damp oscllations of MCR_DTR and MCR_RTS by not restoring them. + * XXX damp oscillations of MCR_DTR and MCR_RTS by not restoring them. */ outb(iobase + com_mcr, sp->mcr | MCR_DTR | MCR_RTS); outb(iobase + com_ier, sp->ier); |