diff options
author | gingold <gingold@cvs.openbsd.org> | 1997-10-14 07:25:35 +0000 |
---|---|---|
committer | gingold <gingold@cvs.openbsd.org> | 1997-10-14 07:25:35 +0000 |
commit | f6491d400ca651a8a1493d72c6a74c622aa231b1 (patch) | |
tree | 39f29255154f230f637c12e9214f12a9e64bd9d4 /sys/arch/kbus/dev | |
parent | 7a9e3739a66bd0fadfdc611c72e879fcc6f9ef01 (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.c | 1111 | ||||
-rw-r--r-- | sys/arch/kbus/dev/am7990reg.h | 179 | ||||
-rw-r--r-- | sys/arch/kbus/dev/am7990var.h | 147 | ||||
-rw-r--r-- | sys/arch/kbus/dev/dmavar.h | 47 | ||||
-rw-r--r-- | sys/arch/kbus/dev/if_le.c | 183 | ||||
-rw-r--r-- | sys/arch/kbus/dev/kbus.c | 110 | ||||
-rw-r--r-- | sys/arch/kbus/dev/rd.c | 193 | ||||
-rw-r--r-- | sys/arch/kbus/dev/sbic.c | 2913 | ||||
-rw-r--r-- | sys/arch/kbus/dev/sbic.c.mvme | 2719 | ||||
-rw-r--r-- | sys/arch/kbus/dev/sbicdma.c | 256 | ||||
-rw-r--r-- | sys/arch/kbus/dev/sbicdma.h | 53 | ||||
-rw-r--r-- | sys/arch/kbus/dev/sbicreg.h | 441 | ||||
-rw-r--r-- | sys/arch/kbus/dev/sbicvar.h | 235 | ||||
-rw-r--r-- | sys/arch/kbus/dev/sbicvar.h.mvme | 210 | ||||
-rw-r--r-- | sys/arch/kbus/dev/vme.c | 321 | ||||
-rw-r--r-- | sys/arch/kbus/dev/xd.c | 2380 | ||||
-rw-r--r-- | sys/arch/kbus/dev/xdreg.h | 424 | ||||
-rw-r--r-- | sys/arch/kbus/dev/xdvar.h | 167 | ||||
-rw-r--r-- | sys/arch/kbus/dev/xio.h | 65 | ||||
-rw-r--r-- | sys/arch/kbus/dev/zs.c | 745 | ||||
-rw-r--r-- | sys/arch/kbus/dev/zs_cons.h | 9 | ||||
-rw-r--r-- | sys/arch/kbus/dev/zs_kgdb.c | 279 |
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 = ⊤ + + 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 */ +}; |