summaryrefslogtreecommitdiff
path: root/sys/arch/mvme68k/dev
diff options
context:
space:
mode:
authorTheo de Raadt <deraadt@cvs.openbsd.org>1995-10-18 08:53:40 +0000
committerTheo de Raadt <deraadt@cvs.openbsd.org>1995-10-18 08:53:40 +0000
commitd6583bb2a13f329cf0332ef2570eb8bb8fc0e39c (patch)
treeece253b876159b39c620e62b6c9b1174642e070e /sys/arch/mvme68k/dev
initial import of NetBSD tree
Diffstat (limited to 'sys/arch/mvme68k/dev')
-rw-r--r--sys/arch/mvme68k/dev/if_le.c965
-rw-r--r--sys/arch/mvme68k/dev/if_lereg.h167
-rw-r--r--sys/arch/mvme68k/dev/iio.c114
-rw-r--r--sys/arch/mvme68k/dev/iio.h18
-rw-r--r--sys/arch/mvme68k/dev/pcc.c171
-rw-r--r--sys/arch/mvme68k/dev/pccreg.h130
-rw-r--r--sys/arch/mvme68k/dev/scc.h64
-rw-r--r--sys/arch/mvme68k/dev/zs.c996
8 files changed, 2625 insertions, 0 deletions
diff --git a/sys/arch/mvme68k/dev/if_le.c b/sys/arch/mvme68k/dev/if_le.c
new file mode 100644
index 00000000000..cd7fe440304
--- /dev/null
+++ b/sys/arch/mvme68k/dev/if_le.c
@@ -0,0 +1,965 @@
+/* $NetBSD: if_le.c,v 1.1.1.1 1995/07/25 23:12:09 chuck Exp $ */
+
+/*-
+ * Copyright (c) 1982, 1992, 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.
+ *
+ * @(#)if_le.c 8.2 (Berkeley) 10/30/93
+ */
+
+#include "bpfilter.h"
+
+/*
+ * AMD 7990 LANCE
+ */
+#include <sys/param.h>
+#include <sys/device.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/mbuf.h>
+#include <sys/buf.h>
+#include <sys/socket.h>
+#include <sys/syslog.h>
+#include <sys/ioctl.h>
+#include <sys/malloc.h>
+#include <sys/errno.h>
+
+#include <vm/vm.h>
+
+#include <net/if.h>
+#include <net/netisr.h>
+#include <net/route.h>
+#if NBPFILTER > 0
+#include <sys/select.h>
+#include <net/bpf.h>
+#include <net/bpfdesc.h>
+#endif
+
+#ifdef INET
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/in_var.h>
+#include <netinet/ip.h>
+#include <netinet/if_ether.h>
+#endif
+
+#ifdef NS
+#include <netns/ns.h>
+#include <netns/ns_if.h>
+#endif
+
+#ifdef APPLETALK
+#include <netddp/atalk.h>
+#endif
+
+#include <machine/cpu.h>
+#include <machine/pmap.h>
+
+#include <mvme68k/dev/iio.h>
+#include <mvme68k/dev/if_lereg.h>
+#include <mvme68k/dev/pccreg.h>
+
+/* DVMA address to LANCE address -- the Sbus/MMU will resupply the 0xff */
+#define LANCE_ADDR(x) ((int)x)
+
+int ledebug = 0; /* console error messages */
+
+#ifdef PACKETSTATS
+long lexpacketsizes[LEMTU+1];
+long lerpacketsizes[LEMTU+1];
+#endif
+
+/* Per interface statistics */
+/* XXX this should go in something like if_levar.h */
+struct lestats {
+ long lexints; /* transmitter interrupts */
+ long lerints; /* receiver interrupts */
+ long lerbufs; /* total buffers received during interrupts */
+ long lerhits; /* times current rbuf was full */
+ long lerscans; /* rbufs scanned before finding first full */
+};
+
+/*
+ * Ethernet software status per interface.
+ *
+ * Each interface is referenced by a network interface structure,
+ * le_if, which the routing code uses to locate the interface.
+ * This structure contains the output queue for the interface, its address, ...
+ */
+struct le_softc {
+ struct device sc_dev; /* base device */
+ struct evcnt sc_intrcnt; /* # of interrupts, per le */
+ struct evcnt sc_errcnt; /* # of errors, per le */
+
+ struct arpcom sc_ac; /* common Ethernet structures */
+#define sc_if sc_ac.ac_if /* network-visible interface */
+#define sc_addr sc_ac.ac_enaddr /* hardware Ethernet address */
+ struct lereg1 *sc_r1; /* LANCE registers */
+ struct lereg2 *sc_r2; /* dual-port RAM */
+ int sc_rmd; /* predicted next rmd to process */
+ int sc_runt;
+ int sc_jab;
+ int sc_merr;
+ int sc_babl;
+ int sc_cerr;
+ int sc_miss;
+ int sc_xint;
+ int sc_xown;
+ int sc_uflo;
+ int sc_rxlen;
+ int sc_rxoff;
+ int sc_txoff;
+ int sc_busy;
+ short sc_iflags;
+ struct lestats sc_lestats; /* per interface statistics */
+};
+
+
+/* autoconfiguration driver */
+void leattach(struct device *, struct device *, void *);
+int lematch(struct device *, void *, void *);
+struct cfdriver lecd =
+ { NULL, "le", lematch, leattach, DV_IFNET, sizeof(struct le_softc) };
+
+/* Forwards */
+void leattach(struct device *, struct device *, void *);
+void lesetladrf(struct le_softc *);
+void lereset(struct device *);
+int leinit(int);
+void lestart(struct ifnet *);
+int leintr(void *);
+void lexint(struct le_softc *);
+void lerint(struct le_softc *);
+void leread(struct le_softc *, char *, int);
+int leput(char *, struct mbuf *);
+struct mbuf *leget(char *, int, int, struct ifnet *);
+int leioctl(struct ifnet *, u_long, caddr_t);
+void leerror(struct le_softc *, int);
+void lererror(struct le_softc *, char *);
+void lexerror(struct le_softc *);
+
+void *ledatabuf; /* XXXCDC hack from pmap bootstrap */
+
+int
+lematch(parent, vcf, args)
+ struct device *parent;
+ void *vcf, *args;
+{
+ struct cfdata *cf = vcf;
+ struct iioargs *ia = args;
+
+ return !badbaddr((caddr_t) IIO_CFLOC_ADDR(cf));
+}
+
+/*
+ * Interface exists: make available by filling in network interface
+ * record. System will initialize the interface when it is ready
+ * to accept packets.
+ */
+void
+leattach(parent, self, args)
+ struct device *parent;
+ struct device *self;
+ void *args;
+{
+ register struct le_softc *sc = (struct le_softc *)self;
+ register struct lereg2 *ler2;
+ struct ifnet *ifp = &sc->sc_if;
+ register int a;
+ int pri = IIO_CFLOC_LEVEL(self->dv_cfdata);
+
+ /* XXX the following declarations should be elsewhere */
+ extern void myetheraddr(u_char *);
+
+ iio_print(self->dv_cfdata);
+
+ /* connect the interrupt */
+ pccintr_establish(PCCV_LE, leintr, pri, sc);
+
+ sc->sc_r1 = (struct lereg1 *) IIO_CFLOC_ADDR(self->dv_cfdata);
+
+
+ ler2 = sc->sc_r2 = (struct lereg2 *) ledatabuf;
+
+ myetheraddr(sc->sc_addr);
+ printf(" ler2 0x%x address %s\n", ler2, ether_sprintf(sc->sc_addr));
+
+ /*
+ * Setup for transmit/receive
+ *
+ * According to Van, some versions of the Lance only use this
+ * address to receive packets; it doesn't put them in
+ * output packets. We'll want to make sure that lestart()
+ * installs the address.
+ */
+ ler2->ler2_padr[0] = sc->sc_addr[1];
+ ler2->ler2_padr[1] = sc->sc_addr[0];
+ ler2->ler2_padr[2] = sc->sc_addr[3];
+ ler2->ler2_padr[3] = sc->sc_addr[2];
+ ler2->ler2_padr[4] = sc->sc_addr[5];
+ ler2->ler2_padr[5] = sc->sc_addr[4];
+ a = LANCE_ADDR(&ler2->ler2_rmd);
+ ler2->ler2_rlen = LE_RLEN | (a >> 16);
+ ler2->ler2_rdra = a;
+ a = LANCE_ADDR(&ler2->ler2_tmd);
+ ler2->ler2_tlen = LE_TLEN | (a >> 16);
+ ler2->ler2_tdra = a;
+
+ /*
+ * Set up event counters.
+ */
+ evcnt_attach(&sc->sc_dev, "intr", &sc->sc_intrcnt);
+ evcnt_attach(&sc->sc_dev, "errs", &sc->sc_errcnt);
+
+ ifp->if_unit = sc->sc_dev.dv_unit;
+ ifp->if_name = "le";
+ ifp->if_ioctl = leioctl;
+ ifp->if_start = lestart;
+ ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST;
+#ifdef IFF_NOTRAILERS
+ /* XXX still compile when the blasted things are gone... */
+ ifp->if_flags |= IFF_NOTRAILERS;
+#endif
+#if NBPFILTER > 0
+ bpfattach(&ifp->if_bpf, ifp, DLT_EN10MB, sizeof(struct ether_header));
+#endif
+ if_attach(ifp);
+ ether_ifattach(ifp);
+ sys_pcc->le_int = pri | PCC_IENABLE;
+
+}
+
+/*
+ * Setup the logical address filter
+ */
+void
+lesetladrf(sc)
+ register struct le_softc *sc;
+{
+ register struct lereg2 *ler2 = sc->sc_r2;
+ register struct ifnet *ifp = &sc->sc_if;
+ register struct ether_multi *enm;
+ register u_char *cp, c;
+ register u_long crc;
+ register int i, len;
+ struct ether_multistep step;
+
+ /*
+ * Set up multicast address filter by passing all multicast
+ * addresses through a crc generator, and then using the high
+ * order 6 bits as a index into the 64 bit logical address
+ * filter. The high order two bits select the word, while the
+ * rest of the bits select the bit within the word.
+ */
+
+ ler2->ler2_ladrf[0] = 0;
+ ler2->ler2_ladrf[1] = 0;
+ ler2->ler2_ladrf[2] = 0;
+ ler2->ler2_ladrf[3] = 0;
+ ifp->if_flags &= ~IFF_ALLMULTI;
+ ETHER_FIRST_MULTI(step, &sc->sc_ac, enm);
+ while (enm != NULL) {
+ if (bcmp((caddr_t)&enm->enm_addrlo,
+ (caddr_t)&enm->enm_addrhi, sizeof(enm->enm_addrlo)) != 0) {
+ /*
+ * We must listen to a range of multicast
+ * addresses. For now, just accept all
+ * multicasts, rather than trying to set only
+ * those filter bits needed to match the range.
+ * (At this time, the only use of address
+ * ranges is for IP multicast routing, for
+ * which the range is big enough to require all
+ * bits set.)
+ */
+ ler2->ler2_ladrf[0] = 0xffff;
+ ler2->ler2_ladrf[1] = 0xffff;
+ ler2->ler2_ladrf[2] = 0xffff;
+ ler2->ler2_ladrf[3] = 0xffff;
+ ifp->if_flags |= IFF_ALLMULTI;
+ return;
+ }
+
+ /*
+ * One would think, given the AM7990 document's polynomial
+ * of 0x04c11db6, that this should be 0x6db88320 (the bit
+ * reversal of the AMD value), but that is not right. See
+ * the BASIC listing: bit 0 (our bit 31) must then be set.
+ */
+ cp = (unsigned char *)&enm->enm_addrlo;
+ crc = 0xffffffff;
+ for (len = 6; --len >= 0;) {
+ c = *cp++;
+ for (i = 0; i < 8; i++) {
+ if ((c & 0x01) ^ (crc & 0x01)) {
+ crc >>= 1;
+ crc = crc ^ 0xedb88320;
+ } else
+ crc >>= 1;
+ c >>= 1;
+ }
+ }
+ /* Just want the 6 most significant bits. */
+ crc = crc >> 26;
+
+ /* Turn on the corresponding bit in the filter. */
+ ler2->ler2_ladrf[crc >> 4] |= 1 << (crc & 0xf);
+
+ ETHER_NEXT_MULTI(step, enm);
+ }
+}
+
+void
+lereset(dev)
+ struct device *dev;
+{
+ register struct le_softc *sc = (struct le_softc *)dev;
+ register struct lereg1 *ler1 = sc->sc_r1;
+ register struct lereg2 *ler2 = sc->sc_r2;
+ register int i, a, timo, stat;
+
+#if NBPFILTER > 0
+ if (sc->sc_if.if_flags & IFF_PROMISC)
+ ler2->ler2_mode = LE_MODE_NORMAL | LE_MODE_PROM;
+ else
+#endif
+ ler2->ler2_mode = LE_MODE_NORMAL;
+ ler1->ler1_rap = LE_CSR0;
+ ler1->ler1_rdp = LE_C0_STOP;
+
+ /* Setup the logical address filter */
+ lesetladrf(sc);
+
+ /* init receive and transmit rings */
+ for (i = 0; i < LERBUF; i++) {
+ a = LANCE_ADDR(&ler2->ler2_rbuf[i][0]);
+ ler2->ler2_rmd[i].rmd0 = a;
+ ler2->ler2_rmd[i].rmd1_hadr = a >> 16;
+ ler2->ler2_rmd[i].rmd1_bits = LE_R1_OWN;
+ ler2->ler2_rmd[i].rmd2 = -LEMTU | LE_XMD2_ONES;
+ ler2->ler2_rmd[i].rmd3 = 0;
+ }
+ for (i = 0; i < LETBUF; i++) {
+ a = LANCE_ADDR(&ler2->ler2_tbuf[i][0]);
+ ler2->ler2_tmd[i].tmd0 = a;
+ ler2->ler2_tmd[i].tmd1_hadr = a >> 16;
+ ler2->ler2_tmd[i].tmd1_bits = 0;
+ ler2->ler2_tmd[i].tmd2 = LE_XMD2_ONES;
+ ler2->ler2_tmd[i].tmd3 = 0;
+ }
+
+bzero((void *)&ler2->ler2_rbuf[0][0], (LERBUF + LETBUF) * LEMTU);
+ /* lance will stuff packet into receive buffer 0 next */
+ sc->sc_rmd = 0;
+
+ /* tell the chip where to find the initialization block */
+ a = LANCE_ADDR(&ler2->ler2_mode);
+ ler1->ler1_rap = LE_CSR1;
+ ler1->ler1_rdp = a;
+ ler1->ler1_rap = LE_CSR2;
+ ler1->ler1_rdp = a >> 16;
+ ler1->ler1_rap = LE_CSR3;
+ ler1->ler1_rdp = LE_C3_BSWP /*| LE_C3_ACON | LE_C3_BCON*/;
+ ler1->ler1_rap = LE_CSR0;
+ ler1->ler1_rdp = LE_C0_INIT;
+ timo = 100000;
+ while (((stat = ler1->ler1_rdp) & (LE_C0_ERR | LE_C0_IDON)) == 0) {
+ if (--timo == 0) {
+ printf("%s: init timeout, stat=%b\n",
+ sc->sc_dev.dv_xname, stat, LE_C0_BITS);
+ break;
+ }
+ }
+ if (stat & LE_C0_ERR)
+ printf("%s: init failed, ler1=0x%x, stat=%b\n",
+ sc->sc_dev.dv_xname, ler1, stat, LE_C0_BITS);
+ else
+ ler1->ler1_rdp = LE_C0_IDON; /* clear IDON */
+ ler1->ler1_rdp = LE_C0_STRT | LE_C0_INEA;
+ sc->sc_if.if_flags &= ~IFF_OACTIVE;
+}
+
+/*
+ * Initialization of interface
+ */
+int
+leinit(unit)
+ int unit;
+{
+ register struct le_softc *sc = lecd.cd_devs[unit];
+ register struct ifnet *ifp = &sc->sc_if;
+ register int s;
+
+ if ((ifp->if_flags & IFF_RUNNING) == 0) {
+ s = splimp();
+ ifp->if_flags |= IFF_RUNNING;
+ lereset(&sc->sc_dev);
+ lestart(ifp);
+ splx(s);
+ }
+ return (0);
+}
+
+/*
+ * Start output on interface. Get another datagram to send
+ * off of the interface queue, and copy it to the interface
+ * before starting the output.
+ */
+void
+lestart(ifp)
+ register struct ifnet *ifp;
+{
+ register struct le_softc *sc = lecd.cd_devs[ifp->if_unit];
+ register struct letmd *tmd;
+ register struct mbuf *m;
+ register int len;
+ if ((sc->sc_if.if_flags & IFF_RUNNING) == 0)
+ return;
+ IF_DEQUEUE(&sc->sc_if.if_snd, m);
+ if (m == 0)
+ return;
+
+ len = leput((char *)sc->sc_r2->ler2_tbuf[0], m);
+
+#if NBPFILTER > 0
+ /*
+ * If bpf is listening on this interface, let it
+ * see the packet before we commit it to the wire.
+ */
+ if (sc->sc_if.if_bpf)
+ bpf_tap(sc->sc_if.if_bpf, (char *)sc->sc_r2->ler2_tbuf[0], len);
+#endif
+
+#ifdef PACKETSTATS
+ if (len <= LEMTU)
+ lexpacketsizes[len]++;
+#endif
+ tmd = sc->sc_r2->ler2_tmd;
+ tmd->tmd3 = 0;
+ tmd->tmd2 = -len | LE_XMD2_ONES;
+ tmd->tmd1_bits = LE_T1_OWN | LE_T1_STP | LE_T1_ENP;
+ sc->sc_if.if_flags |= IFF_OACTIVE;
+ return;
+}
+
+int
+leintr(dev)
+ register void *dev;
+{
+ register struct le_softc *sc = dev;
+ register struct lereg1 *ler1 = sc->sc_r1;
+ register int csr0;
+
+ csr0 = ler1->ler1_rdp;
+ if ((csr0 & LE_C0_INTR) == 0)
+ return (0);
+ sc->sc_intrcnt.ev_count++;
+
+ if (csr0 & LE_C0_ERR) {
+ sc->sc_errcnt.ev_count++;
+ leerror(sc, csr0);
+ if (csr0 & LE_C0_MERR) {
+ sc->sc_merr++;
+ lereset(&sc->sc_dev);
+ return (1);
+ }
+ if (csr0 & LE_C0_BABL)
+ sc->sc_babl++;
+ if (csr0 & LE_C0_CERR)
+ sc->sc_cerr++;
+ if (csr0 & LE_C0_MISS)
+ sc->sc_miss++;
+ ler1->ler1_rdp = LE_C0_BABL|LE_C0_CERR|LE_C0_MISS|LE_C0_INEA;
+ }
+ if ((csr0 & LE_C0_RXON) == 0) {
+ sc->sc_rxoff++;
+ lereset(&sc->sc_dev);
+ return (1);
+ }
+ if ((csr0 & LE_C0_TXON) == 0) {
+ sc->sc_txoff++;
+ lereset(&sc->sc_dev);
+ return (1);
+ }
+ if (csr0 & LE_C0_RINT) {
+ /* interrupt is cleared in lerint */
+ lerint(sc);
+ }
+ if (csr0 & LE_C0_TINT) {
+ ler1->ler1_rdp = LE_C0_TINT|LE_C0_INEA;
+ lexint(sc);
+ }
+ return (1);
+}
+
+/*
+ * Ethernet interface transmitter interrupt.
+ * Start another output if more data to send.
+ */
+void
+lexint(sc)
+ register struct le_softc *sc;
+{
+ register struct letmd *tmd = sc->sc_r2->ler2_tmd;
+
+ sc->sc_lestats.lexints++;
+ if ((sc->sc_if.if_flags & IFF_OACTIVE) == 0) {
+ sc->sc_xint++;
+ return;
+ }
+ if (tmd->tmd1_bits & LE_T1_OWN) {
+ sc->sc_xown++;
+ return;
+ }
+ if (tmd->tmd1_bits & LE_T1_ERR) {
+err:
+ lexerror(sc);
+ sc->sc_if.if_oerrors++;
+ if (tmd->tmd3 & (LE_T3_BUFF|LE_T3_UFLO)) {
+ sc->sc_uflo++;
+ lereset(&sc->sc_dev);
+ } else if (tmd->tmd3 & LE_T3_LCOL)
+ sc->sc_if.if_collisions++;
+ else if (tmd->tmd3 & LE_T3_RTRY)
+ sc->sc_if.if_collisions += 16;
+ }
+ else if (tmd->tmd3 & LE_T3_BUFF)
+ /* XXX documentation says BUFF not included in ERR */
+ goto err;
+ else if (tmd->tmd1_bits & LE_T1_ONE)
+ sc->sc_if.if_collisions++;
+ else if (tmd->tmd1_bits & LE_T1_MORE)
+ /* what is the real number? */
+ sc->sc_if.if_collisions += 2;
+ else
+ sc->sc_if.if_opackets++;
+ sc->sc_if.if_flags &= ~IFF_OACTIVE;
+ lestart(&sc->sc_if);
+}
+
+#define LENEXTRMP \
+ if (++bix == LERBUF) bix = 0, rmd = sc->sc_r2->ler2_rmd; else ++rmd
+
+/*
+ * Ethernet interface receiver interrupt.
+ * If input error just drop packet.
+ * Decapsulate packet based on type and pass to type specific
+ * higher-level input routine.
+ */
+void
+lerint(sc)
+ register struct le_softc *sc;
+{
+ register int bix = sc->sc_rmd;
+ register struct lermd *rmd = &sc->sc_r2->ler2_rmd[bix];
+
+ sc->sc_lestats.lerints++;
+ /*
+ * Out of sync with hardware, should never happen?
+ */
+ if (rmd->rmd1_bits & LE_R1_OWN) {
+ do {
+ sc->sc_lestats.lerscans++;
+ LENEXTRMP;
+ } while ((rmd->rmd1_bits & LE_R1_OWN) && bix != sc->sc_rmd);
+ if (bix == sc->sc_rmd)
+ printf("%s: RINT with no buffer\n",
+ sc->sc_dev.dv_xname);
+ } else
+ sc->sc_lestats.lerhits++;
+
+ /*
+ * Process all buffers with valid data
+ */
+ while ((rmd->rmd1_bits & LE_R1_OWN) == 0) {
+ int len = rmd->rmd3;
+
+ /* Clear interrupt to avoid race condition */
+ sc->sc_r1->ler1_rdp = LE_C0_RINT|LE_C0_INEA;
+
+ if (rmd->rmd1_bits & LE_R1_ERR) {
+ sc->sc_rmd = bix;
+ lererror(sc, "bad packet");
+ sc->sc_if.if_ierrors++;
+ } else if ((rmd->rmd1_bits & (LE_R1_STP|LE_R1_ENP)) !=
+ (LE_R1_STP|LE_R1_ENP)) {
+ /* XXX make a define for LE_R1_STP|LE_R1_ENP? */
+ /*
+ * Find the end of the packet so we can see how long
+ * it was. We still throw it away.
+ */
+ do {
+ sc->sc_r1->ler1_rdp = LE_C0_RINT|LE_C0_INEA;
+ rmd->rmd3 = 0;
+ rmd->rmd1_bits = LE_R1_OWN;
+ LENEXTRMP;
+ } while (!(rmd->rmd1_bits &
+ (LE_R1_OWN|LE_R1_ERR|LE_R1_STP|LE_R1_ENP)));
+ sc->sc_rmd = bix;
+ lererror(sc, "chained buffer");
+ sc->sc_rxlen++;
+ /*
+ * If search terminated without successful completion
+ * we reset the hardware (conservative).
+ */
+ if ((rmd->rmd1_bits &
+ (LE_R1_OWN|LE_R1_ERR|LE_R1_STP|LE_R1_ENP)) !=
+ LE_R1_ENP) {
+ lereset(&sc->sc_dev);
+ return;
+ }
+ } else {
+ leread(sc, (char *)sc->sc_r2->ler2_rbuf[bix], len);
+#ifdef PACKETSTATS
+ lerpacketsizes[len]++;
+#endif
+ sc->sc_lestats.lerbufs++;
+ }
+ rmd->rmd3 = 0;
+ rmd->rmd1_bits = LE_R1_OWN;
+ LENEXTRMP;
+ }
+ sc->sc_rmd = bix;
+}
+
+void
+leread(sc, pkt, len)
+ register struct le_softc *sc;
+ char *pkt;
+ int len;
+{
+ register struct ether_header *et;
+ register struct ifnet *ifp = &sc->sc_if;
+ struct mbuf *m;
+ struct ifqueue *inq;
+ int flags;
+
+ ifp->if_ipackets++;
+ et = (struct ether_header *)pkt;
+ et->ether_type = ntohs((u_short)et->ether_type);
+ /* adjust input length to account for header and CRC */
+ len -= sizeof(struct ether_header) + 4;
+
+ if (len <= 0) {
+ if (ledebug)
+ log(LOG_WARNING,
+ "%s: ierror(runt packet): from %s: len=%d\n",
+ sc->sc_dev.dv_xname,
+ ether_sprintf(et->ether_shost), len);
+ sc->sc_runt++;
+ ifp->if_ierrors++;
+ return;
+ }
+
+ /* Setup mbuf flags we'll need later */
+ flags = 0;
+ if (bcmp((caddr_t)etherbroadcastaddr,
+ (caddr_t)et->ether_dhost, sizeof(etherbroadcastaddr)) == 0)
+ flags |= M_BCAST;
+ if (et->ether_dhost[0] & 1)
+ flags |= M_MCAST;
+
+#if NBPFILTER > 0
+ /*
+ * Check if there's a bpf filter listening on this interface.
+ * If so, hand off the raw packet to enet, then discard things
+ * not destined for us (but be sure to keep broadcast/multicast).
+ */
+ if (sc->sc_if.if_bpf) {
+ bpf_tap(sc->sc_if.if_bpf, pkt,
+ len + sizeof(struct ether_header));
+ if ((flags & (M_BCAST | M_MCAST)) == 0 &&
+ bcmp(et->ether_dhost, sc->sc_addr,
+ sizeof(et->ether_dhost)) != 0)
+ return;
+ }
+#endif
+ m = leget(pkt, len, 0, ifp);
+ if (m == 0)
+ return;
+ ether_input(ifp, et, m);
+}
+
+/*
+ * Routine to copy from mbuf chain to transmit
+ * buffer in board local memory.
+ *
+ * ### this can be done by remapping in some cases
+ */
+int
+leput(lebuf, m)
+ register char *lebuf;
+ register struct mbuf *m;
+{
+ register struct mbuf *mp;
+ register int len, tlen = 0;
+
+ for (mp = m; mp; mp = mp->m_next) {
+ len = mp->m_len;
+ if (len == 0)
+ continue;
+ tlen += len;
+ bcopy(mtod(mp, char *), lebuf, len);
+ lebuf += len;
+ }
+ m_freem(m);
+ if (tlen < LEMINSIZE) {
+ bzero(lebuf, LEMINSIZE - tlen);
+ tlen = LEMINSIZE;
+ }
+ return (tlen);
+}
+
+/*
+ * Routine to copy from board local memory into mbufs.
+ */
+struct mbuf *
+leget(lebuf, totlen, off0, ifp)
+ char *lebuf;
+ int totlen, off0;
+ struct ifnet *ifp;
+{
+ register struct mbuf *m;
+ struct mbuf *top = 0, **mp = &top;
+ register int off = off0, len;
+ register char *cp;
+ char *epkt;
+
+ lebuf += sizeof(struct ether_header);
+ cp = lebuf;
+ epkt = cp + totlen;
+ if (off) {
+ cp += off + 2 * sizeof(u_short);
+ totlen -= 2 * sizeof(u_short);
+ }
+
+ MGETHDR(m, M_DONTWAIT, MT_DATA);
+ if (m == 0)
+ return (0);
+ m->m_pkthdr.rcvif = ifp;
+ m->m_pkthdr.len = totlen;
+ m->m_len = MHLEN;
+
+ while (totlen > 0) {
+ if (top) {
+ MGET(m, M_DONTWAIT, MT_DATA);
+ if (m == 0) {
+ m_freem(top);
+ return (0);
+ }
+ m->m_len = MLEN;
+ }
+ len = min(totlen, epkt - cp);
+ if (len >= MINCLSIZE) {
+ MCLGET(m, M_DONTWAIT);
+ if (m->m_flags & M_EXT)
+ m->m_len = len = min(len, MCLBYTES);
+ else
+ len = m->m_len;
+ } else {
+ /*
+ * Place initial small packet/header at end of mbuf.
+ */
+ if (len < m->m_len) {
+ if (top == 0 && len + max_linkhdr <= m->m_len)
+ m->m_data += max_linkhdr;
+ m->m_len = len;
+ } else
+ len = m->m_len;
+ }
+ bcopy(cp, mtod(m, caddr_t), (unsigned)len);
+ cp += len;
+ *mp = m;
+ mp = &m->m_next;
+ totlen -= len;
+ if (cp == epkt)
+ cp = lebuf;
+ }
+ return (top);
+}
+
+/*
+ * Process an ioctl request.
+ */
+int
+leioctl(ifp, cmd, data)
+ register struct ifnet *ifp;
+ u_long cmd;
+ caddr_t data;
+{
+ register struct ifaddr *ifa;
+ register struct le_softc *sc = lecd.cd_devs[ifp->if_unit];
+ register struct lereg1 *ler1;
+ int s = splimp(), error = 0;
+
+ switch (cmd) {
+
+ case SIOCSIFADDR:
+ ifa = (struct ifaddr *)data;
+ ifp->if_flags |= IFF_UP;
+ switch (ifa->ifa_addr->sa_family) {
+#ifdef INET
+ case AF_INET:
+ (void)leinit(ifp->if_unit);
+ arp_ifinit(&sc->sc_ac, ifa);
+ break;
+#endif
+#ifdef NS
+ case AF_NS:
+ {
+ register struct ns_addr *ina = &(IA_SNS(ifa)->sns_addr);
+
+ if (ns_nullhost(*ina))
+ ina->x_host = *(union ns_host *)(sc->sc_addr);
+ else {
+ /*
+ * The manual says we can't change the address
+ * while the receiver is armed,
+ * so reset everything
+ */
+ ifp->if_flags &= ~IFF_RUNNING;
+ bcopy((caddr_t)ina->x_host.c_host,
+ (caddr_t)sc->sc_addr, sizeof(sc->sc_addr));
+ }
+ (void)leinit(ifp->if_unit); /* does le_setaddr() */
+ break;
+ }
+#endif
+ default:
+ (void)leinit(ifp->if_unit);
+ break;
+ }
+ break;
+
+ case SIOCSIFFLAGS:
+ ler1 = sc->sc_r1;
+ if ((ifp->if_flags & IFF_UP) == 0 &&
+ ifp->if_flags & IFF_RUNNING) {
+ ler1->ler1_rdp = LE_C0_STOP;
+ ifp->if_flags &= ~IFF_RUNNING;
+ } else if (ifp->if_flags & IFF_UP &&
+ (ifp->if_flags & IFF_RUNNING) == 0)
+ (void)leinit(ifp->if_unit);
+ /*
+ * If the state of the promiscuous bit changes, the interface
+ * must be reset to effect the change.
+ */
+ if (((ifp->if_flags ^ sc->sc_iflags) & IFF_PROMISC) &&
+ (ifp->if_flags & IFF_RUNNING)) {
+ sc->sc_iflags = ifp->if_flags;
+ lereset(&sc->sc_dev);
+ lestart(ifp);
+ }
+ break;
+
+ case SIOCADDMULTI:
+ error = ether_addmulti((struct ifreq *)data, &sc->sc_ac);
+ goto update_multicast;
+
+ case SIOCDELMULTI:
+ error = ether_delmulti((struct ifreq *)data, &sc->sc_ac);
+ update_multicast:
+ if (error == ENETRESET) {
+ /*
+ * Multicast list has changed; set the hardware
+ * filter accordingly.
+ */
+ lereset(&sc->sc_dev);
+ error = 0;
+ }
+ break;
+
+ default:
+ error = EINVAL;
+ }
+ splx(s);
+ return (error);
+}
+
+void
+leerror(sc, stat)
+ register struct le_softc *sc;
+ int stat;
+{
+ if (!ledebug)
+ return;
+
+ /*
+ * Not all transceivers implement heartbeat
+ * so we only log CERR once.
+ */
+ if ((stat & LE_C0_CERR) && sc->sc_cerr)
+ return;
+ log(LOG_WARNING, "%s: error: stat=%b\n",
+ sc->sc_dev.dv_xname, stat, LE_C0_BITS);
+}
+
+void
+lererror(sc, msg)
+ register struct le_softc *sc;
+ char *msg;
+{
+ register struct lermd *rmd;
+ int len;
+
+ if (!ledebug)
+ return;
+
+ rmd = &sc->sc_r2->ler2_rmd[sc->sc_rmd];
+ len = rmd->rmd3;
+ log(LOG_WARNING, "%s: ierror(%s): from %s: buf=%d, len=%d, rmd1=%b\n",
+ sc->sc_dev.dv_xname, msg, len > 11 ?
+ ether_sprintf((u_char *)&sc->sc_r2->ler2_rbuf[sc->sc_rmd][6]) :
+ "unknown",
+ sc->sc_rmd, len, rmd->rmd1_bits, LE_R1_BITS);
+}
+
+void
+lexerror(sc)
+ register struct le_softc *sc;
+{
+ register struct letmd *tmd;
+ register int len, tmd3, tdr;
+
+ if (!ledebug)
+ return;
+
+ tmd = sc->sc_r2->ler2_tmd;
+ tmd3 = tmd->tmd3;
+ tdr = tmd3 & LE_T3_TDR_MASK;
+ len = -(tmd->tmd2 & ~LE_XMD2_ONES);
+ log(LOG_WARNING,
+ "%s: oerror: to %s: buf=%d, len=%d, tmd1=%b, tmd3=%b, tdr=%d (%d nsecs)\n",
+ sc->sc_dev.dv_xname, len > 5 ?
+ ether_sprintf((u_char *)&sc->sc_r2->ler2_tbuf[0][0]) : "unknown",
+ 0, len,
+ tmd->tmd1_bits, LE_T1_BITS,
+ tmd3, LE_T3_BITS, tdr, tdr * 100);
+}
diff --git a/sys/arch/mvme68k/dev/if_lereg.h b/sys/arch/mvme68k/dev/if_lereg.h
new file mode 100644
index 00000000000..6e802470966
--- /dev/null
+++ b/sys/arch/mvme68k/dev/if_lereg.h
@@ -0,0 +1,167 @@
+/* $NetBSD: if_lereg.h,v 1.1.1.1 1995/07/25 23:12:10 chuck Exp $ */
+
+/*-
+ * Copyright (c) 1982, 1992, 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.
+ *
+ * @(#)if_lereg.h 8.2 (Berkeley) 10/30/93
+ */
+
+#define LEMTU 1518
+#define LEMINSIZE 60 /* should be 64 if mode DTCR is set */
+#define LERBUF 8
+#define LERBUFLOG2 3
+#define LE_RLEN (LERBUFLOG2 << 13)
+#define LETBUF 1
+#define LETBUFLOG2 0
+#define LE_TLEN (LETBUFLOG2 << 13)
+
+/* Local Area Network Controller for Ethernet (LANCE) registers */
+struct lereg1 {
+ volatile u_short ler1_rdp; /* register data port */
+ volatile u_short ler1_rap; /* register address port */
+};
+
+/* register addresses */
+#define LE_CSR0 0 /* Control and status register */
+#define LE_CSR1 1 /* low address of init block */
+#define LE_CSR2 2 /* high address of init block */
+#define LE_CSR3 3 /* Bus master and control */
+
+/* Control and status register 0 (csr0) */
+#define LE_C0_ERR 0x8000 /* error summary */
+#define LE_C0_BABL 0x4000 /* transmitter timeout error */
+#define LE_C0_CERR 0x2000 /* collision */
+#define LE_C0_MISS 0x1000 /* missed a packet */
+#define LE_C0_MERR 0x0800 /* memory error */
+#define LE_C0_RINT 0x0400 /* receiver interrupt */
+#define LE_C0_TINT 0x0200 /* transmitter interrupt */
+#define LE_C0_IDON 0x0100 /* initalization done */
+#define LE_C0_INTR 0x0080 /* interrupt condition */
+#define LE_C0_INEA 0x0040 /* interrupt enable */
+#define LE_C0_RXON 0x0020 /* receiver on */
+#define LE_C0_TXON 0x0010 /* transmitter on */
+#define LE_C0_TDMD 0x0008 /* transmit demand */
+#define LE_C0_STOP 0x0004 /* disable all external activity */
+#define LE_C0_STRT 0x0002 /* enable external activity */
+#define LE_C0_INIT 0x0001 /* begin initalization */
+
+#define LE_C0_BITS \
+ "\20\20ERR\17BABL\16CERR\15MISS\14MERR\13RINT\
+\12TINT\11IDON\10INTR\07INEA\06RXON\05TXON\04TDMD\03STOP\02STRT\01INIT"
+
+/* Control and status register 3 (csr3) */
+#define LE_C3_BSWP 0x4 /* byte swap */
+#define LE_C3_ACON 0x2 /* ALE control, eh? */
+#define LE_C3_BCON 0x1 /* byte control */
+/*
+ * Current size is 13,758 bytes with 8 x 1518 receive buffers and
+ * 1 x 1518 transmit buffer.
+ */
+struct lereg2 {
+ /* initialization block */
+ volatile u_short ler2_mode; /* mode */
+ volatile u_char ler2_padr[6]; /* physical address */
+ volatile u_short ler2_ladrf[4]; /* logical address filter */
+ volatile u_short ler2_rdra; /* receive descriptor addr */
+ volatile u_short ler2_rlen; /* rda high and ring size */
+ volatile u_short ler2_tdra; /* transmit descriptor addr */
+ volatile u_short ler2_tlen; /* tda high and ring size */
+ /* receive message descriptors. bits/hadr are byte order dependent. */
+ struct lermd {
+ volatile u_short rmd0; /* low address of packet */
+ volatile u_char rmd1_bits; /* descriptor bits */
+ volatile u_char rmd1_hadr; /* high address of packet */
+ volatile short rmd2; /* buffer byte count */
+ volatile u_short rmd3; /* message byte count */
+ } ler2_rmd[LERBUF];
+ /* transmit message descriptors */
+ struct letmd {
+ volatile u_short tmd0; /* low address of packet */
+ volatile u_char tmd1_bits; /* descriptor bits */
+ volatile u_char tmd1_hadr; /* high address of packet */
+ volatile short tmd2; /* buffer byte count */
+ volatile u_short tmd3; /* transmit error bits */
+ } ler2_tmd[LETBUF];
+ volatile char ler2_rbuf[LERBUF][LEMTU];
+ volatile char ler2_tbuf[LETBUF][LEMTU];
+};
+
+/* Initialzation block (mode) */
+#define LE_MODE_PROM 0x8000 /* promiscuous mode */
+/* 0x7f80 reserved, must be zero */
+#define LE_MODE_INTL 0x0040 /* internal loopback */
+#define LE_MODE_DRTY 0x0020 /* disable retry */
+#define LE_MODE_COLL 0x0010 /* force a collision */
+#define LE_MODE_DTCR 0x0008 /* disable transmit CRC */
+#define LE_MODE_LOOP 0x0004 /* loopback mode */
+#define LE_MODE_DTX 0x0002 /* disable transmitter */
+#define LE_MODE_DRX 0x0001 /* disable receiver */
+#define LE_MODE_NORMAL 0 /* none of the above */
+
+
+/* Receive message descriptor 1 (rmd1_bits) */
+#define LE_R1_OWN 0x80 /* LANCE owns the packet */
+#define LE_R1_ERR 0x40 /* error summary */
+#define LE_R1_FRAM 0x20 /* framing error */
+#define LE_R1_OFLO 0x10 /* overflow error */
+#define LE_R1_CRC 0x08 /* CRC error */
+#define LE_R1_BUFF 0x04 /* buffer error */
+#define LE_R1_STP 0x02 /* start of packet */
+#define LE_R1_ENP 0x01 /* end of packet */
+
+#define LE_R1_BITS \
+ "\20\10OWN\7ERR\6FRAM\5OFLO\4CRC\3BUFF\2STP\1ENP"
+
+/* Transmit message descriptor 1 (tmd1_bits) */
+#define LE_T1_OWN 0x80 /* LANCE owns the packet */
+#define LE_T1_ERR 0x40 /* error summary */
+#define LE_T1_MORE 0x10 /* multiple collisions */
+#define LE_T1_ONE 0x08 /* single collision */
+#define LE_T1_DEF 0x04 /* defferred transmit */
+#define LE_T1_STP 0x02 /* start of packet */
+#define LE_T1_ENP 0x01 /* end of packet */
+
+#define LE_T1_BITS \
+ "\20\10OWN\7ERR\6RES\5MORE\4ONE\3DEF\2STP\1ENP"
+
+/* Transmit message descriptor 3 (tmd3) */
+#define LE_T3_BUFF 0x8000 /* buffer error */
+#define LE_T3_UFLO 0x4000 /* underflow error */
+#define LE_T3_LCOL 0x1000 /* late collision */
+#define LE_T3_LCAR 0x0800 /* loss of carrier */
+#define LE_T3_RTRY 0x0400 /* retry error */
+#define LE_T3_TDR_MASK 0x03ff /* time domain reflectometry counter */
+
+#define LE_XMD2_ONES 0xf000
+
+#define LE_T3_BITS \
+ "\20\20BUFF\17UFLO\16RES\15LCOL\14LCAR\13RTRY"
diff --git a/sys/arch/mvme68k/dev/iio.c b/sys/arch/mvme68k/dev/iio.c
new file mode 100644
index 00000000000..ea9abed48b6
--- /dev/null
+++ b/sys/arch/mvme68k/dev/iio.c
@@ -0,0 +1,114 @@
+/* $Id: iio.c,v 1.1 1995/10/18 08:51:10 deraadt Exp $ */
+
+/*
+ *
+ * Copyright (c) 1995 Charles D. Cranor
+ * 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 Charles D. Cranor.
+ * 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.
+ */
+
+/*
+ * peripheral channel controller
+ */
+
+#include <sys/param.h>
+#include <sys/conf.h>
+#include <sys/ioctl.h>
+#include <sys/proc.h>
+#include <sys/user.h>
+#include <sys/tty.h>
+#include <sys/uio.h>
+#include <sys/callout.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/syslog.h>
+#include <sys/fcntl.h>
+#include <sys/device.h>
+#include <machine/cpu.h>
+#include <dev/cons.h>
+#include <mvme68k/mvme68k/isr.h>
+#include <mvme68k/dev/iio.h>
+
+/*
+ * Configuration routines for the internal I/O bus
+ */
+void iioattach __P((struct device *, struct device *, void *));
+int iiomatch __P((struct device *, void *, void *));
+
+struct iiosoftc {
+ struct device sc_dev;
+};
+
+struct cfdriver iiocd = {
+ NULL, "iio", iiomatch, iioattach,
+ DV_DULL, sizeof(struct iiosoftc), 0
+};
+
+int
+iiomatch(parent, cf, args)
+ struct device *parent;
+ void *cf;
+ void *args;
+{
+ return (1);
+}
+
+void
+iioattach(parent, self, args)
+ struct device *parent, *self;
+ void *args;
+{
+ extern struct cfdata cfdata[];
+ extern struct cfdriver pcccd;
+ struct cfdata *cf, *pcccf = NULL;
+
+ printf(" addr 0x%x\n", INTIOBASE);
+
+ /*
+ * attach the pcc first!
+ */
+ for (cf = cfdata; pcccf==NULL && cf->cf_driver; cf++) {
+ if (cf->cf_driver != &pcccd)
+ continue;
+ pcccf = cf;
+ }
+ if (!pcccf)
+ panic("no pcc device configured");
+ config_attach(self, pcccf, NULL, NULL);
+
+ while (config_found(self, NULL, NULL))
+ ;
+}
+
+void
+iio_print(cf)
+ struct cfdata *cf;
+{
+ printf(" offset 0x%x", cf->cf_loc[0]);
+ if (cf->cf_loc[1] > 0)
+ printf(" ipl %d", cf->cf_loc[1]);
+}
diff --git a/sys/arch/mvme68k/dev/iio.h b/sys/arch/mvme68k/dev/iio.h
new file mode 100644
index 00000000000..0a87c698cc7
--- /dev/null
+++ b/sys/arch/mvme68k/dev/iio.h
@@ -0,0 +1,18 @@
+/* $NetBSD: iio.h,v 1.1.1.1 1995/07/25 23:12:11 chuck Exp $ */
+/* $Id: iio.h,v 1.1 1995/10/18 08:51:10 deraadt Exp $ */
+
+struct iioargs {
+ int ic_addr;
+ int ic_lev;
+};
+
+#define IIO_CFLOC_ADDR(cf) (IIOV(INTIOBASE + (cf)->cf_loc[0]))
+#define IIO_CFLOC_LEVEL(cf) ((cf)->cf_loc[1])
+
+/*
+ * for the console we need zs phys addr
+ */
+
+#define ZS0_PHYS (INTIOBASE + 0x3000)
+#define ZS1_PHYS (INTIOBASE + 0x3800)
+
diff --git a/sys/arch/mvme68k/dev/pcc.c b/sys/arch/mvme68k/dev/pcc.c
new file mode 100644
index 00000000000..8ea641b7501
--- /dev/null
+++ b/sys/arch/mvme68k/dev/pcc.c
@@ -0,0 +1,171 @@
+/* $Id: pcc.c,v 1.1 1995/10/18 08:51:10 deraadt Exp $ */
+
+/*
+ *
+ * Copyright (c) 1995 Charles D. Cranor
+ * 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 Charles D. Cranor.
+ * 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.
+ */
+
+/*
+ * peripheral channel controller
+ */
+
+#include <sys/param.h>
+#include <sys/conf.h>
+#include <sys/ioctl.h>
+#include <sys/proc.h>
+#include <sys/user.h>
+#include <sys/tty.h>
+#include <sys/uio.h>
+#include <sys/callout.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/syslog.h>
+#include <sys/fcntl.h>
+#include <sys/device.h>
+#include <machine/cpu.h>
+#include <dev/cons.h>
+#include <mvme68k/mvme68k/isr.h>
+#include <mvme68k/dev/iio.h>
+#include <mvme68k/dev/pccreg.h>
+
+/*
+ * Autoconfiguration stuff.
+ */
+
+struct pccsoftc {
+ struct device sc_dev;
+ struct pcc *sc_pcc;
+};
+
+
+void pccattach __P((struct device *, struct device *, void *));
+int pccmatch __P((struct device *, void *, void *));
+
+struct cfdriver pcccd = {
+ NULL, "pcc", pccmatch, pccattach,
+ DV_DULL, sizeof(struct pccsoftc), 0
+};
+
+/*
+ * globals
+ */
+
+struct pcc *sys_pcc = NULL;
+
+struct {
+ int (*pcc_fn)();
+ void *arg;
+ int lvl;
+} pcc_vecs[PCC_NVEC];
+
+int
+pccmatch(parent, vcf, args)
+ struct device *parent;
+ void *vcf, *args;
+{
+ struct cfdata *cf = vcf;
+
+ return !badbaddr((caddr_t) IIO_CFLOC_ADDR(cf));
+}
+
+void
+pccattach(parent, self, args)
+ struct device *parent, *self;
+ void *args;
+{
+ struct pccsoftc *pccsc;
+
+ if (sys_pcc)
+ panic("pcc already attached!");
+
+ iio_print(self->dv_cfdata);
+
+ /*
+ * link into softc and set up interrupt vector base
+ */
+ pccsc = (struct pccsoftc *) self;
+ sys_pcc = pccsc->sc_pcc = (struct pcc *)IIO_CFLOC_ADDR(self->dv_cfdata);
+ pccsc->sc_pcc->int_vectr = PCC_VECBASE;
+ bzero(pcc_vecs, sizeof(pcc_vecs));
+
+ printf(" rev %d intbvr 0x%x\n", pccsc->sc_pcc->pcc_rev,
+ pccsc->sc_pcc->int_vectr);
+}
+
+
+/*
+ * pccintr: called from locore with the PC and evec from the trap frame.
+ */
+int
+pccintr(pc, evec, frame)
+ int pc;
+ int evec;
+ void *frame;
+{
+ int vec = (evec & 0xfff) >> 2; /* XXX should be m68k macro? */
+ extern u_long intrcnt[]; /* XXX from locore */
+
+ vec = vec & 0xf; /* XXX mask out */
+ if (vec >= PCC_NVEC || pcc_vecs[vec].pcc_fn == NULL)
+ return(straytrap(pc, evec));
+
+ cnt.v_intr++;
+ intrcnt[pcc_vecs[vec].lvl]++;
+
+ /* arg override? only timer1 gets access to frame */
+ if (vec != PCCV_TIMER1)
+ frame = pcc_vecs[vec].arg;
+ return((*pcc_vecs[vec].pcc_fn)(frame));
+}
+
+
+/*
+ * pccintr_establish: establish pcc interrupt
+ */
+int
+pccintr_establish(vec, hand, lvl, arg)
+ u_long vec;
+ int (*hand)(), lvl;
+ void *arg;
+{
+ if (vec >= PCC_NVEC) {
+ printf("pcc: illegal vector: 0x%x\n", vec);
+ panic("pccintr_establish");
+ }
+
+ if (pcc_vecs[vec].pcc_fn) {
+ printf("pcc: vector 0x%x in use: (0x%x,0x%x) (0x%x,0x%x)\n",
+ hand, arg, pcc_vecs[vec].pcc_fn, pcc_vecs[vec].arg);
+ panic("pccintr_establish");
+ }
+
+ pcc_vecs[vec].pcc_fn = hand;
+ pcc_vecs[vec].lvl = lvl;
+ pcc_vecs[vec].arg = arg;
+}
diff --git a/sys/arch/mvme68k/dev/pccreg.h b/sys/arch/mvme68k/dev/pccreg.h
new file mode 100644
index 00000000000..114d42a7dab
--- /dev/null
+++ b/sys/arch/mvme68k/dev/pccreg.h
@@ -0,0 +1,130 @@
+/* $Id: pccreg.h,v 1.1 1995/10/18 08:51:10 deraadt Exp $ */
+
+/*
+ *
+ * Copyright (c) 1995 Charles D. Cranor
+ * 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 Charles D. Cranor.
+ * 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.
+ */
+
+/*
+ * peripheral channel controller (at pa fffe1000)
+ */
+
+struct pcc {
+ volatile u_long dma_taddr; /* dma table address */
+ volatile u_long dma_daddr; /* dma data address */
+ volatile u_long dma_bcnt; /* dma byte count */
+ volatile u_long dma_hold; /* dma data hold register */
+ volatile u_short t1_pload; /* timer1 preload */
+ volatile u_short t1_count; /* timer1 count */
+ volatile u_short t2_pload; /* timer2 preload */
+ volatile u_short t2_count; /* timer2 count */
+ volatile u_char t1_int; /* timer1 interrupt ctrl */
+ volatile u_char t1_cr; /* timer1 ctrl reg */
+ volatile u_char t2_int; /* timer2 interrupt ctrl */
+ volatile u_char t2_cr; /* timer2 ctrl reg */
+ volatile u_char acf_int; /* acfail intr reg */
+ volatile u_char dog_int; /* watchdog intr reg */
+ volatile u_char pr_int; /* printer intr reg */
+ volatile u_char pr_cr; /* printer ctrl */
+ volatile u_char dma_int; /* dma interrupt control */
+ volatile u_char dma_csr; /* dma csr */
+ volatile u_char bus_int; /* bus error interrupt */
+ volatile u_char dma_sr; /* dma status register */
+ volatile u_char abrt_int; /* abort interrupt control reg */
+ volatile u_char ta_fcr; /* table address function code reg */
+ volatile u_char zs_int; /* serial interrupt reg */
+ volatile u_char gen_cr; /* general control register */
+ volatile u_char le_int; /* ethernet interrupt */
+ volatile u_char gen_sr; /* general status */
+ volatile u_char scsi_int; /* scsi interrupt reg */
+ volatile u_char slave_ba; /* slave base addr reg */
+ volatile u_char sw1_int; /* software interrupt #1 cr */
+ volatile u_char int_vectr; /* interrupt base vector register */
+ volatile u_char sw2_int; /* software interrupt #2 cr */
+ volatile u_char pcc_rev; /* revision level */
+};
+
+
+/*
+ * points to system's PCC
+ */
+
+extern struct pcc *sys_pcc;
+
+/*
+ * we lock off our interrupt vector at 0x40. if this is changed
+ * we'll need to change vector.s
+ */
+
+#define PCC_VECBASE 0x40
+#define PCC_NVEC 12
+
+/*
+ * vectors we use
+ */
+
+#define PCCV_ACFAIL 0
+#define PCCV_BERR 1
+#define PCCV_ABORT 2
+#define PCCV_ZS 3
+#define PCCV_LE 4
+#define PCCV_SCSIP 5
+#define PCCV_SCSID 6
+#define PCCV_PRINTER 7
+#define PCCV_TIMER1 8
+#define PCCV_TIMER2 9
+#define PCCV_SOFT1 10
+#define PCCV_SOFT2 11
+
+/*
+ * enable interrupt
+ */
+
+#define PCC_IENABLE 0x08
+
+/*
+ * interrupt mask
+ */
+
+#define PCC_IMASK 0x7
+
+/*
+ * clock/timer
+ */
+
+#define PCC_TIMERACK 0x80 /* ack intr */
+#define PCC_TIMER100HZ 63936 /* load value for 100Hz */
+#define PCC_TIMERCLEAR 0x0 /* reset and clear timer */
+#define PCC_TIMERSTART 0x3 /* start timer */
+
+/*
+ * serial control
+ */
+
+#define PCC_ZSEXTERN 0x10 /* let PCC supply vector */
diff --git a/sys/arch/mvme68k/dev/scc.h b/sys/arch/mvme68k/dev/scc.h
new file mode 100644
index 00000000000..4d94f6c3fde
--- /dev/null
+++ b/sys/arch/mvme68k/dev/scc.h
@@ -0,0 +1,64 @@
+/* $NetBSD: scc.h,v 1.1.1.1 1995/07/25 23:12:07 chuck Exp $ */
+
+/*
+ * Copyright (c) 1993 Paul Mackerras.
+ * 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. The name of the author may not be used to endorse or promote products
+ * derived from this software withough 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.
+ */
+/*
+ * SCC I/O register definitions
+ */
+
+#define PCLK_FREQ 8333333 /* XXX */
+
+struct scc {
+ unsigned char cr;
+ unsigned char dr;
+};
+
+struct sccregs {
+ volatile struct scc *s_adr;
+ unsigned char s_val[16];
+};
+
+#define ZREAD0(scc) ((scc)->s_adr->cr)
+#define ZREAD(scc, n) ((scc)->s_adr->cr = n, (scc)->s_adr->cr)
+#define ZREADD(scc) ((scc)->s_adr->dr)
+
+#define ZWRITE0(scc, v) ((scc)->s_adr->cr = v)
+#define ZWRITE(scc, n, v) (ZWRITE0(scc, n), ZWRITE0(scc, (scc)->s_val[n] = v))
+#define ZWRITED(scc, v) ((scc)->s_adr->dr = v)
+
+#define ZBIS(scc, n, v) (ZWRITE(scc, n, (scc)->s_val[n] | (v)))
+#define ZBIC(scc, n, v) (ZWRITE(scc, n, (scc)->s_val[n] & ~(v)))
+
+#define SCC_RXFULL 1 /* bits in rr0 */
+#define SCC_TXRDY 4
+#define SCC_DCD 8
+#define SCC_CTS 0x20
+
+#define SCC_RCVEN 1 /* bits in wr3 */
+
+#define SCC_RTS 2 /* bits in wr5 */
+#define SCC_DTR 0x80
diff --git a/sys/arch/mvme68k/dev/zs.c b/sys/arch/mvme68k/dev/zs.c
new file mode 100644
index 00000000000..9a52dbd8f9c
--- /dev/null
+++ b/sys/arch/mvme68k/dev/zs.c
@@ -0,0 +1,996 @@
+/* $NetBSD: zs.c,v 1.1.1.1 1995/07/25 23:12:07 chuck Exp $ */
+
+/*
+ * Copyright (c) 1993 Paul Mackerras.
+ * 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. The name of the author may not be used to endorse or promote products
+ * derived from this software withough 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.
+ */
+/*
+ * Serial I/O via an SCC,
+ */
+#include <sys/param.h>
+#include <sys/conf.h>
+#include <sys/ioctl.h>
+#include <sys/proc.h>
+#include <sys/user.h>
+#include <sys/tty.h>
+#include <sys/uio.h>
+#include <sys/callout.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/syslog.h>
+#include <sys/fcntl.h>
+#include <sys/device.h>
+#include <machine/cpu.h>
+#include <dev/cons.h>
+#include <mvme68k/dev/iio.h>
+#include <mvme68k/dev/scc.h>
+#include <mvme68k/dev/pccreg.h>
+
+#include "zs.h"
+#if NZS > 0
+
+/*#define PCLK_FREQ 8333333*/
+#undef PCLK_FREQ /* XXXCDC */
+#define PCLK_FREQ 5000000
+#define NZSLINE (NZS*2)
+
+#define RECV_BUF 512
+#define ERROR_DET 0xed
+
+#define TS_DRAIN TS_FLUSH/* waiting for output to drain */
+
+#define splzs() spl4()
+
+struct zs {
+ short flags; /* see below */
+ char rr0; /* holds previous CTS, DCD state */
+ unsigned char imask; /* mask for input chars */
+ int nzs_open; /* # opens as /dev/zsn */
+ int nkbd_open; /* # opens as a keyboard */
+ int gsp_unit; /* unit to send kbd chars to */
+ struct tty *tty; /* link to tty structure */
+ struct sccregs scc; /* SCC shadow registers */
+ u_char *rcv_get;
+ u_char *rcv_put;
+ u_char *rcv_end;
+ volatile int rcv_count;
+ int rcv_len;
+ char *send_ptr;
+ int send_count;
+ int sent_count;
+ volatile char modem_state;
+ volatile char modem_change;
+ volatile short hflags;
+ char rcv_buf[RECV_BUF];
+};
+
+/* Bits in flags */
+#define ZS_SIDEA 1
+#define ZS_INITED 2
+#define ZS_INTEN 4
+#define ZS_RESET 8
+#define ZS_CONSOLE 0x20
+
+/* Bits in hflags */
+#define ZH_OBLOCK 1 /* output blocked by CTS */
+#define ZH_SIRQ 2 /* soft interrupt request */
+#define ZH_TXING 4 /* transmitter active */
+#define ZH_RXOVF 8 /* receiver buffer overflow */
+
+struct zssoftc {
+ struct device dev;
+ struct zs zs[2];
+};
+
+struct tty *zs_tty[NZSLINE];
+
+struct termios zs_cons_termios;
+int zs_cons_unit = 0;
+int zs_is_console = 0;
+struct sccregs *zs_cons_scc;
+
+int zsopen __P((dev_t, int, int, struct proc *));
+void zsstart __P((struct tty *));
+int zsparam __P((struct tty *, struct termios *));
+int zsirq __P((int unit));
+void zs_softint __P((void));
+
+unsigned long sir_zs;
+void zs_softint();
+
+#define zsunit(dev) (minor(dev) >> 1)
+#define zsside(dev) (minor(dev) & 1)
+
+/*
+ * Autoconfiguration stuff.
+ */
+void zsattach __P((struct device *, struct device *, void *));
+int zsmatch __P((struct device *, void *, void *));
+
+struct cfdriver zscd = {
+ NULL, "zs", zsmatch, zsattach, DV_TTY, sizeof(struct zssoftc), 0
+};
+
+int
+zsmatch(parent, vcf, args)
+ struct device *parent;
+ void *vcf, *args;
+{
+ struct cfdata *cf = vcf;
+
+ return !badbaddr((caddr_t) IIO_CFLOC_ADDR(cf));
+}
+
+void
+zsattach(parent, self, args)
+ struct device *parent, *self;
+ void *args;
+{
+ struct zssoftc *dv;
+ struct zs *zp, *zc;
+ u_char ir;
+ volatile struct scc *scc;
+ int zs_level = IIO_CFLOC_LEVEL(self->dv_cfdata);
+
+ iio_print(self->dv_cfdata);
+
+ /* connect the interrupt */
+ dv = (struct zssoftc *) self;
+ pccintr_establish(PCCV_ZS, zsirq, zs_level, self->dv_unit);
+ /* XXXCDC: needs some work to handle zs1 */
+
+ zp = &dv->zs[0];
+ scc = (volatile struct scc *) IIO_CFLOC_ADDR(self->dv_cfdata);
+
+ if (zs_is_console && self->dv_unit == zsunit(zs_cons_unit)) {
+ /* SCC is the console - it's already reset */
+ zc = zp + zsside(zs_cons_unit);
+ zc->scc = *zs_cons_scc;
+ zs_cons_scc = &zc->scc;
+ zc->flags |= ZS_CONSOLE;
+ } else {
+ /* reset the SCC */
+ scc->cr = 0;
+ scc->cr = 9;
+ scc->cr = 0xC0; /* hardware reset of SCC, both sides */
+ }
+
+ /* side A */
+ zp->scc.s_adr = scc + 1;
+ zp->flags |= ZS_SIDEA | ZS_RESET;
+
+ /* side B */
+ ++zp;
+ zp->scc.s_adr = scc;
+ zp->flags |= ZS_RESET;
+
+ if (sir_zs == 0)
+ sir_zs = allocate_sir(zs_softint, 0);
+ printf("\n");
+
+ ir = sys_pcc->zs_int;
+ if ((ir & PCC_IMASK) != 0 && (ir & PCC_IMASK) != zs_level)
+ panic("zs configured at different IPLs");
+ sys_pcc->zs_int = zs_level | PCC_IENABLE | PCC_ZSEXTERN;
+}
+
+zs_ttydef(struct zs *zp)
+{
+ struct tty *tp = zp->tty;
+
+ if ((zp->flags & ZS_CONSOLE) == 0) {
+ 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 = TTYDEF_SPEED;
+ } else
+ tp->t_termios = zs_cons_termios;
+ ttychars(tp);
+ ttsetwater(tp);
+ tp->t_oproc = zsstart;
+ tp->t_param = zsparam;
+
+ zp->rcv_get = zp->rcv_buf;
+ zp->rcv_put = zp->rcv_buf;
+ zp->rcv_end = zp->rcv_buf + sizeof(zp->rcv_buf);
+ zp->rcv_len = sizeof(zp->rcv_buf) / 2;
+}
+
+struct tty *
+zstty(dev)
+ dev_t dev;
+{
+
+ if (minor(dev) < NZSLINE)
+ return (zs_tty[minor(dev)]);
+
+ return (NULL);
+}
+
+/* ARGSUSED */
+zsopen(dev_t dev, int flag, int mode, struct proc * p)
+{
+ register struct tty *tp;
+ int error;
+ struct zs *zp;
+ struct zssoftc *dv;
+
+ if (zsunit(dev) > zscd.cd_ndevs
+ || (dv = (struct zssoftc *) zscd.cd_devs[zsunit(dev)]) == NULL)
+ return ENODEV;
+
+ zp = &dv->zs[zsside(dev)];
+ if (zp->tty == NULL) {
+ zp->tty = ttymalloc();
+ zs_ttydef(zp);
+ if (minor(dev) < NZSLINE)
+ zs_tty[minor(dev)] = zp->tty;
+ }
+ tp = zp->tty;
+ tp->t_dev = dev;
+
+ if ((tp->t_state & TS_ISOPEN) == 0) {
+ tp->t_state |= TS_WOPEN;
+ zs_init(zp);
+ if ((zp->modem_state & SCC_DCD) != 0)
+ tp->t_state |= TS_CARR_ON;
+ } else
+ if (tp->t_state & TS_XCLUDE && p->p_ucred->cr_uid != 0)
+ return (EBUSY);
+
+ error = ((*linesw[tp->t_line].l_open) (dev, tp));
+
+ if (error == 0)
+ ++zp->nzs_open;
+ return error;
+}
+
+int
+zsclose(dev, flag, mode, p)
+ dev_t dev;
+ int flag, mode;
+ struct proc *p;
+{
+ struct zs *zp;
+ struct tty *tp;
+ struct zssoftc *dv;
+ int s;
+
+ if (zsunit(dev) > zscd.cd_ndevs
+ || (dv = (struct zssoftc *) zscd.cd_devs[zsunit(dev)]) == NULL)
+ return ENODEV;
+ zp = &dv->zs[zsside(dev)];
+ tp = zp->tty;
+
+ if (zp->nkbd_open == 0) {
+ (*linesw[tp->t_line].l_close) (tp, flag);
+ s = splzs();
+ if ((zp->flags & ZS_CONSOLE) == 0 && (tp->t_cflag & HUPCL) != 0)
+ ZBIC(&zp->scc, 5, 0x82); /* drop DTR, RTS */
+ ZBIC(&zp->scc, 3, 1); /* disable receiver */
+ splx(s);
+ ttyclose(tp);
+ }
+ zp->nzs_open = 0;
+ return (0);
+}
+
+/*ARGSUSED*/
+zsread(dev, uio, flag)
+ dev_t dev;
+ struct uio *uio;
+ int flag;
+{
+ struct zssoftc *dv = (struct zssoftc *) zscd.cd_devs[zsunit(dev)];
+ struct zs *zp = &dv->zs[zsside(dev)];
+ struct tty *tp = zp->tty;
+
+ return ((*linesw[tp->t_line].l_read) (tp, uio, flag));
+}
+
+/*ARGSUSED*/
+zswrite(dev, uio, flag)
+ dev_t dev;
+ struct uio *uio;
+ int flag;
+{
+ struct zssoftc *dv = (struct zssoftc *) zscd.cd_devs[zsunit(dev)];
+ struct zs *zp = &dv->zs[zsside(dev)];
+ struct tty *tp = zp->tty;
+
+ return ((*linesw[tp->t_line].l_write) (tp, uio, flag));
+}
+
+zsioctl(dev, cmd, data, flag, p)
+ dev_t dev;
+ caddr_t data;
+ int cmd, flag;
+ struct proc *p;
+{
+ struct zssoftc *dv = (struct zssoftc *) zscd.cd_devs[zsunit(dev)];
+ struct zs *zp = &dv->zs[zsside(dev)];
+ struct tty *tp = zp->tty;
+ register struct sccregs *scc = &zp->scc;
+ register int error, s;
+
+ error = (*linesw[tp->t_line].l_ioctl) (tp, cmd, data, flag, p);
+ if (error >= 0)
+ return (error);
+ error = ttioctl(tp, cmd, data, flag, p);
+ if (error >= 0)
+ return (error);
+ error = 0;
+ s = splzs();
+ switch (cmd) {
+ case TIOCSDTR:
+ ZBIS(scc, 5, 0x80);
+ break;
+ case TIOCCDTR:
+ ZBIC(scc, 5, 0x80);
+ break;
+ case TIOCSBRK:
+ splx(s);
+ zs_drain(zp);
+ s = splzs();
+ ZBIS(scc, 5, 0x10);
+ spltty();
+ zs_unblock(tp);
+ break;
+ case TIOCCBRK:
+ ZBIC(scc, 5, 0x10);
+ break;
+ case TIOCMGET:
+ *(int *) data = zscc_mget(scc);
+ break;
+ case TIOCMSET:
+ zscc_mset(scc, *(int *) data);
+ zscc_mclr(scc, ~*(int *) data);
+ break;
+ case TIOCMBIS:
+ zscc_mset(scc, *(int *) data);
+ break;
+ case TIOCMBIC:
+ zscc_mclr(scc, *(int *) data);
+ break;
+ default:
+ error = ENOTTY;
+ }
+ splx(s);
+ return error;
+}
+
+zsparam(tp, t)
+ struct tty *tp;
+ struct termios *t;
+{
+ struct zssoftc *dv = (struct zssoftc *) zscd.cd_devs[zsunit(tp->t_dev)];
+ struct zs *zp = &dv->zs[zsside(tp->t_dev)];
+ register int s;
+
+ zs_drain(zp);
+ s = splzs();
+ zp->imask = zscc_params(&zp->scc, t);
+ tp->t_ispeed = t->c_ispeed;
+ tp->t_ospeed = t->c_ospeed;
+ tp->t_cflag = t->c_cflag;
+ if ((tp->t_cflag & CCTS_OFLOW) == 0)
+ zp->hflags &= ~ZH_OBLOCK;
+ else
+ if ((zp->modem_state & 0x20) == 0)
+ zp->hflags |= ZH_OBLOCK;
+ spltty();
+ zs_unblock(tp);
+ splx(s);
+ return 0;
+}
+
+void
+zsstart(tp)
+ struct tty *tp;
+{
+ struct zssoftc *dv = (struct zssoftc *) zscd.cd_devs[zsunit(tp->t_dev)];
+ struct zs *zp = &dv->zs[zsside(tp->t_dev)];
+ register int s, n;
+
+ s = spltty();
+ if ((tp->t_state & (TS_TIMEOUT | TS_BUSY | TS_TTSTOP | TS_DRAIN)) == 0) {
+ n = ndqb(&tp->t_outq, 0);
+ if (n > 0) {
+ tp->t_state |= TS_BUSY;
+ splzs();
+ zp->hflags |= ZH_TXING;
+ zp->send_ptr = tp->t_outq.c_cf;
+ zp->send_count = n;
+ zp->sent_count = 0;
+ zs_txint(zp);
+ spltty();
+ }
+ }
+ splx(s);
+}
+
+zsstop(struct tty * tp, int flag)
+{
+ struct zssoftc *dv = (struct zssoftc *) zscd.cd_devs[zsunit(tp->t_dev)];
+ struct zs *zp = &dv->zs[zsside(tp->t_dev)];
+ int s, n;
+
+ s = splzs();
+ zp->send_count = 0;
+ n = zp->sent_count;
+ zp->sent_count = 0;
+ if ((tp->t_state & TS_BUSY) != 0 && (flag & FWRITE) == 0) {
+ tp->t_state &= ~TS_BUSY;
+ spltty();
+ ndflush(&tp->t_outq, n);
+ 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);
+ }
+ selwakeup(&tp->t_wsel);
+ }
+ }
+ splx(s);
+}
+
+zs_init(zp)
+ struct zs *zp;
+{
+ register int s;
+
+ s = splzs();
+ zscc_init(zp, &zp->tty->t_termios);
+ zp->rr0 = zp->modem_state = ZREAD0(&zp->scc);
+ ZBIS(&zp->scc, 1, 0x13);/* ints on tx, rx and ext/status */
+ ZBIS(&zp->scc, 9, 8); /* enable ints */
+ zp->flags |= ZS_INTEN;
+ splx(s);
+}
+
+zscc_init(zp, par)
+ struct zs *zp;
+ struct termios *par;
+{
+ struct sccregs *scc;
+
+ scc = &zp->scc;
+ ZWRITE(scc, 2, 0);
+ ZWRITE(scc, 10, 0);
+ ZWRITE(scc, 11, 0x50); /* rx & tx clock = brgen */
+ ZWRITE(scc, 14, 3); /* brgen enabled, from pclk */
+ zp->imask = zscc_params(scc, par);
+ ZBIS(scc, 5, 0x82); /* set DTR and RTS */
+ zp->flags |= ZS_INITED;
+}
+
+int
+zscc_params(scc, par)
+ struct sccregs *scc;
+ struct termios *par;
+{
+ unsigned divisor, speed;
+ int spd, imask, ints;
+
+ speed = par->c_ospeed;
+ if (speed == 0) {
+ /* disconnect - drop DTR & RTS, disable receiver */
+ ZBIC(scc, 5, 0x82);
+ ZBIC(scc, 3, 1);
+ return 0xFF;
+ }
+ if ((par->c_cflag & CREAD) == 0)
+ ZBIC(scc, 3, 1);/* disable receiver */
+ divisor = (PCLK_FREQ / 32 + (speed >> 1)) / speed - 2;
+ ZWRITE(scc, 12, divisor);
+ ZWRITE(scc, 13, divisor >> 8);
+ switch (par->c_cflag & CSIZE) {
+ case CS5:
+ spd = 0;
+ imask = 0x1F;
+ break;
+ case CS6:
+ spd = 0x40;
+ imask = 0x3F;
+ break;
+ case CS7:
+ spd = 0x20;
+ imask = 0x7F;
+ break;
+ default:
+ spd = 0x60;
+ imask = 0xFF;
+ }
+ ZWRITE(scc, 5, (scc->s_val[5] & ~0x60) | spd);
+ ZWRITE(scc, 3, (scc->s_val[3] & ~0xC0) | (spd << 1));
+ spd = par->c_cflag & CSTOPB ? 8 : 0;
+ spd |= par->c_cflag & PARENB ? par->c_cflag & PARODD ? 1 : 3 : 0;
+ ZWRITE(scc, 4, 0x44 | spd);
+ ZBIS(scc, 5, 8); /* enable transmitter */
+ if ((par->c_cflag & CREAD) != 0)
+ ZBIS(scc, 3, 1);/* enable receiver */
+ ints = 0;
+ if ((par->c_cflag & CLOCAL) == 0)
+ ints |= SCC_DCD;
+ if ((par->c_cflag & CCTS_OFLOW) != 0)
+ ints |= SCC_CTS;
+ ZWRITE(scc, 15, ints);
+ return imask;
+}
+
+zscc_mget(register struct sccregs * scc)
+{
+ int bits = 0, rr0;
+
+ if ((scc->s_val[3] & SCC_RCVEN) != 0)
+ bits |= TIOCM_LE;
+ if ((scc->s_val[5] & SCC_DTR) != 0)
+ bits |= TIOCM_DTR;
+ if ((scc->s_val[5] & SCC_RTS) != 0)
+ bits |= TIOCM_RTS;
+ rr0 = ZREAD0(scc);
+ if ((rr0 & SCC_CTS) != 0)
+ bits |= TIOCM_CTS;
+ if ((rr0 & SCC_DCD) != 0)
+ bits |= TIOCM_CAR;
+ return bits;
+}
+
+zscc_mset(register struct sccregs * scc, int bits)
+{
+ if ((bits & TIOCM_LE) != 0)
+ ZBIS(scc, 3, SCC_RCVEN);
+ if ((bits & TIOCM_DTR) != 0)
+ ZBIS(scc, 5, SCC_DTR);
+ if ((bits & TIOCM_RTS) != 0)
+ ZBIS(scc, 5, SCC_RTS);
+}
+
+zscc_mclr(register struct sccregs * scc, int bits)
+{
+ if ((bits & TIOCM_LE) != 0)
+ ZBIC(scc, 3, SCC_RCVEN);
+ if ((bits & TIOCM_DTR) != 0)
+ ZBIC(scc, 5, TIOCM_DTR);
+ if ((bits & TIOCM_RTS) != 0)
+ ZBIC(scc, 5, SCC_RTS);
+}
+
+zs_drain(register struct zs * zp)
+{
+ register int s;
+
+ zp->tty->t_state |= TS_DRAIN;
+ /* wait for Tx buffer empty and All sent bits to be set */
+ s = splzs();
+ while ((ZREAD0(&zp->scc) & SCC_TXRDY) == 0
+ || (ZREAD(&zp->scc, 1) & 1) == 0) {
+ splx(s);
+ DELAY(100);
+ s = splzs();
+ }
+ splx(s);
+}
+
+zs_unblock(register struct tty * tp)
+{
+ tp->t_state &= ~TS_DRAIN;
+ if (tp->t_outq.c_cc != 0)
+ zsstart(tp);
+}
+
+/*
+ * Hardware interrupt from an SCC.
+ */
+int
+zsirq(int unit)
+{
+ struct zssoftc *dv = (struct zssoftc *) zscd.cd_devs[unit];
+ register struct zs *zp = &dv->zs[0];
+ register int ipend, x;
+ register volatile struct scc *scc;
+
+ x = splzs();
+ scc = zp->scc.s_adr;
+ scc->cr = 3; /* read int pending from A side */
+ DELAY(5);
+ ipend = scc->cr;
+ if ((ipend & 0x20) != 0)
+ zs_rxint(zp);
+ if ((ipend & 0x10) != 0)
+ zs_txint(zp);
+ if ((ipend & 0x8) != 0)
+ zs_extint(zp);
+ ++zp; /* now look for B side ints */
+ if ((ipend & 0x4) != 0)
+ zs_rxint(zp);
+ if ((ipend & 0x2) != 0)
+ zs_txint(zp);
+ if ((ipend & 0x1) != 0)
+ zs_extint(zp);
+ splx(x);
+ return ipend != 0;
+}
+
+zs_txint(register struct zs * zp)
+{
+ struct tty *tp = zp->tty;
+ struct sccregs *scc;
+ int c;
+ u_char *get;
+
+ scc = &zp->scc;
+ ZWRITE0(scc, 0x28); /* reset Tx interrupt */
+ if ((zp->hflags & ZH_OBLOCK) == 0) {
+ get = zp->send_ptr;
+ while ((ZREAD0(scc) & SCC_TXRDY) != 0 && zp->send_count > 0) {
+ c = *get++;
+ ZWRITED(scc, c);
+ --zp->send_count;
+ ++zp->sent_count;
+ }
+ zp->send_ptr = get;
+ if (zp->send_count == 0 && (zp->hflags & ZH_TXING) != 0) {
+ zp->hflags &= ~ZH_TXING;
+ zp->hflags |= ZH_SIRQ;
+ setsoftint(sir_zs);
+ }
+ }
+}
+
+zs_rxint(register struct zs * zp)
+{
+ register int stat, c, n, extra;
+ u_char *put;
+
+ put = zp->rcv_put;
+ n = zp->rcv_count;
+ for (;;) {
+ if ((ZREAD0(&zp->scc) & SCC_RXFULL) == 0) /* check Rx full */
+ break;
+ stat = ZREAD(&zp->scc, 1) & 0x70;
+ c = ZREADD(&zp->scc) & zp->imask;
+ /* stat encodes parity, overrun, framing errors */
+ if (stat != 0)
+ ZWRITE0(&zp->scc, 0x30); /* reset error */
+ if ((zp->hflags & ZH_RXOVF) != 0) {
+ zp->hflags &= ~ZH_RXOVF;
+ stat |= 0x20;
+ }
+ extra = (stat != 0 || c == ERROR_DET) ? 2 : 0;
+ if (n + extra + 1 < zp->rcv_len) {
+ if (extra != 0) {
+ *put++ = ERROR_DET;
+ if (put >= zp->rcv_end)
+ put = zp->rcv_buf;
+ *put++ = stat;
+ if (put >= zp->rcv_end)
+ put = zp->rcv_buf;
+ n += 2;
+ }
+ *put++ = c;
+ if (put >= zp->rcv_end)
+ put = zp->rcv_buf;
+ ++n;
+ } else
+ zp->hflags |= ZH_RXOVF;
+ }
+ if (n > zp->rcv_count) {
+ zp->rcv_put = put;
+ zp->rcv_count = n;
+ zp->hflags |= ZH_SIRQ;
+ setsoftint(sir_zs);
+ }
+}
+
+/* Ext/status interrupt */
+zs_extint(register struct zs * zp)
+{
+ int rr0;
+ struct tty *tp = zp->tty;
+
+ rr0 = ZREAD0(&zp->scc);
+ ZWRITE0(&zp->scc, 0x10);/* reset ext/status int */
+ if ((tp->t_cflag & CCTS_OFLOW) != 0) {
+ if ((rr0 & 0x20) == 0)
+ zp->hflags |= ZH_OBLOCK;
+ else {
+ zp->hflags &= ~ZH_OBLOCK;
+ if ((rr0 & SCC_TXRDY) != 0)
+ zs_txint(zp);
+ }
+ }
+ zp->modem_change |= rr0 ^ zp->modem_state;
+ zp->modem_state = rr0;
+ zp->hflags |= ZH_SIRQ;
+ setsoftint(sir_zs);
+}
+
+void
+zs_softint()
+{
+ int s, n, n0, c, stat, rr0;
+ struct zs *zp;
+ struct tty *tp;
+ u_char *get;
+ int unit, side;
+
+ s = splzs();
+ for (unit = 0; unit < zscd.cd_ndevs; ++unit) {
+ if (zscd.cd_devs[unit] == NULL)
+ continue;
+ zp = &((struct zssoftc *) zscd.cd_devs[unit])->zs[0];
+ for (side = 0; side < 2; ++side, ++zp) {
+ if ((zp->hflags & ZH_SIRQ) == 0)
+ continue;
+ zp->hflags &= ~ZH_SIRQ;
+ tp = zp->tty;
+
+ /* check for tx done */
+ spltty();
+ if (tp != NULL && zp->send_count == 0
+ && (tp->t_state & TS_BUSY) != 0) {
+ tp->t_state &= ~(TS_BUSY | TS_FLUSH);
+ ndflush(&tp->t_outq, zp->sent_count);
+ 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);
+ }
+ selwakeup(&tp->t_wsel);
+ }
+ if (tp->t_line != 0)
+ (*linesw[tp->t_line].l_start) (tp);
+ else
+ zsstart(tp);
+ }
+ splzs();
+
+ /* check for received characters */
+ get = zp->rcv_get;
+ while (zp->rcv_count > 0) {
+ c = *get++;
+ if (get >= zp->rcv_end)
+ get = zp->rcv_buf;
+ if (c == ERROR_DET) {
+ stat = *get++;
+ if (get >= zp->rcv_end)
+ get = zp->rcv_buf;
+ c = *get++;
+ if (get >= zp->rcv_end)
+ get = zp->rcv_buf;
+ zp->rcv_count -= 3;
+ } else {
+ stat = 0;
+ --zp->rcv_count;
+ }
+ spltty();
+ if (tp == NULL || (tp->t_state & TS_ISOPEN) == 0)
+ continue;
+ if (zp->nzs_open == 0) {
+#ifdef notdef
+ if (stat == 0)
+ kbd_newchar(zp->gsp_unit, c);
+#endif
+ } else {
+ if ((stat & 0x10) != 0)
+ c |= TTY_PE;
+ if ((stat & 0x20) != 0) {
+ log(LOG_WARNING, "zs: fifo overflow\n");
+ c |= TTY_FE; /* need some error for
+ * slip stuff */
+ }
+ if ((stat & 0x40) != 0)
+ c |= TTY_FE;
+ (*linesw[tp->t_line].l_rint) (c, tp);
+ }
+ splzs();
+ }
+ zp->rcv_get = get;
+
+ /* check for modem lines changing */
+ while (zp->modem_change != 0 || zp->modem_state != zp->rr0) {
+ rr0 = zp->rr0 ^ zp->modem_change;
+ zp->modem_change = rr0 ^ zp->modem_state;
+
+ /* Check if DCD (carrier detect) has changed */
+ if (tp != NULL && (rr0 & 8) != (zp->rr0 & 8)) {
+ spltty();
+ ttymodem(tp, rr0 & 8);
+ /* XXX possibly should disable line if
+ * return value is 0 */
+ splzs();
+ }
+ zp->rr0 = rr0;
+ }
+ }
+ }
+ splx(s);
+}
+
+/*
+ * Routines to divert an SCC channel to the input side of /dev/gsp
+ * for the keyboard.
+ */
+int
+zs_kbdopen(int unit, int gsp_unit, struct termios * tiop, struct proc * p)
+{
+ struct zssoftc *dv = (struct zssoftc *) zscd.cd_devs[zsunit(unit)];
+ struct zs *zp = &dv->zs[zsside(unit)];
+ int error;
+
+ error = zsopen(unit, 0, 0, p);
+ if (error != 0)
+ return error;
+ ++zp->nkbd_open;
+ --zp->nzs_open;
+ zsparam(zp->tty, tiop);
+ zp->gsp_unit = gsp_unit;
+ return 0;
+}
+
+void
+zs_kbdclose(int unit)
+{
+ struct zssoftc *dv = (struct zssoftc *) zscd.cd_devs[zsunit(unit)];
+ struct zs *zp = &dv->zs[zsside(unit)];
+
+ zp->nkbd_open = 0;
+ if (zp->nzs_open == 0)
+ zsclose(unit, 0, 0, 0);
+}
+
+void
+zs_kbdput(int unit, int c)
+{
+ struct zssoftc *dv = (struct zssoftc *) zscd.cd_devs[zsunit(unit)];
+ struct zs *zp = &dv->zs[zsside(unit)];
+ struct tty *tp = zp->tty;
+
+ putc(c, &tp->t_outq);
+ zsstart(tp);
+}
+
+/*
+ * Routines for using side A of the first SCC as a console.
+ */
+
+/* probe for the SCC; should check hardware */
+zscnprobe(cp)
+ struct consdev *cp;
+{
+ int maj;
+ char *prom_cons;
+ extern char *prom_getvar();
+
+ /* locate the major number */
+ for (maj = 0; maj < nchrdev; maj++)
+ if (cdevsw[maj].d_open == zsopen)
+ break;
+
+ /* initialize required fields */
+ cp->cn_dev = makedev(maj, 0);
+ cp->cn_pri = CN_NORMAL;
+
+ return 1;
+}
+
+/* initialize the keyboard for use as the console */
+struct termios zscn_termios = {
+ TTYDEF_IFLAG,
+ TTYDEF_OFLAG,
+ TTYDEF_CFLAG,
+ TTYDEF_LFLAG,
+ {0},
+ TTYDEF_SPEED,
+ TTYDEF_SPEED
+};
+
+struct sccregs zs_cons_sccregs;
+int zs_cons_imask;
+
+unsigned zs_cons_addrs[] = {ZS0_PHYS, ZS1_PHYS};
+
+
+zscninit()
+{
+ zs_cnsetup(0, &zscn_termios);
+}
+
+/* Polling routine for console input from a serial port. */
+int
+zscngetc(dev_t dev)
+{
+ register struct sccregs *scc = zs_cons_scc;
+ int c, s, stat;
+
+ s = splzs();
+ for (;;) {
+ while ((ZREAD0(scc) & SCC_RXFULL) == 0) /* wait for Rx full */
+ ;
+ stat = ZREAD(scc, 1) & 0x70;
+ c = ZREADD(scc) & zs_cons_imask;
+ /* stat encodes parity, overrun, framing errors */
+ if (stat == 0)
+ break;
+ ZWRITE0(scc, 0x30); /* reset error */
+ }
+ splx(s);
+ return c;
+}
+
+zscnputc(dev_t dev, int c)
+{
+ register struct sccregs *scc = zs_cons_scc;
+ int s;
+
+ s = splzs();
+ while ((ZREAD0(scc) & SCC_TXRDY) == 0);
+ ZWRITED(scc, c);
+ splx(s);
+}
+
+zs_cnsetup(int unit, struct termios * tiop)
+{
+ register volatile struct scc *scc_adr;
+ register struct sccregs *scc;
+
+ zs_cons_unit = unit;
+ zs_is_console = 1;
+ zs_cons_scc = scc = &zs_cons_sccregs;
+
+ scc_adr = (volatile struct scc *) IIOV(zs_cons_addrs[zsunit(unit)]);
+
+ scc_adr[1].cr = 0;
+ scc_adr[1].cr = 9;
+ scc_adr[1].cr = 0xC0; /* hardware reset of SCC, both sides */
+ if (!zsside(unit))
+ ++scc_adr;
+
+ scc->s_adr = scc_adr;
+ ZWRITE(scc, 2, 0);
+ ZWRITE(scc, 10, 0);
+ ZWRITE(scc, 11, 0x50); /* rx & tx clock = brgen */
+ ZWRITE(scc, 14, 3); /* brgen enabled, from pclk */
+ zs_cons_imask = zscc_params(scc, tiop);
+ ZBIS(scc, 5, 0x82); /* set DTR and RTS */
+
+ zs_cons_termios = *tiop;/* save for later */
+}
+
+/*
+ * Routines for using the keyboard SCC as the input side of
+ * the 'gsp' console device.
+ */
+
+/* probe for the keyboard; should check hardware */
+zs_kbdcnprobe(cp, unit)
+ struct consdev *cp;
+ int unit;
+{
+ return (unsigned) unit < NZSLINE;
+}
+#endif /* NZS */