summaryrefslogtreecommitdiff
path: root/sys/dev/sio/sio.c
diff options
context:
space:
mode:
authorBruce Evans <bde@FreeBSD.org>1994-10-12 19:49:11 +0000
committerBruce Evans <bde@FreeBSD.org>1994-10-12 19:49:11 +0000
commitb2f6e97280b127214591b5186f16e9952ae70ee4 (patch)
tree43e219de40bc14c3ea655fa060f9a4e681e69ece /sys/dev/sio/sio.c
parenta9db8618242d8174d502709f08a6954a617369a7 (diff)
Notes
Diffstat (limited to 'sys/dev/sio/sio.c')
-rw-r--r--sys/dev/sio/sio.c135
1 files changed, 86 insertions, 49 deletions
diff --git a/sys/dev/sio/sio.c b/sys/dev/sio/sio.c
index c39a739bda43..1df32605229c 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);