diff options
author | Theo de Raadt <deraadt@cvs.openbsd.org> | 1995-10-18 08:53:40 +0000 |
---|---|---|
committer | Theo de Raadt <deraadt@cvs.openbsd.org> | 1995-10-18 08:53:40 +0000 |
commit | d6583bb2a13f329cf0332ef2570eb8bb8fc0e39c (patch) | |
tree | ece253b876159b39c620e62b6c9b1174642e070e /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.c | 965 | ||||
-rw-r--r-- | sys/arch/mvme68k/dev/if_lereg.h | 167 | ||||
-rw-r--r-- | sys/arch/mvme68k/dev/iio.c | 114 | ||||
-rw-r--r-- | sys/arch/mvme68k/dev/iio.h | 18 | ||||
-rw-r--r-- | sys/arch/mvme68k/dev/pcc.c | 171 | ||||
-rw-r--r-- | sys/arch/mvme68k/dev/pccreg.h | 130 | ||||
-rw-r--r-- | sys/arch/mvme68k/dev/scc.h | 64 | ||||
-rw-r--r-- | sys/arch/mvme68k/dev/zs.c | 996 |
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 = ⊤ + 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 */ |