diff options
author | scottl <scottl@FreeBSD.org> | 2004-06-10 05:11:39 +0000 |
---|---|---|
committer | scottl <scottl@FreeBSD.org> | 2004-06-10 05:11:39 +0000 |
commit | 1154159183e80dfcf7a52dc4b4ba8d5db88bca1e (patch) | |
tree | 907cdedb826accd7ccb9399e3a5a3281d4e86864 /sys/dev/esp | |
parent | fe56c5bdd9a68195f81addc0c76aec6f142d1da0 (diff) | |
download | FreeBSD-src-1154159183e80dfcf7a52dc4b4ba8d5db88bca1e.zip FreeBSD-src-1154159183e80dfcf7a52dc4b4ba8d5db88bca1e.tar.gz |
Port the NetBSD esp(4) driver. This only includes the sbus front-end, so
its primary use is for the FEPS/FAS366 SCSI found in Sun Ultra 1e and 2
machines. Once the pci front-end is ported, this driver can replace the
amd(4) driver.
The code as-is is fairly stable. I've disabled tagged-queueing until I can
figure out a corruption bug related to it. I'm importing it now so that
people with these machines can (finally) stop netbooting and report bugs
before 5.3.
Diffstat (limited to 'sys/dev/esp')
-rw-r--r-- | sys/dev/esp/esp_sbus.c | 581 | ||||
-rw-r--r-- | sys/dev/esp/lsi64854.c | 722 | ||||
-rw-r--r-- | sys/dev/esp/lsi64854reg.h | 205 | ||||
-rw-r--r-- | sys/dev/esp/lsi64854var.h | 114 | ||||
-rw-r--r-- | sys/dev/esp/ncr53c9x.c | 2938 | ||||
-rw-r--r-- | sys/dev/esp/ncr53c9xreg.h | 290 | ||||
-rw-r--r-- | sys/dev/esp/ncr53c9xvar.h | 457 |
7 files changed, 5307 insertions, 0 deletions
diff --git a/sys/dev/esp/esp_sbus.c b/sys/dev/esp/esp_sbus.c new file mode 100644 index 0000000..70c6538 --- /dev/null +++ b/sys/dev/esp/esp_sbus.c @@ -0,0 +1,581 @@ +/*- + * Copyright (c) 2004 Scott Long + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. + * + */ + +/* $NetBSD: esp_sbus.c,v 1.27 2002/12/10 13:44:47 pk Exp $ */ + +/*- + * Copyright (c) 1997, 1998 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Charles M. Hannum; Jason R. Thorpe of the Numerical Aerospace + * Simulation Facility, NASA Ames Research Center; Paul Kranenburg. + * + * 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 FOUNDATION 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/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/bus.h> +#include <sys/kernel.h> +#include <sys/resource.h> +#include <sys/lock.h> +#include <sys/mutex.h> + +#include <machine/bus.h> +#include <dev/ofw/openfirm.h> +#include <machine/ofw_machdep.h> +#include <machine/resource.h> +#include <sys/rman.h> +#include <sparc64/sbus/sbusvar.h> + +#include <cam/cam.h> +#include <cam/cam_ccb.h> +#include <cam/scsi/scsi_all.h> + +#include <dev/esp/lsi64854reg.h> +#include <dev/esp/lsi64854var.h> + +#include <dev/esp/ncr53c9xreg.h> +#include <dev/esp/ncr53c9xvar.h> + +/* #define ESP_SBUS_DEBUG */ + +struct esp_softc { + struct ncr53c9x_softc sc_ncr53c9x; /* glue to MI code */ + struct device *sc_dev; + + int sc_rid; + struct resource *sc_res; + bus_space_handle_t sc_regh; + bus_space_tag_t sc_regt; + + int sc_irqrid; + struct resource *sc_irqres; + void *sc_irq; + + struct lsi64854_softc *sc_dma; /* pointer to my dma */ + + int sc_pri; /* SBUS priority */ +}; + +static int esp_sbus_probe(device_t); +static int esp_sbus_attach(device_t); +static int esp_sbus_detach(device_t); +static int esp_sbus_suspend(device_t); +static int esp_sbus_resume(device_t); + +static device_method_t esp_sbus_methods[] = { + DEVMETHOD(device_probe, esp_sbus_probe), + DEVMETHOD(device_attach, esp_sbus_attach), + DEVMETHOD(device_detach, esp_sbus_detach), + DEVMETHOD(device_suspend, esp_sbus_suspend), + DEVMETHOD(device_resume, esp_sbus_resume), + {0, 0} +}; + +static driver_t esp_sbus_driver = { + "esp", + esp_sbus_methods, + sizeof(struct esp_softc) +}; + +static devclass_t esp_devclass; +DRIVER_MODULE(esp, sbus, esp_sbus_driver, esp_devclass, 0, 0); + +/* + * Functions and the switch for the MI code. + */ +static u_char esp_read_reg(struct ncr53c9x_softc *, int); +static void esp_write_reg(struct ncr53c9x_softc *, int, u_char); +static int esp_dma_isintr(struct ncr53c9x_softc *); +static void esp_dma_reset(struct ncr53c9x_softc *); +static int esp_dma_intr(struct ncr53c9x_softc *); +static int esp_dma_setup(struct ncr53c9x_softc *, caddr_t *, size_t *, + int, size_t *); +static void esp_dma_go(struct ncr53c9x_softc *); +static void esp_dma_stop(struct ncr53c9x_softc *); +static int esp_dma_isactive(struct ncr53c9x_softc *); +static void espattach(struct esp_softc *, struct ncr53c9x_glue *); + +static struct ncr53c9x_glue esp_sbus_glue = { + esp_read_reg, + esp_write_reg, + esp_dma_isintr, + esp_dma_reset, + esp_dma_intr, + esp_dma_setup, + esp_dma_go, + esp_dma_stop, + esp_dma_isactive, + NULL, /* gl_clear_latched_intr */ +}; + +static int +esp_sbus_probe(device_t dev) +{ + char *name; + + name = sbus_get_name(dev); + if (strcmp("SUNW,fas", name) == 0) { + device_set_desc(dev, "Sun FAS366 Fast-Wide SCSI"); + return (-10); + } + + return (ENXIO); +} + +static int +esp_sbus_attach(device_t dev) +{ + struct esp_softc *esc = device_get_softc(dev); + struct ncr53c9x_softc *sc = &esc->sc_ncr53c9x; + struct lsi64854_softc *lsc; + phandle_t node; + int burst; + + esc->sc_dev = dev; + node = sbus_get_node(dev); + if (OF_getprop(node, "initiator-id", &sc->sc_id, + sizeof(sc->sc_id)) == -1) + sc->sc_id = 7;; + sc->sc_freq = sbus_get_clockfreq(dev); + +#ifdef ESP_SBUS_DEBUG + device_printf(dev, "espattach_sbus: sc_id %d, freq %d\n", + sc->sc_id, sc->sc_freq); +#endif + + /* + * allocate space for dma, in SUNW,fas there are no separate + * dma device + */ + lsc = malloc(sizeof (struct lsi64854_softc), M_DEVBUF, M_NOWAIT); + + if (lsc == NULL) { + device_printf(dev, "out of memory (lsi64854_softc)\n"); + return (ENOMEM); + } + esc->sc_dma = lsc; + + /* + * fas has 2 register spaces: dma(lsi64854) and SCSI core (ncr53c9x) + */ + + /* Map dma registers */ + lsc->sc_rid = 0; + if ((lsc->sc_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, + &lsc->sc_rid, RF_ACTIVE)) == NULL) { + device_printf(dev, "cannot map dma registers\n"); + free(lsc, M_DEVBUF); + return (ENXIO); + } + lsc->sc_regt = rman_get_bustag(lsc->sc_res); + lsc->sc_regh = rman_get_bushandle(lsc->sc_res); + + /* Create a parent DMA tag based on this bus */ + if (bus_dma_tag_create(NULL, /* parent */ + PAGE_SIZE, 0, /* algnmnt, boundary */ + BUS_SPACE_MAXADDR, /* lowaddr */ + BUS_SPACE_MAXADDR, /* highaddr */ + NULL, NULL, /* filter, filterarg */ + BUS_SPACE_MAXSIZE_32BIT,/* maxsize */ + 0, /* nsegments */ + BUS_SPACE_MAXSIZE_32BIT,/* maxsegsize */ + 0, /* flags */ + NULL, NULL, /* No locking */ + &lsc->sc_parent_dmat)) { + device_printf(dev, "cannot allocate parent DMA tag\n"); + free(lsc, M_DEVBUF); + return (ENOMEM); + } + burst = sbus_get_burstsz(dev); + +#ifdef ESP_SBUS_DEBUG + printf("espattach_sbus: burst 0x%x\n", burst); +#endif + + lsc->sc_burst = (burst & SBUS_BURST_32) ? 32 : + (burst & SBUS_BURST_16) ? 16 : 0; + + lsc->sc_channel = L64854_CHANNEL_SCSI; + lsc->sc_client = sc; + lsc->sc_dev = dev; + + lsi64854_attach(lsc); + + /* + * map SCSI core registers + */ + esc->sc_rid = 1; + if ((esc->sc_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, + &esc->sc_rid, RF_ACTIVE)) == NULL) { + device_printf(dev, "cannot map scsi core registers\n"); + free(lsc, M_DEVBUF); + return (ENXIO); + } + esc->sc_regt = rman_get_bustag(esc->sc_res); + esc->sc_regh = rman_get_bushandle(esc->sc_res); + +#if 0 + esc->sc_pri = sa->sa_pri; + + /* add me to the sbus structures */ + esc->sc_sd.sd_reset = (void *) ncr53c9x_reset; + sbus_establish(&esc->sc_sd, &sc->sc_dev); +#endif + + espattach(esc, &esp_sbus_glue); + + return (0); +} + +static int +esp_sbus_detach(device_t dev) +{ + struct ncr53c9x_softc *sc; + struct esp_softc *esc; + + esc = device_get_softc(dev); + sc = &esc->sc_ncr53c9x; + return (ncr53c9x_detach(sc, 0)); +} + +static int +esp_sbus_suspend(device_t dev) +{ + return (ENXIO); +} + +static int +esp_sbus_resume(device_t dev) +{ + return (ENXIO); +} + +/* + * Attach this instance, and then all the sub-devices + */ +void +espattach(struct esp_softc *esc, struct ncr53c9x_glue *gluep) +{ + struct ncr53c9x_softc *sc = &esc->sc_ncr53c9x; + unsigned int uid = 0; + + /* + * Set up glue for MI code early; we use some of it here. + */ + sc->sc_glue = gluep; + + /* gimme MHz */ + sc->sc_freq /= 1000000; + + /* + * XXX More of this should be in ncr53c9x_attach(), but + * XXX should we really poke around the chip that much in + * XXX the MI code? Think about this more... + */ + + /* + * It is necessary to try to load the 2nd config register here, + * to find out what rev the esp chip is, else the ncr53c9x_reset + * will not set up the defaults correctly. + */ + sc->sc_cfg1 = sc->sc_id | NCRCFG1_PARENB; + sc->sc_cfg2 = NCRCFG2_SCSI2 | NCRCFG2_RPE; + sc->sc_cfg3 = NCRCFG3_CDB; + NCR_WRITE_REG(sc, NCR_CFG2, sc->sc_cfg2); + + if ((NCR_READ_REG(sc, NCR_CFG2) & ~NCRCFG2_RSVD) != + (NCRCFG2_SCSI2 | NCRCFG2_RPE)) { + sc->sc_rev = NCR_VARIANT_ESP100; + } else { + sc->sc_cfg2 = NCRCFG2_SCSI2; + NCR_WRITE_REG(sc, NCR_CFG2, sc->sc_cfg2); + sc->sc_cfg3 = 0; + NCR_WRITE_REG(sc, NCR_CFG3, sc->sc_cfg3); + sc->sc_cfg3 = (NCRCFG3_CDB | NCRCFG3_FCLK); + NCR_WRITE_REG(sc, NCR_CFG3, sc->sc_cfg3); + if (NCR_READ_REG(sc, NCR_CFG3) != + (NCRCFG3_CDB | NCRCFG3_FCLK)) { + sc->sc_rev = NCR_VARIANT_ESP100A; + } else { + /* NCRCFG2_FE enables > 64K transfers */ + sc->sc_cfg2 |= NCRCFG2_FE; + sc->sc_cfg3 = 0; + NCR_WRITE_REG(sc, NCR_CFG3, sc->sc_cfg3); + sc->sc_rev = NCR_VARIANT_ESP200; + + /* XXX spec says it's valid after power up or chip reset */ + uid = NCR_READ_REG(sc, NCR_UID); + if (((uid & 0xf8) >> 3) == 0x0a) /* XXX */ + sc->sc_rev = NCR_VARIANT_FAS366; + } + } + +#ifdef ESP_SBUS_DEBUG + printf("espattach: revision %d, uid 0x%x\n", sc->sc_rev, uid); +#endif + + /* + * XXX minsync and maxxfer _should_ be set up in MI code, + * XXX but it appears to have some dependency on what sort + * XXX of DMA we're hooked up to, etc. + */ + + /* + * This is the value used to start sync negotiations + * Note that the NCR register "SYNCTP" is programmed + * in "clocks per byte", and has a minimum value of 4. + * The SCSI period used in negotiation is one-fourth + * of the time (in nanoseconds) needed to transfer one byte. + * Since the chip's clock is given in MHz, we have the following + * formula: 4 * period = (1000 / freq) * 4 + */ + sc->sc_minsync = 1000 / sc->sc_freq; + + /* limit minsync due to unsolved performance issues */ + sc->sc_maxsync = sc->sc_minsync; + sc->sc_maxoffset = 15; + + /* + * Alas, we must now modify the value a bit, because it's + * only valid when can switch on FASTCLK and FASTSCSI bits + * in config register 3... + */ + switch (sc->sc_rev) { + case NCR_VARIANT_ESP100: + sc->sc_maxwidth = 0; + sc->sc_maxxfer = 64 * 1024; + sc->sc_minsync = 0; /* No synch on old chip? */ + break; + + case NCR_VARIANT_ESP100A: + sc->sc_maxwidth = 1; + sc->sc_maxxfer = 64 * 1024; + /* Min clocks/byte is 5 */ + sc->sc_minsync = ncr53c9x_cpb2stp(sc, 5); + break; + + case NCR_VARIANT_ESP200: + case NCR_VARIANT_FAS366: + sc->sc_maxwidth = 1; + sc->sc_maxxfer = 16 * 1024 * 1024; + /* XXX - do actually set FAST* bits */ + break; + } + + /* Establish interrupt channel */ + esc->sc_irqrid = 0; + if ((esc->sc_irqres = bus_alloc_resource_any(esc->sc_dev, SYS_RES_IRQ, + &esc->sc_irqrid, RF_SHAREABLE|RF_ACTIVE)) == NULL) { + device_printf(esc->sc_dev, "Cannot allocate interrupt\n"); + return; + } + if (bus_setup_intr(esc->sc_dev, esc->sc_irqres, + INTR_TYPE_BIO|INTR_ENTROPY, ncr53c9x_intr, sc, &esc->sc_irq)) { + device_printf(esc->sc_dev, "Cannot set up interrupt\n"); + return; + } + + /* Turn on target selection using the `dma' method */ + if (sc->sc_rev != NCR_VARIANT_FAS366) + sc->sc_features |= NCR_F_DMASELECT; + + /* Do the common parts of attachment. */ + sc->sc_dev = esc->sc_dev; + ncr53c9x_attach(sc); +} + +/* + * Glue functions. + */ + +#ifdef ESP_SBUS_DEBUG +int esp_sbus_debug = 0; + +static struct { + char *r_name; + int r_flag; +} esp__read_regnames [] = { + { "TCL", 0}, /* 0/00 */ + { "TCM", 0}, /* 1/04 */ + { "FIFO", 0}, /* 2/08 */ + { "CMD", 0}, /* 3/0c */ + { "STAT", 0}, /* 4/10 */ + { "INTR", 0}, /* 5/14 */ + { "STEP", 0}, /* 6/18 */ + { "FFLAGS", 1}, /* 7/1c */ + { "CFG1", 1}, /* 8/20 */ + { "STAT2", 0}, /* 9/24 */ + { "CFG4", 1}, /* a/28 */ + { "CFG2", 1}, /* b/2c */ + { "CFG3", 1}, /* c/30 */ + { "-none", 1}, /* d/34 */ + { "TCH", 1}, /* e/38 */ + { "TCX", 1}, /* f/3c */ +}; + +static struct { + char *r_name; + int r_flag; +} esp__write_regnames[] = { + { "TCL", 1}, /* 0/00 */ + { "TCM", 1}, /* 1/04 */ + { "FIFO", 0}, /* 2/08 */ + { "CMD", 0}, /* 3/0c */ + { "SELID", 1}, /* 4/10 */ + { "TIMEOUT", 1}, /* 5/14 */ + { "SYNCTP", 1}, /* 6/18 */ + { "SYNCOFF", 1}, /* 7/1c */ + { "CFG1", 1}, /* 8/20 */ + { "CCF", 1}, /* 9/24 */ + { "TEST", 1}, /* a/28 */ + { "CFG2", 1}, /* b/2c */ + { "CFG3", 1}, /* c/30 */ + { "-none", 1}, /* d/34 */ + { "TCH", 1}, /* e/38 */ + { "TCX", 1}, /* f/3c */ +}; +#endif + +u_char +esp_read_reg(struct ncr53c9x_softc *sc, int reg) +{ + struct esp_softc *esc = (struct esp_softc *)sc; + u_char v; + + v = bus_space_read_1(esc->sc_regt, esc->sc_regh, reg * 4); +#ifdef ESP_SBUS_DEBUG + if (esp_sbus_debug && (reg < 0x10) && esp__read_regnames[reg].r_flag) + printf("RD:%x <%s> %x\n", reg * 4, + ((unsigned)reg < 0x10) ? esp__read_regnames[reg].r_name : "<***>", v); +#endif + return v; +} + +void +esp_write_reg(struct ncr53c9x_softc *sc, int reg, u_char v) +{ + struct esp_softc *esc = (struct esp_softc *)sc; + +#ifdef ESP_SBUS_DEBUG + if (esp_sbus_debug && (reg < 0x10) && esp__write_regnames[reg].r_flag) + printf("WR:%x <%s> %x\n", reg * 4, + ((unsigned)reg < 0x10) ? esp__write_regnames[reg].r_name : "<***>", v); +#endif + bus_space_write_1(esc->sc_regt, esc->sc_regh, reg * 4, v); +} + +int +esp_dma_isintr(struct ncr53c9x_softc *sc) +{ + struct esp_softc *esc = (struct esp_softc *)sc; + + return (DMA_ISINTR(esc->sc_dma)); +} + +void +esp_dma_reset(struct ncr53c9x_softc *sc) +{ + struct esp_softc *esc = (struct esp_softc *)sc; + + DMA_RESET(esc->sc_dma); +} + +int +esp_dma_intr(struct ncr53c9x_softc *sc) +{ + struct esp_softc *esc = (struct esp_softc *)sc; + + return (DMA_INTR(esc->sc_dma)); +} + +int +esp_dma_setup(struct ncr53c9x_softc *sc, caddr_t *addr, size_t *len, + int datain, size_t *dmasize) +{ + struct esp_softc *esc = (struct esp_softc *)sc; + + return (DMA_SETUP(esc->sc_dma, addr, len, datain, dmasize)); +} + +void +esp_dma_go(struct ncr53c9x_softc *sc) +{ + struct esp_softc *esc = (struct esp_softc *)sc; + + DMA_GO(esc->sc_dma); +} + +void +esp_dma_stop(struct ncr53c9x_softc *sc) +{ + struct esp_softc *esc = (struct esp_softc *)sc; + u_int32_t csr; + + csr = L64854_GCSR(esc->sc_dma); + csr &= ~D_EN_DMA; + L64854_SCSR(esc->sc_dma, csr); +} + +int +esp_dma_isactive(struct ncr53c9x_softc *sc) +{ + struct esp_softc *esc = (struct esp_softc *)sc; + + return (DMA_ISACTIVE(esc->sc_dma)); +} diff --git a/sys/dev/esp/lsi64854.c b/sys/dev/esp/lsi64854.c new file mode 100644 index 0000000..dca8526f7 --- /dev/null +++ b/sys/dev/esp/lsi64854.c @@ -0,0 +1,722 @@ +/*- + * Copyright (c) 2004 Scott Long + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. + * + */ + +/* $NetBSD: lsi64854.c,v 1.22 2002/10/01 07:07:03 petrov Exp $ */ + +/*- + * Copyright (c) 1998 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Paul Kranenburg. + * + * 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 FOUNDATION 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/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/bus.h> +#include <sys/kernel.h> +#include <sys/resource.h> +#include <sys/lock.h> +#include <sys/mutex.h> + +#include <machine/bus.h> + +#include <cam/cam.h> +#include <cam/cam_ccb.h> +#include <cam/scsi/scsi_all.h> + +#include <dev/esp/lsi64854reg.h> +#include <dev/esp/lsi64854var.h> + +#include <dev/esp/ncr53c9xreg.h> +#include <dev/esp/ncr53c9xvar.h> + +void lsi64854_reset(struct lsi64854_softc *); +int lsi64854_setup(struct lsi64854_softc *, caddr_t *, size_t *, + int, size_t *); +int lsi64854_setup_pp(struct lsi64854_softc *, caddr_t *, size_t *, + int, size_t *); + +#ifdef DEBUG +#define LDB_SCSI 1 +#define LDB_ENET 2 +#define LDB_PP 4 +#define LDB_ANY 0xff +int lsi64854debug = 0; +#define DPRINTF(a,x) do { if (lsi64854debug & (a)) printf x ; } while (0) +#else +#define DPRINTF(a,x) +#endif + +#define MAX_DMA_SZ (16*1024*1024) + +/* + * Finish attaching this DMA device. + * Front-end must fill in these fields: + * sc_regs + * sc_burst + * sc_channel (one of SCSI, ENET, PP) + * sc_client (one of SCSI, ENET, PP `soft_c' pointers) + */ +void +lsi64854_attach(struct lsi64854_softc *sc) +{ + u_int32_t csr; + + sc->dv_name = device_get_nameunit(sc->sc_dev); + + /* Indirect functions */ + switch (sc->sc_channel) { + case L64854_CHANNEL_SCSI: + sc->intr = lsi64854_scsi_intr; + sc->setup = lsi64854_setup; + break; + case L64854_CHANNEL_ENET: + sc->intr = lsi64854_enet_intr; + break; + case L64854_CHANNEL_PP: + sc->setup = lsi64854_setup_pp; + break; + default: + printf("%s: unknown channel\n", sc->dv_name); + } + sc->reset = lsi64854_reset; + + /* Allocate a dmamap */ + if (bus_dma_tag_create(sc->sc_parent_dmat, /* parent */ + 1, 0, /* algnment, boundary */ + BUS_SPACE_MAXADDR, /* lowaddr */ + BUS_SPACE_MAXADDR, /* highaddr */ + NULL, NULL, /* filter, filterarg */ + MAX_DMA_SZ, /* maxsize */ + 1, /* nsegments */ + MAX_DMA_SZ, /* maxsegsize */ + BUS_DMA_ALLOCNOW, /* flags */ + NULL, NULL, /* lockfunc, lockarg */ + &sc->sc_buffer_dmat)) { + printf("%s: can't allocate buffer DMA tag\n", sc->dv_name); + return; + } + + if (bus_dmamap_create(sc->sc_buffer_dmat, 0, &sc->sc_dmamap) != 0) { + printf("%s: DMA map create failed\n", sc->dv_name); + return; + } + + csr = L64854_GCSR(sc); + sc->sc_rev = csr & L64854_DEVID; + if (sc->sc_rev == DMAREV_HME) { + return; + } + printf(": DMA rev "); + switch (sc->sc_rev) { + case DMAREV_0: + printf("0"); + break; + case DMAREV_ESC: + printf("esc"); + break; + case DMAREV_1: + printf("1"); + break; + case DMAREV_PLUS: + printf("1+"); + break; + case DMAREV_2: + printf("2"); + break; + default: + printf("unknown (0x%x)", sc->sc_rev); + } + + DPRINTF(LDB_ANY, (", burst 0x%x, csr 0x%x", sc->sc_burst, csr)); + printf("\n"); +} + +/* + * DMAWAIT waits while condition is true + */ +#define DMAWAIT(SC, COND, MSG, DONTPANIC) do if (COND) { \ + int count = 500000; \ + while ((COND) && --count > 0) DELAY(1); \ + if (count == 0) { \ + printf("%s: line %d: CSR = 0x%lx\n", __FILE__, __LINE__, \ + (u_long)L64854_GCSR(SC)); \ + if (DONTPANIC) \ + printf(MSG); \ + else \ + panic(MSG); \ + } \ +} while (0) + +#define DMA_DRAIN(sc, dontpanic) do { \ + u_int32_t csr; \ + /* \ + * DMA rev0 & rev1: we are not allowed to touch the DMA "flush" \ + * and "drain" bits while it is still thinking about a \ + * request. \ + * other revs: D_ESC_R_PEND bit reads as 0 \ + */ \ + DMAWAIT(sc, L64854_GCSR(sc) & D_ESC_R_PEND, "R_PEND", dontpanic);\ + if (sc->sc_rev != DMAREV_HME) { \ + /* \ + * Select drain bit based on revision \ + * also clears errors and D_TC flag \ + */ \ + csr = L64854_GCSR(sc); \ + if (sc->sc_rev == DMAREV_1 || sc->sc_rev == DMAREV_0) \ + csr |= D_ESC_DRAIN; \ + else \ + csr |= L64854_INVALIDATE; \ + \ + L64854_SCSR(sc,csr); \ + } \ + /* \ + * Wait for draining to finish \ + * rev0 & rev1 call this PACKCNT \ + */ \ + DMAWAIT(sc, L64854_GCSR(sc) & L64854_DRAINING, "DRAINING", dontpanic);\ +} while(0) + +#define DMA_FLUSH(sc, dontpanic) do { \ + u_int32_t csr; \ + /* \ + * DMA rev0 & rev1: we are not allowed to touch the DMA "flush" \ + * and "drain" bits while it is still thinking about a \ + * request. \ + * other revs: D_ESC_R_PEND bit reads as 0 \ + */ \ + DMAWAIT(sc, L64854_GCSR(sc) & D_ESC_R_PEND, "R_PEND", dontpanic);\ + csr = L64854_GCSR(sc); \ + csr &= ~(L64854_WRITE|L64854_EN_DMA); /* no-ops on ENET */ \ + csr |= L64854_INVALIDATE; /* XXX FAS ? */ \ + L64854_SCSR(sc,csr); \ +} while(0) + +void +lsi64854_reset(struct lsi64854_softc *sc) +{ + u_int32_t csr; + + DMA_FLUSH(sc, 1); + csr = L64854_GCSR(sc); + + DPRINTF(LDB_ANY, ("lsi64854_reset: csr 0x%x\n", csr)); + + /* + * XXX is sync needed? + if (sc->sc_dmamap->dm_nsegs > 0) + bus_dmamap_unload(sc->sc_buffer_dmat, sc->sc_dmamap); + */ + + if (sc->sc_rev == DMAREV_HME) + L64854_SCSR(sc, csr | D_HW_RESET_FAS366); + + + csr |= L64854_RESET; /* reset DMA */ + L64854_SCSR(sc, csr); + DELAY(200); /* > 10 Sbus clocks(?) */ + + /*DMAWAIT1(sc); why was this here? */ + csr = L64854_GCSR(sc); + csr &= ~L64854_RESET; /* de-assert reset line */ + L64854_SCSR(sc, csr); + DELAY(5); /* allow a few ticks to settle */ + + csr = L64854_GCSR(sc); + csr |= L64854_INT_EN; /* enable interrupts */ + if (sc->sc_rev > DMAREV_1 && sc->sc_channel == L64854_CHANNEL_SCSI) { + if (sc->sc_rev == DMAREV_HME) + csr |= D_TWO_CYCLE; + else + csr |= D_FASTER; + } + + /* Set burst */ + switch (sc->sc_rev) { + case DMAREV_HME: + case DMAREV_2: + csr &= ~L64854_BURST_SIZE; + if (sc->sc_burst == 32) { + csr |= L64854_BURST_32; + } else if (sc->sc_burst == 16) { + csr |= L64854_BURST_16; + } else { + csr |= L64854_BURST_0; + } + break; + case DMAREV_ESC: + csr |= D_ESC_AUTODRAIN; /* Auto-drain */ + if (sc->sc_burst == 32) { + csr &= ~D_ESC_BURST; + } else + csr |= D_ESC_BURST; + break; + default: + break; + } + L64854_SCSR(sc, csr); + + if (sc->sc_rev == DMAREV_HME) { + bus_space_write_4(sc->sc_regt, sc->sc_regh, L64854_REG_ADDR, 0); + sc->sc_dmactl = csr; + } + sc->sc_active = 0; + + DPRINTF(LDB_ANY, ("lsi64854_reset: done, csr 0x%x\n", csr)); +} + +static void +lsi64854_map_scsi(void *arg, bus_dma_segment_t *segs, int nseg, int error) +{ + struct lsi64854_softc *sc; + + sc = (struct lsi64854_softc *)arg; + + if (nseg != 1) + panic("%s: cannot map %d segments\n", sc->dv_name, nseg); + + bus_dmamap_sync(sc->sc_buffer_dmat, sc->sc_dmamap, sc->sc_datain ? + BUS_DMASYNC_PREREAD : BUS_DMASYNC_PREWRITE); + bus_space_write_4(sc->sc_regt, sc->sc_regh, L64854_REG_ADDR, + segs[0].ds_addr); +} + + +#define DMAMAX(a) (MAX_DMA_SZ - ((a) & (MAX_DMA_SZ-1))) +/* + * setup a DMA transfer + */ +int +lsi64854_setup(struct lsi64854_softc *sc, caddr_t *addr, size_t *len, + int datain, size_t *dmasize) +{ + u_int32_t csr; + + DMA_FLUSH(sc, 0); + +#if 0 + DMACSR(sc) &= ~D_INT_EN; +#endif + sc->sc_dmaaddr = addr; + sc->sc_dmalen = len; + sc->sc_datain = datain; + + /* + * the rules say we cannot transfer more than the limit + * of this DMA chip (64k for old and 16Mb for new), + * and we cannot cross a 16Mb boundary. + */ + *dmasize = sc->sc_dmasize = + min(*dmasize, DMAMAX((size_t) *sc->sc_dmaaddr)); + + DPRINTF(LDB_ANY, ("dma_setup: dmasize = %ld\n", (long)sc->sc_dmasize)); + + /* + * XXX what length? + */ + if (sc->sc_rev == DMAREV_HME) { + + L64854_SCSR(sc, sc->sc_dmactl | L64854_RESET); + L64854_SCSR(sc, sc->sc_dmactl); + + bus_space_write_4(sc->sc_regt, sc->sc_regh, L64854_REG_CNT, *dmasize); + } + + /* Program the DMA address */ + if (sc->sc_dmasize) { + if (bus_dmamap_load(sc->sc_buffer_dmat, sc->sc_dmamap, + *sc->sc_dmaaddr, sc->sc_dmasize, + lsi64854_map_scsi, sc, 0) != 0) + panic("%s: cannot allocate DVMA address", sc->dv_name); + } + + if (sc->sc_rev == DMAREV_ESC) { + /* DMA ESC chip bug work-around */ + long bcnt = sc->sc_dmasize; + long eaddr = bcnt + (long)*sc->sc_dmaaddr; + if ((eaddr & PAGE_MASK_8K) != 0) + bcnt = roundup(bcnt, PAGE_SIZE_8K); + bus_space_write_4(sc->sc_regt, sc->sc_regh, L64854_REG_CNT, + bcnt); + } + + /* Setup DMA control register */ + csr = L64854_GCSR(sc); + + if (datain) + csr |= L64854_WRITE; + else + csr &= ~L64854_WRITE; + csr |= L64854_INT_EN; + + if (sc->sc_rev == DMAREV_HME) { + csr |= (D_DSBL_SCSI_DRN | D_EN_DMA); + } + + L64854_SCSR(sc, csr); + + return (0); +} + +/* + * Pseudo (chained) interrupt from the esp driver to kick the + * current running DMA transfer. Called from ncr53c9x_intr() + * for now. + * + * return 1 if it was a DMA continue. + */ +int +lsi64854_scsi_intr(void *arg) +{ + struct lsi64854_softc *sc = arg; + struct ncr53c9x_softc *nsc = sc->sc_client; + int trans, resid; + u_int32_t csr; + + csr = L64854_GCSR(sc); + + DPRINTF(LDB_SCSI, ("%s: dmaintr: addr 0x%x, csr %b\n", sc->dv_name, + bus_space_read_4(sc->sc_regt, sc->sc_regh, L64854_REG_ADDR), + csr, DDMACSR_BITS)); + + if (csr & (D_ERR_PEND|D_SLAVE_ERR)) { + printf("%s: error: csr=%b\n", sc->dv_name, csr, DDMACSR_BITS); + csr &= ~D_EN_DMA; /* Stop DMA */ + /* Invalidate the queue; SLAVE_ERR bit is write-to-clear */ + csr |= D_INVALIDATE|D_SLAVE_ERR; + L64854_SCSR(sc, csr); + return (-1); + } + + /* This is an "assertion" :) */ + if (sc->sc_active == 0) + panic("dmaintr: DMA wasn't active"); + + DMA_DRAIN(sc, 0); + + /* DMA has stopped */ + csr &= ~D_EN_DMA; + L64854_SCSR(sc, csr); + sc->sc_active = 0; + + if (sc->sc_dmasize == 0) { + /* A "Transfer Pad" operation completed */ + DPRINTF(LDB_SCSI, ("dmaintr: discarded %d bytes (tcl=%d, tcm=%d)\n", + NCR_READ_REG(nsc, NCR_TCL) | + (NCR_READ_REG(nsc, NCR_TCM) << 8), + NCR_READ_REG(nsc, NCR_TCL), + NCR_READ_REG(nsc, NCR_TCM))); + return 0; + } + + resid = 0; + /* + * If a transfer onto the SCSI bus gets interrupted by the device + * (e.g. for a SAVEPOINTER message), the data in the FIFO counts + * as residual since the NCR53C9X counter registers get decremented + * as bytes are clocked into the FIFO. + */ + if (!(csr & D_WRITE) && + (resid = (NCR_READ_REG(nsc, NCR_FFLAG) & NCRFIFO_FF)) != 0) { + DPRINTF(LDB_SCSI, ("dmaintr: empty esp FIFO of %d ", resid)); + if (nsc->sc_rev == NCR_VARIANT_FAS366 && + (NCR_READ_REG(nsc, NCR_CFG3) & NCRFASCFG3_EWIDE)) + resid <<= 1; + } + + if ((nsc->sc_espstat & NCRSTAT_TC) == 0) { + /* + * `Terminal count' is off, so read the residue + * out of the NCR53C9X counter registers. + */ + resid += (NCR_READ_REG(nsc, NCR_TCL) | + (NCR_READ_REG(nsc, NCR_TCM) << 8) | + ((nsc->sc_cfg2 & NCRCFG2_FE) + ? (NCR_READ_REG(nsc, NCR_TCH) << 16) + : 0)); + + if (resid == 0 && sc->sc_dmasize == 65536 && + (nsc->sc_cfg2 & NCRCFG2_FE) == 0) + /* A transfer of 64K is encoded as `TCL=TCM=0' */ + resid = 65536; + } + + trans = sc->sc_dmasize - resid; + if (trans < 0) { /* transferred < 0 ? */ +#if 0 + /* + * This situation can happen in perfectly normal operation + * if the ESP is reselected while using DMA to select + * another target. As such, don't print the warning. + */ + printf("%s: xfer (%d) > req (%d)\n", sc->dv_name, trans, + sc->sc_dmasize); +#endif + trans = sc->sc_dmasize; + } + + DPRINTF(LDB_SCSI, ("dmaintr: tcl=%d, tcm=%d, tch=%d; trans=%d, resid=%d\n", + NCR_READ_REG(nsc, NCR_TCL), + NCR_READ_REG(nsc, NCR_TCM), + (nsc->sc_cfg2 & NCRCFG2_FE) + ? NCR_READ_REG(nsc, NCR_TCH) : 0, + trans, resid)); + +#if 0 /* XXX */ + if (sc->sc_dmamap->dm_nsegs > 0) { + bus_dmamap_sync(sc->sc_buffer_dmat, sc->sc_dmamap, + (csr & D_WRITE) != 0 + ? BUS_DMASYNC_POSTREAD + : BUS_DMASYNC_POSTWRITE); + bus_dmamap_unload(sc->sc_buffer_dmat, sc->sc_dmamap); + } +#endif + + *sc->sc_dmalen -= trans; + *sc->sc_dmaaddr += trans; + +#if 0 /* this is not normal operation just yet */ + if (*sc->sc_dmalen == 0 || + nsc->sc_phase != nsc->sc_prevphase) + return 0; + + /* and again */ + dma_start(sc, sc->sc_dmaaddr, sc->sc_dmalen, DMACSR(sc) & D_WRITE); + return 1; +#endif + return 0; +} + +/* + * Pseudo (chained) interrupt to le driver to handle DMA errors. + */ +int +lsi64854_enet_intr(void *arg) +{ + struct lsi64854_softc *sc = arg; + u_int32_t csr; + static int dodrain = 0; + int rv; + + csr = L64854_GCSR(sc); + + /* If the DMA logic shows an interrupt, claim it */ + rv = ((csr & E_INT_PEND) != 0) ? 1 : 0; + + if (csr & (E_ERR_PEND|E_SLAVE_ERR)) { + printf("%s: error: csr=%b\n", sc->dv_name, csr, EDMACSR_BITS); + csr &= ~L64854_EN_DMA; /* Stop DMA */ + /* Invalidate the queue; SLAVE_ERR bit is write-to-clear */ + csr |= E_INVALIDATE|E_SLAVE_ERR; + L64854_SCSR(sc, csr); + DMA_RESET(sc); + dodrain = 1; + return (1); + } + + if (dodrain) { /* XXX - is this necessary with D_DSBL_WRINVAL on? */ + int i = 10; + csr |= E_DRAIN; + L64854_SCSR(sc, csr); + while (i-- > 0 && (L64854_GCSR(sc) & D_DRAINING)) + DELAY(1); + } + + return (rv | (*sc->sc_intrchain)(sc->sc_intrchainarg)); +} + +static void +lsi64854_map_pp(void *arg, bus_dma_segment_t *segs, int nsegs, int error) +{ + struct lsi64854_softc *sc; + + sc = (struct lsi64854_softc *)arg; + + if (nsegs != 1) + panic("%s: cannot map %d segments\n", sc->dv_name, nsegs); + + bus_dmamap_sync(sc->sc_buffer_dmat, sc->sc_dmamap, sc->sc_datain + ? BUS_DMASYNC_PREREAD + : BUS_DMASYNC_PREWRITE); + bus_space_write_4(sc->sc_regt, sc->sc_regh, L64854_REG_ADDR, + segs[0].ds_addr); + + bus_space_write_4(sc->sc_regt, sc->sc_regh, L64854_REG_CNT, + sc->sc_dmasize); +} + +/* + * setup a DMA transfer + */ +int +lsi64854_setup_pp(struct lsi64854_softc *sc, caddr_t *addr, size_t *len, + int datain, size_t *dmasize) +{ + u_int32_t csr; + + DMA_FLUSH(sc, 0); + + sc->sc_dmaaddr = addr; + sc->sc_dmalen = len; + sc->sc_datain = datain; + + DPRINTF(LDB_PP, ("%s: pp start %ld@%p,%d\n", sc->dv_name, + (long)*sc->sc_dmalen, *sc->sc_dmaaddr, datain ? 1 : 0)); + + /* + * the rules say we cannot transfer more than the limit + * of this DMA chip (64k for old and 16Mb for new), + * and we cannot cross a 16Mb boundary. + */ + *dmasize = sc->sc_dmasize = + min(*dmasize, DMAMAX((size_t) *sc->sc_dmaaddr)); + + DPRINTF(LDB_PP, ("dma_setup_pp: dmasize = %ld\n", (long)sc->sc_dmasize)); + + /* Program the DMA address */ + if (sc->sc_dmasize) { + if (bus_dmamap_load(sc->sc_buffer_dmat, sc->sc_dmamap, + *sc->sc_dmaaddr, sc->sc_dmasize, + lsi64854_map_pp, sc, 0) != 0) + panic("%s: pp cannot allocate DVMA address", + sc->dv_name); + } + + /* Setup DMA control register */ + csr = L64854_GCSR(sc); + csr &= ~L64854_BURST_SIZE; + if (sc->sc_burst == 32) { + csr |= L64854_BURST_32; + } else if (sc->sc_burst == 16) { + csr |= L64854_BURST_16; + } else { + csr |= L64854_BURST_0; + } + csr |= P_EN_DMA|P_INT_EN|P_EN_CNT; +#if 0 + /* This bit is read-only in PP csr register */ + if (datain) + csr |= P_WRITE; + else + csr &= ~P_WRITE; +#endif + L64854_SCSR(sc, csr); + + return (0); +} +/* + * Parallel port DMA interrupt. + */ +int +lsi64854_pp_intr(void *arg) +{ + struct lsi64854_softc *sc = arg; + int ret, trans, resid = 0; + u_int32_t csr; + + csr = L64854_GCSR(sc); + + DPRINTF(LDB_PP, ("%s: pp intr: addr 0x%x, csr %b\n", sc->dv_name, + bus_space_read_4(sc->sc_regt, sc->sc_regh, L64854_REG_ADDR), + csr, PDMACSR_BITS)); + + if (csr & (P_ERR_PEND|P_SLAVE_ERR)) { + resid = bus_space_read_4(sc->sc_regt, sc->sc_regh, + L64854_REG_CNT); + printf("%s: pp error: resid %d csr=%b\n", sc->dv_name, resid, + csr, PDMACSR_BITS); + csr &= ~P_EN_DMA; /* Stop DMA */ + /* Invalidate the queue; SLAVE_ERR bit is write-to-clear */ + csr |= P_INVALIDATE|P_SLAVE_ERR; + L64854_SCSR(sc, csr); + return (1); + } + + ret = (csr & P_INT_PEND) != 0; + + if (sc->sc_active != 0) { + DMA_DRAIN(sc, 0); + resid = bus_space_read_4(sc->sc_regt, sc->sc_regh, + L64854_REG_CNT); + } + + /* DMA has stopped */ + csr &= ~D_EN_DMA; + L64854_SCSR(sc, csr); + sc->sc_active = 0; + + trans = sc->sc_dmasize - resid; + if (trans < 0) { /* transferred < 0 ? */ + trans = sc->sc_dmasize; + } + *sc->sc_dmalen -= trans; + *sc->sc_dmaaddr += trans; + +#if 0 /* XXX */ + if (sc->sc_dmamap->dm_nsegs > 0) { + bus_dmamap_sync(sc->sc_buffer_dmat, sc->sc_dmamap, + (csr & D_WRITE) != 0 + ? BUS_DMASYNC_POSTREAD + : BUS_DMASYNC_POSTWRITE); + bus_dmamap_unload(sc->sc_buffer_dmat, sc->sc_dmamap); + } +#endif + + return (ret != 0); +} diff --git a/sys/dev/esp/lsi64854reg.h b/sys/dev/esp/lsi64854reg.h new file mode 100644 index 0000000..763d312 --- /dev/null +++ b/sys/dev/esp/lsi64854reg.h @@ -0,0 +1,205 @@ +/* $NetBSD: lsi64854reg.h,v 1.4 1998/09/21 21:26:52 pk Exp $ */ + +/*- + * Copyright (c) 1998 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Paul Kranenburg. + * + * 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 FOUNDATION 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. + */ + +/* $FreeBSD$ */ + +/* + * LSI 64854 DMA engine. Contains three independent channels + * designed to interface with (a) a NCR539X SCSI controller, + * (b) a AM7990 Ethernet controller, (c) Parallel port hardware.. + */ + +/* + * Register offsets to bus handle. + */ +#define L64854_REG_CSR 0 /* Control bits */ +#define L64854_REG_ADDR 4 /* DMA Address */ +#define L64854_REG_CNT 8 /* DMA count */ +#define L64854_REG_CNT_MASK 0x00ffffff /* only 24 bits */ +#define L64854_REG_ENBAR 12 /* ENET Base register */ +#define L64854_REG_TEST 12 /* SCSI Test register */ +#define L64854_REG_HCR 16 /* PP Hardware Configuration */ +#define L64854_REG_OCR 18 /* PP Operation Configuration */ +#define L64854_REG_DR 20 /* PP Data register */ +#define L64854_REG_TCR 21 /* PP Transfer Control */ +#define L64854_REG_OR 22 /* PP Output register */ +#define L64854_REG_IR 23 /* PP Input register */ +#define L64854_REG_ICR 24 /* PP Interrupt Control */ + + +/* + * Control bits common to all three channels. + */ +#define L64854_INT_PEND 0x00000001 /* Interrupt pending */ +#define L64854_ERR_PEND 0x00000002 /* Error pending */ +#define L64854_DRAINING 0x0000000c /* FIFO draining */ +#define L64854_INT_EN 0x00000010 /* Interrupt enable */ +#define L64854_INVALIDATE 0x00000020 /* Invalidate FIFO */ +#define L64854_SLAVE_ERR 0x00000040 /* Slave access size error */ +#define L64854_RESET 0x00000080 /* Reset device */ +#define L64854_WRITE 0x00000100 /* 1: xfer to memory */ +#define L64854_EN_DMA 0x00000200 /* enable DMA transfers */ + +#define L64854_BURST_SIZE 0x000c0000 /* Read/write burst size */ +#define L64854_BURST_0 0x00080000 /* no bursts (SCSI-only) */ +#define L64854_BURST_16 0x00000000 /* 16-byte bursts */ +#define L64854_BURST_32 0x00040000 /* 32-byte bursts */ +#define L64854_BURST_64 0x000c0000 /* 64-byte bursts (fas) */ + +#define L64854_RST_FAS366 0x08000000 /* FAS366 hardware reset */ + +#define L64854_DEVID 0xf0000000 /* device ID bits */ + +/* + * SCSI DMA control bits. + */ +#define D_INT_PEND L64854_INT_PEND /* interrupt pending */ +#define D_ERR_PEND L64854_ERR_PEND /* error pending */ +#define D_DRAINING L64854_DRAINING /* fifo draining */ +#define D_INT_EN L64854_INT_EN /* interrupt enable */ +#define D_INVALIDATE L64854_INVALIDATE/* invalidate fifo */ +#define D_SLAVE_ERR L64854_SLAVE_ERR/* slave access size error */ +#define D_RESET L64854_RESET /* reset scsi */ +#define D_WRITE L64854_WRITE /* 1 = dev -> mem */ +#define D_EN_DMA L64854_EN_DMA /* enable DMA requests */ +#define D_EN_CNT 0x00002000 /* enable byte counter */ +#define D_TC 0x00004000 /* terminal count */ +#define D_WIDE_EN 0x00008000 /* enable wide mode SBUS DMA (fas) */ +#define D_DSBL_CSR_DRN 0x00010000 /* disable fifo drain on csr */ +#define D_DSBL_SCSI_DRN 0x00020000 /* disable fifo drain on reg */ + +#define D_DIAG 0x00100000 /* disable fifo drain on addr */ +#define D_TWO_CYCLE 0x00200000 /* 2 clocks per transfer */ +#define D_FASTER 0x00400000 /* 3 clocks per transfer */ +#define D_TCI_DIS 0x00800000 /* disable intr on D_TC */ +#define D_EN_NEXT 0x01000000 /* enable auto next address */ +#define D_DMA_ON 0x02000000 /* enable dma from scsi XXX */ +#define D_DSBL_PARITY_CHK \ + 0x02000000 /* disable checking for parity on bus (default 1:fas) */ +#define D_A_LOADED 0x04000000 /* address loaded */ +#define D_NA_LOADED 0x08000000 /* next address loaded */ +#define D_HW_RESET_FAS366 \ + 0x08000000 /* hardware reset FAS366 (fas) */ +#define D_DEV_ID L64854_DEVID /* device ID */ +#define DMAREV_0 0x00000000 /* Sunray DMA */ +#define DMAREV_ESC 0x40000000 /* DMA ESC array */ +#define DMAREV_1 0x80000000 /* 'DMA' */ +#define DMAREV_PLUS 0x90000000 /* 'DMA+' */ +#define DMAREV_2 0xa0000000 /* 'DMA2' */ +#define DMAREV_HME 0xb0000000 /* 'HME' */ + +/* + * revisions 0,1 and ESC have different bits. + */ +#define D_ESC_DRAIN 0x00000040 /* rev0,1,esc: drain fifo */ +#define D_ESC_R_PEND 0x00000400 /* rev0,1: request pending */ +#define D_ESC_BURST 0x00000800 /* DMA ESC: 16 byte bursts */ +#define D_ESC_AUTODRAIN 0x00040000 /* DMA ESC: Auto-drain */ + +#define DDMACSR_BITS "\177\020" \ + "b\00INT\0b\01ERR\0f\02\02DRAINING\0b\04IEN\0" \ + "b\06SLVERR\0b\07RST\0b\10WRITE\0b\11ENDMA\0" \ + "b\15ENCNT\0b\16TC\0\b\20DSBL_CSR_DRN\0" \ + "b\21DSBL_SCSI_DRN\0f\22\2BURST\0b\25TWOCYCLE\0" \ + "b\26FASTER\0b\27TCIDIS\0b\30ENNXT\0b\031DMAON\0" \ + "b\32ALOADED\0b\33NALOADED\0" + + +/* + * ENET DMA control bits. + */ +#define E_INT_PEND L64854_INT_PEND /* interrupt pending */ +#define E_ERR_PEND L64854_ERR_PEND /* error pending */ +#define E_DRAINING L64854_DRAINING /* fifo draining */ +#define E_INT_EN L64854_INT_EN /* interrupt enable */ +#define E_INVALIDATE L64854_INVALIDATE/* invalidate fifo */ +#define E_SLAVE_ERR L64854_SLAVE_ERR/* slave access size error */ +#define E_RESET L64854_RESET /* reset ENET */ +#define E_reserved1 0x00000300 /* */ +#define E_DRAIN 0x00000400 /* force Ecache drain */ +#define E_DSBL_WR_DRN 0x00000800 /* disable Ecache drain on .. */ +#define E_DSBL_RD_DRN 0x00001000 /* disable Ecache drain on .. */ +#define E_reserved2 0x00006000 /* */ +#define E_ILACC 0x00008000 /* ... */ +#define E_DSBL_BUF_WR 0x00010000 /* no buffering of slave writes */ +#define E_DSBL_WR_INVAL 0x00020000 /* no Ecache invalidate on slave writes */ + +#define E_reserved3 0x00100000 /* */ +#define E_LOOP_TEST 0x00200000 /* loopback mode */ +#define E_TP_AUI 0x00400000 /* 1 for TP, 0 for AUI */ +#define E_reserved4 0x0c800000 /* */ +#define E_DEV_ID L64854_DEVID /* ID bits */ + +#define EDMACSR_BITS "\177\020" \ + "b\00INT\0b\01ERR\0f\02\02DRAINING\0b\04IEN\0" \ + "b\06SLVERR\0b\07RST\0b\10WRITE\0b\12DRAIN\0" \ + "b\13DSBL_WR_DRN\0b\14DSBL_RD_DRN\0b\17ILACC\0" \ + "b\20DSBL_BUF_WR\0b\21DSBL_WR_INVAL\0" \ + "b\25LOOPTEST\0b\26TP\0" + +/* + * PP DMA control bits. + */ +#define P_INT_PEND L64854_INT_PEND /* interrupt pending */ +#define P_ERR_PEND L64854_ERR_PEND /* error pending */ +#define P_DRAINING L64854_DRAINING /* fifo draining */ +#define P_INT_EN L64854_INT_EN /* interrupt enable */ +#define P_INVALIDATE L64854_INVALIDATE/* invalidate fifo */ +#define P_SLAVE_ERR L64854_SLAVE_ERR/* slave access size error */ +#define P_RESET L64854_RESET /* reset PP */ +#define P_WRITE L64854_WRITE /* 1: xfer to memory */ +#define P_EN_DMA L64854_EN_DMA /* enable DMA transfers */ +#define P_reserved1 0x00001c00 /* */ +#define P_EN_CNT 0x00002000 /* enable counter */ +#define P_TC 0x00004000 /* terminal count */ +#define P_reserved2 0x00038000 /* */ + +#define P_DIAG 0x00100000 /* ... */ +#define P_reserved3 0x00600000 /* */ +#define P_TCI_DIS 0x00800000 /* no interrupt on terminal count */ +#define P_EN_NEXT 0x01000000 /* enable DMA chaining */ +#define P_DMA_ON 0x02000000 /* DMA xfers enabled */ +#define P_A_LOADED 0x04000000 /* addr and byte count valid */ +#define P_NA_LOADED 0x08000000 /* next addr & count valid but not used */ +#define P_DEV_ID L64854_DEVID /* ID bits */ + +#define PDMACSR_BITS "\177\020" \ + "b\00INT\0b\01ERR\0f\02\02DRAINING\0b\04IEN\0" \ + "b\06SLVERR\0b\07RST\0b\10WRITE\0b\11ENDMA\0" \ + "b\15ENCNT\0b\16TC\0\b\24DIAG\0b\27TCIDIS\0" \ + "b\30ENNXT\0b\031DMAON\0b\32ALOADED\0b\33NALOADED\0" diff --git a/sys/dev/esp/lsi64854var.h b/sys/dev/esp/lsi64854var.h new file mode 100644 index 0000000..04b2297 --- /dev/null +++ b/sys/dev/esp/lsi64854var.h @@ -0,0 +1,114 @@ +/* $NetBSD: lsi64854var.h,v 1.4 2001/03/29 02:58:39 petrov Exp $ */ + +/*- + * Copyright (c) 1998 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Paul Kranenburg. + * + * 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 FOUNDATION 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. + */ + +/* $FreeBSD$ */ + +struct lsi64854_softc { + device_t sc_dev; + const char *dv_name; + + int sc_rid; + struct resource *sc_res; + bus_space_handle_t sc_regh; + bus_space_tag_t sc_regt; + u_int sc_rev; /* revision */ + int sc_burst; /* max suported burst size */ + + int sc_channel; +#define L64854_CHANNEL_SCSI 1 +#define L64854_CHANNEL_ENET 2 +#define L64854_CHANNEL_PP 3 + void *sc_client; + + int sc_active; /* DMA active ? */ + bus_dmamap_t sc_dmamap; /* DMA map for bus_dma_* */ + + bus_dma_tag_t sc_parent_dmat; + bus_dma_tag_t sc_buffer_dmat; + int sc_datain; + size_t sc_dmasize; + caddr_t *sc_dmaaddr; + size_t *sc_dmalen; + + void (*reset)(struct lsi64854_softc *);/* reset routine */ + int (*setup)(struct lsi64854_softc *, caddr_t *, size_t *, + int, size_t *); /* DMA setup */ + int (*intr)(void *); /* interrupt handler */ + + int (*sc_intrchain)(void *); /* next handler in intr chain */ + void *sc_intrchainarg; /* arg for next intr handler */ + + u_int sc_dmactl; +}; + +#define L64854_GCSR(sc) \ + (bus_space_read_4((sc)->sc_regt, (sc)->sc_regh, L64854_REG_CSR)) + +#define L64854_SCSR(sc, csr) \ + bus_space_write_4((sc)->sc_regt, (sc)->sc_regh, L64854_REG_CSR, csr) + + +/* + * DMA engine interface functions. + */ +#define DMA_RESET(sc) (((sc)->reset)(sc)) +#define DMA_INTR(sc) (((sc)->intr)(sc)) +#define DMA_SETUP(sc, a, l, d, s) (((sc)->setup)(sc, a, l, d, s)) + +#define DMA_ISACTIVE(sc) ((sc)->sc_active) + +#define DMA_ENINTR(sc) do { \ + u_int32_t csr = L64854_GCSR(sc); \ + csr |= L64854_INT_EN; \ + L64854_SCSR(sc, csr); \ +} while (0) + +#define DMA_ISINTR(sc) (L64854_GCSR(sc) & (D_INT_PEND|D_ERR_PEND)) + +#define DMA_GO(sc) do { \ + u_int32_t csr = L64854_GCSR(sc); \ + csr |= D_EN_DMA; \ + L64854_SCSR(sc, csr); \ + sc->sc_active = 1; \ +} while (0) + + +void lsi64854_attach(struct lsi64854_softc *); +int lsi64854_scsi_intr(void *); +int lsi64854_enet_intr(void *); +int lsi64854_pp_intr(void *); diff --git a/sys/dev/esp/ncr53c9x.c b/sys/dev/esp/ncr53c9x.c new file mode 100644 index 0000000..d160dcb --- /dev/null +++ b/sys/dev/esp/ncr53c9x.c @@ -0,0 +1,2938 @@ +/*- + * Copyright (c) 2004 Scott Long + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. + * + */ + +/* $NetBSD: ncr53c9x.c,v 1.106 2003/04/16 18:53:50 petrov Exp $ */ + +/*- + * Copyright (c) 1998, 2002 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Charles M. Hannum. + * + * 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 FOUNDATION 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. + */ + +/* + * Copyright (c) 1994 Peter Galbavy + * Copyright (c) 1995 Paul Kranenburg + * 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 Peter Galbavy + * 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. + */ + +/* + * Based on aic6360 by Jarle Greipsland + * + * Acknowledgements: Many of the algorithms used in this driver are + * inspired by the work of Julian Elischer (julian@tfs.com) and + * Charles Hannum (mycroft@duality.gnu.ai.mit.edu). Thanks a million! + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/bus.h> +#include <sys/kernel.h> +#include <sys/malloc.h> +#include <sys/resource.h> +#include <sys/lock.h> +#include <sys/mutex.h> +#include <sys/queue.h> +#include <sys/time.h> +#include <sys/callout.h> +#include <vm/uma.h> + +#include <cam/cam.h> +#include <cam/cam_ccb.h> +#include <cam/cam_debug.h> +#include <cam/cam_sim.h> +#include <cam/cam_xpt_sim.h> +#include <cam/scsi/scsi_all.h> +#include <cam/scsi/scsi_message.h> + +#include <dev/esp/ncr53c9xreg.h> +#include <dev/esp/ncr53c9xvar.h> + +int ncr53c9x_debug = NCR_SHOWMISC /*|NCR_SHOWPHASE|NCR_SHOWTRAC|NCR_SHOWCMDS*/; +#ifdef DEBUG +int ncr53c9x_notag = 0; +#endif + +static void ncr53c9x_select(struct ncr53c9x_softc *, struct ncr53c9x_ecb *); +static int ncr53c9x_reselect(struct ncr53c9x_softc *, int, int, int); +static void ncr53c9x_scsi_reset(struct ncr53c9x_softc *); +static void ncr53c9x_poll(struct cam_sim *); +static void ncr53c9x_sched(struct ncr53c9x_softc *); +static void ncr53c9x_done(struct ncr53c9x_softc *, struct ncr53c9x_ecb *); +static void ncr53c9x_msgin(struct ncr53c9x_softc *); +static void ncr53c9x_msgout(struct ncr53c9x_softc *); +static void ncr53c9x_timeout(void *arg); +static void ncr53c9x_watch(void *arg); +static void ncr53c9x_abort(struct ncr53c9x_softc *, struct ncr53c9x_ecb *); +static void ncr53c9x_dequeue(struct ncr53c9x_softc *, + struct ncr53c9x_ecb *); +static void ncr53c9x_sense(struct ncr53c9x_softc *, struct ncr53c9x_ecb *); +static void ncr53c9x_free_ecb(struct ncr53c9x_softc *, + struct ncr53c9x_ecb *); +static void ncr53c9x_wrfifo(struct ncr53c9x_softc *, u_char *, int); +static int ncr53c9x_rdfifo(struct ncr53c9x_softc *, int); + +static struct ncr53c9x_ecb *ncr53c9x_get_ecb(struct ncr53c9x_softc *); +static struct ncr53c9x_linfo *ncr53c9x_lunsearch(struct ncr53c9x_tinfo *, + int64_t lun); + +static __inline void ncr53c9x_readregs(struct ncr53c9x_softc *); +static __inline int ncr53c9x_stp2cpb(struct ncr53c9x_softc *, int); +static __inline void ncr53c9x_setsync(struct ncr53c9x_softc *, + struct ncr53c9x_tinfo *); + +#define NCR_RDFIFO_START 0 +#define NCR_RDFIFO_CONTINUE 1 + +#define NCR_SET_COUNT(sc, size) do { \ + NCR_WRITE_REG((sc), NCR_TCL, (size)); \ + NCR_WRITE_REG((sc), NCR_TCM, (size) >> 8); \ + if ((sc->sc_cfg2 & NCRCFG2_FE) || \ + (sc->sc_rev == NCR_VARIANT_FAS366)) { \ + NCR_WRITE_REG((sc), NCR_TCH, (size) >> 16); \ + } \ + if (sc->sc_rev == NCR_VARIANT_FAS366) { \ + NCR_WRITE_REG(sc, NCR_RCH, 0); \ + } \ +} while (0) + +#ifndef mstohz +#define mstohz(ms) \ + (((ms) < 0x20000) ? \ + ((ms +0u) / 1000u) * hz : \ + ((ms +0u) * hz) /1000u) +#endif + +static int ecb_zone_initialized = 0; +static uma_zone_t ecb_zone; + +/* + * Names for the NCR53c9x variants, correspnding to the variant tags + * in ncr53c9xvar.h. + */ +static const char *ncr53c9x_variant_names[] = { + "ESP100", + "ESP100A", + "ESP200", + "NCR53C94", + "NCR53C96", + "ESP406", + "FAS408", + "FAS216", + "AM53C974", + "FAS366/HME", + "NCR53C90 (86C01)", +}; + +/* + * Search linked list for LUN info by LUN id. + */ +static struct ncr53c9x_linfo * +ncr53c9x_lunsearch(struct ncr53c9x_tinfo *ti, int64_t lun) +{ + struct ncr53c9x_linfo *li; + LIST_FOREACH(li, &ti->luns, link) + if (li->lun == lun) + return (li); + return (NULL); +} + +/* + * Attach this instance, and then all the sub-devices + */ +int +ncr53c9x_attach(struct ncr53c9x_softc *sc) +{ + struct cam_devq *devq; + struct cam_sim *sim; + struct cam_path *path; + + mtx_init(&sc->sc_lock, "ncr", "ncr53c9x lock", MTX_DEF); + + /* + * Note, the front-end has set us up to print the chip variation. + */ + if (sc->sc_rev >= NCR_VARIANT_MAX) { + device_printf(sc->sc_dev, "unknown variant %d, devices not " + "attached\n", sc->sc_rev); + return (EINVAL); + } + + device_printf(sc->sc_dev, "%s, %dMHz, SCSI ID %d\n", + ncr53c9x_variant_names[sc->sc_rev], sc->sc_freq, sc->sc_id); + + sc->sc_ntarg = (sc->sc_rev == NCR_VARIANT_FAS366) ? 16 : 8; + + /* + * Allocate SCSI message buffers. + * Front-ends can override allocation to avoid alignment + * handling in the DMA engines. Note that that ncr53c9x_msgout() + * can request a 1 byte DMA transfer. + */ + if (sc->sc_omess == NULL) + sc->sc_omess = malloc(NCR_MAX_MSG_LEN, M_DEVBUF, M_NOWAIT); + + if (sc->sc_imess == NULL) + sc->sc_imess = malloc(NCR_MAX_MSG_LEN + 1, M_DEVBUF, M_NOWAIT); + + sc->sc_tinfo = malloc(sc->sc_ntarg * sizeof(sc->sc_tinfo[0]), + M_DEVBUF, M_NOWAIT | M_ZERO); + + if (!sc->sc_omess || !sc->sc_imess || !sc->sc_tinfo) { + printf("out of memory\n"); + return (ENOMEM); + } + + callout_init(&sc->sc_watchdog, 0); + + /* + * Treat NCR53C90 with the 86C01 DMA chip exactly as ESP100 + * from now on. + */ + if (sc->sc_rev == NCR_VARIANT_NCR53C90_86C01) + sc->sc_rev = NCR_VARIANT_ESP100; + + sc->sc_ccf = FREQTOCCF(sc->sc_freq); + + /* The value *must not* be == 1. Make it 2 */ + if (sc->sc_ccf == 1) + sc->sc_ccf = 2; + + /* + * The recommended timeout is 250ms. This register is loaded + * with a value calculated as follows, from the docs: + * + * (timout period) x (CLK frequency) + * reg = ------------------------------------- + * 8192 x (Clock Conversion Factor) + * + * Since CCF has a linear relation to CLK, this generally computes + * to the constant of 153. + */ + sc->sc_timeout = ((250 * 1000) * sc->sc_freq) / (8192 * sc->sc_ccf); + + /* CCF register only has 3 bits; 0 is actually 8 */ + sc->sc_ccf &= 7; + + /* + * Register with CAM + */ + devq = cam_simq_alloc(sc->sc_ntarg); + if (devq == NULL) + return (ENOMEM); + + sim = cam_sim_alloc(ncr53c9x_action, ncr53c9x_poll, "esp", sc, + device_get_unit(sc->sc_dev), 256, 256, devq); + if (sim == NULL) { + cam_simq_free(devq); + return (ENOMEM); + } + if (xpt_bus_register(sim, 0) != CAM_SUCCESS) { + cam_sim_free(sim, TRUE); + return (EIO); + } + + if (xpt_create_path(&path, NULL, cam_sim_path(sim), + CAM_TARGET_WILDCARD, CAM_LUN_WILDCARD) + != CAM_REQ_CMP) { + xpt_bus_deregister(cam_sim_path(sim)); + cam_sim_free(sim, TRUE); + return (EIO); + } + + sc->sc_sim = sim; + sc->sc_path = path; + + /* Reset state & bus */ +#if 0 + sc->sc_cfflags = sc->sc_dev.dv_cfdata->cf_flags; +#endif + sc->sc_state = 0; + ncr53c9x_init(sc, 1); + + callout_reset(&sc->sc_watchdog, 60*hz, ncr53c9x_watch, sc); + + return (0); +} + +int +ncr53c9x_detach(struct ncr53c9x_softc *sc, int flags) +{ + +#if 0 /* don't allow detach for now */ + free(sc->sc_imess, M_DEVBUF); + free(sc->sc_omess, M_DEVBUF); +#endif + + return (EINVAL); +} + +/* + * This is the generic ncr53c9x reset function. It does not reset the SCSI bus, + * only this controller, but kills any on-going commands, and also stops + * and resets the DMA. + * + * After reset, registers are loaded with the defaults from the attach + * routine above. + */ +void +ncr53c9x_reset(struct ncr53c9x_softc *sc) +{ + + /* reset DMA first */ + NCRDMA_RESET(sc); + + /* reset SCSI chip */ + NCRCMD(sc, NCRCMD_RSTCHIP); + NCRCMD(sc, NCRCMD_NOP); + DELAY(500); + + /* do these backwards, and fall through */ + switch (sc->sc_rev) { + case NCR_VARIANT_ESP406: + case NCR_VARIANT_FAS408: + NCR_WRITE_REG(sc, NCR_CFG5, sc->sc_cfg5 | NCRCFG5_SINT); + NCR_WRITE_REG(sc, NCR_CFG4, sc->sc_cfg4); + case NCR_VARIANT_AM53C974: + case NCR_VARIANT_FAS216: + case NCR_VARIANT_NCR53C94: + case NCR_VARIANT_NCR53C96: + case NCR_VARIANT_ESP200: + sc->sc_features |= NCR_F_HASCFG3; + NCR_WRITE_REG(sc, NCR_CFG3, sc->sc_cfg3); + case NCR_VARIANT_ESP100A: + sc->sc_features |= NCR_F_SELATN3; + NCR_WRITE_REG(sc, NCR_CFG2, sc->sc_cfg2); + case NCR_VARIANT_ESP100: + NCR_WRITE_REG(sc, NCR_CFG1, sc->sc_cfg1); + NCR_WRITE_REG(sc, NCR_CCF, sc->sc_ccf); + NCR_WRITE_REG(sc, NCR_SYNCOFF, 0); + NCR_WRITE_REG(sc, NCR_TIMEOUT, sc->sc_timeout); + break; + + case NCR_VARIANT_FAS366: + sc->sc_features |= + NCR_F_HASCFG3 | NCR_F_FASTSCSI | NCR_F_SELATN3; + sc->sc_cfg3 = NCRFASCFG3_FASTCLK | NCRFASCFG3_OBAUTO; + sc->sc_cfg3_fscsi = NCRFASCFG3_FASTSCSI; + NCR_WRITE_REG(sc, NCR_CFG3, sc->sc_cfg3); + sc->sc_cfg2 = 0; /* NCRCFG2_HMEFE| NCRCFG2_HME32 */ + NCR_WRITE_REG(sc, NCR_CFG2, sc->sc_cfg2); + NCR_WRITE_REG(sc, NCR_CFG1, sc->sc_cfg1); + NCR_WRITE_REG(sc, NCR_CCF, sc->sc_ccf); + NCR_WRITE_REG(sc, NCR_SYNCOFF, 0); + NCR_WRITE_REG(sc, NCR_TIMEOUT, sc->sc_timeout); + break; + + default: + device_printf(sc->sc_dev, "unknown revision code, " + "assuming ESP100\n"); + NCR_WRITE_REG(sc, NCR_CFG1, sc->sc_cfg1); + NCR_WRITE_REG(sc, NCR_CCF, sc->sc_ccf); + NCR_WRITE_REG(sc, NCR_SYNCOFF, 0); + NCR_WRITE_REG(sc, NCR_TIMEOUT, sc->sc_timeout); + } + + if (sc->sc_rev == NCR_VARIANT_AM53C974) + NCR_WRITE_REG(sc, NCR_AMDCFG4, sc->sc_cfg4); + +#if 0 + device_printf(sc->sc_dev, "ncr53c9x_reset: revision %d\n", + sc->sc_rev); + device_printf(sc->sc_dev, "ncr53c9x_reset: cfg1 0x%x, cfg2 0x%x, " + "cfg3 0x%x, ccf 0x%x, timeout 0x%x\n", + sc->sc_cfg1, sc->sc_cfg2, sc->sc_cfg3, sc->sc_ccf, sc->sc_timeout); +#endif +} + +/* + * Reset the SCSI bus, but not the chip + */ +static void +ncr53c9x_scsi_reset(struct ncr53c9x_softc *sc) +{ + + (*sc->sc_glue->gl_dma_stop)(sc); + + NCR_MISC(("%s: resetting SCSI bus\n", device_get_nameunit(sc->sc_dev))); + NCRCMD(sc, NCRCMD_RSTSCSI); + DELAY(250000); /* Give the bus a fighting chance to settle */ +} + +/* + * Initialize ncr53c9x state machine + */ +void +ncr53c9x_init(struct ncr53c9x_softc *sc, int doreset) +{ + struct ncr53c9x_ecb *ecb; + struct ncr53c9x_linfo *li; + int i, r; + + NCR_MISC(("[NCR_INIT(%d) %d] ", doreset, sc->sc_state)); + + if (!ecb_zone_initialized) { + /* All instances share this zone */ + ecb_zone = uma_zcreate("ncr53c9x ecb zone", + sizeof(struct ncr53c9x_ecb), NULL, NULL, + NULL, NULL, 0, 0); + ecb_zone_initialized = 1; + } + + if (sc->sc_state == 0) { + /* First time through; initialize. */ + + TAILQ_INIT(&sc->ready_list); + sc->sc_nexus = NULL; + memset(sc->sc_tinfo, 0, sizeof(sc->sc_tinfo)); + for (r = 0; r < sc->sc_ntarg; r++) { + LIST_INIT(&sc->sc_tinfo[r].luns); + } + } else { + /* Cancel any active commands. */ + sc->sc_state = NCR_CLEANING; + sc->sc_msgify = 0; + if ((ecb = sc->sc_nexus) != NULL) { + ecb->ccb->ccb_h.status = CAM_CMD_TIMEOUT; + ncr53c9x_done(sc, ecb); + } + /* Cancel outstanding disconnected commands on each LUN */ + for (r = 0; r < sc->sc_ntarg; r++) { + LIST_FOREACH(li, &sc->sc_tinfo[r].luns, link) { + if ((ecb = li->untagged) != NULL) { + li->untagged = NULL; + /* + * XXXXXXX + * + * Should we terminate a command + * that never reached the disk? + */ + li->busy = 0; + ecb->ccb->ccb_h.status = + CAM_CMD_TIMEOUT; + ncr53c9x_done(sc, ecb); + } + for (i = 0; i < 256; i++) + if ((ecb = li->queued[i])) { + li->queued[i] = NULL; + ecb->ccb->ccb_h.status = + CAM_CMD_TIMEOUT; + ncr53c9x_done(sc, ecb); + } + li->used = 0; + } + } + } + + /* + * reset the chip to a known state + */ + ncr53c9x_reset(sc); + + sc->sc_phase = sc->sc_prevphase = INVALID_PHASE; + for (r = 0; r < sc->sc_ntarg; r++) { + struct ncr53c9x_tinfo *ti = &sc->sc_tinfo[r]; +/* XXX - config flags per target: low bits: no reselect; high bits: no synch */ + + ti->flags = ((sc->sc_minsync && !(sc->sc_cfflags & (1<<((r&7)+8)))) + ? 0 : T_SYNCHOFF) | + ((sc->sc_cfflags & (1<<(r&7))) ? T_RSELECTOFF : 0); +#ifdef DEBUG + if (ncr53c9x_notag) + ti->flags &= ~T_TAG; +#endif + ti->period = sc->sc_minsync; + ti->offset = 0; + ti->cfg3 = 0; + } + + if (doreset) { + sc->sc_state = NCR_SBR; + NCRCMD(sc, NCRCMD_RSTSCSI); + } else { + sc->sc_state = NCR_IDLE; + ncr53c9x_sched(sc); + } +} + +/* + * Read the NCR registers, and save their contents for later use. + * NCR_STAT, NCR_STEP & NCR_INTR are mostly zeroed out when reading + * NCR_INTR - so make sure it is the last read. + * + * I think that (from reading the docs) most bits in these registers + * only make sense when he DMA CSR has an interrupt showing. Call only + * if an interrupt is pending. + */ +static __inline void +ncr53c9x_readregs(struct ncr53c9x_softc *sc) +{ + + sc->sc_espstat = NCR_READ_REG(sc, NCR_STAT); + /* Only the stepo bits are of interest */ + sc->sc_espstep = NCR_READ_REG(sc, NCR_STEP) & NCRSTEP_MASK; + + if (sc->sc_rev == NCR_VARIANT_FAS366) + sc->sc_espstat2 = NCR_READ_REG(sc, NCR_STAT2); + + sc->sc_espintr = NCR_READ_REG(sc, NCR_INTR); + + if (sc->sc_glue->gl_clear_latched_intr != NULL) + (*sc->sc_glue->gl_clear_latched_intr)(sc); + + /* + * Determine the SCSI bus phase, return either a real SCSI bus phase + * or some pseudo phase we use to detect certain exceptions. + */ + + sc->sc_phase = (sc->sc_espintr & NCRINTR_DIS) ? + /* Disconnected */ BUSFREE_PHASE : sc->sc_espstat & NCRSTAT_PHASE; + + NCR_INTS(("regs[intr=%02x,stat=%02x,step=%02x,stat2=%02x] ", + sc->sc_espintr, sc->sc_espstat, sc->sc_espstep, sc->sc_espstat2)); +} + +/* + * Convert Synchronous Transfer Period to chip register Clock Per Byte value. + */ +static __inline int +ncr53c9x_stp2cpb(struct ncr53c9x_softc *sc, int period) +{ + int v; + v = (sc->sc_freq * period) / 250; + if (ncr53c9x_cpb2stp(sc, v) < period) + /* Correct round-down error */ + v++; + return (v); +} + +static __inline void +ncr53c9x_setsync(struct ncr53c9x_softc *sc, struct ncr53c9x_tinfo *ti) +{ + u_char syncoff, synctp; + u_char cfg3 = sc->sc_cfg3 | ti->cfg3; + + if (ti->flags & T_SYNCMODE) { + syncoff = ti->offset; + synctp = ncr53c9x_stp2cpb(sc, ti->period); + if (sc->sc_features & NCR_F_FASTSCSI) { + /* + * If the period is 200ns or less (ti->period <= 50), + * put the chip in Fast SCSI mode. + */ + if (ti->period <= 50) + /* + * There are (at least) 4 variations of the + * configuration 3 register. The drive attach + * routine sets the appropriate bit to put the + * chip into Fast SCSI mode so that it doesn't + * have to be figured out here each time. + */ + cfg3 |= sc->sc_cfg3_fscsi; + } + + /* + * Am53c974 requires different SYNCTP values when the + * FSCSI bit is off. + */ + if (sc->sc_rev == NCR_VARIANT_AM53C974 && + (cfg3 & NCRAMDCFG3_FSCSI) == 0) + synctp--; + } else { + syncoff = 0; + synctp = 0; + } + + if (sc->sc_features & NCR_F_HASCFG3) + NCR_WRITE_REG(sc, NCR_CFG3, cfg3); + + NCR_WRITE_REG(sc, NCR_SYNCOFF, syncoff); + NCR_WRITE_REG(sc, NCR_SYNCTP, synctp); +} + +/* + * Send a command to a target, set the driver state to NCR_SELECTING + * and let the caller take care of the rest. + * + * Keeping this as a function allows me to say that this may be done + * by DMA instead of programmed I/O soon. + */ +static void +ncr53c9x_select(struct ncr53c9x_softc *sc, struct ncr53c9x_ecb *ecb) +{ + int target = ecb->ccb->ccb_h.target_id; + int lun = ecb->ccb->ccb_h.target_lun; + struct ncr53c9x_tinfo *ti; + int tiflags; + u_char *cmd; + int clen; + int selatn3, selatns; + size_t dmasize; + + NCR_TRACE(("[ncr53c9x_select(t%d,l%d,cmd:%x,tag:%x,%x)] ", + target, lun, ecb->cmd.cmd.opcode, ecb->tag[0], ecb->tag[1])); + + ti = &sc->sc_tinfo[target]; + tiflags = ti->flags; + sc->sc_state = NCR_SELECTING; + /* + * Schedule the timeout now, the first time we will go away + * expecting to come back due to an interrupt, because it is + * always possible that the interrupt may never happen. + */ + ecb->ccb->ccb_h.timeout_ch = + timeout(ncr53c9x_timeout, sc, mstohz(ecb->timeout)); + + /* + * The docs say the target register is never reset, and I + * can't think of a better place to set it + */ + if (sc->sc_rev == NCR_VARIANT_FAS366) { + NCRCMD(sc, NCRCMD_FLUSH); + NCR_WRITE_REG(sc, NCR_SELID, target | NCR_BUSID_HME); + } else { + NCR_WRITE_REG(sc, NCR_SELID, target); + } + ncr53c9x_setsync(sc, ti); + + if ((ecb->flags & ECB_SENSE) != 0) { + /* + * For REQUEST SENSE, we should not send an IDENTIFY or + * otherwise mangle the target. There should be no MESSAGE IN + * phase. + */ + if (sc->sc_features & NCR_F_DMASELECT) { + /* setup DMA transfer for command */ + dmasize = clen = ecb->clen; + sc->sc_cmdlen = clen; + sc->sc_cmdp = (caddr_t)&ecb->cmd.cmd; + + /* Program the SCSI counter */ + NCR_SET_COUNT(sc, dmasize); + + if (sc->sc_rev != NCR_VARIANT_FAS366) + NCRCMD(sc, NCRCMD_NOP|NCRCMD_DMA); + + /* And get the targets attention */ + NCRCMD(sc, NCRCMD_SELNATN | NCRCMD_DMA); + NCRDMA_SETUP(sc, &sc->sc_cmdp, &sc->sc_cmdlen, 0, + &dmasize); + NCRDMA_GO(sc); + } else { + ncr53c9x_wrfifo(sc, (u_char *)&ecb->cmd.cmd, ecb->clen); + NCRCMD(sc, NCRCMD_SELNATN); + } + return; + } + + selatn3 = selatns = 0; + if (ecb->tag[0] != 0) { + if (sc->sc_features & NCR_F_SELATN3) + /* use SELATN3 to send tag messages */ + selatn3 = 1; + else + /* We don't have SELATN3; use SELATNS to send tags */ + selatns = 1; + } + + if (ti->flags & T_NEGOTIATE) { + /* We have to use SELATNS to send sync/wide messages */ + selatn3 = 0; + selatns = 1; + } + + cmd = (u_char *)&ecb->cmd.cmd; + + if (selatn3) { + /* We'll use tags with SELATN3 */ + clen = ecb->clen + 3; + cmd -= 3; + cmd[0] = MSG_IDENTIFY(lun, 1); /* msg[0] */ + cmd[1] = ecb->tag[0]; /* msg[1] */ + cmd[2] = ecb->tag[1]; /* msg[2] */ + } else { + /* We don't have tags, or will send messages with SELATNS */ + clen = ecb->clen + 1; + cmd -= 1; + cmd[0] = MSG_IDENTIFY(lun, (tiflags & T_RSELECTOFF) == 0); + } + + if ((sc->sc_features & NCR_F_DMASELECT) && !selatns) { + + /* setup DMA transfer for command */ + dmasize = clen; + sc->sc_cmdlen = clen; + sc->sc_cmdp = cmd; + + /* Program the SCSI counter */ + NCR_SET_COUNT(sc, dmasize); + + /* load the count in */ + /* if (sc->sc_rev != NCR_VARIANT_FAS366) */ + NCRCMD(sc, NCRCMD_NOP|NCRCMD_DMA); + + /* And get the targets attention */ + if (selatn3) { + sc->sc_msgout = SEND_TAG; + sc->sc_flags |= NCR_ATN; + NCRCMD(sc, NCRCMD_SELATN3 | NCRCMD_DMA); + } else + NCRCMD(sc, NCRCMD_SELATN | NCRCMD_DMA); + NCRDMA_SETUP(sc, &sc->sc_cmdp, &sc->sc_cmdlen, 0, &dmasize); + NCRDMA_GO(sc); + return; + } + + /* + * Who am I. This is where we tell the target that we are + * happy for it to disconnect etc. + */ + + /* Now get the command into the FIFO */ + ncr53c9x_wrfifo(sc, cmd, clen); + + /* And get the targets attention */ + if (selatns) { + NCR_MSGS(("SELATNS \n")); + /* Arbitrate, select and stop after IDENTIFY message */ + NCRCMD(sc, NCRCMD_SELATNS); + } else if (selatn3) { + sc->sc_msgout = SEND_TAG; + sc->sc_flags |= NCR_ATN; + NCRCMD(sc, NCRCMD_SELATN3); + } else + NCRCMD(sc, NCRCMD_SELATN); +} + +static void +ncr53c9x_free_ecb(struct ncr53c9x_softc *sc, struct ncr53c9x_ecb *ecb) +{ + + ecb->flags = 0; + uma_zfree(ecb_zone, (void *)ecb); + return; +} + +static struct ncr53c9x_ecb * +ncr53c9x_get_ecb(struct ncr53c9x_softc *sc) +{ + struct ncr53c9x_ecb *ecb; + + ecb = (struct ncr53c9x_ecb *)uma_zalloc(ecb_zone, M_NOWAIT); + if (ecb) { + bzero(ecb, sizeof(struct ncr53c9x_ecb)); + ecb->flags |= ECB_ALLOC; + ecb->sc = sc; + } + return (ecb); +} + +/* + * DRIVER FUNCTIONS CALLABLE FROM HIGHER LEVEL DRIVERS + */ + +/* + * Start a SCSI-command + * This function is called by the higher level SCSI-driver to queue/run + * SCSI-commands. + */ + +void +ncr53c9x_action(struct cam_sim *sim, union ccb *ccb) +{ + struct ncr53c9x_softc *sc; + struct ncr53c9x_ecb *ecb; + + NCR_TRACE(("[ncr53c9x_action %d]", ccb->ccb_h.func_code)); + + sc = cam_sim_softc(sim); + mtx_lock(&sc->sc_lock); + + switch (ccb->ccb_h.func_code) { + case XPT_RESET_BUS: + ncr53c9x_scsi_reset(sc); + ccb->ccb_h.status = CAM_REQ_CMP; + mtx_unlock(&sc->sc_lock); + xpt_done(ccb); + return; + case XPT_CALC_GEOMETRY: + mtx_unlock(&sc->sc_lock); + cam_calc_geometry(&ccb->ccg, 0); /* XXX Extended? */ + xpt_done(ccb); + return; + case XPT_PATH_INQ: + { + struct ccb_pathinq *cpi = &ccb->cpi; + + cpi->version_num = 1; + cpi->hba_inquiry = PI_SDTR_ABLE/*|PI_TAG_ABLE*/; + cpi->hba_inquiry |= + (sc->sc_rev == NCR_VARIANT_FAS366) ? PI_WIDE_16 : 0; + cpi->target_sprt = 0; + cpi->hba_misc = 0; + cpi->hba_eng_cnt = 0; + cpi->max_target = sc->sc_ntarg - 1; + cpi->max_lun = 8; + cpi->initiator_id = sc->sc_id; + cpi->bus_id = 0; + cpi->base_transfer_speed = 3300; + strncpy(cpi->sim_vid, "FreeBSD", SIM_IDLEN); + strncpy(cpi->hba_vid, "Sun", HBA_IDLEN); + strncpy(cpi->dev_name, cam_sim_name(sim), DEV_IDLEN); + cpi->unit_number = cam_sim_unit(sim); + ccb->ccb_h.status = CAM_REQ_CMP; + mtx_unlock(&sc->sc_lock); + xpt_done(ccb); + return; + } + case XPT_GET_TRAN_SETTINGS: + { + struct ccb_trans_settings *cts = &ccb->cts; + struct ncr53c9x_tinfo *ti; + + ti = &sc->sc_tinfo[ccb->ccb_h.target_id]; + + if ((cts->flags & CCB_TRANS_CURRENT_SETTINGS) != 0) { + cts->sync_period = ti->period; + cts->sync_offset = ti->offset; + cts->bus_width = ti->width; + if ((ti->flags & T_TAG) != 0) + cts->flags |= + (CCB_TRANS_DISC_ENB | CCB_TRANS_TAG_ENB); + else + cts->flags &= + ~(CCB_TRANS_DISC_ENB | CCB_TRANS_TAG_ENB); + } else { + cts->sync_period = sc->sc_maxsync; + cts->sync_offset = sc->sc_maxoffset; + cts->bus_width = sc->sc_maxwidth; + cts->flags |= (CCB_TRANS_DISC_ENB | CCB_TRANS_TAG_ENB); + } + cts->valid = CCB_TRANS_BUS_WIDTH_VALID | + CCB_TRANS_SYNC_RATE_VALID | + CCB_TRANS_SYNC_OFFSET_VALID | + CCB_TRANS_DISC_VALID | + CCB_TRANS_TQ_VALID; + ccb->ccb_h.status = CAM_REQ_CMP; + mtx_unlock(&sc->sc_lock); + xpt_done(ccb); + return; + } + case XPT_ABORT: + printf("XPT_ABORT called\n"); + ccb->ccb_h.status = CAM_FUNC_NOTAVAIL; + mtx_unlock(&sc->sc_lock); + xpt_done(ccb); + return; + case XPT_TERM_IO: + printf("XPT_TERM_IO called\n"); + ccb->ccb_h.status = CAM_FUNC_NOTAVAIL; + mtx_unlock(&sc->sc_lock); + xpt_done(ccb); + return; + case XPT_RESET_DEV: + printf("XPT_RESET_DEV called\n"); + case XPT_SCSI_IO: + { + struct ccb_scsiio *csio; + + if (ccb->ccb_h.target_id < 0 || + ccb->ccb_h.target_id >= sc->sc_ntarg) { + ccb->ccb_h.status = CAM_PATH_INVALID; + mtx_unlock(&sc->sc_lock); + xpt_done(ccb); + return; + } + /* Get an ECB to use. */ + ecb = ncr53c9x_get_ecb(sc); + /* + * This should never happen as we track resources + * in the mid-layer. + */ + if (ecb == NULL) { + xpt_freeze_simq(sim, 1); + ccb->ccb_h.status = CAM_REQUEUE_REQ; + printf("unable to allocate ecb\n"); + mtx_unlock(&sc->sc_lock); + xpt_done(ccb); + return; + } + + /* Initialize ecb */ + ecb->ccb = ccb; + ecb->timeout = ccb->ccb_h.timeout; + + if (ccb->ccb_h.func_code == XPT_RESET_BUS) { + ecb->flags |= ECB_RESET; + ecb->clen = 0; + ecb->dleft = 0; + } else { + csio = &ccb->csio; + if ((ccb->ccb_h.flags & CAM_CDB_POINTER) != 0) + bcopy(csio->cdb_io.cdb_ptr, &ecb->cmd.cmd, + csio->cdb_len); + else + bcopy(csio->cdb_io.cdb_bytes, &ecb->cmd.cmd, + csio->cdb_len); + ecb->clen = csio->cdb_len; + ecb->daddr = csio->data_ptr; + ecb->dleft = csio->dxfer_len; + } + ecb->stat = 0; + + TAILQ_INSERT_TAIL(&sc->ready_list, ecb, chain); + ecb->flags |= ECB_READY; + if (sc->sc_state == NCR_IDLE) + ncr53c9x_sched(sc); + + break; + } + + case XPT_SET_TRAN_SETTINGS: + { + struct ncr53c9x_tinfo *ti; + struct ccb_trans_settings *cts = &ccb->cts; + int target = ccb->ccb_h.target_id; + + ti = &sc->sc_tinfo[target]; + + if ((cts->valid & CCB_TRANS_TQ_VALID) != 0) { + if ((sc->sc_cfflags & (1<<((target & 7) + 16))) == 0 && + (cts->flags & CCB_TRANS_TAG_ENB)) { + NCR_MISC(("%s: target %d: tagged queuing\n", + device_get_nameunit(sc->sc_dev), target)); + ti->flags |= T_TAG; + } else + ti->flags &= ~T_TAG; + } + + if ((cts->valid & CCB_TRANS_BUS_WIDTH_VALID) != 0) { + if (cts->bus_width != 0) { + NCR_MISC(("%s: target %d: wide negotiation\n", + device_get_nameunit(sc->sc_dev), target)); + if (sc->sc_rev == NCR_VARIANT_FAS366) { + ti->flags |= T_WIDE; + ti->width = 1; + } + } else { + ti->flags &= ~T_WIDE; + ti->width = 0; + } + } + + if ((cts->valid & CCB_TRANS_SYNC_RATE_VALID) != 0) { + NCR_MISC(("%s: target %d: sync period negotiation\n", + device_get_nameunit(sc->sc_dev), target)); + ti->flags |= T_NEGOTIATE; + ti->period = cts->sync_period; + } + + if ((cts->valid & CCB_TRANS_SYNC_OFFSET_VALID) != 0) { + NCR_MISC(("%s: target %d: sync offset negotiation\n", + device_get_nameunit(sc->sc_dev), target)); + ti->flags |= T_NEGOTIATE; + ti->offset = cts->sync_offset; + } + + mtx_unlock(&sc->sc_lock); + ccb->ccb_h.status = CAM_REQ_CMP; + xpt_done(ccb); + return; + } + + default: + device_printf(sc->sc_dev, "Unhandled function code %d\n", + ccb->ccb_h.func_code); + ccb->ccb_h.status = CAM_PROVIDE_FAIL; + mtx_unlock(&sc->sc_lock); + xpt_done(ccb); + return; + } + + mtx_unlock(&sc->sc_lock); +} + +/* + * Used when interrupt driven I/O isn't allowed, e.g. during boot. + */ +static void +ncr53c9x_poll(struct cam_sim *sim) +{ + struct ncr53c9x_softc *sc; + + NCR_TRACE(("[ncr53c9x_poll] ")); + sc = cam_sim_softc(sim); + if (NCRDMA_ISINTR(sc)) { + ncr53c9x_intr(sc); + } +} + +/* + * LOW LEVEL SCSI UTILITIES + */ + +/* + * Schedule a scsi operation. This has now been pulled out of the interrupt + * handler so that we may call it from ncr53c9x_scsipi_request and + * ncr53c9x_done. This may save us an unecessary interrupt just to get + * things going. Should only be called when state == NCR_IDLE and at bio pl. + */ +static void +ncr53c9x_sched(struct ncr53c9x_softc *sc) +{ + struct ncr53c9x_ecb *ecb; + struct ncr53c9x_tinfo *ti; + struct ncr53c9x_linfo *li; + int lun; + int tag; + + NCR_TRACE(("[ncr53c9x_sched] ")); + if (sc->sc_state != NCR_IDLE) + panic("ncr53c9x_sched: not IDLE (state=%d)", sc->sc_state); + + /* + * Find first ecb in ready queue that is for a target/lunit + * combinations that is not busy. + */ + for (ecb = TAILQ_FIRST(&sc->ready_list); ecb != NULL; + ecb = TAILQ_NEXT(ecb, chain)) { + ti = &sc->sc_tinfo[ecb->ccb->ccb_h.target_id]; + lun = ecb->ccb->ccb_h.target_lun; + + /* Select type of tag for this command */ + if ((ti->flags & (T_RSELECTOFF)) != 0) + tag = 0; + else if ((ti->flags & (T_TAG)) == 0) + tag = 0; + else if ((ecb->flags & ECB_SENSE) != 0) + tag = 0; + else + tag = ecb->ccb->csio.tag_action; + + li = TINFO_LUN(ti, lun); + if (li == NULL) { + /* Initialize LUN info and add to list. */ + if ((li = malloc(sizeof(*li), + M_DEVBUF, M_NOWAIT|M_ZERO)) == NULL) { + continue; + } + li->lun = lun; + + LIST_INSERT_HEAD(&ti->luns, li, link); + if (lun < NCR_NLUN) + ti->lun[lun] = li; + } + li->last_used = time_second; + if (tag == 0) { + /* Try to issue this as an un-tagged command */ + if (li->untagged == NULL) + li->untagged = ecb; + } + if (li->untagged != NULL) { + tag = 0; + if ((li->busy != 1) && li->used == 0) { + /* We need to issue this untagged command now */ + ecb = li->untagged; + } else { + /* Not ready yet */ + continue; + } + } + ecb->tag[0] = tag; + if (tag != 0) { + li->queued[ecb->ccb->csio.tag_id] = ecb; + ecb->tag[1] = ecb->ccb->csio.tag_id; + li->used++; + } + if (li->untagged != NULL && (li->busy != 1)) { + li->busy = 1; + TAILQ_REMOVE(&sc->ready_list, ecb, chain); + ecb->flags &= ~ECB_READY; + sc->sc_nexus = ecb; + ncr53c9x_select(sc, ecb); + break; + } + if (li->untagged == NULL && tag != 0) { + TAILQ_REMOVE(&sc->ready_list, ecb, chain); + ecb->flags &= ~ECB_READY; + sc->sc_nexus = ecb; + ncr53c9x_select(sc, ecb); + break; + } else + NCR_TRACE(("%d:%d busy\n", + ecb->ccb->ccb_h.target_id, + ecb->ccb->ccb_h.target_lun)); + } +} + +static void +ncr53c9x_sense(struct ncr53c9x_softc *sc, struct ncr53c9x_ecb *ecb) +{ + union ccb *ccb = ecb->ccb; + struct ncr53c9x_tinfo *ti; + struct scsi_request_sense *ss = (void *)&ecb->cmd.cmd; + struct ncr53c9x_linfo *li; + int lun; + + NCR_TRACE(("requesting sense ")); + + lun = ccb->ccb_h.target_lun; + ti = &sc->sc_tinfo[ccb->ccb_h.target_id]; + + /* Next, setup a request sense command block */ + memset(ss, 0, sizeof(*ss)); + ss->opcode = REQUEST_SENSE; + ss->byte2 = ccb->ccb_h.target_lun << SCSI_CMD_LUN_SHIFT; + ss->length = sizeof(struct scsi_sense_data); + ecb->clen = sizeof(*ss); + ecb->daddr = (char *)&ecb->ccb->csio.sense_data; + ecb->dleft = sizeof(struct scsi_sense_data); + ecb->flags |= ECB_SENSE; + ecb->timeout = NCR_SENSE_TIMEOUT; + ti->senses++; + li = TINFO_LUN(ti, lun); + if (li->busy) + li->busy = 0; + ncr53c9x_dequeue(sc, ecb); + li->untagged = ecb; /* must be executed first to fix C/A */ + li->busy = 2; + if (ecb == sc->sc_nexus) { + ncr53c9x_select(sc, ecb); + } else { + TAILQ_INSERT_HEAD(&sc->ready_list, ecb, chain); + ecb->flags |= ECB_READY; + if (sc->sc_state == NCR_IDLE) + ncr53c9x_sched(sc); + } +} + +/* + * POST PROCESSING OF SCSI_CMD (usually current) + */ +static void +ncr53c9x_done(struct ncr53c9x_softc *sc, struct ncr53c9x_ecb *ecb) +{ + union ccb *ccb = ecb->ccb; + struct ncr53c9x_tinfo *ti; + struct ncr53c9x_linfo *li; + int lun; + + NCR_TRACE(("[ncr53c9x_done(status:%x)] ", ccb->ccb_h.status)); + + ti = &sc->sc_tinfo[ccb->ccb_h.target_id]; + lun = ccb->ccb_h.target_lun; + li = TINFO_LUN(ti, lun); + + untimeout(ncr53c9x_timeout, sc, ccb->ccb_h.timeout_ch); + + /* + * Now, if we've come here with no error code, i.e. we've kept the + * initial XS_NOERROR, and the status code signals that we should + * check sense, we'll need to set up a request sense cmd block and + * push the command back into the ready queue *before* any other + * commands for this target/lunit, else we lose the sense info. + * We don't support chk sense conditions for the request sense cmd. + */ + if (ccb->ccb_h.status == CAM_REQ_CMP) { + if ((ecb->flags & ECB_ABORT) != 0) { + ccb->ccb_h.status = CAM_CMD_TIMEOUT; + } else if ((ecb->flags & ECB_SENSE) != 0) { + } else if (ecb->stat == SCSI_STATUS_CHECK_COND) { + if ((ecb->flags & ECB_SENSE) != 0) + ccb->ccb_h.status = CAM_AUTOSENSE_FAIL; + else { + /* First, save the return values */ + ccb->csio.resid = ecb->dleft; + ncr53c9x_sense(sc, ecb); + return; + } + } else { + ccb->csio.resid = ecb->dleft; + } +#if 0 + if (xs->status == SCSI_QUEUE_FULL || xs->status == XS_BUSY) + xs->error = XS_BUSY; +#endif + } + +#ifdef NCR53C9X_DEBUG + if (ncr53c9x_debug & NCR_SHOWTRAC) { + if (ccb->csio.resid != 0) + printf("resid=%d ", ccb->csio.resid); +#if 0 + if (xs->error == XS_SENSE) + printf("sense=0x%02x\n", + xs->sense.scsi_sense.error_code); + else + printf("error=%d\n", xs->error); +#endif + } +#endif + + /* + * Remove the ECB from whatever queue it's on. + */ + ncr53c9x_dequeue(sc, ecb); + if (ecb == sc->sc_nexus) { + sc->sc_nexus = NULL; + if (sc->sc_state != NCR_CLEANING) { + sc->sc_state = NCR_IDLE; + ncr53c9x_sched(sc); + } + } + + if (ccb->ccb_h.status == CAM_SEL_TIMEOUT) { + /* Selection timeout -- discard this LUN if empty */ + if (li->untagged == NULL && li->used == 0) { + if (lun < NCR_NLUN) + ti->lun[lun] = NULL; + LIST_REMOVE(li, link); + free(li, M_DEVBUF); + } + } + + ncr53c9x_free_ecb(sc, ecb); + ti->cmds++; + mtx_unlock(&sc->sc_lock); + xpt_done(ccb); + mtx_lock(&sc->sc_lock); +} + +static void +ncr53c9x_dequeue(struct ncr53c9x_softc *sc, struct ncr53c9x_ecb *ecb) +{ + struct ncr53c9x_tinfo *ti; + struct ncr53c9x_linfo *li; + int64_t lun; + + ti = &sc->sc_tinfo[ecb->ccb->ccb_h.target_id]; + lun = ecb->ccb->ccb_h.target_lun; + li = TINFO_LUN(ti, lun); +#ifdef DIAGNOSTIC + if (li == NULL || li->lun != lun) + panic("ncr53c9x_dequeue: lun %qx for ecb %p does not exist", + (long long) lun, ecb); +#endif + if (li->untagged == ecb) { + li->busy = 0; + li->untagged = NULL; + } + if (ecb->tag[0] && li->queued[ecb->tag[1]] != NULL) { +#ifdef DIAGNOSTIC + if (li->queued[ecb->tag[1]] != NULL && + (li->queued[ecb->tag[1]] != ecb)) + panic("ncr53c9x_dequeue: slot %d for lun %qx has %p " + "instead of ecb %p\n", ecb->tag[1], + (long long) lun, + li->queued[ecb->tag[1]], ecb); +#endif + li->queued[ecb->tag[1]] = NULL; + li->used--; + } + + if ((ecb->flags & ECB_READY) != 0) { + ecb->flags &= ~ECB_READY; + TAILQ_REMOVE(&sc->ready_list, ecb, chain); + } +} + +/* + * INTERRUPT/PROTOCOL ENGINE + */ + +/* + * Schedule an outgoing message by prioritizing it, and asserting + * attention on the bus. We can only do this when we are the initiator + * else there will be an illegal command interrupt. + */ +#define ncr53c9x_sched_msgout(m) \ + do { \ + NCR_MSGS(("ncr53c9x_sched_msgout %x %d", m, __LINE__)); \ + NCRCMD(sc, NCRCMD_SETATN); \ + sc->sc_flags |= NCR_ATN; \ + sc->sc_msgpriq |= (m); \ + } while (0) + +static void +ncr53c9x_flushfifo(struct ncr53c9x_softc *sc) +{ + NCR_TRACE(("[flushfifo] ")); + + NCRCMD(sc, NCRCMD_FLUSH); + + if (sc->sc_phase == COMMAND_PHASE || + sc->sc_phase == MESSAGE_OUT_PHASE) + DELAY(2); +} + +static int +ncr53c9x_rdfifo(struct ncr53c9x_softc *sc, int how) +{ + int i, n; + u_char *buf; + + switch(how) { + case NCR_RDFIFO_START: + buf = sc->sc_imess; + sc->sc_imlen = 0; + break; + case NCR_RDFIFO_CONTINUE: + buf = sc->sc_imess + sc->sc_imlen; + break; + default: + panic("ncr53c9x_rdfifo: bad flag"); + break; + } + + /* + * XXX buffer (sc_imess) size for message + */ + + n = NCR_READ_REG(sc, NCR_FFLAG) & NCRFIFO_FF; + + if (sc->sc_rev == NCR_VARIANT_FAS366) { + n *= 2; + + for (i = 0; i < n; i++) + buf[i] = NCR_READ_REG(sc, NCR_FIFO); + + if (sc->sc_espstat2 & NCRFAS_STAT2_ISHUTTLE) { + + NCR_WRITE_REG(sc, NCR_FIFO, 0); + buf[i++] = NCR_READ_REG(sc, NCR_FIFO); + + NCR_READ_REG(sc, NCR_FIFO); + + ncr53c9x_flushfifo(sc); + } + } else { + for (i = 0; i < n; i++) + buf[i] = NCR_READ_REG(sc, NCR_FIFO); + } + + sc->sc_imlen += i; + +#if 0 +#ifdef NCR53C9X_DEBUG + { + int j; + + NCR_TRACE(("\n[rdfifo %s (%d):", + (how == NCR_RDFIFO_START) ? "start" : "cont", + (int)sc->sc_imlen)); + if (ncr53c9x_debug & NCR_SHOWTRAC) { + for (j = 0; j < sc->sc_imlen; j++) + printf(" %02x", sc->sc_imess[j]); + printf("]\n"); + } + } +#endif +#endif + return sc->sc_imlen; +} + +static void +ncr53c9x_wrfifo(struct ncr53c9x_softc *sc, u_char *p, int len) +{ + int i; + +#ifdef NCR53C9X_DEBUG + NCR_MSGS(("[wrfifo(%d):", len)); + if (ncr53c9x_debug & NCR_SHOWMSGS) { + for (i = 0; i < len; i++) + printf(" %02x", p[i]); + printf("]\n"); + } +#endif + + for (i = 0; i < len; i++) { + NCR_WRITE_REG(sc, NCR_FIFO, p[i]); + + if (sc->sc_rev == NCR_VARIANT_FAS366) + NCR_WRITE_REG(sc, NCR_FIFO, 0); + } +} + +static int +ncr53c9x_reselect(struct ncr53c9x_softc *sc, int message, int tagtype, + int tagid) +{ + u_char selid, target, lun; + struct ncr53c9x_ecb *ecb = NULL; + struct ncr53c9x_tinfo *ti; + struct ncr53c9x_linfo *li; + + + if (sc->sc_rev == NCR_VARIANT_FAS366) { + target = sc->sc_selid; + } else { + /* + * The SCSI chip made a snapshot of the data bus + * while the reselection was being negotiated. + * This enables us to determine which target did + * the reselect. + */ + selid = sc->sc_selid & ~(1 << sc->sc_id); + if (selid & (selid - 1)) { + device_printf(sc->sc_dev, "reselect with invalid " + "selid %02x; sending DEVICE RESET\n", selid); + goto reset; + } + + target = ffs(selid) - 1; + } + lun = message & 0x07; + + /* + * Search wait queue for disconnected cmd + * The list should be short, so I haven't bothered with + * any more sophisticated structures than a simple + * singly linked list. + */ + ti = &sc->sc_tinfo[target]; + li = TINFO_LUN(ti, lun); + + /* + * We can get as far as the LUN with the IDENTIFY + * message. Check to see if we're running an + * un-tagged command. Otherwise ack the IDENTIFY + * and wait for a tag message. + */ + if (li != NULL) { + if (li->untagged != NULL && li->busy) + ecb = li->untagged; + else if (tagtype != MSG_SIMPLE_Q_TAG) { + /* Wait for tag to come by */ + sc->sc_state = NCR_IDENTIFIED; + return (0); + } else if (tagtype) + ecb = li->queued[tagid]; + } + if (ecb == NULL) { + device_printf(sc->sc_dev, "reselect from target %d lun %d " + "tag %x:%x with no nexus; sending ABORT\n", + target, lun, tagtype, tagid); + goto abort; + } + + /* Make this nexus active again. */ + sc->sc_state = NCR_CONNECTED; + sc->sc_nexus = ecb; + ncr53c9x_setsync(sc, ti); + + if (ecb->flags & ECB_RESET) + ncr53c9x_sched_msgout(SEND_DEV_RESET); + else if (ecb->flags & ECB_ABORT) + ncr53c9x_sched_msgout(SEND_ABORT); + + /* Do an implicit RESTORE POINTERS. */ + sc->sc_dp = ecb->daddr; + sc->sc_dleft = ecb->dleft; + + return (0); + +reset: + ncr53c9x_sched_msgout(SEND_DEV_RESET); + return (1); + +abort: + ncr53c9x_sched_msgout(SEND_ABORT); + return (1); +} + +/* From NetBSD. These should go into CAM at some point */ +#define MSG_ISEXTENDED(m) ((m) == MSG_EXTENDED) +#define MSG_IS1BYTE(m) \ + ((!MSG_ISEXTENDED(m) && (m) < 0x20) || MSG_ISIDENTIFY(m)) +#define MSG_IS2BYTE(m) (((m) & 0xf0) == 0x20) + +static inline int +__verify_msg_format(u_char *p, int len) +{ + + if (len == 1 && MSG_IS1BYTE(p[0])) + return 1; + if (len == 2 && MSG_IS2BYTE(p[0])) + return 1; + if (len >= 3 && MSG_ISEXTENDED(p[0]) && + len == p[1] + 2) + return 1; + + return 0; +} + +/* + * Get an incoming message as initiator. + * + * The SCSI bus must already be in MESSAGE_IN_PHASE and there is a + * byte in the FIFO + */ +static void +ncr53c9x_msgin(struct ncr53c9x_softc *sc) +{ + + NCR_TRACE(("[ncr53c9x_msgin(curmsglen:%ld)] ", (long)sc->sc_imlen)); + + if (sc->sc_imlen == 0) { + device_printf(sc->sc_dev, "msgin: no msg byte available\n"); + return; + } + + /* + * Prepare for a new message. A message should (according + * to the SCSI standard) be transmitted in one single + * MESSAGE_IN_PHASE. If we have been in some other phase, + * then this is a new message. + */ + if (sc->sc_prevphase != MESSAGE_IN_PHASE && + sc->sc_state != NCR_RESELECTED) { + device_printf(sc->sc_dev, "phase change, dropping message, " + "prev %d, state %d\n", sc->sc_prevphase, sc->sc_state); + sc->sc_flags &= ~NCR_DROP_MSGI; + sc->sc_imlen = 0; + } + + /* + * If we're going to reject the message, don't bother storing + * the incoming bytes. But still, we need to ACK them. + */ + if ((sc->sc_flags & NCR_DROP_MSGI) != 0) { + NCRCMD(sc, NCRCMD_MSGOK); + printf("<dropping msg byte %x>", sc->sc_imess[sc->sc_imlen]); + return; + } + + if (sc->sc_imlen >= NCR_MAX_MSG_LEN) { + ncr53c9x_sched_msgout(SEND_REJECT); + sc->sc_flags |= NCR_DROP_MSGI; + } else { + u_char *pb; + int plen; + + switch (sc->sc_state) { + /* + * if received message is the first of reselection + * then first byte is selid, and then message + */ + case NCR_RESELECTED: + pb = sc->sc_imess + 1; + plen = sc->sc_imlen - 1; + break; + default: + pb = sc->sc_imess; + plen = sc->sc_imlen; + break; + } + + if (__verify_msg_format(pb, plen)) + goto gotit; + } + + /* Ack what we have so far */ + NCRCMD(sc, NCRCMD_MSGOK); + return; + +gotit: + NCR_MSGS(("gotmsg(%x) state %d", sc->sc_imess[0], sc->sc_state)); + /* we got complete message, flush the imess, */ + /* XXX nobody uses imlen below */ + sc->sc_imlen = 0; + /* + * Now we should have a complete message (1 byte, 2 byte + * and moderately long extended messages). We only handle + * extended messages which total length is shorter than + * NCR_MAX_MSG_LEN. Longer messages will be amputated. + */ + switch (sc->sc_state) { + struct ncr53c9x_ecb *ecb; + struct ncr53c9x_tinfo *ti; + struct ncr53c9x_linfo *li; + int lun; + + case NCR_CONNECTED: + ecb = sc->sc_nexus; + ti = &sc->sc_tinfo[ecb->ccb->ccb_h.target_id]; + + switch (sc->sc_imess[0]) { + case MSG_CMDCOMPLETE: + NCR_MSGS(("cmdcomplete ")); + if (sc->sc_dleft < 0) { + xpt_print_path(ecb->ccb->ccb_h.path); + printf("got %ld extra bytes\n", + -(long)sc->sc_dleft); + sc->sc_dleft = 0; + } + ecb->dleft = (ecb->flags & ECB_TENTATIVE_DONE) ? + 0 : sc->sc_dleft; + if ((ecb->flags & ECB_SENSE) == 0) + ecb->ccb->csio.resid = ecb->dleft; + sc->sc_state = NCR_CMDCOMPLETE; + break; + + case MSG_MESSAGE_REJECT: + NCR_MSGS(("msg reject (msgout=%x) ", sc->sc_msgout)); + switch (sc->sc_msgout) { + case SEND_TAG: + /* + * Target does not like tagged queuing. + * - Flush the command queue + * - Disable tagged queuing for the target + * - Dequeue ecb from the queued array. + */ + device_printf(sc->sc_dev, "tagged queuing " + "rejected: target %d\n", + ecb->ccb->ccb_h.target_id); + + NCR_MSGS(("(rejected sent tag)")); + NCRCMD(sc, NCRCMD_FLUSH); + DELAY(1); + ti->flags &= ~T_TAG; + lun = ecb->ccb->ccb_h.target_lun; + li = TINFO_LUN(ti, lun); + if (ecb->tag[0] && + li->queued[ecb->tag[1]] != NULL) { + li->queued[ecb->tag[1]] = NULL; + li->used--; + } + ecb->tag[0] = ecb->tag[1] = 0; + li->untagged = ecb; + li->busy = 1; + break; + + case SEND_SDTR: + device_printf(sc->sc_dev, "sync transfer " + "rejected: target %d\n", + ecb->ccb->ccb_h.target_id); + + sc->sc_flags &= ~NCR_SYNCHNEGO; + ti->flags &= ~(T_NEGOTIATE | T_SYNCMODE); + ncr53c9x_setsync(sc, ti); + break; + + case SEND_WDTR: + device_printf(sc->sc_dev, "wide transfer " + "rejected: target %d\n", + ecb->ccb->ccb_h.target_id); + ti->flags &= ~(T_WIDE | T_WDTRSENT); + ti->width = 0; + break; + + case SEND_INIT_DET_ERR: + goto abort; + } + break; + + case MSG_NOOP: + NCR_MSGS(("noop ")); + break; + + case MSG_HEAD_OF_Q_TAG: + case MSG_SIMPLE_Q_TAG: + case MSG_ORDERED_Q_TAG: + NCR_MSGS(("TAG %x:%x", + sc->sc_imess[0], sc->sc_imess[1])); + break; + + case MSG_DISCONNECT: + NCR_MSGS(("disconnect ")); + ti->dconns++; + sc->sc_state = NCR_DISCONNECT; + + /* + * Mark the fact that all bytes have moved. The + * target may not bother to do a SAVE POINTERS + * at this stage. This flag will set the residual + * count to zero on MSG COMPLETE. + */ + if (sc->sc_dleft == 0) + ecb->flags |= ECB_TENTATIVE_DONE; + + break; + + case MSG_SAVEDATAPOINTER: + NCR_MSGS(("save datapointer ")); + ecb->daddr = sc->sc_dp; + ecb->dleft = sc->sc_dleft; + break; + + case MSG_RESTOREPOINTERS: + NCR_MSGS(("restore datapointer ")); + sc->sc_dp = ecb->daddr; + sc->sc_dleft = ecb->dleft; + break; + + case MSG_EXTENDED: + NCR_MSGS(("extended(%x) ", sc->sc_imess[2])); + switch (sc->sc_imess[2]) { + case MSG_EXT_SDTR: + NCR_MSGS(("SDTR period %d, offset %d ", + sc->sc_imess[3], sc->sc_imess[4])); + if (sc->sc_imess[1] != 3) + goto reject; + ti->period = sc->sc_imess[3]; + ti->offset = sc->sc_imess[4]; + ti->flags &= ~T_NEGOTIATE; + if (sc->sc_minsync == 0 || + ti->offset == 0 || + ti->period > 124) { +#if 0 +#ifdef NCR53C9X_DEBUG + xpt_print_path(ecb->ccb->ccb_h.path); + printf("async mode\n"); +#endif +#endif + ti->flags &= ~T_SYNCMODE; + if ((sc->sc_flags&NCR_SYNCHNEGO) == 0) { + /* + * target initiated negotiation + */ + ti->offset = 0; + ncr53c9x_sched_msgout( + SEND_SDTR); + } + } else { + int p; + + p = ncr53c9x_stp2cpb(sc, ti->period); + ti->period = ncr53c9x_cpb2stp(sc, p); + if ((sc->sc_flags&NCR_SYNCHNEGO) == 0) { + /* + * target initiated negotiation + */ + if (ti->period < + sc->sc_minsync) + ti->period = + sc->sc_minsync; + if (ti->offset > 15) + ti->offset = 15; + ti->flags &= ~T_SYNCMODE; + ncr53c9x_sched_msgout( + SEND_SDTR); + } else { + /* we are sync */ + ti->flags |= T_SYNCMODE; + } + } + sc->sc_flags &= ~NCR_SYNCHNEGO; + ncr53c9x_setsync(sc, ti); + break; + + case MSG_EXT_WDTR: +#ifdef NCR53C9X_DEBUG + device_printf(sc->sc_dev, "wide mode %d\n", + sc->sc_imess[3]); +#endif + if (sc->sc_imess[3] == 1) { + ti->cfg3 |= NCRFASCFG3_EWIDE; + ncr53c9x_setsync(sc, ti); + } else + ti->width = 0; + /* + * Device started width negotiation. + */ + if (!(ti->flags & T_WDTRSENT)) + ncr53c9x_sched_msgout(SEND_WDTR); + ti->flags &= ~(T_WIDE | T_WDTRSENT); + break; + default: + xpt_print_path(ecb->ccb->ccb_h.path); + printf("unrecognized MESSAGE EXTENDED;" + " sending REJECT\n"); + goto reject; + } + break; + + default: + NCR_MSGS(("ident ")); + xpt_print_path(ecb->ccb->ccb_h.path); + printf("unrecognized MESSAGE; sending REJECT\n"); + reject: + ncr53c9x_sched_msgout(SEND_REJECT); + break; + } + break; + + case NCR_IDENTIFIED: + /* + * IDENTIFY message was received and queue tag is expected now + */ + if ((sc->sc_imess[0] != MSG_SIMPLE_Q_TAG) || + (sc->sc_msgify == 0)) { + device_printf(sc->sc_dev, "TAG reselect without " + "IDENTIFY; MSG %x; sending DEVICE RESET\n", + sc->sc_imess[0]); + goto reset; + } + (void) ncr53c9x_reselect(sc, sc->sc_msgify, + sc->sc_imess[0], sc->sc_imess[1]); + break; + + case NCR_RESELECTED: + if (MSG_ISIDENTIFY(sc->sc_imess[1])) { + sc->sc_msgify = sc->sc_imess[1]; + } else { + device_printf(sc->sc_dev, "reselect without IDENTIFY;" + " MSG %x; sending DEVICE RESET\n", sc->sc_imess[1]); + goto reset; + } + (void) ncr53c9x_reselect(sc, sc->sc_msgify, 0, 0); + break; + + default: + device_printf(sc->sc_dev, "unexpected MESSAGE IN; " + "sending DEVICE RESET\n"); + reset: + ncr53c9x_sched_msgout(SEND_DEV_RESET); + break; + + abort: + ncr53c9x_sched_msgout(SEND_ABORT); + break; + } + + /* if we have more messages to send set ATN */ + if (sc->sc_msgpriq) + NCRCMD(sc, NCRCMD_SETATN); + + /* Ack last message byte */ + NCRCMD(sc, NCRCMD_MSGOK); + + /* Done, reset message pointer. */ + sc->sc_flags &= ~NCR_DROP_MSGI; + sc->sc_imlen = 0; +} + + +/* + * Send the highest priority, scheduled message + */ +static void +ncr53c9x_msgout(struct ncr53c9x_softc *sc) +{ + struct ncr53c9x_tinfo *ti; + struct ncr53c9x_ecb *ecb; + size_t size; + + NCR_TRACE(("[ncr53c9x_msgout(priq:%x, prevphase:%x)]", + sc->sc_msgpriq, sc->sc_prevphase)); + + /* + * XXX - the NCR_ATN flag is not in sync with the actual ATN + * condition on the SCSI bus. The 53c9x chip + * automatically turns off ATN before sending the + * message byte. (see also the comment below in the + * default case when picking out a message to send) + */ + if (sc->sc_flags & NCR_ATN) { + if (sc->sc_prevphase != MESSAGE_OUT_PHASE) { + new: + NCRCMD(sc, NCRCMD_FLUSH); +/* DELAY(1); */ + sc->sc_msgoutq = 0; + sc->sc_omlen = 0; + } + } else { + if (sc->sc_prevphase == MESSAGE_OUT_PHASE) { + ncr53c9x_sched_msgout(sc->sc_msgoutq); + goto new; + } else { + device_printf(sc->sc_dev, "at line %d: unexpected " + "MESSAGE OUT phase\n", __LINE__); + } + } + + if (sc->sc_omlen == 0) { + /* Pick up highest priority message */ + sc->sc_msgout = sc->sc_msgpriq & -sc->sc_msgpriq; + sc->sc_msgoutq |= sc->sc_msgout; + sc->sc_msgpriq &= ~sc->sc_msgout; + sc->sc_omlen = 1; /* "Default" message len */ + switch (sc->sc_msgout) { + case SEND_SDTR: + ecb = sc->sc_nexus; + ti = &sc->sc_tinfo[ecb->ccb->ccb_h.target_id]; + sc->sc_omess[0] = MSG_EXTENDED; + sc->sc_omess[1] = MSG_EXT_SDTR_LEN; + sc->sc_omess[2] = MSG_EXT_SDTR; + sc->sc_omess[3] = ti->period; + sc->sc_omess[4] = ti->offset; + sc->sc_omlen = 5; + if ((sc->sc_flags & NCR_SYNCHNEGO) == 0) { + ti->flags |= T_SYNCMODE; + ncr53c9x_setsync(sc, ti); + } + break; + case SEND_WDTR: + ecb = sc->sc_nexus; + ti = &sc->sc_tinfo[ecb->ccb->ccb_h.target_id]; + sc->sc_omess[0] = MSG_EXTENDED; + sc->sc_omess[1] = MSG_EXT_WDTR_LEN; + sc->sc_omess[2] = MSG_EXT_WDTR; + sc->sc_omess[3] = ti->width; + sc->sc_omlen = 4; + break; + case SEND_IDENTIFY: + if (sc->sc_state != NCR_CONNECTED) { + device_printf(sc->sc_dev, "at line %d: no " + "nexus\n", __LINE__); + } + ecb = sc->sc_nexus; + sc->sc_omess[0] = + MSG_IDENTIFY(ecb->ccb->ccb_h.target_lun, 0); + break; + case SEND_TAG: + if (sc->sc_state != NCR_CONNECTED) { + device_printf(sc->sc_dev, "at line %d: no " + "nexus\n", __LINE__); + } + ecb = sc->sc_nexus; + sc->sc_omess[0] = ecb->tag[0]; + sc->sc_omess[1] = ecb->tag[1]; + sc->sc_omlen = 2; + break; + case SEND_DEV_RESET: + sc->sc_flags |= NCR_ABORTING; + sc->sc_omess[0] = MSG_BUS_DEV_RESET; + ecb = sc->sc_nexus; + ti = &sc->sc_tinfo[ecb->ccb->ccb_h.target_id]; + ti->flags &= ~T_SYNCMODE; + if ((ti->flags & T_SYNCHOFF) == 0) + /* We can re-start sync negotiation */ + ti->flags |= T_NEGOTIATE; + break; + case SEND_PARITY_ERROR: + sc->sc_omess[0] = MSG_PARITY_ERROR; + break; + case SEND_ABORT: + sc->sc_flags |= NCR_ABORTING; + sc->sc_omess[0] = MSG_ABORT; + break; + case SEND_INIT_DET_ERR: + sc->sc_omess[0] = MSG_INITIATOR_DET_ERR; + break; + case SEND_REJECT: + sc->sc_omess[0] = MSG_MESSAGE_REJECT; + break; + default: + /* + * We normally do not get here, since the chip + * automatically turns off ATN before the last + * byte of a message is sent to the target. + * However, if the target rejects our (multi-byte) + * message early by switching to MSG IN phase + * ATN remains on, so the target may return to + * MSG OUT phase. If there are no scheduled messages + * left we send a NO-OP. + * + * XXX - Note that this leaves no useful purpose for + * the NCR_ATN flag. + */ + sc->sc_flags &= ~NCR_ATN; + sc->sc_omess[0] = MSG_NOOP; + break; + } + sc->sc_omp = sc->sc_omess; + } + +#ifdef DEBUG + if (ncr53c9x_debug & NCR_SHOWMSGS) { + int i; + + NCR_MSGS(("<msgout:")); + for (i = 0; i < sc->sc_omlen; i++) + NCR_MSGS((" %02x", sc->sc_omess[i])); + NCR_MSGS(("> ")); + } +#endif + if (sc->sc_rev == NCR_VARIANT_FAS366) { + /* + * XXX fifo size + */ + ncr53c9x_flushfifo(sc); + ncr53c9x_wrfifo(sc, sc->sc_omp, sc->sc_omlen); + NCRCMD(sc, NCRCMD_TRANS); + } else { + /* (re)send the message */ + size = min(sc->sc_omlen, sc->sc_maxxfer); + NCRDMA_SETUP(sc, &sc->sc_omp, &sc->sc_omlen, 0, &size); + /* Program the SCSI counter */ + NCR_SET_COUNT(sc, size); + + /* Load the count in and start the message-out transfer */ + NCRCMD(sc, NCRCMD_NOP|NCRCMD_DMA); + NCRCMD(sc, NCRCMD_TRANS|NCRCMD_DMA); + NCRDMA_GO(sc); + } +} + +/* + * This is the most critical part of the driver, and has to know + * how to deal with *all* error conditions and phases from the SCSI + * bus. If there are no errors and the DMA was active, then call the + * DMA pseudo-interrupt handler. If this returns 1, then that was it + * and we can return from here without further processing. + * + * Most of this needs verifying. + */ +void +ncr53c9x_intr(void *arg) +{ + struct ncr53c9x_softc *sc = arg; + struct ncr53c9x_ecb *ecb; + struct ncr53c9x_tinfo *ti; + size_t size; + int nfifo; + + NCR_INTS(("[ncr53c9x_intr: state %d]", sc->sc_state)); + + if (!NCRDMA_ISINTR(sc)) + return; + + mtx_lock(&sc->sc_lock); +again: + /* and what do the registers say... */ + ncr53c9x_readregs(sc); + + /* + * At the moment, only a SCSI Bus Reset or Illegal + * Command are classed as errors. A disconnect is a + * valid condition, and we let the code check is the + * "NCR_BUSFREE_OK" flag was set before declaring it + * and error. + * + * Also, the status register tells us about "Gross + * Errors" and "Parity errors". Only the Gross Error + * is really bad, and the parity errors are dealt + * with later + * + * TODO + * If there are too many parity error, go to slow + * cable mode ? + */ + + /* SCSI Reset */ + if ((sc->sc_espintr & NCRINTR_SBR) != 0) { + if ((NCR_READ_REG(sc, NCR_FFLAG) & NCRFIFO_FF) != 0) { + NCRCMD(sc, NCRCMD_FLUSH); + DELAY(1); + } + if (sc->sc_state != NCR_SBR) { + device_printf(sc->sc_dev, "SCSI bus reset\n"); + ncr53c9x_init(sc, 0); /* Restart everything */ + goto out; + } +#if 0 +/*XXX*/ printf("<expected bus reset: " + "[intr %x, stat %x, step %d]>\n", + sc->sc_espintr, sc->sc_espstat, sc->sc_espstep); +#endif + if (sc->sc_nexus != NULL) + panic("%s: nexus in reset state", + device_get_nameunit(sc->sc_dev)); + goto sched; + } + + ecb = sc->sc_nexus; + +#define NCRINTR_ERR (NCRINTR_SBR|NCRINTR_ILL) + if (sc->sc_espintr & NCRINTR_ERR || + sc->sc_espstat & NCRSTAT_GE) { + + if ((sc->sc_espstat & NCRSTAT_GE) != 0) { + /* Gross Error; no target ? */ + if (NCR_READ_REG(sc, NCR_FFLAG) & NCRFIFO_FF) { + NCRCMD(sc, NCRCMD_FLUSH); + DELAY(1); + } + if (sc->sc_state == NCR_CONNECTED || + sc->sc_state == NCR_SELECTING) { + ecb->ccb->ccb_h.status = CAM_SEL_TIMEOUT; + ncr53c9x_done(sc, ecb); + } + goto out; + } + + if ((sc->sc_espintr & NCRINTR_ILL) != 0) { + if ((sc->sc_flags & NCR_EXPECT_ILLCMD) != 0) { + /* + * Eat away "Illegal command" interrupt + * on a ESP100 caused by a re-selection + * while we were trying to select + * another target. + */ +#ifdef DEBUG + device_printf(sc->sc_dev, "ESP100 work-around " + "activated\n"); +#endif + sc->sc_flags &= ~NCR_EXPECT_ILLCMD; + goto out; + } + /* illegal command, out of sync ? */ + device_printf(sc->sc_dev, "illegal command: 0x%x " + "(state %d, phase %x, prevphase %x)\n", + sc->sc_lastcmd, + sc->sc_state, sc->sc_phase, sc->sc_prevphase); + if (NCR_READ_REG(sc, NCR_FFLAG) & NCRFIFO_FF) { + NCRCMD(sc, NCRCMD_FLUSH); + DELAY(1); + } + ncr53c9x_init(sc, 1); /* Restart everything */ + goto out; + } + } + sc->sc_flags &= ~NCR_EXPECT_ILLCMD; + + /* + * Call if DMA is active. + * + * If DMA_INTR returns true, then maybe go 'round the loop + * again in case there is no more DMA queued, but a phase + * change is expected. + */ + if (NCRDMA_ISACTIVE(sc)) { + int r = NCRDMA_INTR(sc); + if (r == -1) { + device_printf(sc->sc_dev, "DMA error; resetting\n"); + ncr53c9x_init(sc, 1); + goto out; + } + /* If DMA active here, then go back to work... */ + if (NCRDMA_ISACTIVE(sc)) + goto out; + + if ((sc->sc_espstat & NCRSTAT_TC) == 0) { + /* + * DMA not completed. If we can not find a + * acceptable explanation, print a diagnostic. + */ + if (sc->sc_state == NCR_SELECTING) + /* + * This can happen if we are reselected + * while using DMA to select a target. + */ + /*void*/; + else if (sc->sc_prevphase == MESSAGE_OUT_PHASE) { + /* + * Our (multi-byte) message (eg SDTR) was + * interrupted by the target to send + * a MSG REJECT. + * Print diagnostic if current phase + * is not MESSAGE IN. + */ + if (sc->sc_phase != MESSAGE_IN_PHASE) + device_printf(sc->sc_dev,"!TC on MSGOUT" + " [intr %x, stat %x, step %d]" + " prevphase %x, resid %lx\n", + sc->sc_espintr, + sc->sc_espstat, + sc->sc_espstep, + sc->sc_prevphase, + (u_long)sc->sc_omlen); + } else if (sc->sc_dleft == 0) { + /* + * The DMA operation was started for + * a DATA transfer. Print a diagnostic + * if the DMA counter and TC bit + * appear to be out of sync. + */ + device_printf(sc->sc_dev, "!TC on DATA XFER" + " [intr %x, stat %x, step %d]" + " prevphase %x, resid %x\n", + sc->sc_espintr, + sc->sc_espstat, + sc->sc_espstep, + sc->sc_prevphase, + ecb ? ecb->dleft : -1); + } + } + } + + /* + * Check for less serious errors. + */ + if ((sc->sc_espstat & NCRSTAT_PE) != 0) { + device_printf(sc->sc_dev, "SCSI bus parity error\n"); + if (sc->sc_prevphase == MESSAGE_IN_PHASE) + ncr53c9x_sched_msgout(SEND_PARITY_ERROR); + else + ncr53c9x_sched_msgout(SEND_INIT_DET_ERR); + } + + if ((sc->sc_espintr & NCRINTR_DIS) != 0) { + sc->sc_msgify = 0; + NCR_INTS(("<DISC [intr %x, stat %x, step %d]>", + sc->sc_espintr,sc->sc_espstat,sc->sc_espstep)); + if (NCR_READ_REG(sc, NCR_FFLAG) & NCRFIFO_FF) { + NCRCMD(sc, NCRCMD_FLUSH); +/* DELAY(1); */ + } + /* + * This command must (apparently) be issued within + * 250mS of a disconnect. So here you are... + */ + NCRCMD(sc, NCRCMD_ENSEL); + + switch (sc->sc_state) { + case NCR_RESELECTED: + goto sched; + + case NCR_SELECTING: + { + struct ncr53c9x_linfo *li; + + ecb->ccb->ccb_h.status = CAM_SEL_TIMEOUT; + + /* Selection timeout -- discard all LUNs if empty */ + ti = &sc->sc_tinfo[ecb->ccb->ccb_h.target_id]; + li = LIST_FIRST(&ti->luns); + while (li != NULL) { + if (li->untagged == NULL && li->used == 0) { + if (li->lun < NCR_NLUN) + ti->lun[li->lun] = NULL; + LIST_REMOVE(li, link); + free(li, M_DEVBUF); + /* + * Restart the search at the beginning + */ + li = LIST_FIRST(&ti->luns); + continue; + } + li = LIST_NEXT(li, link); + } + goto finish; + } + case NCR_CONNECTED: + if ((sc->sc_flags & NCR_SYNCHNEGO) != 0) { +#ifdef NCR53C9X_DEBUG + if (ecb != NULL) + xpt_print_path(ecb->ccb->ccb_h.path); + printf("sync nego not completed!\n"); +#endif + ti = &sc->sc_tinfo[ecb->ccb->ccb_h.target_id]; + sc->sc_flags &= ~NCR_SYNCHNEGO; + ti->flags &= ~(T_NEGOTIATE | T_SYNCMODE); + } + + /* it may be OK to disconnect */ + if ((sc->sc_flags & NCR_ABORTING) == 0) { + /* + * Section 5.1.1 of the SCSI 2 spec + * suggests issuing a REQUEST SENSE + * following an unexpected disconnect. + * Some devices go into a contingent + * allegiance condition when + * disconnecting, and this is necessary + * to clean up their state. + */ + device_printf(sc->sc_dev, "unexpected " + "disconnect [state %d, intr %x, stat %x, " + "phase(c %x, p %x)]; ", sc->sc_state, + sc->sc_espintr, sc->sc_espstat, + sc->sc_phase, sc->sc_prevphase); + + if ((ecb->flags & ECB_SENSE) != 0) { + printf("resetting\n"); + goto reset; + } + printf("sending REQUEST SENSE\n"); + untimeout(ncr53c9x_timeout, sc, + ecb->ccb->ccb_h.timeout_ch); + ncr53c9x_sense(sc, ecb); + goto out; + } + + ecb->ccb->ccb_h.status = CAM_CMD_TIMEOUT; + goto finish; + + case NCR_DISCONNECT: + sc->sc_nexus = NULL; + goto sched; + + case NCR_CMDCOMPLETE: + ecb->ccb->ccb_h.status = CAM_REQ_CMP; + goto finish; + } + } + + switch (sc->sc_state) { + + case NCR_SBR: + device_printf(sc->sc_dev, "waiting for Bus Reset to happen\n"); + goto out; + + case NCR_RESELECTED: + /* + * we must be continuing a message ? + */ + device_printf(sc->sc_dev, "unhandled reselect continuation, " + "state %d, intr %02x\n", sc->sc_state, sc->sc_espintr); + ncr53c9x_init(sc, 1); + goto out; + break; + + case NCR_IDENTIFIED: + ecb = sc->sc_nexus; + if (sc->sc_phase != MESSAGE_IN_PHASE) { + int i = (NCR_READ_REG(sc, NCR_FFLAG) & NCRFIFO_FF); + /* + * Things are seriously fucked up. + * Pull the brakes, i.e. reset + */ + device_printf(sc->sc_dev, "target didn't send tag: %d " + "bytes in fifo\n", i); + /* Drain and display fifo */ + while (i-- > 0) + printf("[%d] ", NCR_READ_REG(sc, NCR_FIFO)); + + ncr53c9x_init(sc, 1); + goto out; + } else + goto msgin; + + break; + + case NCR_IDLE: + case NCR_SELECTING: + ecb = sc->sc_nexus; + if (sc->sc_espintr & NCRINTR_RESEL) { + sc->sc_msgpriq = sc->sc_msgout = sc->sc_msgoutq = 0; + sc->sc_flags = 0; + /* + * If we're trying to select a + * target ourselves, push our command + * back into the ready list. + */ + if (sc->sc_state == NCR_SELECTING) { + NCR_INTS(("backoff selector ")); + untimeout(ncr53c9x_timeout, sc, + ecb->ccb->ccb_h.timeout_ch); + ncr53c9x_dequeue(sc, ecb); + TAILQ_INSERT_HEAD(&sc->ready_list, ecb, chain); + ecb->flags |= ECB_READY; + ecb = sc->sc_nexus = NULL; + } + sc->sc_state = NCR_RESELECTED; + if (sc->sc_phase != MESSAGE_IN_PHASE) { + /* + * Things are seriously fucked up. + * Pull the brakes, i.e. reset + */ + device_printf(sc->sc_dev, "target didn't " + "identify\n"); + ncr53c9x_init(sc, 1); + goto out; + } + /* + * The C90 only inhibits FIFO writes until reselection + * is complete, instead of waiting until the interrupt + * status register has been read. So, if the reselect + * happens while we were entering command bytes (for + * another target) some of those bytes can appear in + * the FIFO here, after the interrupt is taken. + * + * To remedy this situation, pull the Selection ID + * and Identify message from the FIFO directly, and + * ignore any extraneous fifo contents. Also, set + * a flag that allows one Illegal Command Interrupt + * to occur which the chip also generates as a result + * of writing to the FIFO during a reselect. + */ + if (sc->sc_rev == NCR_VARIANT_ESP100) { + nfifo = NCR_READ_REG(sc, NCR_FFLAG) & NCRFIFO_FF; + sc->sc_imess[0] = NCR_READ_REG(sc, NCR_FIFO); + sc->sc_imess[1] = NCR_READ_REG(sc, NCR_FIFO); + sc->sc_imlen = 2; + if (nfifo != 2) { + /* Flush the rest */ + NCRCMD(sc, NCRCMD_FLUSH); + } + sc->sc_flags |= NCR_EXPECT_ILLCMD; + if (nfifo > 2) + nfifo = 2; /* We fixed it.. */ + } else + nfifo = ncr53c9x_rdfifo(sc, NCR_RDFIFO_START); + + if (nfifo != 2) { + device_printf(sc->sc_dev, "RESELECT: %d bytes " + "in FIFO! [intr %x, stat %x, step %d, " + "prevphase %x]\n", + nfifo, + sc->sc_espintr, + sc->sc_espstat, + sc->sc_espstep, + sc->sc_prevphase); + ncr53c9x_init(sc, 1); + goto out; + } + sc->sc_selid = sc->sc_imess[0]; + NCR_INTS(("selid=%02x ", sc->sc_selid)); + + /* Handle identify message */ + ncr53c9x_msgin(sc); + + if (sc->sc_state != NCR_CONNECTED && + sc->sc_state != NCR_IDENTIFIED) { + /* IDENTIFY fail?! */ + device_printf(sc->sc_dev, "identify failed, " + "state %d, intr %02x\n", sc->sc_state, + sc->sc_espintr); + ncr53c9x_init(sc, 1); + goto out; + } + goto shortcut; /* ie. next phase expected soon */ + } + +#define NCRINTR_DONE (NCRINTR_FC|NCRINTR_BS) + if ((sc->sc_espintr & NCRINTR_DONE) == NCRINTR_DONE) { + /* + * Arbitration won; examine the `step' register + * to determine how far the selection could progress. + */ + ecb = sc->sc_nexus; + if (ecb == NULL) + panic("ncr53c9x: no nexus"); + + ti = &sc->sc_tinfo[ecb->ccb->ccb_h.target_id]; + + switch (sc->sc_espstep) { + case 0: + /* + * The target did not respond with a + * message out phase - probably an old + * device that doesn't recognize ATN. + * Clear ATN and just continue, the + * target should be in the command + * phase. + * XXXX check for command phase? + */ + NCRCMD(sc, NCRCMD_RSTATN); + break; + case 1: + if ((ti->flags & T_NEGOTIATE) == 0 && + ecb->tag[0] == 0) { + device_printf(sc->sc_dev, "step 1 & " + "!NEG\n"); + goto reset; + } + if (sc->sc_phase != MESSAGE_OUT_PHASE) { + device_printf(sc->sc_dev, "!MSGOUT\n"); + goto reset; + } + if (ti->flags & T_WIDE) { + ti->flags |= T_WDTRSENT; + ncr53c9x_sched_msgout(SEND_WDTR); + } + if (ti->flags & T_NEGOTIATE) { + /* Start negotiating */ + sc->sc_flags |= NCR_SYNCHNEGO; + if (ecb->tag[0]) + ncr53c9x_sched_msgout( + SEND_TAG|SEND_SDTR); + else + ncr53c9x_sched_msgout( + SEND_SDTR); + } else { + /* Could not do ATN3 so send TAG */ + ncr53c9x_sched_msgout(SEND_TAG); + } + sc->sc_prevphase = MESSAGE_OUT_PHASE; /* XXXX */ + break; + case 3: + /* + * Grr, this is supposed to mean + * "target left command phase prematurely". + * It seems to happen regularly when + * sync mode is on. + * Look at FIFO to see if command went out. + * (Timing problems?) + */ + if (sc->sc_features & NCR_F_DMASELECT) { + if (sc->sc_cmdlen == 0) + /* Hope for the best.. */ + break; + } else if ((NCR_READ_REG(sc, NCR_FFLAG) + & NCRFIFO_FF) == 0) { + /* Hope for the best.. */ + break; + } + printf("(%s:%d:%d): selection failed;" + " %d left in FIFO " + "[intr %x, stat %x, step %d]\n", + device_get_nameunit(sc->sc_dev), + ecb->ccb->ccb_h.target_id, + ecb->ccb->ccb_h.target_lun, + NCR_READ_REG(sc, NCR_FFLAG) + & NCRFIFO_FF, + sc->sc_espintr, sc->sc_espstat, + sc->sc_espstep); + NCRCMD(sc, NCRCMD_FLUSH); + ncr53c9x_sched_msgout(SEND_ABORT); + goto out; + case 2: + /* Select stuck at Command Phase */ + NCRCMD(sc, NCRCMD_FLUSH); + break; + case 4: + if (sc->sc_features & NCR_F_DMASELECT && + sc->sc_cmdlen != 0) + printf("(%s:%d:%d): select; " + "%lu left in DMA buffer " + "[intr %x, stat %x, step %d]\n", + device_get_nameunit(sc->sc_dev), + ecb->ccb->ccb_h.target_id, + ecb->ccb->ccb_h.target_lun, + (u_long)sc->sc_cmdlen, + sc->sc_espintr, + sc->sc_espstat, + sc->sc_espstep); + /* So far, everything went fine */ + break; + } + + sc->sc_prevphase = INVALID_PHASE; /* ?? */ + /* Do an implicit RESTORE POINTERS. */ + sc->sc_dp = ecb->daddr; + sc->sc_dleft = ecb->dleft; + sc->sc_state = NCR_CONNECTED; + break; + + } else { + + device_printf(sc->sc_dev, "unexpected status after " + "select: [intr %x, stat %x, step %x]\n", + sc->sc_espintr, sc->sc_espstat, sc->sc_espstep); + NCRCMD(sc, NCRCMD_FLUSH); + DELAY(1); + goto reset; + } + if (sc->sc_state == NCR_IDLE) { + device_printf(sc->sc_dev, "stray interrupt\n"); + mtx_unlock(&sc->sc_lock); + return; + } + break; + + case NCR_CONNECTED: + if ((sc->sc_flags & NCR_ICCS) != 0) { + /* "Initiate Command Complete Steps" in progress */ + u_char msg; + + sc->sc_flags &= ~NCR_ICCS; + + if (!(sc->sc_espintr & NCRINTR_DONE)) { + device_printf(sc->sc_dev, "ICCS: " + ": [intr %x, stat %x, step %x]\n", + sc->sc_espintr, sc->sc_espstat, + sc->sc_espstep); + } + ncr53c9x_rdfifo(sc, NCR_RDFIFO_START); + if (sc->sc_imlen < 2) + device_printf(sc->sc_dev, "can't get status, " + "only %d bytes\n", (int)sc->sc_imlen); + ecb->stat = sc->sc_imess[sc->sc_imlen - 2]; + msg = sc->sc_imess[sc->sc_imlen - 1]; + NCR_PHASE(("<stat:(%x,%x)>", ecb->stat, msg)); + if (msg == MSG_CMDCOMPLETE) { + ecb->dleft = (ecb->flags & ECB_TENTATIVE_DONE) + ? 0 : sc->sc_dleft; + if ((ecb->flags & ECB_SENSE) == 0) + ecb->ccb->csio.resid = ecb->dleft; + sc->sc_state = NCR_CMDCOMPLETE; + } else + device_printf(sc->sc_dev, "STATUS_PHASE: " + "msg %d\n", msg); + sc->sc_imlen = 0; + NCRCMD(sc, NCRCMD_MSGOK); + goto shortcut; /* ie. wait for disconnect */ + } + break; + + default: + device_printf(sc->sc_dev, "invalid state: %d [intr %x, " + "phase(c %x, p %x)]\n", sc->sc_state, + sc->sc_espintr, sc->sc_phase, sc->sc_prevphase); + goto reset; + } + + /* + * Driver is now in state NCR_CONNECTED, i.e. we + * have a current command working the SCSI bus. + */ + if (sc->sc_state != NCR_CONNECTED || ecb == NULL) { + panic("ncr53c9x: no nexus"); + } + + switch (sc->sc_phase) { + case MESSAGE_OUT_PHASE: + NCR_PHASE(("MESSAGE_OUT_PHASE ")); + ncr53c9x_msgout(sc); + sc->sc_prevphase = MESSAGE_OUT_PHASE; + break; + + case MESSAGE_IN_PHASE: +msgin: + NCR_PHASE(("MESSAGE_IN_PHASE ")); + if ((sc->sc_espintr & NCRINTR_BS) != 0) { + if ((sc->sc_rev != NCR_VARIANT_FAS366) || + !(sc->sc_espstat2 & NCRFAS_STAT2_EMPTY)) { + NCRCMD(sc, NCRCMD_FLUSH); + } + sc->sc_flags |= NCR_WAITI; + NCRCMD(sc, NCRCMD_TRANS); + } else if ((sc->sc_espintr & NCRINTR_FC) != 0) { + if ((sc->sc_flags & NCR_WAITI) == 0) { + device_printf(sc->sc_dev, "MSGIN: unexpected " + "FC bit: [intr %x, stat %x, step %x]\n", + sc->sc_espintr, sc->sc_espstat, + sc->sc_espstep); + } + sc->sc_flags &= ~NCR_WAITI; + ncr53c9x_rdfifo(sc, + (sc->sc_prevphase == sc->sc_phase) ? + NCR_RDFIFO_CONTINUE : NCR_RDFIFO_START); + ncr53c9x_msgin(sc); + } else { + device_printf(sc->sc_dev, "MSGIN: weird bits: " + "[intr %x, stat %x, step %x]\n", + sc->sc_espintr, sc->sc_espstat, sc->sc_espstep); + } + sc->sc_prevphase = MESSAGE_IN_PHASE; + goto shortcut; /* i.e. expect data to be ready */ + break; + + case COMMAND_PHASE: + /* + * Send the command block. Normally we don't see this + * phase because the SEL_ATN command takes care of + * all this. However, we end up here if either the + * target or we wanted to exchange some more messages + * first (e.g. to start negotiations). + */ + + NCR_PHASE(("COMMAND_PHASE 0x%02x (%d) ", + ecb->cmd.cmd.opcode, ecb->clen)); + if (NCR_READ_REG(sc, NCR_FFLAG) & NCRFIFO_FF) { + NCRCMD(sc, NCRCMD_FLUSH); +/* DELAY(1);*/ + } + if (sc->sc_features & NCR_F_DMASELECT) { + /* setup DMA transfer for command */ + size = ecb->clen; + sc->sc_cmdlen = size; + sc->sc_cmdp = (caddr_t)&ecb->cmd.cmd; + NCRDMA_SETUP(sc, &sc->sc_cmdp, &sc->sc_cmdlen, + 0, &size); + /* Program the SCSI counter */ + NCR_SET_COUNT(sc, size); + + /* load the count in */ + NCRCMD(sc, NCRCMD_NOP|NCRCMD_DMA); + + /* start the command transfer */ + NCRCMD(sc, NCRCMD_TRANS | NCRCMD_DMA); + NCRDMA_GO(sc); + } else { + ncr53c9x_wrfifo(sc, (u_char *)&ecb->cmd.cmd, ecb->clen); + NCRCMD(sc, NCRCMD_TRANS); + } + sc->sc_prevphase = COMMAND_PHASE; + break; + + case DATA_OUT_PHASE: + NCR_PHASE(("DATA_OUT_PHASE [%ld] ",(long)sc->sc_dleft)); + NCRCMD(sc, NCRCMD_FLUSH); + size = min(sc->sc_dleft, sc->sc_maxxfer); + NCRDMA_SETUP(sc, &sc->sc_dp, &sc->sc_dleft, 0, &size); + sc->sc_prevphase = DATA_OUT_PHASE; + goto setup_xfer; + + case DATA_IN_PHASE: + NCR_PHASE(("DATA_IN_PHASE ")); + if (sc->sc_rev == NCR_VARIANT_ESP100) + NCRCMD(sc, NCRCMD_FLUSH); + size = min(sc->sc_dleft, sc->sc_maxxfer); + NCRDMA_SETUP(sc, &sc->sc_dp, &sc->sc_dleft, 1, &size); + sc->sc_prevphase = DATA_IN_PHASE; + setup_xfer: + /* Target returned to data phase: wipe "done" memory */ + ecb->flags &= ~ECB_TENTATIVE_DONE; + + /* Program the SCSI counter */ + NCR_SET_COUNT(sc, size); + + /* load the count in */ + NCRCMD(sc, NCRCMD_NOP|NCRCMD_DMA); + + /* + * Note that if `size' is 0, we've already transceived + * all the bytes we want but we're still in DATA PHASE. + * Apparently, the device needs padding. Also, a + * transfer size of 0 means "maximum" to the chip + * DMA logic. + */ + NCRCMD(sc, + (size == 0 ? NCRCMD_TRPAD : NCRCMD_TRANS) | NCRCMD_DMA); + NCRDMA_GO(sc); + goto out; + + case STATUS_PHASE: + NCR_PHASE(("STATUS_PHASE ")); + sc->sc_flags |= NCR_ICCS; + NCRCMD(sc, NCRCMD_ICCS); + sc->sc_prevphase = STATUS_PHASE; + goto shortcut; /* i.e. expect status results soon */ + break; + + case INVALID_PHASE: + break; + + default: + device_printf(sc->sc_dev, "unexpected bus phase; resetting\n"); + goto reset; + } + +out: + mtx_unlock(&sc->sc_lock); + return; + +reset: + ncr53c9x_init(sc, 1); + goto out; + +finish: + ncr53c9x_done(sc, ecb); + goto out; + +sched: + sc->sc_state = NCR_IDLE; + ncr53c9x_sched(sc); + goto out; + +shortcut: + /* + * The idea is that many of the SCSI operations take very little + * time, and going away and getting interrupted is too high an + * overhead to pay. For example, selecting, sending a message + * and command and then doing some work can be done in one "pass". + * + * The delay is a heuristic. It is 2 when at 20MHz, 2 at 25MHz and 1 + * at 40MHz. This needs testing. + */ + { + struct timeval wait, cur; + + microtime(&wait); + wait.tv_usec += 50 / sc->sc_freq; + if (wait.tv_usec > 1000000) { + wait.tv_sec++; + wait.tv_usec -= 1000000; + } + do { + if (NCRDMA_ISINTR(sc)) + goto again; + microtime(&cur); + } while (cur.tv_sec <= wait.tv_sec && + cur.tv_usec <= wait.tv_usec); + } + goto out; +} + +static void +ncr53c9x_abort(sc, ecb) + struct ncr53c9x_softc *sc; + struct ncr53c9x_ecb *ecb; +{ + + /* 2 secs for the abort */ + ecb->timeout = NCR_ABORT_TIMEOUT; + ecb->flags |= ECB_ABORT; + + if (ecb == sc->sc_nexus) { + /* + * If we're still selecting, the message will be scheduled + * after selection is complete. + */ + if (sc->sc_state == NCR_CONNECTED) + ncr53c9x_sched_msgout(SEND_ABORT); + + /* + * Reschedule timeout. + */ + ecb->ccb->ccb_h.timeout_ch = + timeout(ncr53c9x_timeout, sc, mstohz(ecb->timeout)); + } else { + /* + * Just leave the command where it is. + * XXX - what choice do we have but to reset the SCSI + * eventually? + */ + if (sc->sc_state == NCR_IDLE) + ncr53c9x_sched(sc); + } +} + +static void +ncr53c9x_timeout(void *arg) +{ + struct ncr53c9x_ecb *ecb = arg; + union ccb *ccb = ecb->ccb; + struct ncr53c9x_softc *sc = ecb->sc; + struct ncr53c9x_tinfo *ti = &sc->sc_tinfo[ccb->ccb_h.target_id]; + + xpt_print_path(ccb->ccb_h.path); + device_printf(sc->sc_dev, "timed out [ecb %p (flags 0x%x, dleft %x, " + "stat %x)], <state %d, nexus %p, phase(l %x, c %x, p %x), " + "resid %lx, msg(q %x,o %x) %s>", + ecb, ecb->flags, ecb->dleft, ecb->stat, + sc->sc_state, sc->sc_nexus, + NCR_READ_REG(sc, NCR_STAT), + sc->sc_phase, sc->sc_prevphase, + (long)sc->sc_dleft, sc->sc_msgpriq, sc->sc_msgout, + NCRDMA_ISACTIVE(sc) ? "DMA active" : ""); +#if NCR53C9X_DEBUG > 1 + printf("TRACE: %s.", ecb->trace); +#endif + + mtx_lock(&sc->sc_lock); + + if (ecb->flags & ECB_ABORT) { + /* abort timed out */ + printf(" AGAIN\n"); + + ncr53c9x_init(sc, 1); + } else { + /* abort the operation that has timed out */ + printf("\n"); + ccb->ccb_h.status = CAM_CMD_TIMEOUT; + ncr53c9x_abort(sc, ecb); + + /* Disable sync mode if stuck in a data phase */ + if (ecb == sc->sc_nexus && + (ti->flags & T_SYNCMODE) != 0 && + (sc->sc_phase & (MSGI|CDI)) == 0) { + /* XXX ASYNC CALLBACK! */ + xpt_print_path(ccb->ccb_h.path); + printf("sync negotiation disabled\n"); + sc->sc_cfflags |= + (1 << ((ccb->ccb_h.target_id & 7) + 8)); + } + } + + mtx_unlock(&sc->sc_lock); +} + +static void +ncr53c9x_watch(void *arg) +{ + struct ncr53c9x_softc *sc = (struct ncr53c9x_softc *)arg; + struct ncr53c9x_tinfo *ti; + struct ncr53c9x_linfo *li; + int t; + /* Delete any structures that have not been used in 10min. */ + time_t old = time_second - (10 * 60); + + mtx_lock(&sc->sc_lock); + for (t = 0; t < sc->sc_ntarg; t++) { + ti = &sc->sc_tinfo[t]; + li = LIST_FIRST(&ti->luns); + while (li) { + if (li->last_used < old && + li->untagged == NULL && + li->used == 0) { + if (li->lun < NCR_NLUN) + ti->lun[li->lun] = NULL; + LIST_REMOVE(li, link); + free(li, M_DEVBUF); + /* Restart the search at the beginning */ + li = LIST_FIRST(&ti->luns); + continue; + } + li = LIST_NEXT(li, link); + } + } + mtx_unlock(&sc->sc_lock); + callout_reset(&sc->sc_watchdog, 60 * hz, ncr53c9x_watch, sc); +} + diff --git a/sys/dev/esp/ncr53c9xreg.h b/sys/dev/esp/ncr53c9xreg.h new file mode 100644 index 0000000..a2d9668 --- /dev/null +++ b/sys/dev/esp/ncr53c9xreg.h @@ -0,0 +1,290 @@ +/* $NetBSD: ncr53c9xreg.h,v 1.11 2003/02/21 17:14:05 tsutsui Exp $ */ + +/* + * Copyright (c) 1994 Peter Galbavy. 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 Peter Galbavy. + * 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. + */ + +/* $FreeBSD$ */ + +/* + * Register addresses, relative to some base address + */ + +#define NCR_TCL 0x00 /* RW - Transfer Count Low */ +#define NCR_TCM 0x01 /* RW - Transfer Count Mid */ +#define NCR_TCH 0x0e /* RW - Transfer Count High */ + /* NOT on 53C90 */ + +#define NCR_FIFO 0x02 /* RW - FIFO data */ + +#define NCR_CMD 0x03 /* RW - Command (2 deep) */ +#define NCRCMD_DMA 0x80 /* DMA Bit */ +#define NCRCMD_NOP 0x00 /* No Operation */ +#define NCRCMD_FLUSH 0x01 /* Flush FIFO */ +#define NCRCMD_RSTCHIP 0x02 /* Reset Chip */ +#define NCRCMD_RSTSCSI 0x03 /* Reset SCSI Bus */ +#define NCRCMD_RESEL 0x40 /* Reselect Sequence */ +#define NCRCMD_SELNATN 0x41 /* Select without ATN */ +#define NCRCMD_SELATN 0x42 /* Select with ATN */ +#define NCRCMD_SELATNS 0x43 /* Select with ATN & Stop */ +#define NCRCMD_ENSEL 0x44 /* Enable (Re)Selection */ +#define NCRCMD_DISSEL 0x45 /* Disable (Re)Selection */ +#define NCRCMD_SELATN3 0x46 /* Select with ATN3 */ +#define NCRCMD_RESEL3 0x47 /* Reselect3 Sequence */ +#define NCRCMD_SNDMSG 0x20 /* Send Message */ +#define NCRCMD_SNDSTAT 0x21 /* Send Status */ +#define NCRCMD_SNDDATA 0x22 /* Send Data */ +#define NCRCMD_DISCSEQ 0x23 /* Disconnect Sequence */ +#define NCRCMD_TERMSEQ 0x24 /* Terminate Sequence */ +#define NCRCMD_TCCS 0x25 /* Target Command Comp Seq */ +#define NCRCMD_DISC 0x27 /* Disconnect */ +#define NCRCMD_RECMSG 0x28 /* Receive Message */ +#define NCRCMD_RECCMD 0x29 /* Receive Command */ +#define NCRCMD_RECDATA 0x2a /* Receive Data */ +#define NCRCMD_RECCSEQ 0x2b /* Receive Command Sequence*/ +#define NCRCMD_ABORT 0x04 /* Target Abort DMA */ +#define NCRCMD_TRANS 0x10 /* Transfer Information */ +#define NCRCMD_ICCS 0x11 /* Initiator Cmd Comp Seq */ +#define NCRCMD_MSGOK 0x12 /* Message Accepted */ +#define NCRCMD_TRPAD 0x18 /* Transfer Pad */ +#define NCRCMD_SETATN 0x1a /* Set ATN */ +#define NCRCMD_RSTATN 0x1b /* Reset ATN */ + +#define NCR_STAT 0x04 /* RO - Status */ +#define NCRSTAT_INT 0x80 /* Interrupt */ +#define NCRSTAT_GE 0x40 /* Gross Error */ +#define NCRSTAT_PE 0x20 /* Parity Error */ +#define NCRSTAT_TC 0x10 /* Terminal Count */ +#define NCRSTAT_VGC 0x08 /* Valid Group Code */ +#define NCRSTAT_PHASE 0x07 /* Phase bits */ + +#define NCR_SELID 0x04 /* WO - Select/Reselect Bus ID */ +#define NCR_BUSID_HME 0x10 /* XXX HME reselect ID */ +#define NCR_BUSID_HME32 0x40 /* XXX HME to select more than 16 */ + +#define NCR_INTR 0x05 /* RO - Interrupt */ +#define NCRINTR_SBR 0x80 /* SCSI Bus Reset */ +#define NCRINTR_ILL 0x40 /* Illegal Command */ +#define NCRINTR_DIS 0x20 /* Disconnect */ +#define NCRINTR_BS 0x10 /* Bus Service */ +#define NCRINTR_FC 0x08 /* Function Complete */ +#define NCRINTR_RESEL 0x04 /* Reselected */ +#define NCRINTR_SELATN 0x02 /* Select with ATN */ +#define NCRINTR_SEL 0x01 /* Selected */ + +#define NCR_TIMEOUT 0x05 /* WO - Select/Reselect Timeout */ + +#define NCR_STEP 0x06 /* RO - Sequence Step */ +#define NCRSTEP_MASK 0x07 /* the last 3 bits */ +#define NCRSTEP_DONE 0x04 /* command went out */ + +#define NCR_SYNCTP 0x06 /* WO - Synch Transfer Period */ + /* Default 5 (53C9X) */ + +#define NCR_FFLAG 0x07 /* RO - FIFO Flags */ +#define NCRFIFO_SS 0xe0 /* Sequence Step (Dup) */ +#define NCRFIFO_FF 0x1f /* Bytes in FIFO */ + +#define NCR_SYNCOFF 0x07 /* WO - Synch Offset */ + /* 0 = ASYNC */ + /* 1 - 15 = SYNC bytes */ + +#define NCR_CFG1 0x08 /* RW - Configuration #1 */ +#define NCRCFG1_SLOW 0x80 /* Slow Cable Mode */ +#define NCRCFG1_SRR 0x40 /* SCSI Reset Rep Int Dis */ +#define NCRCFG1_PTEST 0x20 /* Parity Test Mod */ +#define NCRCFG1_PARENB 0x10 /* Enable Parity Check */ +#define NCRCFG1_CTEST 0x08 /* Enable Chip Test */ +#define NCRCFG1_BUSID 0x07 /* Bus ID */ + +#define NCR_CCF 0x09 /* WO - Clock Conversion Factor */ + /* 0 = 35.01 - 40MHz */ + /* NEVER SET TO 1 */ + /* 2 = 10MHz */ + /* 3 = 10.01 - 15MHz */ + /* 4 = 15.01 - 20MHz */ + /* 5 = 20.01 - 25MHz */ + /* 6 = 25.01 - 30MHz */ + /* 7 = 30.01 - 35MHz */ + +#define NCR_TEST 0x0a /* WO - Test (Chip Test Only) */ + +#define NCR_CFG2 0x0b /* RW - Configuration #2 */ +#define NCRCFG2_RSVD 0xa0 /* reserved */ +#define NCRCFG2_FE 0x40 /* Features Enable */ +#define NCRCFG2_DREQ 0x10 /* DREQ High Impedance */ +#define NCRCFG2_SCSI2 0x08 /* SCSI-2 Enable */ +#define NCRCFG2_BPA 0x04 /* Target Bad Parity Abort */ +#define NCRCFG2_RPE 0x02 /* Register Parity Error */ +#define NCRCFG2_DPE 0x01 /* DMA Parity Error */ + +#define NCRCFG2_HMEFE 0x10 /* HME feature enable */ +#define NCRCFG2_HME32 0x80 /* HME 32 extended */ + +/* Config #3 only on 53C9X */ +#define NCR_CFG3 0x0c /* RW - Configuration #3 */ +#define NCRCFG3_RSVD 0xe0 /* reserved */ +#define NCRCFG3_IDM 0x10 /* ID Message Res Check */ +#define NCRCFG3_QTE 0x08 /* Queue Tag Enable */ +#define NCRCFG3_CDB 0x04 /* CDB 10-bytes OK */ +#define NCRCFG3_FSCSI 0x02 /* Fast SCSI */ +#define NCRCFG3_FCLK 0x01 /* Fast Clock (>25MHz) */ + +/* + * For some unknown reason, the ESP406/FAS408 looks like every + * other ncr53c9x, except for configuration #3 register. At any + * rate, if you're dealing with these chips, you need to use these + * defines instead. + */ + +/* Config #3 different on ESP406/FAS408 */ +#define NCR_ESPCFG3 0x0c /* RW - Configuration #3 */ +#define NCRESPCFG3_IDM 0x80 /* ID Message Res Check */ +#define NCRESPCFG3_QTE 0x40 /* Queue Tag Enable */ +#define NCRESPCFG3_CDB 0x20 /* CDB 10-bytes OK */ +#define NCRESPCFG3_FSCSI 0x10 /* Fast SCSI */ +#define NCRESPCFG3_SRESB 0x08 /* Save Residual Byte */ +#define NCRESPCFG3_FCLK 0x04 /* Fast Clock (>25MHz) */ +#define NCRESPCFG3_ADMA 0x02 /* Alternate DMA Mode */ +#define NCRESPCFG3_T8M 0x01 /* Threshold 8 Mode */ + +/* Config #3 also different on NCR53CF9x/FAS216 */ +#define NCR_F9XCFG3 0x0c /* RW - Configuration #3 */ +#define NCRF9XCFG3_IDM 0x80 /* ID Message Res Check */ +#define NCRF9XCFG3_QTE 0x40 /* Queue Tag Enable */ +#define NCRF9XCFG3_CDB 0x20 /* CDB 10-bytes OK */ +#define NCRF9XCFG3_FSCSI 0x10 /* Fast SCSI */ +#define NCRF9XCFG3_FCLK 0x08 /* Fast Clock (>25MHz) */ +#define NCRF9XCFG3_SRESB 0x04 /* Save Residual Byte */ +#define NCRF9XCFG3_ADMA 0x02 /* Alternate DMA Mode */ +#define NCRF9XCFG3_T8M 0x01 /* Threshold 8 Mode */ + +/* Config #3 on FAS366 */ +#define NCRFASCFG3_OBAUTO 0x80 /* auto push odd-byte to DMA */ +#define NCRFASCFG3_EWIDE 0x40 /* Enable Wide-SCSI */ +#define NCRFASCFG3_IDBIT3 0x20 /* Bit 3 of HME SCSI-ID */ +#define NCRFASCFG3_IDRESCHK 0x10 /* ID message checking */ +#define NCRFASCFG3_QUENB 0x08 /* 3-byte msg support */ +#define NCRFASCFG3_CDB10 0x04 /* group 2 scsi-2 support */ +#define NCRFASCFG3_FASTSCSI 0x02 /* 10 MB/S fast scsi mode */ +#define NCRFASCFG3_FASTCLK 0x01 /* fast clock mode */ + +/* Config #4 only on ESP406/FAS408 */ +#define NCR_CFG4 0x0d /* RW - Configuration #4 */ +#define NCRCFG4_CRS1 0x80 /* Select register set #1 */ +#define NCRCFG4_RSVD 0x7b /* reserved */ +#define NCRCFG4_ACTNEG 0x04 /* Active negation */ + +/* + The following registers are only on the ESP406/FAS408. The + documentation refers to them as "Control Register Set #1". + These are the registers that are visible when bit 7 of + register 0x0d is set. This bit is common to both register sets. +*/ + +#define NCR_JMP 0x00 /* RO - Jumper Sense Register */ +#define NCRJMP_RSVD 0xc0 /* reserved */ +#define NCRJMP_ROMSZ 0x20 /* ROM Size 1=16K, 0=32K */ +#define NCRJMP_J4 0x10 /* Jumper #4 */ +#define NCRJMP_J3 0x08 /* Jumper #3 */ +#define NCRJMP_J2 0x04 /* Jumper #2 */ +#define NCRJMP_J1 0x02 /* Jumper #1 */ +#define NCRJMP_J0 0x01 /* Jumper #0 */ + +#define NCR_PIOFIFO 0x04 /* WO - PIO FIFO, 4 bytes deep */ + +#define NCR_PSTAT 0x08 /* RW - PIO Status Register */ +#define NCRPSTAT_PERR 0x80 /* PIO Error */ +#define NCRPSTAT_SIRQ 0x40 /* Active High of SCSI IRQ */ +#define NCRPSTAT_ATAI 0x20 /* ATA IRQ */ +#define NCRPSTAT_FEMPT 0x10 /* PIO FIFO Empty */ +#define NCRPSTAT_F13 0x08 /* PIO FIFO 1/3 */ +#define NCRPSTAT_F23 0x04 /* PIO FIFO 2/3 */ +#define NCRPSTAT_FFULL 0x02 /* PIO FIFO Full */ +#define NCRPSTAT_PIOM 0x01 /* PIO/DMA Mode */ + +#define NCR_PIOI 0x0b /* RW - PIO Interrupt Enable */ +#define NCRPIOI_RSVD 0xe0 /* reserved */ +#define NCRPIOI_EMPTY 0x10 /* IRQ When Empty */ +#define NCRPIOI_13 0x08 /* IRQ When 1/3 */ +#define NCRPIOI_23 0x04 /* IRQ When 2/3 */ +#define NCRPIOI_FULL 0x02 /* IRQ When Full */ +#define NCRPIOI_FINV 0x01 /* Flag Invert */ + +#define NCR_CFG5 0x0d /* RW - Configuration #5 */ +#define NCRCFG5_CRS1 0x80 /* Select Register Set #1 */ +#define NCRCFG5_SRAM 0x40 /* SRAM Memory Map */ +#define NCRCFG5_AADDR 0x20 /* Auto Address */ +#define NCRCFG5_PTRINC 0x10 /* Pointer Increment */ +#define NCRCFG5_LOWPWR 0x08 /* Low Power Mode */ +#define NCRCFG5_SINT 0x04 /* SCSI Interupt Enable */ +#define NCRCFG5_INTP 0x02 /* INT Polarity */ +#define NCRCFG5_AINT 0x01 /* ATA Interupt Enable */ + +#define NCR_SIGNTR 0x0e /* RO - Signature */ + +/* Am53c974 Config #3 */ +#define NCR_AMDCFG3 0x0c /* RW - Configuration #3 */ +#define NCRAMDCFG3_IDM 0x80 /* ID Message Res Check */ +#define NCRAMDCFG3_QTE 0x40 /* Queue Tag Enable */ +#define NCRAMDCFG3_CDB 0x20 /* CDB 10-bytes OK */ +#define NCRAMDCFG3_FSCSI 0x10 /* Fast SCSI */ +#define NCRAMDCFG3_FCLK 0x08 /* Fast Clock (40MHz) */ +#define NCRAMDCFG3_RSVD 0x07 /* Reserved */ + +/* Am53c974 Config #4 */ +#define NCR_AMDCFG4 0x0d /* RW - Configuration #4 */ +#define NCRAMDCFG4_GE 0xc0 /* Glitch Eater */ +#define NCRAMDCFG4_GE12NS 0x00 /* Signal window 12ns */ +#define NCRAMDCFG4_GE25NS 0x80 /* Signal window 25ns */ +#define NCRAMDCFG4_GE35NS 0x40 /* Signal window 35ns */ +#define NCRAMDCFG4_GE0NS 0xc0 /* Signal window 0ns */ +#define NCRAMDCFG4_PWD 0x20 /* Reduced power feature */ +#define NCRAMDCFG4_RSVD 0x13 /* Reserved */ +#define NCRAMDCFG4_RAE 0x08 /* Active neg. REQ/ACK */ +#define NCRAMDCFG4_RADE 0x04 /* Active neg. REQ/ACK/DAT */ + +/* + * FAS366 + */ +#define NCR_RCL NCR_TCH /* Recommand counter low */ +#define NCR_RCH 0xf /* Recommand counter high */ +#define NCR_UID NCR_RCL /* fas366 part-uniq id */ + + +/* status register #2 definitions (read only) */ +#define NCR_STAT2 NCR_CCF +#define NCRFAS_STAT2_SEQCNT 0x01 /* Sequence counter bit 7-3 enabled */ +#define NCRFAS_STAT2_FLATCHED 0x02 /* FIFO flags register latched */ +#define NCRFAS_STAT2_CLATCHED 0x04 /* Xfer cntr & recommand ctr latched */ +#define NCRFAS_STAT2_CACTIVE 0x08 /* Command register is active */ +#define NCRFAS_STAT2_SCSI16 0x10 /* SCSI interface is wide */ +#define NCRFAS_STAT2_ISHUTTLE 0x20 /* FIFO Top register contains 1 byte */ +#define NCRFAS_STAT2_OSHUTTLE 0x40 /* next byte from FIFO is MSB */ +#define NCRFAS_STAT2_EMPTY 0x80 /* FIFO is empty */ + diff --git a/sys/dev/esp/ncr53c9xvar.h b/sys/dev/esp/ncr53c9xvar.h new file mode 100644 index 0000000..fa4c6c4 --- /dev/null +++ b/sys/dev/esp/ncr53c9xvar.h @@ -0,0 +1,457 @@ +/* $NetBSD: ncr53c9xvar.h,v 1.41 2003/02/04 20:05:11 pk Exp $ */ + +/*- + * Copyright (c) 1997 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Jason R. Thorpe of the Numerical Aerospace Simulation Facility, + * NASA Ames Research Center. + * + * 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 FOUNDATION 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. + */ + +/* + * Copyright (c) 1994 Peter Galbavy. 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 Peter Galbavy. + * 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. + */ + +/* $FreeBSD$ */ + +#ifndef _DEV_IC_NCR53C9XVAR_H_ +#define _DEV_IC_NCR53C9XVAR_H_ + +#include <sys/lock.h> + +/* Set this to 1 for normal debug, or 2 for per-target tracing. */ +/* #define NCR53C9X_DEBUG 2 */ + +/* Wide or differential can have 16 targets */ +#define NCR_NLUN 8 + +#define NCR_ABORT_TIMEOUT 2000 /* time to wait for abort */ +#define NCR_SENSE_TIMEOUT 1000 /* time to wait for sense */ + +#define FREQTOCCF(freq) (((freq + 4) / 5)) + +/* + * NCR 53c9x variants. Note, these values are used as indexes into + * a table; don't modify them unless you know what you're doing. + */ +#define NCR_VARIANT_ESP100 0 +#define NCR_VARIANT_ESP100A 1 +#define NCR_VARIANT_ESP200 2 +#define NCR_VARIANT_NCR53C94 3 +#define NCR_VARIANT_NCR53C96 4 +#define NCR_VARIANT_ESP406 5 +#define NCR_VARIANT_FAS408 6 +#define NCR_VARIANT_FAS216 7 +#define NCR_VARIANT_AM53C974 8 +#define NCR_VARIANT_FAS366 9 +#define NCR_VARIANT_NCR53C90_86C01 10 +#define NCR_VARIANT_MAX 11 + +/* + * ECB. 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 scsipi_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 ncr53c9x_ecb { + TAILQ_ENTRY(ncr53c9x_ecb) chain; + union ccb *ccb; /* SCSI xfer ctrl block from above */ + struct ncr53c9x_softc *sc; + int flags; +#define ECB_ALLOC 0x01 +#define ECB_READY 0x02 +#define ECB_SENSE 0x04 +#define ECB_ABORT 0x40 +#define ECB_RESET 0x80 +#define ECB_TENTATIVE_DONE 0x100 + int timeout; + + struct { + u_char msg[3]; /* Selection Id msg and tags */ + struct scsi_generic cmd; /* SCSI command block */ + } cmd; + char *daddr; /* Saved data pointer */ + int clen; /* Size of command in cmd.cmd */ + int dleft; /* Residue */ + u_char stat; /* SCSI status byte */ + u_char tag[2]; /* TAG bytes */ + u_char pad[1]; + +#if NCR53C9X_DEBUG > 1 + char trace[1000]; +#endif +}; +#if NCR53C9X_DEBUG > 1 +#define ECB_TRACE(ecb, msg, a, b) do { \ + const char *f = "[" msg "]"; \ + int n = strlen((ecb)->trace); \ + if (n < (sizeof((ecb)->trace)-100)) \ + sprintf((ecb)->trace + n, f, a, b); \ +} while(0) +#else +#define ECB_TRACE(ecb, msg, a, b) +#endif + +/* + * Some info about each (possible) target and LUN on the SCSI bus. + * + * SCSI I and II devices can have up to 8 LUNs, each with up to 256 + * outstanding tags. SCSI III devices have 64-bit LUN identifiers + * that can be sparsely allocated. + * + * Since SCSI II devices can have up to 8 LUNs, we use an array + * of 8 pointers to ncr53c9x_linfo structures for fast lookup. + * Longer LUNs need to traverse the linked list. + */ + +struct ncr53c9x_linfo { + int64_t lun; + LIST_ENTRY(ncr53c9x_linfo) link; + time_t last_used; + unsigned char used; /* # slots in use */ + unsigned char avail; /* where to start scanning */ + unsigned char busy; + struct ncr53c9x_ecb *untagged; + struct ncr53c9x_ecb *queued[256]; +}; + +struct ncr53c9x_tinfo { + int cmds; /* # of commands processed */ + int dconns; /* # of disconnects */ + int touts; /* # of timeouts */ + int perrs; /* # of parity errors */ + int senses; /* # of request sense commands sent */ + u_char flags; +#define T_NEGOTIATE 0x02 /* (Re)Negotiate synchronous options */ +#define T_SYNCMODE 0x08 /* SYNC mode has been negotiated */ +#define T_SYNCHOFF 0x10 /* SYNC mode for is permanently off */ +#define T_RSELECTOFF 0x20 /* RE-SELECT mode is off */ +#define T_TAG 0x40 /* Turn on TAG QUEUEs */ +#define T_WIDE 0x80 /* Negotiate wide options */ +#define T_WDTRSENT 0x04 /* WDTR message has been sent to */ + u_char period; /* Period suggestion */ + u_char offset; /* Offset suggestion */ + u_char cfg3; /* per target config 3 */ + u_char nextag; /* Next available tag */ + u_char width; /* width suggesion */ + LIST_HEAD(lun_list, ncr53c9x_linfo) luns; + struct ncr53c9x_linfo *lun[NCR_NLUN]; /* For speedy lookups */ +}; + +/* Look up a lun in a tinfo */ +#define TINFO_LUN(t, l) ( \ + (((l) < NCR_NLUN) && (((t)->lun[(l)]) != NULL)) \ + ? ((t)->lun[(l)]) \ + : ncr53c9x_lunsearch((t), (int64_t)(l)) \ +) + +/* Register a linenumber (for debugging) */ +#define LOGLINE(p) + +#define NCR_SHOWECBS 0x01 +#define NCR_SHOWINTS 0x02 +#define NCR_SHOWCMDS 0x04 +#define NCR_SHOWMISC 0x08 +#define NCR_SHOWTRAC 0x10 +#define NCR_SHOWSTART 0x20 +#define NCR_SHOWPHASE 0x40 +#define NCR_SHOWDMA 0x80 +#define NCR_SHOWCCMDS 0x100 +#define NCR_SHOWMSGS 0x200 + +#ifdef NCR53C9X_DEBUG +extern int ncr53c9x_debug; +#define NCR_ECBS(str) \ + do {if (ncr53c9x_debug & NCR_SHOWECBS) printf str;} while (0) +#define NCR_MISC(str) \ + do {if (ncr53c9x_debug & NCR_SHOWMISC) printf str;} while (0) +#define NCR_INTS(str) \ + do {if (ncr53c9x_debug & NCR_SHOWINTS) printf str;} while (0) +#define NCR_TRACE(str) \ + do {if (ncr53c9x_debug & NCR_SHOWTRAC) printf str;} while (0) +#define NCR_CMDS(str) \ + do {if (ncr53c9x_debug & NCR_SHOWCMDS) printf str;} while (0) +#define NCR_START(str) \ + do {if (ncr53c9x_debug & NCR_SHOWSTART) printf str;}while (0) +#define NCR_PHASE(str) \ + do {if (ncr53c9x_debug & NCR_SHOWPHASE) printf str;}while (0) +#define NCR_DMA(str) \ + do {if (ncr53c9x_debug & NCR_SHOWDMA) printf str;}while (0) +#define NCR_MSGS(str) \ + do {if (ncr53c9x_debug & NCR_SHOWMSGS) printf str;}while (0) +#else +#define NCR_ECBS(str) +#define NCR_MISC(str) +#define NCR_INTS(str) +#define NCR_TRACE(str) +#define NCR_CMDS(str) +#define NCR_START(str) +#define NCR_PHASE(str) +#define NCR_DMA(str) +#define NCR_MSGS(str) +#endif + +#define NCR_MAX_MSG_LEN 8 + +struct ncr53c9x_softc; + +/* + * Function switch used as glue to MD code. + */ +struct ncr53c9x_glue { + /* Mandatory entry points. */ + u_char (*gl_read_reg)(struct ncr53c9x_softc *, int); + void (*gl_write_reg)(struct ncr53c9x_softc *, int, u_char); + int (*gl_dma_isintr)(struct ncr53c9x_softc *); + void (*gl_dma_reset)(struct ncr53c9x_softc *); + int (*gl_dma_intr)(struct ncr53c9x_softc *); + int (*gl_dma_setup)(struct ncr53c9x_softc *, + caddr_t *, size_t *, int, size_t *); + void (*gl_dma_go)(struct ncr53c9x_softc *); + void (*gl_dma_stop)(struct ncr53c9x_softc *); + int (*gl_dma_isactive)(struct ncr53c9x_softc *); + + /* Optional entry points. */ + void (*gl_clear_latched_intr)(struct ncr53c9x_softc *); +}; + +struct ncr53c9x_softc { + device_t sc_dev; /* us as a device */ + + struct cam_sim *sc_sim; /* our scsi adapter */ + struct cam_path *sc_path; /* our scsi channel */ + struct callout sc_watchdog; /* periodic timer */ + + struct ncr53c9x_glue *sc_glue; /* glue to MD code */ + + int sc_cfflags; /* Copy of config flags */ + + /* register defaults */ + u_char sc_cfg1; /* Config 1 */ + u_char sc_cfg2; /* Config 2, not ESP100 */ + u_char sc_cfg3; /* Config 3, ESP200,FAS */ + u_char sc_cfg3_fscsi; /* Chip-specific FSCSI bit */ + u_char sc_cfg4; /* Config 4, only ESP200 */ + u_char sc_cfg5; /* Config 5, only ESP200 */ + u_char sc_ccf; /* Clock Conversion */ + u_char sc_timeout; + + /* register copies, see espreadregs() */ + u_char sc_espintr; + u_char sc_espstat; + u_char sc_espstep; + u_char sc_espstat2; + u_char sc_espfflags; + + /* Lists of command blocks */ + TAILQ_HEAD(ecb_list, ncr53c9x_ecb) + ready_list; + + struct ncr53c9x_ecb *sc_nexus; /* Current command */ + int sc_ntarg; + struct ncr53c9x_tinfo *sc_tinfo; + + /* Data about the current nexus (updated for every cmd switch) */ + caddr_t sc_dp; /* Current data pointer */ + ssize_t sc_dleft; /* Data left to transfer */ + + /* Adapter state */ + int sc_phase; /* Copy of what bus phase we are in */ + int sc_prevphase; /* Copy of what bus phase we were in */ + u_char sc_state; /* State applicable to the adapter */ + u_char sc_flags; /* See below */ + u_char sc_selid; + u_char sc_lastcmd; + + /* Message stuff */ + u_short sc_msgify; /* IDENTIFY message associated with this nexus */ + u_short sc_msgout; /* What message is on its way out? */ + u_short sc_msgpriq; /* One or more messages to send (encoded) */ + u_short sc_msgoutq; /* What messages have been sent so far? */ + + u_char *sc_omess; /* MSGOUT buffer */ + caddr_t sc_omp; /* Message pointer (for multibyte messages) */ + size_t sc_omlen; + u_char *sc_imess; /* MSGIN buffer */ + caddr_t sc_imp; /* Message pointer (for multibyte messages) */ + size_t sc_imlen; + + caddr_t sc_cmdp; /* Command pointer (for DMAed commands) */ + size_t sc_cmdlen; /* Size of command in transit */ + + /* Hardware attributes */ + int sc_freq; /* SCSI bus frequency in MHz */ + int sc_id; /* Our SCSI id */ + int sc_rev; /* Chip revision */ + int sc_features; /* Chip features */ + int sc_minsync; /* Minimum sync period / 4 */ + int sc_maxxfer; /* Maximum transfer size */ + int sc_maxsync; /* Maximum sync period */ + int sc_maxoffset; /* Maximum offset */ + int sc_maxwidth; /* Maximum width */ + + struct mtx sc_lock; /* driver mutex */ +}; + +/* values for sc_state */ +#define NCR_IDLE 1 /* waiting for something to do */ +#define NCR_SELECTING 2 /* SCSI command is arbiting */ +#define NCR_RESELECTED 3 /* Has been reselected */ +#define NCR_IDENTIFIED 4 /* Has gotten IFY but not TAG */ +#define NCR_CONNECTED 5 /* Actively using the SCSI bus */ +#define NCR_DISCONNECT 6 /* MSG_DISCONNECT received */ +#define NCR_CMDCOMPLETE 7 /* MSG_CMDCOMPLETE received */ +#define NCR_CLEANING 8 +#define NCR_SBR 9 /* Expect a SCSI RST because we commanded it */ + +/* values for sc_flags */ +#define NCR_DROP_MSGI 0x01 /* Discard all msgs (parity err detected) */ +#define NCR_ABORTING 0x02 /* Bailing out */ +#define NCR_DOINGDMA 0x04 /* The FIFO data path is active! */ +#define NCR_SYNCHNEGO 0x08 /* Synch negotiation in progress. */ +#define NCR_ICCS 0x10 /* Expect status phase results */ +#define NCR_WAITI 0x20 /* Waiting for non-DMA data to arrive */ +#define NCR_ATN 0x40 /* ATN asserted */ +#define NCR_EXPECT_ILLCMD 0x80 /* Expect Illegal Command Interrupt */ + +/* values for sc_features */ +#define NCR_F_HASCFG3 0x01 /* chip has CFG3 register */ +#define NCR_F_FASTSCSI 0x02 /* chip supports Fast mode */ +#define NCR_F_DMASELECT 0x04 /* can do dmaselect */ +#define NCR_F_SELATN3 0x08 /* chip supports SELATN3 command */ + +/* values for sc_msgout */ +#define SEND_DEV_RESET 0x0001 +#define SEND_PARITY_ERROR 0x0002 +#define SEND_INIT_DET_ERR 0x0004 +#define SEND_REJECT 0x0008 +#define SEND_IDENTIFY 0x0010 +#define SEND_ABORT 0x0020 +#define SEND_WDTR 0x0040 +#define SEND_SDTR 0x0080 +#define SEND_TAG 0x0100 + +/* SCSI Status codes */ +#define ST_MASK 0x3e /* bit 0,6,7 is reserved */ + +/* phase bits */ +#define IOI 0x01 +#define CDI 0x02 +#define MSGI 0x04 + +/* Information transfer phases */ +#define DATA_OUT_PHASE (0) +#define DATA_IN_PHASE (IOI) +#define COMMAND_PHASE (CDI) +#define STATUS_PHASE (CDI|IOI) +#define MESSAGE_OUT_PHASE (MSGI|CDI) +#define MESSAGE_IN_PHASE (MSGI|CDI|IOI) + +#define PHASE_MASK (MSGI|CDI|IOI) + +/* Some pseudo phases for getphase()*/ +#define BUSFREE_PHASE 0x100 /* Re/Selection no longer valid */ +#define INVALID_PHASE 0x101 /* Re/Selection valid, but no REQ yet */ +#define PSEUDO_PHASE 0x100 /* "pseudo" bit */ + +/* + * Macros to read and write the chip's registers. + */ +#define NCR_READ_REG(sc, reg) \ + (*(sc)->sc_glue->gl_read_reg)((sc), (reg)) +#define NCR_WRITE_REG(sc, reg, val) \ + (*(sc)->sc_glue->gl_write_reg)((sc), (reg), (val)) + +#ifdef NCR53C9X_DEBUG +#define NCRCMD(sc, cmd) do { \ + if ((ncr53c9x_debug & NCR_SHOWCCMDS) != 0) \ + printf("<CMD:0x%x %d>", (unsigned)cmd, __LINE__); \ + sc->sc_lastcmd = cmd; \ + NCR_WRITE_REG(sc, NCR_CMD, cmd); \ +} while (0) +#else +#define NCRCMD(sc, cmd) NCR_WRITE_REG(sc, NCR_CMD, cmd) +#endif + +/* + * DMA macros for NCR53c9x + */ +#define NCRDMA_ISINTR(sc) (*(sc)->sc_glue->gl_dma_isintr)((sc)) +#define NCRDMA_RESET(sc) (*(sc)->sc_glue->gl_dma_reset)((sc)) +#define NCRDMA_INTR(sc) (*(sc)->sc_glue->gl_dma_intr)((sc)) +#define NCRDMA_SETUP(sc, addr, len, datain, dmasize) \ + (*(sc)->sc_glue->gl_dma_setup)((sc), (addr), (len), (datain), (dmasize)) +#define NCRDMA_GO(sc) (*(sc)->sc_glue->gl_dma_go)((sc)) +#define NCRDMA_ISACTIVE(sc) (*(sc)->sc_glue->gl_dma_isactive)((sc)) + +/* + * Macro to convert the chip register Clock Per Byte value to + * Sunchronous Transfer Period. + */ +#define ncr53c9x_cpb2stp(sc, cpb) \ + ((250 * (cpb)) / (sc)->sc_freq) + +int ncr53c9x_attach(struct ncr53c9x_softc *); +int ncr53c9x_detach(struct ncr53c9x_softc *, int); +void ncr53c9x_action(struct cam_sim *, union ccb *); +void ncr53c9x_reset(struct ncr53c9x_softc *); +void ncr53c9x_intr(void *); +void ncr53c9x_init(struct ncr53c9x_softc *, int); + +#endif /* _DEV_IC_NCR53C9XVAR_H_ */ |