summaryrefslogtreecommitdiff
path: root/sys/arch/sparc/dev/spif.c
diff options
context:
space:
mode:
Diffstat (limited to 'sys/arch/sparc/dev/spif.c')
-rw-r--r--sys/arch/sparc/dev/spif.c1047
1 files changed, 1047 insertions, 0 deletions
diff --git a/sys/arch/sparc/dev/spif.c b/sys/arch/sparc/dev/spif.c
new file mode 100644
index 00000000000..72463efa2ff
--- /dev/null
+++ b/sys/arch/sparc/dev/spif.c
@@ -0,0 +1,1047 @@
+/* $OpenBSD: spif.c,v 1.1 1999/02/01 00:30:42 jason Exp $ */
+
+/*
+ * Copyright (c) 1999 Jason L. Wright (jason@thought.net)
+ * 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 Jason L. Wright
+ * 4. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
+ */
+
+/*
+ * Driver for the SUNW,spif: 8 serial, 1 parallel sbus board
+ * based heavily on Iain Hibbert's driver for the MAGMA driver
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/proc.h>
+#include <sys/device.h>
+#include <sys/kernel.h>
+#include <sys/file.h>
+#include <sys/errno.h>
+#include <sys/ioctl.h>
+#include <sys/mbuf.h>
+#include <sys/socket.h>
+#include <sys/syslog.h>
+#include <sys/malloc.h>
+#include <sys/tty.h>
+#include <sys/conf.h>
+
+#include <machine/autoconf.h>
+#include <sparc/cpu.h>
+#include <sparc/sparc/cpuvar.h>
+#include <sparc/dev/sbusvar.h>
+#include <sparc/dev/spifreg.h>
+#include <sparc/dev/spifvar.h>
+
+#if PIL_TTY == 1
+# define IE_MSOFT IE_L1
+#elif PIL_TTY == 4
+# define IE_MSOFT IE_L4
+#elif PIL_TTY == 6
+# define IE_MSOFT IE_L6
+#else
+# error "no suitable software interrupt bit"
+#endif
+
+/*
+ * useful macros
+ */
+#define SET(t, f) ((t) |= (f))
+#define CLR(t, f) ((t) &= ~(f))
+#define ISSET(t, f) ((t) & (f))
+
+int spifmatch __P((struct device *, void *, void *));
+void spifattach __P((struct device *, struct device *, void *));
+
+int sttymatch __P((struct device *, void *, void *));
+void sttyattach __P((struct device *, struct device *, void *));
+int sttyopen __P((dev_t, int, int, struct proc *));
+int sttyclose __P((dev_t, int, int, struct proc *));
+int sttyread __P((dev_t, struct uio *, int));
+int sttywrite __P((dev_t, struct uio *, int));
+int sttyioctl __P((dev_t, u_long, caddr_t, int, struct proc *));
+int sttystop __P((struct tty *, int));
+int spifstcintr __P((void *));
+int spifsoftintr __P((void *));
+int stty_param __P((struct tty *, struct termios *));
+struct tty *sttytty __P((dev_t));
+int stty_modem_control __P((struct stty_port *, int, int));
+static __inline void spif_write_ccr __P((struct stcregs *, u_int8_t));
+int spif_compute_baud __P((speed_t, int, u_int8_t *, u_int8_t *));
+void stty_start __P((struct tty *));
+
+int sbppmatch __P((struct device *, void *, void *));
+void sbppattach __P((struct device *, struct device *, void *));
+int sbppopen __P((dev_t, int, int, struct proc *));
+int sbppclose __P((dev_t, int, int, struct proc *));
+int sbppread __P((dev_t, struct uio *, int));
+int sbppwrite __P((dev_t, struct uio *, int));
+int sbpp_rw __P((dev_t, struct uio *));
+int spifppcintr __P((void *));
+int sbppselect __P((dev_t, int, struct proc *));
+int sbppioctl __P((dev_t, u_long, caddr_t, int, struct proc *));
+
+struct cfattach spif_ca = {
+ sizeof (struct spif_softc), spifmatch, spifattach
+};
+
+struct cfdriver spif_cd = {
+ NULL, "spif", DV_IFNET
+};
+
+struct cfattach stty_ca = {
+ sizeof(struct stty_softc), sttymatch, sttyattach
+};
+
+struct cfdriver stty_cd = {
+ NULL, "stty", DV_TTY
+};
+
+struct cfattach sbpp_ca = {
+ sizeof(struct sbpp_softc), sbppmatch, sbppattach
+};
+
+struct cfdriver sbpp_cd = {
+ NULL, "sbpp", DV_DULL
+};
+
+int
+spifmatch(parent, vcf, aux)
+ struct device *parent;
+ void *vcf, *aux;
+{
+ struct cfdata *cf = vcf;
+ struct confargs *ca = aux;
+ register struct romaux *ra = &ca->ca_ra;
+
+ if (strcmp(cf->cf_driver->cd_name, ra->ra_name) &&
+ strcmp("SUNW,spif", ra->ra_name))
+ return (0);
+ return (1);
+}
+
+void
+spifattach(parent, self, aux)
+ struct device *parent, *self;
+ void *aux;
+{
+ struct spif_softc *sc = (struct spif_softc *)self;
+ struct confargs *ca = aux;
+ int stcpri, ppcpri;
+
+ if (ca->ca_ra.ra_nintr != 2) {
+ printf(": expected 2 interrupts, got %d\n",
+ ca->ca_ra.ra_nintr);
+ return;
+ }
+ stcpri = ca->ca_ra.ra_intr[SERIAL_INTR].int_pri;
+ ppcpri = ca->ca_ra.ra_intr[PARALLEL_INTR].int_pri;
+
+ if (ca->ca_ra.ra_nreg != 1) {
+ printf(": expected %d registers, got %d\n",
+ 1, ca->ca_ra.ra_nreg);
+ return;
+ }
+ sc->sc_regs = mapiodev(ca->ca_ra.ra_reg, 0, ca->ca_ra.ra_len);
+
+ sc->sc_node = ca->ca_ra.ra_node;
+
+ sc->sc_rev = getpropint(sc->sc_node, "revlev", 0);
+
+ sc->sc_osc = getpropint(sc->sc_node, "verosc", 0);
+ switch (sc->sc_osc) {
+ case SPIF_OSC10:
+ sc->sc_osc = 10;
+ break;
+ case SPIF_OSC9:
+ default:
+ sc->sc_osc = 9;
+ break;
+ }
+
+ sc->sc_nser = 8;
+ sc->sc_npar = 1;
+
+ sc->sc_rev2 = sc->sc_regs->stc.gfrcr;
+ sc->sc_regs->stc.gsvr = 0;
+
+ spif_write_ccr(&sc->sc_regs->stc, CD180_CCR_RESET | CD180_CCR_RESETALL);
+ while (sc->sc_regs->stc.gsvr != 0xff);
+ while (sc->sc_regs->stc.gfrcr != sc->sc_rev2);
+
+ sc->sc_regs->stc.gsvr = 0;
+ sc->sc_regs->stc.msmr = SPIF_MSMR;
+ sc->sc_regs->stc.tsmr = SPIF_TSMR;
+ sc->sc_regs->stc.rsmr = SPIF_RSMR;
+ sc->sc_regs->stc.pprh = SPIF_PPRH;
+ sc->sc_regs->stc.pprl = SPIF_PPRL;
+
+ printf(": rev %d chiprev %d osc %dMhz stcpri %d ppcpri %d softpri %d\n",
+ sc->sc_rev, sc->sc_rev2, sc->sc_osc, stcpri, ppcpri, PIL_TTY);
+
+ if (sc->sc_osc == 10)
+ sc->sc_osc = 10000000;
+ else
+ sc->sc_osc = 9830400;
+
+ (void)config_found(self, sttymatch, NULL);
+ (void)config_found(self, sbppmatch, NULL);
+
+ sc->sc_ppcih.ih_fun = spifppcintr;
+ sc->sc_ppcih.ih_arg = sc;
+ intr_establish(ppcpri, &sc->sc_ppcih);
+
+ sc->sc_stcih.ih_fun = spifstcintr;
+ sc->sc_stcih.ih_arg = sc;
+ intr_establish(stcpri, &sc->sc_stcih);
+
+ sc->sc_softih.ih_fun = spifsoftintr;
+ sc->sc_softih.ih_arg = sc;
+ intr_establish(PIL_TTY, &sc->sc_softih);
+}
+
+int
+sttymatch(parent, vcf, aux)
+ struct device *parent;
+ void *vcf, *aux;
+{
+ struct spif_softc *sc = (struct spif_softc *)parent;
+
+ return (aux == sttymatch && sc->sc_ttys == NULL);
+}
+
+void
+sttyattach(parent, dev, aux)
+ struct device *parent, *dev;
+ void *aux;
+{
+ struct spif_softc *sc = (struct spif_softc *)parent;
+ struct stty_softc *ssc = (struct stty_softc *)dev;
+ int port;
+
+ sc->sc_ttys = ssc;
+
+ for (port = 0; port < sc->sc_nser; port++) {
+ struct stty_port *sp = &ssc->sc_port[port];
+ struct tty *tp;
+
+ tp = ttymalloc();
+ if (tp == NULL)
+ break;
+ tty_attach(tp);
+
+ tp->t_oproc = stty_start;
+ tp->t_param = stty_param;
+
+ sp->sp_tty = tp;
+ sp->sp_sc = sc;
+ sp->sp_channel = port;
+
+ sp->sp_rbuf = malloc(STTY_RBUF_SIZE, M_DEVBUF, M_NOWAIT);
+ if(sp->sp_rbuf == NULL)
+ break;
+
+ sp->sp_rend = sp->sp_rbuf + STTY_RBUF_SIZE;
+ }
+
+ ssc->sc_nports = port;
+
+ printf(": %d tty%s\n", port, port == 1 ? "" : "s");
+}
+
+int
+sttyopen(dev, flags, mode, p)
+ dev_t dev;
+ int flags;
+ int mode;
+ struct proc *p;
+{
+ struct spif_softc *csc;
+ struct stty_softc *sc;
+ struct stty_port *sp;
+ struct tty *tp;
+ int card = SPIF_CARD(dev);
+ int port = SPIF_PORT(dev);
+ int s;
+
+ if (card >= stty_cd.cd_ndevs || card >= spif_cd.cd_ndevs)
+ return (ENXIO);
+
+ sc = stty_cd.cd_devs[card];
+ csc = spif_cd.cd_devs[card];
+ if (sc == NULL)
+ return (ENXIO);
+
+ if (port >= sc->sc_nports)
+ return (ENXIO);
+
+ sp = &sc->sc_port[port];
+ tp = sp->sp_tty;
+ tp->t_dev = dev;
+
+ if (!ISSET(tp->t_state, TS_ISOPEN)) {
+ SET(tp->t_state, TS_WOPEN);
+
+ ttychars(tp);
+ tp->t_iflag = TTYDEF_IFLAG;
+ tp->t_oflag = TTYDEF_OFLAG;
+ tp->t_cflag = TTYDEF_CFLAG;
+ if (ISSET(sp->sp_openflags, TIOCFLAG_CLOCAL))
+ SET(tp->t_cflag, CLOCAL);
+ if (ISSET(sp->sp_openflags, TIOCFLAG_CRTSCTS))
+ SET(tp->t_cflag, CRTSCTS);
+ if (ISSET(sp->sp_openflags, TIOCFLAG_MDMBUF))
+ SET(tp->t_cflag, MDMBUF);
+ tp->t_lflag = TTYDEF_LFLAG;
+ tp->t_ispeed = tp->t_ospeed = TTYDEF_SPEED;
+
+ sp->sp_rput = sp->sp_rget = sp->sp_rbuf;
+
+ s = spltty();
+
+ csc->sc_regs->stc.car = sp->sp_channel;
+ spif_write_ccr(&csc->sc_regs->stc,
+ CD180_CCR_RESET | CD180_CCR_RESETCHAN);
+
+ stty_param(tp, &tp->t_termios);
+
+ ttsetwater(tp);
+
+ csc->sc_regs->stc.srer = CD180_SRER_DSR | CD180_SRER_RXD;
+
+ if (ISSET(sp->sp_openflags, TIOCFLAG_SOFTCAR) || sp->sp_carrier)
+ SET(tp->t_state, TS_CARR_ON);
+ else
+ CLR(tp->t_state, TS_CARR_ON);
+ }
+ else if (ISSET(tp->t_state, TS_XCLUDE) && p->p_ucred->cr_uid != 0) {
+ return (EBUSY);
+ } else {
+ s = spltty();
+ }
+
+ if (!ISSET(flags, O_NONBLOCK)) {
+ while (!ISSET(tp->t_cflag, CLOCAL) &&
+ !ISSET(tp->t_state, TS_CARR_ON)) {
+ int error;
+
+ SET(tp->t_state, TS_WOPEN);
+ error = ttysleep(tp, &tp->t_rawq, TTIPRI | PCATCH,
+ "sttycd", 0);
+ if (error != 0) {
+ splx(s);
+ CLR(tp->t_state, TS_WOPEN);
+ return (error);
+ }
+ }
+ }
+
+ splx(s);
+
+ return ((*linesw[tp->t_line].l_open)(dev, tp));
+}
+
+int
+sttyclose(dev, flags, mode, p)
+ dev_t dev;
+ int flags;
+ int mode;
+ struct proc *p;
+{
+ struct stty_softc *sc = stty_cd.cd_devs[SPIF_CARD(dev)];
+ struct stty_port *sp = &sc->sc_port[SPIF_PORT(dev)];
+ struct spif_softc *csc = sp->sp_sc;
+ struct tty *tp = sp->sp_tty;
+ int port = SPIF_PORT(dev);
+ int s;
+
+ (*linesw[tp->t_line].l_close)(tp, flags);
+ s = spltty();
+
+ if (ISSET(tp->t_cflag, HUPCL) || !ISSET(tp->t_state, TS_ISOPEN)) {
+ csc->sc_regs->stc.car = port;
+ csc->sc_regs->stc.ccr = CD180_CCR_RESET | CD180_CCR_RESETCHAN;
+ }
+
+ splx(s);
+ ttyclose(tp);
+ return (0);
+}
+
+int
+sttyioctl(dev, cmd, data, flags, p)
+ dev_t dev;
+ u_long cmd;
+ caddr_t data;
+ int flags;
+ struct proc *p;
+{
+ struct stty_softc *stc = stty_cd.cd_devs[SPIF_CARD(dev)];
+ struct stty_port *sp = &stc->sc_port[SPIF_PORT(dev)];
+ struct spif_softc *sc = sp->sp_sc;
+ struct tty *tp = sp->sp_tty;
+ int error;
+
+ error = (*linesw[tp->t_line].l_ioctl)(tp, cmd, data, flags, p);
+ if (error >= 0)
+ return (error);
+
+ error = ttioctl(tp, cmd, data, flags, p);
+ if (error >= 0)
+ return (error);
+
+ error = 0;
+
+ switch (cmd) {
+ case TIOCSBRK:
+ SET(sp->sp_flags, STTYF_SET_BREAK);
+ sc->sc_regs->stc.car = sp->sp_channel;
+ sc->sc_regs->stc.srer |= CD180_SRER_TXD;
+ break;
+ case TIOCCBRK:
+ SET(sp->sp_flags, STTYF_CLR_BREAK);
+ sc->sc_regs->stc.car = sp->sp_channel;
+ sc->sc_regs->stc.srer |= CD180_SRER_TXD;
+ break;
+ case TIOCSDTR:
+ stty_modem_control(sp, TIOCM_DTR, DMBIS);
+ break;
+ case TIOCCDTR:
+ stty_modem_control(sp, TIOCM_DTR, DMBIC);
+ break;
+ case TIOCMBIS:
+ stty_modem_control(sp, *((int *)data), DMBIS);
+ break;
+ case TIOCMBIC:
+ stty_modem_control(sp, *((int *)data), DMBIC);
+ break;
+ case TIOCMGET:
+ *((int *)data) = stty_modem_control(sp, 0, DMGET);
+ break;
+ case TIOCGFLAGS:
+ *((int *)data) = sp->sp_openflags;
+ break;
+ case TIOCSFLAGS:
+ if (suser(p->p_ucred, &p->p_acflag))
+ error = EPERM;
+ else
+ sp->sp_openflags = *((int *)data) &
+ (TIOCFLAG_SOFTCAR | TIOCFLAG_CLOCAL |
+ TIOCFLAG_CRTSCTS | TIOCFLAG_MDMBUF);
+ break;
+ default:
+ error = ENOTTY;
+ }
+
+ return (error);
+}
+
+int
+stty_modem_control(sp, bits, how)
+ struct stty_port *sp;
+ int bits, how;
+{
+ struct spif_softc *csc = sp->sp_sc;
+ struct tty *tp = sp->sp_tty;
+ int s, msvr;
+
+ s = spltty();
+ csc->sc_regs->stc.car = sp->sp_channel;
+
+ switch (how) {
+ case DMGET:
+ bits = TIOCM_LE;
+ if (sp->sp_dtr)
+ bits |= TIOCM_DTR;
+ msvr = csc->sc_regs->stc.msvr;
+ if (ISSET(msvr, CD180_MSVR_DSR))
+ bits |= TIOCM_DSR;
+ if (ISSET(msvr, CD180_MSVR_CD))
+ bits |= TIOCM_CD;
+ if (ISSET(msvr, CD180_MSVR_CTS))
+ bits |= TIOCM_CTS;
+ if (ISSET(msvr, CD180_MSVR_RTS))
+ bits |= TIOCM_RTS;
+ break;
+ case DMSET:
+ if (ISSET(bits, TIOCM_DTR)) {
+ sp->sp_dtr = 1;
+ csc->sc_regs->dtrlatch[sp->sp_channel] = 1;
+ }
+ else {
+ sp->sp_dtr = 0;
+ csc->sc_regs->dtrlatch[sp->sp_channel] = 0;
+ }
+ if (ISSET(bits, TIOCM_RTS))
+ csc->sc_regs->stc.msvr &= ~CD180_MSVR_RTS;
+ else
+ csc->sc_regs->stc.msvr |= CD180_MSVR_RTS;
+ break;
+ case DMBIS:
+ if (ISSET(bits, TIOCM_DTR)) {
+ sp->sp_dtr = 1;
+ csc->sc_regs->dtrlatch[sp->sp_channel] = 1;
+ }
+ if (ISSET(bits, TIOCM_RTS) && !ISSET(tp->t_cflag, CRTSCTS))
+ csc->sc_regs->stc.msvr &= ~CD180_MSVR_RTS;
+ break;
+ case DMBIC:
+ if (ISSET(bits, TIOCM_DTR)) {
+ sp->sp_dtr = 0;
+ csc->sc_regs->dtrlatch[sp->sp_channel] = 0;
+ }
+ if (ISSET(bits, TIOCM_RTS))
+ csc->sc_regs->stc.msvr |= CD180_MSVR_RTS;
+ break;
+ }
+
+ splx(s);
+ return (bits);
+}
+
+int
+stty_param(tp, t)
+ struct tty *tp;
+ struct termios *t;
+{
+ struct stty_softc *st = stty_cd.cd_devs[SPIF_CARD(tp->t_dev)];
+ struct stty_port *sp = &st->sc_port[SPIF_PORT(tp->t_dev)];
+ struct spif_softc *sc = sp->sp_sc;
+ u_int8_t rbprl, rbprh, tbprl, tbprh;
+ int s, opt;
+
+ if (t->c_ospeed &&
+ spif_compute_baud(t->c_ospeed, sc->sc_osc, &tbprl, &tbprh))
+ return (EINVAL);
+
+ if (t->c_ispeed &&
+ spif_compute_baud(t->c_ispeed, sc->sc_osc, &rbprl, &rbprh))
+ return (EINVAL);
+
+ s = spltty();
+
+ /* hang up line if ospeed is zero, otherwise raise DTR */
+ stty_modem_control(sp, TIOCM_DTR,
+ (t->c_ospeed == 0 ? DMBIC : DMBIS));
+
+ sc->sc_regs->stc.car = sp->sp_channel;
+
+ opt = 0;
+ if (ISSET(t->c_cflag, PARENB)) {
+ opt |= CD180_COR1_PARMODE_NORMAL;
+ opt |= (ISSET(t->c_cflag, PARODD) ?
+ CD180_COR1_ODDPAR :
+ CD180_COR1_EVENPAR);
+ }
+ else
+ opt |= CD180_COR1_PARMODE_NO;
+
+ if (!ISSET(t->c_iflag, INPCK))
+ opt |= CD180_COR1_IGNPAR;
+
+ if (ISSET(t->c_cflag, CSTOPB))
+ opt |= CD180_COR1_STOP2;
+
+ switch (t->c_cflag & CSIZE) {
+ case CS5:
+ opt |= CD180_COR1_CS5;
+ break;
+ case CS6:
+ opt |= CD180_COR1_CS6;
+ break;
+ case CS7:
+ opt |= CD180_COR1_CS7;
+ break;
+ default:
+ opt |= CD180_COR1_CS8;
+ break;
+ }
+ sc->sc_regs->stc.cor1 = opt;
+
+ opt = CD180_COR2_ETC;
+ if (ISSET(t->c_cflag, CRTSCTS))
+ opt |= CD180_COR2_CTSAE;
+ sc->sc_regs->stc.cor2 = opt;
+
+ sc->sc_regs->stc.cor3 = STTY_RX_FIFO_THRESHOLD;
+
+ spif_write_ccr(&sc->sc_regs->stc, CD180_CCR_CORCHG |
+ CD180_CCR_CORCHG1 | CD180_CCR_CORCHG2 | CD180_CCR_CORCHG3);
+
+ sc->sc_regs->stc.schr1 = 0x11;
+ sc->sc_regs->stc.schr2 = 0x13;
+ sc->sc_regs->stc.schr3 = 0x11;
+ sc->sc_regs->stc.schr4 = 0x13;
+ sc->sc_regs->stc.rtpr = 0x28;
+
+ sc->sc_regs->stc.mcor1 = CD180_MCOR1_CDZD | STTY_RX_DTR_THRESHOLD;
+ sc->sc_regs->stc.mcor2 = CD180_MCOR2_CDOD;
+
+ if (t->c_ospeed) {
+ sc->sc_regs->stc.tbprh = tbprh;
+ sc->sc_regs->stc.tbprl = tbprl;
+ }
+
+ if (t->c_ispeed) {
+ sc->sc_regs->stc.rbprh = rbprh;
+ sc->sc_regs->stc.rbprl = rbprl;
+ }
+
+ spif_write_ccr(&sc->sc_regs->stc, CD180_CCR_CHANCTL |
+ CD180_CCR_CHAN_TXEN | CD180_CCR_CHAN_RXEN);
+
+ splx(s);
+ return (0);
+}
+
+int
+sttyread(dev, uio, flags)
+ dev_t dev;
+ struct uio *uio;
+ int flags;
+{
+ struct stty_softc *sc = stty_cd.cd_devs[SPIF_CARD(dev)];
+ struct stty_port *sp = &sc->sc_port[SPIF_PORT(dev)];
+ struct tty *tp = sp->sp_tty;
+
+ return ((*linesw[tp->t_line].l_read)(tp, uio, flags));
+}
+
+int
+sttywrite(dev, uio, flags)
+ dev_t dev;
+ struct uio *uio;
+ int flags;
+{
+ struct stty_softc *sc = stty_cd.cd_devs[SPIF_CARD(dev)];
+ struct stty_port *sp = &sc->sc_port[SPIF_PORT(dev)];
+ struct tty *tp = sp->sp_tty;
+
+ return ((*linesw[tp->t_line].l_write)(tp, uio, flags));
+}
+
+struct tty *
+sttytty(dev)
+ dev_t dev;
+{
+ struct stty_softc *sc = stty_cd.cd_devs[SPIF_CARD(dev)];
+ struct stty_port *sp = &sc->sc_port[SPIF_PORT(dev)];
+
+ return (sp->sp_tty);
+}
+
+int
+sttystop(tp, flags)
+ struct tty *tp;
+ int flags;
+{
+ struct stty_softc *sc = stty_cd.cd_devs[SPIF_CARD(tp->t_dev)];
+ struct stty_port *sp = &sc->sc_port[SPIF_PORT(tp->t_dev)];
+ int s;
+
+ s = spltty();
+ if (ISSET(tp->t_state, TS_BUSY)) {
+ if (!ISSET(tp->t_state, TS_TTSTOP))
+ SET(tp->t_state, TS_FLUSH);
+ SET(sp->sp_flags, STTYF_STOP);
+ }
+ splx(s);
+ return (0);
+}
+
+void
+stty_start(tp)
+ struct tty *tp;
+{
+ struct stty_softc *stc = stty_cd.cd_devs[SPIF_CARD(tp->t_dev)];
+ struct stty_port *sp = &stc->sc_port[SPIF_PORT(tp->t_dev)];
+ struct spif_softc *sc = sp->sp_sc;
+ int s;
+
+ s = spltty();
+
+ if (!ISSET(tp->t_state, TS_TTSTOP | TS_TIMEOUT | TS_BUSY)) {
+ if (tp->t_outq.c_cc <= tp->t_lowat) {
+ if (ISSET(tp->t_state, TS_ASLEEP)) {
+ CLR(tp->t_state, TS_ASLEEP);
+ wakeup(&tp->t_outq);
+ }
+ selwakeup(&tp->t_wsel);
+ }
+ if (tp->t_outq.c_cc) {
+ sp->sp_txc = ndqb(&tp->t_outq, 0);
+ sp->sp_txp = tp->t_outq.c_cf;
+ SET(tp->t_state, TS_BUSY);
+ sc->sc_regs->stc.car = sp->sp_channel;
+ sc->sc_regs->stc.srer |= CD180_SRER_TXD;
+ }
+ }
+
+ splx(s);
+}
+
+int
+spifstcintr(vsc)
+ void *vsc;
+{
+ struct spif_softc *sc = (struct spif_softc *)vsc;
+ struct stty_port *sp;
+ u_int8_t channel, ar, *ptr;
+ int needsoft = 0, r = 0, i;
+
+ /*
+ * Receive data service request
+ * (also Receive error service request)
+ */
+ ar = sc->sc_regs->istc.rrar & 7;
+
+ switch (ar) {
+ case CD180_GSVR_RXGOOD:
+ r = 1;
+ channel = (sc->sc_regs->stc.gscr1 >> 2) & 7;
+ sp = &sc->sc_ttys->sc_port[channel];
+ ptr = sp->sp_rput;
+ for (i = sc->sc_regs->stc.rdcr; i > 0; i--) {
+ *ptr++ = 0;
+ *ptr++ = sc->sc_regs->stc.rdr;
+ if (ptr == sp->sp_rend)
+ ptr = sp->sp_rbuf;
+ if (ptr == sp->sp_rget) {
+ if (ptr == sp->sp_rbuf)
+ ptr = sp->sp_rend;
+ ptr -= 2;
+ SET(sp->sp_flags, STTYF_RING_OVERFLOW);
+ break;
+ }
+ }
+ sp->sp_rput = ptr;
+ needsoft = 1;
+ break;
+ case CD180_GSVR_RXEXCEPTION:
+ r = 1;
+ channel = (sc->sc_regs->stc.gscr1 >> 2) & 7;
+ sp = &sc->sc_ttys->sc_port[channel];
+ ptr = sp->sp_rput;
+ *ptr++ = sc->sc_regs->stc.rcsr;
+ *ptr++ = sc->sc_regs->stc.rdr;
+ if (ptr == sp->sp_rend)
+ ptr = sp->sp_rbuf;
+ if (ptr == sp->sp_rget) {
+ if (ptr == sp->sp_rbuf)
+ ptr = sp->sp_rend;
+ ptr -= 2;
+ SET(sp->sp_flags, STTYF_RING_OVERFLOW);
+ break;
+ }
+ sp->sp_rput = ptr;
+ needsoft = 1;
+ break;
+ }
+ sc->sc_regs->stc.eosrr = 0;
+
+ /*
+ * Transmit service request
+ */
+ ar = sc->sc_regs->istc.trar & 7;
+ if (ar == 2) {
+ int cnt = 0;
+
+ r = 1;
+ channel = (sc->sc_regs->stc.gscr1 >> 2) & 7;
+ sp = &sc->sc_ttys->sc_port[channel];
+
+ if (!ISSET(sp->sp_flags, STTYF_STOP)) {
+ if (ISSET(sp->sp_flags, STTYF_SET_BREAK)) {
+ sc->sc_regs->stc.tdr = 0;
+ sc->sc_regs->stc.tdr = 0x81;
+ CLR(sp->sp_flags, STTYF_SET_BREAK);
+ cnt += 2;
+ }
+ if (ISSET(sp->sp_flags, STTYF_CLR_BREAK)) {
+ sc->sc_regs->stc.tdr = 0;
+ sc->sc_regs->stc.tdr = 0x83;
+ CLR(sp->sp_flags, STTYF_CLR_BREAK);
+ cnt += 2;
+ }
+
+ while (sp->sp_txc > 0 && cnt < (CD180_TX_FIFO_SIZE-1)) {
+ u_int8_t ch;
+
+ ch = *sp->sp_txp;
+ sp->sp_txc--;
+ sp->sp_txp++;
+
+ if (ch == 0) {
+ sc->sc_regs->stc.tdr = ch;
+ cnt++;
+ }
+ sc->sc_regs->stc.tdr = ch;
+ cnt++;
+ }
+
+ if (sp->sp_txc == 0 ||
+ ISSET(sp->sp_flags, STTYF_STOP)) {
+ sc->sc_regs->stc.srer &= ~CD180_SRER_TXD;
+ CLR(sp->sp_flags, STTYF_STOP);
+ SET(sp->sp_flags, STTYF_DONE);
+ needsoft = 1;
+ }
+ }
+ }
+ sc->sc_regs->stc.eosrr = 0;
+
+#if 0
+ /*
+ * Modem signal service request
+ */
+ ar = sc->sc_regs->istc.mrar & 7;
+ sc->sc_regs->stc.eosrr = 0;
+#endif
+
+ if (needsoft) {
+#if defined(SUN4M)
+ if (CPU_ISSUN4M)
+ raise(0, PIL_TTY);
+ else
+#endif
+ ienab_bis(IE_MSOFT);
+ }
+ return (r);
+}
+
+int
+spifsoftintr(vsc)
+ void *vsc;
+{
+ struct spif_softc *sc = (struct spif_softc *)vsc;
+ struct stty_softc *stc = sc->sc_ttys;
+ int r = 0, i, data, s, flags;
+ u_int8_t stat;
+ struct stty_port *sp;
+ struct tty *tp;
+
+ if (stc != NULL) {
+ for (i = 0; i < stc->sc_nports; i++) {
+ sp = &stc->sc_port[i];
+ tp = sp->sp_tty;
+
+ if (!ISSET(tp->t_state, TS_ISOPEN))
+ continue;
+
+ while (sp->sp_rget != sp->sp_rput) {
+ stat = sp->sp_rget[0];
+ data = sp->sp_rget[1];
+ if ((sp->sp_rget + 2) == sp->sp_rend)
+ sp->sp_rget = sp->sp_rbuf;
+ else
+ sp->sp_rget = sp->sp_rget + 2;
+
+ if (stat & (CD180_RCSR_BE | CD180_RCSR_FE))
+ data |= TTY_FE;
+
+ if (stat & CD180_RCSR_PE)
+ data |= TTY_PE;
+
+ if (stat & CD180_RCSR_OE)
+ log(LOG_WARNING,
+ "%s%x: fifo overflow\n",
+ stc->sc_dev, i);
+ (*linesw[tp->t_line].l_rint)(data, tp);
+ r = 1;
+ }
+
+ s = splhigh();
+ flags = sp->sp_flags;
+ CLR(sp->sp_flags, STTYF_DONE | STTYF_CDCHG |
+ STTYF_RING_OVERFLOW);
+ splx(s);
+
+ if (ISSET(flags, STTYF_CDCHG)) {
+ (*linesw[tp->t_line].l_modem)(tp,
+ sp->sp_carrier);
+ r = 1;
+ }
+
+ if (ISSET(flags, STTYF_RING_OVERFLOW)) {
+ log(LOG_WARNING, "%s%x: ring overflow\n",
+ stc->sc_dev, i);
+ r = 1;
+ }
+
+ if (ISSET(flags, STTYF_DONE)) {
+ ndflush(&tp->t_outq,
+ sp->sp_txp - tp->t_outq.c_cf);
+ CLR(tp->t_state, TS_BUSY);
+ (*linesw[tp->t_line].l_start)(tp);
+ r = 1;
+ }
+ }
+ }
+
+ return (r);
+}
+
+int
+spif_compute_baud(speed, clock, bprlp, bprhp)
+ speed_t speed;
+ int clock;
+ u_int8_t *bprlp, *bprhp;
+{
+ u_int32_t rate;
+
+ rate = (2 * clock) / (16 * speed);
+ if (rate & 1)
+ rate = (rate >> 1) + 1;
+ else
+ rate = rate >> 1;
+
+ if (rate > 0xffff || rate == 0)
+ return (1);
+
+ *bprlp = rate & 0xff;
+ *bprhp = (rate >> 8) & 0xff;
+ return (0);
+}
+
+int
+sbppmatch(parent, vcf, aux)
+ struct device *parent;
+ void *vcf, *aux;
+{
+ struct spif_softc *sc = (struct spif_softc *)parent;
+
+ return (aux == sbppmatch && sc->sc_bpps == NULL);
+}
+
+void
+sbppattach(parent, dev, aux)
+ struct device *parent, *dev;
+ void *aux;
+{
+ struct spif_softc *sc = (struct spif_softc *)parent;
+ struct sbpp_softc *psc = (struct sbpp_softc *)dev;
+ int port;
+
+ sc->sc_bpps = psc;
+
+ for (port = 0; port < sc->sc_npar; port++) {
+ }
+
+ psc->sc_nports = port;
+ printf(": %d port%s\n", port, port == 1 ? "" : "s");
+}
+
+int
+sbppopen(dev, flags, mode, p)
+ dev_t dev;
+ int flags;
+ int mode;
+ struct proc *p;
+{
+ return (ENXIO);
+}
+
+int
+sbppclose(dev, flags, mode, p)
+ dev_t dev;
+ int flags;
+ int mode;
+ struct proc *p;
+{
+ return (ENXIO);
+}
+
+int
+spifppcintr(v)
+ void *v;
+{
+ return (0);
+}
+
+int
+sbppread(dev, uio, flags)
+ dev_t dev;
+ struct uio *uio;
+ int flags;
+{
+ return (sbpp_rw(dev, uio));
+}
+
+int
+sbppwrite(dev, uio, flags)
+ dev_t dev;
+ struct uio *uio;
+ int flags;
+{
+ return (sbpp_rw(dev, uio));
+}
+
+int
+sbpp_rw(dev, uio)
+ dev_t dev;
+ struct uio *uio;
+{
+ return (ENXIO);
+}
+
+int
+sbppselect(dev, rw, p)
+ dev_t dev;
+ int rw;
+ struct proc *p;
+{
+ return (ENODEV);
+}
+
+int
+sbppioctl(dev, cmd, data, flags, p)
+ dev_t dev;
+ u_long cmd;
+ caddr_t data;
+ int flags;
+ struct proc *p;
+{
+ int error;
+
+ error = ENOTTY;
+
+ return (error);
+}
+
+static __inline void
+spif_write_ccr(stc, val)
+ struct stcregs *stc;
+ u_int8_t val;
+{
+ int tries = 100000;
+
+ while (stc->ccr && tries--);
+ if (tries == 0)
+ printf("CCR: timeout\n");
+ stc->ccr = val;
+}