summaryrefslogtreecommitdiff
path: root/sys/dev/sbus/spif.c
diff options
context:
space:
mode:
authorJason Wright <jason@cvs.openbsd.org>2002-01-13 02:06:47 +0000
committerJason Wright <jason@cvs.openbsd.org>2002-01-13 02:06:47 +0000
commit81ce5687ba5a7876107ea8029e52cdf543a5ac3d (patch)
treea03fed8b271c9a55ef09c7c0846e4fe90b964853 /sys/dev/sbus/spif.c
parentec9c6e9fcaa9decdd1cfe28c4a9575a1690d2bac (diff)
Port of my spif driver to sparc64 (untested)
Diffstat (limited to 'sys/dev/sbus/spif.c')
-rw-r--r--sys/dev/sbus/spif.c1149
1 files changed, 1149 insertions, 0 deletions
diff --git a/sys/dev/sbus/spif.c b/sys/dev/sbus/spif.c
new file mode 100644
index 00000000000..4c8496ba528
--- /dev/null
+++ b/sys/dev/sbus/spif.c
@@ -0,0 +1,1149 @@
+/* $OpenBSD: spif.c,v 1.1 2002/01/13 02:06:45 jason Exp $ */
+
+/*
+ * Copyright (c) 1999-2002 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 cards
+ */
+
+#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 <dev/sbus/sbusvar.h>
+#include <dev/sbus/spifreg.h>
+#include <dev/sbus/spifvar.h>
+
+/*
+ * 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 spifstcintr_mx __P((struct spif_softc *, int *));
+int spifstcintr_tx __P((struct spif_softc *, int *));
+int spifstcintr_rx __P((struct spif_softc *, int *));
+int spifstcintr_rxexception __P((struct spif_softc *, int *));
+void 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));
+void stty_write_ccr __P((struct spif_softc *, u_int8_t));
+int stty_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_DULL
+};
+
+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
+};
+
+/* normal STC access */
+#define STC_WRITE(sc,r,v) \
+ bus_space_write_1((sc)->sc_bustag, (sc)->sc_stch, (r), (v))
+#define STC_READ(sc,r) \
+ bus_space_read_1((sc)->sc_bustag, (sc)->sc_stch, (r))
+
+/* IACK STC access */
+#define ISTC_WRITE(sc,r,v) \
+ bus_space_write_1((sc)->sc_bustag, (sc)->sc_istch, (r), (v))
+#define ISTC_READ(sc,r) \
+ bus_space_read_1((sc)->sc_bustag, (sc)->sc_istch, (r))
+
+/* PPC access */
+#define PPC_WRITE(sc,r,v) \
+ bus_space_write_1((sc)->sc_bustag, (sc)->sc_ppch, (r), (v))
+#define PPC_READ(sc,r) \
+ bus_space_read_1((sc)->sc_bustag, (sc)->sc_ppch, (r))
+
+#define DTR_WRITE(sc,port,v) \
+ do { \
+ sc->sc_ttys->sc_port[(port)].sp_dtr = v; \
+ bus_space_write_1((sc)->sc_bustag, \
+ sc->sc_dtrh, port, (v == 0) ? 1 : 0); \
+ } while (0)
+
+#define DTR_READ(sc,port) ((sc)->sc_ttys->sc_port[(port)].sp_dtr)
+
+int
+spifmatch(parent, vcf, aux)
+ struct device *parent;
+ void *vcf, *aux;
+{
+ struct cfdata *cf = vcf;
+ struct sbus_attach_args *sa = aux;
+
+ if (strcmp(cf->cf_driver->cd_name, sa->sa_name) &&
+ strcmp("SUNW,spif", sa->sa_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 sbus_attach_args *sa = aux;
+
+ if (sa->sa_nintr != 2) {
+ printf(": expected 2 interrupts, got %d\n",
+ sa->sa_nintr);
+ return;
+ }
+
+ if (sa->sa_nreg != 1) {
+ printf(": expected %d registers, got %d\n",
+ 1, sa->sa_nreg);
+ return;
+ }
+
+ sc->sc_bustag = sa->sa_bustag;
+ if (sbus_bus_map(sa->sa_bustag, sa->sa_reg[0].sbr_slot,
+ sa->sa_reg[0].sbr_offset, sa->sa_reg[0].sbr_size,
+ BUS_SPACE_MAP_LINEAR, 0, &sc->sc_regh) != 0) {
+ printf(": can't map registers\n");
+ return;
+ }
+
+ if (bus_space_subregion(sc->sc_bustag, sc->sc_regh,
+ DTR_REG_OFFSET, DTR_REG_LEN, &sc->sc_dtrh) != 0) {
+ printf(": can't map dtr regs\n");
+ return;
+ }
+
+ if (bus_space_subregion(sc->sc_bustag, sc->sc_regh,
+ STC_REG_OFFSET, STC_REG_LEN, &sc->sc_stch) != 0) {
+ printf(": can't map dtr regs\n");
+ return;
+ }
+
+ if (bus_space_subregion(sc->sc_bustag, sc->sc_regh,
+ ISTC_REG_OFFSET, ISTC_REG_LEN, &sc->sc_istch) != 0) {
+ printf(": can't map dtr regs\n");
+ return;
+ }
+
+ if (bus_space_subregion(sc->sc_bustag, sc->sc_regh,
+ PPC_REG_OFFSET, PPC_REG_LEN, &sc->sc_ppch) != 0) {
+ printf(": can't map dtr regs\n");
+ return;
+ }
+
+ sc->sc_ppcih = bus_intr_establish(sa->sa_bustag,
+ sa->sa_intr[PARALLEL_INTR].sbi_pri, IPL_TTY, 0, spifppcintr, sc);
+ if (sc->sc_ppcih == NULL) {
+ printf(": failed to establish ppc interrupt\n");
+ return;
+ }
+
+ sc->sc_stcih = bus_intr_establish(sa->sa_bustag,
+ sa->sa_intr[SERIAL_INTR].sbi_pri, IPL_TTY, 0, spifstcintr, sc);
+ if (sc->sc_stcih == NULL) {
+ printf(": failed to establish stc interrupt\n");
+ return;
+ }
+
+ sc->sc_softih = softintr_establish(IPL_TTY, spifsoftintr, sc);
+ if (sc->sc_softih == NULL) {
+ printf(": can't get soft intr\n");
+ return;
+ }
+
+ sc->sc_node = sa->sa_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 = 10000000;
+ break;
+ case SPIF_OSC9:
+ default:
+ sc->sc_osc = 9830400;
+ break;
+ }
+
+ sc->sc_nser = 8;
+ sc->sc_npar = 1;
+
+ sc->sc_rev2 = STC_READ(sc, STC_GFRCR);
+ STC_WRITE(sc, STC_GSVR, 0);
+
+ stty_write_ccr(sc, CD180_CCR_CMD_RESET | CD180_CCR_RESETALL);
+ while (STC_READ(sc, STC_GSVR) != 0xff);
+ while (STC_READ(sc, STC_GFRCR) != sc->sc_rev2);
+
+ STC_WRITE(sc, STC_PPRH, CD180_PPRH);
+ STC_WRITE(sc, STC_PPRL, CD180_PPRL);
+ STC_WRITE(sc, STC_MSMR, SPIF_MSMR);
+ STC_WRITE(sc, STC_TSMR, SPIF_TSMR);
+ STC_WRITE(sc, STC_RSMR, SPIF_RSMR);
+ STC_WRITE(sc, STC_GSVR, 0);
+ STC_WRITE(sc, STC_GSCR1, 0);
+ STC_WRITE(sc, STC_GSCR2, 0);
+ STC_WRITE(sc, STC_GSCR3, 0);
+
+ printf(": rev %x chiprev %x osc %sMhz\n",
+ sc->sc_rev, sc->sc_rev2, clockfreq(sc->sc_osc));
+
+ (void)config_found(self, sttymatch, NULL);
+ (void)config_found(self, sbppmatch, NULL);
+}
+
+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;
+
+ DTR_WRITE(sc, port, 0);
+
+ 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();
+
+ STC_WRITE(csc, STC_CAR, sp->sp_channel);
+ stty_write_ccr(csc, CD180_CCR_CMD_RESET|CD180_CCR_RESETCHAN);
+ STC_WRITE(csc, STC_CAR, sp->sp_channel);
+
+ stty_param(tp, &tp->t_termios);
+
+ ttsetwater(tp);
+
+ STC_WRITE(csc, STC_SRER, CD180_SRER_CD | 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)) {
+ stty_modem_control(sp, 0, DMSET);
+ STC_WRITE(csc, STC_CAR, port);
+ STC_WRITE(csc, STC_CCR,
+ CD180_CCR_CMD_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);
+ STC_WRITE(sc, STC_CAR, sp->sp_channel);
+ STC_WRITE(sc, STC_SRER,
+ STC_READ(sc, STC_SRER) | CD180_SRER_TXD);
+ break;
+ case TIOCCBRK:
+ SET(sp->sp_flags, STTYF_CLR_BREAK);
+ STC_WRITE(sc, STC_CAR, sp->sp_channel);
+ STC_WRITE(sc, STC_SRER,
+ STC_READ(sc, 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 TIOCMSET:
+ stty_modem_control(sp, *((int *)data), DMSET);
+ 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();
+ STC_WRITE(csc, STC_CAR, sp->sp_channel);
+
+ switch (how) {
+ case DMGET:
+ bits = TIOCM_LE;
+ if (DTR_READ(csc, sp->sp_channel))
+ bits |= TIOCM_DTR;
+ msvr = STC_READ(csc, 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:
+ DTR_WRITE(csc, sp->sp_channel, ISSET(bits, TIOCM_DTR) ? 1 : 0);
+ if (ISSET(bits, TIOCM_RTS))
+ STC_WRITE(csc, STC_MSVR,
+ STC_READ(csc, STC_MSVR) & (~CD180_MSVR_RTS));
+ else
+ STC_WRITE(csc, STC_MSVR,
+ STC_READ(csc, STC_MSVR) | CD180_MSVR_RTS);
+ break;
+ case DMBIS:
+ if (ISSET(bits, TIOCM_DTR))
+ DTR_WRITE(csc, sp->sp_channel, 1);
+ if (ISSET(bits, TIOCM_RTS) && !ISSET(tp->t_cflag, CRTSCTS))
+ STC_WRITE(csc, STC_MSVR,
+ STC_READ(csc, STC_MSVR) & (~CD180_MSVR_RTS));
+ break;
+ case DMBIC:
+ if (ISSET(bits, TIOCM_DTR))
+ DTR_WRITE(csc, sp->sp_channel, 0);
+ if (ISSET(bits, TIOCM_RTS))
+ STC_WRITE(csc, STC_MSVR,
+ STC_READ(csc, 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 &&
+ stty_compute_baud(t->c_ospeed, sc->sc_osc, &tbprl, &tbprh))
+ return (EINVAL);
+
+ if (t->c_ispeed &&
+ stty_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));
+
+ STC_WRITE(sc, 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;
+ }
+ STC_WRITE(sc, STC_COR1, opt);
+ stty_write_ccr(sc, CD180_CCR_CMD_COR|CD180_CCR_CORCHG1);
+
+ opt = CD180_COR2_ETC;
+ if (ISSET(t->c_cflag, CRTSCTS))
+ opt |= CD180_COR2_CTSAE;
+ STC_WRITE(sc, STC_COR2, opt);
+ stty_write_ccr(sc, CD180_CCR_CMD_COR|CD180_CCR_CORCHG2);
+
+ STC_WRITE(sc, STC_COR3, STTY_RX_FIFO_THRESHOLD);
+ stty_write_ccr(sc, CD180_CCR_CMD_COR|CD180_CCR_CORCHG3);
+
+ STC_WRITE(sc, STC_SCHR1, 0x11);
+ STC_WRITE(sc, STC_SCHR2, 0x13);
+ STC_WRITE(sc, STC_SCHR3, 0x11);
+ STC_WRITE(sc, STC_SCHR4, 0x13);
+ STC_WRITE(sc, STC_RTPR, 0x12);
+
+ STC_WRITE(sc, STC_MCOR1, CD180_MCOR1_CDZD | STTY_RX_DTR_THRESHOLD);
+ STC_WRITE(sc, STC_MCOR2, CD180_MCOR2_CDOD);
+ STC_WRITE(sc, STC_MCR, 0);
+
+ if (t->c_ospeed) {
+ STC_WRITE(sc, STC_TBPRH, tbprh);
+ STC_WRITE(sc, STC_TBPRL, tbprl);
+ }
+
+ if (t->c_ispeed) {
+ STC_WRITE(sc, STC_RBPRH, rbprh);
+ STC_WRITE(sc, STC_RBPRL, rbprl);
+ }
+
+ stty_write_ccr(sc, CD180_CCR_CMD_CHAN |
+ CD180_CCR_CHAN_TXEN | CD180_CCR_CHAN_RXEN);
+
+ sp->sp_carrier = STC_READ(sc, STC_MSVR) & CD180_MSVR_CD;
+
+ 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);
+ STC_WRITE(sc, STC_CAR, sp->sp_channel);
+ STC_WRITE(sc, STC_SRER,
+ STC_READ(sc, STC_SRER) | CD180_SRER_TXD);
+ }
+ }
+
+ splx(s);
+}
+
+int
+spifstcintr_rxexception(sc, needsoftp)
+ struct spif_softc *sc;
+ int *needsoftp;
+{
+ struct stty_port *sp;
+ u_int8_t channel, *ptr;
+ int cnt;
+
+ channel = CD180_GSCR_CHANNEL(STC_READ(sc, STC_GSCR1));
+ sp = &sc->sc_ttys->sc_port[channel];
+ ptr = sp->sp_rput;
+ *ptr++ = STC_READ(sc, STC_RCSR);
+ *ptr++ = STC_READ(sc, 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);
+ }
+ STC_WRITE(sc, STC_EOSRR, 0);
+ if (cnt) {
+ *needsoftp = 1;
+ sp->sp_rput = ptr;
+ }
+ return (1);
+}
+
+int
+spifstcintr_rx(sc, needsoftp)
+ struct spif_softc *sc;
+ int *needsoftp;
+{
+ struct stty_port *sp;
+ u_int8_t channel, *ptr, cnt, rcsr;
+ int i;
+
+ channel = CD180_GSCR_CHANNEL(STC_READ(sc, STC_GSCR1));
+ sp = &sc->sc_ttys->sc_port[channel];
+ ptr = sp->sp_rput;
+ cnt = STC_READ(sc, STC_RDCR);
+ for (i = 0; i < cnt; i++) {
+ *ptr++ = 0;
+ rcsr = STC_READ(sc, STC_RCSR);
+ *ptr++ = STC_READ(sc, 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;
+ }
+ }
+ STC_WRITE(sc, STC_EOSRR, 0);
+ if (cnt) {
+ *needsoftp = 1;
+ sp->sp_rput = ptr;
+ }
+ return (1);
+}
+
+int
+spifstcintr_tx(sc, needsoftp)
+ struct spif_softc *sc;
+ int *needsoftp;
+{
+ struct stty_port *sp;
+ u_int8_t channel, ch;
+ int cnt = 0;
+
+ channel = CD180_GSCR_CHANNEL(STC_READ(sc, STC_GSCR1));
+ sp = &sc->sc_ttys->sc_port[channel];
+ if (!ISSET(sp->sp_flags, STTYF_STOP)) {
+ if (ISSET(sp->sp_flags, STTYF_SET_BREAK)) {
+ STC_WRITE(sc, STC_TDR, 0);
+ STC_WRITE(sc, STC_TDR, 0x81);
+ CLR(sp->sp_flags, STTYF_SET_BREAK);
+ cnt += 2;
+ }
+ if (ISSET(sp->sp_flags, STTYF_CLR_BREAK)) {
+ STC_WRITE(sc, STC_TDR, 0);
+ STC_WRITE(sc, STC_TDR, 0x83);
+ CLR(sp->sp_flags, STTYF_CLR_BREAK);
+ cnt += 2;
+ }
+
+ while (sp->sp_txc > 0 && cnt < (CD180_TX_FIFO_SIZE-1)) {
+ ch = *sp->sp_txp;
+ sp->sp_txc--;
+ sp->sp_txp++;
+
+ if (ch == 0) {
+ STC_WRITE(sc, STC_TDR, ch);
+ cnt++;
+ }
+ STC_WRITE(sc, STC_TDR, ch);
+ cnt++;
+ }
+ }
+
+ if (sp->sp_txc == 0 ||
+ ISSET(sp->sp_flags, STTYF_STOP)) {
+ STC_WRITE(sc, STC_SRER, STC_READ(sc, STC_SRER) &
+ (~CD180_SRER_TXD));
+ CLR(sp->sp_flags, STTYF_STOP);
+ SET(sp->sp_flags, STTYF_DONE);
+ *needsoftp = 1;
+ }
+
+ STC_WRITE(sc, STC_EOSRR, 0);
+
+ return (1);
+}
+
+int
+spifstcintr_mx(sc, needsoftp)
+ struct spif_softc *sc;
+ int *needsoftp;
+{
+ struct stty_port *sp;
+ u_int8_t channel, mcr;
+
+ channel = CD180_GSCR_CHANNEL(STC_READ(sc, STC_GSCR1));
+ sp = &sc->sc_ttys->sc_port[channel];
+ mcr = STC_READ(sc, STC_MCR);
+ if (mcr & CD180_MCR_CD) {
+ SET(sp->sp_flags, STTYF_CDCHG);
+ *needsoftp = 1;
+ }
+ STC_WRITE(sc, STC_MCR, 0);
+ STC_WRITE(sc, STC_EOSRR, 0);
+ return (1);
+}
+
+int
+spifstcintr(vsc)
+ void *vsc;
+{
+ struct spif_softc *sc = (struct spif_softc *)vsc;
+ int needsoft = 0, r = 0, i;
+ u_int8_t ar;
+
+ for (i = 0; i < 8; i++) {
+ ar = ISTC_READ(sc, STC_RRAR) & CD180_GSVR_IMASK;
+ if (ar == CD180_GSVR_RXGOOD)
+ r |= spifstcintr_rx(sc, &needsoft);
+ else if (ar == CD180_GSVR_RXEXCEPTION)
+ r |= spifstcintr_rxexception(sc, &needsoft);
+ }
+
+ for (i = 0; i < 8; i++) {
+ ar = ISTC_READ(sc, STC_TRAR) & CD180_GSVR_IMASK;
+ if (ar == CD180_GSVR_TXDATA)
+ r |= spifstcintr_tx(sc, &needsoft);
+ }
+
+ for (i = 0; i < 8; i++) {
+ ar = ISTC_READ(sc, STC_MRAR) & CD180_GSVR_IMASK;
+ if (ar == CD180_GSVR_STATCHG)
+ r |= spifstcintr_mx(sc, &needsoft);
+ }
+
+ if (needsoft)
+ softintr_schedule(sc->sc_softih);
+ return (r);
+}
+
+void
+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, msvr;
+ 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];
+ sp->sp_rget += 2;
+ if (sp->sp_rget == sp->sp_rend)
+ sp->sp_rget = sp->sp_rbuf;
+
+ if (stat & (CD180_RCSR_BE | CD180_RCSR_FE))
+ data |= TTY_FE;
+
+ if (stat & CD180_RCSR_PE)
+ data |= TTY_PE;
+
+ (*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)) {
+ s = spltty();
+ STC_WRITE(sc, STC_CAR, i);
+ msvr = STC_READ(sc, STC_MSVR);
+ splx(s);
+
+ sp->sp_carrier = msvr & CD180_MSVR_CD;
+ (*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.dv_xname, 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;
+ }
+ }
+ }
+}
+
+void
+stty_write_ccr(sc, val)
+ struct spif_softc *sc;
+ u_int8_t val;
+{
+ int tries = 100000;
+
+ while (STC_READ(sc, STC_CCR) && tries--)
+ /*EMPTY*/;
+ if (tries == 0)
+ printf("%s: ccr timeout\n", sc->sc_dev.dv_xname);
+ STC_WRITE(sc, STC_CCR, val);
+}
+
+int
+stty_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);
+}