diff options
author | Jason Wright <jason@cvs.openbsd.org> | 2002-01-13 02:06:47 +0000 |
---|---|---|
committer | Jason Wright <jason@cvs.openbsd.org> | 2002-01-13 02:06:47 +0000 |
commit | 81ce5687ba5a7876107ea8029e52cdf543a5ac3d (patch) | |
tree | a03fed8b271c9a55ef09c7c0846e4fe90b964853 /sys/dev/sbus/spif.c | |
parent | ec9c6e9fcaa9decdd1cfe28c4a9575a1690d2bac (diff) |
Port of my spif driver to sparc64 (untested)
Diffstat (limited to 'sys/dev/sbus/spif.c')
-rw-r--r-- | sys/dev/sbus/spif.c | 1149 |
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); +} |