summaryrefslogtreecommitdiff
path: root/sys/arch/kbus/dev
diff options
context:
space:
mode:
authorgingold <gingold@cvs.openbsd.org>1997-10-14 07:25:35 +0000
committergingold <gingold@cvs.openbsd.org>1997-10-14 07:25:35 +0000
commitf6491d400ca651a8a1493d72c6a74c622aa231b1 (patch)
tree39f29255154f230f637c12e9214f12a9e64bd9d4 /sys/arch/kbus/dev
parent7a9e3739a66bd0fadfdc611c72e879fcc6f9ef01 (diff)
Gingold's port for kbus Series5 machine. Not fully finished and not very stable
Diffstat (limited to 'sys/arch/kbus/dev')
-rw-r--r--sys/arch/kbus/dev/am7990.c1111
-rw-r--r--sys/arch/kbus/dev/am7990reg.h179
-rw-r--r--sys/arch/kbus/dev/am7990var.h147
-rw-r--r--sys/arch/kbus/dev/dmavar.h47
-rw-r--r--sys/arch/kbus/dev/if_le.c183
-rw-r--r--sys/arch/kbus/dev/kbus.c110
-rw-r--r--sys/arch/kbus/dev/rd.c193
-rw-r--r--sys/arch/kbus/dev/sbic.c2913
-rw-r--r--sys/arch/kbus/dev/sbic.c.mvme2719
-rw-r--r--sys/arch/kbus/dev/sbicdma.c256
-rw-r--r--sys/arch/kbus/dev/sbicdma.h53
-rw-r--r--sys/arch/kbus/dev/sbicreg.h441
-rw-r--r--sys/arch/kbus/dev/sbicvar.h235
-rw-r--r--sys/arch/kbus/dev/sbicvar.h.mvme210
-rw-r--r--sys/arch/kbus/dev/vme.c321
-rw-r--r--sys/arch/kbus/dev/xd.c2380
-rw-r--r--sys/arch/kbus/dev/xdreg.h424
-rw-r--r--sys/arch/kbus/dev/xdvar.h167
-rw-r--r--sys/arch/kbus/dev/xio.h65
-rw-r--r--sys/arch/kbus/dev/zs.c745
-rw-r--r--sys/arch/kbus/dev/zs_cons.h9
-rw-r--r--sys/arch/kbus/dev/zs_kgdb.c279
22 files changed, 13187 insertions, 0 deletions
diff --git a/sys/arch/kbus/dev/am7990.c b/sys/arch/kbus/dev/am7990.c
new file mode 100644
index 00000000000..e7ce53826fc
--- /dev/null
+++ b/sys/arch/kbus/dev/am7990.c
@@ -0,0 +1,1111 @@
+/* $OpenBSD: am7990.c,v 1.1 1997/10/14 07:25:29 gingold Exp $ */
+/* $NetBSD: am7990.c,v 1.22 1996/10/13 01:37:19 christos Exp $ */
+
+/*-
+ * Copyright (c) 1995 Charles M. Hannum. All rights reserved.
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Ralph Campbell and Rick Macklem.
+ *
+ * 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) 11/16/93
+ */
+
+#include "bpfilter.h"
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/mbuf.h>
+#include <sys/syslog.h>
+#include <sys/socket.h>
+#include <sys/device.h>
+#include <sys/malloc.h>
+#include <sys/ioctl.h>
+#include <sys/errno.h>
+
+#include <net/if.h>
+#include <netinet/in.h>
+#include <netinet/if_ether.h>
+
+#ifdef INET
+#include <netinet/in_systm.h>
+#include <netinet/in_var.h>
+#include <netinet/ip.h>
+#endif
+
+#if NBPFILTER > 0
+#include <net/bpf.h>
+#include <net/bpfdesc.h>
+#endif
+
+#include <machine/ioasic.h>
+#include "am7990reg.h"
+#include "am7990var.h"
+
+#ifdef LEDEBUG
+void am7990_recv_print __P((struct am7990_softc *, int));
+void am7990_xmit_print __P((struct am7990_softc *, int));
+#endif
+
+integrate void am7990_rint __P((struct am7990_softc *));
+integrate void am7990_tint __P((struct am7990_softc *));
+
+integrate int am7990_put __P((struct am7990_softc *, int, struct mbuf *));
+integrate struct mbuf *am7990_get __P((struct am7990_softc *, int, int));
+integrate void am7990_read __P((struct am7990_softc *, int, int));
+
+hide void am7990_shutdown __P((void *));
+
+#define ifp (&sc->sc_arpcom.ac_if)
+
+#if 0 /* XXX what do we do about this?! --thorpej */
+static inline u_int16_t ether_cmp __P((void *, void *));
+
+/*
+ * Compare two Ether/802 addresses for equality, inlined and
+ * unrolled for speed. I'd love to have an inline assembler
+ * version of this... XXX: Who wanted that? mycroft?
+ * I wrote one, but the following is just as efficient.
+ * This expands to 10 short m68k instructions! -gwr
+ * Note: use this like bcmp()
+ */
+static inline u_short
+ether_cmp(one, two)
+ void *one, *two;
+{
+ register u_int16_t *a = (u_short *) one;
+ register u_int16_t *b = (u_short *) two;
+ register u_int16_t diff;
+
+ diff = *a++ - *b++;
+ diff |= *a++ - *b++;
+ diff |= *a++ - *b++;
+
+ return (diff);
+}
+
+#define ETHER_CMP ether_cmp
+#endif /* XXX */
+
+#ifndef ETHER_CMP
+#define ETHER_CMP(a, b) bcmp((a), (b), ETHER_ADDR_LEN)
+#endif
+
+/*
+ * am7990 configuration driver. Attachments are provided by
+ * machine-dependent driver front-ends.
+ */
+struct cfdriver sle_cd = {
+ NULL, "le", DV_IFNET
+};
+
+void
+am7990_config(sc)
+ struct am7990_softc *sc;
+{
+ int mem;
+
+ /* Make sure IOASIC is OK. */
+ if (!ioasic)
+ panic ("am7990_config: IOASIC not mapped");
+
+ /* Make sure the chip is stopped. */
+ am7990_stop(sc);
+
+ /* Initialize ifnet structure. */
+ bcopy(sc->sc_dev.dv_xname, ifp->if_xname, IFNAMSIZ);
+ ifp->if_softc = sc;
+ ifp->if_start = am7990_start;
+ ifp->if_ioctl = am7990_ioctl;
+ ifp->if_watchdog = am7990_watchdog;
+ ifp->if_flags =
+ IFF_BROADCAST | IFF_SIMPLEX | IFF_NOTRAILERS | IFF_MULTICAST;
+#ifdef LANCE_REVC_BUG
+ ifp->if_flags &= ~IFF_MULTICAST;
+#endif
+
+ /* Attach the interface. */
+ if_attach(ifp);
+ ether_ifattach(ifp);
+
+#if NBPFILTER > 0
+ bpfattach(&ifp->if_bpf, ifp, DLT_EN10MB, sizeof(struct ether_header));
+#endif
+
+ sc->sc_nrbuf = 16;
+ sc->sc_ntbuf = 4;
+
+ printf(": address %s\n", ether_sprintf(sc->sc_arpcom.ac_enaddr));
+ printf("%s: %d receive buffers, %d transmit buffers\n",
+ sc->sc_dev.dv_xname, sc->sc_nrbuf, sc->sc_ntbuf);
+
+ sc->sc_sh = shutdownhook_establish(am7990_shutdown, sc);
+ if (sc->sc_sh == NULL)
+ panic("am7990_config: can't establish shutdownhook");
+
+ mem = 0;
+ sc->sc_initaddr = mem;
+ mem += sizeof(struct leinit);
+ sc->sc_rmdaddr = mem;
+ mem += sizeof(struct lermd) * sc->sc_nrbuf;
+ sc->sc_tmdaddr = mem;
+ mem += sizeof(struct letmd) * sc->sc_ntbuf;
+ sc->sc_rbufaddr = mem;
+ mem += LEBLEN * sc->sc_nrbuf;
+ sc->sc_tbufaddr = 0x8000;
+ mem += LEBLEN * sc->sc_ntbuf;
+
+ if (mem > sc->sc_memsize)
+ panic("am7990_config: mem > memsize");
+}
+
+void
+am7990_reset(sc)
+ struct am7990_softc *sc;
+{
+ int s;
+
+ s = splimp();
+ am7990_init(sc);
+ splx(s);
+}
+
+/*
+ * Set up the initialization block and the descriptor rings.
+ */
+void
+am7990_meminit(sc)
+ register struct am7990_softc *sc;
+{
+ u_long a;
+ int bix;
+ struct leinit init;
+ struct lermd rmd;
+ struct letmd tmd;
+
+#if NBPFILTER > 0
+ if (ifp->if_flags & IFF_PROMISC)
+ init.init_mode = LE_MODE_NORMAL | LE_MODE_PROM;
+ else
+#endif
+ init.init_mode = LE_MODE_NORMAL;
+ init.init_padr[0] =
+ (sc->sc_arpcom.ac_enaddr[1] << 8) | sc->sc_arpcom.ac_enaddr[0];
+ init.init_padr[1] =
+ (sc->sc_arpcom.ac_enaddr[3] << 8) | sc->sc_arpcom.ac_enaddr[2];
+ init.init_padr[2] =
+ (sc->sc_arpcom.ac_enaddr[5] << 8) | sc->sc_arpcom.ac_enaddr[4];
+ am7990_setladrf(&sc->sc_arpcom, init.init_ladrf);
+
+ /* Set up ioasic. */
+ ioasic->ioasic_ei_rar = sc->sc_addr >> IOASIC_EI_RAR_SHIFT;
+ ioasic->ioasic_ei_tar1 = (sc->sc_addr + 0x8000) >> IOASIC_EI_TAR_SHIFT;
+ ioasic->ioasic_ei_tar2 = (sc->sc_addr + 0xa000) >> IOASIC_EI_TAR_SHIFT;
+
+ sc->sc_last_rd = 0;
+ sc->sc_first_td = sc->sc_last_td = sc->sc_no_td = 0;
+
+ a = sc->sc_addr + LE_RMDADDR(sc, 0);
+ init.init_rdra = (a & 0x1fff) | 0x8000;
+ init.init_rlen = (ffs(sc->sc_nrbuf) - 1) << 13;
+
+ a = sc->sc_addr + LE_TMDADDR(sc, 0);
+ init.init_tdra = (a & 0x1fff) | 0x8000;
+ init.init_tlen = (ffs(sc->sc_ntbuf) - 1) << 13;
+
+ (*sc->sc_copytodesc)(sc, &init, LE_INITADDR(sc), sizeof(init));
+
+
+ /*
+ * Set up receive ring descriptors.
+ */
+ for (bix = 0; bix < sc->sc_nrbuf; bix++) {
+ a = sc->sc_addr + LE_RBUFADDR(sc, bix);
+ rmd.rmd0 = a & 0x7fff;
+ rmd.rmd1_hadr = 0;
+ rmd.rmd1_bits = LE_R1_OWN;
+ rmd.rmd2 = -LEBLEN | LE_XMD2_ONES;
+ rmd.rmd3 = 0;
+ (*sc->sc_copytodesc)(sc, &rmd, LE_RMDADDR(sc, bix),
+ sizeof(rmd));
+ }
+
+ /*
+ * Set up transmit ring descriptors.
+ */
+ for (bix = 0; bix < sc->sc_ntbuf; bix++) {
+ a = sc->sc_addr + LE_TBUFADDR(sc, bix);
+ tmd.tmd0 = (a & 0x3fff) | 0xc000;
+ tmd.tmd1_hadr = 0;
+ tmd.tmd1_bits = 0;
+ tmd.tmd2 = 0 | LE_XMD2_ONES;
+ tmd.tmd3 = 0;
+ (*sc->sc_copytodesc)(sc, &tmd, LE_TMDADDR(sc, bix),
+ sizeof(tmd));
+ }
+}
+
+void
+am7990_stop(sc)
+ struct am7990_softc *sc;
+{
+
+ (*sc->sc_wrcsr)(sc, LE_CSR0, LE_C0_STOP);
+}
+
+/*
+ * Initialization of interface; set up initialization block
+ * and transmit/receive descriptor rings.
+ */
+void
+am7990_init(sc)
+ register struct am7990_softc *sc;
+{
+ register int timo;
+ u_long a;
+
+ (*sc->sc_wrcsr)(sc, LE_CSR0, LE_C0_STOP);
+ DELAY(100);
+
+ /* Set the correct byte swapping mode, etc. */
+ (*sc->sc_wrcsr)(sc, LE_CSR3, sc->sc_conf3);
+
+ /* Set up LANCE init block. */
+ am7990_meminit(sc);
+
+ /* Give LANCE the physical address of its init block. */
+ a = sc->sc_addr + LE_INITADDR(sc);
+ (*sc->sc_wrcsr)(sc, LE_CSR1, (a & 0x7fff) | 0x8000);
+ (*sc->sc_wrcsr)(sc, LE_CSR2, 0);
+
+ /* Try to initialize the LANCE. */
+ DELAY(100);
+ (*sc->sc_wrcsr)(sc, LE_CSR0, LE_C0_INIT);
+
+ /* Wait for initialization to finish. */
+ for (timo = 100000; timo; timo--)
+ if ((*sc->sc_rdcsr)(sc, LE_CSR0) & LE_C0_IDON)
+ break;
+
+ if ((*sc->sc_rdcsr)(sc, LE_CSR0) & LE_C0_IDON) {
+ /* Start the LANCE. */
+ (*sc->sc_wrcsr)(sc, LE_CSR0, LE_C0_INEA | LE_C0_STRT |
+ LE_C0_IDON);
+ ifp->if_flags |= IFF_RUNNING;
+ ifp->if_flags &= ~IFF_OACTIVE;
+ ifp->if_timer = 0;
+ am7990_start(ifp);
+ } else
+ panic("%s: card failed to initialize\n", sc->sc_dev.dv_xname);
+ if (sc->sc_hwinit)
+ (*sc->sc_hwinit)(sc);
+}
+
+/*
+ * Routine to copy from mbuf chain to transmit buffer in
+ * network buffer memory.
+ */
+integrate int
+am7990_put(sc, boff, m)
+ struct am7990_softc *sc;
+ int boff;
+ register struct mbuf *m;
+{
+ register struct mbuf *n;
+ register int len, tlen = 0;
+
+#if 0
+ printf ("am7990_put: psr=0x%08x, IPR=0x%02x, LIPR=0x%02x\n",
+ getpsr(), lda (ASI_IPR, 0) & 0xff,
+ lduba (ASI_DGRAM, 0)); /* TG */
+#endif
+
+ for (; m; m = n) {
+ len = m->m_len;
+ if (len == 0) {
+ MFREE(m, n);
+ continue;
+ }
+ (*sc->sc_copytobuf)(sc, mtod(m, caddr_t), boff, len);
+ boff += len;
+ tlen += len;
+ MFREE(m, n);
+ }
+ if (tlen < LEMINSIZE) {
+ (*sc->sc_zerobuf)(sc, boff, LEMINSIZE - tlen);
+ tlen = LEMINSIZE;
+ }
+ return (tlen);
+}
+
+/*
+ * Pull data off an interface.
+ * Len is length of data, with local net header stripped.
+ * We copy the data into mbufs. When full cluster sized units are present
+ * we copy into clusters.
+ */
+integrate struct mbuf *
+am7990_get(sc, boff, totlen)
+ struct am7990_softc *sc;
+ int boff, totlen;
+{
+ register struct mbuf *m;
+ struct mbuf *top, **mp;
+ int len, pad;
+
+ MGETHDR(m, M_DONTWAIT, MT_DATA);
+ if (m == 0)
+ return (0);
+ m->m_pkthdr.rcvif = ifp;
+ m->m_pkthdr.len = totlen;
+ pad = ALIGN(sizeof(struct ether_header)) - sizeof(struct ether_header);
+ m->m_data += pad;
+ len = MHLEN - pad;
+ top = 0;
+ mp = &top;
+
+ while (totlen > 0) {
+ if (top) {
+ MGET(m, M_DONTWAIT, MT_DATA);
+ if (m == 0) {
+ m_freem(top);
+ return 0;
+ }
+ len = MLEN;
+ }
+ if (top && totlen >= MINCLSIZE) {
+ MCLGET(m, M_DONTWAIT);
+ if (m->m_flags & M_EXT)
+ len = MCLBYTES;
+ }
+ m->m_len = len = min(totlen, len);
+ (*sc->sc_copyfrombuf)(sc, mtod(m, caddr_t), boff, len);
+ boff += len;
+ totlen -= len;
+ *mp = m;
+ mp = &m->m_next;
+ }
+
+ return (top);
+}
+
+/*
+ * Pass a packet to the higher levels.
+ */
+integrate void
+am7990_read(sc, boff, len)
+ register struct am7990_softc *sc;
+ int boff, len;
+{
+ struct mbuf *m;
+ struct ether_header *eh;
+
+ if (len <= sizeof(struct ether_header) ||
+ len > ETHERMTU + sizeof(struct ether_header)) {
+#ifdef LEDEBUG
+ printf("%s: invalid packet size %d; dropping\n",
+ sc->sc_dev.dv_xname, len);
+#endif
+ ifp->if_ierrors++;
+ return;
+ }
+
+ /* Pull packet off interface. */
+ m = am7990_get(sc, boff, len);
+ if (m == 0) {
+ ifp->if_ierrors++;
+ return;
+ }
+
+ ifp->if_ipackets++;
+
+ /* We assume that the header fit entirely in one mbuf. */
+ eh = mtod(m, struct ether_header *);
+
+#if NBPFILTER > 0
+ /*
+ * Check if there's a BPF listener on this interface.
+ * If so, hand off the raw packet to BPF.
+ */
+ if (ifp->if_bpf) {
+ bpf_mtap(ifp->if_bpf, m);
+
+#ifndef LANCE_REVC_BUG
+ /*
+ * Note that the interface cannot be in promiscuous mode if
+ * there are no BPF listeners. And if we are in promiscuous
+ * mode, we have to check if this packet is really ours.
+ */
+ if ((ifp->if_flags & IFF_PROMISC) != 0 &&
+ (eh->ether_dhost[0] & 1) == 0 && /* !mcast and !bcast */
+ ETHER_CMP(eh->ether_dhost, sc->sc_arpcom.ac_enaddr)) {
+ m_freem(m);
+ return;
+ }
+#endif
+ }
+#endif
+
+#ifdef LANCE_REVC_BUG
+ /*
+ * The old LANCE (Rev. C) chips have a bug which causes
+ * garbage to be inserted in front of the received packet.
+ * The work-around is to ignore packets with an invalid
+ * destination address (garbage will usually not match).
+ * Of course, this precludes multicast support...
+ */
+ if (ETHER_CMP(eh->ether_dhost, sc->sc_arpcom.ac_enaddr) &&
+ ETHER_CMP(eh->ether_dhost, etherbroadcastaddr)) {
+ m_freem(m);
+ return;
+ }
+#endif
+
+ /* Pass the packet up, with the ether header sort-of removed. */
+ m_adj(m, sizeof(struct ether_header));
+/* printf ("Call ether_input\n"); TG */
+ ether_input(ifp, eh, m);
+}
+
+integrate void
+am7990_rint(sc)
+ struct am7990_softc *sc;
+{
+ register int bix;
+ int rp;
+ struct lermd rmd;
+
+ bix = sc->sc_last_rd;
+
+ /* Process all buffers with valid data. */
+ for (;;) {
+ rp = LE_RMDADDR(sc, bix);
+ (*sc->sc_copyfromdesc)(sc, &rmd, rp, sizeof(rmd));
+
+ if (rmd.rmd1_bits & LE_R1_OWN)
+ break;
+
+ if (rmd.rmd1_bits & LE_R1_ERR) {
+ if (rmd.rmd1_bits & LE_R1_ENP) {
+#ifdef LEDEBUG
+ if ((rmd.rmd1_bits & LE_R1_OFLO) == 0) {
+ if (rmd.rmd1_bits & LE_R1_FRAM)
+ printf("%s: framing error\n",
+ sc->sc_dev.dv_xname);
+ if (rmd.rmd1_bits & LE_R1_CRC)
+ printf("%s: crc mismatch\n",
+ sc->sc_dev.dv_xname);
+ }
+#endif
+ } else {
+ if (rmd.rmd1_bits & LE_R1_OFLO)
+ printf("%s: overflow\n",
+ sc->sc_dev.dv_xname);
+ }
+ if (rmd.rmd1_bits & LE_R1_BUFF)
+ printf("%s: receive buffer error\n",
+ sc->sc_dev.dv_xname);
+ ifp->if_ierrors++;
+ } else if ((rmd.rmd1_bits & (LE_R1_STP | LE_R1_ENP)) !=
+ (LE_R1_STP | LE_R1_ENP)) {
+ printf("%s: dropping chained buffer\n",
+ sc->sc_dev.dv_xname);
+ ifp->if_ierrors++;
+ } else {
+#ifdef LEDEBUG
+ if (sc->sc_debug)
+ am7990_recv_print(sc, sc->sc_last_rd);
+#endif
+ am7990_read(sc, LE_RBUFADDR(sc, bix),
+ (int)rmd.rmd3 - 4);
+ }
+
+ rmd.rmd1_bits = LE_R1_OWN;
+ rmd.rmd2 = -LEBLEN | LE_XMD2_ONES;
+ rmd.rmd3 = 0;
+ (*sc->sc_copytodesc)(sc, &rmd, rp, sizeof(rmd));
+
+#ifdef LEDEBUG
+ if (sc->sc_debug)
+ printf("sc->sc_last_rd = %x, rmd: "
+ "ladr %04x, hadr %02x, flags %02x, "
+ "bcnt %04x, mcnt %04x\n",
+ sc->sc_last_rd,
+ rmd.rmd0, rmd.rmd1_hadr, rmd.rmd1_bits,
+ rmd.rmd2, rmd.rmd3);
+#endif
+
+ if (++bix == sc->sc_nrbuf)
+ bix = 0;
+ }
+
+ sc->sc_last_rd = bix;
+}
+
+integrate void
+am7990_tint(sc)
+ register struct am7990_softc *sc;
+{
+ register int bix;
+ struct letmd tmd;
+
+ bix = sc->sc_first_td;
+
+ for (;;) {
+ if (sc->sc_no_td <= 0)
+ break;
+
+#ifdef LEDEBUG
+ if (sc->sc_debug)
+ printf("trans tmd: "
+ "ladr %04x, hadr %02x, flags %02x, "
+ "bcnt %04x, mcnt %04x\n",
+ tmd.tmd0, tmd.tmd1_hadr, tmd.tmd1_bits,
+ tmd.tmd2, tmd.tmd3);
+#endif
+
+ (*sc->sc_copyfromdesc)(sc, &tmd, LE_TMDADDR(sc, bix),
+ sizeof(tmd));
+
+ if (tmd.tmd1_bits & LE_T1_OWN)
+ break;
+
+ ifp->if_flags &= ~IFF_OACTIVE;
+
+ if (tmd.tmd1_bits & LE_T1_ERR) {
+ if (tmd.tmd3 & LE_T3_BUFF)
+ printf("%s: transmit buffer error\n",
+ sc->sc_dev.dv_xname);
+ else if (tmd.tmd3 & LE_T3_UFLO)
+ printf("%s: underflow\n", sc->sc_dev.dv_xname);
+ if (tmd.tmd3 & (LE_T3_BUFF | LE_T3_UFLO)) {
+ am7990_reset(sc);
+ return;
+ }
+ if (tmd.tmd3 & LE_T3_LCAR) {
+ if (sc->sc_nocarrier)
+ (*sc->sc_nocarrier)(sc);
+ else
+ printf("%s: lost carrier\n",
+ sc->sc_dev.dv_xname);
+ }
+ if (tmd.tmd3 & LE_T3_LCOL)
+ ifp->if_collisions++;
+ if (tmd.tmd3 & LE_T3_RTRY) {
+ printf("%s: excessive collisions, tdr %d\n",
+ sc->sc_dev.dv_xname,
+ tmd.tmd3 & LE_T3_TDR_MASK);
+ ifp->if_collisions += 16;
+ }
+ ifp->if_oerrors++;
+ } else {
+ if (tmd.tmd1_bits & LE_T1_ONE)
+ ifp->if_collisions++;
+ else if (tmd.tmd1_bits & LE_T1_MORE)
+ /* Real number is unknown. */
+ ifp->if_collisions += 2;
+ ifp->if_opackets++;
+ }
+
+ if (++bix == sc->sc_ntbuf)
+ bix = 0;
+
+ --sc->sc_no_td;
+ }
+
+ sc->sc_first_td = bix;
+
+ am7990_start(ifp);
+
+ if (sc->sc_no_td == 0)
+ ifp->if_timer = 0;
+}
+
+/*
+ * Controller interrupt.
+ */
+int
+am7990_intr(arg)
+ register void *arg;
+{
+ register struct am7990_softc *sc = arg;
+ register u_int16_t isr;
+
+ isr = (*sc->sc_rdcsr)(sc, LE_CSR0);
+ if ((isr & LE_C0_INTR) == 0)
+ return (0);
+
+#ifdef LEDEBUG
+ if (sc->sc_debug)
+ printf("%s: am7990_intr entering with isr=%04x\n",
+ sc->sc_dev.dv_xname, isr);
+#endif
+
+ (*sc->sc_wrcsr)(sc, LE_CSR0,
+ isr & (LE_C0_INEA | LE_C0_BABL | LE_C0_MISS | LE_C0_MERR |
+ LE_C0_RINT | LE_C0_TINT | LE_C0_IDON));
+ if (isr & LE_C0_ERR) {
+ if (isr & LE_C0_BABL) {
+#ifdef LEDEBUG
+ printf("%s: babble\n", sc->sc_dev.dv_xname);
+#endif
+ ifp->if_oerrors++;
+ }
+#if 0
+ if (isr & LE_C0_CERR) {
+ printf("%s: collision error\n", sc->sc_dev.dv_xname);
+ ifp->if_collisions++;
+ }
+#endif
+ if (isr & LE_C0_MISS) {
+#ifdef LEDEBUG
+ printf("%s: missed packet\n", sc->sc_dev.dv_xname);
+#endif
+ ifp->if_ierrors++;
+ }
+ if (isr & LE_C0_MERR) {
+ printf("%s: memory error\n", sc->sc_dev.dv_xname);
+ am7990_reset(sc);
+ return (1);
+ }
+ }
+
+ if ((isr & LE_C0_RXON) == 0) {
+ printf("%s: receiver disabled\n", sc->sc_dev.dv_xname);
+ ifp->if_ierrors++;
+ am7990_reset(sc);
+ return (1);
+ }
+ if ((isr & LE_C0_TXON) == 0) {
+ printf("%s: transmitter disabled\n", sc->sc_dev.dv_xname);
+ ifp->if_oerrors++;
+ am7990_reset(sc);
+ return (1);
+ }
+
+ if (isr & LE_C0_RINT)
+ am7990_rint(sc);
+ if (isr & LE_C0_TINT)
+ am7990_tint(sc);
+
+ return (1);
+}
+
+#undef ifp
+
+void
+am7990_watchdog(ifp)
+ struct ifnet *ifp;
+{
+ struct am7990_softc *sc = ifp->if_softc;
+
+ log(LOG_ERR, "%s: device timeout\n", sc->sc_dev.dv_xname);
+ ++ifp->if_oerrors;
+
+ am7990_reset(sc);
+}
+
+/*
+ * Setup output on interface.
+ * Get another datagram to send off of the interface queue, and map it to the
+ * interface before starting the output.
+ * Called only at splimp or interrupt level.
+ */
+void
+am7990_start(ifp)
+ register struct ifnet *ifp;
+{
+ register struct am7990_softc *sc = ifp->if_softc;
+ register int bix;
+ register struct mbuf *m;
+ struct letmd tmd;
+ int rp;
+ int len;
+
+ if ((ifp->if_flags & (IFF_RUNNING | IFF_OACTIVE)) != IFF_RUNNING)
+ return;
+
+ bix = sc->sc_last_td;
+
+ for (;;) {
+ rp = LE_TMDADDR(sc, bix);
+ (*sc->sc_copyfromdesc)(sc, &tmd, rp, sizeof(tmd));
+
+ if (tmd.tmd1_bits & LE_T1_OWN) {
+ ifp->if_flags |= IFF_OACTIVE;
+ printf("missing buffer, no_td = %d, last_td = %d\n",
+ sc->sc_no_td, sc->sc_last_td);
+ }
+
+ IF_DEQUEUE(&ifp->if_snd, m);
+ if (m == 0)
+ break;
+
+#if NBPFILTER > 0
+ /*
+ * If BPF is listening on this interface, let it see the packet
+ * before we commit it to the wire.
+ */
+ if (ifp->if_bpf)
+ bpf_mtap(ifp->if_bpf, m);
+#endif
+
+ /*
+ * Copy the mbuf chain into the transmit buffer.
+ */
+ len = am7990_put(sc, LE_TBUFADDR(sc, bix), m);
+
+#ifdef LEDEBUG
+ if (len > ETHERMTU + sizeof(struct ether_header))
+ printf("packet length %d\n", len);
+#endif
+
+ ifp->if_timer = 5;
+
+ /*
+ * Init transmit registers, and set transmit start flag.
+ */
+ tmd.tmd1_bits = LE_T1_OWN | LE_T1_STP | LE_T1_ENP;
+ tmd.tmd2 = -len | LE_XMD2_ONES;
+ tmd.tmd3 = 0;
+
+ (*sc->sc_copytodesc)(sc, &tmd, rp, sizeof(tmd));
+
+#ifdef LEDEBUG
+ if (sc->sc_debug)
+ am7990_xmit_print(sc, sc->sc_last_td);
+#endif
+
+ (*sc->sc_wrcsr)(sc, LE_CSR0, LE_C0_INEA | LE_C0_TDMD);
+
+ if (++bix == sc->sc_ntbuf)
+ bix = 0;
+
+ if (++sc->sc_no_td == sc->sc_ntbuf) {
+ ifp->if_flags |= IFF_OACTIVE;
+ break;
+ }
+
+ }
+
+ sc->sc_last_td = bix;
+}
+
+/*
+ * Process an ioctl request.
+ */
+int
+am7990_ioctl(ifp, cmd, data)
+ register struct ifnet *ifp;
+ u_long cmd;
+ caddr_t data;
+{
+ register struct am7990_softc *sc = ifp->if_softc;
+ struct ifaddr *ifa = (struct ifaddr *)data;
+ struct ifreq *ifr = (struct ifreq *)data;
+ int s, error = 0;
+
+ s = splimp();
+
+ if ((error = ether_ioctl(ifp, &sc->sc_arpcom, cmd, data)) > 0) {
+ splx(s);
+ return error;
+ }
+
+ switch (cmd) {
+
+ case SIOCSIFADDR:
+ ifp->if_flags |= IFF_UP;
+
+ switch (ifa->ifa_addr->sa_family) {
+#ifdef INET
+ case AF_INET:
+ am7990_init(sc);
+ arp_ifinit(&sc->sc_arpcom, ifa);
+ break;
+#endif
+ default:
+ am7990_init(sc);
+ break;
+ }
+ break;
+
+ case SIOCSIFFLAGS:
+ if ((ifp->if_flags & IFF_UP) == 0 &&
+ (ifp->if_flags & IFF_RUNNING) != 0) {
+ /*
+ * If interface is marked down and it is running, then
+ * stop it.
+ */
+ am7990_stop(sc);
+ ifp->if_flags &= ~IFF_RUNNING;
+ } else if ((ifp->if_flags & IFF_UP) != 0 &&
+ (ifp->if_flags & IFF_RUNNING) == 0) {
+ /*
+ * If interface is marked up and it is stopped, then
+ * start it.
+ */
+ am7990_init(sc);
+ } else {
+ /*
+ * Reset the interface to pick up changes in any other
+ * flags that affect hardware registers.
+ */
+ /*am7990_stop(sc);*/
+ am7990_init(sc);
+ }
+#if 0 /* def LEDEBUG */
+ if (ifp->if_flags & IFF_DEBUG)
+ sc->sc_debug = 1;
+ else
+ sc->sc_debug = 0;
+#endif
+ break;
+
+ case SIOCADDMULTI:
+ case SIOCDELMULTI:
+ error = (cmd == SIOCADDMULTI) ?
+ ether_addmulti(ifr, &sc->sc_arpcom) :
+ ether_delmulti(ifr, &sc->sc_arpcom);
+
+ if (error == ENETRESET) {
+ /*
+ * Multicast list has changed; set the hardware filter
+ * accordingly.
+ */
+ am7990_reset(sc);
+ error = 0;
+ }
+ break;
+
+ default:
+ error = EINVAL;
+ break;
+ }
+
+ splx(s);
+ return (error);
+}
+
+hide void
+am7990_shutdown(arg)
+ void *arg;
+{
+
+ am7990_stop((struct am7990_softc *)arg);
+}
+
+#ifdef LEDEBUG
+void
+am7990_recv_print(sc, no)
+ struct am7990_softc *sc;
+ int no;
+{
+ struct lermd rmd;
+ u_int16_t len;
+ struct ether_header eh;
+
+ (*sc->sc_copyfromdesc)(sc, &rmd, LE_RMDADDR(sc, no), sizeof(rmd));
+ len = rmd.rmd3;
+ printf("%s: receive buffer %d, len = %d\n", sc->sc_dev.dv_xname, no,
+ len);
+ printf("%s: status %04x\n", sc->sc_dev.dv_xname,
+ (*sc->sc_rdcsr)(sc, LE_CSR0));
+ printf("%s: ladr %04x, hadr %02x, flags %02x, bcnt %04x, mcnt %04x\n",
+ sc->sc_dev.dv_xname,
+ rmd.rmd0, rmd.rmd1_hadr, rmd.rmd1_bits, rmd.rmd2, rmd.rmd3);
+ if (len >= sizeof(eh)) {
+ (*sc->sc_copyfrombuf)(sc, &eh, LE_RBUFADDR(sc, no), sizeof(eh));
+ printf("%s: dst %s", sc->sc_dev.dv_xname,
+ ether_sprintf(eh.ether_dhost));
+ printf(" src %s type %04x\n", ether_sprintf(eh.ether_shost),
+ ntohs(eh.ether_type));
+ }
+}
+
+void
+am7990_xmit_print(sc, no)
+ struct am7990_softc *sc;
+ int no;
+{
+ struct letmd tmd;
+ u_int16_t len;
+ struct ether_header eh;
+
+ (*sc->sc_copyfromdesc)(sc, &tmd, LE_TMDADDR(sc, no), sizeof(tmd));
+ len = -tmd.tmd2;
+ printf("%s: transmit buffer %d, len = %d\n", sc->sc_dev.dv_xname, no,
+ len);
+ printf("%s: status %04x\n", sc->sc_dev.dv_xname,
+ (*sc->sc_rdcsr)(sc, LE_CSR0));
+ printf("%s: ladr %04x, hadr %02x, flags %02x, bcnt %04x, mcnt %04x\n",
+ sc->sc_dev.dv_xname,
+ tmd.tmd0, tmd.tmd1_hadr, tmd.tmd1_bits, tmd.tmd2, tmd.tmd3);
+ if (len >= sizeof(eh)) {
+ (*sc->sc_copyfrombuf)(sc, &eh, LE_TBUFADDR(sc, no), sizeof(eh));
+ printf("%s: dst %s", sc->sc_dev.dv_xname,
+ ether_sprintf(eh.ether_dhost));
+ printf(" src %s type %04x\n", ether_sprintf(eh.ether_shost),
+ ntohs(eh.ether_type));
+ }
+}
+#endif /* LEDEBUG */
+
+/*
+ * Set up the logical address filter.
+ */
+void
+am7990_setladrf(ac, af)
+ struct arpcom *ac;
+ u_int16_t *af;
+{
+ struct ifnet *ifp = &ac->ac_if;
+ struct ether_multi *enm;
+ register u_char *cp, c;
+ register u_int32_t 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 an
+ * index into the 64 bit logical address filter. The high order bit
+ * selects the word, while the rest of the bits select the bit within
+ * the word.
+ */
+
+ if (ifp->if_flags & IFF_PROMISC)
+ goto allmulti;
+
+ af[0] = af[1] = af[2] = af[3] = 0x0000;
+ ETHER_FIRST_MULTI(step, ac, enm);
+ while (enm != NULL) {
+ if (ETHER_CMP(enm->enm_addrlo, enm->enm_addrhi)) {
+ /*
+ * 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.)
+ */
+ goto allmulti;
+ }
+
+ cp = enm->enm_addrlo;
+ crc = 0xffffffff;
+ for (len = sizeof(enm->enm_addrlo); --len >= 0;) {
+ c = *cp++;
+ for (i = 8; --i >= 0;) {
+ if ((crc & 0x01) ^ (c & 0x01)) {
+ crc >>= 1;
+ crc ^= 0xedb88320;
+ } else
+ crc >>= 1;
+ c >>= 1;
+ }
+ }
+ /* Just want the 6 most significant bits. */
+ crc >>= 26;
+
+ /* Set the corresponding bit in the filter. */
+ af[crc >> 4] |= 1 << (crc & 0xf);
+
+ ETHER_NEXT_MULTI(step, enm);
+ }
+ ifp->if_flags &= ~IFF_ALLMULTI;
+ return;
+
+allmulti:
+ ifp->if_flags |= IFF_ALLMULTI;
+ af[0] = af[1] = af[2] = af[3] = 0xffff;
+}
+
+
+/*
+ * Routines for accessing the transmit and receive buffers.
+ * The various CPU and adapter configurations supported by this
+ * driver require three different access methods for buffers
+ * and descriptors:
+ * (1) contig (contiguous data; no padding),
+ * (2) gap2 (two bytes of data followed by two bytes of padding),
+ * (3) gap16 (16 bytes of data followed by 16 bytes of padding).
+ */
+
+/*
+ * contig: contiguous data with no padding.
+ *
+ * Buffers may have any alignment.
+ */
+
+void
+am7990_copytobuf_contig(sc, from, boff, len)
+ struct am7990_softc *sc;
+ void *from;
+ int boff, len;
+{
+ volatile caddr_t buf = sc->sc_mem;
+
+/* printf ("Copytobuf: %p -> %p\n", from, buf + boff); */
+ /*
+ * Just call bcopy() to do the work.
+ */
+ bcopy(from, buf + boff, len);
+}
+
+void
+am7990_copyfrombuf_contig(sc, to, boff, len)
+ struct am7990_softc *sc;
+ void *to;
+ int boff, len;
+{
+ volatile caddr_t buf = sc->sc_mem;
+
+ /*
+ * Just call bcopy() to do the work.
+ */
+ bcopy(buf + boff, to, len);
+}
+
+void
+am7990_zerobuf_contig(sc, boff, len)
+ struct am7990_softc *sc;
+ int boff, len;
+{
+ volatile caddr_t buf = sc->sc_mem;
+
+ /*
+ * Just let bzero() do the work
+ */
+ bzero(buf + boff, len);
+}
+
diff --git a/sys/arch/kbus/dev/am7990reg.h b/sys/arch/kbus/dev/am7990reg.h
new file mode 100644
index 00000000000..6bf8b61248c
--- /dev/null
+++ b/sys/arch/kbus/dev/am7990reg.h
@@ -0,0 +1,179 @@
+/* $OpenBSD: am7990reg.h,v 1.1 1997/10/14 07:25:30 gingold Exp $ */
+/* $NetBSD: am7990reg.h,v 1.1 1995/04/11 04:17:50 mycroft Exp $ */
+
+/*-
+ * Copyright (c) 1995 Charles M. Hannum. All rights reserved.
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Ralph Campbell and Rick Macklem.
+ *
+ * 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.1 (Berkeley) 6/10/93
+ */
+
+#define LEBLEN 1536 /* ETHERMTU + header + CRC */
+#define LEMINSIZE 60 /* should be 64 if mode DTCR is set */
+
+/*
+ * Receive message descriptor
+ */
+struct lermd {
+ u_int16_t rmd0;
+#if BYTE_ORDER == BIG_ENDIAN
+ u_int8_t rmd1_bits;
+ u_int8_t rmd1_hadr;
+#else
+ u_int8_t rmd1_hadr;
+ u_int8_t rmd1_bits;
+#endif
+ int16_t rmd2;
+ u_int16_t rmd3;
+};
+
+/*
+ * Transmit message descriptor
+ */
+struct letmd {
+ u_int16_t tmd0;
+#if BYTE_ORDER == BIG_ENDIAN
+ u_int8_t tmd1_bits;
+ u_int8_t tmd1_hadr;
+#else
+ u_int8_t tmd1_hadr;
+ u_int8_t tmd1_bits;
+#endif
+ int16_t tmd2;
+ u_int16_t tmd3;
+};
+
+/*
+ * Initialization block
+ */
+struct leinit {
+ u_int16_t init_mode; /* +0x0000 */
+ u_int16_t init_padr[3]; /* +0x0002 */
+ u_int16_t init_ladrf[4]; /* +0x0008 */
+ u_int16_t init_rdra; /* +0x0010 */
+ u_int16_t init_rlen; /* +0x0012 */
+ u_int16_t init_tdra; /* +0x0014 */
+ u_int16_t init_tlen; /* +0x0016 */
+ int16_t pad0[4]; /* Pad to 16 shorts */
+};
+
+#define LE_INITADDR(sc) (sc->sc_initaddr)
+#define LE_RMDADDR(sc, bix) (sc->sc_rmdaddr + sizeof(struct lermd) * (bix))
+#define LE_TMDADDR(sc, bix) (sc->sc_tmdaddr + sizeof(struct letmd) * (bix))
+#define LE_RBUFADDR(sc, bix) (sc->sc_rbufaddr + LEBLEN * (bix))
+#define LE_TBUFADDR(sc, bix) (sc->sc_tbufaddr + LEBLEN * (bix))
+
+/* register addresses */
+#define LE_CSR0 0x0000 /* Control and status register */
+#define LE_CSR1 0x0001 /* low address of init block */
+#define LE_CSR2 0x0002 /* high address of init block */
+#define LE_CSR3 0x0003 /* 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 0x0004 /* byte swap */
+#define LE_C3_ACON 0x0002 /* ALE control, eh? */
+#define LE_C3_BCON 0x0001 /* byte control */
+
+/* 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/kbus/dev/am7990var.h b/sys/arch/kbus/dev/am7990var.h
new file mode 100644
index 00000000000..9b8e19b2138
--- /dev/null
+++ b/sys/arch/kbus/dev/am7990var.h
@@ -0,0 +1,147 @@
+/* $OpenBSD: am7990var.h,v 1.1 1997/10/14 07:25:30 gingold Exp $ */
+/* $NetBSD: am7990var.h,v 1.8 1996/07/05 23:57:01 abrown Exp $ */
+
+/*
+ * Copyright (c) 1995 Charles M. Hannum. 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 M. Hannum.
+ * 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.
+ */
+
+#ifdef DDB
+#define integrate
+#define hide
+#else
+#define integrate static __inline
+#define hide static
+#endif
+
+/*
+ * Ethernet software status per device.
+ *
+ * Each interface is referenced by a network interface structure,
+ * arpcom.ac_if, which the routing code uses to locate the interface.
+ * This structure contains the output queue for the interface, its address, ...
+ *
+ * NOTE: this structure MUST be the first element in machine-dependent
+ * le_softc structures! This is designed SPECIFICALLY to make it possible
+ * to simply cast a "void *" to "struct le_softc *" or to
+ * "struct am7990_softc *". Among other things, this saves a lot of hair
+ * in the interrupt handlers.
+ */
+struct am7990_softc {
+ struct device sc_dev; /* base device glue */
+ struct arpcom sc_arpcom; /* Ethernet common part */
+
+ /*
+ * Memory functions:
+ *
+ * copy to/from descriptor
+ * copy to/from buffer
+ * zero bytes in buffer
+ */
+ void (*sc_copytodesc)
+ __P((struct am7990_softc *, void *, int, int));
+ void (*sc_copyfromdesc)
+ __P((struct am7990_softc *, void *, int, int));
+ void (*sc_copytobuf)
+ __P((struct am7990_softc *, void *, int, int));
+ void (*sc_copyfrombuf)
+ __P((struct am7990_softc *, void *, int, int));
+ void (*sc_zerobuf)
+ __P((struct am7990_softc *, int, int));
+
+ /*
+ * Machine-dependent functions:
+ *
+ * read/write CSR
+ * hardware init hook - may be NULL
+ * no carrier hook - may be NULL
+ */
+ u_int16_t (*sc_rdcsr)
+ __P((struct am7990_softc *, u_int16_t));
+ void (*sc_wrcsr)
+ __P((struct am7990_softc *, u_int16_t, u_int16_t));
+ void (*sc_hwinit) __P((struct am7990_softc *));
+ void (*sc_nocarrier) __P((struct am7990_softc *));
+
+ void *sc_sh; /* shutdownhook cookie */
+
+ u_int16_t sc_conf3; /* CSR3 value */
+
+ void *sc_mem; /* base address of RAM -- CPU's view */
+ u_long sc_addr; /* base address of RAM -- LANCE's view */
+
+ u_long sc_memsize; /* size of RAM */
+
+ int sc_nrbuf; /* number of receive buffers */
+ int sc_ntbuf; /* number of transmit buffers */
+ int sc_last_rd;
+ int sc_first_td, sc_last_td, sc_no_td;
+
+ int sc_initaddr;
+ int sc_rmdaddr;
+ int sc_tmdaddr;
+ int sc_rbufaddr;
+ int sc_tbufaddr;
+
+#ifdef LEDEBUG
+ int sc_debug;
+#endif
+};
+
+/* Export this to machine-dependent drivers. */
+extern struct cfdriver le_cd;
+
+void am7990_config __P((struct am7990_softc *));
+void am7990_init __P((struct am7990_softc *));
+int am7990_ioctl __P((struct ifnet *, u_long, caddr_t));
+void am7990_meminit __P((struct am7990_softc *));
+void am7990_reset __P((struct am7990_softc *));
+void am7990_setladrf __P((struct arpcom *, u_int16_t *));
+void am7990_start __P((struct ifnet *));
+void am7990_stop __P((struct am7990_softc *));
+void am7990_watchdog __P((struct ifnet *));
+int am7990_intr __P((void *));
+
+/*
+ * The following functions are only useful on certain cpu/bus
+ * combinations. They should be written in assembly language for
+ * maximum efficiency, but machine-independent versions are provided
+ * for drivers that have not yet been optimized.
+ */
+void am7990_copytobuf_contig __P((struct am7990_softc *, void *, int, int));
+void am7990_copyfrombuf_contig __P((struct am7990_softc *, void *, int, int));
+void am7990_zerobuf_contig __P((struct am7990_softc *, int, int));
+
+#if 0 /* Example only - see am7990.c */
+void am7990_copytobuf_gap2 __P((struct am7990_softc *, void *, int, int));
+void am7990_copyfrombuf_gap2 __P((struct am7990_softc *, void *, int, int));
+void am7990_zerobuf_gap2 __P((struct am7990_softc *, int, int));
+
+void am7990_copytobuf_gap16 __P((struct am7990_softc *, void *, int, int));
+void am7990_copyfrombuf_gap16 __P((struct am7990_softc *, void *, int, int));
+void am7990_zerobuf_gap16 __P((struct am7990_softc *, int, int));
+#endif /* Example only */
diff --git a/sys/arch/kbus/dev/dmavar.h b/sys/arch/kbus/dev/dmavar.h
new file mode 100644
index 00000000000..a32ebda6c89
--- /dev/null
+++ b/sys/arch/kbus/dev/dmavar.h
@@ -0,0 +1,47 @@
+/* $OpenBSD: dmavar.h,v 1.1 1997/10/14 07:25:30 gingold Exp $ */
+
+/*
+ * Copyright (c) 1982, 1990 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.
+ *
+ * @(#)dmavar.h 7.2 (Berkeley) 11/4/90
+ */
+
+/* dmago flags */
+#define DMAGO_READ 0x08 /* transfer is a read */
+#define DMAGO_NOINT 0x80 /* don't interrupt on completion */
+
+#ifdef _KERNEL
+typedef void (*dmafree_t) (void *dev);
+typedef int (*dmago_t) (void *dev, char *, int, int);
+typedef int (*dmanext_t) (void *dev);
+typedef void (*dmastop_t) (void *dev);
+#endif
diff --git a/sys/arch/kbus/dev/if_le.c b/sys/arch/kbus/dev/if_le.c
new file mode 100644
index 00000000000..d9c779fc906
--- /dev/null
+++ b/sys/arch/kbus/dev/if_le.c
@@ -0,0 +1,183 @@
+/* $OpenBSD: if_le.c,v 1.1 1997/10/14 07:25:29 gingold Exp $ */
+/* $NetBSD: if_le.c,v 1.33 1996/11/20 18:56:52 gwr Exp $ */
+
+/*-
+ * Copyright (c) 1996 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Adam Glass and Gordon W. Ross.
+ *
+ * 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 NetBSD
+ * Foundation, Inc. and its contributors.
+ * 4. Neither the name of The NetBSD Foundation 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 NETBSD FOUNDATION, INC. 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.
+ */
+
+#include "bpfilter.h"
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/mbuf.h>
+#include <sys/syslog.h>
+#include <sys/socket.h>
+#include <sys/device.h>
+
+#include <net/if.h>
+
+#include <netinet/in.h>
+#include <netinet/if_ether.h>
+
+#include <machine/autoconf.h>
+#include <machine/cpu.h>
+#include <machine/prom.h>
+#include <machine/ioasic.h>
+
+#include <vm/vm.h>
+#include <vm/vm_kern.h>
+
+#include "am7990reg.h"
+#include "am7990var.h"
+
+static char *le_pages[2 * 0x8000 + 2 * 0x2000];
+
+/*
+ * LANCE registers.
+ * The real stuff is in dev/ic/am7990reg.h
+ */
+struct lereg1 {
+ volatile u_int16_t ler1_rdp; /* data port */
+ char pad0 [30];
+ volatile u_int16_t ler1_rap; /* register select port */
+ char pad1 [30];
+};
+
+/*
+ * Ethernet software status per interface.
+ * The real stuff is in dev/ic/am7990var.h
+ */
+struct le_softc {
+ struct am7990_softc sc_am7990; /* glue to MI code */
+
+ struct lereg1 *sc_r1; /* LANCE registers */
+};
+
+static int le_match __P((struct device *, void *, void *));
+static void le_attach __P((struct device *, struct device *, void *));
+
+struct cfattach sle_ca = {
+ sizeof(struct le_softc), le_match, le_attach
+};
+
+hide void lewrcsr __P((struct am7990_softc *, u_int16_t, u_int16_t));
+hide u_int16_t lerdcsr __P((struct am7990_softc *, u_int16_t));
+
+hide void
+lewrcsr(sc, port, val)
+ struct am7990_softc *sc;
+ u_int16_t port, val;
+{
+ register struct lereg1 *ler1 = ((struct le_softc *)sc)->sc_r1;
+
+ ler1->ler1_rap = port;
+ ler1->ler1_rdp = val;
+}
+
+hide u_int16_t
+lerdcsr(sc, port)
+ struct am7990_softc *sc;
+ u_int16_t port;
+{
+ register struct lereg1 *ler1 = ((struct le_softc *)sc)->sc_r1;
+ u_int16_t val;
+
+ ler1->ler1_rap = port;
+ val = ler1->ler1_rdp;
+ return (val);
+}
+
+int
+le_match(parent, vcf, aux)
+ struct device *parent;
+ void *vcf, *aux;
+{
+ struct confargs *ca = aux;
+
+ if (ca->ca_paddr != LANCE_ADDR)
+ return 0;
+
+ /* Make sure there is something there... */
+ if (bus_peek(ca->ca_bustype, ca->ca_paddr, 1) == -1)
+ return (0);
+
+ /* Default interrupt priority. */
+ if (ca->ca_intpri == -1)
+ ca->ca_intpri = 3;
+
+ return (1);
+}
+
+static struct intrhand levelhard = {am7990_intr};
+
+void
+le_attach(parent, self, aux)
+ struct device *parent, *self;
+ void *aux;
+{
+ struct le_softc *lesc = (struct le_softc *)self;
+ struct am7990_softc *sc = &lesc->sc_am7990;
+
+ if (!ioasic)
+ panic ("IOASIC not mapped");
+ lesc->sc_r1 = (struct lereg1 *)((char *)ioasic + LANCE_OFFSET);
+
+ sc->sc_memsize = 0xa000; /* 40K */
+ sc->sc_mem = (void *)(((u_long)le_pages + 0x7fff) & 0xffff8000);
+ sc->sc_addr = (u_long)sc->sc_mem & 0xffffff;
+ sc->sc_conf3 = LE_C3_BSWP;
+
+ idprom_etheraddr(sc->sc_arpcom.ac_enaddr);
+
+ sc->sc_copytodesc = am7990_copytobuf_contig;
+ sc->sc_copyfromdesc = am7990_copyfrombuf_contig;
+ sc->sc_copytobuf = am7990_copytobuf_contig;
+ sc->sc_copyfrombuf = am7990_copyfrombuf_contig;
+ sc->sc_zerobuf = am7990_zerobuf_contig;
+
+ sc->sc_rdcsr = lerdcsr;
+ sc->sc_wrcsr = lewrcsr;
+ sc->sc_hwinit = NULL;
+#ifdef LEDEBUG
+ sc->sc_debug = 0;
+#endif
+
+ am7990_config(sc);
+
+ /* Install interrupt handler. */
+ levelhard.ih_arg = (void *)sc;
+ intr_establish (INTR_LANCE, IH_CAN_DELAY, &levelhard);
+/* isr_add_autovect(am7990_intr, (void *)sc, ca->ca_intpri); */
+}
diff --git a/sys/arch/kbus/dev/kbus.c b/sys/arch/kbus/dev/kbus.c
new file mode 100644
index 00000000000..f23be84b314
--- /dev/null
+++ b/sys/arch/kbus/dev/kbus.c
@@ -0,0 +1,110 @@
+/* $OpenBSD: kbus.c,v 1.1 1997/10/14 07:25:29 gingold Exp $ */
+/* $NetBSD: kbus.c,v 1.23 1996/11/20 18:56:56 gwr Exp $ */
+
+/*-
+ * Copyright (c) 1996 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Adam Glass and Gordon W. Ross.
+ *
+ * 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 NetBSD
+ * Foundation, Inc. and its contributors.
+ * 4. Neither the name of The NetBSD Foundation 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 NETBSD FOUNDATION, INC. 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.
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/device.h>
+
+#include <machine/autoconf.h>
+#include <machine/kbus.h>
+#include <machine/ioasic.h>
+
+static int kbus_match __P((struct device *, void *, void *));
+static void kbus_attach __P((struct device *, struct device *, void *));
+static int ioasic_intr __P((void *arg));
+
+#if 0
+static int kbus_print __P((void *, const char *parentname));
+static int kbus_submatch __P((struct device *, void *, void *));
+#endif
+
+struct cfattach kbus_ca = {
+ sizeof(struct device), kbus_match, kbus_attach
+};
+
+struct cfdriver kbus_cd = {
+ NULL, "kbus", DV_DULL
+};
+
+/* A mapped page for IOASIC. */
+struct ioasic_reg *ioasic;
+
+static struct intrhand ioasic_intrhand = {ioasic_intr};
+
+static int
+kbus_match(parent, vcf, aux)
+ struct device *parent;
+ void *vcf, *aux;
+{
+ struct confargs *ca = aux;
+
+ if (ca->ca_bustype != BUS_KBUS)
+ return (0);
+ return(1);
+}
+
+static void
+kbus_attach(parent, self, aux)
+ struct device *parent;
+ struct device *self;
+ void *aux;
+{
+ if (!ioasic)
+ {
+ ioasic = (struct ioasic_reg *)
+ bus_mapin (BUS_KBUS, IOASIC_ADDR, IOASIC_SIZE);
+ if (!ioasic)
+ panic ("Cannot map IOASIC\n");
+ intr_establish (INTR_IOASIC, 0, &ioasic_intrhand);
+ }
+
+ printf("\n");
+
+ config_search (bus_scan, self, aux);
+}
+
+static int
+ioasic_intr (arg)
+ void *arg;
+{
+ unsigned int val = ioasic->ioasic_ir;
+ printf ("int 131, val = 0x%x\n", val);
+ return 0;
+}
+
diff --git a/sys/arch/kbus/dev/rd.c b/sys/arch/kbus/dev/rd.c
new file mode 100644
index 00000000000..aa9d9be66d6
--- /dev/null
+++ b/sys/arch/kbus/dev/rd.c
@@ -0,0 +1,193 @@
+/*
+ * Copyright (c) 1995 Philip A. Nelson.
+ * 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 Philip A. Nelson.
+ * 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.
+ *
+ * $Id: rd.c,v 1.1 1997/10/14 07:25:30 gingold Exp $
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/buf.h>
+#include <sys/device.h>
+#include <sys/conf.h>
+#include <sys/disk.h>
+
+static int rdmatch(struct device *parent, void *cf, void *aux);
+static void rdattach(struct device *parent, struct device *self, void *aux);
+
+struct rdsoftc {
+ struct device sc_dev; /* generic device glue */
+ struct disk sc_dkdev; /* generic disk glue */
+};
+
+struct cfdriver rdcd = {
+ NULL,
+ "rd",
+ rdmatch,
+ rdattach,
+ DV_DISK,
+ sizeof(struct rdsoftc),
+ NULL,
+ 0
+};
+
+void rdstrategy __P((struct buf *));
+
+struct dkdriver rddkdriver = { rdstrategy };
+
+#if !defined(RD_SIZE)
+# define RD_SIZE 0x200000
+#endif
+
+u_char ram_disk[RD_SIZE] = "Ramdiskorigin";
+
+static int
+rdmatch(parent, cf, aux)
+ struct device *parent;
+ void *cf, *aux;
+{
+ return(((struct cfdata *)cf)->cf_unit == 0);
+}
+
+static void
+rdattach(parent, self, aux)
+ struct device *parent, *self;
+ void *aux;
+{
+ struct rdsoftc *sc = (struct rdsoftc *)self;
+
+ printf(" addr 0x%x, size 0x%x\n", ram_disk, RD_SIZE);
+
+ /*
+ * Initialize and attach the disk structure.
+ */
+ bzero(&sc->sc_dkdev, sizeof(sc->sc_dkdev));
+ sc->sc_dkdev.dk_driver = &rddkdriver;
+ sc->sc_dkdev.dk_name = sc->sc_dev.dv_xname;
+ disk_attach(&sc->sc_dkdev);
+}
+
+
+/* operational routines */
+
+int
+rdopen(dev, flags, devtype, p)
+ dev_t dev;
+ int flags, devtype;
+ struct proc *p;
+{
+ if (minor(dev) == 0)
+ return(0);
+ else
+ return(ENXIO);
+}
+
+int
+rdclose(dev, flags, devtype, p)
+ dev_t dev;
+ int flags, devtype;
+ struct proc *p;
+{
+ return(0);
+}
+
+int
+rdioctl(dev, cmd, addr, flag, p)
+ dev_t dev;
+ u_long cmd;
+ int flag;
+ caddr_t addr;
+ struct proc *p;
+{
+ return(ENOTTY);
+}
+
+int
+rdsize(dev)
+ dev_t dev;
+{
+ if (minor(dev) == 0)
+ return(RD_SIZE / DEV_BSIZE);
+ else
+ return(0);
+}
+
+int
+rddump(dev, blkno, va, size)
+ dev_t dev;
+ daddr_t blkno;
+ caddr_t va;
+ size_t size;
+{
+ return(ENXIO);
+}
+
+void
+rdstrategy(bp)
+ struct buf *bp;
+{
+ int loc, size;
+ char *adr;
+
+ if (minor(bp->b_dev) == 0)
+ loc = bp->b_blkno * DEV_BSIZE;
+ else {
+ bp->b_error = EINVAL;
+ bp->b_flags |= B_ERROR;
+ return;
+ }
+ size = bp->b_bcount;
+ adr = (char *) bp->b_un.b_addr;
+ if (loc + size > sizeof(ram_disk)) {
+ bp->b_error = EINVAL;
+ bp->b_flags |= B_ERROR;
+ return;
+ }
+ if (bp->b_flags & B_READ)
+ bcopy(&ram_disk[loc], adr, size);
+ else
+ bcopy(adr, &ram_disk[loc], size);
+ biodone(bp);
+}
+
+int
+rdread(dev, uio)
+ dev_t dev;
+ struct uio *uio;
+{
+ return(physio(rdstrategy, NULL, dev, B_READ, minphys, uio));
+}
+
+int
+rdwrite(dev, uio)
+ dev_t dev;
+ struct uio *uio;
+{
+ return(physio(rdstrategy, NULL, dev, B_WRITE, minphys, uio));
+}
diff --git a/sys/arch/kbus/dev/sbic.c b/sys/arch/kbus/dev/sbic.c
new file mode 100644
index 00000000000..761692200b0
--- /dev/null
+++ b/sys/arch/kbus/dev/sbic.c
@@ -0,0 +1,2913 @@
+/* $OpenBSD: sbic.c,v 1.1 1997/10/14 07:25:29 gingold Exp $ */
+/* $NetBSD: sbic.c,v 1.28 1996/10/13 03:07:29 christos Exp $ */
+
+/*
+ * Copyright (c) 1994 Christian E. Hopps
+ * Copyright (c) 1990 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Van Jacobson of Lawrence Berkeley Laboratory.
+ *
+ * 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.
+ *
+ * @(#)scsi.c 7.5 (Berkeley) 5/4/91
+ */
+
+/*
+ * AMIGA AMD 33C93 scsi adaptor driver
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/device.h>
+#include <sys/kernel.h> /* For hz */
+#include <sys/disklabel.h>
+#include <sys/dkstat.h>
+#include <sys/buf.h>
+#include <scsi/scsi_all.h>
+#include <scsi/scsiconf.h>
+#include <vm/vm.h>
+#include <vm/vm_kern.h>
+#include <vm/vm_page.h>
+#include <machine/pmap.h>
+#include <machine/cpu.h>
+#include "dmavar.h"
+#include "sbicreg.h"
+#include "sbicvar.h"
+
+#include <vm/pmap.h>
+
+extern int kvtop __P((caddr_t));
+
+/* Since I can't find this in any other header files */
+#define SCSI_PHASE(reg) (reg&0x07)
+
+/*
+ * SCSI delays
+ * In u-seconds, primarily for state changes on the SPC.
+ */
+#define SBIC_CMD_WAIT 50000 /* wait per step of 'immediate' cmds */
+#define SBIC_DATA_WAIT 50000 /* wait per data in/out step */
+#define SBIC_INIT_WAIT 50000 /* wait per step (both) during init */
+
+#define b_cylin b_resid
+#define SBIC_WAIT(regs, until, timeo) sbicwait(regs, until, timeo, __LINE__)
+
+int sbicicmd __P((struct sbic_softc *, int, int, void *, int, void *, int));
+int sbicgo __P((struct sbic_softc *, struct scsi_xfer *));
+int sbicdmaok __P((struct sbic_softc *, struct scsi_xfer *));
+int sbicwait __P((sbic_regmap_p, char, int , int));
+int sbiccheckdmap __P((void *, u_long, u_long));
+int sbicselectbus __P((struct sbic_softc *, sbic_regmap_p, u_char, u_char, u_char));
+int sbicxfstart __P((sbic_regmap_p, int, u_char, int));
+int sbicxfout __P((sbic_regmap_p regs, int, void *, int));
+int sbicfromscsiperiod __P((struct sbic_softc *, sbic_regmap_p, int));
+int sbictoscsiperiod __P((struct sbic_softc *, sbic_regmap_p, int));
+int sbicpoll __P((struct sbic_softc *));
+int sbicnextstate __P((struct sbic_softc *, u_char, u_char));
+int sbicmsgin __P((struct sbic_softc *));
+int sbicxfin __P((sbic_regmap_p regs, int, void *));
+int sbicabort __P((struct sbic_softc *, sbic_regmap_p, char *));
+void sbicxfdone __P((struct sbic_softc *, sbic_regmap_p, int));
+void sbicerror __P((struct sbic_softc *, sbic_regmap_p, u_char));
+void sbicstart __P((struct sbic_softc *));
+void sbicreset __P((struct sbic_softc *));
+void sbic_scsidone __P((struct sbic_acb *, int));
+void sbic_sched __P((struct sbic_softc *));
+void sbic_save_ptrs __P((struct sbic_softc *, sbic_regmap_p,int,int));
+void sbic_load_ptrs __P((struct sbic_softc *, sbic_regmap_p,int,int));
+#ifdef DEBUG
+void sbicdumpstate __P((void));
+void sbic_dump_acb __P((struct sbic_acb *));
+#endif
+
+/*
+ * Synch xfer parameters, and timing conversions
+ */
+int sbic_min_period = SBIC_SYN_MIN_PERIOD; /* in cycles = f(ICLK,FSn) */
+int sbic_max_offset = SBIC_SYN_MAX_OFFSET; /* pure number */
+
+int sbic_cmd_wait = SBIC_CMD_WAIT;
+int sbic_data_wait = SBIC_DATA_WAIT;
+int sbic_init_wait = SBIC_INIT_WAIT;
+
+/*
+ * was broken before.. now if you want this you get it for all drives
+ * on sbic controllers.
+ */
+u_char sbic_inhibit_sync[8];
+int sbic_enable_reselect = 1;
+int sbic_clock_override = 0;
+int sbic_no_dma = 0;
+int sbic_parallel_operations = 1;
+
+#ifdef DEBUG
+sbic_regmap_p debug_sbic_regs;
+int sbicdma_ops = 0; /* total DMA operations */
+int sbicdma_bounces = 0; /* number operations using bounce buffer */
+int sbicdma_hits = 0; /* number of DMA chains that were contiguous */
+int sbicdma_misses = 0; /* number of DMA chains that were not contiguous */
+int sbicdma_saves = 0;
+#define QPRINTF(a) if (sbic_debug > 1) printf a
+int sbic_debug = 2;
+int sync_debug = 0;
+int sbic_dma_debug = 0;
+int reselect_debug = 0;
+int report_sense = 0;
+int data_pointer_debug = 2;
+u_char debug_asr, debug_csr, routine;
+void sbictimeout __P((struct sbic_softc *dev));
+
+#define CSR_TRACE_SIZE 32
+#if CSR_TRACE_SIZE
+#define CSR_TRACE(w,c,a,x) do { \
+ int s = splbio(); \
+ csr_trace[csr_traceptr].whr = (w); csr_trace[csr_traceptr].csr = (c); \
+ csr_trace[csr_traceptr].asr = (a); csr_trace[csr_traceptr].xtn = (x); \
+ csr_traceptr = (csr_traceptr + 1) & (CSR_TRACE_SIZE - 1); \
+ splx(s); \
+} while (0)
+int csr_traceptr;
+int csr_tracesize = CSR_TRACE_SIZE;
+struct {
+ u_char whr;
+ u_char csr;
+ u_char asr;
+ u_char xtn;
+} csr_trace[CSR_TRACE_SIZE];
+#else
+#define CSR_TRACE(w,c,a,x)
+#endif
+
+#define SBIC_TRACE_SIZE 16
+#if SBIC_TRACE_SIZE
+#define SBIC_TRACE(dev) do { \
+ int s = splbio(); \
+ sbic_trace[sbic_traceptr].sp = &s; \
+ sbic_trace[sbic_traceptr].line = __LINE__; \
+ sbic_trace[sbic_traceptr].sr = s; \
+ sbic_trace[sbic_traceptr].csr = csr_traceptr; \
+ sbic_traceptr = (sbic_traceptr + 1) & (SBIC_TRACE_SIZE - 1); \
+ splx(s); \
+} while (0)
+int sbic_traceptr;
+int sbic_tracesize = SBIC_TRACE_SIZE;
+struct {
+ void *sp;
+ u_short line;
+ u_short sr;
+ int csr;
+} sbic_trace[SBIC_TRACE_SIZE];
+#else
+#define SBIC_TRACE(dev)
+#endif
+
+#else /* DEBUG */
+#define QPRINTF(a)
+#define CSR_TRACE(w,c,a,x)
+#define SBIC_TRACE(dev)
+#endif /* DEBUG */
+
+/*
+ * default minphys routine for sbic based controllers
+ */
+void
+sbic_minphys(bp)
+ struct buf *bp;
+{
+
+ /*
+ * No max transfer at this level.
+ */
+ minphys(bp);
+}
+
+/*
+ * Save DMA pointers. Take into account partial transfer. Shut down DMA.
+ */
+void
+sbic_save_ptrs(dev, regs, target, lun)
+ struct sbic_softc *dev;
+ sbic_regmap_p regs;
+ int target, lun;
+{
+ int count, asr, s;
+ struct sbic_acb* acb;
+
+ SBIC_TRACE(dev);
+ if( !dev->sc_cur ) return;
+ if( !(dev->sc_flags & SBICF_INDMA) ) return; /* DMA not active */
+
+ s = splbio();
+
+ acb = dev->sc_nexus;
+ count = -1;
+ do {
+ GET_SBIC_asr(regs, asr);
+ if( asr & SBIC_ASR_DBR ) {
+ printf("sbic_save_ptrs: asr %02x canceled!\n", asr);
+ splx(s);
+ SBIC_TRACE(dev);
+ return;
+ }
+ } while( asr & (SBIC_ASR_BSY|SBIC_ASR_CIP) );
+
+ /* Save important state */
+ /* must be done before dmastop */
+ acb->sc_dmacmd = dev->sc_dmacmd;
+ SBIC_TC_GET(regs, count);
+
+ /* Shut down DMA ====CAREFUL==== */
+ dev->sc_dmastop(dev);
+ dev->sc_flags &= ~SBICF_INDMA;
+ SBIC_TC_PUT(regs, 0);
+
+#ifdef DEBUG
+ if(!count && sbic_debug) printf("%dcount0",target);
+ if(data_pointer_debug == -1)
+ printf("SBIC saving target %d data pointers from (%p,%x)%xASR:%02x",
+ target, dev->sc_cur->dc_addr, dev->sc_cur->dc_count,
+ acb->sc_dmacmd, asr);
+#endif
+
+ /* Fixup partial xfers */
+ acb->sc_kv.dc_addr += (dev->sc_tcnt - count);
+ acb->sc_kv.dc_count -= (dev->sc_tcnt - count);
+ acb->sc_pa.dc_addr += (dev->sc_tcnt - count);
+ acb->sc_pa.dc_count -= ((dev->sc_tcnt - count)>>1);
+
+ acb->sc_tcnt = dev->sc_tcnt = count;
+#ifdef DEBUG
+ if(data_pointer_debug)
+ printf(" at (%p,%x):%x\n",
+ dev->sc_cur->dc_addr, dev->sc_cur->dc_count,count);
+ sbicdma_saves++;
+#endif
+ splx(s);
+ SBIC_TRACE(dev);
+}
+
+
+/*
+ * DOES NOT RESTART DMA!!!
+ */
+void sbic_load_ptrs(dev, regs, target, lun)
+ struct sbic_softc *dev;
+ sbic_regmap_p regs;
+ int target, lun;
+{
+ int s, count;
+ char* vaddr, * paddr;
+ struct sbic_acb *acb;
+
+ SBIC_TRACE(dev);
+ acb = dev->sc_nexus;
+ if( !acb->sc_kv.dc_count ) {
+ /* No data to xfer */
+ SBIC_TRACE(dev);
+ return;
+ }
+
+ s = splbio();
+
+ dev->sc_last = dev->sc_cur = &acb->sc_pa;
+ dev->sc_tcnt = acb->sc_tcnt;
+ dev->sc_dmacmd = acb->sc_dmacmd;
+
+#ifdef DEBUG
+ sbicdma_ops++;
+#endif
+ if( !dev->sc_tcnt ) {
+ /* sc_tcnt == 0 implies end of segment */
+
+ /* do kvm to pa mappings */
+ paddr = acb->sc_pa.dc_addr =
+ (char *) kvtop(acb->sc_kv.dc_addr);
+
+ vaddr = acb->sc_kv.dc_addr;
+ count = acb->sc_kv.dc_count;
+ for(count = (NBPG - ((int)vaddr & PGOFSET));
+ count < acb->sc_kv.dc_count
+ && (char*)kvtop(vaddr + count + 4) == paddr + count + 4;
+ count += NBPG);
+ /* If it's all contiguous... */
+ if(count > acb->sc_kv.dc_count ) {
+ count = acb->sc_kv.dc_count;
+#ifdef DEBUG
+ sbicdma_hits++;
+#endif
+ } else {
+#ifdef DEBUG
+ sbicdma_misses++;
+#endif
+ }
+ acb->sc_tcnt = count;
+ acb->sc_pa.dc_count = count >> 1;
+
+#ifdef DEBUG
+ if(data_pointer_debug)
+ printf("DMA recalc:kv(%p,%x)pa(%p,%lx)\n",
+ acb->sc_kv.dc_addr,
+ acb->sc_kv.dc_count,
+ acb->sc_pa.dc_addr,
+ acb->sc_tcnt);
+#endif
+ }
+ splx(s);
+#ifdef DEBUG
+ if(data_pointer_debug)
+ printf("SBIC restoring target %d data pointers at (%p,%x)%x\n",
+ target, dev->sc_cur->dc_addr, dev->sc_cur->dc_count,
+ dev->sc_dmacmd);
+#endif
+ SBIC_TRACE(dev);
+}
+
+/*
+ * used by specific sbic controller
+ *
+ * it appears that the higher level code does nothing with LUN's
+ * so I will too. I could plug it in, however so could they
+ * in scsi_scsi_cmd().
+ */
+int
+sbic_scsicmd(xs)
+ struct scsi_xfer *xs;
+{
+ struct sbic_acb *acb;
+ struct sbic_softc *dev;
+ struct scsi_link *slp;
+ int flags, s, stat;
+
+ slp = xs->sc_link;
+ dev = slp->adapter_softc;
+ SBIC_TRACE(dev);
+ flags = xs->flags;
+
+ if (flags & SCSI_DATA_UIO)
+ panic("sbic: scsi data uio requested");
+
+ if (dev->sc_nexus && flags & SCSI_POLL)
+ panic("sbic_scsicmd: busy");
+
+ if (slp->target == slp->adapter_target)
+ return ESCAPE_NOT_SUPPORTED;
+
+ s = splbio();
+ acb = dev->free_list.tqh_first;
+ if (acb)
+ TAILQ_REMOVE(&dev->free_list, acb, chain);
+ splx(s);
+
+ if (acb == NULL) {
+#ifdef DEBUG
+ printf("sbic_scsicmd: unable to queue request for target %d\n",
+ slp->target);
+#ifdef DDB
+ Debugger();
+#endif
+#endif
+ xs->error = XS_DRIVER_STUFFUP;
+ SBIC_TRACE(dev);
+ return(TRY_AGAIN_LATER);
+ }
+
+ acb->flags = ACB_ACTIVE;
+ if (flags & SCSI_DATA_IN)
+ acb->flags |= ACB_DATAIN;
+ acb->xs = xs;
+ bcopy(xs->cmd, &acb->cmd, xs->cmdlen);
+ acb->clen = xs->cmdlen;
+ acb->sc_kv.dc_addr = xs->data;
+ acb->sc_kv.dc_count = xs->datalen;
+ acb->pa_addr = xs->data ? (char *)kvtop(xs->data) : 0; /* XXXX check */
+
+ if (flags & SCSI_POLL) {
+ s = splbio();
+ /*
+ * This has major side effects -- it locks up the machine
+ */
+
+ dev->sc_flags |= SBICF_ICMD;
+ do {
+ while(dev->sc_nexus)
+ sbicpoll(dev);
+ dev->sc_nexus = acb;
+ dev->sc_stat[0] = -1;
+ dev->sc_xs = xs;
+ dev->target = slp->target;
+ dev->lun = slp->lun;
+ stat = sbicicmd(dev, slp->target, slp->lun,
+ &acb->cmd, acb->clen,
+ acb->sc_kv.dc_addr, acb->sc_kv.dc_count);
+ } while (dev->sc_nexus != acb);
+ sbic_scsidone(acb, stat);
+
+ splx(s);
+ SBIC_TRACE(dev);
+ return(COMPLETE);
+ }
+
+ s = splbio();
+ TAILQ_INSERT_TAIL(&dev->ready_list, acb, chain);
+
+ if (dev->sc_nexus) {
+ splx(s);
+ SBIC_TRACE(dev);
+ return(SUCCESSFULLY_QUEUED);
+ }
+
+ /*
+ * nothing is active, try to start it now.
+ */
+ sbic_sched(dev);
+ splx(s);
+
+ SBIC_TRACE(dev);
+/* TODO: add sbic_poll to do SCSI_POLL operations */
+#if 0
+ if (flags & SCSI_POLL)
+ return(COMPLETE);
+#endif
+ return(SUCCESSFULLY_QUEUED);
+}
+
+/*
+ * attempt to start the next available command
+ */
+void
+sbic_sched(dev)
+ struct sbic_softc *dev;
+{
+ struct scsi_xfer *xs;
+ struct scsi_link *slp;
+ struct sbic_acb *acb;
+ int flags, /*phase,*/ stat, i;
+
+ SBIC_TRACE(dev);
+ if (dev->sc_nexus)
+ return; /* a command is current active */
+
+ SBIC_TRACE(dev);
+ for (acb = dev->ready_list.tqh_first; acb; acb = acb->chain.tqe_next) {
+ slp = acb->xs->sc_link;
+ i = slp->target;
+ if (!(dev->sc_tinfo[i].lubusy & (1 << slp->lun))) {
+ struct sbic_tinfo *ti = &dev->sc_tinfo[i];
+
+ TAILQ_REMOVE(&dev->ready_list, acb, chain);
+ dev->sc_nexus = acb;
+ slp = acb->xs->sc_link;
+ ti = &dev->sc_tinfo[slp->target];
+ ti->lubusy |= (1 << slp->lun);
+ acb->sc_pa.dc_addr = acb->pa_addr; /* XXXX check */
+ break;
+ }
+ }
+
+ SBIC_TRACE(dev);
+ if (acb == NULL)
+ return; /* did not find an available command */
+
+ dev->sc_xs = xs = acb->xs;
+ slp = xs->sc_link;
+ flags = xs->flags;
+
+ if (flags & SCSI_RESET)
+ sbicreset(dev);
+
+#ifdef DEBUG
+ if( data_pointer_debug > 1 )
+ printf("sbic_sched(%d,%d)\n",slp->target,slp->lun);
+#endif
+ dev->sc_stat[0] = -1;
+ dev->target = slp->target;
+ dev->lun = slp->lun;
+ if ( flags & SCSI_POLL || ( !sbic_parallel_operations
+ && (/*phase == STATUS_PHASE ||*/
+ sbicdmaok(dev, xs) == 0) ) )
+ stat = sbicicmd(dev, slp->target, slp->lun, &acb->cmd,
+ acb->clen, acb->sc_kv.dc_addr, acb->sc_kv.dc_count);
+ else if (sbicgo(dev, xs) == 0) {
+ SBIC_TRACE(dev);
+ return;
+ } else
+ stat = dev->sc_stat[0];
+
+ sbic_scsidone(acb, stat);
+ SBIC_TRACE(dev);
+}
+
+void
+sbic_scsidone(acb, stat)
+ struct sbic_acb *acb;
+ int stat;
+{
+ struct scsi_xfer *xs;
+ struct scsi_link *slp;
+ struct sbic_softc *dev;
+ int dosched = 0;
+
+ xs = acb->xs;
+ slp = xs->sc_link;
+ dev = slp->adapter_softc;
+ SBIC_TRACE(dev);
+#ifdef DIAGNOSTIC
+ if (acb == NULL || xs == NULL) {
+ printf("sbic_scsidone -- (%d,%d) no scsi_xfer\n",
+ dev->target, dev->lun);
+#ifdef DDB
+ Debugger();
+#endif
+ return;
+ }
+#endif
+ /*
+ * is this right?
+ */
+ xs->status = stat;
+
+#ifdef DEBUG
+ if( data_pointer_debug > 1 )
+ printf("scsidone: (%d,%d)->(%d,%d)%02x\n",
+ slp->target, slp->lun,
+ dev->target, dev->lun, stat);
+ if( xs->sc_link->target == dev->sc_link.adapter_target )
+ panic("target == hostid");
+#endif
+
+ if (xs->error == XS_NOERROR && !(acb->flags & ACB_CHKSENSE)) {
+ if (stat == SCSI_CHECK) {
+ /* Schedule a REQUEST SENSE */
+ struct scsi_sense *ss = (void *)&acb->cmd;
+#ifdef DEBUG
+ if (report_sense)
+ printf("sbic_scsidone: autosense %02x targ %d lun %d",
+ acb->cmd.opcode, slp->target, slp->lun);
+#endif
+ bzero(ss, sizeof(*ss));
+ ss->opcode = REQUEST_SENSE;
+ ss->byte2 = slp->lun << 5;
+ ss->length = sizeof(struct scsi_sense_data);
+ acb->clen = sizeof(*ss);
+ acb->sc_kv.dc_addr = (char *)&xs->sense;
+ acb->sc_kv.dc_count = sizeof(struct scsi_sense_data);
+ acb->pa_addr = (char *)kvtop((u_char *)&xs->sense); /* XXX check */
+ acb->flags = ACB_ACTIVE | ACB_CHKSENSE | ACB_DATAIN;
+ TAILQ_INSERT_HEAD(&dev->ready_list, acb, chain);
+ dev->sc_tinfo[slp->target].lubusy &=
+ ~(1 << slp->lun);
+ dev->sc_tinfo[slp->target].senses++;
+ if (dev->sc_nexus == acb) {
+ dev->sc_nexus = NULL;
+ dev->sc_xs = NULL;
+ sbic_sched(dev);
+ }
+ SBIC_TRACE(dev);
+ return;
+ }
+ }
+ if (xs->error == XS_NOERROR && (acb->flags & ACB_CHKSENSE)) {
+ xs->error = XS_SENSE;
+#ifdef DEBUG
+ if (report_sense)
+ printf(" => %02x %02x\n", xs->sense.flags,
+ xs->sense.extra_bytes[3]);
+#endif
+ } else {
+ xs->resid = 0; /* XXXX */
+ }
+#if whataboutthisone
+ case SCSI_BUSY:
+ xs->error = XS_BUSY;
+ break;
+#endif
+ xs->flags |= ITSDONE;
+
+ /*
+ * Remove the ACB from whatever queue it's on. We have to do a bit of
+ * a hack to figure out which queue it's on. Note that it is *not*
+ * necessary to cdr down the ready queue, but we must cdr down the
+ * nexus queue and see if it's there, so we can mark the unit as no
+ * longer busy. This code is sickening, but it works.
+ */
+ if (acb == dev->sc_nexus) {
+ dev->sc_nexus = NULL;
+ dev->sc_xs = NULL;
+ dev->sc_tinfo[slp->target].lubusy &= ~(1<<slp->lun);
+ if (dev->ready_list.tqh_first)
+ dosched = 1; /* start next command */
+ } else if (dev->ready_list.tqh_last == &acb->chain.tqe_next) {
+ TAILQ_REMOVE(&dev->ready_list, acb, chain);
+ } else {
+ register struct sbic_acb *acb2;
+ for (acb2 = dev->nexus_list.tqh_first; acb2;
+ acb2 = acb2->chain.tqe_next) {
+ if (acb2 == acb) {
+ TAILQ_REMOVE(&dev->nexus_list, acb, chain);
+ dev->sc_tinfo[slp->target].lubusy
+ &= ~(1<<slp->lun);
+ break;
+ }
+ }
+ if (acb2)
+ ;
+ else if (acb->chain.tqe_next) {
+ TAILQ_REMOVE(&dev->ready_list, acb, chain);
+ } else {
+ printf("%s: can't find matching acb\n",
+ dev->sc_dev.dv_xname);
+#ifdef DDB
+ Debugger();
+#endif
+ }
+ }
+ /* Put it on the free list. */
+ acb->flags = ACB_FREE;
+ TAILQ_INSERT_HEAD(&dev->free_list, acb, chain);
+
+ dev->sc_tinfo[slp->target].cmds++;
+
+ scsi_done(xs);
+
+ if (dosched)
+ sbic_sched(dev);
+ SBIC_TRACE(dev);
+}
+
+int
+sbicdmaok(dev, xs)
+ struct sbic_softc *dev;
+ struct scsi_xfer *xs;
+{
+ if (sbic_no_dma || xs->datalen & 0x1 || (u_int)xs->data & 0x3)
+ return(0);
+ /*
+ * controller supports dma to any addresses?
+ */
+ else if ((dev->sc_flags & SBICF_BADDMA) == 0)
+ return(1);
+ /*
+ * this address is ok for dma?
+ */
+ else if (sbiccheckdmap(xs->data, xs->datalen, dev->sc_dmamask) == 0)
+ return(1);
+#if 0
+ /*
+ * we have a bounce buffer?
+ */
+ else if (dev->sc_tinfo[xs->sc_link->target].bounce)
+ return(1);
+ /*
+ * try to get one
+ */
+ else if ((dev->sc_tinfo[xs->sc_link->target].bounce
+ = (char *)alloc_z2mem(MAXPHYS))) {
+ if (isztwomem(dev->sc_tinfo[xs->sc_link->target].bounce))
+ printf("alloc ZII target %d bounce pa 0x%x\n",
+ xs->sc_link->target,
+ kvtop(dev->sc_tinfo[xs->sc_link->target].bounce));
+ else if (dev->sc_tinfo[xs->sc_link->target].bounce)
+ printf("alloc CHIP target %d bounce pa 0x%p\n",
+ xs->sc_link->target,
+ PREP_DMA_MEM(dev->sc_tinfo[xs->sc_link->target].bounce));
+ return(1);
+ }
+#endif /* 0 */
+ return(0);
+}
+
+
+int
+sbicwait(regs, until, timeo, line)
+ sbic_regmap_p regs;
+ char until;
+ int timeo;
+ int line;
+{
+ u_char val;
+ int csr;
+
+ SBIC_TRACE((struct sbic_softc *)0);
+ if (timeo == 0)
+ timeo = 1000000; /* some large value.. */
+
+ GET_SBIC_asr(regs,val);
+ while ((val & until) == 0) {
+ if (timeo-- == 0) {
+ GET_SBIC_csr(regs, csr);
+ printf("sbicwait TIMEO @%d with asr=x%x csr=x%x\n",
+ line, val, csr);
+#if defined(DDB) && defined(DEBUG)
+ Debugger();
+#endif
+ return(val); /* Maybe I should abort */
+ break;
+ }
+ DELAY(1);
+ GET_SBIC_asr(regs,val);
+ }
+ SBIC_TRACE((struct sbic_softc *)0);
+ return(val);
+}
+
+int
+sbicabort(dev, regs, where)
+ struct sbic_softc *dev;
+ sbic_regmap_p regs;
+ char *where;
+{
+ u_char csr, asr;
+
+ GET_SBIC_asr(regs, asr);
+ GET_SBIC_csr(regs, csr);
+
+ printf ("%s: abort %s: csr = 0x%02x, asr = 0x%02x\n",
+ dev->sc_dev.dv_xname, where, csr, asr);
+
+
+#if 0
+ /* Clean up running command */
+ if (dev->sc_nexus != NULL) {
+ dev->sc_nexus->xs->error = XS_DRIVER_STUFFUP;
+ sbic_scsidone(dev->sc_nexus, dev->sc_stat[0]);
+ }
+ while (acb = dev->nexus_list.tqh_first) {
+ acb->xs->error = XS_DRIVER_STUFFUP;
+ sbic_scsidone(acb, -1 /*acb->stat[0]*/);
+ }
+#endif
+
+ /* Clean up chip itself */
+ if (dev->sc_flags & SBICF_SELECTED) {
+ while( asr & SBIC_ASR_DBR ) {
+ /* sbic is jammed w/data. need to clear it */
+ /* But we don't know what direction it needs to go */
+ GET_SBIC_data(regs, asr);
+ printf("%s: abort %s: clearing data buffer 0x%02x\n",
+ dev->sc_dev.dv_xname, where, asr);
+ GET_SBIC_asr(regs, asr);
+ if( asr & SBIC_ASR_DBR ) /* Not the read direction, then */
+ SET_SBIC_data(regs, asr);
+ GET_SBIC_asr(regs, asr);
+ }
+ WAIT_CIP(regs);
+printf("%s: sbicabort - sending ABORT command\n", dev->sc_dev.dv_xname);
+ SET_SBIC_cmd(regs, SBIC_CMD_ABORT);
+ WAIT_CIP(regs);
+
+ GET_SBIC_asr(regs, asr);
+ if (asr & (SBIC_ASR_BSY|SBIC_ASR_LCI)) {
+ /* ok, get more drastic.. */
+
+printf("%s: sbicabort - asr %x, trying to reset\n", dev->sc_dev.dv_xname, asr);
+ sbicreset(dev);
+ dev->sc_flags &= ~SBICF_SELECTED;
+ return -1;
+ }
+printf("%s: sbicabort - sending DISC command\n", dev->sc_dev.dv_xname);
+ SET_SBIC_cmd(regs, SBIC_CMD_DISC);
+
+ do {
+ asr = SBIC_WAIT (regs, SBIC_ASR_INT, 0);
+ GET_SBIC_csr (regs, csr);
+ CSR_TRACE('a',csr,asr,0);
+ } while ((csr != SBIC_CSR_DISC) && (csr != SBIC_CSR_DISC_1)
+ && (csr != SBIC_CSR_CMD_INVALID));
+
+ /* lets just hope it worked.. */
+ dev->sc_flags &= ~SBICF_SELECTED;
+ }
+ return -1;
+}
+
+
+/*
+ * Initialize driver-private structures
+ */
+
+void
+sbicinit(dev)
+ struct sbic_softc *dev;
+{
+ sbic_regmap_p regs;
+ u_int i;
+ struct sbic_acb *acb;
+ u_int inhibit_sync;
+
+ extern u_long scsi_nosync;
+ extern int shift_nosync;
+
+ regs = dev->sc_sbicp;
+
+ if ((dev->sc_flags & SBICF_ALIVE) == 0) {
+ TAILQ_INIT(&dev->ready_list);
+ TAILQ_INIT(&dev->nexus_list);
+ TAILQ_INIT(&dev->free_list);
+ dev->sc_nexus = NULL;
+ dev->sc_xs = NULL;
+ acb = dev->sc_acb;
+ bzero(acb, sizeof(dev->sc_acb));
+ for (i = 0; i < sizeof(dev->sc_acb) / sizeof(*acb); i++) {
+ TAILQ_INSERT_TAIL(&dev->free_list, acb, chain);
+ acb++;
+ }
+ bzero(dev->sc_tinfo, sizeof(dev->sc_tinfo));
+#ifdef DEBUG
+ /* make sure timeout is really not needed */
+ timeout((void *)sbictimeout, dev, 30 * hz);
+#endif
+
+ } else panic("sbic: reinitializing driver!");
+
+ dev->sc_flags |= SBICF_ALIVE;
+ dev->sc_flags &= ~SBICF_SELECTED;
+
+ /* initialize inhibit array */
+ if (scsi_nosync) {
+ inhibit_sync = (scsi_nosync >> shift_nosync) & 0xff;
+ shift_nosync += 8;
+#ifdef DEBUG
+ if (inhibit_sync)
+ printf("%s: Inhibiting synchronous transfer %02x\n",
+ dev->sc_dev.dv_xname, inhibit_sync);
+#endif
+ for (i = 0; i < 8; ++i)
+ if (inhibit_sync & (1 << i))
+ sbic_inhibit_sync[i] = 1;
+ }
+
+ sbicreset(dev);
+}
+
+void
+sbicreset(dev)
+ struct sbic_softc *dev;
+{
+ sbic_regmap_p regs;
+ u_int my_id, s;
+ u_char csr;
+ int microcode;
+ int chips;
+#if 0
+ u_int i;
+ struct sbic_acb *acb;
+#endif
+
+ regs = dev->sc_sbicp;
+#if 0
+ if (dev->sc_flags & SBICF_ALIVE) {
+ SET_SBIC_cmd(regs, SBIC_CMD_ABORT);
+ WAIT_CIP(regs);
+ }
+#else
+ SET_SBIC_cmd(regs, SBIC_CMD_ABORT);
+ WAIT_CIP(regs);
+#endif
+ s = splbio();
+ my_id = dev->sc_link.adapter_target & SBIC_ID_MASK;
+
+ /* Enable advanced mode */
+ my_id |= SBIC_ID_EAF /*| SBIC_ID_EHP*/ ;
+ SET_SBIC_myid(regs, my_id);
+
+ /*
+ * Disable interrupts (in dmainit) then reset the chip
+ */
+ SET_SBIC_cmd(regs, SBIC_CMD_RESET);
+ DELAY(25);
+ SBIC_WAIT(regs, SBIC_ASR_INT, 0);
+ GET_SBIC_csr(regs, csr); /* clears interrupt also */
+
+ GET_SBIC_cdb1 (regs, microcode);
+ if (csr == 0x00)
+ chips = 0;
+ else if (csr == 0x01)
+ {
+ SET_SBIC_queue_tag (regs, 0xa5);
+ GET_SBIC_queue_tag (regs, csr);
+ if (csr == 0xa5)
+ {
+ chips = 2;
+ SET_SBIC_queue_tag (regs, 0);
+ }
+ else
+ chips = 1;
+ }
+ else
+ chips = -1;
+
+ printf ("SBIC reset WD22C93%c - microcode %d\n",
+ '@' + chips, microcode);
+
+ if (dev->sc_clkfreq < 110)
+ my_id |= SBIC_ID_FS_8_10;
+ else if (dev->sc_clkfreq < 160)
+ my_id |= SBIC_ID_FS_12_15;
+ else if (dev->sc_clkfreq < 210)
+ my_id |= SBIC_ID_FS_16_20;
+
+ SET_SBIC_myid(regs, my_id);
+
+ /*
+ * Set up various chip parameters
+ */
+ SET_SBIC_control(regs, SBIC_CTL_EDI | SBIC_CTL_IDI /* | SBIC_CTL_HSP */
+ | SBIC_MACHINE_DMA_MODE);
+ /*
+ * don't allow (re)selection (SBIC_RID_ES)
+ * until we can handle target mode!!
+ */
+ SET_SBIC_rselid(regs, SBIC_RID_ER);
+ SET_SBIC_syn(regs, 0); /* asynch for now */
+
+ /*
+ * anything else was zeroed by reset
+ */
+ splx(s);
+
+#if 0
+ if ((dev->sc_flags & SBICF_ALIVE) == 0) {
+ TAILQ_INIT(&dev->ready_list);
+ TAILQ_INIT(&dev->nexus_list);
+ TAILQ_INIT(&dev->free_list);
+ dev->sc_nexus = NULL;
+ dev->sc_xs = NULL;
+ acb = dev->sc_acb;
+ bzero(acb, sizeof(dev->sc_acb));
+ for (i = 0; i < sizeof(dev->sc_acb) / sizeof(*acb); i++) {
+ TAILQ_INSERT_TAIL(&dev->free_list, acb, chain);
+ acb++;
+ }
+ bzero(dev->sc_tinfo, sizeof(dev->sc_tinfo));
+ } else {
+ if (dev->sc_nexus != NULL) {
+ dev->sc_nexus->xs->error = XS_DRIVER_STUFFUP;
+ sbic_scsidone(dev->sc_nexus, dev->sc_stat[0]);
+ }
+ while (acb = dev->nexus_list.tqh_first) {
+ acb->xs->error = XS_DRIVER_STUFFUP;
+ sbic_scsidone(acb, -1 /*acb->stat[0]*/);
+ }
+ }
+
+ dev->sc_flags |= SBICF_ALIVE;
+#endif
+ dev->sc_flags &= ~SBICF_SELECTED;
+}
+
+void
+sbicerror(dev, regs, csr)
+ struct sbic_softc *dev;
+ sbic_regmap_p regs;
+ u_char csr;
+{
+ struct scsi_xfer *xs;
+
+ xs = dev->sc_xs;
+
+#ifdef DIAGNOSTIC
+ if (xs == NULL)
+ panic("sbicerror");
+#endif
+ if (xs->flags & SCSI_SILENT)
+ return;
+
+ printf("%s: ", dev->sc_dev.dv_xname);
+ printf("csr == 0x%02x\n", csr); /* XXX */
+}
+
+/*
+ * select the bus, return when selected or error.
+ */
+int
+sbicselectbus(dev, regs, target, lun, our_addr)
+ struct sbic_softc *dev;
+ sbic_regmap_p regs;
+ u_char target, lun, our_addr;
+{
+ u_char asr, csr, id;
+
+ SBIC_TRACE(dev);
+ QPRINTF(("sbicselectbus %d\n", target));
+
+ /*
+ * if we're already selected, return (XXXX panic maybe?)
+ */
+ if (dev->sc_flags & SBICF_SELECTED) {
+ SBIC_TRACE(dev);
+ return(1);
+ }
+
+ /*
+ * issue select
+ */
+ SBIC_TC_PUT(regs, 0);
+ SET_SBIC_selid(regs, target);
+ SET_SBIC_timeo(regs, SBIC_TIMEOUT(250,dev->sc_clkfreq));
+
+ /*
+ * set sync or async
+ */
+ if (dev->sc_sync[target].state == SYNC_DONE)
+ SET_SBIC_syn(regs, SBIC_SYN (dev->sc_sync[target].offset,
+ dev->sc_sync[target].period));
+ else
+ SET_SBIC_syn(regs, SBIC_SYN (0, sbic_min_period));
+
+ GET_SBIC_asr(regs, asr);
+ if( asr & (SBIC_ASR_INT|SBIC_ASR_BSY) ) {
+ /* This means we got ourselves reselected upon */
+/* printf("sbicselectbus: INT/BSY asr %02x\n", asr);*/
+#ifdef DDB
+/* Debugger();*/
+#endif
+ SBIC_TRACE(dev);
+ return 1;
+ }
+
+ SET_SBIC_cmd(regs, SBIC_CMD_SEL_ATN);
+
+ /*
+ * wait for select (merged from seperate function may need
+ * cleanup)
+ */
+ WAIT_CIP(regs);
+ do {
+ asr = SBIC_WAIT(regs, SBIC_ASR_INT | SBIC_ASR_LCI, 0);
+ if (asr & SBIC_ASR_LCI) {
+#ifdef DEBUG
+ if (reselect_debug)
+ printf("sbicselectbus: late LCI asr %02x\n", asr);
+#endif
+ SBIC_TRACE(dev);
+ return 1;
+ }
+ GET_SBIC_csr (regs, csr);
+ CSR_TRACE('s',csr,asr,target);
+ QPRINTF(("%02x ", csr));
+ if( csr == SBIC_CSR_RSLT_NI || csr == SBIC_CSR_RSLT_IFY) {
+#ifdef DEBUG
+ if(reselect_debug)
+ printf("sbicselectbus: reselected asr %02x\n", asr);
+#endif
+ /* We need to handle this now so we don't lock up later */
+ sbicnextstate(dev, csr, asr);
+ SBIC_TRACE(dev);
+ return 1;
+ }
+ if( csr == SBIC_CSR_SLT || csr == SBIC_CSR_SLT_ATN) {
+ panic("sbicselectbus: target issued select!");
+ return 1;
+ }
+ } while (csr != (SBIC_CSR_MIS_2|MESG_OUT_PHASE)
+ && csr != (SBIC_CSR_MIS_2|CMD_PHASE) && csr != SBIC_CSR_SEL_TIMEO);
+
+ /* Enable (or not) reselection */
+ if(!sbic_enable_reselect && dev->nexus_list.tqh_first == NULL)
+ SET_SBIC_rselid (regs, 0);
+ else
+ SET_SBIC_rselid (regs, SBIC_RID_ER);
+
+ if (csr == (SBIC_CSR_MIS_2|CMD_PHASE)) {
+ dev->sc_flags |= SBICF_SELECTED; /* device ignored ATN */
+ GET_SBIC_selid(regs, id);
+ dev->target = id;
+ GET_SBIC_tlun(regs,dev->lun);
+ if( dev->lun & SBIC_TLUN_VALID )
+ dev->lun &= SBIC_TLUN_MASK;
+ else
+ dev->lun = lun;
+ } else if (csr == (SBIC_CSR_MIS_2|MESG_OUT_PHASE)) {
+ /*
+ * Send identify message
+ * (SCSI-2 requires an identify msg (?))
+ */
+ GET_SBIC_selid(regs, id);
+ dev->target = id;
+ GET_SBIC_tlun(regs,dev->lun);
+ if( dev->lun & SBIC_TLUN_VALID )
+ dev->lun &= SBIC_TLUN_MASK;
+ else
+ dev->lun = lun;
+ /*
+ * handle drives that don't want to be asked
+ * whether to go sync at all.
+ */
+ if (sbic_inhibit_sync[id]
+ && dev->sc_sync[id].state == SYNC_START) {
+#ifdef DEBUG
+ if (sync_debug)
+ printf("Forcing target %d asynchronous.\n", id);
+#endif
+ dev->sc_sync[id].offset = 0;
+ dev->sc_sync[id].period = sbic_min_period;
+ dev->sc_sync[id].state = SYNC_DONE;
+ }
+
+
+ if (dev->sc_sync[id].state != SYNC_START){
+ if( dev->sc_xs->flags & SCSI_POLL
+ || (dev->sc_flags & SBICF_ICMD)
+ || !sbic_enable_reselect )
+ SEND_BYTE (regs, MSG_IDENTIFY | lun);
+ else
+ SEND_BYTE (regs, MSG_IDENTIFY_DR | lun);
+ } else {
+ /*
+ * try to initiate a sync transfer.
+ * So compose the sync message we're going
+ * to send to the target
+ */
+
+#ifdef DEBUG
+ if (sync_debug)
+ printf("Sending sync request to target %d ... ",
+ id);
+#endif
+ /*
+ * setup scsi message sync message request
+ */
+ dev->sc_msg[0] = MSG_IDENTIFY | lun;
+ dev->sc_msg[1] = MSG_EXT_MESSAGE;
+ dev->sc_msg[2] = 3;
+ dev->sc_msg[3] = MSG_SYNC_REQ;
+ dev->sc_msg[4] = sbictoscsiperiod(dev, regs,
+ sbic_min_period);
+ dev->sc_msg[5] = sbic_max_offset;
+
+ if (sbicxfstart(regs, 6, MESG_OUT_PHASE, sbic_cmd_wait))
+ sbicxfout(regs, 6, dev->sc_msg, MESG_OUT_PHASE);
+
+ dev->sc_sync[id].state = SYNC_SENT;
+#ifdef DEBUG
+ if (sync_debug)
+ printf ("sent\n");
+#endif
+ }
+
+ asr = SBIC_WAIT (regs, SBIC_ASR_INT, 0);
+ GET_SBIC_csr (regs, csr);
+ CSR_TRACE('y',csr,asr,target);
+ QPRINTF(("[%02x]", csr));
+#ifdef DEBUG
+ if (sync_debug && dev->sc_sync[id].state == SYNC_SENT)
+ printf("csr-result of last msgout: 0x%x\n", csr);
+#endif
+
+ if (csr != SBIC_CSR_SEL_TIMEO)
+ dev->sc_flags |= SBICF_SELECTED;
+ }
+ if (csr == SBIC_CSR_SEL_TIMEO)
+ dev->sc_xs->error = XS_SELTIMEOUT;
+
+ QPRINTF(("\n"));
+
+ SBIC_TRACE(dev);
+ return(csr == SBIC_CSR_SEL_TIMEO);
+}
+
+int
+sbicxfstart(regs, len, phase, wait)
+ sbic_regmap_p regs;
+ int len, wait;
+ u_char phase;
+{
+ u_char id;
+
+ switch (phase) {
+ case DATA_IN_PHASE:
+ case MESG_IN_PHASE:
+ GET_SBIC_selid (regs, id);
+ id |= SBIC_SID_FROM_SCSI;
+ SET_SBIC_selid (regs, id);
+ SBIC_TC_PUT (regs, (unsigned)len);
+ break;
+ case DATA_OUT_PHASE:
+ case MESG_OUT_PHASE:
+ case CMD_PHASE:
+ GET_SBIC_selid (regs, id);
+ id &= ~SBIC_SID_FROM_SCSI;
+ SET_SBIC_selid (regs, id);
+ SBIC_TC_PUT (regs, (unsigned)len);
+ break;
+ default:
+ SBIC_TC_PUT (regs, 0);
+ }
+ QPRINTF(("sbicxfstart %d, %d, %d\n", len, phase, wait));
+
+ return(1);
+}
+
+int
+sbicxfout(regs, len, bp, phase)
+ sbic_regmap_p regs;
+ int len;
+ void *bp;
+ int phase;
+{
+ u_char orig_csr, asr, *buf;
+ int wait;
+
+ buf = bp;
+ wait = sbic_data_wait;
+
+ QPRINTF(("sbicxfout {%d} %02x %02x %02x %02x %02x "
+ "%02x %02x %02x %02x %02x\n", len, buf[0], buf[1], buf[2],
+ buf[3], buf[4], buf[5], buf[6], buf[7], buf[8], buf[9]));
+
+ GET_SBIC_csr (regs, orig_csr);
+ CSR_TRACE('>',orig_csr,0,0);
+
+ /*
+ * sigh.. WD-PROTO strikes again.. sending the command in one go
+ * causes the chip to lock up if talking to certain (misbehaving?)
+ * targets. Anyway, this procedure should work for all targets, but
+ * it's slightly slower due to the overhead
+ */
+ WAIT_CIP (regs);
+ SET_SBIC_cmd (regs, SBIC_CMD_XFER_INFO);
+ for (;len > 0; len--) {
+ GET_SBIC_asr (regs, asr);
+ while ((asr & SBIC_ASR_DBR) == 0) {
+ if ((asr & SBIC_ASR_INT) || --wait < 0) {
+#ifdef DEBUG
+ if (sbic_debug)
+ printf("sbicxfout fail: l%d i%x w%d\n",
+ len, asr, wait);
+#endif
+ return (len);
+ }
+/* DELAY(1);*/
+ GET_SBIC_asr (regs, asr);
+ }
+
+ SET_SBIC_data (regs, *buf);
+ buf++;
+ }
+ SBIC_TC_GET(regs, len);
+ QPRINTF(("sbicxfout done %d bytes\n", len));
+ /*
+ * this leaves with one csr to be read
+ */
+ return(0);
+}
+
+/* returns # bytes left to read */
+int
+sbicxfin(regs, len, bp)
+ sbic_regmap_p regs;
+ int len;
+ void *bp;
+{
+ int wait;
+ u_char *obp, *buf;
+ u_char orig_csr, csr, asr;
+
+ wait = sbic_data_wait;
+ obp = bp;
+ buf = bp;
+
+ GET_SBIC_csr (regs, orig_csr);
+ CSR_TRACE('<',orig_csr,0,0);
+
+ QPRINTF(("sbicxfin %d, csr=%02x\n", len, orig_csr));
+
+ WAIT_CIP (regs);
+ SET_SBIC_cmd (regs, SBIC_CMD_XFER_INFO);
+ for (;len > 0; len--) {
+ GET_SBIC_asr (regs, asr);
+ if((asr & SBIC_ASR_PE)) {
+#ifdef DEBUG
+ printf("sbicxfin parity error: l%d i%x w%d\n",
+ len, asr, wait);
+/* return ((unsigned long)buf - (unsigned long)bp); */
+#ifdef DDB
+ Debugger();
+#endif
+#endif
+ }
+ while ((asr & SBIC_ASR_DBR) == 0) {
+ if ((asr & SBIC_ASR_INT) || --wait < 0) {
+#ifdef DEBUG
+ if (sbic_debug) {
+ QPRINTF(("sbicxfin fail:{%d} %02x %02x %02x %02x %02x %02x "
+ "%02x %02x %02x %02x\n", len, obp[0], obp[1], obp[2],
+ obp[3], obp[4], obp[5], obp[6], obp[7], obp[8], obp[9]));
+ printf("sbicxfin fail: l%d i%x w%d\n",
+ len, asr, wait);
+}
+#endif
+ return len;
+ }
+
+ if( ! asr & SBIC_ASR_BSY ) {
+ GET_SBIC_csr(regs, csr);
+ CSR_TRACE('<',csr,asr,len);
+ QPRINTF(("[CSR%02xASR%02x]", csr, asr));
+ }
+
+/* DELAY(1);*/
+ GET_SBIC_asr (regs, asr);
+ }
+
+ GET_SBIC_data (regs, *buf);
+/* QPRINTF(("asr=%02x, csr=%02x, data=%02x\n", asr, csr, *buf));*/
+ buf++;
+ }
+
+ QPRINTF(("sbicxfin {%d} %02x %02x %02x %02x %02x %02x "
+ "%02x %02x %02x %02x\n", len, obp[0], obp[1], obp[2],
+ obp[3], obp[4], obp[5], obp[6], obp[7], obp[8], obp[9]));
+
+ /* this leaves with one csr to be read */
+ return len;
+}
+
+/*
+ * SCSI 'immediate' command: issue a command to some SCSI device
+ * and get back an 'immediate' response (i.e., do programmed xfer
+ * to get the response data). 'cbuf' is a buffer containing a scsi
+ * command of length clen bytes. 'buf' is a buffer of length 'len'
+ * bytes for data. The transfer direction is determined by the device
+ * (i.e., by the scsi bus data xfer phase). If 'len' is zero, the
+ * command must supply no data.
+ */
+int
+sbicicmd(dev, target, lun, cbuf, clen, buf, len)
+ struct sbic_softc *dev;
+ void *cbuf, *buf;
+ int clen, len;
+{
+ sbic_regmap_p regs;
+ u_char phase, csr, asr;
+ int wait, i;
+ struct sbic_acb *acb;
+
+#define CSR_LOG_BUF_SIZE 0
+#if CSR_LOG_BUF_SIZE
+ int bufptr;
+ int csrbuf[CSR_LOG_BUF_SIZE];
+ bufptr=0;
+#endif
+
+ SBIC_TRACE(dev);
+ regs = dev->sc_sbicp;
+ acb = dev->sc_nexus;
+
+ /* Make sure pointers are OK */
+ dev->sc_last = dev->sc_cur = &acb->sc_pa;
+ dev->sc_tcnt = acb->sc_tcnt = 0;
+ acb->sc_pa.dc_count = 0; /* No DMA */
+ acb->sc_kv.dc_addr = buf;
+ acb->sc_kv.dc_count = len;
+
+#ifdef DEBUG
+ routine = 3;
+ debug_sbic_regs = regs; /* store this to allow debug calls */
+ if( data_pointer_debug > 1 )
+ printf("sbicicmd(%d,%d):%d\n", target, lun,
+ acb->sc_kv.dc_count);
+#endif
+
+ /*
+ * set the sbic into non-DMA mode
+ */
+ SET_SBIC_control(regs, SBIC_CTL_EDI | SBIC_CTL_IDI /*| SBIC_CTL_HSP*/);
+
+ dev->sc_stat[0] = 0xff;
+ dev->sc_msg[0] = 0xff;
+ i = 1; /* pre-load */
+
+ /* We're stealing the SCSI bus */
+ dev->sc_flags |= SBICF_ICMD;
+
+ do {
+ /*
+ * select the SCSI bus (it's an error if bus isn't free)
+ */
+ if (!( dev->sc_flags & SBICF_SELECTED )
+ && sbicselectbus(dev, regs, target, lun, dev->sc_scsiaddr)) {
+ /*printf("sbicicmd trying to select busy bus!\n");*/
+ dev->sc_flags &= ~SBICF_ICMD;
+ return(-1);
+ }
+
+ /*
+ * Wait for a phase change (or error) then let the device sequence
+ * us through the various SCSI phases.
+ */
+
+ wait = sbic_cmd_wait;
+
+ asr = GET_SBIC_asr (regs, asr);
+ GET_SBIC_csr (regs, csr);
+ CSR_TRACE('I',csr,asr,target);
+ QPRINTF((">ASR:%02xCSR:%02x<", asr, csr));
+
+#if CSR_LOG_BUF_SIZE
+ csrbuf[bufptr++] = csr;
+#endif
+
+
+ switch (csr) {
+ case SBIC_CSR_S_XFERRED:
+ case SBIC_CSR_DISC:
+ case SBIC_CSR_DISC_1:
+ dev->sc_flags &= ~SBICF_SELECTED;
+ GET_SBIC_cmd_phase (regs, phase);
+ if (phase == 0x60) {
+ GET_SBIC_tlun (regs, dev->sc_stat[0]);
+ i = 0; /* done */
+/* break; */ /* Bypass all the state gobldygook */
+ } else {
+#ifdef DEBUG
+ if(reselect_debug>1)
+ printf("sbicicmd: handling disconnect\n");
+#endif
+ i = SBIC_STATE_DISCONNECT;
+ }
+ break;
+
+ case SBIC_CSR_XFERRED|CMD_PHASE:
+ case SBIC_CSR_MIS|CMD_PHASE:
+ case SBIC_CSR_MIS_1|CMD_PHASE:
+ case SBIC_CSR_MIS_2|CMD_PHASE:
+ if (sbicxfstart(regs, clen, CMD_PHASE, sbic_cmd_wait))
+ if (sbicxfout(regs, clen,
+ cbuf, CMD_PHASE))
+ i = sbicabort(dev, regs,"icmd sending cmd");
+#if 0
+ GET_SBIC_csr(regs, csr); /* Lets us reload tcount */
+ WAIT_CIP(regs);
+ GET_SBIC_asr(regs, asr);
+ CSR_TRACE('I',csr,asr,target);
+ if( asr & (SBIC_ASR_BSY|SBIC_ASR_LCI|SBIC_ASR_CIP) )
+ printf("next: cmd sent asr %02x, csr %02x\n",
+ asr, csr);
+#endif
+ break;
+
+#if 0
+ case SBIC_CSR_XFERRED|DATA_OUT_PHASE:
+ case SBIC_CSR_XFERRED|DATA_IN_PHASE:
+ case SBIC_CSR_MIS|DATA_OUT_PHASE:
+ case SBIC_CSR_MIS|DATA_IN_PHASE:
+ case SBIC_CSR_MIS_1|DATA_OUT_PHASE:
+ case SBIC_CSR_MIS_1|DATA_IN_PHASE:
+ case SBIC_CSR_MIS_2|DATA_OUT_PHASE:
+ case SBIC_CSR_MIS_2|DATA_IN_PHASE:
+ if (acb->sc_kv.dc_count <= 0)
+ i = sbicabort(dev, regs, "icmd out of data");
+ else {
+ wait = sbic_data_wait;
+ if (sbicxfstart(regs,
+ acb->sc_kv.dc_count,
+ SBIC_PHASE(csr), wait))
+ if (csr & 0x01)
+ /* data in? */
+ i=sbicxfin(regs,
+ acb->sc_kv.dc_count,
+ acb->sc_kv.dc_addr);
+ else
+ i=sbicxfout(regs,
+ acb->sc_kv.dc_count,
+ acb->sc_kv.dc_addr,
+ SBIC_PHASE(csr));
+ acb->sc_kv.dc_addr +=
+ (acb->sc_kv.dc_count - i);
+ acb->sc_kv.dc_count = i;
+ i = 1;
+ }
+ break;
+
+#endif
+ case SBIC_CSR_XFERRED|STATUS_PHASE:
+ case SBIC_CSR_MIS|STATUS_PHASE:
+ case SBIC_CSR_MIS_1|STATUS_PHASE:
+ case SBIC_CSR_MIS_2|STATUS_PHASE:
+ /*
+ * the sbic does the status/cmd-complete reading ok,
+ * so do this with its hi-level commands.
+ */
+#ifdef DEBUG
+ if(sbic_debug)
+ printf("SBICICMD status phase\n");
+#endif
+ SBIC_TC_PUT(regs, 0);
+ SET_SBIC_cmd_phase(regs, 0x46);
+ SET_SBIC_cmd(regs, SBIC_CMD_SEL_ATN_XFER);
+ break;
+
+#if THIS_IS_A_RESERVED_STATE
+ case BUS_FREE_PHASE: /* This is not legal */
+ if( dev->sc_stat[0] != 0xff )
+ goto out;
+ break;
+#endif
+
+ default:
+ i = sbicnextstate(dev, csr, asr);
+ }
+
+ /*
+ * make sure the last command was taken,
+ * ie. we're not hunting after an ignored command..
+ */
+ GET_SBIC_asr(regs, asr);
+
+ /* tapes may take a loooong time.. */
+ while (asr & SBIC_ASR_BSY){
+ if(asr & SBIC_ASR_DBR) {
+ printf("sbicicmd: Waiting while sbic is jammed, CSR:%02x,ASR:%02x\n",
+ csr,asr);
+#ifdef DDB
+ Debugger();
+#endif
+ /* SBIC is jammed */
+ /* DUNNO which direction */
+ /* Try old direction */
+ GET_SBIC_data(regs,i);
+ GET_SBIC_asr(regs, asr);
+ if( asr & SBIC_ASR_DBR) /* Wants us to write */
+ SET_SBIC_data(regs,i);
+ }
+ GET_SBIC_asr(regs, asr);
+ }
+
+ /*
+ * wait for last command to complete
+ */
+ if (asr & SBIC_ASR_LCI) {
+ printf("sbicicmd: last command ignored\n");
+ }
+ else if( i == 1 ) /* Bsy */
+ SBIC_WAIT (regs, SBIC_ASR_INT, wait);
+
+ /*
+ * do it again
+ */
+ } while ( i > 0 && dev->sc_stat[0] == 0xff);
+
+ /* Sometimes we need to do an extra read of the CSR */
+ GET_SBIC_csr(regs, csr);
+ CSR_TRACE('I',csr,asr,0xff);
+
+#if CSR_LOG_BUF_SIZE
+ if(reselect_debug>1)
+ for(i=0; i<bufptr; i++)
+ printf("CSR:%02x", csrbuf[i]);
+#endif
+
+#ifdef DEBUG
+ if(data_pointer_debug > 1)
+ printf("sbicicmd done(%d,%d):%d =%d=\n",
+ dev->target, lun,
+ acb->sc_kv.dc_count,
+ dev->sc_stat[0]);
+#endif
+
+ QPRINTF(("=STS:%02x=", dev->sc_stat[0]));
+ dev->sc_flags &= ~SBICF_ICMD;
+
+ SBIC_TRACE(dev);
+ return(dev->sc_stat[0]);
+}
+
+/*
+ * Finish SCSI xfer command: After the completion interrupt from
+ * a read/write operation, sequence through the final phases in
+ * programmed i/o. This routine is a lot like sbicicmd except we
+ * skip (and don't allow) the select, cmd out and data in/out phases.
+ */
+void
+sbicxfdone(dev, regs, target)
+ struct sbic_softc *dev;
+ sbic_regmap_p regs;
+ int target;
+{
+ u_char phase, asr, csr;
+ int s;
+
+ SBIC_TRACE(dev);
+ QPRINTF(("{"));
+ s = splbio();
+
+ /*
+ * have the sbic complete on its own
+ */
+ SBIC_TC_PUT(regs, 0);
+ SET_SBIC_cmd_phase(regs, 0x46);
+ SET_SBIC_cmd(regs, SBIC_CMD_SEL_ATN_XFER);
+
+ do {
+ asr = SBIC_WAIT (regs, SBIC_ASR_INT, 0);
+ GET_SBIC_csr (regs, csr);
+ CSR_TRACE('f',csr,asr,target);
+ QPRINTF(("%02x:", csr));
+ } while ((csr != SBIC_CSR_DISC) && (csr != SBIC_CSR_DISC_1)
+ && (csr != SBIC_CSR_S_XFERRED));
+
+ dev->sc_flags &= ~SBICF_SELECTED;
+
+ GET_SBIC_cmd_phase (regs, phase);
+ QPRINTF(("}%02x", phase));
+ if (phase == 0x60)
+ GET_SBIC_tlun(regs, dev->sc_stat[0]);
+ else
+ sbicerror(dev, regs, csr);
+
+ QPRINTF(("=STS:%02x=\n", dev->sc_stat[0]));
+ splx(s);
+ SBIC_TRACE(dev);
+}
+
+ /*
+ * No DMA chains
+ */
+
+int
+sbicgo(dev, xs)
+ struct sbic_softc *dev;
+ struct scsi_xfer *xs;
+{
+ int i, dmaflags, count, usedma;
+ u_char csr, asr, *addr;
+ sbic_regmap_p regs;
+ struct sbic_acb *acb;
+
+ SBIC_TRACE(dev);
+ dev->target = xs->sc_link->target;
+ dev->lun = xs->sc_link->lun;
+ acb = dev->sc_nexus;
+ regs = dev->sc_sbicp;
+
+ usedma = sbicdmaok(dev, xs);
+#ifdef DEBUG
+ routine = 1;
+ debug_sbic_regs = regs; /* store this to allow debug calls */
+ if( data_pointer_debug > 1 )
+ printf("sbicgo(%d,%d)\n", dev->target, dev->lun);
+#endif
+
+ /*
+ * set the sbic into DMA mode
+ */
+ if( usedma )
+ SET_SBIC_control(regs, SBIC_CTL_EDI | SBIC_CTL_IDI |
+ SBIC_MACHINE_DMA_MODE);
+ else
+ SET_SBIC_control(regs, SBIC_CTL_EDI | SBIC_CTL_IDI);
+
+
+ /*
+ * select the SCSI bus (it's an error if bus isn't free)
+ */
+ if (sbicselectbus(dev, regs, dev->target, dev->lun,
+ dev->sc_scsiaddr)) {
+/* printf("sbicgo: Trying to select busy bus!\n"); */
+ SBIC_TRACE(dev);
+ return(0); /* Not done: needs to be rescheduled */
+ }
+ dev->sc_stat[0] = 0xff;
+
+ /*
+ * Calculate DMA chains now
+ */
+
+ dmaflags = 0;
+ if (acb->flags & ACB_DATAIN)
+ dmaflags |= DMAGO_READ;
+
+
+ /*
+ * Deal w/bounce buffers.
+ */
+
+ addr = acb->sc_kv.dc_addr;
+ count = acb->sc_kv.dc_count;
+ if (count && (char *)kvtop(addr) != acb->sc_pa.dc_addr) { /* XXXX check */
+ printf("sbic: DMA buffer mapping changed %p->%x\n",
+ acb->sc_pa.dc_addr, kvtop(addr));
+#ifdef DDB
+ Debugger();
+#endif
+ }
+
+#ifdef DEBUG
+ ++sbicdma_ops; /* count total DMA operations */
+#endif
+#if 0
+ if (count && usedma && dev->sc_flags & SBICF_BADDMA &&
+ sbiccheckdmap(addr, count, dev->sc_dmamask)) {
+ /*
+ * need to bounce the dma.
+ */
+ if (dmaflags & DMAGO_READ) {
+ acb->flags |= ACB_BBUF;
+ acb->sc_dmausrbuf = addr;
+ acb->sc_dmausrlen = count;
+ acb->sc_usrbufpa = (u_char *)kvtop(addr);
+ if(!dev->sc_tinfo[dev->target].bounce) {
+ printf("sbicgo: HELP! no bounce allocated for %d\n",
+ dev->target);
+ printf("xfer: (%p->%p,%lx)\n", acb->sc_dmausrbuf,
+ acb->sc_usrbufpa, acb->sc_dmausrlen);
+ dev->sc_tinfo[xs->sc_link->target].bounce
+ = (char *)alloc_z2mem(MAXPHYS);
+ if (isztwomem(dev->sc_tinfo[xs->sc_link->target].bounce))
+ printf("alloc ZII target %d bounce pa 0x%x\n",
+ xs->sc_link->target,
+ kvtop(dev->sc_tinfo[xs->sc_link->target].bounce));
+ else if (dev->sc_tinfo[xs->sc_link->target].bounce)
+ printf("alloc CHIP target %d bounce pa 0x%p\n",
+ xs->sc_link->target,
+ PREP_DMA_MEM(dev->sc_tinfo[xs->sc_link->target].bounce));
+
+ printf("Allocating %d bounce at %x\n",
+ dev->target,
+ kvtop(dev->sc_tinfo[dev->target].bounce));
+ }
+ } else { /* write: copy to dma buffer */
+#ifdef DEBUG
+ if(data_pointer_debug)
+ printf("sbicgo: copying %x bytes to target %d bounce %x\n",
+ count, dev->target,
+ kvtop(dev->sc_tinfo[dev->target].bounce));
+#endif
+ bcopy (addr, dev->sc_tinfo[dev->target].bounce, count);
+ }
+ addr = dev->sc_tinfo[dev->target].bounce;/* and use dma buffer */
+ acb->sc_kv.dc_addr = addr;
+#ifdef DEBUG
+ ++sbicdma_bounces; /* count number of bounced */
+#endif
+ }
+#endif /* 0 */
+
+ /*
+ * Allocate the DMA chain
+ */
+
+ /* Set start KVM addresses */
+#if 0
+ acb->sc_kv.dc_addr = addr;
+ acb->sc_kv.dc_count = count;
+#endif
+
+ /* Mark end of segment */
+ acb->sc_tcnt = dev->sc_tcnt = 0;
+ acb->sc_pa.dc_count = 0;
+
+ sbic_load_ptrs(dev, regs, dev->target, dev->lun);
+ SBIC_TRACE(dev);
+ /* Enable interrupts but don't do any DMA */
+ dev->sc_enintr(dev);
+ if (usedma) {
+ dev->sc_tcnt = dev->sc_dmago(dev, acb->sc_pa.dc_addr,
+ acb->sc_pa.dc_count,
+ dmaflags);
+#ifdef DEBUG
+ dev->sc_dmatimo = dev->sc_tcnt ? 1 : 0;
+#endif
+ } else
+ dev->sc_dmacmd = 0; /* Don't use DMA */
+ dev->sc_flags |= SBICF_INDMA;
+/* SBIC_TC_PUT(regs, dev->sc_tcnt); */ /* XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX */
+ SBIC_TRACE(dev);
+ sbic_save_ptrs(dev, regs, dev->target, dev->lun);
+
+ /*
+ * push the data cache ( I think this won't work (EH))
+ */
+#if defined(M68040) || defined(M68060)
+ if (mmutype == MMU_68040 && usedma && count) {
+ dma_cachectl(addr, count);
+ if (((u_int)addr & 0xF) || (((u_int)addr + count) & 0xF))
+ dev->sc_flags |= SBICF_DCFLUSH;
+ }
+#endif
+
+ /*
+ * enintr() also enables interrupts for the sbic
+ */
+#ifdef DEBUG
+ if( data_pointer_debug > 1 )
+ printf("sbicgo dmago:%d(%p:%lx)\n",
+ dev->target,dev->sc_cur->dc_addr,dev->sc_tcnt);
+#if 0
+ /*
+ * Hmm - this isn't right: asr and csr haven't been set yet.
+ */
+ debug_asr = asr;
+ debug_csr = csr;
+#endif
+#endif
+
+ /*
+ * Lets cycle a while then let the interrupt handler take over
+ */
+
+ asr = GET_SBIC_asr(regs, asr);
+ do {
+ GET_SBIC_csr(regs, csr);
+ CSR_TRACE('g',csr,asr,dev->target);
+#ifdef DEBUG
+ debug_csr = csr;
+ routine = 1;
+#endif
+ QPRINTF(("go[0x%x]", csr));
+
+ i = sbicnextstate(dev, csr, asr);
+
+ WAIT_CIP(regs);
+ GET_SBIC_asr(regs, asr);
+#ifdef DEBUG
+ debug_asr = asr;
+#endif
+ if(asr & SBIC_ASR_LCI) printf("sbicgo: LCI asr:%02x csr:%02x\n",
+ asr,csr);
+ } while( i == SBIC_STATE_RUNNING
+ && asr & (SBIC_ASR_INT|SBIC_ASR_LCI) );
+
+ CSR_TRACE('g',csr,asr,i<<4);
+ SBIC_TRACE(dev);
+if (i == SBIC_STATE_DONE && dev->sc_stat[0] == 0xff) printf("sbicgo: done & stat = 0xff\n");
+ if (i == SBIC_STATE_DONE && dev->sc_stat[0] != 0xff) {
+/* if( i == SBIC_STATE_DONE && dev->sc_stat[0] ) { */
+ /* Did we really finish that fast? */
+ return 1;
+ }
+ return 0;
+}
+
+
+int
+sbicintr(dev)
+ struct sbic_softc *dev;
+{
+ sbic_regmap_p regs;
+ u_char asr, csr;
+ int i;
+
+ regs = dev->sc_sbicp;
+
+ /*
+ * pending interrupt?
+ */
+ GET_SBIC_asr (regs, asr);
+ if ((asr & SBIC_ASR_INT) == 0)
+ return(0);
+
+ SBIC_TRACE(dev);
+ do {
+ GET_SBIC_csr(regs, csr);
+ CSR_TRACE('i',csr,asr,dev->target);
+#ifdef DEBUG
+ debug_csr = csr;
+ routine = 2;
+#endif
+ QPRINTF(("intr[0x%x]", csr));
+
+ i = sbicnextstate(dev, csr, asr);
+
+ WAIT_CIP(regs);
+ GET_SBIC_asr(regs, asr);
+#ifdef DEBUG
+ debug_asr = asr;
+#endif
+#if 0
+ if(asr & SBIC_ASR_LCI) printf("sbicintr: LCI asr:%02x csr:%02x\n",
+ asr,csr);
+#endif
+ } while(i == SBIC_STATE_RUNNING &&
+ asr & (SBIC_ASR_INT|SBIC_ASR_LCI));
+ CSR_TRACE('i',csr,asr,i<<4);
+ SBIC_TRACE(dev);
+ return(1);
+}
+
+/*
+ * Run commands and wait for disconnect
+ */
+int
+sbicpoll(dev)
+ struct sbic_softc *dev;
+{
+ sbic_regmap_p regs;
+ u_char asr, csr;
+ int i;
+
+ SBIC_TRACE(dev);
+ regs = dev->sc_sbicp;
+
+ do {
+ GET_SBIC_asr (regs, asr);
+#ifdef DEBUG
+ debug_asr = asr;
+#endif
+ GET_SBIC_csr(regs, csr);
+ CSR_TRACE('p',csr,asr,dev->target);
+#ifdef DEBUG
+ debug_csr = csr;
+ routine = 2;
+#endif
+ QPRINTF(("poll[0x%x]", csr));
+
+ i = sbicnextstate(dev, csr, asr);
+
+ WAIT_CIP(regs);
+ GET_SBIC_asr(regs, asr);
+ /* tapes may take a loooong time.. */
+ while (asr & SBIC_ASR_BSY){
+ if(asr & SBIC_ASR_DBR) {
+ printf("sbipoll: Waiting while sbic is jammed, CSR:%02x,ASR:%02x\n",
+ csr,asr);
+#ifdef DDB
+ Debugger();
+#endif
+ /* SBIC is jammed */
+ /* DUNNO which direction */
+ /* Try old direction */
+ GET_SBIC_data(regs,i);
+ GET_SBIC_asr(regs, asr);
+ if( asr & SBIC_ASR_DBR) /* Wants us to write */
+ SET_SBIC_data(regs,i);
+ }
+ GET_SBIC_asr(regs, asr);
+ }
+
+ if(asr & SBIC_ASR_LCI) printf("sbicpoll: LCI asr:%02x csr:%02x\n",
+ asr,csr);
+ else if( i == 1 ) /* BSY */
+ SBIC_WAIT(regs, SBIC_ASR_INT, sbic_cmd_wait);
+ } while(i == SBIC_STATE_RUNNING);
+ CSR_TRACE('p',csr,asr,i<<4);
+ SBIC_TRACE(dev);
+ return(1);
+}
+
+/*
+ * Handle a single msgin
+ */
+
+int
+sbicmsgin(dev)
+ struct sbic_softc *dev;
+{
+ sbic_regmap_p regs;
+ int recvlen;
+ u_char asr, csr, *tmpaddr;
+
+ regs = dev->sc_sbicp;
+
+ dev->sc_msg[0] = 0xff;
+ dev->sc_msg[1] = 0xff;
+
+ GET_SBIC_asr(regs, asr);
+#ifdef DEBUG
+ if(reselect_debug>1)
+ printf("sbicmsgin asr=%02x\n", asr);
+#endif
+
+ sbic_save_ptrs(dev, regs, dev->target, dev->lun);
+
+ GET_SBIC_selid (regs, csr);
+ SET_SBIC_selid (regs, csr | SBIC_SID_FROM_SCSI);
+
+ SBIC_TC_PUT(regs, 0);
+ tmpaddr = dev->sc_msg;
+ recvlen = 1;
+ do {
+ while( recvlen-- ) {
+ asr = GET_SBIC_asr(regs, asr);
+ GET_SBIC_csr(regs, csr);
+ QPRINTF(("sbicmsgin ready to go (csr,asr)=(%02x,%02x)\n",
+ csr, asr));
+
+ RECV_BYTE(regs, *tmpaddr);
+ CSR_TRACE('m',csr,asr,*tmpaddr);
+#if 1
+ /*
+ * get the command completion interrupt, or we
+ * can't send a new command (LCI)
+ */
+ SBIC_WAIT(regs, SBIC_ASR_INT, 0);
+ GET_SBIC_csr(regs, csr);
+ CSR_TRACE('X',csr,asr,dev->target);
+#else
+ WAIT_CIP(regs);
+ do {
+ GET_SBIC_asr(regs, asr);
+ csr = 0xff;
+ GET_SBIC_csr(regs, csr);
+ CSR_TRACE('X',csr,asr,dev->target);
+ if( csr == 0xff )
+ printf("sbicmsgin waiting: csr %02x asr %02x\n", csr, asr);
+ } while( csr == 0xff );
+#endif
+#ifdef DEBUG
+ if(reselect_debug>1)
+ printf("sbicmsgin: got %02x csr %02x asr %02x\n",
+ *tmpaddr, csr, asr);
+#endif
+#if do_parity_check
+ if( asr & SBIC_ASR_PE ) {
+ printf ("Parity error");
+ /* This code simply does not work. */
+ WAIT_CIP(regs);
+ SET_SBIC_cmd(regs, SBIC_CMD_SET_ATN);
+ WAIT_CIP(regs);
+ GET_SBIC_asr(regs, asr);
+ WAIT_CIP(regs);
+ SET_SBIC_cmd(regs, SBIC_CMD_CLR_ACK);
+ WAIT_CIP(regs);
+ if( !(asr & SBIC_ASR_LCI) )
+ /* Target wants to send garbled msg*/
+ continue;
+ printf("--fixing\n");
+ /* loop until a msgout phase occurs on target */
+ while(csr & 0x07 != MESG_OUT_PHASE) {
+ while( asr & SBIC_ASR_BSY &&
+ !(asr & SBIC_ASR_DBR|SBIC_ASR_INT) )
+ GET_SBIC_asr(regs, asr);
+ if( asr & SBIC_ASR_DBR )
+ panic("msgin: jammed again!\n");
+ GET_SBIC_csr(regs, csr);
+ CSR_TRACE('e',csr,asr,dev->target);
+ if( csr & 0x07 != MESG_OUT_PHASE ) {
+ sbicnextstate(dev, csr, asr);
+ sbic_save_ptrs(dev, regs,
+ dev->target,
+ dev->lun);
+ }
+ }
+ /* Should be msg out by now */
+ SEND_BYTE(regs, MSG_PARITY_ERROR);
+ }
+ else
+#endif
+ tmpaddr++;
+
+ if(recvlen) {
+ /* Clear ACK */
+ WAIT_CIP(regs);
+ GET_SBIC_asr(regs, asr);
+ GET_SBIC_csr(regs, csr);
+ CSR_TRACE('X',csr,asr,dev->target);
+ QPRINTF(("sbicmsgin pre byte CLR_ACK (csr,asr)=(%02x,%02x)\n",
+ csr, asr));
+ SET_SBIC_cmd(regs, SBIC_CMD_CLR_ACK);
+ SBIC_WAIT(regs, SBIC_ASR_INT, 0);
+ }
+
+ };
+
+ if(dev->sc_msg[0] == 0xff) {
+ printf("sbicmsgin: sbic swallowed our message\n");
+ break;
+ }
+#ifdef DEBUG
+ if (sync_debug)
+ printf("msgin done csr 0x%x asr 0x%x msg 0x%x\n",
+ csr, asr, dev->sc_msg[0]);
+#endif
+ /*
+ * test whether this is a reply to our sync
+ * request
+ */
+ if (MSG_ISIDENTIFY(dev->sc_msg[0])) {
+ QPRINTF(("IFFY"));
+#if 0
+ /* There is an implied load-ptrs here */
+ sbic_load_ptrs(dev, regs, dev->target, dev->lun);
+#endif
+ /* Got IFFY msg -- ack it */
+ } else if (dev->sc_msg[0] == MSG_REJECT
+ && dev->sc_sync[dev->target].state == SYNC_SENT) {
+ QPRINTF(("REJECT of SYN"));
+#ifdef DEBUG
+ if (sync_debug)
+ printf("target %d rejected sync, going async\n",
+ dev->target);
+#endif
+ dev->sc_sync[dev->target].period = sbic_min_period;
+ dev->sc_sync[dev->target].offset = 0;
+ dev->sc_sync[dev->target].state = SYNC_DONE;
+ SET_SBIC_syn(regs,
+ SBIC_SYN(dev->sc_sync[dev->target].offset,
+ dev->sc_sync[dev->target].period));
+ } else if ((dev->sc_msg[0] == MSG_REJECT)) {
+ QPRINTF(("REJECT"));
+ /*
+ * we'll never REJECt a REJECT message..
+ */
+ } else if ((dev->sc_msg[0] == MSG_SAVE_DATA_PTR)) {
+ QPRINTF(("MSG_SAVE_DATA_PTR"));
+ /*
+ * don't reject this either.
+ */
+ } else if ((dev->sc_msg[0] == MSG_DISCONNECT)) {
+ QPRINTF(("DISCONNECT"));
+#ifdef DEBUG
+ if( reselect_debug>1 && dev->sc_msg[0] == MSG_DISCONNECT )
+ printf("sbicmsgin: got disconnect msg %s\n",
+ (dev->sc_flags & SBICF_ICMD)?"rejecting":"");
+#endif
+ if( dev->sc_flags & SBICF_ICMD ) {
+ /* We're in immediate mode. Prevent disconnects. */
+ /* prepare to reject the message, NACK */
+ SET_SBIC_cmd(regs, SBIC_CMD_SET_ATN);
+ WAIT_CIP(regs);
+ }
+ } else if (dev->sc_msg[0] == MSG_CMD_COMPLETE ) {
+ QPRINTF(("CMD_COMPLETE"));
+ /* !! KLUDGE ALERT !! quite a few drives don't seem to
+ * really like the current way of sending the
+ * sync-handshake together with the ident-message, and
+ * they react by sending command-complete and
+ * disconnecting right after returning the valid sync
+ * handshake. So, all I can do is reselect the drive,
+ * and hope it won't disconnect again. I don't think
+ * this is valid behavior, but I can't help fixing a
+ * problem that apparently exists.
+ *
+ * Note: we should not get here on `normal' command
+ * completion, as that condition is handled by the
+ * high-level sel&xfer resume command used to walk
+ * thru status/cc-phase.
+ */
+
+#ifdef DEBUG
+ if (sync_debug)
+ printf ("GOT MSG %d! target %d acting weird.."
+ " waiting for disconnect...\n",
+ dev->sc_msg[0], dev->target);
+#endif
+ /* Check to see if sbic is handling this */
+ GET_SBIC_asr(regs, asr);
+ if(asr & SBIC_ASR_BSY)
+ return SBIC_STATE_RUNNING;
+
+ /* Let's try this: Assume it works and set status to 00 */
+ dev->sc_stat[0] = 0;
+ } else if (dev->sc_msg[0] == MSG_EXT_MESSAGE
+ && tmpaddr == &dev->sc_msg[1]) {
+ QPRINTF(("ExtMSG\n"));
+ /* Read in whole extended message */
+ SET_SBIC_cmd(regs, SBIC_CMD_CLR_ACK);
+ SBIC_WAIT(regs, SBIC_ASR_INT, 0);
+ GET_SBIC_asr(regs, asr);
+ GET_SBIC_csr(regs, csr);
+ QPRINTF(("CLR ACK asr %02x, csr %02x\n", asr, csr));
+ RECV_BYTE(regs, *tmpaddr);
+ CSR_TRACE('x',csr,asr,*tmpaddr);
+ /* Wait for command completion IRQ */
+ SBIC_WAIT(regs, SBIC_ASR_INT, 0);
+ recvlen = *tmpaddr++;
+ QPRINTF(("Recving ext msg, asr %02x csr %02x len %02x\n",
+ asr, csr, recvlen));
+ } else if (dev->sc_msg[0] == MSG_EXT_MESSAGE && dev->sc_msg[1] == 3
+ && dev->sc_msg[2] == MSG_SYNC_REQ) {
+ QPRINTF(("SYN"));
+ dev->sc_sync[dev->target].period =
+ sbicfromscsiperiod(dev,
+ regs, dev->sc_msg[3]);
+ dev->sc_sync[dev->target].offset = dev->sc_msg[4];
+ dev->sc_sync[dev->target].state = SYNC_DONE;
+ SET_SBIC_syn(regs,
+ SBIC_SYN(dev->sc_sync[dev->target].offset,
+ dev->sc_sync[dev->target].period));
+ printf("%s: target %d now synchronous,"
+ " period=%dns, offset=%d.\n",
+ dev->sc_dev.dv_xname, dev->target,
+ dev->sc_msg[3] * 4, dev->sc_msg[4]);
+ } else {
+#ifdef DEBUG
+ if (sbic_debug || sync_debug)
+ printf ("sbicmsgin: Rejecting message 0x%02x\n",
+ dev->sc_msg[0]);
+#endif
+ /* prepare to reject the message, NACK */
+ SET_SBIC_cmd(regs, SBIC_CMD_SET_ATN);
+ WAIT_CIP(regs);
+ }
+ /* Clear ACK */
+ WAIT_CIP(regs);
+ GET_SBIC_asr(regs, asr);
+ GET_SBIC_csr(regs, csr);
+ CSR_TRACE('X',csr,asr,dev->target);
+ QPRINTF(("sbicmsgin pre CLR_ACK (csr,asr)=(%02x,%02x)%d\n",
+ csr, asr, recvlen));
+ SET_SBIC_cmd(regs, SBIC_CMD_CLR_ACK);
+ SBIC_WAIT(regs, SBIC_ASR_INT, 0);
+ }
+#if 0
+ while((csr == SBIC_CSR_MSGIN_W_ACK)
+ || (SBIC_PHASE(csr) == MESG_IN_PHASE));
+#else
+ while (recvlen>0);
+#endif
+
+ QPRINTF(("sbicmsgin finished: csr %02x, asr %02x\n",csr, asr));
+
+ /* Should still have one CSR to read */
+ return SBIC_STATE_RUNNING;
+}
+
+
+/*
+ * sbicnextstate()
+ * return:
+ * 0 == done
+ * 1 == working
+ * 2 == disconnected
+ * -1 == error
+ */
+int
+sbicnextstate(dev, csr, asr)
+ struct sbic_softc *dev;
+ u_char csr, asr;
+{
+ sbic_regmap_p regs;
+ struct sbic_acb *acb;
+ int i, newtarget, newlun, wait;
+#if 0
+ unsigned tcnt;
+#endif
+
+ i = 0;
+ SBIC_TRACE(dev);
+ regs = dev->sc_sbicp;
+ acb = dev->sc_nexus;
+
+ QPRINTF(("next[%02x,%02x]",asr,csr));
+
+ switch (csr) {
+ case SBIC_CSR_XFERRED|CMD_PHASE:
+ case SBIC_CSR_MIS|CMD_PHASE:
+ case SBIC_CSR_MIS_1|CMD_PHASE:
+ case SBIC_CSR_MIS_2|CMD_PHASE:
+ sbic_save_ptrs(dev, regs, dev->target, dev->lun);
+ if (sbicxfstart(regs, acb->clen, CMD_PHASE, sbic_cmd_wait))
+ if (sbicxfout(regs, acb->clen,
+ &acb->cmd, CMD_PHASE))
+ goto abort;
+ break;
+
+ case SBIC_CSR_XFERRED|STATUS_PHASE:
+ case SBIC_CSR_MIS|STATUS_PHASE:
+ case SBIC_CSR_MIS_1|STATUS_PHASE:
+ case SBIC_CSR_MIS_2|STATUS_PHASE:
+ /*
+ * this should be the normal i/o completion case.
+ * get the status & cmd complete msg then let the
+ * device driver look at what happened.
+ */
+ sbicxfdone(dev,regs,dev->target);
+ /*
+ * check for overlapping cache line, flush if so
+ */
+#if defined(M68040) || defined(M68060)
+ if (dev->sc_flags & SBICF_DCFLUSH) {
+#if 0
+ printf("sbic: 68040/68060 DMA cache flush needs "
+ "fixing? %x:%x\n",
+ dev->sc_xs->data, dev->sc_xs->datalen);
+#endif
+ }
+#endif
+#ifdef DEBUG
+ if( data_pointer_debug > 1 )
+ printf("next dmastop: %d(%p:%lx)\n",
+ dev->target,dev->sc_cur->dc_addr,dev->sc_tcnt);
+ dev->sc_dmatimo = 0;
+#endif
+ dev->sc_dmastop(dev); /* was dmafree */
+ if (acb->flags & ACB_BBUF) {
+ if ((u_char *)kvtop(acb->sc_dmausrbuf) != acb->sc_usrbufpa)
+ printf("%s: WARNING - buffer mapping changed %p->%x\n",
+ dev->sc_dev.dv_xname, acb->sc_usrbufpa,
+ kvtop(acb->sc_dmausrbuf));
+#ifdef DEBUG
+ if(data_pointer_debug)
+ printf("sbicgo:copying %lx bytes from target %d bounce %x\n",
+ acb->sc_dmausrlen,
+ dev->target,
+ kvtop(dev->sc_tinfo[dev->target].bounce));
+#endif
+ bcopy(dev->sc_tinfo[dev->target].bounce,
+ acb->sc_dmausrbuf,
+ acb->sc_dmausrlen);
+ }
+ dev->sc_flags &= ~(SBICF_INDMA | SBICF_DCFLUSH);
+ sbic_scsidone(acb, dev->sc_stat[0]);
+ SBIC_TRACE(dev);
+ return SBIC_STATE_DONE;
+
+ case SBIC_CSR_XFERRED|DATA_OUT_PHASE:
+ case SBIC_CSR_XFERRED|DATA_IN_PHASE:
+ case SBIC_CSR_MIS|DATA_OUT_PHASE:
+ case SBIC_CSR_MIS|DATA_IN_PHASE:
+ case SBIC_CSR_MIS_1|DATA_OUT_PHASE:
+ case SBIC_CSR_MIS_1|DATA_IN_PHASE:
+ case SBIC_CSR_MIS_2|DATA_OUT_PHASE:
+ case SBIC_CSR_MIS_2|DATA_IN_PHASE:
+ if( dev->sc_xs->flags & SCSI_POLL || dev->sc_flags & SBICF_ICMD
+ || acb->sc_dmacmd == 0 ) {
+ /* Do PIO */
+ SET_SBIC_control(regs, SBIC_CTL_EDI | SBIC_CTL_IDI);
+ if (acb->sc_kv.dc_count <= 0) {
+ printf("sbicnextstate:xfer count %d asr%x csr%x\n",
+ acb->sc_kv.dc_count, asr, csr);
+ goto abort;
+ }
+ wait = sbic_data_wait;
+ if( sbicxfstart(regs,
+ acb->sc_kv.dc_count,
+ SBIC_PHASE(csr), wait))
+ if( SBIC_PHASE(csr) == DATA_IN_PHASE )
+ /* data in? */
+ i=sbicxfin(regs,
+ acb->sc_kv.dc_count,
+ acb->sc_kv.dc_addr);
+ else
+ i=sbicxfout(regs,
+ acb->sc_kv.dc_count,
+ acb->sc_kv.dc_addr,
+ SBIC_PHASE(csr));
+ acb->sc_kv.dc_addr +=
+ (acb->sc_kv.dc_count - i);
+ acb->sc_kv.dc_count = i;
+ } else {
+ if (acb->sc_kv.dc_count <= 0) {
+ printf("sbicnextstate:xfer count %d asr%x csr%x\n",
+ acb->sc_kv.dc_count, asr, csr);
+ goto abort;
+ }
+ /*
+ * do scatter-gather dma
+ * hacking the controller chip, ouch..
+ */
+ SET_SBIC_control(regs, SBIC_CTL_EDI | SBIC_CTL_IDI |
+ SBIC_MACHINE_DMA_MODE);
+ /*
+ * set next dma addr and dec count
+ */
+#if 0
+ SBIC_TC_GET(regs, tcnt);
+ dev->sc_cur->dc_count -= ((dev->sc_tcnt - tcnt) >> 1);
+ dev->sc_cur->dc_addr += (dev->sc_tcnt - tcnt);
+ dev->sc_tcnt = acb->sc_tcnt = tcnt;
+#else
+ sbic_save_ptrs(dev, regs, dev->target, dev->lun);
+ sbic_load_ptrs(dev, regs, dev->target, dev->lun);
+#endif
+#ifdef DEBUG
+ if( data_pointer_debug > 1 )
+ printf("next dmanext: %d(%p:%lx)\n",
+ dev->target,dev->sc_cur->dc_addr,
+ dev->sc_tcnt);
+ dev->sc_dmatimo = 1;
+#endif
+ dev->sc_tcnt = dev->sc_dmanext(dev);
+ SBIC_TC_PUT(regs, (unsigned)dev->sc_tcnt);
+ SET_SBIC_cmd(regs, SBIC_CMD_XFER_INFO);
+ dev->sc_flags |= SBICF_INDMA;
+ }
+ break;
+
+ case SBIC_CSR_XFERRED|MESG_IN_PHASE:
+ case SBIC_CSR_MIS|MESG_IN_PHASE:
+ case SBIC_CSR_MIS_1|MESG_IN_PHASE:
+ case SBIC_CSR_MIS_2|MESG_IN_PHASE:
+ SBIC_TRACE(dev);
+ return sbicmsgin(dev);
+
+ case SBIC_CSR_MSGIN_W_ACK:
+ SET_SBIC_cmd(regs, SBIC_CMD_CLR_ACK); /* Dunno what I'm ACKing */
+ printf("Acking unknown msgin CSR:%02x",csr);
+ break;
+
+ case SBIC_CSR_XFERRED|MESG_OUT_PHASE:
+ case SBIC_CSR_MIS|MESG_OUT_PHASE:
+ case SBIC_CSR_MIS_1|MESG_OUT_PHASE:
+ case SBIC_CSR_MIS_2|MESG_OUT_PHASE:
+#ifdef DEBUG
+ if (sync_debug)
+ printf ("sending REJECT msg to last msg.\n");
+#endif
+
+ sbic_save_ptrs(dev, regs, dev->target, dev->lun);
+ /*
+ * should only get here on reject,
+ * since it's always US that
+ * initiate a sync transfer
+ */
+ SEND_BYTE(regs, MSG_REJECT);
+ WAIT_CIP(regs);
+ if( asr & (SBIC_ASR_BSY|SBIC_ASR_LCI|SBIC_ASR_CIP) )
+ printf("next: REJECT sent asr %02x\n", asr);
+ SBIC_TRACE(dev);
+ return SBIC_STATE_RUNNING;
+
+ case SBIC_CSR_DISC:
+ case SBIC_CSR_DISC_1:
+ dev->sc_flags &= ~(SBICF_INDMA|SBICF_SELECTED);
+
+ /* Try to schedule another target */
+#ifdef DEBUG
+ if(reselect_debug>1)
+ printf("sbicnext target %d disconnected\n", dev->target);
+#endif
+ TAILQ_INSERT_HEAD(&dev->nexus_list, acb, chain);
+ ++dev->sc_tinfo[dev->target].dconns;
+ dev->sc_nexus = NULL;
+ dev->sc_xs = NULL;
+
+ if( acb->xs->flags & SCSI_POLL
+ || (dev->sc_flags & SBICF_ICMD)
+ || !sbic_parallel_operations ) {
+ SBIC_TRACE(dev);
+ return SBIC_STATE_DISCONNECT;
+ }
+ sbic_sched(dev);
+ SBIC_TRACE(dev);
+ return SBIC_STATE_DISCONNECT;
+
+ case SBIC_CSR_RSLT_NI:
+ case SBIC_CSR_RSLT_IFY:
+ GET_SBIC_rselid(regs, newtarget);
+ /* check SBIC_RID_SIV? */
+ newtarget &= SBIC_RID_MASK;
+ if (csr == SBIC_CSR_RSLT_IFY) {
+ /* Read IFY msg to avoid lockup */
+ GET_SBIC_data(regs, newlun);
+ WAIT_CIP(regs);
+ newlun &= SBIC_TLUN_MASK;
+ CSR_TRACE('r',csr,asr,newtarget);
+ } else {
+ /* Need to get IFY message */
+ for (newlun = 256; newlun; --newlun) {
+ GET_SBIC_asr(regs, asr);
+ if (asr & SBIC_ASR_INT)
+ break;
+ delay(1);
+ }
+ newlun = 0; /* XXXX */
+ if ((asr & SBIC_ASR_INT) == 0) {
+#ifdef DEBUG
+ if (reselect_debug)
+ printf("RSLT_NI - no IFFY message? asr %x\n", asr);
+#endif
+ } else {
+ GET_SBIC_csr(regs,csr);
+ CSR_TRACE('n',csr,asr,newtarget);
+ if (csr == (SBIC_CSR_MIS | MESG_IN_PHASE) ||
+ csr == (SBIC_CSR_MIS_1 | MESG_IN_PHASE) ||
+ csr == (SBIC_CSR_MIS_2 | MESG_IN_PHASE)) {
+ sbicmsgin(dev);
+ newlun = dev->sc_msg[0] & 7;
+ } else {
+ printf("RSLT_NI - not MESG_IN_PHASE %x\n",
+ csr);
+ }
+ }
+ }
+#ifdef DEBUG
+ if(reselect_debug>1 || (reselect_debug && csr==SBIC_CSR_RSLT_NI))
+ printf("sbicnext: reselect %s from targ %d lun %d\n",
+ csr == SBIC_CSR_RSLT_NI ? "NI" : "IFY",
+ newtarget, newlun);
+#endif
+ if (dev->sc_nexus) {
+#ifdef DEBUG
+ if (reselect_debug > 1)
+ printf("%s: reselect %s with active command\n",
+ dev->sc_dev.dv_xname,
+ csr == SBIC_CSR_RSLT_NI ? "NI" : "IFY");
+#ifdef DDB
+/* Debugger();*/
+#endif
+#endif
+ TAILQ_INSERT_HEAD(&dev->ready_list, dev->sc_nexus, chain);
+ dev->sc_tinfo[dev->target].lubusy &= ~(1 << dev->lun);
+ dev->sc_nexus = NULL;
+ dev->sc_xs = NULL;
+ }
+ /* Reload sync values for this target */
+ if (dev->sc_sync[newtarget].state == SYNC_DONE)
+ SET_SBIC_syn(regs, SBIC_SYN (dev->sc_sync[newtarget].offset,
+ dev->sc_sync[newtarget].period));
+ else
+ SET_SBIC_syn(regs, SBIC_SYN (0, sbic_min_period));
+ for (acb = dev->nexus_list.tqh_first; acb;
+ acb = acb->chain.tqe_next) {
+ if (acb->xs->sc_link->target != newtarget ||
+ acb->xs->sc_link->lun != newlun)
+ continue;
+ TAILQ_REMOVE(&dev->nexus_list, acb, chain);
+ dev->sc_nexus = acb;
+ dev->sc_xs = acb->xs;
+ dev->sc_flags |= SBICF_SELECTED;
+ dev->target = newtarget;
+ dev->lun = newlun;
+ break;
+ }
+ if (acb == NULL) {
+ printf("%s: reselect %s targ %d not in nexus_list %p\n",
+ dev->sc_dev.dv_xname,
+ csr == SBIC_CSR_RSLT_NI ? "NI" : "IFY", newtarget,
+ &dev->nexus_list.tqh_first);
+ panic("bad reselect in sbic");
+ }
+ if (csr == SBIC_CSR_RSLT_IFY)
+ SET_SBIC_cmd(regs, SBIC_CMD_CLR_ACK);
+ break;
+
+ default:
+ abort:
+ /*
+ * Something unexpected happened -- deal with it.
+ */
+ printf("sbicnextstate: aborting csr %02x asr %02x\n", csr, asr);
+#ifdef DDB
+ Debugger();
+#endif
+#ifdef DEBUG
+ if( data_pointer_debug > 1 )
+ printf("next dmastop: %d(%p:%lx)\n",
+ dev->target,dev->sc_cur->dc_addr,dev->sc_tcnt);
+ dev->sc_dmatimo = 0;
+#endif
+ dev->sc_dmastop(dev);
+ SET_SBIC_control(regs, SBIC_CTL_EDI | SBIC_CTL_IDI);
+ sbicerror(dev, regs, csr);
+ sbicabort(dev, regs, "next");
+ if (dev->sc_flags & SBICF_INDMA) {
+ /*
+ * check for overlapping cache line, flush if so
+ */
+#if defined(M68040) || defined(M68060)
+
+ if (dev->sc_flags & SBICF_DCFLUSH) {
+#if 0
+ printf("sbic: 68040/060 DMA cache flush needs "
+ "fixing? %x:%x\n",
+ dev->sc_xs->data, dev->sc_xs->datalen);
+#endif
+ }
+#endif
+ dev->sc_flags &=
+ ~(SBICF_INDMA | SBICF_DCFLUSH);
+#ifdef DEBUG
+ if( data_pointer_debug > 1 )
+ printf("next dmastop: %d(%p:%lx)\n",
+ dev->target,dev->sc_cur->dc_addr,dev->sc_tcnt);
+ dev->sc_dmatimo = 0;
+#endif
+ dev->sc_dmastop(dev);
+ sbic_scsidone(acb, -1);
+ }
+ SBIC_TRACE(dev);
+ return SBIC_STATE_ERROR;
+ }
+
+ SBIC_TRACE(dev);
+ return(SBIC_STATE_RUNNING);
+}
+
+
+/*
+ * Check if DMA can not be used with specified buffer
+ */
+
+int
+sbiccheckdmap(bp, len, mask)
+ void *bp;
+ u_long len, mask;
+{
+ u_char *buffer;
+ u_long phy_buf;
+ u_long phy_len;
+
+ buffer = bp;
+
+ if (len == 0)
+ return(0);
+
+ while (len) {
+ phy_buf = kvtop(buffer);
+ if (len < (phy_len = NBPG - ((int) buffer & PGOFSET)))
+ phy_len = len;
+ if (phy_buf & mask)
+ return(1);
+ buffer += phy_len;
+ len -= phy_len;
+ }
+ return(0);
+}
+
+int
+sbictoscsiperiod(dev, regs, a)
+ struct sbic_softc *dev;
+ sbic_regmap_p regs;
+ int a;
+{
+ unsigned int fs;
+
+ /*
+ * cycle = DIV / (2*CLK)
+ * DIV = FS+2
+ * best we can do is 200ns at 20Mhz, 2 cycles
+ */
+
+ GET_SBIC_myid(regs,fs);
+ fs = (fs >>6) + 2; /* DIV */
+ fs = (fs * 10000) / (dev->sc_clkfreq<<1); /* Cycle, in ns */
+ if (a < 2) a = 8; /* map to Cycles */
+ return ((fs*a)>>2); /* in 4 ns units */
+}
+
+int
+sbicfromscsiperiod(dev, regs, p)
+ struct sbic_softc *dev;
+ sbic_regmap_p regs;
+ int p;
+{
+ register unsigned int fs, ret;
+
+ /* Just the inverse of the above */
+
+ GET_SBIC_myid(regs,fs);
+ fs = (fs >>6) + 2; /* DIV */
+ fs = (fs * 10000) / (dev->sc_clkfreq<<1); /* Cycle, in ns */
+
+ ret = p << 2; /* in ns units */
+ ret = ret / fs; /* in Cycles */
+ if (ret < sbic_min_period)
+ return(sbic_min_period);
+
+ /* verify rounding */
+ if (sbictoscsiperiod(dev, regs, ret) < p)
+ ret++;
+ return (ret >= 8) ? 0 : ret;
+}
+
+#ifdef DEBUG
+
+void
+sbicdumpstate()
+{
+ u_char csr, asr;
+
+ GET_SBIC_asr(debug_sbic_regs,asr);
+ GET_SBIC_csr(debug_sbic_regs,csr);
+ printf("%s: asr:csr(%02x:%02x)->(%02x:%02x)\n",
+ (routine==1)?"sbicgo":
+ (routine==2)?"sbicintr":
+ (routine==3)?"sbicicmd":
+ (routine==4)?"sbicnext":"unknown",
+ debug_asr, debug_csr, asr, csr);
+
+}
+
+void
+sbictimeout(dev)
+ struct sbic_softc *dev;
+{
+ int s, asr;
+
+ s = splbio();
+ if (dev->sc_dmatimo) {
+ if (dev->sc_dmatimo > 1) {
+ printf("%s: dma timeout #%d\n",
+ dev->sc_dev.dv_xname, dev->sc_dmatimo - 1);
+ GET_SBIC_asr(dev->sc_sbicp, asr);
+ if( asr & SBIC_ASR_INT ) {
+ /* We need to service a missed IRQ */
+ printf("Servicing a missed int:(%02x,%02x)->(%02x,??)\n",
+ debug_asr, debug_csr, asr);
+ sbicintr(dev);
+ }
+ sbicdumpstate();
+ }
+ dev->sc_dmatimo++;
+ }
+ splx(s);
+ timeout((void *)sbictimeout, dev, 30 * hz);
+}
+
+void
+sbic_dump_acb(acb)
+ struct sbic_acb *acb;
+{
+ u_char *b = (u_char *) &acb->cmd;
+ int i;
+
+ printf("acb@%p ", acb);
+ if (acb->xs == NULL) {
+ printf("<unused>\n");
+ return;
+ }
+ printf("(%d:%d) flags %2x clen %2d cmd ", acb->xs->sc_link->target,
+ acb->xs->sc_link->lun, acb->flags, acb->clen);
+ for (i = acb->clen; i; --i)
+ printf(" %02x", *b++);
+ printf("\n");
+ printf(" xs: %8p data %8p:%04x ", acb->xs, acb->xs->data,
+ acb->xs->datalen);
+ printf("va %8p:%04x ", acb->sc_kv.dc_addr, acb->sc_kv.dc_count);
+ printf("pa %8p:%04x tcnt %lx\n", acb->sc_pa.dc_addr, acb->sc_pa.dc_count,
+ acb->sc_tcnt);
+}
+
+void
+sbic_dump(dev)
+ struct sbic_softc *dev;
+{
+ sbic_regmap_p regs;
+ u_char csr, asr;
+ struct sbic_acb *acb;
+ int s;
+ int i;
+
+ s = splbio();
+ regs = dev->sc_sbicp;
+#if CSR_TRACE_SIZE
+ printf("csr trace: ");
+ i = csr_traceptr;
+ do {
+ printf("%c%02x%02x%02x ", csr_trace[i].whr,
+ csr_trace[i].csr, csr_trace[i].asr, csr_trace[i].xtn);
+ switch(csr_trace[i].whr) {
+ case 'g':
+ printf("go "); break;
+ case 's':
+ printf("select "); break;
+ case 'y':
+ printf("select+ "); break;
+ case 'i':
+ printf("intr "); break;
+ case 'f':
+ printf("finish "); break;
+ case '>':
+ printf("out "); break;
+ case '<':
+ printf("in "); break;
+ case 'm':
+ printf("msgin "); break;
+ case 'x':
+ printf("msginx "); break;
+ case 'X':
+ printf("msginX "); break;
+ case 'r':
+ printf("reselect "); break;
+ case 'I':
+ printf("icmd "); break;
+ case 'a':
+ printf("abort "); break;
+ default:
+ printf("? ");
+ }
+ switch(csr_trace[i].csr) {
+ case 0x11:
+ printf("INITIATOR"); break;
+ case 0x16:
+ printf("S_XFERRED"); break;
+ case 0x20:
+ printf("MSGIN_ACK"); break;
+ case 0x41:
+ printf("DISC"); break;
+ case 0x42:
+ printf("SEL_TIMEO"); break;
+ case 0x80:
+ printf("RSLT_NI"); break;
+ case 0x81:
+ printf("RSLT_IFY"); break;
+ case 0x85:
+ printf("DISC_1"); break;
+ case 0x18: case 0x19: case 0x1a:
+ case 0x1b: case 0x1e: case 0x1f:
+ case 0x28: case 0x29: case 0x2a:
+ case 0x2b: case 0x2e: case 0x2f:
+ case 0x48: case 0x49: case 0x4a:
+ case 0x4b: case 0x4e: case 0x4f:
+ case 0x88: case 0x89: case 0x8a:
+ case 0x8b: case 0x8e: case 0x8f:
+ switch(csr_trace[i].csr & 0xf0) {
+ case 0x10:
+ printf("DONE_"); break;
+ case 0x20:
+ printf("STOP_"); break;
+ case 0x40:
+ printf("ERR_"); break;
+ case 0x80:
+ printf("REQ_"); break;
+ }
+ switch(csr_trace[i].csr & 7) {
+ case 0:
+ printf("DATA_OUT"); break;
+ case 1:
+ printf("DATA_IN"); break;
+ case 2:
+ printf("CMD"); break;
+ case 3:
+ printf("STATUS"); break;
+ case 6:
+ printf("MSG_OUT"); break;
+ case 7:
+ printf("MSG_IN"); break;
+ default:
+ printf("invld phs");
+ }
+ break;
+ default: printf("****"); break;
+ }
+ if (csr_trace[i].asr & SBIC_ASR_INT)
+ printf(" ASR_INT");
+ if (csr_trace[i].asr & SBIC_ASR_LCI)
+ printf(" ASR_LCI");
+ if (csr_trace[i].asr & SBIC_ASR_BSY)
+ printf(" ASR_BSY");
+ if (csr_trace[i].asr & SBIC_ASR_CIP)
+ printf(" ASR_CIP");
+ printf("\n");
+ i = (i + 1) & (CSR_TRACE_SIZE - 1);
+ } while (i != csr_traceptr);
+#endif
+ GET_SBIC_asr(regs, asr);
+ if ((asr & SBIC_ASR_INT) == 0)
+ GET_SBIC_csr(regs, csr);
+ else
+ csr = 0;
+ printf("%s@%p regs %p asr %x csr %x\n", dev->sc_dev.dv_xname,
+ dev, regs, asr, csr);
+ if ((acb = dev->free_list.tqh_first)) {
+ printf("Free list:\n");
+ while (acb) {
+ sbic_dump_acb(acb);
+ acb = acb->chain.tqe_next;
+ }
+ }
+ if ((acb = dev->ready_list.tqh_first)) {
+ printf("Ready list:\n");
+ while (acb) {
+ sbic_dump_acb(acb);
+ acb = acb->chain.tqe_next;
+ }
+ }
+ if ((acb = dev->nexus_list.tqh_first)) {
+ printf("Nexus list:\n");
+ while (acb) {
+ sbic_dump_acb(acb);
+ acb = acb->chain.tqe_next;
+ }
+ }
+ if (dev->sc_nexus) {
+ printf("nexus:\n");
+ sbic_dump_acb(dev->sc_nexus);
+ }
+ printf("sc_xs %p targ %d lun %d flags %x tcnt %lx dmacmd %x mask %lx\n",
+ dev->sc_xs, dev->target, dev->lun, dev->sc_flags, dev->sc_tcnt,
+ dev->sc_dmacmd, dev->sc_dmamask);
+ for (i = 0; i < 8; ++i) {
+ if (dev->sc_tinfo[i].cmds > 2) {
+ printf("tgt %d: cmds %d disc %d senses %d lubusy %x\n",
+ i, dev->sc_tinfo[i].cmds,
+ dev->sc_tinfo[i].dconns,
+ dev->sc_tinfo[i].senses,
+ dev->sc_tinfo[i].lubusy);
+ }
+ }
+ splx(s);
+}
+
+#endif
diff --git a/sys/arch/kbus/dev/sbic.c.mvme b/sys/arch/kbus/dev/sbic.c.mvme
new file mode 100644
index 00000000000..adf244611ac
--- /dev/null
+++ b/sys/arch/kbus/dev/sbic.c.mvme
@@ -0,0 +1,2719 @@
+/* $OpenBSD: sbic.c.mvme,v 1.1 1997/10/14 07:25:29 gingold Exp $ */
+/* $NetBSD: sbic.c,v 1.2 1996/04/23 16:32:54 chuck Exp $ */
+/*
+ * Changes Copyright (c) 1996 Steve Woodford
+ * Original Copyright (c) 1994 Christian E. Hopps
+ * Copyright (c) 1990 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Van Jacobson of Lawrence Berkeley Laboratory.
+ *
+ * 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.
+ *
+ * @(#)scsi.c 7.5 (Berkeley) 5/4/91
+ */
+
+/*
+ * Steve Woodford (SCW), Apr, 1996
+ * MVME147S WD33C93 Scsi Bus Interface Controller driver,
+ *
+ * Basically a de-loused and tidied up version of the Amiga AMD 33C93 driver.
+ *
+ * The original driver used features which required at least a WD33C93A
+ * chip. The '147 has the original WD33C93 chip (no 'A' suffix).
+ *
+ * This version of the driver is pretty well generic, so should work with
+ * any flavour of WD33C93 chip.
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/device.h>
+#include <sys/kernel.h> /* For hz */
+#include <sys/disklabel.h>
+#include <sys/dkstat.h>
+#include <sys/buf.h>
+#include <scsi/scsi_all.h>
+#include <scsi/scsiconf.h>
+#include <vm/vm.h>
+#include <vm/vm_kern.h>
+#include <vm/vm_page.h>
+#include <vm/pmap.h>
+#include <machine/pmap.h>
+#include <dev/dmavar.h>
+#include <dev/sbicreg.h>
+#include <dev/sbicvar.h>
+#include <machine/autoconf.h>
+
+/*
+ * Since I can't find this in any other header files
+ */
+#define SCSI_PHASE(reg) (reg&0x07)
+
+/*
+ * SCSI delays
+ * In u-seconds, primarily for state changes on the SPC.
+ */
+#define SBIC_CMD_WAIT 50000 /* wait per step of 'immediate' cmds */
+#define SBIC_DATA_WAIT 50000 /* wait per data in/out step */
+#define SBIC_INIT_WAIT 50000 /* wait per step (both) during init */
+
+/*
+ * Convenience macro for waiting for a particular sbic event
+ */
+#define SBIC_WAIT(regs, until, timeo) sbicwait(regs, until, timeo, __LINE__)
+
+int sbicicmd __P((struct sbic_softc *, void *, int, void *, int));
+int sbicgo __P((struct sbic_softc *, struct scsi_xfer *));
+int sbicdmaok __P((struct sbic_softc *, struct scsi_xfer *));
+int sbicwait __P((sbic_regmap_p, u_char, int , int));
+int sbiccheckdmap __P((void *, u_long, u_long));
+u_char sbicselectbus __P((struct sbic_softc *));
+int sbicxfout __P((sbic_regmap_p, int, void *));
+int sbicxfin __P((sbic_regmap_p, int, void *));
+int sbicfromscsiperiod __P((struct sbic_softc *, int));
+int sbictoscsiperiod __P((struct sbic_softc *, int));
+int sbicintr __P((struct sbic_softc *));
+int sbicpoll __P((struct sbic_softc *));
+int sbicnextstate __P((struct sbic_softc *, u_char, u_char));
+int sbicmsgin __P((struct sbic_softc *));
+int sbicabort __P((struct sbic_softc *, char *));
+void sbicxfdone __P((struct sbic_softc *));
+void sbicerror __P((struct sbic_softc *,u_char));
+void sbicreset __P((struct sbic_softc *));
+void sbic_scsidone __P((struct sbic_acb *, int));
+void sbic_sched __P((struct sbic_softc *));
+void sbic_save_ptrs __P((struct sbic_softc *));
+void sbic_load_ptrs __P((struct sbic_softc *));
+
+/*
+ * Synch xfer parameters, and timing conversions
+ */
+int sbic_min_period = SBIC_SYN_MIN_PERIOD; /* in cycles = f(ICLK,FSn) */
+int sbic_max_offset = SBIC_SYN_MAX_OFFSET; /* pure number */
+int sbic_cmd_wait = SBIC_CMD_WAIT;
+int sbic_data_wait = SBIC_DATA_WAIT;
+int sbic_init_wait = SBIC_INIT_WAIT;
+
+/*
+ * was broken before.. now if you want this you get it for all drives
+ * on sbic controllers.
+ */
+u_char sbic_inhibit_sync[8];
+int sbic_enable_reselect = 1; /* Allow Disconnect / Reselect */
+int sbic_no_dma = 0; /* Use PIO transfers instead of DMA */
+int sbic_parallel_operations = 1; /* Allow command queues */
+
+/*
+ * Some useful stuff for debugging purposes
+ */
+#ifdef DEBUG
+int sbicdma_ops = 0; /* total DMA operations */
+int sbicdma_hits = 0; /* number of DMA chains that were contiguous */
+int sbicdma_misses = 0; /* number of DMA chains that were not contiguous */
+int sbicdma_saves = 0;
+
+#define QPRINTF(a) if (sbic_debug > 1) printf a
+
+int sbic_debug = 2; /* Debug all chip related things */
+int sync_debug = 0; /* Debug all Synchronous Scsi related things */
+int reselect_debug = 0; /* Debug all reselection related things */
+int report_sense = 0; /* Always print Sense information */
+int data_pointer_debug = 2; /* Debug Data Pointer related things */
+
+void sbictimeout __P((struct sbic_softc *dev));
+
+#else
+#define QPRINTF(a) /* */
+#endif
+
+extern int kvtop __P((caddr_t addr));
+
+/*
+ * default minphys routine for sbic based controllers
+ */
+void
+sbic_minphys(bp)
+ struct buf *bp;
+{
+ /*
+ * No max transfer at this level.
+ */
+ minphys(bp);
+}
+
+
+/*
+ * Save DMA pointers. Take into account partial transfer. Shut down DMA.
+ */
+void
+sbic_save_ptrs(dev)
+ struct sbic_softc *dev;
+{
+ sbic_regmap_p regs;
+ struct sbic_acb* acb;
+ int count,
+ asr,
+ s;
+
+ /*
+ * Only need to save pointers if DMA was active...
+ */
+ if ( dev->sc_cur == NULL || (dev->sc_flags & SBICF_INDMA) == 0 )
+ return;
+
+ regs = dev->sc_sbicp;
+
+ s = splbio();
+
+ /*
+ * Wait until WD chip is idle
+ */
+ do {
+ GET_SBIC_asr(regs, asr);
+ if( asr & SBIC_ASR_DBR ) {
+ printf("sbic_save_ptrs: asr %02x canceled!\n", asr);
+ splx(s);
+ return;
+ }
+ } while( asr & (SBIC_ASR_BSY|SBIC_ASR_CIP) );
+
+
+ /*
+ * Save important state.
+ * must be done before dmastop
+ */
+ acb = dev->sc_nexus;
+ acb->sc_dmacmd = dev->sc_dmacmd;
+
+ /*
+ * Fetch the residual count
+ */
+ SBIC_TC_GET(regs, count);
+
+ /*
+ * Shut down DMA
+ */
+ dev->sc_dmastop(dev);
+
+ /*
+ * No longer in DMA
+ */
+ dev->sc_flags &= ~SBICF_INDMA;
+
+ /*
+ * Ensure the WD chip is back in polled I/O mode, with nothing to
+ * transfer.
+ */
+ SBIC_TC_PUT(regs, 0);
+ SET_SBIC_control(regs, SBIC_CTL_EDI | SBIC_CTL_IDI);
+
+ /*
+ * Update current count...
+ */
+ acb->sc_tcnt = count;
+
+ /*
+ * Work out how many bytes were actually transferred
+ */
+ count = dev->sc_tcnt - count;
+ dev->sc_tcnt = acb->sc_tcnt;
+
+ /*
+ * Fixup partial xfers
+ */
+ acb->sc_kv.dc_addr += count;
+ acb->sc_kv.dc_count -= count;
+ acb->sc_pa.dc_addr += count;
+ acb->sc_pa.dc_count -= count >> 1;
+
+#ifdef DEBUG
+ if ( data_pointer_debug )
+ printf("save at (%x,%x):%x\n",
+ dev->sc_cur->dc_addr, dev->sc_cur->dc_count,count);
+ sbicdma_saves++;
+#endif
+
+ splx(s);
+}
+
+
+/*
+ * DOES NOT RESTART DMA!!!
+ */
+void
+sbic_load_ptrs(dev)
+ struct sbic_softc *dev;
+{
+ struct sbic_acb *acb = dev->sc_nexus;
+ int s;
+
+ if ( acb->sc_kv.dc_count == 0 ) {
+ /*
+ * No data to xfer
+ */
+ return;
+ }
+
+ s = splbio();
+
+ /*
+ * Reset the Scatter-Gather chain
+ */
+ dev->sc_last = dev->sc_cur = &acb->sc_pa;
+
+ /*
+ * Restore the Transfer Count and DMA specific data
+ */
+ dev->sc_tcnt = acb->sc_tcnt;
+ dev->sc_dmacmd = acb->sc_dmacmd;
+
+#ifdef DEBUG
+ sbicdma_ops++;
+#endif
+
+ /*
+ * Need to fixup new segment?
+ */
+ if ( dev->sc_tcnt == 0 ) {
+ /*
+ * sc_tcnt == 0 implies end of segment
+ */
+ char *vaddr, *paddr;
+ int count;
+
+ /*
+ * do kvm to pa mappings
+ */
+ vaddr = acb->sc_kv.dc_addr;
+ paddr = acb->sc_pa.dc_addr = (char *) kvtop(vaddr);
+
+ for (count = (NBPG - ((int)vaddr & PGOFSET));
+ count < acb->sc_kv.dc_count &&
+ (char*)kvtop(vaddr + count + 4) == paddr + count + 4;
+ count += NBPG)
+ ; /* Do nothing */
+
+ /*
+ * If it's all contiguous...
+ */
+ if ( count > acb->sc_kv.dc_count ) {
+ count = acb->sc_kv.dc_count;
+#ifdef DEBUG
+ sbicdma_hits++;
+#endif
+ }
+#ifdef DEBUG
+ else
+ sbicdma_misses++;
+#endif
+
+ acb->sc_tcnt = count;
+ acb->sc_pa.dc_count = count >> 1;
+
+#ifdef DEBUG
+ if ( data_pointer_debug )
+ printf("DMA recalc:kv(%x,%x)pa(%x,%x)\n", acb->sc_kv.dc_addr,
+ acb->sc_kv.dc_count,
+ acb->sc_pa.dc_addr,
+ acb->sc_tcnt);
+#endif
+
+ }
+
+ splx(s);
+}
+
+/*
+ * used by specific sbic controller
+ *
+ * it appears that the higher level code does nothing with LUN's
+ * so I will too. I could plug it in, however so could they
+ * in scsi_scsi_cmd().
+ */
+int
+sbic_scsicmd(xs)
+ struct scsi_xfer *xs;
+{
+ struct scsi_link *slp = xs->sc_link;
+ struct sbic_softc *dev = slp->adapter_softc;
+ struct sbic_acb *acb;
+ int flags = xs->flags,
+ s;
+
+ if ( flags & SCSI_DATA_UIO )
+ panic("sbic: scsi data uio requested");
+
+ if ( dev->sc_nexus && (flags & SCSI_POLL) )
+ panic("sbic_scsicmd: busy");
+
+ if ( slp->target == slp->adapter_target )
+ return ESCAPE_NOT_SUPPORTED;
+
+ s = splbio();
+
+ if ( (acb = dev->free_list.tqh_first) != NULL )
+ TAILQ_REMOVE(&dev->free_list, acb, chain);
+
+ splx(s);
+
+ if ( acb == NULL ) {
+#ifdef DEBUG
+ printf("sbic_scsicmd: unable to queue request for target %d\n",
+ slp->target);
+#ifdef DDB
+ Debugger();
+#endif
+#endif
+ xs->error = XS_DRIVER_STUFFUP;
+
+ return(TRY_AGAIN_LATER);
+ }
+
+ if ( flags & SCSI_DATA_IN )
+ acb->flags = ACB_ACTIVE | ACB_DATAIN;
+ else
+ acb->flags = ACB_ACTIVE;
+
+ acb->xs = xs;
+ acb->clen = xs->cmdlen;
+ acb->sc_kv.dc_addr = xs->data;
+ acb->sc_kv.dc_count = xs->datalen;
+ acb->pa_addr = xs->data ? (char *)kvtop(xs->data) : 0;
+ bcopy(xs->cmd, &acb->cmd, xs->cmdlen);
+
+ if ( flags & SCSI_POLL ) {
+ /*
+ * This has major side effects -- it locks up the machine
+ */
+ int stat;
+
+ s = splbio();
+
+ dev->sc_flags |= SBICF_ICMD;
+
+ do {
+ /*
+ * If we already had a nexus, while away the time until idle...
+ * This is likely only to happen if a reselection occurs between
+ * here and our earlier check for ICMD && sc_nexus (which would
+ * have resulted in a panic() had it been true).
+ */
+ while ( dev->sc_nexus )
+ sbicpoll(dev);
+
+ /*
+ * Fix up the new nexus
+ */
+ dev->sc_nexus = acb;
+ dev->sc_xs = xs;
+ dev->target = slp->target;
+ dev->lun = slp->lun;
+
+ stat = sbicicmd(dev, &acb->cmd, acb->clen,
+ acb->sc_kv.dc_addr, acb->sc_kv.dc_count);
+
+ } while ( dev->sc_nexus != acb );
+
+ sbic_scsidone(acb, stat);
+
+ splx(s);
+
+ return(COMPLETE);
+ }
+
+ s = splbio();
+ TAILQ_INSERT_TAIL(&dev->ready_list, acb, chain);
+
+ /*
+ * If nothing is active, try to start it now.
+ */
+ if ( dev->sc_nexus == NULL )
+ sbic_sched(dev);
+
+ splx(s);
+
+ return(SUCCESSFULLY_QUEUED);
+}
+
+/*
+ * attempt to start the next available command
+ */
+void
+sbic_sched(dev)
+ struct sbic_softc *dev;
+{
+ struct scsi_xfer *xs;
+ struct scsi_link *slp = NULL; /* Gag the compiler */
+ struct sbic_acb *acb;
+ int flags,
+ stat;
+
+ /*
+ * XXXSCW
+ * I'll keep this test here, even though I can't see any obvious way
+ * in which sbic_sched() could be called with sc_nexus non NULL
+ */
+ if ( dev->sc_nexus )
+ return; /* a command is current active */
+
+ /*
+ * Loop through the ready list looking for work to do...
+ */
+ for (acb = dev->ready_list.tqh_first; acb; acb = acb->chain.tqe_next) {
+ int i, j;
+
+ slp = acb->xs->sc_link;
+ i = slp->target;
+ j = 1 << slp->lun;
+
+ /*
+ * We've found a potential command, but is the target/lun busy?
+ */
+ if ( (dev->sc_tinfo[i].lubusy & j) == 0 ) {
+ /*
+ * Nope, it's not busy, so we can use it.
+ */
+ dev->sc_tinfo[i].lubusy |= j;
+ TAILQ_REMOVE(&dev->ready_list, acb, chain);
+ dev->sc_nexus = acb;
+ acb->sc_pa.dc_addr = acb->pa_addr; /* XXXX check */
+ break;
+ }
+ }
+
+ if ( acb == NULL ) {
+ QPRINTF(("sbicsched: no work\n"));
+ return; /* did not find an available command */
+ }
+
+#ifdef DEBUG
+ if ( data_pointer_debug > 1 )
+ printf("sbic_sched(%d,%d)\n", slp->target, slp->lun);
+#endif
+
+ dev->sc_xs = xs = acb->xs;
+ flags = xs->flags;
+
+ if ( flags & SCSI_RESET )
+ sbicreset(dev);
+
+ dev->sc_stat[0] = -1;
+ dev->target = slp->target;
+ dev->lun = slp->lun;
+
+ if ( flags & SCSI_POLL || (!sbic_parallel_operations &&
+ (sbicdmaok(dev, xs) == 0)) )
+ stat = sbicicmd(dev, &acb->cmd, acb->clen,
+ acb->sc_kv.dc_addr, acb->sc_kv.dc_count);
+ else
+ if ( sbicgo(dev, xs) == 0 )
+ return;
+ else
+ stat = dev->sc_stat[0];
+
+ sbic_scsidone(acb, stat);
+}
+
+void
+sbic_scsidone(acb, stat)
+ struct sbic_acb *acb;
+ int stat;
+{
+ struct scsi_xfer *xs = acb->xs;
+ struct scsi_link *slp = xs->sc_link;
+ struct sbic_softc *dev = slp->adapter_softc;
+ int dosched = 0;
+
+#ifdef DIAGNOSTIC
+ if ( acb == NULL || xs == NULL ) {
+ printf("sbic_scsidone -- (%d,%d) no scsi_xfer\n", dev->target, dev->lun);
+#ifdef DDB
+ Debugger();
+#endif
+ return;
+ }
+#endif
+
+ if ( slp->device_softc &&
+ ((struct device *)(slp->device_softc))->dv_unit < dk_ndrive)
+ ++dk_xfer[((struct device *)(slp->device_softc))->dv_unit];
+
+ /*
+ * is this right?
+ */
+ xs->status = stat;
+
+#ifdef DEBUG
+ if ( data_pointer_debug > 1 )
+ printf("scsidone: (%d,%d)->(%d,%d)%02x\n", slp->target, slp->lun,
+ dev->target, dev->lun, stat);
+
+ if ( xs->sc_link->target == dev->sc_link.adapter_target )
+ panic("target == hostid");
+#endif
+
+ if ( xs->error == XS_NOERROR && (acb->flags & ACB_CHKSENSE) == 0 ) {
+
+ if ( stat == SCSI_CHECK ) {
+ /*
+ * Schedule a REQUEST SENSE
+ */
+ struct scsi_sense *ss = (void *)&acb->cmd;
+
+#ifdef DEBUG
+ if ( report_sense )
+ printf("sbic_scsidone: autosense %02x targ %d lun %d",
+ acb->cmd.opcode, slp->target, slp->lun);
+#endif
+
+ bzero(ss, sizeof(*ss));
+
+ ss->opcode = REQUEST_SENSE;
+ ss->byte2 = slp->lun << 5;
+ ss->length = sizeof(struct scsi_sense_data);
+
+ acb->clen = sizeof(*ss);
+ acb->sc_kv.dc_addr = (char *)&xs->sense;
+ acb->sc_kv.dc_count = sizeof(struct scsi_sense_data);
+ acb->pa_addr = (char *)kvtop((caddr_t)&xs->sense); /* XXX check */
+ acb->flags = ACB_ACTIVE | ACB_CHKSENSE | ACB_DATAIN;
+
+ TAILQ_INSERT_HEAD(&dev->ready_list, acb, chain);
+
+ dev->sc_tinfo[slp->target].lubusy &= ~(1 << slp->lun);
+ dev->sc_tinfo[slp->target].senses++;
+
+ if ( dev->sc_nexus == acb ) {
+ dev->sc_nexus = NULL;
+ dev->sc_xs = NULL;
+ sbic_sched(dev);
+ }
+ return;
+ }
+ }
+
+ if ( xs->error == XS_NOERROR && (acb->flags & ACB_CHKSENSE) != 0 ) {
+
+ xs->error = XS_SENSE;
+
+#ifdef DEBUG
+ if (report_sense)
+ printf(" => %02x %02x\n", xs->sense.flags,
+ xs->sense.extra_bytes[3]);
+#endif
+
+ } else {
+ xs->resid = 0; /* XXXX */
+ }
+
+ xs->flags |= ITSDONE;
+
+ /*
+ * Remove the ACB from whatever queue it's on. We have to do a bit of
+ * a hack to figure out which queue it's on. Note that it is *not*
+ * necessary to cdr down the ready queue, but we must cdr down the
+ * nexus queue and see if it's there, so we can mark the unit as no
+ * longer busy. This code is sickening, but it works.
+ */
+ if ( acb == dev->sc_nexus ) {
+
+ dev->sc_nexus = NULL;
+ dev->sc_xs = NULL;
+
+ dev->sc_tinfo[slp->target].lubusy &= ~(1 << slp->lun);
+
+ if ( dev->ready_list.tqh_first )
+ dosched = 1; /* start next command */
+
+ } else
+ if ( dev->ready_list.tqh_last == &acb->chain.tqe_next ) {
+
+ TAILQ_REMOVE(&dev->ready_list, acb, chain);
+
+ } else {
+
+ register struct sbic_acb *a;
+
+ for (a = dev->nexus_list.tqh_first; a; a = a->chain.tqe_next) {
+ if ( a == acb ) {
+ TAILQ_REMOVE(&dev->nexus_list, acb, chain);
+ dev->sc_tinfo[slp->target].lubusy &= ~(1 << slp->lun);
+ break;
+ }
+ }
+
+ if ( a )
+ ;
+ else if ( acb->chain.tqe_next ) {
+ TAILQ_REMOVE(&dev->ready_list, acb, chain);
+ } else {
+ printf("%s: can't find matching acb\n", dev->sc_dev.dv_xname);
+#ifdef DDB
+ Debugger();
+#endif
+ }
+ }
+
+ /*
+ * Put it on the free list.
+ */
+ acb->flags = ACB_FREE;
+ TAILQ_INSERT_HEAD(&dev->free_list, acb, chain);
+
+ dev->sc_tinfo[slp->target].cmds++;
+
+ scsi_done(xs);
+
+ if ( dosched )
+ sbic_sched(dev);
+}
+
+int
+sbicdmaok(dev, xs)
+ struct sbic_softc *dev;
+ struct scsi_xfer *xs;
+{
+ if ( sbic_no_dma || xs->datalen & 0x03 || (int)xs->data & 0x03)
+ return(0);
+
+ /*
+ * controller supports dma to any addresses?
+ */
+ if ( (dev->sc_flags & SBICF_BADDMA) == 0 )
+ return(1);
+
+ /*
+ * this address is ok for dma?
+ */
+ if ( sbiccheckdmap(xs->data, xs->datalen, dev->sc_dmamask) == 0 )
+ return(1);
+
+ return(0);
+}
+
+int
+sbicwait(regs, until, timeo, line)
+ sbic_regmap_p regs;
+ u_char until;
+ int timeo;
+ int line;
+{
+ u_char val;
+
+ if ( timeo == 0 )
+ timeo = 1000000; /* some large value.. */
+
+ GET_SBIC_asr(regs, val);
+
+ while ( (val & until) == 0 ) {
+
+ if ( timeo-- == 0 ) {
+ int csr;
+ GET_SBIC_csr(regs, csr);
+ printf("sbicwait TIMEO @%d with asr=x%x csr=x%x\n", line, val, csr);
+#if defined(DDB) && defined(DEBUG)
+ Debugger();
+#endif
+ return(val); /* Maybe I should abort */
+ break;
+ }
+
+ DELAY(1);
+ GET_SBIC_asr(regs, val);
+ }
+
+ return(val);
+}
+
+int
+sbicabort(dev, where)
+ struct sbic_softc *dev;
+ char *where;
+{
+ sbic_regmap_p regs = dev->sc_sbicp;
+ u_char csr,
+ asr;
+
+ GET_SBIC_asr(regs, asr);
+ GET_SBIC_csr(regs, csr);
+
+ /*
+ * Clean up chip itself
+ */
+ if ( dev->sc_flags & SBICF_SELECTED ) {
+
+ while ( asr & SBIC_ASR_DBR ) {
+ /*
+ * sbic is jammed w/data. need to clear it
+ * But we don't know what direction it needs to go
+ */
+ GET_SBIC_data(regs, asr);
+ printf("%s: abort %s: clearing data buffer 0x%02x\n",
+ dev->sc_dev.dv_xname, where, asr);
+ GET_SBIC_asr(regs, asr);
+ if ( asr & SBIC_ASR_DBR ) /* Not the read direction, then */
+ SET_SBIC_data(regs, asr);
+ GET_SBIC_asr(regs, asr);
+ }
+
+ WAIT_CIP(regs);
+
+ printf("%s: sbicabort - sending ABORT command\n", dev->sc_dev.dv_xname);
+ SET_SBIC_cmd(regs, SBIC_CMD_ABORT);
+ WAIT_CIP(regs);
+
+ GET_SBIC_asr(regs, asr);
+
+ if ( asr & (SBIC_ASR_BSY|SBIC_ASR_LCI) ) {
+ /*
+ * ok, get more drastic..
+ */
+ printf("%s: sbicabort - asr %x, trying to reset\n",
+ dev->sc_dev.dv_xname, asr);
+ sbicreset(dev);
+ dev->sc_flags &= ~SBICF_SELECTED;
+ return SBIC_STATE_ERROR;
+ }
+
+ printf("%s: sbicabort - sending DISC command\n", dev->sc_dev.dv_xname);
+ SET_SBIC_cmd(regs, SBIC_CMD_DISC);
+
+ do {
+ SBIC_WAIT (regs, SBIC_ASR_INT, 0);
+ GET_SBIC_asr(regs, asr);
+ GET_SBIC_csr (regs, csr);
+ QPRINTF(("csr: 0x%02x, asr: 0x%02x\n", csr, asr));
+ } while ( (csr != SBIC_CSR_DISC) && (csr != SBIC_CSR_DISC_1) &&
+ (csr != SBIC_CSR_CMD_INVALID) );
+
+ /*
+ * lets just hope it worked..
+ */
+ dev->sc_flags &= ~SBICF_SELECTED;
+ }
+
+ return SBIC_STATE_ERROR;
+}
+
+
+/*
+ * Initialize driver-private structures
+ */
+void
+sbicinit(dev)
+ struct sbic_softc *dev;
+{
+ u_int i;
+
+ extern u_long scsi_nosync;
+ extern int shift_nosync;
+
+ if ( (dev->sc_flags & SBICF_ALIVE) == 0 ) {
+
+ struct sbic_acb *acb;
+
+ TAILQ_INIT(&dev->ready_list);
+ TAILQ_INIT(&dev->nexus_list);
+ TAILQ_INIT(&dev->free_list);
+
+ dev->sc_nexus = NULL;
+ dev->sc_xs = NULL;
+
+ acb = dev->sc_acb;
+ bzero(acb, sizeof(dev->sc_acb));
+
+ for (i = 0; i < sizeof(dev->sc_acb) / sizeof(*acb); i++) {
+ TAILQ_INSERT_TAIL(&dev->free_list, acb, chain);
+ acb++;
+ }
+
+ bzero(dev->sc_tinfo, sizeof(dev->sc_tinfo));
+
+#ifdef DEBUG
+ /*
+ * make sure timeout is really not needed
+ */
+ timeout((void *)sbictimeout, dev, 30 * hz);
+#endif
+
+ } else
+ panic("sbic: reinitializing driver!");
+
+ dev->sc_flags |= SBICF_ALIVE;
+ dev->sc_flags &= ~SBICF_SELECTED;
+
+ /*
+ * initialize inhibit array
+ */
+ if ( scsi_nosync ) {
+
+ u_int inhibit_sync = (scsi_nosync >> shift_nosync) & 0xff;
+
+ shift_nosync += 8;
+
+#ifdef DEBUG
+ if ( inhibit_sync )
+ printf("%s: Inhibiting synchronous transfer %02x\n",
+ dev->sc_dev.dv_xname, inhibit_sync);
+#endif
+ for (i = 0; i < 8; ++i) {
+ if ( inhibit_sync & (1 << i) )
+ sbic_inhibit_sync[i] = 1;
+ }
+ }
+
+ sbicreset(dev);
+}
+
+void
+sbicreset(dev)
+ struct sbic_softc *dev;
+{
+ sbic_regmap_p regs = dev->sc_sbicp;
+ u_int my_id,
+ s;
+ u_char csr;
+ u_char asr;
+
+ GET_SBIC_asr (regs, asr);
+ s = splbio();
+
+ my_id = dev->sc_link.adapter_target & SBIC_ID_MASK;
+
+ if (dev->sc_clkfreq < 110)
+ my_id |= SBIC_ID_FS_8_10;
+ else if (dev->sc_clkfreq < 160)
+ my_id |= SBIC_ID_FS_12_15;
+ else if (dev->sc_clkfreq < 210)
+ my_id |= SBIC_ID_FS_16_20;
+
+ SET_SBIC_myid(regs, my_id);
+
+ /*
+ * Reset the chip
+ */
+ SET_SBIC_cmd(regs, SBIC_CMD_RESET);
+ DELAY(25);
+
+ SBIC_WAIT(regs, SBIC_ASR_INT, 0);
+ GET_SBIC_csr(regs, csr); /* clears interrupt also */
+ GET_SBIC_asr (regs, asr);
+
+ /*
+ * Set up various chip parameters
+ */
+ SET_SBIC_control(regs,
+ SBIC_CTL_EDI | SBIC_CTL_IDI | SBIC_MACHINE_DMA_MODE);
+
+ /*
+ * don't allow Selection (SBIC_RID_ES)
+ * until we can handle target mode!!
+ */
+ SET_SBIC_rselid(regs, SBIC_RID_ER);
+
+ /*
+ * Asynchronous for now
+ */
+ SET_SBIC_syn(regs, 0);
+
+ /*
+ * Anything else was zeroed by reset
+ */
+ splx(s);
+
+ dev->sc_flags &= ~SBICF_SELECTED;
+ GET_SBIC_asr (regs, asr);
+}
+
+void
+sbicerror(dev, csr)
+ struct sbic_softc *dev;
+ u_char csr;
+{
+ struct scsi_xfer *xs = dev->sc_xs;
+
+#ifdef DIAGNOSTIC
+ if ( xs == NULL )
+ panic("sbicerror: dev->sc_xs == NULL");
+#endif
+
+ if ( xs->flags & SCSI_SILENT )
+ return;
+
+ printf("%s: csr == 0x%02x\n", dev->sc_dev.dv_xname, csr);
+}
+
+/*
+ * select the bus, return when selected or error.
+ *
+ * Returns the current CSR following selection and optionally MSG out phase.
+ * i.e. the returned CSR *should* indicate CMD phase...
+ * If the return value is 0, some error happened.
+ */
+u_char
+sbicselectbus(dev)
+ struct sbic_softc *dev;
+{
+ sbic_regmap_p regs = dev->sc_sbicp;
+ u_char target = dev->target,
+ lun = dev->lun,
+ asr,
+ csr,
+ id;
+
+ /*
+ * if we're already selected, return (XXXX panic maybe?)
+ */
+ if ( dev->sc_flags & SBICF_SELECTED )
+ return(0);
+
+ QPRINTF(("sbicselectbus %d: ", target));
+
+ /*
+ * issue select
+ */
+ SET_SBIC_selid(regs, target);
+ SET_SBIC_timeo(regs, SBIC_TIMEOUT(250, dev->sc_clkfreq));
+
+ GET_SBIC_asr(regs, asr);
+
+ if ( asr & (SBIC_ASR_INT|SBIC_ASR_BSY) ) {
+ /*
+ * This means we got ourselves reselected upon
+ */
+ QPRINTF(("WD busy (reselect?)\n"));
+ return 0;
+ }
+
+ SET_SBIC_cmd(regs, SBIC_CMD_SEL_ATN);
+
+ /*
+ * wait for select (merged from seperate function may need
+ * cleanup)
+ */
+ WAIT_CIP(regs);
+
+ do {
+
+ asr = SBIC_WAIT(regs, SBIC_ASR_INT | SBIC_ASR_LCI, 0);
+
+ if ( asr & SBIC_ASR_LCI ) {
+ QPRINTF(("late LCI: asr %02x\n", asr));
+ return 0;
+ }
+
+ /*
+ * Clear interrupt
+ */
+ GET_SBIC_csr (regs, csr);
+
+ QPRINTF(("%02x ", csr));
+
+ /*
+ * Reselected from under our feet?
+ */
+ if ( csr == SBIC_CSR_RSLT_NI || csr == SBIC_CSR_RSLT_IFY ) {
+ QPRINTF(("got reselected, asr %02x\n", asr));
+ /*
+ * We need to handle this now so we don't lock up later
+ */
+ sbicnextstate(dev, csr, asr);
+
+ return 0;
+ }
+
+ /*
+ * Whoops!
+ */
+ if ( csr == SBIC_CSR_SLT || csr == SBIC_CSR_SLT_ATN ) {
+ panic("sbicselectbus: target issued select!");
+ return 0;
+ }
+
+ } while (csr != (SBIC_CSR_MIS_2 | MESG_OUT_PHASE) &&
+ csr != (SBIC_CSR_MIS_2 | CMD_PHASE) &&
+ csr != SBIC_CSR_SEL_TIMEO);
+
+ /*
+ * Anyone at home?
+ */
+ if ( csr == SBIC_CSR_SEL_TIMEO ) {
+ dev->sc_xs->error = XS_SELTIMEOUT;
+ QPRINTF(("Selection Timeout\n"));
+ return 0;
+ }
+
+ QPRINTF(("Selection Complete\n"));
+
+ /*
+ * Assume we're now selected
+ */
+ GET_SBIC_selid(regs, id);
+ dev->target = id;
+ dev->lun = lun;
+ dev->sc_flags |= SBICF_SELECTED;
+
+ /*
+ * Enable (or not) reselection
+ * XXXSCW This is probably not necessary since we don't use use the
+ * Select-and-Xfer-with-ATN command to initiate a selection...
+ */
+ if ( !sbic_enable_reselect && dev->nexus_list.tqh_first == NULL)
+ SET_SBIC_rselid (regs, 0);
+ else
+ SET_SBIC_rselid (regs, SBIC_RID_ER);
+
+ /*
+ * We only really need to do anything when the target goes to MSG out
+ * If the device ignored ATN, it's probably old and brain-dead,
+ * but we'll try to support it anyhow.
+ * If it doesn't support message out, it definately doesn't
+ * support synchronous transfers, so no point in even asking...
+ */
+ if ( csr == (SBIC_CSR_MIS_2 | MESG_OUT_PHASE) ) {
+ /*
+ * Send identify message (SCSI-2 requires an identify msg)
+ */
+ if ( sbic_inhibit_sync[id] && dev->sc_sync[id].state == SYNC_START ) {
+ /*
+ * Handle drives that don't want to be asked
+ * whether to go sync at all.
+ */
+ dev->sc_sync[id].offset = 0;
+ dev->sc_sync[id].period = sbic_min_period;
+ dev->sc_sync[id].state = SYNC_DONE;
+ }
+
+ /*
+ * Do we need to negotiate Synchronous Xfers for this target?
+ */
+ if ( dev->sc_sync[id].state != SYNC_START ) {
+ /*
+ * Nope, we've already negotiated.
+ * Now see if we should allow the target to disconnect/reselect...
+ */
+ if ( dev->sc_xs->flags & SCSI_POLL || dev->sc_flags & SBICF_ICMD ||
+ !sbic_enable_reselect )
+ SEND_BYTE (regs, MSG_IDENTIFY | lun);
+ else
+ SEND_BYTE (regs, MSG_IDENTIFY_DR | lun);
+
+ } else {
+ /*
+ * try to initiate a sync transfer.
+ * So compose the sync message we're going
+ * to send to the target
+ */
+#ifdef DEBUG
+ if ( sync_debug )
+ printf("\nSending sync request to target %d ... ", id);
+#endif
+ /*
+ * setup scsi message sync message request
+ */
+ dev->sc_msg[0] = MSG_IDENTIFY | lun;
+ dev->sc_msg[1] = MSG_EXT_MESSAGE;
+ dev->sc_msg[2] = 3;
+ dev->sc_msg[3] = MSG_SYNC_REQ;
+ dev->sc_msg[4] = sbictoscsiperiod(dev, sbic_min_period);
+ dev->sc_msg[5] = sbic_max_offset;
+
+ sbicxfout(regs, 6, dev->sc_msg);
+
+ dev->sc_sync[id].state = SYNC_SENT;
+#ifdef DEBUG
+ if ( sync_debug )
+ printf ("sent\n");
+#endif
+ }
+
+ /*
+ * There's one interrupt still to come: the change to CMD phase...
+ */
+ SBIC_WAIT(regs, SBIC_ASR_INT , 0);
+ GET_SBIC_csr(regs, csr);
+ }
+
+ /*
+ * set sync or async
+ */
+ if ( dev->sc_sync[target].state == SYNC_DONE ) {
+#ifdef DEBUG
+ if ( sync_debug )
+ printf("select(%d): sync reg = 0x%02x\n", target,
+ SBIC_SYN(dev->sc_sync[target].offset,
+ dev->sc_sync[target].period));
+#endif
+ SET_SBIC_syn(regs, SBIC_SYN(dev->sc_sync[target].offset,
+ dev->sc_sync[target].period));
+ } else {
+#ifdef DEBUG
+ if ( sync_debug )
+ printf("select(%d): sync reg = 0x%02x\n", target,
+ SBIC_SYN(0,sbic_min_period));
+#endif
+ SET_SBIC_syn(regs, SBIC_SYN(0, sbic_min_period));
+ }
+
+ return csr;
+}
+
+/*
+ * Information Transfer *to* a Scsi Target.
+ *
+ * Note: Don't expect there to be an interrupt immediately after all
+ * the data is transferred out. The WD spec sheet says that the Transfer-
+ * Info command for non-MSG_IN phases only completes when the target
+ * next asserts 'REQ'. That is, when the SCSI bus changes to a new state.
+ *
+ * This can have a nasty effect on commands which take a relatively long
+ * time to complete, for example a START/STOP unit command may remain in
+ * CMD phase until the disk has spun up. Only then will the target change
+ * to STATUS phase. This is really only a problem for immediate commands
+ * since we don't allow disconnection for them (yet).
+ */
+int
+sbicxfout(regs, len, bp)
+ sbic_regmap_p regs;
+ int len;
+ void *bp;
+{
+ int wait = sbic_data_wait;
+ u_char asr,
+ *buf = bp;
+
+ QPRINTF(("sbicxfout {%d} %02x %02x %02x %02x %02x "
+ "%02x %02x %02x %02x %02x\n", len, buf[0], buf[1], buf[2],
+ buf[3], buf[4], buf[5], buf[6], buf[7], buf[8], buf[9]));
+
+ /*
+ * sigh.. WD-PROTO strikes again.. sending the command in one go
+ * causes the chip to lock up if talking to certain (misbehaving?)
+ * targets. Anyway, this procedure should work for all targets, but
+ * it's slightly slower due to the overhead
+ */
+ WAIT_CIP (regs);
+
+ SBIC_TC_PUT (regs, 0);
+ SET_SBIC_control(regs, SBIC_CTL_EDI | SBIC_CTL_IDI);
+ SBIC_TC_PUT (regs, (unsigned)len);
+ SET_SBIC_cmd (regs, SBIC_CMD_XFER_INFO);
+
+ /*
+ * Loop for each byte transferred
+ */
+ do {
+
+ GET_SBIC_asr (regs, asr);
+
+ if ( asr & SBIC_ASR_DBR ) {
+ if ( len ) {
+ SET_SBIC_data (regs, *buf);
+ buf++;
+ len--;
+ } else {
+ SET_SBIC_data (regs, 0);
+ }
+ wait = sbic_data_wait;
+ }
+
+ } while ( len && (asr & SBIC_ASR_INT) == 0 && wait-- > 0 );
+
+#ifdef DEBUG
+ QPRINTF(("sbicxfout done: %d bytes remaining (wait:%d)\n", len, wait));
+#endif
+
+ /*
+ * Normally, an interrupt will be pending when this routing returns.
+ */
+ return(len);
+}
+
+/*
+ * Information Transfer *from* a Scsi Target
+ * returns # bytes left to read
+ */
+int
+sbicxfin(regs, len, bp)
+ sbic_regmap_p regs;
+ int len;
+ void *bp;
+{
+ int wait = sbic_data_wait;
+ u_char *buf = bp;
+ u_char asr;
+#ifdef DEBUG
+ u_char *obp = bp;
+#endif
+
+ WAIT_CIP (regs);
+
+ SET_SBIC_control(regs, SBIC_CTL_EDI | SBIC_CTL_IDI);
+ SBIC_TC_PUT (regs, (unsigned)len);
+ SET_SBIC_cmd (regs, SBIC_CMD_XFER_INFO);
+
+ /*
+ * Loop for each byte transferred
+ */
+ do {
+
+ GET_SBIC_asr (regs, asr);
+
+ if ( asr & SBIC_ASR_DBR ) {
+ if ( len ) {
+ GET_SBIC_data (regs, *buf);
+ buf++;
+ len--;
+ } else {
+ u_char foo;
+ GET_SBIC_data (regs, foo);
+ }
+ wait = sbic_data_wait;
+ }
+
+ } while ( (asr & SBIC_ASR_INT) == 0 && wait-- > 0 );
+
+ QPRINTF(("sbicxfin {%d} %02x %02x %02x %02x %02x %02x "
+ "%02x %02x %02x %02x\n", len, obp[0], obp[1], obp[2],
+ obp[3], obp[4], obp[5], obp[6], obp[7], obp[8], obp[9]));
+
+ SBIC_TC_PUT (regs, 0);
+
+ /*
+ * this leaves with one csr to be read
+ */
+ return len;
+}
+
+/*
+ * SCSI 'immediate' command: issue a command to some SCSI device
+ * and get back an 'immediate' response (i.e., do programmed xfer
+ * to get the response data). 'cbuf' is a buffer containing a scsi
+ * command of length clen bytes. 'buf' is a buffer of length 'len'
+ * bytes for data. The transfer direction is determined by the device
+ * (i.e., by the scsi bus data xfer phase). If 'len' is zero, the
+ * command must supply no data.
+ *
+ * Note that although this routine looks like it can handle disconnect/
+ * reselect, the fact is that it can't. There is still some work to be
+ * done to clean this lot up.
+ */
+int
+sbicicmd(dev, cbuf, clen, buf, len)
+ struct sbic_softc *dev;
+ void *cbuf,
+ *buf;
+ int clen,
+ len;
+{
+ sbic_regmap_p regs = dev->sc_sbicp;
+ struct sbic_acb *acb = dev->sc_nexus;
+ u_char csr,
+ asr;
+ int still_busy = SBIC_STATE_RUNNING;
+#ifdef DEBUG
+ int counter = 0;
+#endif
+
+ /*
+ * Make sure pointers are OK
+ */
+ dev->sc_last = dev->sc_cur = &acb->sc_pa;
+ dev->sc_tcnt = acb->sc_tcnt = 0;
+
+ acb->sc_dmacmd = 0;
+ acb->sc_pa.dc_count = 0; /* No DMA */
+ acb->sc_kv.dc_addr = buf;
+ acb->sc_kv.dc_count = len;
+
+#ifdef DEBUG
+ if ( data_pointer_debug > 1 )
+ printf("sbicicmd(%d,%d):%d\n", dev->target, dev->lun, acb->sc_kv.dc_count);
+#endif
+
+ /*
+ * set the sbic into non-DMA mode
+ */
+ SET_SBIC_control(regs, SBIC_CTL_EDI | SBIC_CTL_IDI);
+
+ dev->sc_stat[0] = 0xff;
+ dev->sc_msg[0] = 0xff;
+
+ /*
+ * We're stealing the SCSI bus
+ */
+ dev->sc_flags |= SBICF_ICMD;
+
+ do {
+ GET_SBIC_asr (regs, asr);
+
+ /*
+ * select the SCSI bus (it's an error if bus isn't free)
+ */
+ if ( (dev->sc_flags & SBICF_SELECTED) == 0 &&
+ still_busy != SBIC_STATE_DISCONNECT ) {
+ if ( (csr = sbicselectbus(dev)) == 0 ) {
+ dev->sc_flags &= ~SBICF_ICMD;
+ return(-1);
+ }
+ } else
+ if ( (asr & (SBIC_ASR_BSY | SBIC_ASR_INT)) == SBIC_ASR_INT )
+ GET_SBIC_csr(regs, csr);
+ else
+ csr = 0;
+
+ if ( csr ) {
+
+ QPRINTF((">ASR:0x%02x CSR:0x%02x< ", asr, csr));
+
+ switch ( csr ) {
+
+ case SBIC_CSR_S_XFERRED:
+ case SBIC_CSR_DISC:
+ case SBIC_CSR_DISC_1:
+ {
+ u_char phase;
+
+ dev->sc_flags &= ~SBICF_SELECTED;
+ GET_SBIC_cmd_phase (regs, phase);
+
+ if ( phase == 0x60 ) {
+ GET_SBIC_tlun (regs, dev->sc_stat[0]);
+ still_busy = SBIC_STATE_DONE; /* done */
+ } else {
+#ifdef DEBUG
+ if ( reselect_debug > 1 )
+ printf("sbicicmd: handling disconnect\n");
+#endif
+ still_busy = SBIC_STATE_DISCONNECT;
+ }
+ }
+ break;
+
+ case SBIC_CSR_XFERRED | CMD_PHASE:
+ case SBIC_CSR_MIS | CMD_PHASE:
+ case SBIC_CSR_MIS_1 | CMD_PHASE:
+ case SBIC_CSR_MIS_2 | CMD_PHASE:
+ {
+ if ( sbicxfout(regs, clen, cbuf) )
+ still_busy = sbicabort(dev, "icmd sending cmd");
+ }
+ break;
+
+ case SBIC_CSR_XFERRED | STATUS_PHASE:
+ case SBIC_CSR_MIS | STATUS_PHASE:
+ case SBIC_CSR_MIS_1 | STATUS_PHASE:
+ case SBIC_CSR_MIS_2 | STATUS_PHASE:
+ {
+ /*
+ * The sbic does the status/cmd-complete reading ok,
+ * so do this with its hi-level commands.
+ */
+#ifdef DEBUG
+ if ( sbic_debug )
+ printf("SBICICMD status phase (bsy=%d)\n", still_busy);
+#endif
+ SET_SBIC_cmd_phase(regs, 0x46);
+ SET_SBIC_cmd(regs, SBIC_CMD_SEL_ATN_XFER);
+ }
+ break;
+
+ default:
+ {
+ still_busy = sbicnextstate(dev, csr, asr);
+ }
+ break;
+ }
+
+ /*
+ * make sure the last command was taken,
+ * ie. we're not hunting after an ignored command..
+ */
+ GET_SBIC_asr(regs, asr);
+
+ /*
+ * tapes may take a loooong time..
+ */
+ while (asr & SBIC_ASR_BSY ) {
+
+ if ( asr & SBIC_ASR_DBR ) {
+ int i;
+
+ printf("sbicicmd: Waiting while sbic is jammed, CSR:%02x,ASR:%02x\n", csr,asr);
+#ifdef DDB
+ Debugger();
+#endif
+ /*
+ * SBIC is jammed
+ * DUNNO which direction
+ * Try old direction
+ */
+ GET_SBIC_data(regs, i);
+ GET_SBIC_asr(regs, asr);
+
+ if ( asr & SBIC_ASR_DBR ) /* Wants us to write */
+ SET_SBIC_data(regs, i);
+ }
+
+ GET_SBIC_asr(regs, asr);
+ }
+ }
+
+ /*
+ * wait for last command to complete
+ */
+ if ( asr & SBIC_ASR_LCI ) {
+ printf("sbicicmd: last command ignored\n");
+ }
+ else
+ if ( still_busy >= SBIC_STATE_RUNNING ) /* Bsy */
+ SBIC_WAIT (regs, SBIC_ASR_INT, sbic_cmd_wait);
+
+ /*
+ * do it again
+ */
+ } while ( still_busy >= SBIC_STATE_RUNNING && dev->sc_stat[0] == 0xff );
+
+ /*
+ * Sometimes we need to do an extra read of the CSR
+ */
+ GET_SBIC_csr(regs, csr);
+
+#ifdef DEBUG
+ if ( data_pointer_debug > 1 )
+ printf("sbicicmd done(%d,%d):%d =%d=\n", dev->target, dev->lun,
+ acb->sc_kv.dc_count,
+ dev->sc_stat[0]);
+#endif
+
+ dev->sc_flags &= ~SBICF_ICMD;
+
+ return(dev->sc_stat[0]);
+}
+
+/*
+ * Finish SCSI xfer command: After the completion interrupt from
+ * a read/write operation, sequence through the final phases in
+ * programmed i/o. This routine is a lot like sbicicmd except we
+ * skip (and don't allow) the select, cmd out and data in/out phases.
+ */
+void
+sbicxfdone(dev)
+ struct sbic_softc *dev;
+{
+ sbic_regmap_p regs = dev->sc_sbicp;
+ u_char phase,
+ csr;
+ int s;
+
+ QPRINTF(("{"));
+ s = splbio();
+
+ /*
+ * have the sbic complete on its own
+ */
+ SBIC_TC_PUT(regs, 0);
+ SET_SBIC_cmd_phase(regs, 0x46);
+ SET_SBIC_cmd(regs, SBIC_CMD_SEL_ATN_XFER);
+
+ do {
+
+ SBIC_WAIT (regs, SBIC_ASR_INT, 0);
+ GET_SBIC_csr (regs, csr);
+ QPRINTF(("%02x:", csr));
+
+ } while ( (csr != SBIC_CSR_DISC) && (csr != SBIC_CSR_DISC_1) &&
+ (csr != SBIC_CSR_S_XFERRED));
+
+ dev->sc_flags &= ~SBICF_SELECTED;
+
+ GET_SBIC_cmd_phase (regs, phase);
+ QPRINTF(("}%02x", phase));
+
+ if ( phase == 0x60 )
+ GET_SBIC_tlun(regs, dev->sc_stat[0]);
+ else
+ sbicerror(dev, csr);
+
+ QPRINTF(("=STS:%02x=\n", dev->sc_stat[0]));
+
+ splx(s);
+}
+
+/*
+ * No DMA chains
+ */
+int
+sbicgo(dev, xs)
+ struct sbic_softc *dev;
+ struct scsi_xfer *xs;
+{
+ struct sbic_acb *acb = dev->sc_nexus;
+ sbic_regmap_p regs = dev->sc_sbicp;
+ int i,
+ dmaflags,
+ count,
+ usedma;
+ u_char csr,
+ asr,
+ *addr;
+
+ dev->target = xs->sc_link->target;
+ dev->lun = xs->sc_link->lun;
+
+ usedma = sbicdmaok(dev, xs);
+
+#ifdef DEBUG
+ if ( data_pointer_debug > 1 )
+ printf("sbicgo(%d,%d): usedma=%d\n", dev->target, dev->lun, usedma);
+#endif
+
+ /*
+ * select the SCSI bus (it's an error if bus isn't free)
+ */
+ if ( (csr = sbicselectbus(dev)) == 0 )
+ return(0); /* Not done: needs to be rescheduled */
+
+ dev->sc_stat[0] = 0xff;
+
+ /*
+ * Calculate DMA chains now
+ */
+ if ( acb->flags & ACB_DATAIN )
+ dmaflags = DMAGO_READ;
+ else
+ dmaflags = 0;
+
+ addr = acb->sc_kv.dc_addr;
+ count = acb->sc_kv.dc_count;
+
+ if ( count && ((char *)kvtop(addr) != acb->sc_pa.dc_addr) ) {
+ printf("sbic: DMA buffer mapping changed %x->%x\n",
+ acb->sc_pa.dc_addr, kvtop(addr));
+#ifdef DDB
+ Debugger();
+#endif
+ }
+
+#ifdef DEBUG
+ ++sbicdma_ops; /* count total DMA operations */
+#endif
+
+ /*
+ * Allocate the DMA chain
+ * Mark end of segment...
+ */
+ acb->sc_tcnt = dev->sc_tcnt = 0;
+ acb->sc_pa.dc_count = 0;
+
+ sbic_load_ptrs(dev);
+
+ /*
+ * Enable interrupts but don't do any DMA
+ * enintr() also enables interrupts for the sbic
+ */
+ dev->sc_enintr(dev);
+
+ if ( usedma ) {
+ dev->sc_tcnt = dev->sc_dmago(dev, acb->sc_pa.dc_addr,
+ acb->sc_pa.dc_count, dmaflags);
+#ifdef DEBUG
+ dev->sc_dmatimo = dev->sc_tcnt ? 1 : 0;
+#endif
+ } else
+ dev->sc_dmacmd = 0; /* Don't use DMA */
+
+ acb->sc_dmacmd = dev->sc_dmacmd;
+
+#ifdef DEBUG
+ if ( data_pointer_debug > 1 ) {
+ printf("sbicgo dmago:%d(%x:%x) dmacmd=0x%02x\n", dev->target,
+ dev->sc_cur->dc_addr,
+ dev->sc_tcnt,
+ dev->sc_dmacmd);
+ }
+#endif
+
+ /*
+ * Lets cycle a while then let the interrupt handler take over.
+ */
+ GET_SBIC_asr(regs, asr);
+
+ do {
+
+ QPRINTF(("go "));
+
+ /*
+ * Handle the new phase
+ */
+ i = sbicnextstate(dev, csr, asr);
+#if 0
+ WAIT_CIP(regs);
+#endif
+ if ( i == SBIC_STATE_RUNNING ) {
+ GET_SBIC_asr(regs, asr);
+
+ if ( asr & SBIC_ASR_LCI )
+ printf("sbicgo: LCI asr:%02x csr:%02x\n", asr, csr);
+
+ if ( asr & SBIC_ASR_INT )
+ GET_SBIC_csr(regs, csr);
+ }
+
+ } while ( i == SBIC_STATE_RUNNING && asr & (SBIC_ASR_INT|SBIC_ASR_LCI) );
+
+ if ( i == SBIC_STATE_DONE ) {
+ if ( dev->sc_stat[0] == 0xff )
+#if 0
+ printf("sbicgo: done & stat = 0xff\n");
+#else
+ ;
+#endif
+ else
+ return 1; /* Did we really finish that fast? */
+ }
+
+ return 0;
+}
+
+
+int
+sbicintr(dev)
+ struct sbic_softc *dev;
+{
+ sbic_regmap_p regs = dev->sc_sbicp;
+ u_char asr,
+ csr;
+ int i;
+
+ /*
+ * pending interrupt?
+ */
+ GET_SBIC_asr (regs, asr);
+ if ( (asr & SBIC_ASR_INT) == 0 )
+ return(0);
+
+ GET_SBIC_csr(regs, csr);
+
+ do {
+
+ QPRINTF(("intr[0x%x]", csr));
+
+ i = sbicnextstate(dev, csr, asr);
+#if 0
+ WAIT_CIP(regs);
+#endif
+ if ( i == SBIC_STATE_RUNNING ) {
+ GET_SBIC_asr(regs, asr);
+
+ if ( asr & SBIC_ASR_LCI )
+ printf("sbicgo: LCI asr:%02x csr:%02x\n", asr, csr);
+
+ if ( asr & SBIC_ASR_INT )
+ GET_SBIC_csr(regs, csr);
+ }
+
+ } while ( i == SBIC_STATE_RUNNING && asr & (SBIC_ASR_INT|SBIC_ASR_LCI) );
+
+ QPRINTF(("intr done. state=%d, asr=0x%02x\n", i, asr));
+
+ return(1);
+}
+
+/*
+ * Run commands and wait for disconnect.
+ * This is only ever called when a command is in progress, when we
+ * want to busy wait for it to finish.
+ */
+int
+sbicpoll(dev)
+ struct sbic_softc *dev;
+{
+ sbic_regmap_p regs = dev->sc_sbicp;
+ u_char asr;
+ u_char csr;
+ int i;
+
+ /*
+ * Wait for the next interrupt
+ */
+ SBIC_WAIT(regs, SBIC_ASR_INT, sbic_cmd_wait);
+
+ /* Just to avoid GCC warnings. */
+ csr = 0;
+
+ do {
+ GET_SBIC_asr (regs, asr);
+
+ if (asr & SBIC_ASR_INT)
+ GET_SBIC_csr(regs, csr);
+
+ QPRINTF(("poll[0x%x]", csr));
+
+ /*
+ * Handle it
+ */
+ i = sbicnextstate(dev, csr, asr);
+
+ WAIT_CIP(regs);
+ GET_SBIC_asr(regs, asr);
+
+ /*
+ * tapes may take a loooong time..
+ */
+ while ( asr & SBIC_ASR_BSY ) {
+ u_char z = 0;
+
+ if ( asr & SBIC_ASR_DBR ) {
+ printf("sbipoll: Waiting while sbic is jammed, CSR:%02x,ASR:%02x\n", csr,asr);
+#ifdef DDB
+ Debugger();
+#endif
+ /*
+ * SBIC is jammed
+ * DUNNO which direction
+ * Try old direction
+ */
+ GET_SBIC_data(regs, z);
+ GET_SBIC_asr(regs, asr);
+
+ if ( asr & SBIC_ASR_DBR ) /* Wants us to write */
+ SET_SBIC_data(regs, z);
+ }
+
+ GET_SBIC_asr(regs, asr);
+ }
+
+ if ( asr & SBIC_ASR_LCI )
+ printf("sbicpoll: LCI asr:%02x csr:%02x\n", asr,csr);
+ else
+ if ( i == SBIC_STATE_RUNNING ) /* BSY */
+ SBIC_WAIT(regs, SBIC_ASR_INT, sbic_cmd_wait);
+
+ } while ( i == SBIC_STATE_RUNNING );
+
+ return(1);
+}
+
+/*
+ * Handle a single msgin
+ */
+int
+sbicmsgin(dev)
+ struct sbic_softc *dev;
+{
+ sbic_regmap_p regs = dev->sc_sbicp;
+ int recvlen = 1;
+ u_char asr,
+ csr,
+ *tmpaddr,
+ *msgaddr;
+
+ tmpaddr = msgaddr = dev->sc_msg;
+
+ tmpaddr[0] = 0xff;
+ tmpaddr[1] = 0xff;
+
+ GET_SBIC_asr(regs, asr);
+
+#ifdef DEBUG
+ if ( reselect_debug > 1 )
+ printf("sbicmsgin asr=%02x\n", asr);
+#endif
+
+ GET_SBIC_selid (regs, csr);
+ SET_SBIC_selid (regs, csr | SBIC_SID_FROM_SCSI);
+
+ SBIC_TC_PUT(regs, 0);
+ SET_SBIC_control(regs, SBIC_CTL_EDI | SBIC_CTL_IDI);
+
+ do {
+ while( recvlen-- ) {
+
+ /*
+ * Fetch the next byte of the message
+ */
+ RECV_BYTE(regs, *tmpaddr);
+
+ /*
+ * get the command completion interrupt, or we
+ * can't send a new command (LCI)
+ */
+ SBIC_WAIT(regs, SBIC_ASR_INT, 0);
+ GET_SBIC_csr(regs, csr);
+
+#ifdef DEBUG
+ if ( reselect_debug > 1 )
+ printf("sbicmsgin: got %02x csr %02x\n", *tmpaddr, csr);
+#endif
+
+ tmpaddr++;
+
+ if ( recvlen ) {
+ /*
+ * Clear ACK, and wait for the interrupt for the next byte
+ */
+ SET_SBIC_cmd(regs, SBIC_CMD_CLR_ACK);
+ SBIC_WAIT(regs, SBIC_ASR_INT, 0);
+ GET_SBIC_csr(regs, csr);
+ }
+ }
+
+ if ( msgaddr[0] == 0xff ) {
+ printf("sbicmsgin: sbic swallowed our message\n");
+ break;
+ }
+
+#ifdef DEBUG
+ if ( sync_debug ) {
+ GET_SBIC_asr(regs, asr);
+ printf("msgin done csr 0x%x asr 0x%x msg 0x%x\n", csr, asr, msgaddr[0]);
+ }
+#endif
+ /*
+ * test whether this is a reply to our sync
+ * request
+ */
+ if ( MSG_ISIDENTIFY(msgaddr[0]) ) {
+
+ /*
+ * Got IFFY msg -- ack it
+ */
+ QPRINTF(("IFFY"));
+
+ } else
+ if ( msgaddr[0] == MSG_REJECT &&
+ dev->sc_sync[dev->target].state == SYNC_SENT) {
+
+ /*
+ * Target probably rejected our Sync negotiation.
+ */
+ QPRINTF(("REJECT of SYN"));
+
+#ifdef DEBUG
+ if ( sync_debug )
+ printf("target %d rejected sync, going async\n", dev->target);
+#endif
+
+ dev->sc_sync[dev->target].period = sbic_min_period;
+ dev->sc_sync[dev->target].offset = 0;
+ dev->sc_sync[dev->target].state = SYNC_DONE;
+ SET_SBIC_syn(regs, SBIC_SYN(dev->sc_sync[dev->target].offset,
+ dev->sc_sync[dev->target].period));
+
+ } else
+ if ( msgaddr[0] == MSG_REJECT ) {
+
+ /*
+ * we'll never REJECt a REJECT message..
+ */
+ QPRINTF(("REJECT"));
+
+ } else
+ if ( msgaddr[0] == MSG_SAVE_DATA_PTR ) {
+
+ /*
+ * don't reject this either.
+ */
+ QPRINTF(("MSG_SAVE_DATA_PTR"));
+
+ } else
+ if ( msgaddr[0] == MSG_RESTORE_PTR ) {
+
+ /*
+ * don't reject this either.
+ */
+ QPRINTF(("MSG_RESTORE_PTR"));
+
+ } else
+ if ( msgaddr[0] == MSG_DISCONNECT ) {
+
+ /*
+ * Target is disconnecting...
+ */
+ QPRINTF(("DISCONNECT"));
+
+#ifdef DEBUG
+ if ( reselect_debug > 1 && msgaddr[0] == MSG_DISCONNECT )
+ printf("sbicmsgin: got disconnect msg %s\n",
+ (dev->sc_flags & SBICF_ICMD) ? "rejecting" : "");
+#endif
+
+ if ( dev->sc_flags & SBICF_ICMD ) {
+ /*
+ * We're in immediate mode. Prevent disconnects.
+ * prepare to reject the message, NACK
+ */
+ SET_SBIC_cmd(regs, SBIC_CMD_SET_ATN);
+ WAIT_CIP(regs);
+ }
+
+ } else
+ if ( msgaddr[0] == MSG_CMD_COMPLETE ) {
+
+ /*
+ * !! KLUDGE ALERT !! quite a few drives don't seem to
+ * really like the current way of sending the
+ * sync-handshake together with the ident-message, and
+ * they react by sending command-complete and
+ * disconnecting right after returning the valid sync
+ * handshake. So, all I can do is reselect the drive,
+ * and hope it won't disconnect again. I don't think
+ * this is valid behavior, but I can't help fixing a
+ * problem that apparently exists.
+ *
+ * Note: we should not get here on `normal' command
+ * completion, as that condition is handled by the
+ * high-level sel&xfer resume command used to walk
+ * thru status/cc-phase.
+ */
+ QPRINTF(("CMD_COMPLETE"));
+
+#ifdef DEBUG
+ if ( sync_debug )
+ printf ("GOT MSG %d! target %d acting weird.."
+ " waiting for disconnect...\n", msgaddr[0], dev->target);
+#endif
+
+ /*
+ * Check to see if sbic is handling this
+ */
+ GET_SBIC_asr(regs, asr);
+
+ /*
+ * XXXSCW: I'm not convinced of this, we haven't negated ACK yet...
+ */
+ if ( asr & SBIC_ASR_BSY )
+ return SBIC_STATE_RUNNING;
+
+ /*
+ * Let's try this: Assume it works and set status to 00
+ */
+ dev->sc_stat[0] = 0;
+
+ } else
+ if ( msgaddr[0] == MSG_EXT_MESSAGE && tmpaddr == &(msgaddr[1]) ) {
+
+ /*
+ * Target is sending us an extended message. We'll assume it's
+ * the response to our Sync. negotiation.
+ */
+ QPRINTF(("ExtMSG\n"));
+
+ /*
+ * Read in whole extended message. First, negate ACK to accept
+ * the MSG_EXT_MESSAGE byte...
+ */
+ SET_SBIC_cmd(regs, SBIC_CMD_CLR_ACK);
+
+ /*
+ * Wait for the interrupt for the next byte (length)
+ */
+ SBIC_WAIT(regs, SBIC_ASR_INT, 0);
+ GET_SBIC_csr(regs, csr);
+
+#ifdef DEBUG
+ QPRINTF(("CLR ACK csr %02x\n", csr));
+#endif
+
+ /*
+ * Read the length byte
+ */
+ RECV_BYTE(regs, *tmpaddr);
+
+ /*
+ * Wait for command completion IRQ
+ */
+ SBIC_WAIT(regs, SBIC_ASR_INT, 0);
+ GET_SBIC_csr(regs, csr);
+
+ /*
+ * Reload the loop counter
+ */
+ recvlen = *tmpaddr++;
+
+ QPRINTF(("Recving ext msg, csr %02x len %02x\n", csr, recvlen));
+
+ } else
+ if ( msgaddr[0] == MSG_EXT_MESSAGE && msgaddr[1] == 3 &&
+ msgaddr[2] == MSG_SYNC_REQ ) {
+
+ /*
+ * We've received the complete Extended Message Sync. Request...
+ */
+ QPRINTF(("SYN"));
+
+ /*
+ * Compute the required Transfer Period for the WD chip...
+ */
+ dev->sc_sync[dev->target].period = sbicfromscsiperiod(dev, msgaddr[3]);
+ dev->sc_sync[dev->target].offset = msgaddr[4];
+ dev->sc_sync[dev->target].state = SYNC_DONE;
+
+ /*
+ * Put the WD chip in synchronous mode
+ */
+ SET_SBIC_syn(regs, SBIC_SYN(dev->sc_sync[dev->target].offset,
+ dev->sc_sync[dev->target].period));
+#ifdef DEBUG
+ if ( sync_debug )
+ printf("msgin(%d): sync reg = 0x%02x\n", dev->target,
+ SBIC_SYN(dev->sc_sync[dev->target].offset,
+ dev->sc_sync[dev->target].period));
+#endif
+
+ printf("%s: target %d now synchronous, period=%dns, offset=%d.\n",
+ dev->sc_dev.dv_xname, dev->target,
+ msgaddr[3] * 4, msgaddr[4]);
+
+ } else {
+
+ /*
+ * We don't support whatever this message is...
+ */
+#ifdef DEBUG
+ if ( sbic_debug || sync_debug )
+ printf ("sbicmsgin: Rejecting message 0x%02x\n", msgaddr[0]);
+#endif
+
+ /*
+ * prepare to reject the message, NACK
+ */
+ SET_SBIC_cmd(regs, SBIC_CMD_SET_ATN);
+ WAIT_CIP(regs);
+ }
+
+ /*
+ * Negate ACK to complete the transfer
+ */
+ SET_SBIC_cmd(regs, SBIC_CMD_CLR_ACK);
+
+ /*
+ * Wait for the interrupt for the next byte, or phase change.
+ * Only read the CSR if we have more data to transfer.
+ * XXXSCW: We should really verify that we're still in MSG IN phase
+ * before blindly going back around this loop, but that would mean
+ * we read the CSR... <sigh>
+ */
+ SBIC_WAIT(regs, SBIC_ASR_INT, 0);
+ if ( recvlen > 0 )
+ GET_SBIC_csr(regs, csr);
+
+ } while ( recvlen > 0 );
+
+ /*
+ * Should still have one CSR to read
+ */
+ return SBIC_STATE_RUNNING;
+}
+
+
+/*
+ * sbicnextstate()
+ * return:
+ * SBIC_STATE_DONE == done
+ * SBIC_STATE_RUNNING == working
+ * SBIC_STATE_DISCONNECT == disconnected
+ * SBIC_STATE_ERROR == error
+ */
+int
+sbicnextstate(dev, csr, asr)
+ struct sbic_softc *dev;
+ u_char csr,
+ asr;
+{
+ sbic_regmap_p regs = dev->sc_sbicp;
+ struct sbic_acb *acb = dev->sc_nexus;
+
+ QPRINTF(("next[%02x,%02x]: ",asr,csr));
+
+ switch (csr) {
+
+ case SBIC_CSR_XFERRED | CMD_PHASE:
+ case SBIC_CSR_MIS | CMD_PHASE:
+ case SBIC_CSR_MIS_1 | CMD_PHASE:
+ case SBIC_CSR_MIS_2 | CMD_PHASE:
+ {
+ if ( sbicxfout(regs, acb->clen, &acb->cmd) )
+ goto abort;
+ }
+ break;
+
+ case SBIC_CSR_XFERRED | STATUS_PHASE:
+ case SBIC_CSR_MIS | STATUS_PHASE:
+ case SBIC_CSR_MIS_1 | STATUS_PHASE:
+ case SBIC_CSR_MIS_2 | STATUS_PHASE:
+ {
+ SET_SBIC_control(regs, SBIC_CTL_EDI | SBIC_CTL_IDI);
+
+ /*
+ * this should be the normal i/o completion case.
+ * get the status & cmd complete msg then let the
+ * device driver look at what happened.
+ */
+ sbicxfdone(dev);
+
+#ifdef DEBUG
+ dev->sc_dmatimo = 0;
+ if ( data_pointer_debug > 1 )
+ printf("next dmastop: %d(%x:%x)\n", dev->target,
+ dev->sc_cur->dc_addr,
+ dev->sc_tcnt);
+#endif
+ /*
+ * Stop the DMA chip
+ */
+ dev->sc_dmastop(dev);
+
+ dev->sc_flags &= ~(SBICF_INDMA | SBICF_DCFLUSH);
+
+ /*
+ * Indicate to the upper layers that the command is done
+ */
+ sbic_scsidone(acb, dev->sc_stat[0]);
+
+ return SBIC_STATE_DONE;
+ }
+
+ case SBIC_CSR_XFERRED | DATA_OUT_PHASE:
+ case SBIC_CSR_XFERRED | DATA_IN_PHASE:
+ case SBIC_CSR_MIS | DATA_OUT_PHASE:
+ case SBIC_CSR_MIS | DATA_IN_PHASE:
+ case SBIC_CSR_MIS_1 | DATA_OUT_PHASE:
+ case SBIC_CSR_MIS_1 | DATA_IN_PHASE:
+ case SBIC_CSR_MIS_2 | DATA_OUT_PHASE:
+ case SBIC_CSR_MIS_2 | DATA_IN_PHASE:
+ {
+ /*
+ * Verify that we expected to transfer data...
+ */
+ if ( acb->sc_kv.dc_count <= 0 ) {
+ printf("next: DATA phase with xfer count == %d, asr:0x%02x csr:0x%02x\n",
+ acb->sc_kv.dc_count, asr, csr);
+ goto abort;
+ }
+
+ /*
+ * Should we transfer using PIO or DMA ?
+ */
+ if ( dev->sc_xs->flags & SCSI_POLL || dev->sc_flags & SBICF_ICMD ||
+ acb->sc_dmacmd == 0 ) {
+
+ /*
+ * Do PIO transfer
+ */
+ int i;
+
+#ifdef DEBUG
+ if ( data_pointer_debug > 1 )
+ printf("next PIO: %d(%x:%x)\n", dev->target,
+ acb->sc_kv.dc_addr,
+ acb->sc_kv.dc_count);
+#endif
+
+ if ( SBIC_PHASE(csr) == DATA_IN_PHASE )
+ /*
+ * data in
+ */
+ i = sbicxfin(regs, acb->sc_kv.dc_count,
+ acb->sc_kv.dc_addr);
+ else
+ /*
+ * data out
+ */
+ i = sbicxfout(regs, acb->sc_kv.dc_count,
+ acb->sc_kv.dc_addr);
+
+ acb->sc_kv.dc_addr += (acb->sc_kv.dc_count - i);
+ acb->sc_kv.dc_count = i;
+
+ /*
+ * Update current count...
+ */
+ acb->sc_tcnt = dev->sc_tcnt = i;
+
+ dev->sc_flags &= ~SBICF_INDMA;
+
+ } else {
+
+ /*
+ * Do DMA transfer
+ * set next dma addr and dec count
+ */
+ sbic_save_ptrs(dev);
+ sbic_load_ptrs(dev);
+
+ SET_SBIC_control(regs, SBIC_CTL_EDI | SBIC_CTL_IDI |
+ SBIC_MACHINE_DMA_MODE);
+
+#ifdef DEBUG
+ dev->sc_dmatimo = 1;
+ if ( data_pointer_debug > 1 )
+ printf("next DMA: %d(%x:%x)\n", dev->target,
+ dev->sc_cur->dc_addr,
+ dev->sc_tcnt);
+#endif
+ /*
+ * Start the DMA chip going
+ */
+ dev->sc_tcnt = dev->sc_dmanext(dev);
+
+ /*
+ * Tell the WD chip how much to transfer this time around
+ */
+ SBIC_TC_PUT(regs, (unsigned)dev->sc_tcnt);
+
+ /*
+ * Start the transfer
+ */
+ SET_SBIC_cmd(regs, SBIC_CMD_XFER_INFO);
+
+ /*
+ * Indicate that we're in DMA mode
+ */
+ dev->sc_flags |= SBICF_INDMA;
+ }
+ }
+ break;
+
+ case SBIC_CSR_XFERRED | MESG_IN_PHASE:
+ case SBIC_CSR_MIS | MESG_IN_PHASE:
+ case SBIC_CSR_MIS_1 | MESG_IN_PHASE:
+ case SBIC_CSR_MIS_2 | MESG_IN_PHASE:
+ {
+ sbic_save_ptrs(dev);
+
+ /*
+ * Handle a single message in...
+ */
+ return sbicmsgin(dev);
+ }
+
+ case SBIC_CSR_MSGIN_W_ACK:
+ {
+ /*
+ * We should never see this since it's handled in 'sbicmsgin()'
+ * but just for the sake of paranoia...
+ */
+ SET_SBIC_cmd(regs, SBIC_CMD_CLR_ACK); /* Dunno what I'm ACKing */
+ printf("Acking unknown msgin CSR:%02x",csr);
+ }
+ break;
+
+ case SBIC_CSR_XFERRED | MESG_OUT_PHASE:
+ case SBIC_CSR_MIS | MESG_OUT_PHASE:
+ case SBIC_CSR_MIS_1 | MESG_OUT_PHASE:
+ case SBIC_CSR_MIS_2 | MESG_OUT_PHASE:
+ {
+ /*
+ * We only ever handle a message out phase here for sending a
+ * REJECT message.
+ */
+ sbic_save_ptrs(dev);
+
+#ifdef DEBUG
+ if (sync_debug)
+ printf ("sending REJECT msg to last msg.\n");
+#endif
+
+ SEND_BYTE(regs, MSG_REJECT);
+ WAIT_CIP(regs);
+ }
+ break;
+
+ case SBIC_CSR_DISC:
+ case SBIC_CSR_DISC_1:
+ {
+ /*
+ * Try to schedule another target
+ */
+ sbic_save_ptrs(dev);
+
+ dev->sc_flags &= ~SBICF_SELECTED;
+
+#ifdef DEBUG
+ if ( reselect_debug > 1 )
+ printf("sbicnext target %d disconnected\n", dev->target);
+#endif
+
+ TAILQ_INSERT_HEAD(&dev->nexus_list, acb, chain);
+
+ ++dev->sc_tinfo[dev->target].dconns;
+
+ dev->sc_nexus = NULL;
+ dev->sc_xs = NULL;
+
+ if ( acb->xs->flags & SCSI_POLL || dev->sc_flags & SBICF_ICMD ||
+ !sbic_parallel_operations )
+ return SBIC_STATE_DISCONNECT;
+
+ QPRINTF(("sbicnext: calling sbic_sched\n"));
+
+ sbic_sched(dev);
+
+ QPRINTF(("sbicnext: sbic_sched returned\n"));
+
+ return SBIC_STATE_DISCONNECT;
+ }
+
+ case SBIC_CSR_RSLT_NI:
+ case SBIC_CSR_RSLT_IFY:
+ {
+ /*
+ * A reselection.
+ * Note that since we don't enable Advanced Features (assuming
+ * the WD chip is at least the 'A' revision), we're only ever
+ * likely to see the 'SBIC_CSR_RSLT_NI' status. But for the
+ * hell of it, we'll handle it anyway, for all the extra code
+ * it needs...
+ */
+ u_char newtarget,
+ newlun;
+
+ GET_SBIC_rselid(regs, newtarget);
+
+ /*
+ * check SBIC_RID_SIV?
+ */
+ newtarget &= SBIC_RID_MASK;
+
+ if ( csr == SBIC_CSR_RSLT_IFY ) {
+
+ /*
+ * Read Identify msg to avoid lockup
+ */
+ GET_SBIC_data(regs, newlun);
+ WAIT_CIP(regs);
+ newlun &= SBIC_TLUN_MASK;
+
+ } else {
+
+ /*
+ * Need to read Identify message the hard way, assuming
+ * the target even sends us one...
+ */
+ for (newlun = 255; newlun; --newlun) {
+ GET_SBIC_asr(regs, asr);
+ if (asr & SBIC_ASR_INT)
+ break;
+ delay(10);
+ }
+
+ /*
+ * If we didn't get an interrupt, somethink's up
+ */
+ if ( (asr & SBIC_ASR_INT) == 0 ) {
+ printf("%s: Reselect without identify? asr %x\n",
+ dev->sc_dev.dv_xname, asr);
+ newlun = 0; /* XXXX */
+ } else {
+ /*
+ * We got an interrupt, verify that it's a change to
+ * message in phase, and if so read the message.
+ */
+ GET_SBIC_csr(regs,csr);
+
+ if (csr == (SBIC_CSR_MIS | MESG_IN_PHASE) ||
+ csr == (SBIC_CSR_MIS_1 | MESG_IN_PHASE) ||
+ csr == (SBIC_CSR_MIS_2 | MESG_IN_PHASE) ) {
+ /*
+ * Yup, gone to message in. Fetch the target LUN
+ */
+ sbicmsgin(dev);
+ newlun = dev->sc_msg[0] & 0x07;
+
+ } else {
+ /*
+ * Whoops! Target didn't go to message in phase!!
+ */
+ printf("RSLT_NI - not MESG_IN_PHASE %x\n", csr);
+ newlun = 0; /* XXXSCW */
+ }
+ }
+ }
+
+ /*
+ * Ok, we have the identity of the reselecting target.
+ */
+#ifdef DEBUG
+ if ( reselect_debug > 1 ||
+ (reselect_debug && csr == SBIC_CSR_RSLT_NI) ) {
+ printf("sbicnext: reselect %s from targ %d lun %d\n",
+ csr == SBIC_CSR_RSLT_NI ? "NI" : "IFY", newtarget, newlun);
+ }
+#endif
+
+ if ( dev->sc_nexus ) {
+ /*
+ * Whoops! We've been reselected with an command in progress!
+ * The best we can do is to put the current command back on the
+ * ready list and hope for the best.
+ */
+#ifdef DEBUG
+ if ( reselect_debug > 1 ) {
+ printf("%s: reselect %s with active command\n",
+ dev->sc_dev.dv_xname,
+ csr == SBIC_CSR_RSLT_NI ? "NI" : "IFY");
+ }
+#endif
+
+ TAILQ_INSERT_HEAD(&dev->ready_list, dev->sc_nexus, chain);
+
+ dev->sc_tinfo[dev->target].lubusy &= ~(1 << dev->lun);
+
+ dev->sc_nexus = NULL;
+ dev->sc_xs = NULL;
+ }
+
+ /*
+ * Reload sync values for this target
+ */
+ if ( dev->sc_sync[newtarget].state == SYNC_DONE )
+ SET_SBIC_syn(regs, SBIC_SYN (dev->sc_sync[newtarget].offset,
+ dev->sc_sync[newtarget].period));
+ else
+ SET_SBIC_syn(regs, SBIC_SYN (0, sbic_min_period));
+
+ /*
+ * Loop through the nexus list until we find the saved entry
+ * for the reselecting target...
+ */
+ for (acb = dev->nexus_list.tqh_first; acb;
+ acb = acb->chain.tqe_next) {
+
+ if ( acb->xs->sc_link->target == newtarget &&
+ acb->xs->sc_link->lun == newlun) {
+ /*
+ * We've found the saved entry. Dequeue it, and
+ * make it current again.
+ */
+ TAILQ_REMOVE(&dev->nexus_list, acb, chain);
+
+ dev->sc_nexus = acb;
+ dev->sc_xs = acb->xs;
+ dev->sc_flags |= SBICF_SELECTED;
+ dev->target = newtarget;
+ dev->lun = newlun;
+ break;
+ }
+ }
+
+ if ( acb == NULL ) {
+ printf("%s: reselect %s targ %d not in nexus_list %x\n",
+ dev->sc_dev.dv_xname,
+ csr == SBIC_CSR_RSLT_NI ? "NI" : "IFY", newtarget,
+ &dev->nexus_list.tqh_first);
+ panic("bad reselect in sbic");
+ }
+
+ if ( csr == SBIC_CSR_RSLT_IFY )
+ SET_SBIC_cmd(regs, SBIC_CMD_CLR_ACK);
+ }
+ break;
+
+ default:
+ abort:
+ {
+ /*
+ * Something unexpected happened -- deal with it.
+ */
+ printf("next: aborting asr 0x%02x csr 0x%02x\n", asr, csr);
+
+#ifdef DDB
+ Debugger();
+#endif
+
+#ifdef DEBUG
+ dev->sc_dmatimo = 0;
+ if ( data_pointer_debug > 1 )
+ printf("next dmastop: %d(%x:%x)\n", dev->target,
+ dev->sc_cur->dc_addr,
+ dev->sc_tcnt);
+#endif
+
+ dev->sc_dmastop(dev);
+ SET_SBIC_control(regs, SBIC_CTL_EDI | SBIC_CTL_IDI);
+ if ( dev->sc_xs ) sbicerror(dev, csr);
+ sbicabort(dev, "next");
+
+ if ( dev->sc_flags & SBICF_INDMA ) {
+ dev->sc_flags &= ~(SBICF_INDMA | SBICF_DCFLUSH);
+
+#ifdef DEBUG
+ dev->sc_dmatimo = 0;
+ if ( data_pointer_debug > 1 )
+ printf("next dmastop: %d(%x:%x)\n", dev->target,
+ dev->sc_cur->dc_addr,
+ dev->sc_tcnt);
+#endif
+ sbic_scsidone(acb, -1);
+ }
+
+ return SBIC_STATE_ERROR;
+ }
+ }
+
+ return(SBIC_STATE_RUNNING);
+}
+
+
+/*
+ * Check if DMA can not be used with specified buffer
+ */
+int
+sbiccheckdmap(bp, len, mask)
+ void *bp;
+ u_long len,
+ mask;
+{
+ u_char *buffer;
+ u_long phy_buf;
+ u_long phy_len;
+
+ buffer = bp;
+
+ if ( len == 0 )
+ return(1);
+
+ while ( len ) {
+
+ phy_buf = kvtop(buffer);
+ phy_len = NBPG - ((int) buffer & PGOFSET);
+
+ if ( len < phy_len )
+ phy_len = len;
+
+ if ( phy_buf & mask )
+ return(1);
+
+ buffer += phy_len;
+ len -= phy_len;
+ }
+
+ return(0);
+}
+
+int
+sbictoscsiperiod(dev, a)
+ struct sbic_softc *dev;
+ int a;
+{
+ unsigned int fs;
+
+ /*
+ * cycle = DIV / (2 * CLK)
+ * DIV = FS + 2
+ * best we can do is 200ns at 20Mhz, 2 cycles
+ */
+
+ GET_SBIC_myid(dev->sc_sbicp, fs);
+
+ fs = (fs >> 6) + 2; /* DIV */
+
+ fs = (fs * 10000) / (dev->sc_clkfreq << 1); /* Cycle, in ns */
+
+ if ( a < 2 )
+ a = 8; /* map to Cycles */
+
+ return ( (fs * a) >> 2 ); /* in 4 ns units */
+}
+
+int
+sbicfromscsiperiod(dev, p)
+ struct sbic_softc *dev;
+ int p;
+{
+ unsigned fs,
+ ret;
+
+ /*
+ * Just the inverse of the above
+ */
+ GET_SBIC_myid(dev->sc_sbicp, fs);
+
+ fs = (fs >> 6) + 2; /* DIV */
+
+ fs = (fs * 10000) / (dev->sc_clkfreq << 1); /* Cycle, in ns */
+
+ ret = p << 2; /* in ns units */
+ ret = ret / fs; /* in Cycles */
+
+ if ( ret < sbic_min_period )
+ return(sbic_min_period);
+
+ /*
+ * verify rounding
+ */
+ if ( sbictoscsiperiod(dev, ret) < p )
+ ret++;
+
+ return( (ret >= 8) ? 0 : ret );
+}
+
+#ifdef DEBUG
+void
+sbictimeout(dev)
+ struct sbic_softc *dev;
+{
+ int s,
+ asr;
+
+ s = splbio();
+
+ if ( dev->sc_dmatimo ) {
+
+ if ( dev->sc_dmatimo > 1 ) {
+
+ printf("%s: dma timeout #%d\n", dev->sc_dev.dv_xname,
+ dev->sc_dmatimo - 1);
+
+ GET_SBIC_asr(dev->sc_sbicp, asr);
+
+ if ( asr & SBIC_ASR_INT ) {
+ /*
+ * We need to service a missed IRQ
+ */
+ sbicintr(dev);
+ } else {
+ (void) sbicabort(dev, "timeout");
+ splx(s);
+ return;
+ }
+ }
+
+ dev->sc_dmatimo++;
+ }
+
+ splx(s);
+
+ timeout((void *)sbictimeout, dev, 30 * hz);
+}
+#endif
diff --git a/sys/arch/kbus/dev/sbicdma.c b/sys/arch/kbus/dev/sbicdma.c
new file mode 100644
index 00000000000..642a78843c6
--- /dev/null
+++ b/sys/arch/kbus/dev/sbicdma.c
@@ -0,0 +1,256 @@
+/* $OpenBSD: sbicdma.c,v 1.1 1997/10/14 07:25:30 gingold Exp $ */
+
+/*
+ * Copyright (c) 1996 Steve Woodford
+ * Copyright (c) 1982, 1990 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.
+ *
+ * @(#)wdsc.c
+ */
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/device.h>
+#include <scsi/scsi_all.h>
+#include <scsi/scsiconf.h>
+#include <dev/dmavar.h>
+#include <dev/sbicreg.h>
+#include <dev/sbicvar.h>
+#include <dev/sbicdma.h>
+#include <machine/ioasic.h>
+#include <machine/autoconf.h>
+#include <vm/vm.h>
+
+void wdscattach __P((struct device *, struct device *, void *));
+int wdscmatch __P((struct device *, struct cfdata *, void *));
+
+void wdsc_enintr __P((struct sbic_softc *));
+int wdsc_dmago __P((struct sbic_softc *, char *, int, int));
+int wdsc_dmanext __P((struct sbic_softc *));
+void wdsc_dmastop __P((struct sbic_softc *));
+int wdsc_dmaintr __P((struct sbic_softc *));
+int wdsc_scsiintr __P((void *));
+
+struct scsi_adapter wdsc_scsiswitch = {
+ sbic_scsicmd,
+ sbic_minphys,
+ 0, /* no lun support */
+ 0, /* no lun support */
+};
+
+struct scsi_device wdsc_scsidev = {
+ NULL, /* use default error handler */
+ NULL, /* do not have a start functio */
+ NULL, /* have no async handler */
+ NULL, /* Use default done routine */
+};
+
+struct cfattach si_ca = {
+ sizeof(struct sbic_softc), (cfmatch_t)wdscmatch, wdscattach
+};
+
+struct cfdriver si_cd = {
+ NULL, "si", DV_DULL, NULL, 0
+};
+
+/*
+ * Define 'scsi_nosync = 0x00' to enable sync SCSI mode.
+ * This is untested as yet, use at your own risk...
+ */
+u_long scsi_nosync = 0xff;
+int shift_nosync = 0;
+
+/*
+ * Match for SCSI devices on the onboard WD33C93 chip
+ */
+int
+wdscmatch(pdp, cdp, auxp)
+ struct device *pdp;
+ struct cfdata *cdp;
+ void *auxp;
+{
+ /*
+ * Match everything
+ */
+ return(1);
+}
+
+
+/*
+ * Attach the wdsc driver
+ */
+void
+wdscattach(pdp, dp, auxp)
+ struct device *pdp, *dp;
+ void *auxp;
+{
+ struct sbic_softc *sc = (struct sbic_softc *)dp;
+/* struct confargs *ca = auxp; */
+
+ sc->sc_enintr = wdsc_enintr;
+ sc->sc_dmago = wdsc_dmago;
+ sc->sc_dmanext = wdsc_dmanext;
+ sc->sc_dmastop = wdsc_dmastop;
+ sc->sc_dmacmd = 0;
+
+ sc->sc_link.adapter_softc = sc;
+ sc->sc_link.adapter_target = 7;
+ sc->sc_link.adapter = &wdsc_scsiswitch;
+ sc->sc_link.device = &wdsc_scsidev;
+ sc->sc_link.openings = 2;
+
+ printf(": target %d\n", sc->sc_link.adapter_target);
+
+
+ sc->sc_sbicp = (sbic_regmap_p)((void *)ioasic + WD33C93A_OFFSET);
+
+ /*
+ * Eveything is a valid dma address.
+ *
+ */
+ sc->sc_dmamask = 0;
+
+ /*
+ * The onboard WD33C93 is usually clocked at 20MHz...
+ * (We use 10 times this for accuracy in later calculations)
+ */
+ sc->sc_clkfreq = 200;
+
+ /*
+ * Initialise the hardware
+ */
+ sbicinit(sc);
+
+ /*
+ * Fix up the interrupts
+ */
+ sc->sc_sbicih.ih_fun = wdsc_scsiintr;
+ sc->sc_sbicih.ih_arg = sc;
+ intr_establish (INTR_IOASIC /* ca->ca_intpri */, 0, &sc->sc_sbicih);
+
+ /*
+ * Attach all scsi units on us, watching for boot device
+ * (see dk_establish).
+ */
+ config_found(dp, &sc->sc_link, scsiprint);
+}
+
+/*
+ * Enable DMA interrupts
+ */
+void
+wdsc_enintr(dev)
+ struct sbic_softc *dev;
+{
+ dev->sc_flags |= SBICF_INTR;
+}
+
+/*
+ * Prime the hardware for a DMA transfer
+ */
+int
+wdsc_dmago (sc, pa, count, flags)
+ struct sbic_softc *sc;
+ char *pa;
+ int count, flags;
+{
+ printf ("dmago: ir=%d, count=%d, pa=%p\n",
+ ioasic->ioasic_ir, count, pa);
+ /*
+ * Set up the command word based on flags
+ */
+ if ( (flags & DMAGO_READ) == 0 )
+ sc->sc_dmacmd = IOASIC_SI_CTL_WRITE;
+ else
+ sc->sc_dmacmd = IOASIC_SI_CTL_READ;
+
+ sc->sc_flags |= SBICF_INTR;
+
+ /*
+ * Prime the hardware.
+ * Note, it's probably not necessary to do this here, since dmanext
+ * is called just prior to the actual transfer.
+ */
+
+ ioasic->ioasic_si_sar = ((u_long)pa) >> IOASIC_SI_SAR_SHIFT;
+ ioasic->ioasic_si_ctl =
+ ((((u_long)pa) << IOASIC_SI_CTL_SHIFT) & IOASIC_SI_CTL_AMASK)
+ | sc->sc_dmacmd;
+
+ return(sc->sc_tcnt);
+}
+
+/*
+ * Prime the hardware for the next DMA transfer
+ */
+int
+wdsc_dmanext(dev)
+ struct sbic_softc *dev;
+{
+ printf ("sbic dmanext called");
+ return 0;
+}
+
+/*
+ * Stop DMA, and disable interrupts
+ */
+void
+wdsc_dmastop(dev)
+ struct sbic_softc *dev;
+{
+}
+
+/*
+ * Come here for SCSI interrupts
+ */
+int
+wdsc_scsiintr(arg)
+ void *arg;
+{
+ struct sbic_softc *dev = (struct sbic_softc *)arg;
+ int found;
+
+ if (ioasic->ioasic_ir & IOASIC_IR_SI_PO)
+ {
+ printf ("IOASIC page overflow, sar = %p, ctl = %p, pre = %p\n",
+ ioasic->ioasic_si_sar << IOASIC_SI_SAR_SHIFT,
+ ioasic->ioasic_si_ctl,
+ ioasic->ioasic_si_sar_pre);
+ return 1;
+ }
+
+ /*
+ * Go handle it
+ */
+ found = sbicintr(dev);
+
+ return(found);
+}
diff --git a/sys/arch/kbus/dev/sbicdma.h b/sys/arch/kbus/dev/sbicdma.h
new file mode 100644
index 00000000000..d4ba3f4a354
--- /dev/null
+++ b/sys/arch/kbus/dev/sbicdma.h
@@ -0,0 +1,53 @@
+/* $OpenBSD: sbicdma.h,v 1.1 1997/10/14 07:25:30 gingold Exp $ */
+
+/*
+ * Copyright (c) 1994 Christian E. Hopps
+ * Copyright (c) 1982, 1990 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.
+ *
+ * @(#)dmareg.h
+ */
+#ifndef _MVME68K_DEV_WDSCREG_H_
+#define _MVME68K_DEV_WDSCREG_H_
+
+#define DMAC_CSR_ENABLE (1 << 0) /* Enable the DMAC */
+#define DMAC_CSR_TABLE (1 << 1) /* Select Table Mode */
+#define DMAC_CSR_WRITE (1 << 2) /* Write data from RAM to SCSI */
+#define DMAC_CSR_TBUSERR (1 << 3) /* Bus error during table walk */
+#define DMAC_CSR_DBUSERR (1 << 4) /* Bus error during data xfer */
+#define DMAC_CSR_TSIZE (1 << 5) /* Table addr. not in 32 bits */
+#define DMAC_CSR_8BITS (1 << 6) /* Non-8 bit handshake */
+#define DMAC_CSR_DONE (1 << 7) /* Transfer complete, or error */
+
+#define DMAC_SR_HOLDING 0x0f /* Data holding state */
+#define DMAC_SR_INCREMENT 0xf0 /* Increment value */
+
+#endif
diff --git a/sys/arch/kbus/dev/sbicreg.h b/sys/arch/kbus/dev/sbicreg.h
new file mode 100644
index 00000000000..c26ac629ce6
--- /dev/null
+++ b/sys/arch/kbus/dev/sbicreg.h
@@ -0,0 +1,441 @@
+/* $OpenBSD: sbicreg.h,v 1.1 1997/10/14 07:25:30 gingold Exp $ */
+
+/*
+ * Copyright (c) 1990 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Van Jacobson of Lawrence Berkeley Laboratory.
+ *
+ * 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.
+ *
+ * @(#)scsireg.h 7.3 (Berkeley) 2/5/91
+ */
+
+/*
+ * WD33C93 SCSI interface hardware description.
+ *
+ * Using parts of the Mach scsi driver for the 33C93
+ */
+
+#define SBIC_myid 0
+#define SBIC_cdbsize 0
+#define SBIC_control 1
+#define SBIC_timeo 2
+#define SBIC_cdb1 3
+#define SBIC_tsecs 3
+#define SBIC_cdb2 4
+#define SBIC_theads 4
+#define SBIC_cdb3 5
+#define SBIC_tcyl_hi 5
+#define SBIC_cdb4 6
+#define SBIC_tcyl_lo 6
+#define SBIC_cdb5 7
+#define SBIC_addr_hi 7
+#define SBIC_cdb6 8
+#define SBIC_addr_2 8
+#define SBIC_cdb7 9
+#define SBIC_addr_3 9
+#define SBIC_cdb8 10
+#define SBIC_addr_lo 10
+#define SBIC_cdb9 11
+#define SBIC_secno 11
+#define SBIC_cdb10 12
+#define SBIC_headno 12
+#define SBIC_cdb11 13
+#define SBIC_cylno_hi 13
+#define SBIC_cdb12 14
+#define SBIC_cylno_lo 14
+#define SBIC_tlun 15
+#define SBIC_cmd_phase 16
+#define SBIC_syn 17
+#define SBIC_count_hi 18
+#define SBIC_count_med 19
+#define SBIC_count_lo 20
+#define SBIC_selid 21
+#define SBIC_rselid 22
+#define SBIC_csr 23
+#define SBIC_cmd 24
+#define SBIC_data 25
+#define SBIC_queue_tag 26
+/* sbic_asr is addressed directly */
+
+/*
+ * Register defines
+ */
+
+/*
+ * Auxiliary Status Register
+ */
+
+#define SBIC_ASR_INT 0x80 /* Interrupt pending */
+#define SBIC_ASR_LCI 0x40 /* Last command ignored */
+#define SBIC_ASR_BSY 0x20 /* Busy, only cmd/data/asr readable */
+#define SBIC_ASR_CIP 0x10 /* Busy, cmd unavail also */
+#define SBIC_ASR_xxx 0x0c
+#define SBIC_ASR_PE 0x02 /* Parity error (even) */
+#define SBIC_ASR_DBR 0x01 /* Data Buffer Ready */
+
+/*
+ * My ID register, and/or CDB Size
+ */
+
+#define SBIC_ID_FS_8_10 0x00 /* Input clock is 8-10 Mhz */
+ /* 11 Mhz is invalid */
+#define SBIC_ID_FS_12_15 0x40 /* Input clock is 12-15 Mhz */
+#define SBIC_ID_FS_16_20 0x80 /* Input clock is 16-20 Mhz */
+#define SBIC_ID_EHP 0x10 /* Enable host parity */
+#define SBIC_ID_EAF 0x08 /* Enable Advanced Features */
+#define SBIC_ID_MASK 0x07
+#define SBIC_ID_CBDSIZE_MASK 0x0f /* if unk SCSI cmd group */
+
+/*
+ * Control register
+ */
+
+#define SBIC_CTL_DMA 0x80 /* Single byte dma */
+#define SBIC_CTL_DBA_DMA 0x40 /* direct buffer acces (bus master)*/
+#define SBIC_CTL_BURST_DMA 0x20 /* continuous mode (8237) */
+#define SBIC_CTL_NO_DMA 0x00 /* Programmed I/O */
+#define SBIC_CTL_HHP 0x10 /* Halt on host parity error */
+#define SBIC_CTL_EDI 0x08 /* Ending disconnect interrupt */
+#define SBIC_CTL_IDI 0x04 /* Intermediate disconnect interrupt*/
+#define SBIC_CTL_HA 0x02 /* Halt on ATN */
+#define SBIC_CTL_HSP 0x01 /* Halt on SCSI parity error */
+
+/*
+ * Timeout period register
+ * [val in msecs, input clk in 0.1 Mhz]
+ */
+
+#define SBIC_TIMEOUT(val,clk) ((((val) * (clk)) / 800) + 1)
+
+/*
+ * CDBn registers, note that
+ * cdb11 is used for status byte in target mode (send-status-and-cc)
+ * cdb12 sez if linked command complete, and w/flag if so
+ */
+
+/*
+ * Target LUN register
+ * [holds target status when select-and-xfer]
+ */
+
+#define SBIC_TLUN_VALID 0x80 /* did we receive an Identify msg */
+#define SBIC_TLUN_DOK 0x40 /* Disconnect OK */
+#define SBIC_TLUN_xxx 0x38
+#define SBIC_TLUN_MASK 0x07
+
+/*
+ * Command Phase register
+ */
+
+#define SBIC_CPH_MASK 0x7f /* values/restarts are cmd specific */
+#define SBIC_CPH(p) ((p) & SBIC_CPH_MASK)
+
+/*
+ * FIFO register
+ */
+
+#define SBIC_FIFO_DEEP 12
+
+/*
+ * maximum possible size in TC registers. Since this is 24 bit, it's easy
+ */
+#define SBIC_TC_MAX ((1 << 24) - 1)
+
+/*
+ * Synchronous xfer register
+ */
+
+#define SBIC_SYN_OFF_MASK 0x0f
+#define SBIC_SYN_MAX_OFFSET SBIC_FIFO_DEEP
+#define SBIC_SYN_PER_MASK 0x70
+#define SBIC_SYN_MIN_PERIOD 2 /* upto 8, encoded as 0 */
+
+#define SBIC_SYN(o,p) \
+ (((o) & SBIC_SYN_OFF_MASK) | (((p) << 4) & SBIC_SYN_PER_MASK))
+
+/*
+ * Transfer count register
+ * optimal access macros depend on addressing
+ */
+
+/*
+ * Destination ID (selid) register
+ */
+
+#define SBIC_SID_SCC 0x80 /* Select command chaining (tgt) */
+#define SBIC_SID_DPD 0x40 /* Data phase direction (inittor) */
+#define SBIC_SID_FROM_SCSI 0x40
+#define SBIC_SID_TO_SCSI 0x00
+#define SBIC_SID_xxx 0x38
+#define SBIC_SID_IDMASK 0x07
+
+/*
+ * Source ID (rselid) register
+ */
+
+#define SBIC_RID_ER 0x80 /* Enable reselection */
+#define SBIC_RID_ES 0x40 /* Enable selection */
+#define SBIC_RID_DSP 0x20 /* Disable select parity */
+#define SBIC_RID_SIV 0x08 /* Source ID valid */
+#define SBIC_RID_MASK 0x07
+
+/*
+ * Status register
+ */
+
+#define SBIC_CSR_CAUSE 0xf0
+#define SBIC_CSR_RESET 0x00 /* chip was reset */
+#define SBIC_CSR_CMD_DONE 0x10 /* cmd completed */
+#define SBIC_CSR_CMD_STOPPED 0x20 /* interrupted or abrted*/
+#define SBIC_CSR_CMD_ERR 0x40 /* end with error */
+#define SBIC_CSR_BUS_SERVICE 0x80 /* REQ pending on the bus */
+
+
+#define SBIC_CSR_QUALIFIER 0x0f
+/* Reset State Interrupts */
+#define SBIC_CSR_RESET 0x00 /* reset w/advanced features*/
+#define SBIC_CSR_RESET_AM 0x01 /* reset w/advanced features*/
+/* Successful Completion Interrupts */
+#define SBIC_CSR_TARGET 0x10 /* reselect complete */
+#define SBIC_CSR_INITIATOR 0x11 /* select complete */
+#define SBIC_CSR_WO_ATN 0x13 /* tgt mode completion */
+#define SBIC_CSR_W_ATN 0x14 /* ditto */
+#define SBIC_CSR_XLATED 0x15 /* translate address cmd */
+#define SBIC_CSR_S_XFERRED 0x16 /* initiator mode completion*/
+#define SBIC_CSR_XFERRED 0x18 /* phase in low bits */
+/* Paused or Aborted Interrupts */
+#define SBIC_CSR_MSGIN_W_ACK 0x20 /* (I) msgin, ACK asserted*/
+#define SBIC_CSR_SDP 0x21 /* (I) SDP msg received */
+#define SBIC_CSR_SEL_ABRT 0x22 /* sel/resel aborted */
+#define SBIC_CSR_XFR_PAUSED 0x23 /* (T) no ATN */
+#define SBIC_CSR_XFR_PAUSED_ATN 0x24 /* (T) ATN is asserted */
+#define SBIC_CSR_RSLT_AM 0x27 /* (I) lost selection (AM) */
+#define SBIC_CSR_MIS 0x28 /* (I) xfer aborted, ph mis */
+/* Terminated Interrupts */
+#define SBIC_CSR_CMD_INVALID 0x40
+#define SBIC_CSR_DISC 0x41 /* (I) tgt disconnected */
+#define SBIC_CSR_SEL_TIMEO 0x42
+#define SBIC_CSR_PE 0x43 /* parity error */
+#define SBIC_CSR_PE_ATN 0x44 /* ditto, ATN is asserted */
+#define SBIC_CSR_XLATE_TOOBIG 0x45
+#define SBIC_CSR_RSLT_NOAM 0x46 /* (I) lost sel, no AM mode */
+#define SBIC_CSR_BAD_STATUS 0x47 /* status byte was nok */
+#define SBIC_CSR_MIS_1 0x48 /* ph mis, see low bits */
+/* Service Required Interrupts */
+#define SBIC_CSR_RSLT_NI 0x80 /* reselected, no ify msg */
+#define SBIC_CSR_RSLT_IFY 0x81 /* ditto, AM mode, got ify */
+#define SBIC_CSR_SLT 0x82 /* selected, no ATN */
+#define SBIC_CSR_SLT_ATN 0x83 /* selected with ATN */
+#define SBIC_CSR_ATN 0x84 /* (T) ATN asserted */
+#define SBIC_CSR_DISC_1 0x85 /* (I) bus is free */
+#define SBIC_CSR_UNK_GROUP 0x87 /* strange CDB1 */
+#define SBIC_CSR_MIS_2 0x88 /* (I) ph mis, see low bits */
+
+#define SBIC_PHASE(csr) SCSI_PHASE(csr)
+
+/*
+ * Command register (command codes)
+ */
+
+#define SBIC_CMD_SBT 0x80 /* Single byte xfer qualifier */
+#define SBIC_CMD_MASK 0x7f
+
+ /* Miscellaneous */
+#define SBIC_CMD_RESET 0x00 /* (DTI) lev I */
+#define SBIC_CMD_ABORT 0x01 /* (DTI) lev I */
+#define SBIC_CMD_DISC 0x04 /* ( TI) lev I */
+#define SBIC_CMD_SSCC 0x0d /* ( TI) lev I */
+#define SBIC_CMD_SET_IDI 0x0f /* (DTI) lev I */
+#define SBIC_CMD_XLATE 0x18 /* (DT ) lev II */
+
+ /* Initiator state */
+#define SBIC_CMD_SET_ATN 0x02 /* ( I) lev I */
+#define SBIC_CMD_CLR_ACK 0x03 /* ( I) lev I */
+#define SBIC_CMD_XFER_PAD 0x19 /* ( I) lev II */
+#define SBIC_CMD_XFER_INFO 0x20 /* ( I) lev II */
+
+ /* Target state */
+#define SBIC_CMD_SND_DISC 0x0e /* ( T ) lev II */
+#define SBIC_CMD_RCV_CMD 0x10 /* ( T ) lev II */
+#define SBIC_CMD_RCV_DATA 0x11 /* ( T ) lev II */
+#define SBIC_CMD_RCV_MSG_OUT 0x12 /* ( T ) lev II */
+#define SBIC_CMD_RCV 0x13 /* ( T ) lev II */
+#define SBIC_CMD_SND_STATUS 0x14 /* ( T ) lev II */
+#define SBIC_CMD_SND_DATA 0x15 /* ( T ) lev II */
+#define SBIC_CMD_SND_MSG_IN 0x16 /* ( T ) lev II */
+#define SBIC_CMD_SND 0x17 /* ( T ) lev II */
+
+ /* Disconnected state */
+#define SBIC_CMD_RESELECT 0x05 /* (D ) lev II */
+#define SBIC_CMD_SEL_ATN 0x06 /* (D ) lev II */
+#define SBIC_CMD_SEL 0x07 /* (D ) lev II */
+#define SBIC_CMD_SEL_ATN_XFER 0x08 /* (D I) lev II */
+#define SBIC_CMD_SEL_XFER 0x09 /* (D I) lev II */
+#define SBIC_CMD_RESELECT_RECV 0x0a /* (DT ) lev II */
+#define SBIC_CMD_RESELECT_SEND 0x0b /* (DT ) lev II */
+#define SBIC_CMD_WAIT_SEL_RECV 0x0c /* (DT ) lev II */
+
+/* approximate, but we won't do SBT on selects */
+#define sbic_isa_select(cmd) (((cmd) > 0x5) && ((cmd) < 0xa))
+
+#define PAD(n) char n;
+#define SBIC_MACHINE_DMA_MODE SBIC_CTL_DMA
+
+typedef struct {
+ volatile unsigned char sbic_asr; /* r : Aux Status Register */
+ volatile unsigned char pad[0x0f];
+#define sbic_address sbic_asr /* w : desired register no */
+ volatile unsigned char sbic_value; /* rw: register value */
+} sbic_padded_ind_regmap_t;
+typedef volatile sbic_padded_ind_regmap_t *sbic_regmap_p;
+
+#define sbic_read_reg(regs,regno,val) \
+ do { \
+ (regs)->sbic_address = (regno); \
+ (val) = (regs)->sbic_value; \
+ } while (0)
+
+#define sbic_write_reg(regs,regno,val) \
+ do { \
+ (regs)->sbic_address = (regno); \
+ (regs)->sbic_value = (val); \
+ } while (0)
+
+#define SET_SBIC_myid(regs,val) sbic_write_reg(regs,SBIC_myid,val)
+#define GET_SBIC_myid(regs,val) sbic_read_reg(regs,SBIC_myid,val)
+#define SET_SBIC_cdbsize(regs,val) sbic_write_reg(regs,SBIC_cdbsize,val)
+#define GET_SBIC_cdbsize(regs,val) sbic_read_reg(regs,SBIC_cdbsize,val)
+#define SET_SBIC_control(regs,val) sbic_write_reg(regs,SBIC_control,val)
+#define GET_SBIC_control(regs,val) sbic_read_reg(regs,SBIC_control,val)
+#define SET_SBIC_timeo(regs,val) sbic_write_reg(regs,SBIC_timeo,val)
+#define GET_SBIC_timeo(regs,val) sbic_read_reg(regs,SBIC_timeo,val)
+#define SET_SBIC_cdb1(regs,val) sbic_write_reg(regs,SBIC_cdb1,val)
+#define GET_SBIC_cdb1(regs,val) sbic_read_reg(regs,SBIC_cdb1,val)
+#define SET_SBIC_cdb2(regs,val) sbic_write_reg(regs,SBIC_cdb2,val)
+#define GET_SBIC_cdb2(regs,val) sbic_read_reg(regs,SBIC_cdb2,val)
+#define SET_SBIC_cdb3(regs,val) sbic_write_reg(regs,SBIC_cdb3,val)
+#define GET_SBIC_cdb3(regs,val) sbic_read_reg(regs,SBIC_cdb3,val)
+#define SET_SBIC_cdb4(regs,val) sbic_write_reg(regs,SBIC_cdb4,val)
+#define GET_SBIC_cdb4(regs,val) sbic_read_reg(regs,SBIC_cdb4,val)
+#define SET_SBIC_cdb5(regs,val) sbic_write_reg(regs,SBIC_cdb5,val)
+#define GET_SBIC_cdb5(regs,val) sbic_read_reg(regs,SBIC_cdb5,val)
+#define SET_SBIC_cdb6(regs,val) sbic_write_reg(regs,SBIC_cdb6,val)
+#define GET_SBIC_cdb6(regs,val) sbic_read_reg(regs,SBIC_cdb6,val)
+#define SET_SBIC_cdb7(regs,val) sbic_write_reg(regs,SBIC_cdb7,val)
+#define GET_SBIC_cdb7(regs,val) sbic_read_reg(regs,SBIC_cdb7,val)
+#define SET_SBIC_cdb8(regs,val) sbic_write_reg(regs,SBIC_cdb8,val)
+#define GET_SBIC_cdb8(regs,val) sbic_read_reg(regs,SBIC_cdb8,val)
+#define SET_SBIC_cdb9(regs,val) sbic_write_reg(regs,SBIC_cdb9,val)
+#define GET_SBIC_cdb9(regs,val) sbic_read_reg(regs,SBIC_cdb9,val)
+#define SET_SBIC_cdb10(regs,val) sbic_write_reg(regs,SBIC_cdb10,val)
+#define GET_SBIC_cdb10(regs,val) sbic_read_reg(regs,SBIC_cdb10,val)
+#define SET_SBIC_cdb11(regs,val) sbic_write_reg(regs,SBIC_cdb11,val)
+#define GET_SBIC_cdb11(regs,val) sbic_read_reg(regs,SBIC_cdb11,val)
+#define SET_SBIC_cdb12(regs,val) sbic_write_reg(regs,SBIC_cdb12,val)
+#define GET_SBIC_cdb12(regs,val) sbic_read_reg(regs,SBIC_cdb12,val)
+#define SET_SBIC_tlun(regs,val) sbic_write_reg(regs,SBIC_tlun,val)
+#define GET_SBIC_tlun(regs,val) sbic_read_reg(regs,SBIC_tlun,val)
+#define SET_SBIC_cmd_phase(regs,val) sbic_write_reg(regs,SBIC_cmd_phase,val)
+#define GET_SBIC_cmd_phase(regs,val) sbic_read_reg(regs,SBIC_cmd_phase,val)
+#define SET_SBIC_syn(regs,val) sbic_write_reg(regs,SBIC_syn,val)
+#define GET_SBIC_syn(regs,val) sbic_read_reg(regs,SBIC_syn,val)
+#define SET_SBIC_count_hi(regs,val) sbic_write_reg(regs,SBIC_count_hi,val)
+#define GET_SBIC_count_hi(regs,val) sbic_read_reg(regs,SBIC_count_hi,val)
+#define SET_SBIC_count_med(regs,val) sbic_write_reg(regs,SBIC_count_med,val)
+#define GET_SBIC_count_med(regs,val) sbic_read_reg(regs,SBIC_count_med,val)
+#define SET_SBIC_count_lo(regs,val) sbic_write_reg(regs,SBIC_count_lo,val)
+#define GET_SBIC_count_lo(regs,val) sbic_read_reg(regs,SBIC_count_lo,val)
+#define SET_SBIC_selid(regs,val) sbic_write_reg(regs,SBIC_selid,val)
+#define GET_SBIC_selid(regs,val) sbic_read_reg(regs,SBIC_selid,val)
+#define SET_SBIC_rselid(regs,val) sbic_write_reg(regs,SBIC_rselid,val)
+#define GET_SBIC_rselid(regs,val) sbic_read_reg(regs,SBIC_rselid,val)
+#define SET_SBIC_csr(regs,val) sbic_write_reg(regs,SBIC_csr,val)
+#define GET_SBIC_csr(regs,val) sbic_read_reg(regs,SBIC_csr,val)
+#define SET_SBIC_cmd(regs,val) sbic_write_reg(regs,SBIC_cmd,val)
+#define GET_SBIC_cmd(regs,val) sbic_read_reg(regs,SBIC_cmd,val)
+#define SET_SBIC_data(regs,val) sbic_write_reg(regs,SBIC_data,val)
+#define GET_SBIC_data(regs,val) sbic_read_reg(regs,SBIC_data,val)
+#define SET_SBIC_queue_tag(regs,val) sbic_write_reg(regs,SBIC_queue_tag,val)
+#define GET_SBIC_queue_tag(regs,val) sbic_read_reg(regs,SBIC_queue_tag,val)
+
+#define SBIC_TC_PUT(regs,val) \
+ do { \
+ sbic_write_reg(regs,SBIC_count_hi,((val)>>16)); \
+ (regs)->sbic_value = (val)>>8; \
+ (regs)->sbic_value = (val); \
+ } while (0)
+
+#define SBIC_TC_GET(regs,val) \
+ do { \
+ sbic_read_reg(regs,SBIC_count_hi,(val)); \
+ (val) = ((val)<<8) | (regs)->sbic_value; \
+ (val) = ((val)<<8) | (regs)->sbic_value; \
+ } while (0)
+
+#define SBIC_LOAD_COMMAND(regs,cmd,cmdsize) \
+ do { \
+ int n = (cmdsize) - 1; \
+ char *ptr = (char *)(cmd); \
+ sbic_write_reg(regs, SBIC_cdb1, *ptr++); \
+ while(n-- > 0) (regs)->sbic_value = *ptr++; \
+ } while (0)
+
+#define GET_SBIC_asr(regs,val) (val) = (regs)->sbic_asr
+
+#define WAIT_CIP(regs) \
+ do { \
+ while ((regs)->sbic_asr & SBIC_ASR_CIP) \
+ ; \
+ } while (0)
+
+/*
+ * transmit a byte in programmed I/O mode
+ **/
+#define SEND_BYTE(regs, ch) \
+ do { \
+ WAIT_CIP(regs); \
+ SET_SBIC_cmd(regs, SBIC_CMD_SBT | SBIC_CMD_XFER_INFO); \
+ SBIC_WAIT(regs, SBIC_ASR_DBR, 0); \
+ SET_SBIC_data(regs, ch); \
+ } while (0)
+
+/*
+ * receive a byte in programmed I/O mode
+ */
+#define RECV_BYTE(regs, ch) \
+ do { \
+ WAIT_CIP(regs); \
+ SET_SBIC_cmd(regs, SBIC_CMD_SBT | SBIC_CMD_XFER_INFO); \
+ SBIC_WAIT(regs, SBIC_ASR_DBR, 0); \
+ GET_SBIC_data(regs, ch); \
+ } while (0)
+
diff --git a/sys/arch/kbus/dev/sbicvar.h b/sys/arch/kbus/dev/sbicvar.h
new file mode 100644
index 00000000000..93228a1341e
--- /dev/null
+++ b/sys/arch/kbus/dev/sbicvar.h
@@ -0,0 +1,235 @@
+/* $OpenBSD: sbicvar.h,v 1.1 1997/10/14 07:25:30 gingold Exp $ */
+/* $NetBSD: sbicvar.h,v 1.11 1996/04/21 21:12:23 veego Exp $ */
+
+/*
+ * Copyright (c) 1990 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Van Jacobson of Lawrence Berkeley Laboratory.
+ *
+ * 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.
+ *
+ * @(#)scsivar.h 7.1 (Berkeley) 5/8/90
+ */
+#ifndef _SBICVAR_H_
+#define _SBICVAR_H_
+#include <sys/malloc.h>
+
+/*
+ * The largest single request will be MAXPHYS bytes which will require
+ * at most MAXPHYS/NBPG+1 chain elements to describe, i.e. if none of
+ * the buffer pages are physically contiguous (MAXPHYS/NBPG) and the
+ * buffer is not page aligned (+1).
+ */
+#define DMAMAXIO (MAXPHYS/NBPG+1)
+
+struct dma_chain {
+ int dc_count;
+ char *dc_addr;
+};
+
+/*
+ * ACB. Holds additional information for each SCSI command Comments: We
+ * need a separate scsi command block because we may need to overwrite it
+ * with a request sense command. Basicly, we refrain from fiddling with
+ * the scsi_xfer struct (except do the expected updating of return values).
+ * We'll generally update: xs->{flags,resid,error,sense,status} and
+ * occasionally xs->retries.
+ */
+struct sbic_acb {
+ TAILQ_ENTRY(sbic_acb) chain;
+ struct scsi_xfer *xs; /* SCSI xfer ctrl block from above */
+ int flags; /* Status */
+#define ACB_FREE 0x00
+#define ACB_ACTIVE 0x01
+#define ACB_DONE 0x04
+#define ACB_CHKSENSE 0x08
+#define ACB_BBUF 0x10 /* DMA input needs to be copied from bounce */
+#define ACB_DATAIN 0x20 /* DMA direction flag */
+ struct scsi_generic cmd; /* SCSI command block */
+ int clen;
+ struct dma_chain sc_kv; /* Virtual address of whole DMA */
+ struct dma_chain sc_pa; /* Physical address of DMA segment */
+ u_long sc_tcnt; /* number of bytes for this DMA */
+ u_char *sc_dmausrbuf; /* user buffer kva - for bounce copy */
+ u_long sc_dmausrlen; /* length of bounce copy */
+ u_short sc_dmacmd; /* Internal data for this DMA */
+ char *pa_addr; /* XXXX initial phys addr */
+ u_char *sc_usrbufpa; /* user buffer phys addr */
+};
+
+/*
+ * Some info about each (possible) target on the SCSI bus. This should
+ * probably have been a "per target+lunit" structure, but we'll leave it at
+ * this for now. Is there a way to reliably hook it up to sc->fordriver??
+ */
+struct sbic_tinfo {
+ int cmds; /* #commands processed */
+ int dconns; /* #disconnects */
+ int touts; /* #timeouts */
+ int perrs; /* #parity errors */
+ int senses; /* #request sense commands sent */
+ u_char* bounce; /* Bounce buffer for this device */
+ ushort lubusy; /* What local units/subr. are busy? */
+ u_char flags;
+ u_char period; /* Period suggestion */
+ u_char offset; /* Offset suggestion */
+} tinfo_t;
+
+struct sbic_softc {
+ struct device sc_dev;
+ struct intrhand sc_sbicih;
+ struct target_sync {
+ u_char state;
+ u_char period;
+ u_char offset;
+ } sc_sync[8];
+ u_char target; /* Currently active target */
+ u_char lun;
+ struct scsi_link sc_link; /* proto for sub devices */
+ sbic_regmap_p sc_sbicp; /* the SBIC */
+ volatile void *sc_cregs; /* driver specific regs */
+
+ /* Lists of command blocks */
+ TAILQ_HEAD(acb_list, sbic_acb) free_list,
+ ready_list,
+ nexus_list;
+
+ struct sbic_acb *sc_nexus; /* current command */
+ struct sbic_acb sc_acb[8]; /* the real command blocks */
+ struct sbic_tinfo sc_tinfo[8];
+
+ struct scsi_xfer *sc_xs; /* transfer from high level code */
+ u_char sc_flags;
+ u_char sc_scsiaddr;
+ u_char sc_stat[2];
+ u_char sc_msg[7];
+ u_long sc_clkfreq;
+ u_long sc_tcnt; /* number of bytes transfered */
+ u_short sc_dmacmd; /* used by dma drivers */
+ u_short sc_dmatimo; /* dma timeout */
+ u_long sc_dmamask; /* dma valid mem mask */
+ struct dma_chain *sc_cur;
+ struct dma_chain *sc_last;
+ int (*sc_dmago) __P((struct sbic_softc *, char *, int, int));
+ int (*sc_dmanext) __P((struct sbic_softc *));
+ void (*sc_enintr) __P((struct sbic_softc *));
+ void (*sc_dmastop) __P((struct sbic_softc *));
+ u_short gtsc_bankmask; /* GVP specific bank selected */
+};
+
+/* sc_flags */
+#define SBICF_ALIVE 0x01 /* controller initialized */
+#define SBICF_DCFLUSH 0x02 /* need flush for overlap after dma finishes */
+#define SBICF_SELECTED 0x04 /* bus is in selected state. */
+#define SBICF_ICMD 0x08 /* Immediate command in execution */
+#define SBICF_BADDMA 0x10 /* controller can only DMA to ztwobus space */
+#define SBICF_INTR 0x40 /* SBICF interrupt expected */
+#define SBICF_INDMA 0x80 /* not used yet, DMA I/O in progress */
+
+/* sync states */
+#define SYNC_START 0 /* no sync handshake started */
+#define SYNC_SENT 1 /* we sent sync request, no answer yet */
+#define SYNC_DONE 2 /* target accepted our (or inferior) settings,
+ or it rejected the request and we stay async */
+#ifdef DEBUG
+#define DDB_FOLLOW 0x04
+#define DDB_IO 0x08
+#endif
+extern u_char sbic_inhibit_sync[8];
+extern int sbic_no_dma;
+extern int sbic_clock_override;
+
+#define PHASE 0x07 /* mask for psns/pctl phase */
+#define DATA_OUT_PHASE 0x00
+#define DATA_IN_PHASE 0x01
+#define CMD_PHASE 0x02
+#define STATUS_PHASE 0x03
+#define BUS_FREE_PHASE 0x04
+#define ARB_SEL_PHASE 0x05 /* Fuji chip combines arbitration with sel. */
+#define MESG_OUT_PHASE 0x06
+#define MESG_IN_PHASE 0x07
+
+#define MSG_CMD_COMPLETE 0x00
+#define MSG_EXT_MESSAGE 0x01
+#define MSG_SAVE_DATA_PTR 0x02
+#define MSG_RESTORE_PTR 0x03
+#define MSG_DISCONNECT 0x04
+#define MSG_INIT_DETECT_ERROR 0x05
+#define MSG_ABORT 0x06
+#define MSG_REJECT 0x07
+#define MSG_NOOP 0x08
+#define MSG_PARITY_ERROR 0x09
+#define MSG_BUS_DEVICE_RESET 0x0C
+#define MSG_IDENTIFY 0x80
+#define MSG_IDENTIFY_DR 0xc0 /* (disconnect/reconnect allowed) */
+#define MSG_SYNC_REQ 0x01
+
+#define MSG_ISIDENTIFY(x) (x&MSG_IDENTIFY)
+#define IFY_TRN 0x20
+#define IFY_LUNTRN(x) (x&0x07)
+#define IFY_LUN(x) (!(x&0x20))
+
+/* Check if high bit set */
+
+#define STS_CHECKCOND 0x02 /* Check Condition (ie., read sense) */
+#define STS_CONDMET 0x04 /* Condition Met (ie., search worked) */
+#define STS_BUSY 0x08
+#define STS_INTERMED 0x10 /* Intermediate status sent */
+#define STS_EXT 0x80 /* Extended status valid */
+
+
+/* States returned by our state machine */
+
+#define SBIC_STATE_ERROR -1
+#define SBIC_STATE_DONE 0
+#define SBIC_STATE_RUNNING 1
+#define SBIC_STATE_DISCONNECT 2
+
+/*
+ * XXXX
+ */
+struct scsi_fmt_cdb {
+ int len; /* cdb length (in bytes) */
+ u_char cdb[28]; /* cdb to use on next read/write */
+};
+
+struct buf;
+struct scsi_xfer;
+
+void sbic_minphys __P((struct buf *bp));
+int sbic_scsicmd __P((struct scsi_xfer *));
+void sbicinit __P((struct sbic_softc *));
+int sbicintr __P((struct sbic_softc *));
+#ifdef DEBUG
+void sbic_dump __P((struct sbic_softc *dev));
+#endif
+
+#endif /* _SBICVAR_H_ */
diff --git a/sys/arch/kbus/dev/sbicvar.h.mvme b/sys/arch/kbus/dev/sbicvar.h.mvme
new file mode 100644
index 00000000000..825f54d957f
--- /dev/null
+++ b/sys/arch/kbus/dev/sbicvar.h.mvme
@@ -0,0 +1,210 @@
+/* $OpenBSD: sbicvar.h.mvme,v 1.1 1997/10/14 07:25:30 gingold Exp $ */
+
+/*
+ * Copyright (c) 1990 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Van Jacobson of Lawrence Berkeley Laboratory.
+ *
+ * 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.
+ *
+ * @(#)scsivar.h 7.1 (Berkeley) 5/8/90
+ */
+#ifndef _SBICVAR_H_
+#define _SBICVAR_H_
+#include <sys/malloc.h>
+
+
+/*
+ * DMA chains are used for Scatter-Gather DMA.
+ */
+struct dma_chain {
+ int dc_count;
+ char *dc_addr;
+};
+
+/*
+ * ACB. Holds additional information for each SCSI command Comments: We
+ * need a separate scsi command block because we may need to overwrite it
+ * with a request sense command. Basicly, we refrain from fiddling with
+ * the scsi_xfer struct (except do the expected updating of return values).
+ * We'll generally update: xs->{flags,resid,error,sense,status} and
+ * occasionally xs->retries.
+ */
+struct sbic_acb {
+ TAILQ_ENTRY(sbic_acb) chain;
+ struct scsi_xfer *xs; /* SCSI xfer ctrl block from above */
+ int flags; /* Status */
+#define ACB_FREE 0x00
+#define ACB_ACTIVE 0x01
+#define ACB_DONE 0x04
+#define ACB_CHKSENSE 0x08
+#define ACB_BBUF 0x10 /* DMA input needs to be copied from bounce */
+#define ACB_DATAIN 0x20 /* DMA direction flag */
+
+ struct scsi_generic cmd; /* SCSI command block */
+ int clen;
+ struct dma_chain sc_kv; /* Virtual address of whole DMA */
+ struct dma_chain sc_pa; /* Physical address of DMA segment */
+ u_long sc_tcnt; /* number of bytes for this DMA */
+ u_short sc_dmacmd; /* Internal data for this DMA */
+ char *pa_addr; /* XXXX initial phys addr */
+};
+
+/*
+ * Some info about each (possible) target on the SCSI bus. This should
+ * probably have been a "per target+lunit" structure, but we'll leave it at
+ * this for now. Is there a way to reliably hook it up to sc->fordriver??
+ */
+struct sbic_tinfo {
+ int cmds; /* #commands processed */
+ int dconns; /* #disconnects */
+ int senses; /* #request sense commands sent */
+ int lubusy; /* What local units/subr. are busy? */
+} tinfo_t;
+
+struct sbic_softc {
+ struct device sc_dev;
+ /* struct intrhand sc_dmaih; */
+ struct intrhand sc_sbicih;
+ struct target_sync {
+ u_char state;
+ u_char period;
+ u_char offset;
+ } sc_sync[8];
+ u_char target; /* Currently active target */
+ u_char lun;
+ struct scsi_link sc_link; /* proto for sub devices */
+ sbic_regmap_p sc_sbicp; /* the SBIC */
+ void *sc_cregs; /* driver specific regs */
+ int sc_ipl;
+
+ /* Lists of command blocks */
+ TAILQ_HEAD(acb_list, sbic_acb) free_list,
+ ready_list,
+ nexus_list;
+
+ struct sbic_acb *sc_nexus; /* current command */
+ struct sbic_acb sc_acb[8]; /* the real command blocks */
+ struct sbic_tinfo sc_tinfo[8];
+
+ struct scsi_xfer *sc_xs; /* transfer from high level code */
+ u_char sc_flags;
+ u_char sc_stat[2];
+ u_char sc_msg[7];
+ u_long sc_clkfreq;
+ u_long sc_tcnt; /* number of bytes transfered */
+ u_short sc_dmacmd; /* used by dma drivers */
+ u_long sc_dmamask; /* dma valid mem mask */
+
+ struct dma_chain *sc_cur;
+ struct dma_chain *sc_last;
+ int (*sc_dmago) __P((struct sbic_softc *, char *, int, int));
+ int (*sc_dmanext) __P((struct sbic_softc *));
+ void (*sc_enintr) __P((struct sbic_softc *));
+ void (*sc_dmastop) __P((struct sbic_softc *));
+#ifdef DEBUG
+ u_short sc_dmatimo; /* dma timeout */
+#endif
+};
+
+/*
+ * sc_flags
+ */
+#define SBICF_ALIVE 0x01 /* controller initialized */
+#define SBICF_DCFLUSH 0x02 /* need flush for overlap after dma finishes */
+#define SBICF_SELECTED 0x04 /* bus is in selected state. */
+#define SBICF_ICMD 0x08 /* Immediate command in execution */
+#define SBICF_BADDMA 0x10 /* controller can only DMA to ztwobus space */
+#define SBICF_INTR 0x40 /* SBICF interrupt expected */
+#define SBICF_INDMA 0x80 /* not used yet, DMA I/O in progress */
+
+/*
+ * sync states
+ */
+#define SYNC_START 0 /* no sync handshake started */
+#define SYNC_SENT 1 /* we sent sync request, no answer yet */
+#define SYNC_DONE 2 /* target accepted our (or inferior) settings,
+ or it rejected the request and we stay async */
+
+#define PHASE 0x07 /* mask for psns/pctl phase */
+#define DATA_OUT_PHASE 0x00
+#define DATA_IN_PHASE 0x01
+#define CMD_PHASE 0x02
+#define STATUS_PHASE 0x03
+#define BUS_FREE_PHASE 0x04
+#define ARB_SEL_PHASE 0x05 /* Fuji chip combines arbitration with sel. */
+#define MESG_OUT_PHASE 0x06
+#define MESG_IN_PHASE 0x07
+
+#define MSG_CMD_COMPLETE 0x00
+#define MSG_EXT_MESSAGE 0x01
+#define MSG_SAVE_DATA_PTR 0x02
+#define MSG_RESTORE_PTR 0x03
+#define MSG_DISCONNECT 0x04
+#define MSG_INIT_DETECT_ERROR 0x05
+#define MSG_ABORT 0x06
+#define MSG_REJECT 0x07
+#define MSG_NOOP 0x08
+#define MSG_PARITY_ERROR 0x09
+#define MSG_BUS_DEVICE_RESET 0x0C
+#define MSG_IDENTIFY 0x80
+#define MSG_IDENTIFY_DR 0xc0 /* (disconnect/reconnect allowed) */
+#define MSG_SYNC_REQ 0x01
+
+#define MSG_ISIDENTIFY(x) ((x) & MSG_IDENTIFY)
+
+
+#define STS_CHECKCOND 0x02 /* Check Condition (ie., read sense) */
+#define STS_CONDMET 0x04 /* Condition Met (ie., search worked) */
+#define STS_BUSY 0x08
+#define STS_INTERMED 0x10 /* Intermediate status sent */
+#define STS_EXT 0x80 /* Extended status valid */
+
+
+/*
+ * States returned by our state machine
+ */
+
+#define SBIC_STATE_ERROR -1
+#define SBIC_STATE_DONE 0
+#define SBIC_STATE_RUNNING 1
+#define SBIC_STATE_DISCONNECT 2
+
+
+struct buf;
+struct scsi_xfer;
+
+void sbic_minphys __P((struct buf *bp));
+int sbic_scsicmd __P((struct scsi_xfer *));
+void sbicinit __P((struct sbic_softc *dev));
+int sbicintr __P((struct sbic_softc *));
+
+#endif /* _SBICVAR_H_ */
diff --git a/sys/arch/kbus/dev/vme.c b/sys/arch/kbus/dev/vme.c
new file mode 100644
index 00000000000..49497e4ee16
--- /dev/null
+++ b/sys/arch/kbus/dev/vme.c
@@ -0,0 +1,321 @@
+/* $OpenBSD: vme.c,v 1.1 1997/10/14 07:25:29 gingold Exp $ */
+/* $NetBSD: vme.c,v 1.6 1996/11/20 18:57:02 gwr Exp $ */
+
+/*-
+ * Copyright (c) 1996 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Gordon W. Ross.
+ *
+ * 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 NetBSD
+ * Foundation, Inc. and its contributors.
+ * 4. Neither the name of The NetBSD Foundation 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 NETBSD FOUNDATION, INC. 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.
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/device.h>
+#include <sys/malloc.h>
+#include <sys/map.h>
+#include <sys/proc.h>
+
+#include <vm/vm.h>
+#include <vm/vm_kern.h>
+
+#include <machine/autoconf.h>
+#include <machine/dvma.h>
+#include <machine/kbus.h>
+/* #include <machine/vme.h> */
+
+static int vme_match __P((struct device *, void *, void *));
+
+static void vme_attach __P((struct device *, struct device *, void *));
+
+struct cfattach vmel_ca = {
+ sizeof(struct device), vme_match, vme_attach
+};
+
+struct cfdriver vmel_cd = {
+ NULL, "vmel", DV_DULL
+};
+
+struct cfattach vmeh_ca = {
+ sizeof(struct device), vme_match, vme_attach
+};
+
+struct cfdriver vmeh_cd = {
+ NULL, "vmeh", DV_DULL
+};
+
+struct cfattach vmes_ca = {
+ sizeof(struct device), vme_match, vme_attach
+};
+
+struct cfdriver vmes_cd = {
+ NULL, "vmes", DV_DULL
+};
+
+struct intrhand **vmeints;
+
+static int
+vme_match(parent, vcf, aux)
+ struct device *parent;
+ void *vcf, *aux;
+{
+ struct confargs *ca = aux;
+
+ if (ca->ca_bustype != BUS_VME32
+ && ca->ca_bustype != BUS_VME24
+ && ca->ca_bustype != BUS_VME16)
+ return 0;
+ return 1;
+}
+
+static char *dvma_map_base;
+static unsigned short *vme_iack;
+
+static void
+vme_attach(parent, self, args)
+ struct device *parent;
+ struct device *self;
+ void *args;
+{
+ printf("\n");
+
+ /* Map the vme address map. */
+ if (!dvma_map_base)
+ {
+ dvma_map_base = bus_mapin (BUS_KBUS, VME_MAP_BASE, DVMA_SIZE);
+ if (!dvma_map_base)
+ panic ("Cannot map vme address map");
+ }
+
+ /* Be sure the vme_iack was set. */
+ if (!vme_iack)
+ {
+ vme_iack = (unsigned short *)
+ bus_mapin (BUS_KBUS, VME_IACK_BASE, VME_IACK_SIZE);
+ if (!vme_iack)
+ panic ("Can't map vme iack page");
+ }
+
+ if (vmeints == NULL) {
+ vmeints = (struct intrhand **)malloc(256 *
+ sizeof(struct intrhand *), M_TEMP, M_NOWAIT);
+ bzero(vmeints, 256 * sizeof(struct intrhand *));
+ }
+
+ /* We know ca_bustype == BUS_VMExx */
+ (void) config_search(bus_scan, self, args);
+}
+
+/*
+ * Wrapper for dvma_mapin() in kernel space,
+ * so drivers need not include VM goo to get at kernel_map.
+ */
+caddr_t
+kdvma_mapin(va, len, canwait)
+ caddr_t va;
+ int len, canwait;
+{
+ return (caddr_t) dvma_mapin(kernel_map, (vm_offset_t)va, len, canwait);
+}
+
+caddr_t
+dvma_malloc(len, kaddr, flags)
+ size_t len;
+ void *kaddr;
+ int flags;
+{
+ vm_offset_t kva;
+ vm_offset_t dva;
+
+ len = round_page(len);
+ kva = (vm_offset_t)malloc(len, M_DEVBUF, flags);
+ if (kva == NULL)
+ return (NULL);
+
+ *(vm_offset_t *)kaddr = kva;
+ dva = dvma_mapin(kernel_map, kva, len, (flags & M_NOWAIT) ? 0 : 1);
+ if (dva == NULL) {
+ free((void *)kva, M_DEVBUF);
+ return (NULL);
+ }
+ return (caddr_t)dva;
+}
+
+void
+dvma_free(dva, len, kaddr)
+ caddr_t dva;
+ size_t len;
+ void *kaddr;
+{
+ vm_offset_t kva = *(vm_offset_t *)kaddr;
+
+ dvma_mapout((vm_offset_t)dva, kva, round_page(len));
+ free((void *)kva, M_DEVBUF);
+}
+
+/*
+ * Map a range [va, va+len] of wired virtual addresses in the given map
+ * to a kernel address in DVMA space.
+ * Return a DVMA address, ie suitable for VME devices.
+ */
+vm_offset_t
+dvma_mapin(map, va, len, canwait)
+ struct vm_map *map;
+ vm_offset_t va;
+ int len, canwait;
+{
+ vm_offset_t kva, tva;
+ register int npf, s;
+ register vm_offset_t pa;
+ long off, pn;
+
+ off = (int)va & PGOFSET;
+ va -= off;
+ len = round_page(len + off);
+ npf = btoc(len);
+
+ s = splimp();
+ for (;;) {
+
+ pn = rmalloc(dvmamap, npf);
+
+ if (pn != 0)
+ break;
+ if (canwait) {
+ (void)tsleep(dvmamap, PRIBIO+1, "physio", 0);
+ continue;
+ }
+ splx(s);
+ return NULL;
+ }
+ splx(s);
+
+ kva = tva = rctov (pn);
+
+ while (npf--) {
+ pa = pmap_extract(vm_map_pmap(map), va);
+ if (pa == 0)
+ panic("dvma_mapin: null page frame");
+ pa = trunc_page(pa);
+
+ *(u_int *)(dvma_map_base + tva) = pa | VME_MAP_V;
+
+ tva += PAGE_SIZE;
+ va += PAGE_SIZE;
+ }
+ return kva + off;
+}
+
+/*
+ * Remove double map of `va' in DVMA space at `kva'.
+ */
+void
+dvma_mapout(kva, va, len)
+ vm_offset_t kva, va;
+ int len;
+{
+ register int s, off;
+ vm_offset_t tva;
+
+ off = (int)kva & PGOFSET;
+ kva -= off;
+ len = round_page(len + off);
+
+ for (tva = kva; tva < kva + len; tva += VME_PAGESIZE)
+ *(u_int *)(dvma_map_base + tva) = VME_MAP_NV;
+
+ s = splimp();
+ rmfree(dvmamap, btoc(len), vtorc(kva));
+ wakeup(dvmamap);
+ splx(s);
+}
+
+static int vmeintr __P((void *arg));
+
+static int
+vmeintr(arg)
+ void *arg;
+{
+ int level = (int)arg, vec;
+ struct intrhand *ih;
+ int i = 0;
+
+ vec = vme_iack[level] & 0xff;
+#if 0
+ printf ("vme intr: level = %d, vec = 0x%x\n", level, vec);
+#endif
+
+ for (ih = vmeints[vec]; ih; ih = ih->ih_next)
+ if (ih->ih_fun)
+ i += (ih->ih_fun)(ih->ih_arg);
+ if (!i)
+ printf ("Stray vme int, level %d, vec 0x%02x\n", level, vec);
+
+ return i;
+}
+
+void
+vmeintr_establish(vec, level, ih)
+ int vec, level;
+ struct intrhand *ih;
+{
+ struct intrhand *ihs;
+
+ if (vec == -1)
+ panic("vmeintr_establish: uninitialized vec\n");
+
+ if (vmeints[vec] == NULL)
+ vmeints[vec] = ih;
+ else {
+ for (ihs = vmeints[vec]; ihs->ih_next; ihs = ihs->ih_next)
+ ;
+ ihs->ih_next = ih;
+ }
+
+ /* Be sure the vme_iack was set. */
+ if (!vme_iack)
+ panic ("vme_iack was not set");
+
+ /* ensure the interrupt subsystem will call us at this level */
+ for (ihs = intrhand[level]; ihs; ihs = ihs->ih_next)
+ if (ihs->ih_fun == vmeintr)
+ return;
+
+ ihs = (struct intrhand *)malloc(sizeof(struct intrhand),
+ M_TEMP, M_NOWAIT);
+ if (ihs == NULL)
+ panic("vme_addirq");
+ bzero(ihs, sizeof *ihs);
+ ihs->ih_fun = vmeintr;
+ ihs->ih_arg = (void *)level;
+ intr_establish(VME_IPL_TO_INTR (level), IH_CAN_DELAY, ihs);
+}
+
diff --git a/sys/arch/kbus/dev/xd.c b/sys/arch/kbus/dev/xd.c
new file mode 100644
index 00000000000..c99e1362cf3
--- /dev/null
+++ b/sys/arch/kbus/dev/xd.c
@@ -0,0 +1,2380 @@
+/* $NetBSD: xd.c,v 1.25 1996/04/22 02:42:06 christos 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.
+ */
+
+/*
+ *
+ * x d . c x y l o g i c s 7 5 3 / 7 0 5 3 v m e / s m d d r i v e r
+ *
+ * author: Chuck Cranor <chuck@ccrc.wustl.edu>
+ * id: $NetBSD: xd.c,v 1.25 1996/04/22 02:42:06 christos Exp $
+ * started: 27-Feb-95
+ * references: [1] Xylogics Model 753 User's Manual
+ * part number: 166-753-001, Revision B, May 21, 1988.
+ * "Your Partner For Performance"
+ * [2] other NetBSD disk device drivers
+ *
+ * Special thanks go to Scott E. Campbell of Xylogics, Inc. for taking
+ * the time to answer some of my questions about the 753/7053.
+ *
+ * note: the 753 and the 7053 are programmed the same way, but are
+ * different sizes. the 753 is a 6U VME card, while the 7053 is a 9U
+ * VME card (found in many VME based suns).
+ */
+
+#undef XDC_DEBUG /* full debug */
+#define XDC_DIAG /* extra sanity checks */
+#if defined(DIAGNOSTIC) && !defined(XDC_DIAG)
+#define XDC_DIAG /* link in with master DIAG option */
+#endif
+
+#include <sys/param.h>
+#include <sys/proc.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/file.h>
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+#include <sys/buf.h>
+#include <sys/uio.h>
+#include <sys/malloc.h>
+#include <sys/device.h>
+#include <sys/disklabel.h>
+#include <sys/disk.h>
+#include <sys/syslog.h>
+#include <sys/dkbad.h>
+#include <sys/conf.h>
+
+#include <vm/vm.h>
+#include <vm/vm_kern.h>
+
+#include <machine/autoconf.h>
+#include <machine/sun_disklabel.h>
+#include <machine/conf.h>
+#include <machine/dvma.h>
+
+#include "xdreg.h"
+#include "xdvar.h"
+#include "xio.h"
+
+
+/*
+ * macros
+ */
+
+/*
+ * XDC_TWAIT: add iorq "N" to tail of SC's wait queue
+ */
+#define XDC_TWAIT(SC, N) { \
+ (SC)->waitq[(SC)->waitend] = (N); \
+ (SC)->waitend = ((SC)->waitend + 1) % XDC_MAXIOPB; \
+ (SC)->nwait++; \
+}
+
+/*
+ * XDC_HWAIT: add iorq "N" to head of SC's wait queue
+ */
+#define XDC_HWAIT(SC, N) { \
+ (SC)->waithead = ((SC)->waithead == 0) ? \
+ (XDC_MAXIOPB - 1) : ((SC)->waithead - 1); \
+ (SC)->waitq[(SC)->waithead] = (N); \
+ (SC)->nwait++; \
+}
+
+/*
+ * XDC_GET_WAITER: gets the first request waiting on the waitq
+ * and removes it (so it can be submitted)
+ */
+#define XDC_GET_WAITER(XDCSC, RQ) { \
+ (RQ) = (XDCSC)->waitq[(XDCSC)->waithead]; \
+ (XDCSC)->waithead = ((XDCSC)->waithead + 1) % XDC_MAXIOPB; \
+ xdcsc->nwait--; \
+}
+
+/*
+ * XDC_FREE: add iorq "N" to SC's free list
+ */
+#define XDC_FREE(SC, N) { \
+ (SC)->freereq[(SC)->nfree++] = (N); \
+ (SC)->reqs[N].mode = 0; \
+ if ((SC)->nfree == 1) wakeup(&(SC)->nfree); \
+}
+
+
+/*
+ * XDC_RQALLOC: allocate an iorq off the free list (assume nfree > 0).
+ */
+#define XDC_RQALLOC(XDCSC) (XDCSC)->freereq[--((XDCSC)->nfree)]
+
+/*
+ * XDC_GO: start iopb ADDR (DVMA addr in a u_long) on XDC
+ */
+#define XDC_GO(XDC, ADDR) { \
+ (XDC)->xdc_iopbaddr0 = ((ADDR) & 0xff); \
+ (ADDR) = ((ADDR) >> 8); \
+ (XDC)->xdc_iopbaddr1 = ((ADDR) & 0xff); \
+ (ADDR) = ((ADDR) >> 8); \
+ (XDC)->xdc_iopbaddr2 = ((ADDR) & 0xff); \
+ (ADDR) = ((ADDR) >> 8); \
+ (XDC)->xdc_iopbaddr3 = (ADDR); \
+ (XDC)->xdc_iopbamod = XDC_ADDRMOD; \
+ (XDC)->xdc_csr = XDC_ADDIOPB; /* go! */ \
+}
+
+/*
+ * XDC_WAIT: wait for XDC's csr "BITS" to come on in "TIME".
+ * LCV is a counter. If it goes to zero then we timed out.
+ */
+#define XDC_WAIT(XDC, LCV, TIME, BITS) { \
+ (LCV) = (TIME); \
+ while ((LCV) > 0) { \
+ if ((XDC)->xdc_csr & (BITS)) break; \
+ (LCV) = (LCV) - 1; \
+ DELAY(1); \
+ } \
+}
+
+/*
+ * XDC_DONE: don't need IORQ, get error code and free (done after xdc_cmd)
+ */
+#define XDC_DONE(SC,RQ,ER) { \
+ if ((RQ) == XD_ERR_FAIL) { \
+ (ER) = (RQ); \
+ } else { \
+ if ((SC)->ndone-- == XDC_SUBWAITLIM) \
+ wakeup(&(SC)->ndone); \
+ (ER) = (SC)->reqs[RQ].errno; \
+ XDC_FREE((SC), (RQ)); \
+ } \
+}
+
+/*
+ * XDC_ADVANCE: advance iorq's pointers by a number of sectors
+ */
+#define XDC_ADVANCE(IORQ, N) { \
+ if (N) { \
+ (IORQ)->sectcnt -= (N); \
+ (IORQ)->blockno += (N); \
+ (IORQ)->dbuf += ((N)*XDFM_BPS); \
+ } \
+}
+
+/*
+ * note - addresses you can sleep on:
+ * [1] & of xd_softc's "state" (waiting for a chance to attach a drive)
+ * [2] & of xdc_softc's "nfree" (waiting for a free iorq/iopb)
+ * [3] & of xdc_softc's "ndone" (waiting for number of done iorq/iopb's
+ * to drop below XDC_SUBWAITLIM)
+ * [4] & an iorq (waiting for an XD_SUB_WAIT iorq to finish)
+ */
+
+
+/*
+ * function prototypes
+ * "xdc_*" functions are internal, all others are external interfaces
+ */
+
+/* internals */
+int xdc_cmd __P((struct xdc_softc *, int, int, int, int, int, char *, int));
+char *xdc_e2str __P((int));
+int xdc_error __P((struct xdc_softc *, struct xd_iorq *,
+ struct xd_iopb *, int, int));
+int xdc_ioctlcmd __P((struct xd_softc *, dev_t dev, struct xd_iocmd *));
+void xdc_perror __P((struct xd_iorq *, struct xd_iopb *, int));
+int xdc_piodriver __P((struct xdc_softc *, int, int));
+int xdc_remove_iorq __P((struct xdc_softc *));
+int xdc_reset __P((struct xdc_softc *, int, int, int, struct xd_softc *));
+inline void xdc_rqinit __P((struct xd_iorq *, struct xdc_softc *,
+ struct xd_softc *, int, u_long, int,
+ caddr_t, struct buf *));
+void xdc_rqtopb __P((struct xd_iorq *, struct xd_iopb *, int, int));
+void xdc_start __P((struct xdc_softc *, int));
+int xdc_startbuf __P((struct xdc_softc *, struct xd_softc *, struct buf *));
+int xdc_submit_iorq __P((struct xdc_softc *, int, int));
+void xdc_tick __P((void *));
+void xdc_xdreset __P((struct xdc_softc *, struct xd_softc *));
+
+/* machine interrupt hook */
+int xdcintr __P((void *));
+
+/* autoconf */
+int xdcmatch __P((struct device *, void *, void *));
+void xdcattach __P((struct device *, struct device *, void *));
+int xdmatch __P((struct device *, void *, void *));
+void xdattach __P((struct device *, struct device *, void *));
+
+static void xddummystrat __P((struct buf *));
+int xdgetdisklabel __P((struct xd_softc *, void *));
+
+/*
+ * cfdrivers: device driver interface to autoconfig
+ */
+
+struct cfattach xdc_ca = {
+ sizeof(struct xdc_softc), xdcmatch, xdcattach
+};
+
+
+struct cfdriver xdc_cd = {
+ NULL, "xdc", DV_DULL
+};
+
+struct cfattach xd_ca = {
+ sizeof(struct xd_softc), xdmatch, xdattach
+};
+
+struct cfdriver xd_cd = {
+ NULL, "xd", DV_DISK
+};
+
+struct xdc_attach_args { /* this is the "aux" args to xdattach */
+ int driveno; /* unit number */
+ char *buf; /* scratch buffer for reading disk label */
+ char *dvmabuf; /* DVMA address of above */
+ int fullmode; /* submit mode */
+ int booting; /* are we booting or not? */
+};
+
+/*
+ * dkdriver
+ */
+
+struct dkdriver xddkdriver = {xdstrategy};
+
+/*
+ * start: disk label fix code (XXX)
+ */
+
+static void *xd_labeldata;
+
+static void
+xddummystrat(bp)
+ struct buf *bp;
+{
+ if (bp->b_bcount != XDFM_BPS)
+ panic("xddummystrat");
+ bcopy(xd_labeldata, bp->b_un.b_addr, XDFM_BPS);
+ bp->b_flags |= B_DONE;
+ bp->b_flags &= ~B_BUSY;
+}
+
+int
+xdgetdisklabel(xd, b)
+ struct xd_softc *xd;
+ void *b;
+{
+ char *err;
+ struct sun_disklabel *sdl;
+
+ /* We already have the label data in `b'; setup for dummy strategy */
+ xd_labeldata = b;
+
+ /* Required parameter for readdisklabel() */
+ xd->sc_dk.dk_label->d_secsize = XDFM_BPS;
+
+ err = readdisklabel(MAKEDISKDEV(0, xd->sc_dev.dv_unit, RAW_PART),
+ xddummystrat,
+ xd->sc_dk.dk_label, xd->sc_dk.dk_cpulabel);
+ if (err) {
+ printf("%s: %s\n", xd->sc_dev.dv_xname, err);
+ return(XD_ERR_FAIL);
+ }
+
+ /* Ok, we have the label; fill in `pcyl' if there's SunOS magic */
+ sdl = (struct sun_disklabel *)xd->sc_dk.dk_cpulabel->cd_block;
+ if (sdl->sl_magic == SUN_DKMAGIC)
+ xd->pcyl = sdl->sl_pcylinders;
+ else {
+ printf("%s: WARNING: no `pcyl' in disk label.\n",
+ xd->sc_dev.dv_xname);
+ xd->pcyl = xd->sc_dk.dk_label->d_ncylinders +
+ xd->sc_dk.dk_label->d_acylinders;
+ printf("%s: WARNING: guessing pcyl=%d (ncyl+acyl)\n",
+ xd->sc_dev.dv_xname, xd->pcyl);
+ }
+
+ xd->ncyl = xd->sc_dk.dk_label->d_ncylinders;
+ xd->acyl = xd->sc_dk.dk_label->d_acylinders;
+ xd->nhead = xd->sc_dk.dk_label->d_ntracks;
+ xd->nsect = xd->sc_dk.dk_label->d_nsectors;
+ xd->sectpercyl = xd->nhead * xd->nsect;
+ xd->sc_dk.dk_label->d_secsize = XDFM_BPS; /* not handled by
+ * sun->bsd */
+ return(XD_ERR_AOK);
+}
+
+/*
+ * end: disk label fix code (XXX)
+ */
+
+/*
+ * a u t o c o n f i g f u n c t i o n s
+ */
+
+/*
+ * xdcmatch: determine if xdc is present or not. we do a
+ * soft reset to detect the xdc.
+ */
+
+int xdcmatch(parent, match, aux)
+ struct device *parent;
+ void *match, *aux;
+{
+ struct confargs *ca = aux;
+ int x;
+
+ if (ca->ca_bustype != BUS_VME16)
+ return (0);
+
+ /* Default interrupt priority always splbio==2 */
+ if (ca->ca_intpri == -1)
+ ca->ca_intpri = 2;
+
+ x = bus_peek(ca->ca_bustype, ca->ca_paddr + 11, 1);
+ if (x == -1)
+ return (0);
+
+ return (1);
+}
+
+/*
+ * xdcattach: attach controller
+ */
+void
+xdcattach(parent, self, aux)
+ struct device *parent, *self;
+ void *aux;
+
+{
+ struct xdc_softc *xdc = (void *) self;
+ struct confargs *ca = aux;
+ struct xdc_attach_args xa;
+ int lcv, rqno, err;
+ struct xd_iopb_ctrl *ctl;
+
+ /* get addressing and intr level stuff from autoconfig and load it
+ * into our xdc_softc. */
+
+ xdc->xdc = (struct xdc *)
+ bus_mapin(ca->ca_bustype, ca->ca_paddr, sizeof(struct xdc));
+ xdc->ipl = ca->ca_intpri;
+ xdc->vector = ca->ca_intvec;
+ printf("\n%s", xdc->sc_dev.dv_xname);
+
+ for (lcv = 0; lcv < XDC_MAXDEV; lcv++)
+ xdc->sc_drives[lcv] = (struct xd_softc *) 0;
+
+ /* allocate and zero buffers
+ *
+ * note: we simplify the code by allocating the max number of iopbs and
+ * iorq's up front. thus, we avoid linked lists and the costs
+ * associated with them in exchange for wasting a little memory. */
+
+ xdc->dvmaiopb = (struct xd_iopb *)
+ dvma_malloc(XDC_MAXIOPB * sizeof(struct xd_iopb), &xdc->iopbase,
+ M_NOWAIT);
+ bzero(xdc->iopbase, XDC_MAXIOPB * sizeof(struct xd_iopb));
+
+ xdc->reqs = (struct xd_iorq *)
+ malloc(XDC_MAXIOPB * sizeof(struct xd_iorq), M_DEVBUF, M_NOWAIT);
+ if (xdc->reqs == NULL)
+ panic("xdc malloc");
+ bzero(xdc->reqs, XDC_MAXIOPB * sizeof(struct xd_iorq));
+
+ /* init free list, iorq to iopb pointers, and non-zero fields in the
+ * iopb which never change. */
+
+ for (lcv = 0; lcv < XDC_MAXIOPB; lcv++) {
+ xdc->reqs[lcv].iopb = &xdc->iopbase[lcv];
+ xdc->freereq[lcv] = lcv;
+ xdc->iopbase[lcv].fixd = 1; /* always the same */
+ xdc->iopbase[lcv].naddrmod = XDC_ADDRMOD; /* always the same */
+ xdc->iopbase[lcv].intr_vec = xdc->vector; /* always the same */
+ }
+ xdc->nfree = XDC_MAXIOPB;
+ xdc->nrun = 0;
+ xdc->waithead = xdc->waitend = xdc->nwait = 0;
+ xdc->ndone = 0;
+
+ /* init queue of waiting bufs */
+
+ xdc->sc_wq.b_active = 0;
+ xdc->sc_wq.b_actf = 0;
+ xdc->sc_wq.b_actb = &xdc->sc_wq.b_actf;
+
+ /*
+ * section 7 of the manual tells us how to init the controller:
+ * - read controller parameters (6/0)
+ * - write controller parameters (5/0)
+ */
+
+ /* read controller parameters and insure we have a 753/7053 */
+
+ rqno = xdc_cmd(xdc, XDCMD_RDP, XDFUN_CTL, 0, 0, 0, 0, XD_SUB_POLL);
+ if (rqno == XD_ERR_FAIL) {
+ printf(": couldn't read controller params\n");
+ return; /* shouldn't ever happen */
+ }
+ ctl = (struct xd_iopb_ctrl *) & xdc->iopbase[rqno];
+ if (ctl->ctype != XDCT_753) {
+ if (xdc->reqs[rqno].errno)
+ printf(": %s: ", xdc_e2str(xdc->reqs[rqno].errno));
+ printf(": doesn't identify as a 753/7053\n");
+ XDC_DONE(xdc, rqno, err);
+ return;
+ }
+ printf(": Xylogics 753/7053, PROM=%x.%02x.%02x\n",
+ ctl->eprom_partno, ctl->eprom_lvl, ctl->eprom_rev);
+ XDC_DONE(xdc, rqno, err);
+
+ /* now write controller parameters (xdc_cmd sets all params for us) */
+
+ rqno = xdc_cmd(xdc, XDCMD_WRP, XDFUN_CTL, 0, 0, 0, 0, XD_SUB_POLL);
+ XDC_DONE(xdc, rqno, err);
+ if (err) {
+ printf("%s: controller config error: %s\n",
+ xdc->sc_dev.dv_xname, xdc_e2str(err));
+ return;
+ }
+ /* link in interrupt with higher level software */
+
+ xdc->sc_ih.ih_fun = xdcintr;
+ xdc->sc_ih.ih_arg = xdc;
+ vmeintr_establish(ca->ca_intvec, ca->ca_intpri, &xdc->sc_ih);
+ evcnt_attach(&xdc->sc_dev, "intr", &xdc->sc_intrcnt);
+
+
+ /* now we must look for disks using autoconfig */
+ xa.dvmabuf = (char *)dvma_malloc(XDFM_BPS, &xa.buf, M_NOWAIT);
+ xa.fullmode = XD_SUB_POLL;
+ xa.booting = 1;
+
+ for (xa.driveno = 0; xa.driveno < XDC_MAXDEV; xa.driveno++)
+ (void) config_found(self, (void *) &xa, NULL);
+
+ dvma_free(xa.dvmabuf, XDFM_BPS, &xa.buf);
+
+ /* start the watchdog clock */
+ timeout(xdc_tick, xdc, XDC_TICKCNT);
+
+}
+
+/*
+ * xdmatch: probe for disk.
+ *
+ * note: we almost always say disk is present. this allows us to
+ * spin up and configure a disk after the system is booted (we can
+ * call xdattach!).
+ */
+int
+xdmatch(parent, match, aux)
+ struct device *parent;
+ void *match, *aux;
+
+{
+ struct cfdata *cf = match;
+ struct xdc_attach_args *xa = aux;
+
+ /* looking for autoconf wildcard or exact match */
+
+ if (cf->cf_loc[0] != -1 && cf->cf_loc[0] != xa->driveno)
+ return 0;
+
+ return 1;
+
+}
+
+/*
+ * xdattach: attach a disk. this can be called from autoconf and also
+ * from xdopen/xdstrategy.
+ */
+void
+xdattach(parent, self, aux)
+ struct device *parent, *self;
+ void *aux;
+
+{
+ struct xd_softc *xd = (void *) self;
+ struct xdc_softc *xdc = (void *) parent;
+ struct xdc_attach_args *xa = aux;
+ int rqno, err, spt = 0, mb, blk, lcv, fmode, s = 0, newstate;
+ struct xd_iopb_drive *driopb;
+ struct dkbad *dkb;
+
+ /*
+ * Always re-initialize the disk structure. We want statistics
+ * to start with a clean slate.
+ */
+ bzero(&xd->sc_dk, sizeof(xd->sc_dk));
+ xd->sc_dk.dk_driver = &xddkdriver;
+ xd->sc_dk.dk_name = xd->sc_dev.dv_xname;
+
+ /* if booting, init the xd_softc */
+
+ if (xa->booting) {
+ xd->state = XD_DRIVE_UNKNOWN; /* to start */
+ xd->flags = 0;
+ xd->parent = xdc;
+ }
+ xd->xd_drive = xa->driveno;
+ fmode = xa->fullmode;
+ xdc->sc_drives[xa->driveno] = xd;
+
+ /* if not booting, make sure we are the only process in the attach for
+ * this drive. if locked out, sleep on it. */
+
+ if (!xa->booting) {
+ s = splbio();
+ while (xd->state == XD_DRIVE_ATTACHING) {
+ if (tsleep(&xd->state, PRIBIO, "xdattach", 0)) {
+ splx(s);
+ return;
+ }
+ }
+ printf("%s at %s",
+ xd->sc_dev.dv_xname, xd->parent->sc_dev.dv_xname);
+ }
+ /* we now have control */
+
+ xd->state = XD_DRIVE_ATTACHING;
+ newstate = XD_DRIVE_UNKNOWN;
+
+ /* first try and reset the drive */
+
+ rqno = xdc_cmd(xdc, XDCMD_RST, 0, xd->xd_drive, 0, 0, 0, fmode);
+ XDC_DONE(xdc, rqno, err);
+ if (err == XD_ERR_NRDY) {
+ printf(" drive %d: off-line\n", xa->driveno);
+ goto done;
+ }
+ if (err) {
+ printf(": ERROR 0x%02x (%s)\n", err, xdc_e2str(err));
+ goto done;
+ }
+ printf(" drive %d: ready\n", xa->driveno);
+
+ /* now set format parameters */
+
+ rqno = xdc_cmd(xdc, XDCMD_WRP, XDFUN_FMT, xd->xd_drive, 0, 0, 0, fmode);
+ XDC_DONE(xdc, rqno, err);
+ if (err) {
+ printf("%s: write format parameters failed: %s\n",
+ xd->sc_dev.dv_xname, xdc_e2str(err));
+ goto done;
+ }
+
+ /* get drive parameters */
+ rqno = xdc_cmd(xdc, XDCMD_RDP, XDFUN_DRV, xd->xd_drive, 0, 0, 0, fmode);
+ if (rqno != XD_ERR_FAIL) {
+ driopb = (struct xd_iopb_drive *) & xdc->iopbase[rqno];
+ spt = driopb->sectpertrk;
+ }
+ XDC_DONE(xdc, rqno, err);
+ if (err) {
+ printf("%s: read drive parameters failed: %s\n",
+ xd->sc_dev.dv_xname, xdc_e2str(err));
+ goto done;
+ }
+
+ /*
+ * now set drive parameters (to semi-bogus values) so we can read the
+ * disk label.
+ */
+ xd->pcyl = xd->ncyl = 1;
+ xd->acyl = 0;
+ xd->nhead = 1;
+ xd->nsect = 1;
+ xd->sectpercyl = 1;
+ for (lcv = 0; lcv < 126; lcv++) /* init empty bad144 table */
+ xd->dkb.bt_bad[lcv].bt_cyl = xd->dkb.bt_bad[lcv].bt_trksec = 0xffff;
+ rqno = xdc_cmd(xdc, XDCMD_WRP, XDFUN_DRV, xd->xd_drive, 0, 0, 0, fmode);
+ XDC_DONE(xdc, rqno, err);
+ if (err) {
+ printf("%s: write drive parameters failed: %s\n",
+ xd->sc_dev.dv_xname, xdc_e2str(err));
+ goto done;
+ }
+
+ /* read disk label */
+ rqno = xdc_cmd(xdc, XDCMD_RD, 0, xd->xd_drive, 0, 1, xa->dvmabuf, fmode);
+ XDC_DONE(xdc, rqno, err);
+ if (err) {
+ printf("%s: reading disk label failed: %s\n",
+ xd->sc_dev.dv_xname, xdc_e2str(err));
+ goto done;
+ }
+ newstate = XD_DRIVE_NOLABEL;
+
+ xd->hw_spt = spt;
+ /* Attach the disk: must be before getdisklabel to malloc label */
+ disk_attach(&xd->sc_dk);
+
+ if (xdgetdisklabel(xd, xa->buf) != XD_ERR_AOK)
+ goto done;
+
+ /* inform the user of what is up */
+ printf("%s: <%s>, pcyl %d, hw_spt %d\n", xd->sc_dev.dv_xname,
+ xa->buf, xd->pcyl, spt);
+ mb = xd->ncyl * (xd->nhead * xd->nsect) / (1048576 / XDFM_BPS);
+ printf("%s: %dMB, %d cyl, %d head, %d sec, %d bytes/sec\n",
+ xd->sc_dev.dv_xname, mb, xd->ncyl, xd->nhead, xd->nsect,
+ XDFM_BPS);
+
+ /* now set the real drive parameters! */
+
+ rqno = xdc_cmd(xdc, XDCMD_WRP, XDFUN_DRV, xd->xd_drive, 0, 0, 0, fmode);
+ XDC_DONE(xdc, rqno, err);
+ if (err) {
+ printf("%s: write real drive parameters failed: %s\n",
+ xd->sc_dev.dv_xname, xdc_e2str(err));
+ goto done;
+ }
+ newstate = XD_DRIVE_ONLINE;
+
+ /*
+ * read bad144 table. this table resides on the first sector of the
+ * last track of the disk (i.e. second cyl of "acyl" area).
+ */
+
+ blk = (xd->ncyl + xd->acyl - 1) * (xd->nhead * xd->nsect) + /* last cyl */
+ (xd->nhead - 1) * xd->nsect; /* last head */
+ rqno = xdc_cmd(xdc, XDCMD_RD, 0, xd->xd_drive, blk, 1, xa->dvmabuf, fmode);
+ XDC_DONE(xdc, rqno, err);
+ if (err) {
+ printf("%s: reading bad144 failed: %s\n",
+ xd->sc_dev.dv_xname, xdc_e2str(err));
+ goto done;
+ }
+
+ /* check dkbad for sanity */
+ dkb = (struct dkbad *) xa->buf;
+ for (lcv = 0; lcv < 126; lcv++) {
+ if ((dkb->bt_bad[lcv].bt_cyl == 0xffff ||
+ dkb->bt_bad[lcv].bt_cyl == 0) &&
+ dkb->bt_bad[lcv].bt_trksec == 0xffff)
+ continue; /* blank */
+ if (dkb->bt_bad[lcv].bt_cyl >= xd->ncyl)
+ break;
+ if ((dkb->bt_bad[lcv].bt_trksec >> 8) >= xd->nhead)
+ break;
+ if ((dkb->bt_bad[lcv].bt_trksec & 0xff) >= xd->nsect)
+ break;
+ }
+ if (lcv != 126) {
+ printf("%s: warning: invalid bad144 sector!\n",
+ xd->sc_dev.dv_xname);
+ } else {
+ bcopy(xa->buf, &xd->dkb, XDFM_BPS);
+ }
+
+ /* XXX - Where is this and what does it do? -gwr */
+ dk_establish(&xd->sc_dk, &xd->sc_dev); /* XXX */
+
+done:
+ xd->state = newstate;
+ if (!xa->booting) {
+ wakeup(&xd->state);
+ splx(s);
+ }
+}
+
+/*
+ * end of autoconfig functions
+ */
+
+/*
+ * { b , c } d e v s w f u n c t i o n s
+ */
+
+/*
+ * xdclose: close device
+ */
+int
+xdclose(dev, flag, fmt, p)
+ dev_t dev;
+ int flag, fmt;
+ struct proc *p;
+{
+ struct xd_softc *xd = xd_cd.cd_devs[DISKUNIT(dev)];
+ int part = DISKPART(dev);
+
+ /* clear mask bits */
+
+ switch (fmt) {
+ case S_IFCHR:
+ xd->sc_dk.dk_copenmask &= ~(1 << part);
+ break;
+ case S_IFBLK:
+ xd->sc_dk.dk_bopenmask &= ~(1 << part);
+ break;
+ }
+ xd->sc_dk.dk_openmask = xd->sc_dk.dk_copenmask | xd->sc_dk.dk_bopenmask;
+
+ return 0;
+}
+
+/*
+ * xddump: crash dump system
+ */
+int
+xddump(dev, blkno, va, size)
+ dev_t dev;
+ daddr_t blkno;
+ caddr_t va;
+ size_t size;
+{
+ int unit, part;
+ struct xd_softc *xd;
+
+ unit = DISKUNIT(dev);
+ if (unit >= xd_cd.cd_ndevs)
+ return ENXIO;
+ part = DISKPART(dev);
+
+ xd = xd_cd.cd_devs[unit];
+
+ printf("%s%c: crash dump not supported (yet)\n", xd->sc_dev.dv_xname,
+ 'a' + part);
+
+ return ENXIO;
+
+ /* outline: globals: "dumplo" == sector number of partition to start
+ * dump at (convert to physical sector with partition table)
+ * "dumpsize" == size of dump in clicks "physmem" == size of physical
+ * memory (clicks, ctob() to get bytes) (normal case: dumpsize ==
+ * physmem)
+ *
+ * dump a copy of physical memory to the dump device starting at sector
+ * "dumplo" in the swap partition (make sure > 0). map in pages as
+ * we go. use polled I/O.
+ *
+ * XXX how to handle NON_CONTIG? */
+
+}
+
+/*
+ * xdioctl: ioctls on XD drives. based on ioctl's of other netbsd disks.
+ */
+int
+xdioctl(dev, command, addr, flag, p)
+ dev_t dev;
+ u_long command;
+ caddr_t addr;
+ int flag;
+ struct proc *p;
+
+{
+ struct xd_softc *xd;
+ struct xd_iocmd *xio;
+ int error, s, unit;
+
+ unit = DISKUNIT(dev);
+
+ if (unit >= xd_cd.cd_ndevs || (xd = xd_cd.cd_devs[unit]) == NULL)
+ return (ENXIO);
+
+ /* switch on ioctl type */
+
+ switch (command) {
+ case DIOCSBAD: /* set bad144 info */
+ if ((flag & FWRITE) == 0)
+ return EBADF;
+ s = splbio();
+ bcopy(addr, &xd->dkb, sizeof(xd->dkb));
+ splx(s);
+ return 0;
+
+ case DIOCGDINFO: /* get disk label */
+ bcopy(xd->sc_dk.dk_label, addr, sizeof(struct disklabel));
+ return 0;
+
+ case DIOCGPART: /* get partition info */
+ ((struct partinfo *) addr)->disklab = xd->sc_dk.dk_label;
+ ((struct partinfo *) addr)->part =
+ &xd->sc_dk.dk_label->d_partitions[DISKPART(dev)];
+ return 0;
+
+ case DIOCSDINFO: /* set disk label */
+ if ((flag & FWRITE) == 0)
+ return EBADF;
+ error = setdisklabel(xd->sc_dk.dk_label,
+ (struct disklabel *) addr, /* xd->sc_dk.dk_openmask : */ 0,
+ xd->sc_dk.dk_cpulabel);
+ if (error == 0) {
+ if (xd->state == XD_DRIVE_NOLABEL)
+ xd->state = XD_DRIVE_ONLINE;
+ }
+ return error;
+
+ case DIOCWLABEL: /* change write status of disk label */
+ if ((flag & FWRITE) == 0)
+ return EBADF;
+ if (*(int *) addr)
+ xd->flags |= XD_WLABEL;
+ else
+ xd->flags &= ~XD_WLABEL;
+ return 0;
+
+ case DIOCWDINFO: /* write disk label */
+ if ((flag & FWRITE) == 0)
+ return EBADF;
+ error = setdisklabel(xd->sc_dk.dk_label,
+ (struct disklabel *) addr, /* xd->sc_dk.dk_openmask : */ 0,
+ xd->sc_dk.dk_cpulabel);
+ if (error == 0) {
+ if (xd->state == XD_DRIVE_NOLABEL)
+ xd->state = XD_DRIVE_ONLINE;
+
+ /* Simulate opening partition 0 so write succeeds. */
+ xd->sc_dk.dk_openmask |= (1 << 0);
+ error = writedisklabel(MAKEDISKDEV(major(dev), DISKUNIT(dev), RAW_PART),
+ xdstrategy, xd->sc_dk.dk_label,
+ xd->sc_dk.dk_cpulabel);
+ xd->sc_dk.dk_openmask =
+ xd->sc_dk.dk_copenmask | xd->sc_dk.dk_bopenmask;
+ }
+ return error;
+
+ case DIOSXDCMD:
+ xio = (struct xd_iocmd *) addr;
+ if ((error = suser(p->p_ucred, &p->p_acflag)) != 0)
+ return (error);
+ return (xdc_ioctlcmd(xd, dev, xio));
+
+ default:
+ return ENOTTY;
+ }
+}
+/*
+ * xdopen: open drive
+ */
+
+int
+xdopen(dev, flag, fmt, p)
+ dev_t dev;
+ int flag, fmt;
+ struct proc *p;
+{
+ int unit, part;
+ struct xd_softc *xd;
+ struct xdc_attach_args xa;
+
+ /* first, could it be a valid target? */
+
+ unit = DISKUNIT(dev);
+ if (unit >= xd_cd.cd_ndevs || (xd = xd_cd.cd_devs[unit]) == NULL)
+ return (ENXIO);
+ part = DISKPART(dev);
+
+ /* do we need to attach the drive? */
+
+ if (xd->state == XD_DRIVE_UNKNOWN) {
+ xa.driveno = xd->xd_drive;
+ xa.dvmabuf = (char *)dvma_malloc(XDFM_BPS, &xa.buf, M_NOWAIT);
+ xa.fullmode = XD_SUB_WAIT;
+ xa.booting = 0;
+ xdattach((struct device *) xd->parent, (struct device *) xd, &xa);
+ dvma_free(xa.dvmabuf, XDFM_BPS, &xa.buf);
+ if (xd->state == XD_DRIVE_UNKNOWN) {
+ return (EIO);
+ }
+ }
+ /* check for partition */
+
+ if (part != RAW_PART &&
+ (part >= xd->sc_dk.dk_label->d_npartitions ||
+ xd->sc_dk.dk_label->d_partitions[part].p_fstype == FS_UNUSED)) {
+ return (ENXIO);
+ }
+ /* set open masks */
+
+ switch (fmt) {
+ case S_IFCHR:
+ xd->sc_dk.dk_copenmask |= (1 << part);
+ break;
+ case S_IFBLK:
+ xd->sc_dk.dk_bopenmask |= (1 << part);
+ break;
+ }
+ xd->sc_dk.dk_openmask = xd->sc_dk.dk_copenmask | xd->sc_dk.dk_bopenmask;
+
+ return 0;
+}
+
+int
+xdread(dev, uio, flags)
+ dev_t dev;
+ struct uio *uio;
+ int flags;
+{
+
+ return (physio(xdstrategy, NULL, dev, B_READ, minphys, uio));
+}
+
+int
+xdwrite(dev, uio, flags)
+ dev_t dev;
+ struct uio *uio;
+ int flags;
+{
+
+ return (physio(xdstrategy, NULL, dev, B_WRITE, minphys, uio));
+}
+
+
+/*
+ * xdsize: return size of a partition for a dump
+ */
+
+int
+xdsize(dev)
+ dev_t dev;
+
+{
+ struct xd_softc *xdsc;
+ int part, size;
+
+ /* valid unit? try an open */
+
+ if (xdopen(dev, 0, S_IFBLK, NULL) != 0)
+ return (-1);
+
+ /* do it */
+
+ xdsc = xd_cd.cd_devs[DISKUNIT(dev)];
+ part = DISKPART(dev);
+ if (xdsc->sc_dk.dk_label->d_partitions[part].p_fstype != FS_SWAP)
+ size = -1; /* only give valid size for swap partitions */
+ else
+ size = xdsc->sc_dk.dk_label->d_partitions[part].p_size;
+ if (xdclose(dev, 0, S_IFBLK, NULL) != 0)
+ return -1;
+ return size;
+}
+/*
+ * xdstrategy: buffering system interface to xd.
+ */
+
+void
+xdstrategy(bp)
+ struct buf *bp;
+
+{
+ struct xd_softc *xd;
+ struct xdc_softc *parent;
+ struct buf *wq;
+ int s, unit;
+ struct xdc_attach_args xa;
+
+ unit = DISKUNIT(bp->b_dev);
+
+ /* check for live device */
+
+ if (unit >= xd_cd.cd_ndevs || (xd = xd_cd.cd_devs[unit]) == 0 ||
+ bp->b_blkno < 0 ||
+ (bp->b_bcount % xd->sc_dk.dk_label->d_secsize) != 0) {
+ bp->b_error = EINVAL;
+ goto bad;
+ }
+ /* do we need to attach the drive? */
+
+ if (xd->state == XD_DRIVE_UNKNOWN) {
+ xa.driveno = xd->xd_drive;
+ xa.dvmabuf = (char *)dvma_malloc(XDFM_BPS, &xa.buf, M_NOWAIT);
+ xa.fullmode = XD_SUB_WAIT;
+ xa.booting = 0;
+ xdattach((struct device *)xd->parent, (struct device *)xd, &xa);
+ dvma_free(xa.dvmabuf, XDFM_BPS, &xa.buf);
+ if (xd->state == XD_DRIVE_UNKNOWN) {
+ bp->b_error = EIO;
+ goto bad;
+ }
+ }
+ if (xd->state != XD_DRIVE_ONLINE && DISKPART(bp->b_dev) != RAW_PART) {
+ /* no I/O to unlabeled disks, unless raw partition */
+ bp->b_error = EIO;
+ goto bad;
+ }
+ /* short circuit zero length request */
+
+ if (bp->b_bcount == 0)
+ goto done;
+
+ /* check bounds with label (disksubr.c). Determine the size of the
+ * transfer, and make sure it is within the boundaries of the
+ * partition. Adjust transfer if needed, and signal errors or early
+ * completion. */
+
+ if (bounds_check_with_label(bp, xd->sc_dk.dk_label,
+ (xd->flags & XD_WLABEL) != 0) <= 0)
+ goto done;
+
+ /*
+ * now we know we have a valid buf structure that we need to do I/O
+ * on.
+ *
+ * note that we don't disksort because the controller has a sorting
+ * algorithm built into the hardware.
+ */
+
+ s = splbio(); /* protect the queues */
+
+ /* first, give jobs in front of us a chance */
+ parent = xd->parent;
+ while (parent->nfree > 0 && parent->sc_wq.b_actf)
+ if (xdc_startbuf(parent, NULL, NULL) != XD_ERR_AOK)
+ break;
+
+ /* if there are no free iorq's, then we just queue and return. the
+ * buffs will get picked up later by xdcintr().
+ */
+
+ if (parent->nfree == 0) {
+ wq = &xd->parent->sc_wq;
+ bp->b_actf = 0;
+ bp->b_actb = wq->b_actb;
+ *wq->b_actb = bp;
+ wq->b_actb = &bp->b_actf;
+ splx(s);
+ return;
+ }
+
+ /* now we have free iopb's and we are at splbio... start 'em up */
+ if (xdc_startbuf(parent, xd, bp) != XD_ERR_AOK) {
+ return;
+ }
+
+ /* done! */
+ splx(s);
+ return;
+
+bad: /* tells upper layers we have an error */
+ bp->b_flags |= B_ERROR;
+done: /* tells upper layers we are done with this
+ * buf */
+ bp->b_resid = bp->b_bcount;
+ biodone(bp);
+}
+/*
+ * end of {b,c}devsw functions
+ */
+
+/*
+ * i n t e r r u p t f u n c t i o n
+ *
+ * xdcintr: hardware interrupt.
+ */
+int
+xdcintr(v)
+ void *v;
+
+{
+ struct xdc_softc *xdcsc = v;
+
+ /* kick the event counter */
+
+ xdcsc->sc_intrcnt.ev_count++;
+
+ /* remove as many done IOPBs as possible */
+
+ xdc_remove_iorq(xdcsc);
+
+ /* start any iorq's already waiting */
+
+ xdc_start(xdcsc, XDC_MAXIOPB);
+
+ /* fill up any remaining iorq's with queue'd buffers */
+
+ while (xdcsc->nfree > 0 && xdcsc->sc_wq.b_actf)
+ if (xdc_startbuf(xdcsc, NULL, NULL) != XD_ERR_AOK)
+ break;
+
+ return (1);
+}
+/*
+ * end of interrupt function
+ */
+
+/*
+ * i n t e r n a l f u n c t i o n s
+ */
+
+/*
+ * xdc_rqinit: fill out the fields of an I/O request
+ */
+
+inline void
+xdc_rqinit(rq, xdc, xd, md, blk, cnt, db, bp)
+ struct xd_iorq *rq;
+ struct xdc_softc *xdc;
+ struct xd_softc *xd;
+ int md;
+ u_long blk;
+ int cnt;
+ caddr_t db;
+ struct buf *bp;
+{
+ rq->xdc = xdc;
+ rq->xd = xd;
+ rq->ttl = XDC_MAXTTL + 10;
+ rq->mode = md;
+ rq->tries = rq->errno = rq->lasterror = 0;
+ rq->blockno = blk;
+ rq->sectcnt = cnt;
+ rq->dbuf = rq->dbufbase = db;
+ rq->buf = bp;
+}
+/*
+ * xdc_rqtopb: load up an IOPB based on an iorq
+ */
+
+void
+xdc_rqtopb(iorq, iopb, cmd, subfun)
+ struct xd_iorq *iorq;
+ struct xd_iopb *iopb;
+ int cmd, subfun;
+
+{
+ u_long block, dp;
+
+ /* standard stuff */
+
+ iopb->errs = iopb->done = 0;
+ iopb->comm = cmd;
+ iopb->errno = iopb->status = 0;
+ iopb->subfun = subfun;
+ if (iorq->xd)
+ iopb->unit = iorq->xd->xd_drive;
+ else
+ iopb->unit = 0;
+
+ /* check for alternate IOPB format */
+
+ if (cmd == XDCMD_WRP) {
+ switch (subfun) {
+ case XDFUN_CTL:{
+ struct xd_iopb_ctrl *ctrl =
+ (struct xd_iopb_ctrl *) iopb;
+ iopb->lll = 0;
+ iopb->intl = (XD_STATE(iorq->mode) == XD_SUB_POLL)
+ ? 0
+ : iorq->xdc->ipl;
+ ctrl->param_a = XDPA_TMOD | XDPA_DACF;
+ ctrl->param_b = XDPB_ROR | XDPB_TDT_3_2USEC;
+ ctrl->param_c = XDPC_OVS | XDPC_COP | XDPC_ASR |
+ XDPC_RBC | XDPC_ECC2;
+ ctrl->throttle = XDC_THROTTLE;
+ ctrl->delay = XDC_DELAY_SPARC;
+ break;
+ }
+ case XDFUN_DRV:{
+ struct xd_iopb_drive *drv =
+ (struct xd_iopb_drive *)iopb;
+ /* we assume that the disk label has the right
+ * info */
+ if (XD_STATE(iorq->mode) == XD_SUB_POLL)
+ drv->dparam_ipl = (XDC_DPARAM << 3);
+ else
+ drv->dparam_ipl = (XDC_DPARAM << 3) |
+ iorq->xdc->ipl;
+ drv->maxsect = iorq->xd->nsect - 1;
+ drv->maxsector = drv->maxsect;
+ /* note: maxsector != maxsect only if you are
+ * doing cyl sparing */
+ drv->headoff = 0;
+ drv->maxcyl = iorq->xd->pcyl - 1;
+ drv->maxhead = iorq->xd->nhead - 1;
+ break;
+ }
+ case XDFUN_FMT:{
+ struct xd_iopb_format *form =
+ (struct xd_iopb_format *) iopb;
+ if (XD_STATE(iorq->mode) == XD_SUB_POLL)
+ form->interleave_ipl = (XDC_INTERLEAVE << 3);
+ else
+ form->interleave_ipl = (XDC_INTERLEAVE << 3) |
+ iorq->xdc->ipl;
+ form->field1 = XDFM_FIELD1;
+ form->field2 = XDFM_FIELD2;
+ form->field3 = XDFM_FIELD3;
+ form->field4 = XDFM_FIELD4;
+ form->bytespersec = XDFM_BPS;
+ form->field6 = XDFM_FIELD6;
+ form->field7 = XDFM_FIELD7;
+ break;
+ }
+ }
+ } else {
+
+ /* normal IOPB case (harmless to RDP command) */
+
+ iopb->lll = 0;
+ iopb->intl = (XD_STATE(iorq->mode) == XD_SUB_POLL)
+ ? 0
+ : iorq->xdc->ipl;
+ iopb->sectcnt = iorq->sectcnt;
+ block = iorq->blockno;
+ if (iorq->xd == NULL || block == 0) {
+ iopb->sectno = iopb->headno = iopb->cylno = 0;
+ } else {
+ iopb->sectno = block % iorq->xd->nsect;
+ block = block / iorq->xd->nsect;
+ iopb->headno = block % iorq->xd->nhead;
+ block = block / iorq->xd->nhead;
+ iopb->cylno = block;
+ }
+ dp = (u_long) iorq->dbuf;
+ dp = iopb->daddr = (iorq->dbuf == NULL) ? 0 : dp;
+ iopb->addrmod = ((dp + (XDFM_BPS * iorq->sectcnt)) > 0x1000000)
+ ? XDC_ADDRMOD32
+ : XDC_ADDRMOD;
+ }
+}
+
+/*
+ * xdc_cmd: front end for POLL'd and WAIT'd commands. Returns rqno.
+ * If you've already got an IORQ, you can call submit directly (currently
+ * there is no need to do this). NORM requests are handled seperately.
+ */
+int
+xdc_cmd(xdcsc, cmd, subfn, unit, block, scnt, dptr, fullmode)
+ struct xdc_softc *xdcsc;
+ int cmd, subfn, unit, block, scnt;
+ char *dptr;
+ int fullmode;
+
+{
+ int rqno, submode = XD_STATE(fullmode), retry;
+ struct xd_iorq *iorq;
+ struct xd_iopb *iopb;
+
+ /* get iorq/iopb */
+ switch (submode) {
+ case XD_SUB_POLL:
+ while (xdcsc->nfree == 0) {
+ if (xdc_piodriver(xdcsc, 0, 1) != XD_ERR_AOK)
+ return (XD_ERR_FAIL);
+ }
+ break;
+ case XD_SUB_WAIT:
+ retry = 1;
+ while (retry) {
+ while (xdcsc->nfree == 0) {
+ if (tsleep(&xdcsc->nfree, PRIBIO, "xdnfree", 0))
+ return (XD_ERR_FAIL);
+ }
+ while (xdcsc->ndone > XDC_SUBWAITLIM) {
+ if (tsleep(&xdcsc->ndone, PRIBIO, "xdsubwait", 0))
+ return (XD_ERR_FAIL);
+ }
+ if (xdcsc->nfree)
+ retry = 0; /* got it */
+ }
+ break;
+ default:
+ return (XD_ERR_FAIL); /* illegal */
+ }
+ if (xdcsc->nfree == 0)
+ panic("xdcmd nfree");
+ rqno = XDC_RQALLOC(xdcsc);
+ iorq = &xdcsc->reqs[rqno];
+ iopb = iorq->iopb;
+
+
+ /* init iorq/iopb */
+
+ xdc_rqinit(iorq, xdcsc,
+ (unit == XDC_NOUNIT) ? NULL : xdcsc->sc_drives[unit],
+ fullmode, block, scnt, dptr, NULL);
+
+ /* load IOPB from iorq */
+
+ xdc_rqtopb(iorq, iopb, cmd, subfn);
+
+ /* submit it for processing */
+
+ xdc_submit_iorq(xdcsc, rqno, fullmode); /* error code will be in iorq */
+
+ return (rqno);
+}
+/*
+ * xdc_startbuf
+ * start a buffer running, assumes nfree > 0
+ */
+
+int
+xdc_startbuf(xdcsc, xdsc, bp)
+ struct xdc_softc *xdcsc;
+ struct xd_softc *xdsc;
+ struct buf *bp;
+
+{
+ int rqno, partno;
+ struct xd_iorq *iorq;
+ struct xd_iopb *iopb;
+ struct buf *wq;
+ u_long block;
+ caddr_t dbuf;
+
+ if (!xdcsc->nfree)
+ panic("xdc_startbuf free");
+ rqno = XDC_RQALLOC(xdcsc);
+ iorq = &xdcsc->reqs[rqno];
+ iopb = iorq->iopb;
+
+ /* get buf */
+
+ if (bp == NULL) {
+ bp = xdcsc->sc_wq.b_actf;
+ if (!bp)
+ panic("xdc_startbuf bp");
+ wq = bp->b_actf;
+ if (wq)
+ wq->b_actb = bp->b_actb;
+ else
+ xdcsc->sc_wq.b_actb = bp->b_actb;
+ *bp->b_actb = wq;
+ xdsc = xdcsc->sc_drives[DISKUNIT(bp->b_dev)];
+ }
+ partno = DISKPART(bp->b_dev);
+#ifdef XDC_DEBUG
+ printf("xdc_startbuf: %s%c: %s block %d\n", xdsc->sc_dev.dv_xname,
+ 'a' + partno, (bp->b_flags & B_READ) ? "read" : "write", bp->b_blkno);
+ printf("xdc_startbuf: b_bcount %d, b_data 0x%x\n",
+ bp->b_bcount, bp->b_data);
+#endif
+
+ /*
+ * load request. we have to calculate the correct block number based
+ * on partition info.
+ *
+ * note that iorq points to the buffer as mapped into DVMA space,
+ * where as the bp->b_data points to its non-DVMA mapping.
+ */
+
+ block = bp->b_blkno + ((partno == RAW_PART) ? 0 :
+ xdsc->sc_dk.dk_label->d_partitions[partno].p_offset);
+
+ dbuf = kdvma_mapin(bp->b_data, bp->b_bcount, 0);
+ if (dbuf == NULL) { /* out of DVMA space */
+ printf("%s: warning: out of DVMA space\n",
+ xdcsc->sc_dev.dv_xname);
+ XDC_FREE(xdcsc, rqno);
+ wq = &xdcsc->sc_wq; /* put at end of queue */
+ bp->b_actf = 0;
+ bp->b_actb = wq->b_actb;
+ *wq->b_actb = bp;
+ wq->b_actb = &bp->b_actf;
+ return (XD_ERR_FAIL); /* XXX: need some sort of
+ * call-back scheme here? */
+ }
+
+#if 0
+ printf("xdc_startbuf: %s%c: %s block %d (%d)\n",
+ xdsc->sc_dev.dv_xname,
+ 'a' + partno, (bp->b_flags & B_READ) ? "read" : "write",
+ bp->b_blkno, block);
+#endif
+ /* init iorq and load iopb from it */
+
+ xdc_rqinit(iorq, xdcsc, xdsc, XD_SUB_NORM | XD_MODE_VERBO, block,
+ bp->b_bcount / XDFM_BPS, dbuf, bp);
+
+ xdc_rqtopb(iorq, iopb, (bp->b_flags & B_READ) ? XDCMD_RD : XDCMD_WR, 0);
+
+ /* Instrumentation. */
+ disk_busy(&xdsc->sc_dk);
+
+ /* now submit [note that xdc_submit_iorq can never fail on NORM reqs] */
+
+ xdc_submit_iorq(xdcsc, rqno, XD_SUB_NORM);
+ return (XD_ERR_AOK);
+}
+
+
+/*
+ * xdc_submit_iorq: submit an iorq for processing. returns XD_ERR_AOK
+ * if ok. if it fail returns an error code. type is XD_SUB_*.
+ *
+ * note: caller frees iorq in all cases except NORM
+ *
+ * return value:
+ * NORM: XD_AOK (req pending), XD_FAIL (couldn't submit request)
+ * WAIT: XD_AOK (success), <error-code> (failed)
+ * POLL: <same as WAIT>
+ * NOQ : <same as NORM>
+ *
+ * there are three sources for i/o requests:
+ * [1] xdstrategy: normal block I/O, using "struct buf" system.
+ * [2] autoconfig/crash dump: these are polled I/O requests, no interrupts.
+ * [3] open/ioctl: these are I/O requests done in the context of a process,
+ * and the process should block until they are done.
+ *
+ * software state is stored in the iorq structure. each iorq has an
+ * iopb structure. the hardware understands the iopb structure.
+ * every command must go through an iopb. a 7053 can only handle
+ * XDC_MAXIOPB (31) active iopbs at one time. iopbs are allocated in
+ * DVMA space at boot up time. what happens if we run out of iopb's?
+ * for i/o type [1], the buffers are queued at the "buff" layer and
+ * picked up later by the interrupt routine. for case [2] the
+ * programmed i/o driver is called with a special flag that says
+ * return when one iopb is free. for case [3] the process can sleep
+ * on the iorq free list until some iopbs are avaliable.
+ */
+
+
+int
+xdc_submit_iorq(xdcsc, iorqno, type)
+ struct xdc_softc *xdcsc;
+ int iorqno;
+ int type;
+
+{
+ u_long iopbaddr;
+ struct xd_iorq *iorq = &xdcsc->reqs[iorqno];
+
+#ifdef XDC_DEBUG
+ printf("xdc_submit_iorq(%s, no=%d, type=%d)\n", xdcsc->sc_dev.dv_xname,
+ iorqno, type);
+#endif
+
+ /* first check and see if controller is busy */
+ if (xdcsc->xdc->xdc_csr & XDC_ADDING) {
+#ifdef XDC_DEBUG
+ printf("xdc_submit_iorq: XDC not ready (ADDING)\n");
+#endif
+ if (type == XD_SUB_NOQ)
+ return (XD_ERR_FAIL); /* failed */
+ XDC_TWAIT(xdcsc, iorqno); /* put at end of waitq */
+ switch (type) {
+ case XD_SUB_NORM:
+ return XD_ERR_AOK; /* success */
+ case XD_SUB_WAIT:
+ while (iorq->iopb->done == 0) {
+ sleep(iorq, PRIBIO);
+ }
+ return (iorq->errno);
+ case XD_SUB_POLL:
+ return (xdc_piodriver(xdcsc, iorqno, 0));
+ default:
+ panic("xdc_submit_iorq adding");
+ }
+ }
+#ifdef XDC_DEBUG
+ {
+ u_char *rio = (u_char *) iorq->iopb;
+ int sz = sizeof(struct xd_iopb), lcv;
+ printf("%s: aio #%d [",
+ xdcsc->sc_dev.dv_xname, iorq - xdcsc->reqs);
+ for (lcv = 0; lcv < sz; lcv++)
+ printf(" %02x", rio[lcv]);
+ printf("]\n");
+ }
+#endif /* XDC_DEBUG */
+
+ /* controller not busy, start command */
+ iopbaddr = (u_long)xdcsc->dvmaiopb +
+ ((u_long) iorq->iopb - (u_long)xdcsc->iopbase);
+
+ XDC_GO(xdcsc->xdc, iopbaddr); /* go! */
+ xdcsc->nrun++;
+ /* command now running, wrap it up */
+ switch (type) {
+ case XD_SUB_NORM:
+ case XD_SUB_NOQ:
+ return (XD_ERR_AOK); /* success */
+ case XD_SUB_WAIT:
+ while (iorq->iopb->done == 0) {
+ sleep(iorq, PRIBIO);
+ }
+ return (iorq->errno);
+ case XD_SUB_POLL:
+ return (xdc_piodriver(xdcsc, iorqno, 0));
+ default:
+ panic("xdc_submit_iorq wrap up");
+ }
+ panic("xdc_submit_iorq");
+ return 0; /* not reached */
+}
+
+
+/*
+ * xdc_piodriver
+ *
+ * programmed i/o driver. this function takes over the computer
+ * and drains off all i/o requests. it returns the status of the iorq
+ * the caller is interesting in. if freeone is true, then it returns
+ * when there is a free iorq.
+ */
+int
+xdc_piodriver(xdcsc, iorqno, freeone)
+ struct xdc_softc *xdcsc;
+ char iorqno;
+ int freeone;
+
+{
+ int nreset = 0;
+ int retval = 0;
+ u_long count;
+ struct xdc *xdc = xdcsc->xdc;
+#ifdef XDC_DEBUG
+ printf("xdc_piodriver(%s, %d, freeone=%d)\n", xdcsc->sc_dev.dv_xname,
+ iorqno, freeone);
+#endif
+
+ while (xdcsc->nwait || xdcsc->nrun) {
+#ifdef XDC_DEBUG
+ printf("xdc_piodriver: wait=%d, run=%d\n",
+ xdcsc->nwait, xdcsc->nrun);
+#endif
+ XDC_WAIT(xdc, count, XDC_MAXTIME, (XDC_REMIOPB | XDC_F_ERROR));
+#ifdef XDC_DEBUG
+ printf("xdc_piodriver: done wait with count = %d\n", count);
+#endif
+ /* we expect some progress soon */
+ if (count == 0 && nreset >= 2) {
+ xdc_reset(xdcsc, 0, XD_RSET_ALL, XD_ERR_FAIL, 0);
+#ifdef XDC_DEBUG
+ printf("xdc_piodriver: timeout\n");
+#endif
+ return (XD_ERR_FAIL);
+ }
+ if (count == 0) {
+ if (xdc_reset(xdcsc, 0,
+ (nreset++ == 0) ? XD_RSET_NONE : iorqno,
+ XD_ERR_FAIL,
+ 0) == XD_ERR_FAIL)
+ return (XD_ERR_FAIL); /* flushes all but POLL
+ * requests, resets */
+ continue;
+ }
+ xdc_remove_iorq(xdcsc); /* could resubmit request */
+ if (freeone) {
+ if (xdcsc->nrun < XDC_MAXIOPB) {
+#ifdef XDC_DEBUG
+ printf("xdc_piodriver: done: one free\n");
+#endif
+ return (XD_ERR_AOK);
+ }
+ continue; /* don't xdc_start */
+ }
+ xdc_start(xdcsc, XDC_MAXIOPB);
+ }
+
+ /* get return value */
+
+ retval = xdcsc->reqs[iorqno].errno;
+
+#ifdef XDC_DEBUG
+ printf("xdc_piodriver: done, retval = 0x%x (%s)\n",
+ xdcsc->reqs[iorqno].errno, xdc_e2str(xdcsc->reqs[iorqno].errno));
+#endif
+
+ /* now that we've drained everything, start up any bufs that have
+ * queued */
+
+ while (xdcsc->nfree > 0 && xdcsc->sc_wq.b_actf)
+ if (xdc_startbuf(xdcsc, NULL, NULL) != XD_ERR_AOK)
+ break;
+
+ return (retval);
+}
+
+/*
+ * xdc_reset: reset one drive. NOTE: assumes xdc was just reset.
+ * we steal iopb[0] for this, but we put it back when we are done.
+ */
+void
+xdc_xdreset(xdcsc, xdsc)
+ struct xdc_softc *xdcsc;
+ struct xd_softc *xdsc;
+
+{
+ struct xd_iopb tmpiopb;
+ u_long addr;
+ int del;
+ bcopy(xdcsc->iopbase, &tmpiopb, sizeof(tmpiopb));
+ bzero(xdcsc->iopbase, sizeof(tmpiopb));
+ xdcsc->iopbase->comm = XDCMD_RST;
+ xdcsc->iopbase->unit = xdsc->xd_drive;
+ addr = (u_long) xdcsc->dvmaiopb;
+ XDC_GO(xdcsc->xdc, addr); /* go! */
+ XDC_WAIT(xdcsc->xdc, del, XDC_RESETUSEC, XDC_REMIOPB);
+ if (del <= 0 || xdcsc->iopbase->errs) {
+ printf("%s: off-line: %s\n", xdcsc->sc_dev.dv_xname,
+ xdc_e2str(xdcsc->iopbase->errno));
+ xdcsc->xdc->xdc_csr = XDC_RESET;
+ XDC_WAIT(xdcsc->xdc, del, XDC_RESETUSEC, XDC_RESET);
+ if (del <= 0)
+ panic("xdc_reset");
+ } else {
+ xdcsc->xdc->xdc_csr = XDC_CLRRIO; /* clear RIO */
+ }
+ bcopy(&tmpiopb, xdcsc->iopbase, sizeof(tmpiopb));
+}
+
+
+/*
+ * xdc_reset: reset everything: requests are marked as errors except
+ * a polled request (which is resubmitted)
+ */
+int
+xdc_reset(xdcsc, quiet, blastmode, error, xdsc)
+ struct xdc_softc *xdcsc;
+ int quiet, blastmode, error;
+ struct xd_softc *xdsc;
+
+{
+ int del = 0, lcv, retval = XD_ERR_AOK;
+ int oldfree = xdcsc->nfree;
+
+ /* soft reset hardware */
+
+ if (!quiet)
+ printf("%s: soft reset\n", xdcsc->sc_dev.dv_xname);
+ xdcsc->xdc->xdc_csr = XDC_RESET;
+ XDC_WAIT(xdcsc->xdc, del, XDC_RESETUSEC, XDC_RESET);
+ if (del <= 0) {
+ blastmode = XD_RSET_ALL; /* dead, flush all requests */
+ retval = XD_ERR_FAIL;
+ }
+ if (xdsc)
+ xdc_xdreset(xdcsc, xdsc);
+
+ /* fix queues based on "blast-mode" */
+
+ for (lcv = 0; lcv < XDC_MAXIOPB; lcv++) {
+ register struct xd_iorq *iorq = &xdcsc->reqs[lcv];
+
+ if (XD_STATE(iorq->mode) != XD_SUB_POLL &&
+ XD_STATE(iorq->mode) != XD_SUB_WAIT &&
+ XD_STATE(iorq->mode) != XD_SUB_NORM)
+ /* is it active? */
+ continue;
+
+ xdcsc->nrun--; /* it isn't running any more */
+ if (blastmode == XD_RSET_ALL || blastmode != lcv) {
+ /* failed */
+ iorq->errno = error;
+ xdcsc->iopbase[lcv].done = xdcsc->iopbase[lcv].errs = 1;
+ switch (XD_STATE(xdcsc->reqs[lcv].mode)) {
+ case XD_SUB_NORM:
+ iorq->buf->b_error = EIO;
+ iorq->buf->b_flags |= B_ERROR;
+ iorq->buf->b_resid =
+ iorq->sectcnt * XDFM_BPS;
+ dvma_mapout(
+ (vm_offset_t)iorq->dbufbase,
+ (vm_offset_t)iorq->buf->b_un.b_addr,
+ iorq->buf->b_bcount);
+ disk_unbusy(&xdcsc->reqs[lcv].xd->sc_dk,
+ (xdcsc->reqs[lcv].buf->b_bcount -
+ xdcsc->reqs[lcv].buf->b_resid));
+ biodone(iorq->buf);
+ XDC_FREE(xdcsc, lcv); /* add to free list */
+ break;
+ case XD_SUB_WAIT:
+ wakeup(iorq);
+ case XD_SUB_POLL:
+ xdcsc->ndone++;
+ iorq->mode =
+ XD_NEWSTATE(iorq->mode, XD_SUB_DONE);
+ break;
+ }
+
+ } else {
+
+ /* resubmit, put at front of wait queue */
+ XDC_HWAIT(xdcsc, lcv);
+ }
+ }
+
+ /*
+ * now, if stuff is waiting, start it.
+ * since we just reset it should go
+ */
+ xdc_start(xdcsc, XDC_MAXIOPB);
+
+ /* ok, we did it */
+ if (oldfree == 0 && xdcsc->nfree)
+ wakeup(&xdcsc->nfree);
+
+#ifdef XDC_DIAG
+ del = xdcsc->nwait + xdcsc->nrun + xdcsc->nfree + xdcsc->ndone;
+ if (del != XDC_MAXIOPB)
+ printf("%s: diag: xdc_reset miscount (%d should be %d)!\n",
+ xdcsc->sc_dev.dv_xname, del, XDC_MAXIOPB);
+ else
+ if (xdcsc->ndone > XDC_MAXIOPB - XDC_SUBWAITLIM)
+ printf("%s: diag: lots of done jobs (%d)\n",
+ xdcsc->sc_dev.dv_xname, xdcsc->ndone);
+#endif
+ printf("RESET DONE\n");
+ return (retval);
+}
+/*
+ * xdc_start: start all waiting buffers
+ */
+
+void
+xdc_start(xdcsc, maxio)
+ struct xdc_softc *xdcsc;
+ int maxio;
+
+{
+ int rqno;
+ while (maxio && xdcsc->nwait &&
+ (xdcsc->xdc->xdc_csr & XDC_ADDING) == 0) {
+ XDC_GET_WAITER(xdcsc, rqno); /* note: rqno is an "out"
+ * param */
+ if (xdc_submit_iorq(xdcsc, rqno, XD_SUB_NOQ) != XD_ERR_AOK)
+ panic("xdc_start"); /* should never happen */
+ maxio--;
+ }
+}
+/*
+ * xdc_remove_iorq: remove "done" IOPB's.
+ */
+
+int
+xdc_remove_iorq(xdcsc)
+ struct xdc_softc *xdcsc;
+
+{
+ int errno, rqno, comm, errs;
+ struct xdc *xdc = xdcsc->xdc;
+ struct xd_iopb *iopb;
+ struct xd_iorq *iorq;
+ struct buf *bp;
+
+ if (xdc->xdc_csr & XDC_F_ERROR) {
+ /*
+ * FATAL ERROR: should never happen under normal use. This
+ * error is so bad, you can't even tell which IOPB is bad, so
+ * we dump them all.
+ */
+ errno = xdc->xdc_f_err;
+ printf("%s: fatal error 0x%02x: %s\n", xdcsc->sc_dev.dv_xname,
+ errno, xdc_e2str(errno));
+ if (xdc_reset(xdcsc, 0, XD_RSET_ALL, errno, 0) != XD_ERR_AOK) {
+ printf("%s: soft reset failed!\n",
+ xdcsc->sc_dev.dv_xname);
+ panic("xdc_remove_iorq: controller DEAD");
+ }
+ return (XD_ERR_AOK);
+ }
+
+ /*
+ * get iopb that is done
+ *
+ * hmm... I used to read the address of the done IOPB off the VME
+ * registers and calculate the rqno directly from that. that worked
+ * until I started putting a load on the controller. when loaded, i
+ * would get interrupts but neither the REMIOPB or F_ERROR bits would
+ * be set, even after DELAY'ing a while! later on the timeout
+ * routine would detect IOPBs that were marked "running" but their
+ * "done" bit was set. rather than dealing directly with this
+ * problem, it is just easier to look at all running IOPB's for the
+ * done bit.
+ */
+ if (xdc->xdc_csr & XDC_REMIOPB) {
+ xdc->xdc_csr = XDC_CLRRIO;
+ }
+
+ for (rqno = 0; rqno < XDC_MAXIOPB; rqno++) {
+ iorq = &xdcsc->reqs[rqno];
+ if (iorq->mode == 0 || XD_STATE(iorq->mode) == XD_SUB_DONE)
+ continue; /* free, or done */
+ iopb = &xdcsc->iopbase[rqno];
+ if (iopb->done == 0)
+ continue; /* not done yet */
+
+#ifdef XDC_DEBUG
+ {
+ u_char *rio = (u_char *) iopb;
+ int sz = sizeof(struct xd_iopb), lcv;
+ printf("%s: rio #%d [", xdcsc->sc_dev.dv_xname, rqno);
+ for (lcv = 0; lcv < sz; lcv++)
+ printf(" %02x", rio[lcv]);
+ printf("]\n");
+ }
+#endif /* XDC_DEBUG */
+
+ xdcsc->nrun--;
+
+ comm = iopb->comm;
+ errs = iopb->errs;
+
+ if (errs)
+ iorq->errno = iopb->errno;
+ else
+ iorq->errno = 0;
+
+ /* handle non-fatal errors */
+
+ if (errs &&
+ xdc_error(xdcsc, iorq, iopb, rqno, comm) == XD_ERR_AOK)
+ continue; /* AOK: we resubmitted it */
+
+
+ /* this iorq is now done (hasn't been restarted or anything) */
+
+ if ((iorq->mode & XD_MODE_VERBO) && iorq->lasterror)
+ xdc_perror(iorq, iopb, 0);
+
+ /* now, if read/write check to make sure we got all the data
+ * we needed. (this may not be the case if we got an error in
+ * the middle of a multisector request). */
+
+ if ((iorq->mode & XD_MODE_B144) != 0 && errs == 0 &&
+ (comm == XDCMD_RD || comm == XDCMD_WR)) {
+ /* we just successfully processed a bad144 sector
+ * note: if we are in bad 144 mode, the pointers have
+ * been advanced already (see above) and are pointing
+ * at the bad144 sector. to exit bad144 mode, we
+ * must advance the pointers 1 sector and issue a new
+ * request if there are still sectors left to process
+ *
+ */
+ XDC_ADVANCE(iorq, 1); /* advance 1 sector */
+
+ /* exit b144 mode */
+ iorq->mode = iorq->mode & (~XD_MODE_B144);
+
+ if (iorq->sectcnt) { /* more to go! */
+ iorq->lasterror = iorq->errno = iopb->errno = 0;
+ iopb->errs = iopb->done = 0;
+ iorq->tries = 0;
+ iopb->sectcnt = iorq->sectcnt;
+ iopb->cylno = iorq->blockno /
+ iorq->xd->sectpercyl;
+ iopb->headno =
+ (iorq->blockno / iorq->xd->nsect) %
+ iorq->xd->nhead;
+ iopb->sectno = iorq->blockno % iorq->xd->nsect;
+ iopb->daddr = (u_long) iorq->dbuf;
+ XDC_HWAIT(xdcsc, rqno);
+ xdc_start(xdcsc, 1); /* resubmit */
+ continue;
+ }
+ }
+ /* final cleanup, totally done with this request */
+
+ switch (XD_STATE(iorq->mode)) {
+ case XD_SUB_NORM:
+ bp = iorq->buf;
+ if (errs) {
+ bp->b_error = EIO;
+ bp->b_flags |= B_ERROR;
+ bp->b_resid = iorq->sectcnt * XDFM_BPS;
+ } else {
+ bp->b_resid = 0; /* done */
+ }
+ dvma_mapout((vm_offset_t) iorq->dbufbase,
+ (vm_offset_t) bp->b_un.b_addr,
+ bp->b_bcount);
+ disk_unbusy(&iorq->xd->sc_dk,
+ (bp->b_bcount - bp->b_resid));
+ XDC_FREE(xdcsc, rqno);
+ biodone(bp);
+ break;
+ case XD_SUB_WAIT:
+ iorq->mode = XD_NEWSTATE(iorq->mode, XD_SUB_DONE);
+ xdcsc->ndone++;
+ wakeup(iorq);
+ break;
+ case XD_SUB_POLL:
+ iorq->mode = XD_NEWSTATE(iorq->mode, XD_SUB_DONE);
+ xdcsc->ndone++;
+ break;
+ }
+ }
+
+ return (XD_ERR_AOK);
+}
+
+/*
+ * xdc_perror: print error.
+ * - if still_trying is true: we got an error, retried and got a
+ * different error. in that case lasterror is the old error,
+ * and errno is the new one.
+ * - if still_trying is not true, then if we ever had an error it
+ * is in lasterror. also, if iorq->errno == 0, then we recovered
+ * from that error (otherwise iorq->errno == iorq->lasterror).
+ */
+void
+xdc_perror(iorq, iopb, still_trying)
+ struct xd_iorq *iorq;
+ struct xd_iopb *iopb;
+ int still_trying;
+
+{
+
+ int error = iorq->lasterror;
+
+ printf("%s", (iorq->xd) ? iorq->xd->sc_dev.dv_xname
+ : iorq->xdc->sc_dev.dv_xname);
+ if (iorq->buf)
+ printf("%c: ", 'a' + DISKPART(iorq->buf->b_dev));
+ if (iopb->comm == XDCMD_RD || iopb->comm == XDCMD_WR)
+ printf("%s %d/%d/%d: ",
+ (iopb->comm == XDCMD_RD) ? "read" : "write",
+ iopb->cylno, iopb->headno, iopb->sectno);
+ printf("%s", xdc_e2str(error));
+
+ if (still_trying)
+ printf(" [still trying, new error=%s]", xdc_e2str(iorq->errno));
+ else
+ if (iorq->errno == 0)
+ printf(" [recovered in %d tries]", iorq->tries);
+
+ printf("\n");
+}
+
+/*
+ * xdc_error: non-fatal error encountered... recover.
+ * return AOK if resubmitted, return FAIL if this iopb is done
+ */
+int
+xdc_error(xdcsc, iorq, iopb, rqno, comm)
+ struct xdc_softc *xdcsc;
+ struct xd_iorq *iorq;
+ struct xd_iopb *iopb;
+ int rqno, comm;
+
+{
+ int errno = iorq->errno;
+ int erract = errno & XD_ERA_MASK;
+ int oldmode, advance, i;
+
+ if (erract == XD_ERA_RSET) { /* some errors require a reset */
+ oldmode = iorq->mode;
+ iorq->mode = XD_SUB_DONE | (~XD_SUB_MASK & oldmode);
+ xdcsc->ndone++;
+ /* make xdc_start ignore us */
+ xdc_reset(xdcsc, 1, XD_RSET_NONE, errno, iorq->xd);
+ iorq->mode = oldmode;
+ xdcsc->ndone--;
+ }
+ /* check for read/write to a sector in bad144 table if bad: redirect
+ * request to bad144 area */
+
+ if ((comm == XDCMD_RD || comm == XDCMD_WR) &&
+ (iorq->mode & XD_MODE_B144) == 0) {
+ advance = iorq->sectcnt - iopb->sectcnt;
+ XDC_ADVANCE(iorq, advance);
+ if ((i = isbad(&iorq->xd->dkb,
+ iorq->blockno / iorq->xd->sectpercyl,
+ (iorq->blockno / iorq->xd->nsect) % iorq->xd->nhead,
+ iorq->blockno % iorq->xd->nsect)) != -1) {
+ iorq->mode |= XD_MODE_B144; /* enter bad144 mode &
+ * redirect */
+ iopb->errno = iopb->done = iopb->errs = 0;
+ iopb->sectcnt = 1;
+ /* From original dkbad.h:
+ Replacement sectors are allocated starting with
+ the first sector before the bad sector information
+ and working backward towards the beginning of the
+ disk. */
+ /* Last cylinder. */
+ iopb->cylno = (iorq->xd->ncyl + iorq->xd->acyl) - 1;
+ /* Last track. */
+ i = iorq->xd->sectpercyl - iorq->xd->nsect - (i + 1);
+ iopb->headno = i / iorq->xd->nsect;
+ iopb->sectno = i % iorq->xd->nsect;
+ XDC_HWAIT(xdcsc, rqno);
+ xdc_start(xdcsc, 1); /* resubmit */
+ return (XD_ERR_AOK); /* recovered! */
+ }
+ }
+
+ /*
+ * it isn't a bad144 sector, must be real error! see if we can retry
+ * it?
+ */
+ if ((iorq->mode & XD_MODE_VERBO) && iorq->lasterror)
+ xdc_perror(iorq, iopb, 1); /* inform of error state
+ * change */
+ iorq->lasterror = errno;
+
+ if ((erract == XD_ERA_RSET || erract == XD_ERA_HARD)
+ && iorq->tries < XDC_MAXTRIES) { /* retry? */
+ iorq->tries++;
+ iorq->errno = iopb->errno = iopb->done = iopb->errs = 0;
+ XDC_HWAIT(xdcsc, rqno);
+ xdc_start(xdcsc, 1); /* restart */
+ return (XD_ERR_AOK); /* recovered! */
+ }
+
+ /* failed to recover from this error */
+ return (XD_ERR_FAIL);
+}
+
+/*
+ * xdc_tick: make sure xd is still alive and ticking (err, kicking).
+ */
+void
+xdc_tick(arg)
+ void *arg;
+
+{
+ struct xdc_softc *xdcsc = arg;
+ int lcv, s, reset = 0;
+#ifdef XDC_DIAG
+ int wait, run, free, done, whd = 0;
+ u_char fqc[XDC_MAXIOPB], wqc[XDC_MAXIOPB], mark[XDC_MAXIOPB];
+ s = splbio();
+ wait = xdcsc->nwait;
+ run = xdcsc->nrun;
+ free = xdcsc->nfree;
+ done = xdcsc->ndone;
+ bcopy(xdcsc->waitq, wqc, sizeof(wqc));
+ bcopy(xdcsc->freereq, fqc, sizeof(fqc));
+ splx(s);
+ if (wait + run + free + done != XDC_MAXIOPB) {
+ printf("%s: diag: IOPB miscount (got w/f/r/d %d/%d/%d/%d, wanted %d)\n",
+ xdcsc->sc_dev.dv_xname, wait, free, run, done, XDC_MAXIOPB);
+ bzero(mark, sizeof(mark));
+ printf("FREE: ");
+ for (lcv = free; lcv > 0; lcv--) {
+ printf("%d ", fqc[lcv - 1]);
+ mark[fqc[lcv - 1]] = 1;
+ }
+ printf("\nWAIT: ");
+ lcv = wait;
+ while (lcv > 0) {
+ printf("%d ", wqc[whd]);
+ mark[wqc[whd]] = 1;
+ whd = (whd + 1) % XDC_MAXIOPB;
+ lcv--;
+ }
+ printf("\n");
+ for (lcv = 0; lcv < XDC_MAXIOPB; lcv++) {
+ if (mark[lcv] == 0)
+ printf("MARK: running %d: mode %d done %d errs %d errno 0x%x ttl %d buf %p\n",
+ lcv, xdcsc->reqs[lcv].mode,
+ xdcsc->iopbase[lcv].done,
+ xdcsc->iopbase[lcv].errs,
+ xdcsc->iopbase[lcv].errno,
+ xdcsc->reqs[lcv].ttl, xdcsc->reqs[lcv].buf);
+ }
+ } else
+ if (done > XDC_MAXIOPB - XDC_SUBWAITLIM)
+ printf("%s: diag: lots of done jobs (%d)\n",
+ xdcsc->sc_dev.dv_xname, done);
+
+#endif
+#ifdef XDC_DEBUG
+ printf("%s: tick: csr 0x%x, w/f/r/d %d/%d/%d/%d\n",
+ xdcsc->sc_dev.dv_xname,
+ xdcsc->xdc->xdc_csr, xdcsc->nwait, xdcsc->nfree, xdcsc->nrun,
+ xdcsc->ndone);
+ for (lcv = 0; lcv < XDC_MAXIOPB; lcv++) {
+ if (xdcsc->reqs[lcv].mode)
+ printf("running %d: mode %d done %d errs %d errno 0x%x\n",
+ lcv,
+ xdcsc->reqs[lcv].mode, xdcsc->iopbase[lcv].done,
+ xdcsc->iopbase[lcv].errs, xdcsc->iopbase[lcv].errno);
+ }
+#endif
+
+ /* reduce ttl for each request if one goes to zero, reset xdc */
+ s = splbio();
+ for (lcv = 0; lcv < XDC_MAXIOPB; lcv++) {
+ if (xdcsc->reqs[lcv].mode == 0 ||
+ XD_STATE(xdcsc->reqs[lcv].mode) == XD_SUB_DONE)
+ continue;
+ xdcsc->reqs[lcv].ttl--;
+ if (xdcsc->reqs[lcv].ttl == 0)
+ reset = 1;
+ }
+ if (reset) {
+ printf("%s: watchdog timeout\n", xdcsc->sc_dev.dv_xname);
+ xdc_reset(xdcsc, 0, XD_RSET_NONE, XD_ERR_FAIL, NULL);
+ }
+ splx(s);
+
+ /* until next time */
+
+ timeout(xdc_tick, xdcsc, XDC_TICKCNT);
+}
+
+/*
+ * xdc_ioctlcmd: this function provides a user level interface to the
+ * controller via ioctl. this allows "format" programs to be written
+ * in user code, and is also useful for some debugging. we return
+ * an error code. called at user priority.
+ */
+int
+xdc_ioctlcmd(xd, dev, xio)
+ struct xd_softc *xd;
+ dev_t dev;
+ struct xd_iocmd *xio;
+
+{
+ int s, err, rqno, dummy;
+ caddr_t dvmabuf = NULL, buf = NULL;
+ struct xdc_softc *xdcsc;
+
+ /* check sanity of requested command */
+
+ switch (xio->cmd) {
+
+ case XDCMD_NOP: /* no op: everything should be zero */
+ if (xio->subfn || xio->dptr || xio->dlen ||
+ xio->block || xio->sectcnt)
+ return (EINVAL);
+ break;
+
+ case XDCMD_RD: /* read / write sectors (up to XD_IOCMD_MAXS) */
+ case XDCMD_WR:
+ if (xio->subfn || xio->sectcnt > XD_IOCMD_MAXS ||
+ xio->sectcnt * XDFM_BPS != xio->dlen || xio->dptr == NULL)
+ return (EINVAL);
+ break;
+
+ case XDCMD_SK: /* seek: doesn't seem useful to export this */
+ return (EINVAL);
+
+ case XDCMD_WRP: /* write parameters */
+ return (EINVAL);/* not useful, except maybe drive
+ * parameters... but drive parameters should
+ * go via disklabel changes */
+
+ case XDCMD_RDP: /* read parameters */
+ if (xio->subfn != XDFUN_DRV ||
+ xio->dlen || xio->block || xio->dptr)
+ return (EINVAL); /* allow read drive params to
+ * get hw_spt */
+ xio->sectcnt = xd->hw_spt; /* we already know the answer */
+ return (0);
+ break;
+
+ case XDCMD_XRD: /* extended read/write */
+ case XDCMD_XWR:
+
+ switch (xio->subfn) {
+
+ case XDFUN_THD:/* track headers */
+ if (xio->sectcnt != xd->hw_spt ||
+ (xio->block % xd->nsect) != 0 ||
+ xio->dlen != XD_IOCMD_HSZ * xd->hw_spt ||
+ xio->dptr == NULL)
+ return (EINVAL);
+ xio->sectcnt = 0;
+ break;
+
+ case XDFUN_FMT:/* NOTE: also XDFUN_VFY */
+ if (xio->cmd == XDCMD_XRD)
+ return (EINVAL); /* no XDFUN_VFY */
+ if (xio->sectcnt || xio->dlen ||
+ (xio->block % xd->nsect) != 0 || xio->dptr)
+ return (EINVAL);
+ break;
+
+ case XDFUN_HDR:/* header, header verify, data, data ECC */
+ return (EINVAL); /* not yet */
+
+ case XDFUN_DM: /* defect map */
+ case XDFUN_DMX:/* defect map (alternate location) */
+ if (xio->sectcnt || xio->dlen != XD_IOCMD_DMSZ ||
+ (xio->block % xd->nsect) != 0 || xio->dptr == NULL)
+ return (EINVAL);
+ break;
+
+ default:
+ return (EINVAL);
+ }
+ break;
+
+ case XDCMD_TST: /* diagnostics */
+ return (EINVAL);
+
+ default:
+ return (EINVAL);/* ??? */
+ }
+
+ /* create DVMA buffer for request if needed */
+
+ if (xio->dlen) {
+ dvmabuf = dvma_malloc(xio->dlen, &buf, M_WAITOK);
+ if (xio->cmd == XDCMD_WR || xio->cmd == XDCMD_XWR) {
+ if ((err = copyin(xio->dptr, buf, xio->dlen)) != 0) {
+ dvma_free(dvmabuf, xio->dlen, &buf);
+ return (err);
+ }
+ }
+ }
+ /* do it! */
+
+ err = 0;
+ xdcsc = xd->parent;
+ s = splbio();
+ rqno = xdc_cmd(xdcsc, xio->cmd, xio->subfn, xd->xd_drive, xio->block,
+ xio->sectcnt, dvmabuf, XD_SUB_WAIT);
+ if (rqno == XD_ERR_FAIL) {
+ err = EIO;
+ goto done;
+ }
+ xio->errno = xdcsc->reqs[rqno].errno;
+ xio->tries = xdcsc->reqs[rqno].tries;
+ XDC_DONE(xdcsc, rqno, dummy);
+
+ if (xio->cmd == XDCMD_RD || xio->cmd == XDCMD_XRD)
+ err = copyout(buf, xio->dptr, xio->dlen);
+
+done:
+ splx(s);
+ if (dvmabuf)
+ dvma_free(dvmabuf, xio->dlen, &buf);
+ return (err);
+}
+
+/*
+ * xdc_e2str: convert error code number into an error string
+ */
+char *
+xdc_e2str(no)
+ int no;
+{
+ switch (no) {
+ case XD_ERR_FAIL:
+ return ("Software fatal error");
+ case XD_ERR_AOK:
+ return ("Successful completion");
+ case XD_ERR_ICYL:
+ return ("Illegal cylinder address");
+ case XD_ERR_IHD:
+ return ("Illegal head address");
+ case XD_ERR_ISEC:
+ return ("Illgal sector address");
+ case XD_ERR_CZER:
+ return ("Count zero");
+ case XD_ERR_UIMP:
+ return ("Unimplemented command");
+ case XD_ERR_IF1:
+ return ("Illegal field length 1");
+ case XD_ERR_IF2:
+ return ("Illegal field length 2");
+ case XD_ERR_IF3:
+ return ("Illegal field length 3");
+ case XD_ERR_IF4:
+ return ("Illegal field length 4");
+ case XD_ERR_IF5:
+ return ("Illegal field length 5");
+ case XD_ERR_IF6:
+ return ("Illegal field length 6");
+ case XD_ERR_IF7:
+ return ("Illegal field length 7");
+ case XD_ERR_ISG:
+ return ("Illegal scatter/gather length");
+ case XD_ERR_ISPT:
+ return ("Not enough sectors per track");
+ case XD_ERR_ALGN:
+ return ("Next IOPB address alignment error");
+ case XD_ERR_SGAL:
+ return ("Scatter/gather address alignment error");
+ case XD_ERR_SGEC:
+ return ("Scatter/gather with auto-ECC");
+ case XD_ERR_SECC:
+ return ("Soft ECC corrected");
+ case XD_ERR_SIGN:
+ return ("ECC ignored");
+ case XD_ERR_ASEK:
+ return ("Auto-seek retry recovered");
+ case XD_ERR_RTRY:
+ return ("Soft retry recovered");
+ case XD_ERR_HECC:
+ return ("Hard data ECC");
+ case XD_ERR_NHDR:
+ return ("Header not found");
+ case XD_ERR_NRDY:
+ return ("Drive not ready");
+ case XD_ERR_TOUT:
+ return ("Operation timeout");
+ case XD_ERR_VTIM:
+ return ("VMEDMA timeout");
+ case XD_ERR_DSEQ:
+ return ("Disk sequencer error");
+ case XD_ERR_HDEC:
+ return ("Header ECC error");
+ case XD_ERR_RVFY:
+ return ("Read verify");
+ case XD_ERR_VFER:
+ return ("Fatail VMEDMA error");
+ case XD_ERR_VBUS:
+ return ("VMEbus error");
+ case XD_ERR_DFLT:
+ return ("Drive faulted");
+ case XD_ERR_HECY:
+ return ("Header error/cyliner");
+ case XD_ERR_HEHD:
+ return ("Header error/head");
+ case XD_ERR_NOCY:
+ return ("Drive not on-cylinder");
+ case XD_ERR_SEEK:
+ return ("Seek error");
+ case XD_ERR_ILSS:
+ return ("Illegal sector size");
+ case XD_ERR_SEC:
+ return ("Soft ECC");
+ case XD_ERR_WPER:
+ return ("Write-protect error");
+ case XD_ERR_IRAM:
+ return ("IRAM self test failure");
+ case XD_ERR_MT3:
+ return ("Maintenance test 3 failure (DSKCEL RAM)");
+ case XD_ERR_MT4:
+ return ("Maintenance test 4 failure (header shift reg)");
+ case XD_ERR_MT5:
+ return ("Maintenance test 5 failure (VMEDMA regs)");
+ case XD_ERR_MT6:
+ return ("Maintenance test 6 failure (REGCEL chip)");
+ case XD_ERR_MT7:
+ return ("Maintenance test 7 failure (buffer parity)");
+ case XD_ERR_MT8:
+ return ("Maintenance test 8 failure (disk FIFO)");
+ case XD_ERR_IOCK:
+ return ("IOPB checksum miscompare");
+ case XD_ERR_IODM:
+ return ("IOPB DMA fatal");
+ case XD_ERR_IOAL:
+ return ("IOPB address alignment error");
+ case XD_ERR_FIRM:
+ return ("Firmware error");
+ case XD_ERR_MMOD:
+ return ("Illegal maintenance mode test number");
+ case XD_ERR_ACFL:
+ return ("ACFAIL asserted");
+ default:
+ return ("Unknown error");
+ }
+}
diff --git a/sys/arch/kbus/dev/xdreg.h b/sys/arch/kbus/dev/xdreg.h
new file mode 100644
index 00000000000..c723d018d0d
--- /dev/null
+++ b/sys/arch/kbus/dev/xdreg.h
@@ -0,0 +1,424 @@
+/* $NetBSD: xdreg.h,v 1.3 1996/03/31 22:38:54 pk 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.
+ */
+
+/*
+ * x d r e g . h
+ *
+ * this file contains the description of the Xylogics 753/7053's hardware
+ * data structures.
+ *
+ * author: Chuck Cranor <chuck@ccrc.wustl.edu>
+ */
+
+#define XDC_MAXDEV 4 /* max devices per controller */
+#define XDC_RESETUSEC 1000000 /* max time for xdc reset (page 21 says 1sec) */
+#define XDC_MAXIOPB 31 /* max number of iopbs that can be active */
+#define XDC_MAXTIME 4*1000000 /* four seconds before we give up and reset */
+#define XDC_MAXTRIES 4 /* max number of times to retry an operation */
+#define XDC_THROTTLE 32 /* dma throttle */
+#define XDC_INTERLEAVE 0 /* interleave (for format param) */
+#define XDC_DPARAM 0 /* dparam (drive param) XDDP_EC32 or 0 */
+
+/*
+ * xdc device interface
+ * (lives in VME address space)
+ */
+
+struct xdc {
+ volatile u_char filler0;
+ volatile u_char xdc_iopbaddr0; /* iopb byte 0 (LSB) */
+ volatile u_char filler1;
+ volatile u_char xdc_iopbaddr1; /* iopb byte 1 */
+ volatile u_char filler2;
+ volatile u_char xdc_iopbaddr2; /* iopb byte 2 */
+ volatile u_char filler3;
+ volatile u_char xdc_iopbaddr3; /* iopb byte 3 (MSB) */
+ volatile u_char filler4;
+ volatile u_char xdc_iopbamod; /* iopb address modifier */
+ volatile u_char filler5;
+ volatile u_char xdc_csr; /* control and status register */
+ volatile u_char filler6;
+ volatile u_char xdc_f_err; /* fatal error register */
+};
+
+/*
+ * xdc_iopbamod: addressing modes
+ * When doing DMA, if the maximum address of the buffer is greater than
+ * 24 bits then you must use the 32 bit mode. Note that on many systems
+ * (e.g. sun-4/300) DVMA space is smaller than 24 bits, so there is no
+ * need for the 32 bit mode. However, the 32-bit mode hooks are in
+ * the driver in case it ever gets ported to an environment that needs it.
+ */
+
+#define XDC_ADDRMOD 0x3d /* standard address modifer, 24 bit max */
+#define XDC_ADDRMOD32 0x0d /* 32 bit version above */
+
+/*
+ * xdc_csr
+ */
+
+#define XDC_RMAINTMD 0x80 /* reserved maintenance mode (write) */
+#define XDC_BUSY 0x80 /* busy (read) */
+#define XDC_F_ERROR 0x40 /* fatal error (read) */
+#define XDC_MAINTMOD 0x20 /* maintenance mode (read/write) */
+#define XDC_RESET 0x08 /* soft reset (read/write) */
+#define XDC_ADDIOPB 0x04 /* add iopb/add pending (write) */
+#define XDC_ADDING 0x04 /* iopb add is pending (read) */
+#define XDC_CLRRIO 0x02 /* clear RIO (remove iopb) request (write) */
+#define XDC_REMIOPB 0x02 /* remove iopb (read) */
+#define XDC_RBUSYSEM 0x01 /* register busy semaphore (read/write) */
+
+/*
+ * Input/Output Parameter Block (iopb)
+ *
+ * all controller commands are done via iopb's. to start a command you
+ * must do this:
+ * [1] allocate space in DVMA space for the iopb
+ * [2] fill out all the fields of the iopb
+ * [3] check to see if controller can accept an iopb (XDC_ADDING bit clear)
+ * [4] put the DVMA address of the iopb in the xdc registers (in vme space)
+ * [5] set the XDC_ADDIOPB bit in the xdc csr
+ * [6] <command started>
+ *
+ * when the controller is done with a command it may interrupt (if you
+ * ask it to) and it will set the XDC_REMIOPB bit in the csr. the address
+ * of the finished iopb will be in the xdc registers. after that is
+ * read, set the XDC_CLRRIO to clear the iopb out of memory.
+ *
+ * the format of the iopb is described in section 4 of the manual.
+ */
+
+struct xd_iopb {
+ /* section 4.1.1: byte 0 */
+ volatile u_char errs:1; /* error summary bit, only valid if
+ "done" is set. must clear "done"
+ and "errs" bits before starting an
+ operation */
+ volatile u_char done:1; /* "done" bit */
+ volatile u_char chen:1; /* chain enable, "next iopb" is valid,
+ note xd returns one iopb at a time */
+ volatile u_char sgm:1; /* scatter/gather mode */
+ volatile u_char comm:4; /* command number (see table 4-2) */
+#define XDCMD_NOP 0x0 /* no-op */
+#define XDCMD_WR 0x1 /* write */
+#define XDCMD_RD 0x2 /* read */
+#define XDCMD_SK 0x3 /* seek */
+#define XDCMD_RST 0x4 /* drive reset */
+#define XDCMD_WRP 0x5 /* write params */
+#define XDCMD_RDP 0x6 /* read params */
+#define XDCMD_XWR 0x7 /* extended write */
+#define XDCMD_XRD 0x8 /* extended read */
+#define XDCMD_TST 0x9 /* diagnostic tests */
+ /* 0xa to 0xf are reserved */
+ /* section 4.1.2: byte 1 */
+ volatile u_char errno; /* status byte 1 (non-zero if error) */
+ /* section 4.1.3: byte 2 */
+ volatile u_char status; /* status byte 2 (see below) */
+#define XDST_SR 0x40 /* slipped revolution */
+#define XDST_CSE 0x20 /* count sectors executed */
+#define XDST_WRPT 0x10 /* write protected drive */
+#define XDST_DFLT 0x08 /* disk fault */
+#define XDST_SKER 0x04 /* seek error: >max, or timeout */
+#define XDST_ONCL 0x02 /* on-cylinder */
+#define XDST_DRDY 0x01 /* drive is ready! */
+ /* section 4.1.4: byte 3 */
+ volatile u_char istat; /* internal status: reserved for xylogics */
+ /* section 4.1.5: byte 4 */
+ volatile u_char subfun; /* sub-function of command (see below) */
+#define XDFUN_R 0x00 /* XDCMD_SK: report current addr */
+#define XDFUN_SR 0x01 /* XDCMD_SK: seek and report addr */
+#define XDFUN_SRI 0x02 /* XDCMD_SK: start seek, report comp imm */
+#define XDFUN_CTL 0x00 /* XDCMD_{WRP,RDP}: controller params */
+#define XDFUN_DRV 0x80 /* XDCMD_{WRP,RDP}: drive params */
+#define XDFUN_FMT 0x81 /* XDCMD_{WRP,RDP}: format params,XWR form.*/
+#define XDFUN_STX 0xa0 /* XDCMD_RDP: read drive status extended */
+#define XDFUN_THD 0x80 /* XDCMD_{XWR,XRD}: track headers */
+#define XDFUN_VFY 0x81 /* XDCMD_XRD: verify data */
+#define XDFUN_HDR 0x82 /* XDCMD_{XWR,XRD}: header, verify,data, ecc*/
+#define XDFUN_DM 0xa0 /* XDCMD_{XWR,XRD}: defect map */
+#define XDFUN_DMX 0xa1 /* XDCMD_{XWR,XRD}: defect map extended */
+ /* section 4.1.6: byte 5 */
+ volatile u_char fixd:1; /* fixed media (vs removeable) */
+ volatile u_char reserved1:4; /* reserved */
+ volatile u_char unit:3; /* unit number */
+ /* note: 6 to 13 are overloaded (see below) */
+ /* section 4.1.7: byte 6 */
+ volatile u_char lll:5; /* linked list length */
+ volatile u_char intl:3; /* interrupt level */
+ /* section 4.1.8: byte 7 */
+ volatile u_char intr_vec; /* interrupt vector */
+ /* section 4.1.9: bytes 8 and 9 */
+ volatile u_short sectcnt; /* sector count (# to xfer) */
+ /* section 4.1.10: byte a and b */
+ volatile u_short cylno; /* cylinder number */
+ /* section 4.1.11: byte c */
+ volatile u_char headno; /* head number */
+ /* section 4.1.12: byte d */
+ volatile u_char sectno; /* sector number */
+ /* section 4.1.13: byte e */
+ volatile u_char addrmod; /* addr modifier (bits 7,6 must be zero) */
+ /* section 4.1.14: byte f */
+ volatile u_char naddrmod; /* next (in chain) address iobp ad. modifer */
+ /* section 4.1.15: bytes 0x10 to 0x13 */
+ volatile u_long daddr; /* DMA data address */
+ /* section 4.1.16: bytes 0x14 to 0x17 */
+ volatile u_long nextiopb; /* next iopb (in chain) address */
+ /* section 4.1.17: bytes 0x18, 0x19 */
+ volatile u_short cksum; /* iopb checksum */
+ /* section 4.1.18: bytes 0x1a, 0x1b */
+ volatile u_short eccpattern; /* ECC pattern word (ecc mode 0) */
+ /* section 4.1.19: bytes 0x1c, 0x1d */
+ volatile u_short eccoffword; /* ECC offset word (ecc mode 0) */
+};
+
+/*
+ * some commands overload bytes 6 to 0x13 of the iopb with different meanings.
+ * these commands include:
+ * section 4.2: controller parameters
+ * section 4.3: drive parameters
+ * sectino 4.4: format parameters
+ *
+ * note that the commands that overload the iopb are not part of the
+ * "critical data path" of the driver. so, we handle them by defining
+ * alternate iopb structures for these commands... it only costs us an
+ * extra pointer.
+ */
+
+/*
+ * controller parameters iopb: redefines bytes: 8 -> 0xe, 0x10 -> 0x13
+ */
+
+struct xd_iopb_ctrl {
+ volatile u_char same[8]; /* same as xd_iopb */
+ /* section 4.2.1: byte 8 */
+ volatile u_char param_a; /* param A (see below) */
+#define XDPA_AUD 0x80 /* auto-update iopb fields when cmd done */
+#define XDPA_TMOD 0x40 /* long-word transfer mode (vs word) */
+#define XDPA_DACF 0x20 /* ignore vme ACFAIL signal */
+#define XDPA_ICS 0x10 /* checksum check (adds 100usec per xfer) */
+#define XDPA_EDT 0x08 /* enable on-board DMA timeout timer */
+#define XDPA_NPRM 0x04 /* enable VME non-priv request mode */
+ /* rest must be zero */
+ /* section 4.2.2: byte 9 */
+ volatile u_char param_b; /* param B (see below) */
+#define XDPB_TDT 0xc0 /* throttle dead time (see 8.11, below) */
+#define XDPB_ROR 0x10 /* release on request */
+#define XDPB_DRA 0x01 /* disable read ahead */
+ /* TDT values: */
+#define XDPB_TDT_0USEC 0x00 /* no TDT */
+#define XDPB_TDT_3_2USEC 0x40 /* 3.2 usec */
+#define XDPB_TDT_6_4USEC 0x80 /* 6.4 usec */
+#define XDPB_TDT_12_8USEC 0xc0 /* 12.8 usec */
+ /* section 4.2.3: byte a */
+ volatile u_char param_c; /* param C (see below) */
+#define XDPC_OVS 0x80 /* over-lapped seek */
+#define XDPC_COP 0x40 /* command optimiziation (elevator alg.) */
+#define XDPC_ASR 0x10 /* auto-seek retry */
+#define XDPC_RBC 0x04 /* retry before correction if ECC error */
+#define XDPC_ECCM 0x03 /* ECC mode (0, 1, and 2) */
+#define XDPC_ECC0 0x00 /* ECC mode 0 */
+#define XDPC_ECC1 0x01 /* ECC mode 1 */
+#define XDPC_ECC2 0x02 /* ECC mode 2 */
+ /* section 4.2.4: byte b */
+ volatile u_char throttle; /* max dma xfers per master (0==256) */
+ /* section 4.2.5: byte c */
+ volatile u_char eprom_lvl; /* EPROM release level */
+ volatile u_char delay; /* delay (see note below) */
+ /* section 4.2.6: byte e */
+ volatile u_char ctype; /* controller type */
+#define XDCT_753 0x53 /* xylogic 753/7053 */
+ volatile u_char same2; /* byte f: same as xd_iopb */
+ /* section 4.2.7: byte 0x10, 0x11 */
+ volatile u_short eprom_partno; /* eprom part number */
+ /* section 4.2.8: byte 12 */
+ volatile u_char eprom_rev; /* eprom revision number */
+};
+
+/*
+ * Note on byte 0xd ("delay"): This byte is not documented in the
+ * Xylogics manual. However, I contacted Xylogics and found out what
+ * it does. The controller sorts read commands into groups of
+ * contiguous sectors. After it processes a group of contiguous
+ * sectors rather than immediately going on to the next group of
+ * contiguous sectors, the controller can delay for a certain amount
+ * of time in hopes of getting another cluster of reads in the same
+ * area of the disk (thus avoiding a long seek). Byte 0xd controls
+ * how long it waits before giving up and going on and doing the next
+ * contiguous cluster.
+ *
+ * it is unclear what unit the delay is in, but it looks like sun
+ * uses the value "20" for sun3's, and "0" for sparc, except for the
+ * 4/300 (where it is "4"). [see /sys/sundev/xd_conf.c on any 4.1.3
+ * machine for how sun configures its controller...]
+ */
+
+#define XDC_DELAY_SUN3 20
+#define XDC_DELAY_4_300 4
+#define XDC_DELAY_SPARC 0
+
+/*
+ * drive parameters iopb: redefines bytes: 6, 8, 9, a, b, c, d, e
+ */
+
+struct xd_iopb_drive {
+ volatile u_char same[6]; /* same as xd_iopb */
+ /* section 4.3.1: byte 6 */
+ volatile u_char dparam_ipl; /* drive params | interrupt level */
+#define XDDP_EC32 0x10 /* 32 bit ECC mode */
+ volatile u_char same1; /* byte 7: same */
+ /* section 4.3.2: byte 8 */
+ volatile u_char maxsect; /* max sector/last head (<= byte d) */
+ /* section 4.3.3: byte 9 */
+ volatile u_char headoff; /* head offset */
+ /* section 4.3.4: bytes 0xa, 0xb */
+ volatile u_short maxcyl; /* max cyl (zero based!) */
+ /* section 4.3.5: byte 0xc */
+ volatile u_char maxhead; /* max head (zero based!) */
+ /* section 4.3.6: byte 0xd */
+ volatile u_char maxsector; /* max sector of disk (zero based!) */
+ /* section 4.3.7: byte 0xe */
+ volatile u_char sectpertrk; /* sectors per track, not zero base, no runt*/
+};
+
+/*
+ * format parameters iopb: redefines bytes: 6, 8, 9, a, b, c, d, 0x10, 0x11
+ */
+
+struct xd_iopb_format {
+ volatile u_char same[6]; /* smae as xd_iopb */
+ /* section 4.4.1: byte 6 */
+ volatile u_char interleave_ipl;/* (interleave << 4) | interupt level */
+ /* interleave ratio 1:1 to 16:1 */
+ volatile u_char same1; /* byte 7: same */
+ /* section 4.4.2: byte 8 */
+ volatile u_char field1; /* >= 1, xylogic says should be 1 */
+#define XDFM_FIELD1 0x01 /* xylogic value */
+ /* section 4.4.3: byte 9 */
+ volatile u_char field2; /* >0, field1+field2 <= 255 */
+#define XDFM_FIELD2 0x0a /* xylogic value */
+ /* section 4.4.4: byte a */
+ volatile u_char field3; /* >= field1+field2 */
+#define XDFM_FIELD3 0x1b /* xylogic value */
+ /* section 4.4.5: byte b */
+ volatile u_char field4; /* field4 */
+#define XDFM_FIELD4 0x14 /* xylogic value */
+ /* section 4.4.6: bytes 0xc, 0xd */
+ volatile u_short bytespersec; /* bytes per sector */
+#define XDFM_BPS 0x200 /* must be 512! */
+ volatile u_char same2[2]; /* bytes e, f */
+ /* section 4.4.7: byte 0x10 */
+ volatile u_char field6; /* field 6 */
+#define XDFM_FIELD6 0x0a /* xylogic value */
+ /* section 4.4.8: byte 0x11 */
+ volatile u_char field7; /* field 7, >= 1 */
+#define XDFM_FIELD7 0x03 /* xylogic value */
+};
+
+
+/*
+ * errors: errors come from either the fatal error register or the
+ * iopb
+ */
+
+#define XD_ERA_MASK 0xf0 /* error action mask */
+#define XD_ERA_PROG 0x10 /* program error */
+#define XD_ERA_PRG2 0x20 /* program error */
+#define XD_ERA_SOFT 0x30 /* soft error: we recovered */
+#define XD_ERA_HARD 0x40 /* hard error: retry */
+#define XD_ERA_RSET 0x60 /* hard error: reset, then retry */
+#define XD_ERA_WPRO 0x90 /* write protected */
+
+/* software error codes */
+#define XD_ERR_FAIL 0xff /* general total failure */
+/* no error */
+#define XD_ERR_AOK 0x00 /* success */
+/* non-retryable programming error */
+#define XD_ERR_ICYL 0x10 /* illegal cyl */
+#define XD_ERR_IHD 0x11 /* illegal head */
+#define XD_ERR_ISEC 0x12 /* illegal sector */
+#define XD_ERR_CZER 0x13 /* count zero */
+#define XD_ERR_UIMP 0x14 /* unknown command */
+#define XD_ERR_IF1 0x15 /* illegal field 1 */
+#define XD_ERR_IF2 0x16 /* illegal field 2 */
+#define XD_ERR_IF3 0x17 /* illegal field 3 */
+#define XD_ERR_IF4 0x18 /* illegal field 4 */
+#define XD_ERR_IF5 0x19 /* illegal field 5 */
+#define XD_ERR_IF6 0x1a /* illegal field 6 */
+#define XD_ERR_IF7 0x1b /* illegal field 7 */
+#define XD_ERR_ISG 0x1c /* illegal scatter/gather */
+#define XD_ERR_ISPT 0x1d /* not enough sectors per track */
+#define XD_ERR_ALGN 0x1e /* next iopb allignment error */
+#define XD_ERR_SGAL 0x1f /* scatter gather address alignment error */
+#define XD_ERR_SGEC 0x20 /* scatter gather with auto ECC */
+/* successfully recovered soft errors */
+#define XD_ERR_SECC 0x30 /* soft ecc corrected */
+#define XD_ERR_SIGN 0x31 /* ecc ignored */
+#define XD_ERR_ASEK 0x32 /* auto-seek retry recovered */
+#define XD_ERR_RTRY 0x33 /* soft retry recovered */
+/* hard errors: please retry */
+#define XD_ERR_HECC 0x40 /* hard data ECC */
+#define XD_ERR_NHDR 0x41 /* header not found */
+#define XD_ERR_NRDY 0x42 /* drive not ready */
+#define XD_ERR_TOUT 0x43 /* timeout */
+#define XD_ERR_VTIM 0x44 /* VME DMA timeout */
+#define XD_ERR_DSEQ 0x45 /* disk sequencer error */
+#define XD_ERR_HDEC 0x48 /* header ECC error */
+#define XD_ERR_RVFY 0x49 /* ready verify */
+#define XD_ERR_VFER 0x4a /* fatal VME DMA error */
+#define XD_ERR_VBUS 0x4b /* VME bus error */
+/* hard error: reset and retry */
+#define XD_ERR_DFLT 0x60 /* drive fault */
+#define XD_ERR_HECY 0x61 /* header error/cyl */
+#define XD_ERR_HEHD 0x62 /* header error/head */
+#define XD_ERR_NOCY 0x63 /* not on cylinder */
+#define XD_ERR_SEEK 0x64 /* seek error */
+/* fatal hardware error */
+#define XD_ERR_ILSS 0x70 /* illegal sector size */
+/* misc */
+#define XD_ERR_SEC 0x80 /* soft ecc */
+/* requires manual intervention */
+#define XD_ERR_WPER 0x90 /* write protected */
+/* FATAL errors */
+#define XD_ERR_IRAM 0xe1 /* IRAM self test failed */
+#define XD_ERR_MT3 0xe3 /* maint test 3 failed (DSKCEL RAM) */
+#define XD_ERR_MT4 0xe4 /* maint test 4 failed (Header shift reg) */
+#define XD_ERR_MT5 0xe5 /* maint test 5 failed (VMEDMA regs) */
+#define XD_ERR_MT6 0xe6 /* maint test 6 failed (REGCEL chip) */
+#define XD_ERR_MT7 0xe7 /* maint test 7 failed (buff. parity) */
+#define XD_ERR_MT8 0xe8 /* maint test 8 failed (fifo) */
+#define XD_ERR_IOCK 0xf0 /* iopb checksume miscompare */
+#define XD_ERR_IODM 0xf1 /* iopb dma fatal */
+#define XD_ERR_IOAL 0xf2 /* iopb allignment error */
+#define XD_ERR_FIRM 0xf3 /* firmware error n*/
+#define XD_ERR_MMOD 0xf5 /* illegal maint mode test number */
+#define XD_ERR_ACFL 0xf6 /* ACFAIL asserted */
diff --git a/sys/arch/kbus/dev/xdvar.h b/sys/arch/kbus/dev/xdvar.h
new file mode 100644
index 00000000000..294f3a8c84b
--- /dev/null
+++ b/sys/arch/kbus/dev/xdvar.h
@@ -0,0 +1,167 @@
+/* $NetBSD: xdvar.h,v 1.5 1996/03/31 22:38:56 pk 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.
+ */
+
+/*
+ * x d v a r . h
+ *
+ * this file defines the software structure we use to control the
+ * 753/7053.
+ *
+ * author: Chuck Cranor <chuck@ccrc.wustl.edu>
+ */
+
+/*
+ * i/o request: wrapper for hardware's iopb data structure
+ */
+
+struct xd_iorq {
+ struct xd_iopb *iopb; /* address of matching iopb */
+ struct xdc_softc *xdc; /* who we are working with */
+ struct xd_softc *xd; /* which disk */
+ int ttl; /* time to live */
+ int mode; /* current mode (state+other data) */
+ int tries; /* number of times we have tried it */
+ int errno; /* error number if we fail */
+ int lasterror; /* last error we got */
+ int blockno; /* starting block no for this xfer */
+ int sectcnt; /* number of sectors in xfer */
+ char *dbuf; /* KVA of data buffer (advances) */
+ char *dbufbase; /* base of dbuf */
+ struct buf *buf; /* for NORM */
+};
+
+/*
+ * state
+ */
+
+#define XD_SUB_MASK 0xf0 /* mask bits for state */
+#define XD_SUB_FREE 0x00 /* free */
+#define XD_SUB_NORM 0x10 /* normal I/O request */
+#define XD_SUB_WAIT 0x20 /* normal I/O request in the
+ context of a process */
+#define XD_SUB_POLL 0x30 /* polled mode */
+#define XD_SUB_DONE 0x40 /* not active, but can't be free'd yet */
+#define XD_SUB_NOQ 0x50 /* don't queue, just submit (internal) */
+
+#define XD_STATE(X) ((X) & XD_SUB_MASK) /* extract state from mode */
+#define XD_NEWSTATE(OLD, NEW) (((OLD) & ~XD_SUB_MASK) |(NEW)) /* new state */
+
+
+/*
+ * other mode data
+ */
+
+#define XD_MODE_VERBO 0x08 /* print error messages */
+#define XD_MODE_B144 0x04 /* handling a bad144 sector */
+
+
+/*
+ * software timers and flags
+ */
+
+#define XDC_SUBWAITLIM 4 /* max number of "done" IOPBs there can be
+ where we still allow a SUB_WAIT command */
+#define XDC_TICKCNT (5*hz) /* call xdc_tick on this interval (5 sec) */
+#define XDC_MAXTTL 2 /* max number of xd ticks to live */
+#define XDC_NOUNIT (-1) /* for xdcmd: no unit number */
+
+/*
+ * a "xd_softc" structure contains per-disk state info.
+ */
+
+struct xd_softc {
+ struct device sc_dev; /* device struct, reqd by autoconf */
+ struct disk sc_dk; /* generic disk info */
+ struct xdc_softc *parent; /* parent */
+ u_short flags; /* flags */
+ u_short state; /* device state */
+ int xd_drive; /* unit number */
+ /* geometry */
+ u_short ncyl, acyl, pcyl; /* number of cyl's */
+ u_short sectpercyl; /* nhead*nsect */
+ u_char nhead; /* number of heads */
+ u_char nsect; /* number of sectors per track */
+ u_char hw_spt; /* as above, but includes spare sectors */
+ struct dkbad dkb; /* bad144 sectors */
+};
+
+/*
+ * flags
+ */
+
+#define XD_WLABEL 0x0001 /* write label */
+/*
+ * state
+ */
+
+#define XD_DRIVE_UNKNOWN 0 /* never talked to it */
+#define XD_DRIVE_ATTACHING 1 /* attach in progress */
+#define XD_DRIVE_NOLABEL 2 /* drive on-line, no label */
+#define XD_DRIVE_ONLINE 3 /* drive is on-line */
+
+/*
+ * a "xdc_softc" structure contains per-disk-controller state info,
+ * including a list of active controllers.
+ */
+
+struct xdc_softc {
+ struct device sc_dev; /* device struct, reqd by autoconf */
+ struct intrhand sc_ih; /* interrupt info */
+ struct evcnt sc_intrcnt; /* event counter (for vmstat -i) */
+
+ struct xdc *xdc; /* vaddr of vme registers */
+
+ struct xd_softc *sc_drives[XDC_MAXDEV]; /* drives on this controller */
+ int ipl; /* interrupt level */
+ int vector; /* interrupt vector */
+
+ struct xd_iorq *reqs; /* i/o requests */
+ struct xd_iopb *iopbase; /* iopb base addr (maps iopb->iorq) */
+ struct xd_iopb *dvmaiopb; /* iopb base in DVMA space, not kvm */
+ struct buf sc_wq; /* queue'd IOPBs for this controller */
+ char freereq[XDC_MAXIOPB]; /* free list (stack) */
+ char waitq[XDC_MAXIOPB]; /* wait queue */
+ u_char nfree; /* number of iopbs free */
+ u_char nrun; /* number running */
+ u_char nwait; /* number of waiting iopbs */
+ u_char ndone; /* number of done IORQs */
+ u_char waithead; /* head of queue */
+ u_char waitend; /* end of queue */
+};
+
+/*
+ * reset blast modes
+ */
+
+#define XD_RSET_NONE (-1) /* restart all requests */
+#define XD_RSET_ALL (-2) /* don't restart anything */
diff --git a/sys/arch/kbus/dev/xio.h b/sys/arch/kbus/dev/xio.h
new file mode 100644
index 00000000000..1ad5662b6be
--- /dev/null
+++ b/sys/arch/kbus/dev/xio.h
@@ -0,0 +1,65 @@
+/* $NetBSD: xio.h,v 1.2 1996/03/31 22:38:58 pk 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.
+ */
+
+/*
+ * x i o . h
+ *
+ * this file defines the software structure we use to ioctl the
+ * 753/7053. this interface isn't set in stone and may (or may not)
+ * need adjustment.
+ *
+ * author: Chuck Cranor <chuck@ccrc.wustl.edu>
+ */
+
+/*
+ * xylogic ioctl interface
+ */
+
+struct xd_iocmd {
+ u_char cmd; /* in: command number */
+ u_char subfn; /* in: subfunction number */
+ u_char errno; /* out: error number */
+ u_char tries; /* out: number of tries */
+ u_short sectcnt; /* in,out: sector count (hw_spt on read drive param) */
+ u_short dlen; /* in: length of data buffer (good sanity check) */
+ u_long block; /* in: block number */
+ caddr_t dptr; /* in: data buffer to do I/O from */
+};
+
+#ifndef DIOSXDCMD
+#define DIOSXDCMD _IOWR('x', 101, struct xd_iocmd) /* do xd command */
+#endif
+
+#define XD_IOCMD_MAXS 16 /* max number of sectors you can do */
+#define XD_IOCMD_HSZ 4 /* size of one header */
+#define XD_IOCMD_DMSZ 24 /* defect map size */
diff --git a/sys/arch/kbus/dev/zs.c b/sys/arch/kbus/dev/zs.c
new file mode 100644
index 00000000000..2973afabe92
--- /dev/null
+++ b/sys/arch/kbus/dev/zs.c
@@ -0,0 +1,745 @@
+/* $OpenBSD: zs.c,v 1.1 1997/10/14 07:25:29 gingold Exp $ */
+/* $NetBSD: zs.c,v 1.42 1996/11/20 18:57:03 gwr Exp $ */
+
+/*-
+ * Copyright (c) 1996 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Gordon W. Ross.
+ *
+ * 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 NetBSD
+ * Foundation, Inc. and its contributors.
+ * 4. Neither the name of The NetBSD Foundation 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 NETBSD FOUNDATION, INC. 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.
+ */
+
+/*
+ * Zilog Z8530 Dual UART driver (machine-dependent part)
+ *
+ * Runs two serial lines per chip using slave drivers.
+ * Plain tty/async lines use the zs_async slave.
+ * Sun keyboard/mouse uses the zs_kbd/zs_ms slaves.
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/proc.h>
+#include <sys/device.h>
+#include <sys/conf.h>
+#include <sys/file.h>
+#include <sys/ioctl.h>
+#include <sys/tty.h>
+#include <sys/time.h>
+#include <sys/kernel.h>
+#include <sys/syslog.h>
+
+#include <dev/cons.h>
+#include <dev/ic/z8530reg.h>
+#include <machine/z8530var.h>
+
+#include <machine/autoconf.h>
+#include <machine/cpu.h>
+/* #include <machine/obio.h> */
+
+/*
+ * XXX: Hard code this to make console init easier...
+ */
+#define NZS 2 /* XXX */
+
+
+/*
+ * The Sun3 provides a 3.6864 MHz clock to the ZS chips.
+ */
+#define PCLK (3686400) /* PCLK pin input clock rate */
+
+/*
+ * Define interrupt levels.
+ */
+#define ZSHARD_PRI 6 /* Wired on the CPU board... */
+#define ZSSOFT_PRI 3 /* Want tty pri (4) but this is OK. */
+
+#define ZS_DELAY() delay(3)
+
+/*
+ * The layout of this is hardware-dependent (padding, order).
+ */
+struct zschan {
+ volatile u_char zc_csr; /* ctrl,status, and indirect access */
+ u_char zc_pad2[15];
+ volatile u_char zc_data; /* data */
+ u_char zc_pad1[15];
+};
+#define ZS_SIZE (sizeof (struct zschan))
+
+struct zsdevice {
+ /* Yes, they are backwards. */
+ struct zschan zs_chan_b;
+ struct zschan zs_chan_a;
+};
+
+
+/* Default OBIO addresses. */
+static int zs_physaddr[NZS] = {0x17011000, 0x17012000};
+
+/* Saved PROM mappings */
+static struct zsdevice *zsaddr[NZS]; /* See zs_init() */
+
+/* Flags from cninit() */
+static int zs_hwflags[NZS][2];
+
+/* Default speed for each channel */
+static int zs_defspeed[NZS][2] = {
+ { 1200, /* keyboard */
+ 1200 }, /* mouse */
+ { 9600, /* ttya */
+ 9600 }, /* ttyb */
+};
+
+
+static struct zschan *zs_get_chan_addr __P((int, int));
+int zs_getc __P((volatile void *));
+static void zs_putc __P((volatile void *, int));
+
+int zscngetc __P((dev_t));
+void zscnputc __P((dev_t, int));
+void nullcnprobe __P((struct consdev *));
+void zscninit __P((struct consdev *));
+
+void umprintf __P((const char *, ...));
+
+/* Find PROM mappings (for console support). */
+void zs_init __P((void));
+void zs_init()
+{
+ int i;
+
+ for (i = 0; i < NZS; i++) {
+ zsaddr[i] = (struct zsdevice *)
+ bus_mapin (BUS_KBUS, zs_physaddr[i], ZS_SIZE);
+ }
+}
+
+
+static struct zschan *
+zs_get_chan_addr(zsc_unit, channel)
+ int zsc_unit, channel;
+{
+ struct zsdevice *addr;
+ struct zschan *zc;
+
+ if (zsc_unit >= NZS)
+ return NULL;
+ addr = zsaddr[zsc_unit];
+ if (addr == NULL)
+ return NULL;
+ if (channel == 0) {
+ zc = &addr->zs_chan_a;
+ } else {
+ zc = &addr->zs_chan_b;
+ }
+ return (zc);
+}
+
+
+static u_char zs_init_reg[16] = {
+ 0, /* 0: CMD (reset, etc.) */
+ ZSWR1_RIE | ZSWR1_SIE, /* | ZSWR1_TIE, */
+ 0, /* 0x18 + ZSHARD_PRI, * IVECT */
+ ZSWR3_RX_8 | ZSWR3_RX_ENABLE,
+ ZSWR4_CLK_X16 | ZSWR4_ONESB | ZSWR4_EVENP,
+ ZSWR5_TX_8 | ZSWR5_TX_ENABLE,
+ 0, /* 6: TXSYNC/SYNCLO */
+ 0, /* 7: RXSYNC/SYNCHI */
+ 0, /* 8: alias for data port */
+ ZSWR9_MASTER_IE,
+ 0, /*10: Misc. TX/RX control bits */
+ ZSWR11_TXCLK_BAUD | ZSWR11_RXCLK_BAUD,
+ 14, /*12: BAUDLO (default=9600) */
+ 0, /*13: BAUDHI (default=9600) */
+ ZSWR14_BAUD_FROM_PCLK | ZSWR14_BAUD_ENA,
+ ZSWR15_BREAK_IE | ZSWR15_DCD_IE
+};
+
+
+/****************************************************************
+ * Autoconfig
+ ****************************************************************/
+
+/* Definition of the driver for autoconfig. */
+static int zsc_match __P((struct device *, void *, void *));
+static void zsc_attach __P((struct device *, struct device *, void *));
+static int zsc_print __P((void *, const char *name));
+
+struct cfattach zsc_ca = {
+ sizeof(struct zsc_softc), zsc_match, zsc_attach
+};
+
+struct cfdriver zsc_cd = {
+ NULL, "zsc", DV_DULL
+};
+
+static int zshard(void *);
+int zssoft(void *);
+static struct intrhand levelhard = { zshard };
+/* static struct intrhand levelsoft = { zssoft }; */
+
+/*
+ * Is the zs chip present?
+ */
+
+static int
+zsc_match(parent, vcf, aux)
+ struct device *parent;
+ void *vcf, *aux;
+{
+ struct cfdata *cf = vcf;
+ struct confargs *ca = aux;
+ int pa, unit, x;
+ void *va;
+
+ if (ca->ca_bustype != BUS_KBUS)
+ return 0;
+
+ unit = cf->cf_unit;
+ if (unit < 0 || unit >= NZS)
+ return (0);
+
+ /*
+ * OBIO match functions may be called for every possible
+ * physical address, so match only our physical address.
+ * This driver only supports its default mappings, so
+ * non-default locators must match our defaults.
+ */
+ if ((pa = ca->ca_paddr) == -1) {
+ /* Use our default PA. */
+ pa = zs_physaddr[unit];
+ } else {
+ /* Validate the given PA. */
+ if (pa != zs_physaddr[unit])
+ return (0);
+ }
+
+ if (pa != ca->ca_paddr)
+ return (0);
+
+ /* Make sure zs_init() found mappings. */
+ va = zsaddr[unit];
+ if (va == NULL)
+ return (0);
+
+ /* This returns -1 on a fault (bus error). */
+ x = peek_byte(va);
+ return (x != -1);
+}
+
+/*
+ * Attach a found zs.
+ *
+ * Match slave number to zs unit number, so that misconfiguration will
+ * not set up the keyboard as ttya, etc.
+ */
+static void
+zsc_attach(parent, self, aux)
+ struct device *parent;
+ struct device *self;
+ void *aux;
+{
+ struct zsc_softc *zsc = (void *) self;
+/* struct cfdata *cf = self->dv_cfdata; */
+ struct zsc_attach_args zsc_args;
+ volatile struct zschan *zc;
+ struct zs_chanstate *cs;
+ int zsc_unit, intpri, channel;
+ int reset, s;
+ static int didintr;
+
+ zsc_unit = zsc->zsc_dev.dv_unit;
+
+/* if ((intpri = cf->cf_intpri) == -1) */
+ intpri = ZSHARD_PRI;
+
+ printf(" level %d (softpri %d)\n", intpri, ZSSOFT_PRI);
+
+ /* Use the mapping setup by the Sun PROM. */
+ if (zsaddr[zsc_unit] == NULL)
+ panic("zs_attach: zs%d not mapped\n", zsc_unit);
+
+ /*
+ * Initialize software state for each channel.
+ */
+ for (channel = 0; channel < 2; channel++) {
+ cs = &zsc->zsc_cs[channel];
+
+ zc = zs_get_chan_addr(zsc_unit, channel);
+ cs->cs_reg_csr = &zc->zc_csr;
+ cs->cs_reg_data = &zc->zc_data;
+
+ cs->cs_channel = channel;
+ cs->cs_private = NULL;
+ cs->cs_ops = &zsops_null;
+
+ /* Define BAUD rate clock for the MI code. */
+ cs->cs_brg_clk = PCLK / 16;
+
+ /* XXX: get defspeed from EEPROM instead? */
+ cs->cs_defspeed = zs_defspeed[zsc_unit][channel];
+
+ bcopy(zs_init_reg, cs->cs_creg, 16);
+ bcopy(zs_init_reg, cs->cs_preg, 16);
+
+ /*
+ * Clear the master interrupt enable.
+ * The INTENA is common to both channels,
+ * so just do it on the A channel.
+ */
+ if (channel == 0) {
+ zs_write_reg(cs, 9, 0);
+ }
+
+ /*
+ * Look for a child driver for this channel.
+ * The child attach will setup the hardware.
+ */
+ zsc_args.channel = channel;
+ zsc_args.hwflags = zs_hwflags[zsc_unit][channel];
+ if (config_found(self, (void *)&zsc_args, zsc_print) == NULL) {
+ /* No sub-driver. Just reset it. */
+ reset = (channel == 0) ?
+ ZSWR9_A_RESET : ZSWR9_B_RESET;
+ s = splzs();
+ zs_write_reg(cs, 9, reset);
+ splx(s);
+ }
+ }
+
+ /* Now safe to install interrupt handlers */
+ if (!didintr) {
+ didintr = 1;
+ /* intr_establish (ZSSOFT_PRI, 0, &levelsoft); */
+ intr_establish (INTR_ZS, IH_CAN_DELAY, &levelhard);
+ }
+
+ /*
+ * Set the master interrupt enable and interrupt vector.
+ * (common to both channels, do it on A)
+ */
+ cs = &zsc->zsc_cs[0];
+ s = splzs();
+ /* interrupt vector */
+ zs_write_reg(cs, 2, zs_init_reg[2]);
+ /* master interrupt control (enable) */
+ zs_write_reg(cs, 9, zs_init_reg[9]);
+ splx(s);
+}
+
+static int
+zsc_print(aux, name)
+ void *aux;
+ const char *name;
+{
+ struct zsc_attach_args *args = aux;
+
+ if (name != NULL)
+ printf("%s: ", name);
+
+ if (args->channel != -1)
+ printf(" channel %d", args->channel);
+
+ return UNCONF;
+}
+
+static int
+zshard(arg)
+ void *arg;
+{
+ struct zsc_softc *zsc;
+ int unit, rval;
+
+ /* Do ttya/ttyb first, because they go faster. */
+ rval = 0;
+ unit = zsc_cd.cd_ndevs;
+ while (--unit >= 0) {
+ zsc = zsc_cd.cd_devs[unit];
+ if (zsc != NULL) {
+ rval |= zsc_intr_hard(zsc);
+ }
+ }
+ return rval;
+}
+
+int zssoftpending;
+
+void
+zsc_req_softint(zsc)
+ struct zsc_softc *zsc;
+{
+ if (zssoftpending == 0) {
+ /* We are at splzs here, so no need to lock. */
+ zssoftpending = ZSSOFT_PRI;
+ setsoftzs ();
+ }
+}
+
+int
+zssoft(arg)
+ void *arg;
+{
+ struct zsc_softc *zsc;
+ int unit;
+
+ /* This is not the only ISR on this IPL. */
+ if (zssoftpending == 0)
+ return (0);
+
+ /*
+ * The soft intr. bit will be set by zshard only if
+ * the variable zssoftpending is zero. The order of
+ * these next two statements prevents our clearing
+ * the soft intr bit just after zshard has set it.
+ */
+/* isr_soft_clear(ZSSOFT_PRI); */
+ zssoftpending = 0;
+
+ /* Do ttya/ttyb first, because they go faster. */
+ unit = zsc_cd.cd_ndevs;
+ while (--unit >= 0) {
+ zsc = zsc_cd.cd_devs[unit];
+ if (zsc != NULL) {
+ (void) zsc_intr_soft(zsc);
+ }
+ }
+ return (1);
+}
+
+
+/*
+ * Read or write the chip with suitable delays.
+ */
+
+u_char
+zs_read_reg(cs, reg)
+ struct zs_chanstate *cs;
+ u_char reg;
+{
+ u_char val;
+
+ *cs->cs_reg_csr = reg;
+ ZS_DELAY();
+ val = *cs->cs_reg_csr;
+ ZS_DELAY();
+ return val;
+}
+
+void
+zs_write_reg(cs, reg, val)
+ struct zs_chanstate *cs;
+ u_char reg, val;
+{
+ *cs->cs_reg_csr = reg;
+ ZS_DELAY();
+ *cs->cs_reg_csr = val;
+ ZS_DELAY();
+}
+
+u_char
+zs_read_csr(cs)
+ struct zs_chanstate *cs;
+{
+ register u_char v;
+
+ v = *cs->cs_reg_csr;
+ ZS_DELAY();
+ return v;
+}
+
+u_char
+zs_read_data(cs)
+ struct zs_chanstate *cs;
+{
+ register u_char v;
+
+ v = *cs->cs_reg_data;
+ ZS_DELAY();
+ return v;
+}
+
+void zs_write_csr(cs, val)
+ struct zs_chanstate *cs;
+ u_char val;
+{
+ *cs->cs_reg_csr = val;
+ ZS_DELAY();
+}
+
+void
+zs_write_data(cs, val)
+ struct zs_chanstate *cs;
+ u_char val;
+{
+ *cs->cs_reg_data = val;
+ ZS_DELAY();
+}
+
+
+/****************************************************************
+ * Console support functions (Sun3 specific!)
+ ****************************************************************/
+
+/*
+ * Polled input char.
+ */
+int
+zs_getc(arg)
+ volatile void *arg;
+{
+ register volatile struct zschan *zc = arg;
+ register int s, c, rr0;
+
+ s = splhigh();
+ /* Wait for a character to arrive. */
+ do {
+ rr0 = zc->zc_csr;
+ ZS_DELAY();
+ } while ((rr0 & ZSRR0_RX_READY) == 0);
+
+ c = zc->zc_data;
+ ZS_DELAY();
+ splx(s);
+
+ /*
+ * This is used by the kd driver to read scan codes,
+ * so don't translate '\r' ==> '\n' here...
+ */
+ return (c);
+}
+
+/*
+ * Polled output char.
+ */
+static void
+zs_putc(arg, c)
+ volatile void *arg;
+ int c;
+{
+ register volatile struct zschan *zc = arg;
+ register int s, rr0;
+
+ s = splhigh();
+ /* Wait for transmitter to become ready. */
+ do {
+ rr0 = zc->zc_csr;
+ ZS_DELAY();
+ } while ((rr0 & ZSRR0_TX_READY) == 0);
+
+ zc->zc_data = c;
+ ZS_DELAY();
+
+ do {
+ rr0 = zc->zc_csr;
+ ZS_DELAY();
+ } while ((rr0 & ZSRR0_TX_READY) == 0);
+ splx(s);
+}
+
+extern struct consdev consdev_kd; /* keyboard/display */
+extern struct consdev consdev_tty;
+extern struct consdev *cn_tab; /* physical console device info */
+
+void *zs_conschan = (void *) 0x17012020;
+
+/*
+ * This function replaces sys/dev/cninit.c
+ * Determine which device is the console using
+ * the PROM "input source" and "output sink".
+ */
+void
+cninit()
+{
+/* struct zschan *zc; */
+ struct consdev *cn;
+ int zsc_unit, channel;
+ char inSource;
+
+ inSource = 1;
+
+ switch (inSource) {
+
+ case 1: /* ttya */
+ case 2: /* ttyb */
+ zsc_unit = 1;
+ channel = inSource - 1;
+ cn = &consdev_tty;
+ cn->cn_dev = makedev(ZSTTY_MAJOR, channel);
+ cn->cn_pri = CN_REMOTE;
+ break;
+
+ case 3: /* ttyc (rewired keyboard connector) */
+ case 4: /* ttyd (rewired mouse connector) */
+ zsc_unit = 0;
+ channel = inSource - 3;
+ cn = &consdev_tty;
+ cn->cn_dev = makedev(ZSTTY_MAJOR, (channel+2));
+ cn->cn_pri = CN_REMOTE;
+ break;
+
+ default:
+#if 0
+ mon_printf("cninit: invalid PROM console selector\n");
+#endif
+ /* assume keyboard/display */
+ /* fallthrough */
+ case 0: /* keyboard/display */
+ zsc_unit = 0;
+ channel = 0;
+ cn = &consdev_kd;
+ /* Set cn_dev, cn_pri in kd.c */
+ break;
+ }
+
+#if 0
+ zc = zs_get_chan_addr(zsc_unit, channel);
+ if (zc == NULL) {
+#if 0
+ mon_printf("cninit: zs not mapped.\n");
+#endif
+ return;
+ }
+ zs_conschan = zc;
+#endif
+ zs_hwflags[zsc_unit][channel] = ZS_HWFLAG_CONSOLE;
+ cn_tab = cn;
+ (*cn->cn_init)(cn);
+}
+
+
+/* We never call this. */
+void
+nullcnprobe(cn)
+ struct consdev *cn;
+{
+}
+
+void
+zscninit(cn)
+ struct consdev *cn;
+{
+#if 0
+ int unit = minor(cn->cn_dev) & 1;
+
+ mon_printf("console is zstty%d (tty%c)\n",
+ unit, unit + 'a');
+#endif
+}
+
+/*
+ * Polled console input putchar.
+ */
+int
+zscngetc(dev)
+ dev_t dev;
+{
+ register volatile struct zschan *zc = zs_conschan;
+ register int c;
+
+ c = zs_getc(zc);
+ return (c);
+}
+
+/*
+ * Polled console output putchar.
+ */
+void
+zscnputc(dev, c)
+ dev_t dev;
+ int c;
+{
+ register volatile struct zschan *zc = zs_conschan;
+
+ zs_putc(zc, c);
+ if (c == '\n')
+ zs_putc (zc, '\r');
+}
+
+
+struct consdev consdev_tty = {
+ nullcnprobe,
+ zscninit,
+ zscngetc,
+ zscnputc,
+ nullcnpollc,
+};
+
+
+/*
+ * Handle user request to enter kernel debugger.
+ */
+void
+zs_abort()
+{
+ register volatile struct zschan *zc = zs_conschan;
+ int rr0;
+
+ /* Wait for end of break to avoid PROM abort. */
+ /* XXX - Limit the wait? */
+ do {
+ rr0 = zc->zc_csr;
+ ZS_DELAY();
+ } while (rr0 & ZSRR0_BREAK);
+
+ /* XXX - Always available, but may be the PROM monitor. */
+ Debugger();
+}
+
+#if 1
+static __inline__ int
+zs_hack_read_reg (int reg)
+{
+ register volatile struct zschan *zc = zs_conschan;
+ int val;
+ zc->zc_csr = reg;
+ ZS_DELAY();
+ val = zc->zc_csr;
+ ZS_DELAY();
+ return val;
+}
+#endif
+
+void zs_hack (void);
+void
+zs_hack (void)
+{
+ int rr[16];
+ static int regs[] = {0, 1, 2, 3, 10, 12, 13, 15, -1};
+ int *p;
+
+ for (p = regs; *p != -1; p++)
+ rr[*p] = zs_hack_read_reg (*p);
+
+ printf ("zs_hack:\n");
+ for (p =regs; *p != -1; p++)
+ printf ("regs[%d] = 0x%02x\n", *p, rr[*p]);
+}
diff --git a/sys/arch/kbus/dev/zs_cons.h b/sys/arch/kbus/dev/zs_cons.h
new file mode 100644
index 00000000000..98890eda222
--- /dev/null
+++ b/sys/arch/kbus/dev/zs_cons.h
@@ -0,0 +1,9 @@
+/* $OpenBSD: zs_cons.h,v 1.1 1997/10/14 07:25:30 gingold Exp $ */
+
+extern void *zs_conschan;
+
+extern void nullcnprobe __P((struct consdev *));
+
+extern int zs_getc __P((void *arg));
+extern void zs_putc __P((void *arg, int c));
+
diff --git a/sys/arch/kbus/dev/zs_kgdb.c b/sys/arch/kbus/dev/zs_kgdb.c
new file mode 100644
index 00000000000..3ea67adf1bc
--- /dev/null
+++ b/sys/arch/kbus/dev/zs_kgdb.c
@@ -0,0 +1,279 @@
+/* $OpenBSD: zs_kgdb.c,v 1.1 1997/10/14 07:25:30 gingold Exp $ */
+/* $NetBSD: zs_kgdb.c,v 1.9 1996/11/20 18:57:04 gwr Exp $ */
+
+/*-
+ * Copyright (c) 1996 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Gordon W. Ross.
+ *
+ * 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 NetBSD
+ * Foundation, Inc. and its contributors.
+ * 4. Neither the name of The NetBSD Foundation 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 NETBSD FOUNDATION, INC. 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.
+ */
+
+/*
+ * Hooks for kgdb when attached vi the z8530 driver
+ * XXX - not tested yet...
+ *
+ * To use this, build a kernel with: option KGDB, and
+ * boot that kernel with "-d". (The kernel will call
+ * zs_kgdb_init, kgdb_connect.) When the console prints
+ * "kgdb waiting..." you run "gdb -k kernel" and then
+ * connect to the remote using: "target remote /dev/ttyX"
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/proc.h>
+#include <sys/device.h>
+#include <sys/conf.h>
+#include <sys/ioctl.h>
+#include <sys/kernel.h>
+#include <sys/syslog.h>
+
+#include <dev/ic/z8530reg.h>
+#include <machine/z8530var.h>
+
+#include <machine/remote-sl.h>
+
+/* The Sun3 provides a 4.9152 MHz clock to the ZS chips. */
+#define PCLK (9600 * 512) /* PCLK pin input clock rate */
+#define ZSHARD_PRI 6 /* Wired on the CPU board... */
+
+#define ZS_DELAY() delay(2)
+
+/* The layout of this is hardware-dependent (padding, order). */
+struct zschan {
+ volatile u_char zc_csr; /* ctrl,status, and indirect access */
+ u_char zc_xxx0;
+ volatile u_char zc_data; /* data */
+ u_char zc_xxx1;
+};
+
+extern int kgdb_dev;
+extern int kgdb_rate;
+
+struct zschan * zs_get_chan_addr __P((int zsc_unit, int channel));
+
+extern int zs_getc __P((void *arg));
+extern void zs_putc __P((void *arg, int c));
+
+struct zsops zsops_kgdb;
+
+static u_char zs_kgdb_regs[16] = {
+ 0, /* 0: CMD (reset, etc.) */
+ ZSWR1_RIE, /* NOT: (ZSWR1_TIE | ZSWR1_SIE) */
+ 0x18 + ZSHARD_PRI, /* IVECT */
+ ZSWR3_RX_8 | ZSWR3_RX_ENABLE,
+ ZSWR4_CLK_X16 | ZSWR4_ONESB | ZSWR4_EVENP,
+ ZSWR5_TX_8 | ZSWR5_TX_ENABLE,
+ 0, /* 6: TXSYNC/SYNCLO */
+ 0, /* 7: RXSYNC/SYNCHI */
+ 0, /* 8: alias for data port */
+ ZSWR9_MASTER_IE,
+ 0, /*10: Misc. TX/RX control bits */
+ ZSWR11_TXCLK_BAUD | ZSWR11_RXCLK_BAUD,
+ 14, /*12: BAUDLO (default=9600) */
+ 0, /*13: BAUDHI (default=9600) */
+ ZSWR14_BAUD_FROM_PCLK | ZSWR14_BAUD_ENA,
+ ZSWR15_BREAK_IE | ZSWR15_DCD_IE,
+};
+
+/*
+ * This replaces "zs_reset()" in the sparc driver.
+ */
+static void
+zs_setparam(cs, iena, rate)
+ struct zs_chanstate *cs;
+ int iena;
+ int rate;
+{
+ int s, tconst;
+
+ bcopy(zs_kgdb_regs, cs->cs_preg, 16);
+
+ if (iena == 0) {
+ cs->cs_preg[1] = 0;
+ }
+
+ /* Initialize the speed, etc. */
+ tconst = BPS_TO_TCONST(cs->cs_brg_clk, rate);
+ cs->cs_preg[5] |= ZSWR5_DTR | ZSWR5_RTS;
+ cs->cs_preg[12] = tconst;
+ cs->cs_preg[13] = tconst >> 8;
+
+ s = splhigh();
+ zs_loadchannelregs(cs);
+ splx(s);
+}
+
+/*
+ * Set up for kgdb; called at boot time before configuration.
+ * KGDB interrupts will be enabled later when zs0 is configured.
+ */
+void
+zs_kgdb_init()
+{
+ struct zs_chanstate cs;
+ volatile struct zschan *zc;
+ int channel, zsc_unit;
+
+ if (major(kgdb_dev) != ZSTTY_MAJOR)
+ return;
+
+ /* Note: (ttya,ttyb) on zsc1, and (ttyc,ttyd) on zsc0 */
+ zsc_unit = 2 - (kgdb_dev & 2);
+ channel = kgdb_dev & 1;
+ printf("zs_kgdb_init: attaching zstty%d at %d baud\n",
+ channel, kgdb_rate);
+
+ /* Setup temporary chanstate. */
+ bzero((caddr_t)&cs, sizeof(cs));
+ zc = zs_get_chan_addr(zsc_unit, channel);
+ cs.cs_reg_csr = &zc->zc_csr;
+ cs.cs_reg_data = &zc->zc_data;
+ cs.cs_channel = channel;
+ cs.cs_brg_clk = PCLK / 16;
+
+ /* Now set parameters. (interrupts disabled) */
+ zs_setparam(&cs, 0, kgdb_rate);
+
+ /* Store the getc/putc functions and arg. */
+ kgdb_attach(zs_getc, zs_putc, (void *)zc);
+}
+
+/*
+ * This is a "hook" called by zstty_attach to allow the tty
+ * to be "taken over" for exclusive use by kgdb.
+ * Return non-zero if this is the kgdb port.
+ *
+ * Set the speed to kgdb_rate, CS8, etc.
+ */
+int
+zs_check_kgdb(cs, dev)
+ struct zs_chanstate *cs;
+ int dev;
+{
+
+ if (dev != kgdb_dev)
+ return (0);
+
+ /*
+ * Yes, this is the kgdb port. Finish the autoconfig
+ * message and set up the port for our exclusive use.
+ */
+ printf(" (kgdb)\n");
+
+ cs->cs_private = NULL;
+ cs->cs_ops = &zsops_kgdb;
+
+ /* Now set parameters. (interrupts enabled) */
+ zs_setparam(&cs, 1, kgdb_rate);
+
+ return (1);
+}
+
+/*
+ * KGDB framing character received: enter kernel debugger. This probably
+ * should time out after a few seconds to avoid hanging on spurious input.
+ */
+void
+zskgdb()
+{
+ int unit = minor(kgdb_dev);
+
+ printf("zstty%d: kgdb interrupt\n", unit);
+ /* This will trap into the debugger. */
+ kgdb_connect(1);
+}
+
+
+/****************************************************************
+ * Interface to the lower layer (zscc)
+ ****************************************************************/
+
+int kgdb_input_lost;
+
+static void
+zs_kgdb_rxint(cs)
+ register struct zs_chanstate *cs;
+{
+ register u_char c, rr1;
+
+ /* Read the input data ASAP. */
+ c = zs_read_data(cs);
+
+ /* Save the status register too. */
+ rr1 = zs_read_reg(cs, 1);
+
+ if (rr1 & (ZSRR1_FE | ZSRR1_DO | ZSRR1_PE)) {
+ /* Clear the receive error. */
+ zs_write_csr(cs, ZSWR0_RESET_ERRORS);
+ }
+
+ if (c == FRAME_START) {
+ zskgdb();
+ } else {
+ kgdb_input_lost++;
+ }
+}
+
+static void
+zs_kgdb_txint(cs)
+ register struct zs_chanstate *cs;
+{
+ register int rr0;
+
+ rr0 = zs_read_csr(cs);
+ zs_write_csr(cs, ZSWR0_RESET_TXINT);
+}
+
+static void
+zs_kgdb_stint(cs)
+ register struct zs_chanstate *cs;
+{
+ register int rr0;
+
+ rr0 = zs_read_csr(cs);
+ zs_write_csr(cs, ZSWR0_RESET_STATUS);
+}
+
+static void
+zs_kgdb_softint(cs)
+ struct zs_chanstate *cs;
+{
+ printf("zs_kgdb_softint?\n");
+}
+
+struct zsops zsops_kgdb = {
+ zs_kgdb_rxint, /* receive char available */
+ zs_kgdb_stint, /* external/status */
+ zs_kgdb_txint, /* xmit buffer empty */
+ zs_kgdb_softint, /* process software interrupt */
+};