/* $NetBSD: dca.c,v 1.51 2002/09/27 20:31:42 thorpej Exp $ */ /*- * Copyright (c) 1996, 1997 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation * by Jason R. Thorpe. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the NetBSD * Foundation, Inc. and its contributors. * 4. Neither the name of The NetBSD Foundation nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ /* * Copyright (c) 1982, 1986, 1990, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * @(#)dca.c 8.2 (Berkeley) 1/12/94 */ /* * Driver for the 98626/98644/internal serial interface on hp300/hp400, * based on the National Semiconductor INS8250/NS16550AF/WD16C552 UARTs. * * N.B. On the hp700 and some hp300s, there is a "secret bit" with * undocumented behavior. The third bit of the Modem Control Register * (MCR_IEN == 0x08) must be set to enable interrupts. Failure to do * so can result in deadlock on those machines, whereas the don't seem to * be any harmful side-effects from setting this bit on non-affected * machines. */ #include __KERNEL_RCSID(0, "$NetBSD: dca.c,v 1.51 2002/09/27 20:31:42 thorpej Exp $"); #include "opt_ddb.h" #include "opt_kgdb.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include struct dca_softc { struct device sc_dev; /* generic device glue */ struct dcadevice *sc_dca; /* pointer to hardware */ struct tty *sc_tty; /* our tty instance */ int sc_oflows; /* overflow counter */ short sc_flags; /* state flags */ /* * Bits for sc_flags. */ #define DCA_ACTIVE 0x0001 /* indicates live unit */ #define DCA_SOFTCAR 0x0002 /* indicates soft-carrier */ #define DCA_HASFIFO 0x0004 /* indicates unit has FIFO */ #define DCA_ISCONSOLE 0x0008 /* indicates unit is console */ }; int dcamatch __P((struct device *, struct cfdata *, void *)); void dcaattach __P((struct device *, struct device *, void *)); const struct cfattach dca_ca = { sizeof(struct dca_softc), dcamatch, dcaattach }; extern struct cfdriver dca_cd; dev_type_open(dcaopen); dev_type_close(dcaclose); dev_type_read(dcaread); dev_type_write(dcawrite); dev_type_ioctl(dcaioctl); dev_type_stop(dcastop); dev_type_tty(dcatty); dev_type_poll(dcapoll); const struct cdevsw dca_cdevsw = { dcaopen, dcaclose, dcaread, dcawrite, dcaioctl, dcastop, dcatty, dcapoll, nommap, D_TTY }; int dcaintr __P((void *)); void dcaeint __P((struct dca_softc *, int)); void dcamint __P((struct dca_softc *)); int dcaparam __P((struct tty *, struct termios *)); void dcastart __P((struct tty *)); int dcamctl __P((struct dca_softc *, int, int)); void dcainit __P((struct dcadevice *, int)); int dcacnattach __P((bus_space_tag_t, bus_addr_t, int)); int dcacngetc __P((dev_t)); void dcacnputc __P((dev_t, int)); /* * Stuff for DCA console support. */ static int dcadefaultrate = TTYDEF_SPEED; static struct consdev dca_cons = { NULL, NULL, dcacngetc, dcacnputc, nullcnpollc, NULL, NODEV, CN_REMOTE }; static struct dcadevice *dca_cn = NULL; /* pointer to hardware */ static int dcaconsinit; /* has been initialized */ static int dcaconscode; struct speedtab dcaspeedtab[] = { { 0, 0 }, { 50, DCABRD(50) }, { 75, DCABRD(75) }, { 110, DCABRD(110) }, { 134, DCABRD(134) }, { 150, DCABRD(150) }, { 200, DCABRD(200) }, { 300, DCABRD(300) }, { 600, DCABRD(600) }, { 1200, DCABRD(1200) }, { 1800, DCABRD(1800) }, { 2400, DCABRD(2400) }, { 4800, DCABRD(4800) }, { 9600, DCABRD(9600) }, { 19200, DCABRD(19200) }, { 38400, DCABRD(38400) }, { -1, -1 }, }; #ifdef KGDB #include extern dev_t kgdb_dev; extern int kgdb_rate; extern int kgdb_debug_init; #endif #define DCAUNIT(x) (minor(x) & 0x7ffff) #define DCADIALOUT(x) (minor(x) & 0x80000) #ifdef DEBUG long fifoin[17]; long fifoout[17]; long dcaintrcount[16]; long dcamintcount[16]; #endif void dcainit __P((struct dcadevice *, int)); int dcamatch(parent, match, aux) struct device *parent; struct cfdata *match; void *aux; { struct dio_attach_args *da = aux; switch (da->da_id) { case DIO_DEVICE_ID_DCA0: case DIO_DEVICE_ID_DCA0REM: case DIO_DEVICE_ID_DCA1: case DIO_DEVICE_ID_DCA1REM: return (1); } return (0); } void dcaattach(parent, self, aux) struct device *parent, *self; void *aux; { struct dca_softc *sc = (struct dca_softc *)self; struct dio_attach_args *da = aux; struct dcadevice *dca; int unit = self->dv_unit; int scode = da->da_scode; int ipl; if (scode == dcaconscode) { dca = dca_cn; sc->sc_flags |= DCA_ISCONSOLE; DELAY(100000); /* * We didn't know which unit this would be during * the console probe, so we have to fixup cn_dev here. */ cn_tab->cn_dev = makedev(cdevsw_lookup_major(&dca_cdevsw), unit); } else { dca = (struct dcadevice *)iomap(dio_scodetopa(da->da_scode), da->da_size); if (dca == NULL) { printf("\n%s: can't map registers\n", sc->sc_dev.dv_xname); return; } } sc->sc_dca = dca; ipl = DIO_IPL(dca); printf(" ipl %d", ipl); DELAY(1000); dca->dca_reset = 0xFF; DELAY(1000); /* look for a NS 16550AF UART with FIFOs */ dca->dca_fifo = FIFO_ENABLE|FIFO_RCV_RST|FIFO_XMT_RST|FIFO_TRIGGER_14; DELAY(100); if ((dca->dca_iir & IIR_FIFO_MASK) == IIR_FIFO_MASK) sc->sc_flags |= DCA_HASFIFO; /* Establish interrupt handler. */ (void) dio_intr_establish(dcaintr, sc, ipl, (sc->sc_flags & DCA_HASFIFO) ? IPL_TTY : IPL_TTYNOBUF); sc->sc_flags |= DCA_ACTIVE; if (self->dv_cfdata->cf_flags) sc->sc_flags |= DCA_SOFTCAR; /* Enable interrupts. */ dca->dca_ic = IC_IE; /* * Need to reset baud rate, etc. of next print so reset dcaconsinit. * Also make sure console is always "hardwired." */ if (sc->sc_flags & DCA_ISCONSOLE) { dcaconsinit = 0; sc->sc_flags |= DCA_SOFTCAR; printf(": console, "); } else printf(": "); if (sc->sc_flags & DCA_HASFIFO) printf("working fifo\n"); else printf("no fifo\n"); #ifdef KGDB if (kgdb_dev == makedev(cdevsw_lookup_major(&dca_cdevsw), unit)) { if (sc->sc_flags & DCA_ISCONSOLE) kgdb_dev = NODEV; /* can't debug over console port */ else { dcainit(dca, kgdb_rate); dcaconsinit = 1; /* don't re-init in dcaputc */ if (kgdb_debug_init) { /* * Print prefix of device name, * let kgdb_connect print the rest. */ printf("%s: ", sc->sc_dev.dv_xname); kgdb_connect(1); } else printf("%s: kgdb enabled\n", sc->sc_dev.dv_xname); } } #endif } /* ARGSUSED */ int dcaopen(dev, flag, mode, p) dev_t dev; int flag, mode; struct proc *p; { int unit = DCAUNIT(dev); struct dca_softc *sc; struct tty *tp; struct dcadevice *dca; u_char code; int s, error = 0; if (unit >= dca_cd.cd_ndevs || (sc = dca_cd.cd_devs[unit]) == NULL) return (ENXIO); if ((sc->sc_flags & DCA_ACTIVE) == 0) return (ENXIO); dca = sc->sc_dca; if (sc->sc_tty == NULL) { tp = sc->sc_tty = ttymalloc(); tty_attach(tp); } else tp = sc->sc_tty; tp->t_oproc = dcastart; tp->t_param = dcaparam; tp->t_dev = dev; if ((tp->t_state & TS_ISOPEN) && (tp->t_state & TS_XCLUDE) && p->p_ucred->cr_uid != 0) return (EBUSY); s = spltty(); if ((tp->t_state & TS_ISOPEN) == 0 && tp->t_wopen == 0) { /* * Sanity clause: reset the card on first open. * The card might be left in an inconsistent state * if card memory is read inadvertently. */ dcainit(dca, dcadefaultrate); ttychars(tp); tp->t_iflag = TTYDEF_IFLAG; tp->t_oflag = TTYDEF_OFLAG; tp->t_cflag = TTYDEF_CFLAG; tp->t_lflag = TTYDEF_LFLAG; tp->t_ispeed = tp->t_ospeed = dcadefaultrate; dcaparam(tp, &tp->t_termios); ttsetwater(tp); /* Set the FIFO threshold based on the receive speed. */ if (sc->sc_flags & DCA_HASFIFO) dca->dca_fifo = FIFO_ENABLE | FIFO_RCV_RST | FIFO_XMT_RST | (tp->t_ispeed <= 1200 ? FIFO_TRIGGER_1 : FIFO_TRIGGER_14); /* Flush any pending I/O */ while ((dca->dca_iir & IIR_IMASK) == IIR_RXRDY) code = dca->dca_data; /* Set modem control state. */ (void) dcamctl(sc, MCR_DTR | MCR_RTS, DMSET); /* Set soft-carrier if so configured. */ if ((sc->sc_flags & DCA_SOFTCAR) || (dcamctl(sc, 0, DMGET) & MSR_DCD)) tp->t_state |= TS_CARR_ON; } splx(s); error = ttyopen(tp, DCADIALOUT(dev), (flag & O_NONBLOCK)); if (error) goto bad; error = (*tp->t_linesw->l_open)(dev, tp); bad: return (error); } /*ARGSUSED*/ int dcaclose(dev, flag, mode, p) dev_t dev; int flag, mode; struct proc *p; { struct dca_softc *sc; struct tty *tp; struct dcadevice *dca; int unit; int s; unit = DCAUNIT(dev); sc = dca_cd.cd_devs[unit]; dca = sc->sc_dca; tp = sc->sc_tty; (*tp->t_linesw->l_close)(tp, flag); s = spltty(); dca->dca_cfcr &= ~CFCR_SBREAK; #ifdef KGDB /* do not disable interrupts if debugging */ if (dev != kgdb_dev) #endif dca->dca_ier = 0; if (tp->t_cflag & HUPCL && (sc->sc_flags & DCA_SOFTCAR) == 0) { /* XXX perhaps only clear DTR */ (void) dcamctl(sc, 0, DMSET); } tp->t_state &= ~(TS_BUSY | TS_FLUSH); splx(s); ttyclose(tp); #if 0 tty_detach(tp); ttyfree(tp); sc->sc_tty = NULL; #endif return (0); } int dcaread(dev, uio, flag) dev_t dev; struct uio *uio; int flag; { int unit = DCAUNIT(dev); struct dca_softc *sc; struct tty *tp; int error, of; sc = dca_cd.cd_devs[unit]; tp = sc->sc_tty; of = sc->sc_oflows; error = (*tp->t_linesw->l_read)(tp, uio, flag); /* * XXX hardly a reasonable thing to do, but reporting overflows * at interrupt time just exacerbates the problem. */ if (sc->sc_oflows != of) log(LOG_WARNING, "%s: silo overflow\n", sc->sc_dev.dv_xname); return (error); } int dcawrite(dev, uio, flag) dev_t dev; struct uio *uio; int flag; { struct dca_softc *sc = dca_cd.cd_devs[DCAUNIT(dev)]; struct tty *tp = sc->sc_tty; return ((*tp->t_linesw->l_write)(tp, uio, flag)); } int dcapoll(dev, events, p) dev_t dev; int events; struct proc *p; { struct dca_softc *sc = dca_cd.cd_devs[DCAUNIT(dev)]; struct tty *tp = sc->sc_tty; return ((*tp->t_linesw->l_poll)(tp, events, p)); } struct tty * dcatty(dev) dev_t dev; { struct dca_softc *sc = dca_cd.cd_devs[DCAUNIT(dev)]; return (sc->sc_tty); } int dcaintr(arg) void *arg; { struct dca_softc *sc = arg; #ifdef KGDB int unit = sc->sc_dev.dv_unit; #endif struct dcadevice *dca = sc->sc_dca; struct tty *tp = sc->sc_tty; u_char code; int iflowdone = 0; /* * If interrupts aren't enabled, then the interrupt can't * be for us. */ if ((dca->dca_ic & (IC_IR|IC_IE)) != (IC_IR|IC_IE)) return (0); for (;;) { code = dca->dca_iir; #ifdef DEBUG dcaintrcount[code & IIR_IMASK]++; #endif switch (code & IIR_IMASK) { case IIR_NOPEND: return (1); case IIR_RXTOUT: case IIR_RXRDY: /* do time-critical read in-line */ /* * Process a received byte. Inline for speed... */ #ifdef KGDB #define RCVBYTE() \ code = dca->dca_data; \ if (tp == NULL || (tp->t_state & TS_ISOPEN) == 0) { \ int maj; \ maj = cdevsw_lookup_major(&dca_cdevsw); \ if (code == FRAME_END && \ kgdb_dev == makedev(maj, unit)) \ kgdb_connect(0); /* trap into kgdb */ \ } else \ (*tp->t_linesw->l_rint)(code, tp) #else #define RCVBYTE() \ code = dca->dca_data; \ if (tp != NULL && (tp->t_state & TS_ISOPEN) != 0) \ (*tp->t_linesw->l_rint)(code, tp) #endif RCVBYTE(); if (sc->sc_flags & DCA_HASFIFO) { #ifdef DEBUG int fifocnt = 1; #endif while ((code = dca->dca_lsr) & LSR_RCV_MASK) { if (code == LSR_RXRDY) { RCVBYTE(); } else dcaeint(sc, code); #ifdef DEBUG fifocnt++; #endif } #ifdef DEBUG if (fifocnt > 16) fifoin[0]++; else fifoin[fifocnt]++; #endif } if (!iflowdone && tp != NULL && (tp->t_cflag&CRTS_IFLOW) && tp->t_rawq.c_cc > TTYHOG/2) { dca->dca_mcr &= ~MCR_RTS; iflowdone = 1; } break; case IIR_TXRDY: if (tp == NULL) break; tp->t_state &=~ (TS_BUSY|TS_FLUSH); (*tp->t_linesw->l_start)(tp); break; case IIR_RLS: dcaeint(sc, dca->dca_lsr); break; default: if (code & IIR_NOPEND) return (1); log(LOG_WARNING, "%s: weird interrupt: 0x%x\n", sc->sc_dev.dv_xname, code); /* fall through */ case IIR_MLSC: dcamint(sc); break; } } } void dcaeint(sc, stat) struct dca_softc *sc; int stat; { struct tty *tp = sc->sc_tty; struct dcadevice *dca = sc->sc_dca; int c; c = dca->dca_data; if (tp == NULL || (tp->t_state & TS_ISOPEN) == 0) { #ifdef KGDB /* we don't care about parity errors */ if (((stat & (LSR_BI|LSR_FE|LSR_PE)) == LSR_PE) && kgdb_dev == makedev(cdevsw_lookup_major(&dca_cdevsw), sc->sc_hd->hp_unit) && c == FRAME_END) kgdb_connect(0); /* trap into kgdb */ #endif return; } #ifdef DDB if ((sc->sc_flags & DCA_ISCONSOLE) && (stat & LSR_BI)) { Debugger(); return; } #endif if (stat & (LSR_BI | LSR_FE)) c |= TTY_FE; else if (stat & LSR_PE) c |= TTY_PE; else if (stat & LSR_OE) sc->sc_oflows++; (*tp->t_linesw->l_rint)(c, tp); } void dcamint(sc) struct dca_softc *sc; { struct tty *tp = sc->sc_tty; struct dcadevice *dca = sc->sc_dca; u_char stat; stat = dca->dca_msr; #ifdef DEBUG dcamintcount[stat & 0xf]++; #endif if (tp == NULL) return; if ((stat & MSR_DDCD) && (sc->sc_flags & DCA_SOFTCAR) == 0) { if (stat & MSR_DCD) (void)(*tp->t_linesw->l_modem)(tp, 1); else if ((*tp->t_linesw->l_modem)(tp, 0) == 0) dca->dca_mcr &= ~(MCR_DTR | MCR_RTS); } /* * CTS change. * If doing HW output flow control start/stop output as appropriate. */ if ((stat & MSR_DCTS) && (tp->t_state & TS_ISOPEN) && (tp->t_cflag & CCTS_OFLOW)) { if (stat & MSR_CTS) { tp->t_state &=~ TS_TTSTOP; dcastart(tp); } else { tp->t_state |= TS_TTSTOP; } } } int dcaioctl(dev, cmd, data, flag, p) dev_t dev; u_long cmd; caddr_t data; int flag; struct proc *p; { int unit = DCAUNIT(dev); struct dca_softc *sc = dca_cd.cd_devs[unit]; struct tty *tp = sc->sc_tty; struct dcadevice *dca = sc->sc_dca; int error; error = (*tp->t_linesw->l_ioctl)(tp, cmd, data, flag, p); if (error != EPASSTHROUGH) return (error); error = ttioctl(tp, cmd, data, flag, p); if (error != EPASSTHROUGH) return (error); switch (cmd) { case TIOCSBRK: dca->dca_cfcr |= CFCR_SBREAK; break; case TIOCCBRK: dca->dca_cfcr &= ~CFCR_SBREAK; break; case TIOCSDTR: (void) dcamctl(sc, MCR_DTR | MCR_RTS, DMBIS); break; case TIOCCDTR: (void) dcamctl(sc, MCR_DTR | MCR_RTS, DMBIC); break; case TIOCMSET: (void) dcamctl(sc, *(int *)data, DMSET); break; case TIOCMBIS: (void) dcamctl(sc, *(int *)data, DMBIS); break; case TIOCMBIC: (void) dcamctl(sc, *(int *)data, DMBIC); break; case TIOCMGET: *(int *)data = dcamctl(sc, 0, DMGET); break; case TIOCGFLAGS: { int bits = 0; if (sc->sc_flags & DCA_SOFTCAR) bits |= TIOCFLAG_SOFTCAR; if (tp->t_cflag & CLOCAL) bits |= TIOCFLAG_CLOCAL; *(int *)data = bits; break; } case TIOCSFLAGS: { int userbits; error = suser(p->p_ucred, &p->p_acflag); if (error) return (EPERM); userbits = *(int *)data; if ((userbits & TIOCFLAG_SOFTCAR) || (sc->sc_flags & DCA_ISCONSOLE)) sc->sc_flags |= DCA_SOFTCAR; if (userbits & TIOCFLAG_CLOCAL) tp->t_cflag |= CLOCAL; break; } default: return (EPASSTHROUGH); } return (0); } int dcaparam(tp, t) struct tty *tp; struct termios *t; { int unit = DCAUNIT(tp->t_dev); struct dca_softc *sc = dca_cd.cd_devs[unit]; struct dcadevice *dca = sc->sc_dca; int cfcr, cflag = t->c_cflag; int ospeed = ttspeedtab(t->c_ospeed, dcaspeedtab); int s; /* check requested parameters */ if (ospeed < 0 || (t->c_ispeed && t->c_ispeed != t->c_ospeed)) return (EINVAL); switch (cflag & CSIZE) { case CS5: cfcr = CFCR_5BITS; break; case CS6: cfcr = CFCR_6BITS; break; case CS7: cfcr = CFCR_7BITS; break; case CS8: default: /* XXX gcc whines about cfcr being unitialized... */ cfcr = CFCR_8BITS; break; } if (cflag & PARENB) { cfcr |= CFCR_PENAB; if ((cflag & PARODD) == 0) cfcr |= CFCR_PEVEN; } if (cflag & CSTOPB) cfcr |= CFCR_STOPB; s = spltty(); if (ospeed == 0) (void) dcamctl(sc, 0, DMSET); /* hang up line */ /* * Set the FIFO threshold based on the receive speed, if we * are changing it. */ if (tp->t_ispeed != t->c_ispeed) { if (sc->sc_flags & DCA_HASFIFO) dca->dca_fifo = FIFO_ENABLE | (t->c_ispeed <= 1200 ? FIFO_TRIGGER_1 : FIFO_TRIGGER_14); } if (ospeed != 0) { dca->dca_cfcr |= CFCR_DLAB; dca->dca_data = ospeed & 0xFF; dca->dca_ier = ospeed >> 8; dca->dca_cfcr = cfcr; } else dca->dca_cfcr = cfcr; /* and copy to tty */ tp->t_ispeed = t->c_ispeed; tp->t_ospeed = t->c_ospeed; tp->t_cflag = cflag; dca->dca_ier = IER_ERXRDY | IER_ETXRDY | IER_ERLS | IER_EMSC; dca->dca_mcr |= MCR_IEN; splx(s); return (0); } void dcastart(tp) struct tty *tp; { int s, c, unit = DCAUNIT(tp->t_dev); struct dca_softc *sc = dca_cd.cd_devs[unit]; struct dcadevice *dca = sc->sc_dca; s = spltty(); if (tp->t_state & (TS_TIMEOUT|TS_TTSTOP)) goto out; if (tp->t_outq.c_cc <= tp->t_lowat) { if (tp->t_state & TS_ASLEEP) { tp->t_state &= ~TS_ASLEEP; wakeup((caddr_t)&tp->t_outq); } if (tp->t_outq.c_cc == 0) goto out; selwakeup(&tp->t_wsel); } if (dca->dca_lsr & LSR_TXRDY) { tp->t_state |= TS_BUSY; if (sc->sc_flags & DCA_HASFIFO) { for (c = 0; c < 16 && tp->t_outq.c_cc; ++c) dca->dca_data = getc(&tp->t_outq); #ifdef DEBUG if (c > 16) fifoout[0]++; else fifoout[c]++; #endif } else dca->dca_data = getc(&tp->t_outq); } out: splx(s); } /* * Stop output on a line. */ /*ARGSUSED*/ void dcastop(tp, flag) struct tty *tp; int flag; { int s; s = spltty(); if (tp->t_state & TS_BUSY) if ((tp->t_state & TS_TTSTOP) == 0) tp->t_state |= TS_FLUSH; splx(s); } int dcamctl(sc, bits, how) struct dca_softc *sc; int bits, how; { struct dcadevice *dca = sc->sc_dca; int s; /* * Always make sure MCR_IEN is set (unless setting to 0) */ #ifdef KGDB if (how == DMSET && kgdb_dev == makedev(cdevsw_lookup_major(&dca_cdevsw), sc->sc_hd->hp_unit)) bits |= MCR_IEN; else #endif if (how == DMBIS || (how == DMSET && bits)) bits |= MCR_IEN; else if (how == DMBIC) bits &= ~MCR_IEN; s = spltty(); switch (how) { case DMSET: dca->dca_mcr = bits; break; case DMBIS: dca->dca_mcr |= bits; break; case DMBIC: dca->dca_mcr &= ~bits; break; case DMGET: bits = dca->dca_msr; break; } splx(s); return (bits); } void dcainit(dca, rate) struct dcadevice *dca; int rate; { int s; short stat; s = splhigh(); dca->dca_reset = 0xFF; DELAY(100); dca->dca_ic = IC_IE; dca->dca_cfcr = CFCR_DLAB; rate = ttspeedtab(rate, dcaspeedtab); dca->dca_data = rate & 0xFF; dca->dca_ier = rate >> 8; dca->dca_cfcr = CFCR_8BITS; dca->dca_ier = IER_ERXRDY | IER_ETXRDY; dca->dca_fifo = FIFO_ENABLE | FIFO_RCV_RST | FIFO_XMT_RST | FIFO_TRIGGER_1; dca->dca_mcr = MCR_DTR | MCR_RTS; DELAY(100); stat = dca->dca_iir; splx(s); } /* * Following are all routines needed for DCA to act as console */ int dcacnattach(bus_space_tag_t bst, bus_addr_t addr, int scode) { bus_space_handle_t bsh; caddr_t va; struct dcadevice *dca; #ifdef KGDB extern const struct cdevsw ctty_cdevsw; #endif if (bus_space_map(bst, addr, DIOCSIZE, 0, &bsh)) return (1); va = bus_space_vaddr(bst, bsh); dca = (struct dcadevice *)va; switch (dca->dca_id) { #ifdef CONSCODE case DCAID0: case DCAID1: #endif case DCAREMID0: case DCAREMID1: break; default: bus_space_unmap(bst, bsh, DIOCSIZE); return (1); } dcainit(dca, dcadefaultrate); dcaconsinit = 1; dcaconscode = scode; dca_cn = dca; /* initialize required fields */ cn_tab = &dca_cons; cn_tab->cn_dev = makedev(cdevsw_lookup_major(&dca_cdevsw), 0); #ifdef KGDB if (cdevsw_lookup(kgdb_dev) == &ctty_cdevsw) kgdb_dev = makedev(maj, minor(kgdb_dev)); #endif return (0); } /* ARGSUSED */ int dcacngetc(dev) dev_t dev; { u_char stat; int c, s; #ifdef lint stat = dev; if (stat) return (0); #endif s = splhigh(); while (((stat = dca_cn->dca_lsr) & LSR_RXRDY) == 0) ; c = dca_cn->dca_data; stat = dca_cn->dca_iir; splx(s); return (c); } /* * Console kernel output character routine. */ /* ARGSUSED */ void dcacnputc(dev, c) dev_t dev; int c; { int timo; u_char stat; int s = splhigh(); #ifdef lint stat = dev; if (stat) return; #endif if (dcaconsinit == 0) { dcainit(dca_cn, dcadefaultrate); dcaconsinit = 1; } /* wait for any pending transmission to finish */ timo = 50000; while (((stat = dca_cn->dca_lsr) & LSR_TXRDY) == 0 && --timo) ; dca_cn->dca_data = c; /* wait for this transmission to complete */ timo = 1500000; while (((stat = dca_cn->dca_lsr) & LSR_TXRDY) == 0 && --timo) ; /* clear any interrupts generated by this transmission */ stat = dca_cn->dca_iir; splx(s); }