diff options
author | Kenneth R Westerback <krw@cvs.openbsd.org> | 2003-01-08 02:11:39 +0000 |
---|---|---|
committer | Kenneth R Westerback <krw@cvs.openbsd.org> | 2003-01-08 02:11:39 +0000 |
commit | a3e988bf5abac645f9e20ebce4dea1643712d2bf (patch) | |
tree | 9897618ae028053895d2c2a6f09729ba224bf8db /sys | |
parent | 5b2a1b9dd89678c855fd966ab513be82848ba61b (diff) |
Merry Christmas Mickey!
First cut at osiop driver (LSI Logic/Symbios/NCR 53C710). For hppa
only at the moment.
Functional for the most part, but there are known problems:
1) SCSI_CHECK/REQUEST_SENSE not handled at all - simply returns a
zero'ed scsi_sense_data buffer. As a result all osiop sc_link's are
created with the ADEV_NODOORLOCK quirk to suppress PREVENT_ALLOW
commands from being issued (and failing) during probe.
2) Sync negotiation (wide is not supported on this chip) needs to be
validated due to some ominous comments in the source about being valid
only for the 33Mhz Zeus board.
3) Probe message needs fixing/completion to issue useful info. See 2).
4) Timeout/hangs occur under heavy load, e.g. make builds.
From NetBSD.
ok mickey@
Diffstat (limited to 'sys')
-rw-r--r-- | sys/arch/hppa/conf/GENERIC | 10 | ||||
-rw-r--r-- | sys/arch/hppa/conf/files.hppa | 5 | ||||
-rw-r--r-- | sys/arch/hppa/gsc/osiop_gsc.c | 213 | ||||
-rw-r--r-- | sys/conf/files | 6 | ||||
-rw-r--r-- | sys/dev/ic/osiop.c | 2000 | ||||
-rw-r--r-- | sys/dev/ic/osiopreg.h | 379 | ||||
-rw-r--r-- | sys/dev/ic/osiopvar.h | 253 | ||||
-rw-r--r-- | sys/dev/microcode/siop/Makefile | 12 | ||||
-rw-r--r-- | sys/dev/microcode/siop/osiop.out | 181 | ||||
-rw-r--r-- | sys/dev/microcode/siop/osiop.ss | 256 |
10 files changed, 3303 insertions, 12 deletions
diff --git a/sys/arch/hppa/conf/GENERIC b/sys/arch/hppa/conf/GENERIC index 787bb7d6671..01c20f3bf71 100644 --- a/sys/arch/hppa/conf/GENERIC +++ b/sys/arch/hppa/conf/GENERIC @@ -1,4 +1,4 @@ -# $OpenBSD: GENERIC,v 1.14 2002/12/19 00:24:40 mickey Exp $ +# $OpenBSD: GENERIC,v 1.15 2003/01/08 02:11:38 krw Exp $ # # Diskless kernel config # @@ -92,9 +92,9 @@ lpt0 at gsc? irq 7 # both com and lpt are on WD16C552 ie0 at gsc0 irq 8 # 82C5[89]6 ether ie1 at gsc1 irq 8 #tms* at gsc? irq 10 # TMS380C26 Network Controller (either 802.3 or 802.5) -#siop0 at gsc? irq 9 # NCR 53C700/710 -#siop1 at gsc? irq 3 # NCR 53C720 (Fast/Wide) -#scsibus* at siop? +osiop0 at gsc? irq 9 # NCR 53C700/710 +#osiop1 at gsc? irq 3 # NCR 53C720 (Fast/Wide) +scsibus* at osiop? #aone* at gsc? irq 13 # Audio Type 1 (PSB 2160-N) #audio* at aone? #harmony* at gsc? irq 13 # Audio Type 2 (CS4215/AD1849) @@ -131,7 +131,7 @@ ie1 at gsc1 irq 8 #fd* at fdc? drive ? # floppy drives -#sd* at scsibus? target ? lun ? +sd* at scsibus? target ? lun ? #st* at scsibus? target ? lun ? #cd* at scsibus? target ? lun ? #ch* at scsibus? target ? lun ? diff --git a/sys/arch/hppa/conf/files.hppa b/sys/arch/hppa/conf/files.hppa index 8d328d1de17..0909280c59e 100644 --- a/sys/arch/hppa/conf/files.hppa +++ b/sys/arch/hppa/conf/files.hppa @@ -1,4 +1,4 @@ -# $OpenBSD: files.hppa,v 1.36 2002/12/18 23:52:41 mickey Exp $ +# $OpenBSD: files.hppa,v 1.37 2003/01/08 02:11:38 krw Exp $ # # hppa-specific configuration info @@ -149,6 +149,9 @@ file arch/hppa/gsc/if_ie_gsc.c ie_gsc attach siop at gsc file arch/hppa/gsc/siop_gsc.c siop +attach osiop at gsc +file arch/hppa/gsc/osiop_gsc.c osiop + device hil: tty attach hil at gsc file arch/hppa/gsc/hil.c hil diff --git a/sys/arch/hppa/gsc/osiop_gsc.c b/sys/arch/hppa/gsc/osiop_gsc.c new file mode 100644 index 00000000000..1ad2728258d --- /dev/null +++ b/sys/arch/hppa/gsc/osiop_gsc.c @@ -0,0 +1,213 @@ +/* $OpenBSD: osiop_gsc.c,v 1.1 2003/01/08 02:11:38 krw Exp $ */ +/* $NetBSD: osiop_gsc.c,v 1.6 2002/10/02 05:17:50 thorpej Exp $ */ + +/* + * Copyright (c) 2001 Matt Fredette. All rights reserved. + * Copyright (c) 2001 Izumi Tsutsui. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``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 AUTHORS 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. + */ + +/* $OpenBSD: osiop_gsc.c,v 1.1 2003/01/08 02:11:38 krw Exp $ */ + +/* + * Copyright (c) 1998 Michael Shalayeff + * 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 Michael Shalayeff. + * 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. + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/device.h> +#include <sys/buf.h> +#include <sys/malloc.h> + +#include <scsi/scsi_all.h> +#include <scsi/scsiconf.h> + +#include <machine/cpu.h> +#include <machine/intr.h> +#include <machine/iomod.h> +#include <machine/autoconf.h> +#include <machine/bus.h> + +#include <dev/ic/osiopreg.h> +#include <dev/ic/osiopvar.h> + +#include <hppa/dev/cpudevs.h> +#include <hppa/gsc/gscbusvar.h> +/* #include <hppa/hppa/machdep.h> */ + +#define OSIOP_GSC_RESET 0x0000 +#define OSIOP_GSC_OFFSET 0x0100 + +int osiop_gsc_match(struct device *, void *, void *); +void osiop_gsc_attach(struct device *, struct device *, void *); +int osiop_gsc_intr(void *); + +struct cfattach osiop_ca = { + sizeof(struct osiop_softc), + osiop_gsc_match, + osiop_gsc_attach +}; + +int +osiop_gsc_match(parent, match, aux) + struct device *parent; + void *match, *aux; +{ + struct gsc_attach_args *ga = aux; + bus_space_handle_t ioh; + int rv = 1; + + if (ga->ga_type.iodc_type != HPPA_TYPE_FIO || + (ga->ga_type.iodc_sv_model != HPPA_FIO_GSCSI && + ga->ga_type.iodc_sv_model != HPPA_FIO_SCSI)) + return 0; + + if (bus_space_map(ga->ga_iot, ga->ga_hpa, + OSIOP_GSC_OFFSET + OSIOP_NREGS, 0, &ioh)) + return 0; + + + bus_space_unmap(ga->ga_iot, ioh, OSIOP_GSC_OFFSET + OSIOP_NREGS); + return rv; +} + +void +osiop_gsc_attach(parent, self, aux) + struct device *parent, *self; + void *aux; +{ + register struct osiop_softc *sc = (void *)self; + register struct gsc_attach_args *ga = aux; + bus_space_handle_t ioh; + + sc->sc_bst = ga->ga_iot; + sc->sc_dmat = ga->ga_dmatag; + if (bus_space_map(sc->sc_bst, ga->ga_hpa, + OSIOP_GSC_OFFSET + OSIOP_NREGS, 0, &ioh)) + panic("osiop_gsc_attach: couldn't map I/O ports"); + if (bus_space_subregion(sc->sc_bst, ioh, + OSIOP_GSC_OFFSET, OSIOP_NREGS, &sc->sc_reg)) + panic("osiop_gsc_attach: couldn't get chip ports"); + + sc->sc_clock_freq = ga->ga_ca.ca_pdc_iodc_read->filler2[14] / 1000000; + if (!sc->sc_clock_freq) + sc->sc_clock_freq = 50; + + if (ga->ga_ca.ca_type.iodc_sv_model == HPPA_FIO_GSCSI) { + sc->sc_ctest7 = 0; /* | OSIOP_CTEST7_TT1 */ + sc->sc_dcntl = OSIOP_DCNTL_EA; + } else { + sc->sc_ctest7 = 0; + sc->sc_dcntl = 0; + } + + sc->sc_flags = 0; + sc->sc_id = 7; + + /* + * Reset the SCSI subsystem. + */ + bus_space_write_1(sc->sc_bst, ioh, OSIOP_GSC_RESET, 0); + DELAY(1000); + + /* + * Call common attachment + */ +#ifdef OSIOP_DEBUG + { + extern int osiop_debug; + osiop_debug = -1; + } +#endif /* OSIOP_DEBUG */ + osiop_attach(sc); + + (void)gsc_intr_establish((struct gsc_softc *)parent, IPL_BIO, + ga->ga_irq, osiop_gsc_intr, sc, &sc->sc_dev); +} + +/* + * interrupt handler + */ +int +osiop_gsc_intr(arg) + void *arg; +{ + struct osiop_softc *sc = arg; + u_int8_t istat; + + /* This is potentially nasty, since the IRQ is level triggered... */ + if (sc->sc_flags & OSIOP_INTSOFF) + return (0); + + istat = osiop_read_1(sc, OSIOP_ISTAT); + + if ((istat & (OSIOP_ISTAT_SIP | OSIOP_ISTAT_DIP)) == 0) + return (0); + + /* Save interrupt details for the back-end interrupt handler */ + sc->sc_sstat0 = osiop_read_1(sc, OSIOP_SSTAT0); + sc->sc_istat = istat; + /* + * Per page 4-18 of the LSI 53C710 Technical Manual, + * "insert a delay equivalent to 12 BCLK periods between + * the reads [of DSTAT and SSTAT0] to ensure that the + * interrupts clear properly." + */ + DELAY(100); + sc->sc_dstat = osiop_read_1(sc, OSIOP_DSTAT); + + /* Deal with the interrupt */ + osiop_intr(sc); + + /* Blink the LED. */ + /* hp700_led_blink(HP700_LED_DISK); */ + + return (1); +} diff --git a/sys/conf/files b/sys/conf/files index 462703706ae..40426a1b4d3 100644 --- a/sys/conf/files +++ b/sys/conf/files @@ -1,4 +1,4 @@ -# $OpenBSD: files,v 1.264 2003/01/07 00:25:05 dhartmei Exp $ +# $OpenBSD: files,v 1.265 2003/01/08 02:11:38 krw Exp $ # $NetBSD: files,v 1.87 1996/05/19 17:17:50 jonathan Exp $ # @(#)files.newconf 7.5 (Berkeley) 5/10/93 @@ -170,6 +170,10 @@ file dev/ic/siop_common.c siop_common device siop: scsi,siop_common file dev/ic/siop.c siop +# Symbios/NCR 53c720/53c8xx SCSI controllers +device osiop: scsi +file dev/ic/osiop.c osiop + # 3Com Etherlink-III Ethernet controller device ep: ether, ifnet, ifmedia, mii file dev/ic/elink3.c ep diff --git a/sys/dev/ic/osiop.c b/sys/dev/ic/osiop.c new file mode 100644 index 00000000000..d4325799b34 --- /dev/null +++ b/sys/dev/ic/osiop.c @@ -0,0 +1,2000 @@ +/* $OpenBSD: osiop.c,v 1.1 2003/01/08 02:11:38 krw Exp $ */ +/* $NetBSD: osiop.c,v 1.9 2002/04/05 18:27:54 bouyer Exp $ */ + +/* + * Copyright (c) 2001 Izumi Tsutsui. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software 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. + */ + +/* + * Copyright (c) 1994 Michael L. Hitch + * Copyright (c) 1990 The Regents of the University of California. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Van Jacobson of Lawrence Berkeley Laboratory. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)siop.c 7.5 (Berkeley) 5/4/91 + */ + +/* + * MI NCR53C710 scsi adaptor driver; based on arch/amiga/dev/siop.c: + * NetBSD: siop.c,v 1.43 1999/09/30 22:59:53 thorpej Exp + * + * bus_space/bus_dma'fied by Izumi Tsutsui <tsutsui@ceres.dti.ne.jp> + * + * The 53c710 datasheet is avaliable at: + * http://www.lsilogic.com/techlib/techdocs/storage_stand_prod/index.html + */ + +#include <sys/cdefs.h> +/* __KERNEL_RCSID(0, "$NetBSD: osiop.c,v 1.9 2002/04/05 18:27:54 bouyer Exp $"); */ + +/* #define OSIOP_DEBUG */ + +/* #include "opt_ddb.h" */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/device.h> +#include <sys/malloc.h> +#include <sys/buf.h> +#include <sys/kernel.h> + +#include <uvm/uvm_extern.h> + +#include <scsi/scsi_all.h> +#include <scsi/scsiconf.h> +#include <scsi/scsi_message.h> + +#include <machine/cpu.h> +#include <machine/bus.h> + +#include <dev/ic/osiopreg.h> +#include <dev/ic/osiopvar.h> + +/* 53C710 script */ +#include <dev/microcode/siop/osiop.out> + +void osiop_attach(struct osiop_softc *); +void osiop_minphys(struct buf *); +int osiop_scsicmd(struct scsi_xfer *xs); +void osiop_poll(struct osiop_softc *, struct osiop_acb *); +void osiop_sched(struct osiop_softc *); +void osiop_scsidone(struct osiop_acb *, int); +void osiop_abort(struct osiop_softc *, const char *); +void osiop_init(struct osiop_softc *); +void osiop_reset(struct osiop_softc *); +void osiop_resetbus(struct osiop_softc *); +void osiop_start(struct osiop_softc *); +int osiop_checkintr(struct osiop_softc *, u_int8_t, u_int8_t, u_int8_t, int *); +void osiop_select(struct osiop_softc *); +void osiop_update_xfer_mode(struct osiop_softc *, int); +void scsi_period_to_osiop(struct osiop_softc *, int); +void osiop_timeout(void *); + +int osiop_reset_delay = 250; /* delay after reset, in milleseconds */ + +#ifdef OSIOP_DEBUG_SYNC +/* + * sync period transfer lookup - only valid for 66MHz clock + */ +static struct { + u_int8_t p; /* period from sync request message */ + u_int8_t r; /* siop_period << 4 | sbcl */ +} sync_tab[] = { + { 60/4, 0<<4 | 1}, + { 76/4, 1<<4 | 1}, + { 92/4, 2<<4 | 1}, + { 92/4, 0<<4 | 2}, + {108/4, 3<<4 | 1}, + {116/4, 1<<4 | 2}, + {120/4, 4<<4 | 1}, + {120/4, 0<<4 | 3}, + {136/4, 5<<4 | 1}, + {140/4, 2<<4 | 2}, + {152/4, 6<<4 | 1}, + {152/4, 1<<4 | 3}, + {164/4, 3<<4 | 2}, + {168/4, 7<<4 | 1}, + {180/4, 2<<4 | 3}, + {184/4, 4<<4 | 2}, + {208/4, 5<<4 | 2}, + {212/4, 3<<4 | 3}, + {232/4, 6<<4 | 2}, + {240/4, 4<<4 | 3}, + {256/4, 7<<4 | 2}, + {272/4, 5<<4 | 3}, + {300/4, 6<<4 | 3}, + {332/4, 7<<4 | 3} +}; +#endif + +#ifdef OSIOP_DEBUG +#define DEBUG_DMA 0x01 +#define DEBUG_INT 0x02 +#define DEBUG_PHASE 0x04 +#define DEBUG_UNEXCEPT 0x08 +#define DEBUG_DISC 0x10 +#define DEBUG_CMD 0x20 +#define DEBUG_ALL 0xff +int osiop_debug = 0; /*DEBUG_ALL;*/ +int osiopsync_debug = 0; +int osiopdma_hits = 1; +int osiopstarts = 0; +int osiopints = 0; +int osiopphmm = 0; +int osiop_trix = 0; +#define OSIOP_TRACE_SIZE 128 +#define OSIOP_TRACE(a,b,c,d) do { \ + osiop_trbuf[osiop_trix + 0] = (a); \ + osiop_trbuf[osiop_trix + 1] = (b); \ + osiop_trbuf[osiop_trix + 2] = (c); \ + osiop_trbuf[osiop_trix + 3] = (d); \ + osiop_trix = (osiop_trix + 4) & (OSIOP_TRACE_SIZE - 1); \ +} while (0) +u_int8_t osiop_trbuf[OSIOP_TRACE_SIZE]; +void osiop_dump_trace(void); +void osiop_dump_acb(struct osiop_acb *); +void osiop_dump(struct osiop_softc *); +#else +#define OSIOP_TRACE(a,b,c,d) +#endif + +struct cfdriver osiop_cd = { + NULL, "osiop", DV_DULL +}; + +struct scsi_adapter osiop_adapter = { + osiop_scsicmd, + osiop_minphys, + NULL, + NULL, +}; + +struct scsi_device osiop_dev = { + NULL, + NULL, + NULL, + NULL, +}; + +void +osiop_attach(sc) + struct osiop_softc *sc; +{ + struct osiop_acb *acb; + bus_dma_segment_t seg; + int nseg; + int i, err; + + /* + * Allocate and map DMA-safe memory for the script. + */ + err = bus_dmamem_alloc(sc->sc_dmat, PAGE_SIZE, PAGE_SIZE, 0, + &seg, 1, &nseg, BUS_DMA_NOWAIT); + if (err) { + printf(": failed to allocate script memory, err=%d\n", err); + return; + } + err = bus_dmamem_map(sc->sc_dmat, &seg, nseg, PAGE_SIZE, + (caddr_t *)&sc->sc_script, BUS_DMA_NOWAIT | BUS_DMA_COHERENT); + if (err) { + printf(": failed to map script memory, err=%d\n", err); + return; + } + err = bus_dmamap_create(sc->sc_dmat, PAGE_SIZE, 1, PAGE_SIZE, 0, + BUS_DMA_NOWAIT, &sc->sc_scrdma); + if (err) { + printf(": failed to create script map, err=%d\n", err); + return; + } + err = bus_dmamap_load(sc->sc_dmat, sc->sc_scrdma, + sc->sc_script, PAGE_SIZE, NULL, BUS_DMA_NOWAIT); + if (err) { + printf(": failed to load script map, err=%d\n", err); + return; + } + bzero(sc->sc_script, PAGE_SIZE); + + /* + * Copy and sync script + */ + memcpy(sc->sc_script, osiop_script, sizeof(osiop_script)); + bus_dmamap_sync(sc->sc_dmat, sc->sc_scrdma, 0, sizeof(osiop_script), + BUS_DMASYNC_PREWRITE); + + /* + * Allocate and map DMA-safe memory for the script data structure. + */ + err = bus_dmamem_alloc(sc->sc_dmat, + sizeof(struct osiop_ds) * OSIOP_NACB, PAGE_SIZE, 0, + &seg, 1, &nseg, BUS_DMA_NOWAIT); + if (err) { + printf(": failed to allocate ds memory, err=%d\n", err); + return; + } + err = bus_dmamem_map(sc->sc_dmat, &seg, nseg, + sizeof(struct osiop_ds) * OSIOP_NACB, (caddr_t *)&sc->sc_ds, + BUS_DMA_NOWAIT | BUS_DMA_COHERENT); + if (err) { + printf(": failed to map ds memory, err=%d\n", err); + return; + } + err = bus_dmamap_create(sc->sc_dmat, + sizeof(struct osiop_ds) * OSIOP_NACB, 1, + sizeof(struct osiop_ds) * OSIOP_NACB, 0, + BUS_DMA_NOWAIT, &sc->sc_dsdma); + if (err) { + printf(": failed to create ds map, err=%d\n", err); + return; + } + err = bus_dmamap_load(sc->sc_dmat, sc->sc_dsdma, sc->sc_ds, + sizeof(struct osiop_ds) * OSIOP_NACB, NULL, BUS_DMA_NOWAIT); + if (err) { + printf(": failed to load ds map, err=%d\n", err); + return; + } + bzero(sc->sc_ds, sizeof(struct osiop_ds) * OSIOP_NACB); + + acb = malloc(sizeof(struct osiop_acb) * OSIOP_NACB, + M_DEVBUF, M_NOWAIT); + if (acb == NULL) { + printf(": can't allocate memory for acb\n"); + return; + } + bzero(acb, sizeof(struct osiop_acb) * OSIOP_NACB); + sc->sc_acb = acb; + sc->sc_cfflags = sc->sc_dev.dv_cfdata->cf_flags; + sc->sc_nexus = NULL; + sc->sc_active = 0; + memset(sc->sc_tinfo, 0, sizeof(sc->sc_tinfo)); + + /* Initialize command block queue */ + TAILQ_INIT(&sc->ready_list); + TAILQ_INIT(&sc->nexus_list); + TAILQ_INIT(&sc->free_list); + + /* Initialize each command block */ + for (i = 0; i < OSIOP_NACB; i++) { + bus_addr_t dsa; + + /* XXX How much size is required for each command block? */ + err = bus_dmamap_create(sc->sc_dmat, PAGE_SIZE, 1, PAGE_SIZE, + 0, BUS_DMA_NOWAIT, &acb->cmddma); + if (err) { + printf(": failed to create cmddma map, err=%d\n", err); + return; + } + err = bus_dmamap_create(sc->sc_dmat, OSIOP_MAX_XFER, OSIOP_NSG, + OSIOP_MAX_XFER, 0, BUS_DMA_NOWAIT, &acb->datadma); + if (err) { + printf(": failed to create datadma map, err=%d\n", + err); + return; + } + + acb->sc = sc; + acb->ds = &sc->sc_ds[i]; + acb->dsoffset = sizeof(struct osiop_ds) * i; + + dsa = sc->sc_dsdma->dm_segs[0].ds_addr + acb->dsoffset; + acb->ds->id.addr = dsa + OSIOP_DSIDOFF; + acb->ds->status.count = 1; + acb->ds->status.addr = dsa + OSIOP_DSSTATOFF; + acb->ds->msg.count = 1; + acb->ds->msg.addr = dsa + OSIOP_DSMSGOFF; + acb->ds->msgin.count = 1; + acb->ds->msgin.addr = dsa + OSIOP_DSMSGINOFF; + acb->ds->extmsg.count = 1; + acb->ds->extmsg.addr = dsa + OSIOP_DSEXTMSGOFF; + acb->ds->synmsg.count = 3; + acb->ds->synmsg.addr = dsa + OSIOP_DSSYNMSGOFF; + TAILQ_INSERT_TAIL(&sc->free_list, acb, chain); + + acb++; + } + + printf(": NCR53C710 rev %d, %dMHz, SCSI ID %d\n", + osiop_read_1(sc, OSIOP_CTEST8) >> 4, sc->sc_clock_freq, sc->sc_id); + + /* + * Initialize all + */ + osiop_init(sc); + + /* + * Fill in the sc_link. + */ + sc->sc_link.adapter = &osiop_adapter; + sc->sc_link.adapter_softc = sc; + sc->sc_link.device = &osiop_dev; + sc->sc_link.openings = 4; + sc->sc_link.adapter_buswidth = OSIOP_NTGT; + sc->sc_link.adapter_target = sc->sc_id; + sc->sc_link.quirks = ADEV_NODOORLOCK; + + /* + * Now try to attach all the sub devices. + */ + config_found(&sc->sc_dev, &sc->sc_link, scsiprint); +} + +/* + * default minphys routine for osiop based controllers + */ +void +osiop_minphys(bp) + struct buf *bp; +{ + + if (bp->b_bcount > OSIOP_MAX_XFER) + bp->b_bcount = OSIOP_MAX_XFER; + minphys(bp); +} + +/* + * used by specific osiop controller + * + */ +int +osiop_scsicmd(xs) + struct scsi_xfer *xs; +{ + struct scsi_link *periph = xs->sc_link; + struct osiop_acb *acb; + struct osiop_softc *sc = periph->adapter_softc; + int err, flags, s; + + flags = xs->flags; + + /* XXXX ?? */ + if (flags & SCSI_DATA_UIO) + panic("osiop: scsi data uio requested"); + + /* XXXX ?? */ + if (sc->sc_nexus && flags & SCSI_POLL) +#if 0 + panic("osiop_scsicmd: busy"); +#else + printf("osiop_scsicmd: busy\n"); +#endif + + s = splbio(); + acb = TAILQ_FIRST(&sc->free_list); + if (acb != NULL) { + TAILQ_REMOVE(&sc->free_list, acb, chain); + } + else { +#ifdef DIAGNOSTIC + sc_print_addr(periph); + printf("unable to allocate acb\n"); + panic("osiop_scsipi_request"); +#endif + xs->error = XS_DRIVER_STUFFUP; + splx(s); + return (TRY_AGAIN_LATER); + } + + acb->flags = 0; + acb->status = ACB_S_READY; + acb->xs = xs; + + /* Setup DMA map for SCSI command buffer */ + err = bus_dmamap_load(sc->sc_dmat, acb->cmddma, + xs->cmd, xs->cmdlen, NULL, BUS_DMA_NOWAIT); + if (err) { + printf("%s: unable to load cmd DMA map: %d", + sc->sc_dev.dv_xname, err); + xs->error = XS_DRIVER_STUFFUP; + TAILQ_INSERT_TAIL(&sc->free_list, acb, chain); + scsi_done(xs); + splx(s); + return (COMPLETE); + } + + /* Setup DMA map for data buffer */ + if (xs->flags & (SCSI_DATA_IN | SCSI_DATA_OUT)) { + err = bus_dmamap_load(sc->sc_dmat, acb->datadma, + xs->data, xs->datalen, NULL, + BUS_DMA_NOWAIT | BUS_DMA_STREAMING | + ((xs->flags & SCSI_DATA_IN) ? + BUS_DMA_READ : BUS_DMA_WRITE)); + if (err) { + printf("%s: unable to load data DMA map: %d", + sc->sc_dev.dv_xname, err); + xs->error = XS_DRIVER_STUFFUP; + scsi_done(xs); + bus_dmamap_unload(sc->sc_dmat, acb->cmddma); + TAILQ_INSERT_TAIL(&sc->free_list, acb, chain); + splx(s); + return (COMPLETE); + } + bus_dmamap_sync(sc->sc_dmat, acb->datadma, + 0, xs->datalen, (xs->flags & SCSI_DATA_IN) ? + BUS_DMASYNC_PREREAD : BUS_DMASYNC_PREWRITE); + } + bus_dmamap_sync(sc->sc_dmat, acb->cmddma, 0, xs->cmdlen, + BUS_DMASYNC_PREWRITE); + + acb->cmdlen = xs->cmdlen; + acb->datalen = xs->datalen; +#ifdef OSIOP_DEBUG + acb->data = xs->data; +#endif + + TAILQ_INSERT_TAIL(&sc->ready_list, acb, chain); + + if (sc->sc_nexus == NULL) + osiop_sched(sc); + + splx(s); + + if (flags & SCSI_POLL || sc->sc_flags & OSIOP_NODMA) + osiop_poll(sc, acb); + + if ((xs->flags & ITSDONE) == 0) + return (SUCCESSFULLY_QUEUED); + else + return (COMPLETE); +} + +void +osiop_poll(sc, acb) + struct osiop_softc *sc; + struct osiop_acb *acb; +{ + struct scsi_xfer *xs = acb->xs; + int status, i, s, to; + u_int8_t istat, dstat, sstat0; + + s = splbio(); + to = xs->timeout / 1000; + if (!TAILQ_EMPTY(&sc->nexus_list)) + printf("%s: osiop_poll called with disconnected device\n", + sc->sc_dev.dv_xname); + for (;;) { + i = 1000; + while (((istat = osiop_read_1(sc, OSIOP_ISTAT)) & + (OSIOP_ISTAT_SIP | OSIOP_ISTAT_DIP)) == 0) { + if (i <= 0) { +#ifdef OSIOP_DEBUG + printf("waiting: tgt %d cmd %02x sbcl %02x" + " dsp %x (+%lx) dcmd %x" + " ds %p timeout %d\n", + xs->sc_link->target, + xs->cmd->opcode, + osiop_read_1(sc, OSIOP_SBCL), + osiop_read_4(sc, OSIOP_DSP), + osiop_read_4(sc, OSIOP_DSP) - + sc->sc_scrdma->dm_segs[0].ds_addr, + osiop_read_1(sc, OSIOP_DCMD), + acb->ds, acb->xs->timeout); +#endif + i = 1000; + to--; + if (to <= 0) { + osiop_reset(sc); + splx(s); + return; + } + } + delay(1000); + i--; + } + sstat0 = osiop_read_1(sc, OSIOP_SSTAT0); + dstat = osiop_read_1(sc, OSIOP_DSTAT); + if (osiop_checkintr(sc, istat, dstat, sstat0, &status)) { + if (acb != sc->sc_nexus) + printf("%s: osiop_poll disconnected device" + " completed\n", sc->sc_dev.dv_xname); + else if ((sc->sc_flags & OSIOP_INTDEFER) == 0) { + sc->sc_flags &= ~OSIOP_INTSOFF; + osiop_write_1(sc, OSIOP_SIEN, sc->sc_sien); + osiop_write_1(sc, OSIOP_DIEN, sc->sc_dien); + } + osiop_scsidone(sc->sc_nexus, status); + } + + if (xs->flags & ITSDONE) + break; + } + + splx(s); + return; +} + +/* + * start next command that's ready + */ +void +osiop_sched(sc) + struct osiop_softc *sc; +{ + struct scsi_link *periph; + struct osiop_acb *acb; + int i; + +#ifdef OSIOP_DEBUG + if (sc->sc_nexus != NULL) { + printf("%s: osiop_sched- nexus %p/%d ready %p/%d\n", + sc->sc_dev.dv_xname, sc->sc_nexus, + sc->sc_nexus->xs->sc_link->target, + sc->ready_list.tqh_first, + sc->ready_list.tqh_first->xs->sc_link->target); + return; + } +#endif + TAILQ_FOREACH(acb, &sc->ready_list, chain) { + periph = acb->xs->sc_link; + i = periph->target; + if ((sc->sc_tinfo[i].lubusy & (1 << periph->lun)) == 0) { + struct osiop_tinfo *ti; + + TAILQ_REMOVE(&sc->ready_list, acb, chain); + sc->sc_nexus = acb; + ti = &sc->sc_tinfo[i]; + ti->lubusy |= (1 << periph->lun); + break; + } + } + + if (acb == NULL) { +#ifdef OSIOP_DEBUG + printf("%s: osiop_sched didn't find ready command\n", + sc->sc_dev.dv_xname); +#endif + return; + } + + if (acb->xs->flags & SCSI_RESET) + osiop_reset(sc); + + sc->sc_active++; + osiop_select(sc); +} + +void +osiop_scsidone(acb, status) + struct osiop_acb *acb; + int status; +{ + struct scsi_xfer *xs; + struct scsi_link *periph; + struct osiop_softc *sc; + int dosched = 0; + +#ifdef DIAGNOSTIC + if (acb == NULL || acb->xs == NULL) { + printf("osiop_scsidone: NULL acb or scsi_xfer\n"); +#if defined(OSIOP_DEBUG) && defined(DDB) + Debugger(); +#endif + return; + } +#endif + xs = acb->xs; + sc = acb->sc; + periph = xs->sc_link; + +#ifdef OSIOP_DEBUG + if (acb->status != ACB_S_DONE) + printf("%s: acb not done (status %d)\n", + sc->sc_dev.dv_xname, acb->status); +#endif + + xs->status = status; + + switch (status) { + case SCSI_OK: + xs->error = XS_NOERROR; + break; + case SCSI_BUSY: + xs->error = XS_BUSY; + break; + case SCSI_CHECK: + bzero(&xs->sense, sizeof(struct scsi_sense_data)); + xs->error = XS_SENSE; + break; + case SCSI_OSIOP_NOCHECK: + /* + * don't check status, xs->error is already valid + */ + break; + case SCSI_OSIOP_NOSTATUS: + /* + * the status byte was not updated, cmd was + * aborted + */ + xs->error = XS_SELTIMEOUT; + break; + default: +#ifdef OSIOP_DEBUG + printf("%s: osiop_scsidone: unknown status code (0x%02x)\n", + sc->sc_dev.dv_xname, status); +#endif + xs->error = XS_DRIVER_STUFFUP; + break; + } + + if (xs->flags & (SCSI_DATA_IN | SCSI_DATA_OUT)) { + bus_dmamap_sync(sc->sc_dmat, acb->datadma, 0, acb->datalen, + (xs->flags & SCSI_DATA_IN) ? + BUS_DMASYNC_POSTREAD : BUS_DMASYNC_POSTWRITE); + bus_dmamap_unload(sc->sc_dmat, acb->datadma); + } + + bus_dmamap_sync(sc->sc_dmat, acb->cmddma, 0, acb->cmdlen, + BUS_DMASYNC_POSTWRITE); + bus_dmamap_unload(sc->sc_dmat, acb->cmddma); + + /* + * Remove the ACB from whatever queue it's on. We have to do a bit of + * a hack to figure out which queue it's on. Note that it is *not* + * necessary to cdr down the ready queue, but we must cdr down the + * nexus queue and see if it's there, so we can mark the unit as no + * longer busy. This code is sickening, but it works. + */ + if (acb == sc->sc_nexus) { + sc->sc_nexus = NULL; + sc->sc_tinfo[periph->target].lubusy &= + ~(1 << periph->lun); + if (!TAILQ_EMPTY(&sc->ready_list)) + dosched = 1; /* start next command */ + sc->sc_active--; + OSIOP_TRACE('d', 'a', status, 0); + } else if (sc->ready_list.tqh_last == &acb->chain.tqe_next) { + TAILQ_REMOVE(&sc->ready_list, acb, chain); + OSIOP_TRACE('d', 'r', status, 0); + } else { + struct osiop_acb *acb2; + TAILQ_FOREACH(acb2, &sc->nexus_list, chain) { + if (acb2 == acb) { + TAILQ_REMOVE(&sc->nexus_list, acb, chain); + sc->sc_tinfo[periph->target].lubusy &= + ~(1 << periph->lun); + sc->sc_active--; + break; + } + } + if (acb2 == NULL) { + if (acb->chain.tqe_next != NULL) { + TAILQ_REMOVE(&sc->ready_list, acb, chain); + sc->sc_active--; + } else { + printf("%s: can't find matching acb\n", + sc->sc_dev.dv_xname); +#ifdef DDB +#if 0 + Debugger(); +#endif +#endif + } + } + OSIOP_TRACE('d', 'n', status, 0); + } + /* Put it on the free list. */ + acb->status = ACB_S_FREE; + TAILQ_INSERT_TAIL(&sc->free_list, acb, chain); + sc->sc_tinfo[periph->target].cmds++; + + timeout_del(&xs->stimeout); + xs->resid = 0; + xs->flags |= ITSDONE; + scsi_done(xs); + + if (dosched && sc->sc_nexus == NULL) + osiop_sched(sc); +} + +void +osiop_abort(sc, where) + struct osiop_softc *sc; + const char *where; +{ + + printf("%s: abort %s: dstat %02x, sstat0 %02x sbcl %02x\n", + sc->sc_dev.dv_xname, where, + osiop_read_1(sc, OSIOP_DSTAT), + osiop_read_1(sc, OSIOP_SSTAT0), + osiop_read_1(sc, OSIOP_SBCL)); + + /* XXX XXX XXX */ + if (sc->sc_active > 0) { + sc->sc_active = 0; + } +} + +void +osiop_init(sc) + struct osiop_softc *sc; +{ + int i, inhibit_sync, inhibit_disc; + + sc->sc_tcp[1] = 1000 / sc->sc_clock_freq; + sc->sc_tcp[2] = 1500 / sc->sc_clock_freq; + sc->sc_tcp[3] = 2000 / sc->sc_clock_freq; + sc->sc_minsync = sc->sc_tcp[1]; /* in 4ns units */ + + if (sc->sc_minsync < 25) + sc->sc_minsync = 25; + + if (sc->sc_clock_freq <= 25) { + sc->sc_dcntl |= OSIOP_DCNTL_CF_1; /* SCLK/1 */ + sc->sc_tcp[0] = sc->sc_tcp[1]; + } else if (sc->sc_clock_freq <= 37) { + sc->sc_dcntl |= OSIOP_DCNTL_CF_1_5; /* SCLK/1.5 */ + sc->sc_tcp[0] = sc->sc_tcp[2]; + } else if (sc->sc_clock_freq <= 50) { + sc->sc_dcntl |= OSIOP_DCNTL_CF_2; /* SCLK/2 */ + sc->sc_tcp[0] = sc->sc_tcp[3]; + } else { + sc->sc_dcntl |= OSIOP_DCNTL_CF_3; /* SCLK/3 */ + sc->sc_tcp[0] = 3000 / sc->sc_clock_freq; + } + + if ((sc->sc_cfflags & 0x10000) != 0) { + sc->sc_flags |= OSIOP_NODMA; +#ifdef OSIOP_DEBUG + printf("%s: DMA disabled; use polling\n", + sc->sc_dev.dv_xname); +#endif + } + + inhibit_sync = (sc->sc_cfflags & 0xff00) >> 8; /* XXX */ + inhibit_disc = sc->sc_cfflags & 0x00ff; /* XXX */ +#ifdef OSIOP_DEBUG + if (inhibit_sync != 0) + printf("%s: Inhibiting synchronous transfer: 0x%02x\n", + sc->sc_dev.dv_xname, inhibit_sync); + if (inhibit_disc != 0) + printf("%s: Inhibiting disconnect: 0x%02x\n", + sc->sc_dev.dv_xname, inhibit_disc); +#endif + for (i = 0; i < OSIOP_NTGT; i++) { + if (inhibit_sync & (1 << i)) + sc->sc_tinfo[i].flags |= TI_NOSYNC; + if (inhibit_disc & (1 << i)) + sc->sc_tinfo[i].flags |= TI_NODISC; + } + + osiop_resetbus(sc); + osiop_reset(sc); +} + +void +osiop_reset(sc) + struct osiop_softc *sc; +{ + struct osiop_acb *acb; + int i, s; + u_int8_t stat; + +#ifdef OSIOP_DEBUG + printf("%s: resetting chip\n", sc->sc_dev.dv_xname); +#endif + if (sc->sc_flags & OSIOP_ALIVE) + osiop_abort(sc, "reset"); + + s = splbio(); + + /* + * Reset the chip + * XXX - is this really needed? + */ + + /* abort current script */ + osiop_write_1(sc, OSIOP_ISTAT, + osiop_read_1(sc, OSIOP_ISTAT) | OSIOP_ISTAT_ABRT); + /* reset chip */ + osiop_write_1(sc, OSIOP_ISTAT, + osiop_read_1(sc, OSIOP_ISTAT) | OSIOP_ISTAT_RST); + delay(100); + osiop_write_1(sc, OSIOP_ISTAT, + osiop_read_1(sc, OSIOP_ISTAT) & ~OSIOP_ISTAT_RST); + delay(100); + + /* + * Set up various chip parameters + */ + osiop_write_1(sc, OSIOP_SCNTL0, + OSIOP_ARB_FULL | OSIOP_SCNTL0_EPC | OSIOP_SCNTL0_EPG); + osiop_write_1(sc, OSIOP_SCNTL1, OSIOP_SCNTL1_ESR); + osiop_write_1(sc, OSIOP_DCNTL, sc->sc_dcntl); + osiop_write_1(sc, OSIOP_DMODE, OSIOP_DMODE_BL4); + /* don't enable interrupts yet */ + osiop_write_1(sc, OSIOP_SIEN, 0x00); + osiop_write_1(sc, OSIOP_DIEN, 0x00); + osiop_write_1(sc, OSIOP_SCID, OSIOP_SCID_VALUE(sc->sc_id)); + osiop_write_1(sc, OSIOP_DWT, 0x00); + osiop_write_1(sc, OSIOP_CTEST0, osiop_read_1(sc, OSIOP_CTEST0) + | OSIOP_CTEST0_BTD | OSIOP_CTEST0_EAN); + osiop_write_1(sc, OSIOP_CTEST7, + osiop_read_1(sc, OSIOP_CTEST7) | sc->sc_ctest7); + + /* will need to re-negotiate sync xfers */ + for (i = 0; i < OSIOP_NTGT; i++) { + sc->sc_tinfo[i].state = NEG_INIT; + sc->sc_tinfo[i].period = 0; + sc->sc_tinfo[i].offset = 0; + } + + stat = osiop_read_1(sc, OSIOP_ISTAT); + if (stat & OSIOP_ISTAT_SIP) + osiop_read_1(sc, OSIOP_SSTAT0); + if (stat & OSIOP_ISTAT_DIP) + osiop_read_1(sc, OSIOP_DSTAT); + + splx(s); + + delay(osiop_reset_delay * 1000); + + s = splbio(); + if (sc->sc_nexus != NULL) { + sc->sc_nexus->xs->error = + (sc->sc_nexus->flags & ACB_F_TIMEOUT) ? + XS_TIMEOUT : XS_RESET; + sc->sc_nexus->status = ACB_S_DONE; + sc->sc_nexus->flags = 0; + osiop_scsidone(sc->sc_nexus, SCSI_OSIOP_NOCHECK); + } + while ((acb = TAILQ_FIRST(&sc->nexus_list)) != NULL) { + acb->xs->error = (acb->flags & ACB_F_TIMEOUT) ? + XS_TIMEOUT : XS_RESET; + acb->status = ACB_S_DONE; + acb->flags = 0; + osiop_scsidone(acb, SCSI_OSIOP_NOCHECK); + } + splx(s); + + sc->sc_flags &= ~(OSIOP_INTDEFER | OSIOP_INTSOFF); + /* enable SCSI and DMA interrupts */ + sc->sc_sien = OSIOP_SIEN_M_A | OSIOP_SIEN_STO | /*OSIOP_SIEN_SEL |*/ + OSIOP_SIEN_SGE | OSIOP_SIEN_UDC | OSIOP_SIEN_RST | OSIOP_SIEN_PAR; + sc->sc_dien = OSIOP_DIEN_BF | OSIOP_DIEN_ABRT | OSIOP_DIEN_SIR | + /*OSIOP_DIEN_WTD |*/ OSIOP_DIEN_IID; + osiop_write_1(sc, OSIOP_SIEN, sc->sc_sien); + osiop_write_1(sc, OSIOP_DIEN, sc->sc_dien); +} + +void +osiop_resetbus(sc) + struct osiop_softc *sc; +{ + + osiop_write_1(sc, OSIOP_SIEN, 0); + osiop_write_1(sc, OSIOP_SCNTL1, + osiop_read_1(sc, OSIOP_SCNTL1) | OSIOP_SCNTL1_RST); + delay(25); + osiop_write_1(sc, OSIOP_SCNTL1, + osiop_read_1(sc, OSIOP_SCNTL1) & ~OSIOP_SCNTL1_RST); +} + +/* + * Setup Data Storage for 53C710 and start SCRIPTS processing + */ + +void +osiop_start(sc) + struct osiop_softc *sc; +{ + struct osiop_acb *acb = sc->sc_nexus; + struct osiop_ds *ds = acb->ds; + struct scsi_xfer *xs = acb->xs; + bus_dmamap_t dsdma = sc->sc_dsdma, datadma = acb->datadma; + struct osiop_tinfo *ti; + int target = xs->sc_link->target; + int lun = xs->sc_link->lun; + int disconnect, i; + +#ifdef OSIOP_DEBUG + if (osiop_debug & DEBUG_DISC && + osiop_read_1(sc, OSIOP_SBCL) & OSIOP_BSY) { + printf("ACK! osiop was busy: script %p dsa %p active %d\n", + sc->sc_script, acb->ds, sc->sc_active); + printf("istat %02x sfbr %02x lcrc %02x sien %02x dien %02x\n", + osiop_read_1(sc, OSIOP_ISTAT), + osiop_read_1(sc, OSIOP_SFBR), + osiop_read_1(sc, OSIOP_LCRC), + osiop_read_1(sc, OSIOP_SIEN), + osiop_read_1(sc, OSIOP_DIEN)); +#ifdef DDB +#if 0 + Debugger(); +#endif +#endif + } +#endif + +#ifdef OSIOP_DEBUG + if (acb->status != ACB_S_READY) + panic("osiop_start: non-ready cmd in acb"); +#endif + + acb->intstat = 0; + + ds->cmd.count = acb->cmdlen; + ds->cmd.addr = acb->cmddma->dm_segs[0].ds_addr; + + ti = &sc->sc_tinfo[target]; + ds->scsi_addr = ((1 << 16) << target) | (ti->sxfer << 8); + + disconnect = (xs->cmd->opcode != REQUEST_SENSE) && + (ti->flags & TI_NODISC) == 0; + + ds->msgout[0] = MSG_IDENTIFY(lun, disconnect); + ds->id.count = 1; + ds->stat[0] = SCSI_OSIOP_NOSTATUS; /* set invalid status */ + ds->msgbuf[0] = ds->msgbuf[1] = MSG_INVALID; + memset(&ds->data, 0, sizeof(ds->data)); + + /* + * Negotiate wide is the initial negotiation state; since the 53c710 + * doesn't do wide transfers, just begin the synchronous transfer + * negotation here. + */ + if (ti->state == NEG_INIT) { + if ((ti->flags & TI_NOSYNC) != 0) { + ti->state = NEG_DONE; + ti->period = 0; + ti->offset = 0; + osiop_update_xfer_mode(sc, target); +#ifdef OSIOP_DEBUG + if (osiopsync_debug) + printf("Forcing target %d asynchronous\n", + target); +#endif + } else { + ds->msgbuf[2] = MSG_INVALID; + ds->msgout[1] = MSG_EXTENDED; + ds->msgout[2] = MSG_EXT_SDTR_LEN; + ds->msgout[3] = MSG_EXT_SDTR; + ds->msgout[4] = sc->sc_minsync; + ds->msgout[5] = OSIOP_MAX_OFFSET; + ds->id.count = MSG_EXT_SDTR_LEN + 3; + ti->state = NEG_WAITS; +#ifdef OSIOP_DEBUG + if (osiopsync_debug) + printf("Sending sync request to target %d\n", + target); +#endif + } + } + + acb->curaddr = 0; + acb->curlen = 0; + + /* + * Build physical DMA addresses for scatter/gather I/O + */ + if (xs->flags & (SCSI_DATA_IN | SCSI_DATA_OUT)) { + for (i = 0; i < datadma->dm_nsegs; i++) { + ds->data[i].count = datadma->dm_segs[i].ds_len; + ds->data[i].addr = datadma->dm_segs[i].ds_addr; + } + } + + /* sync script data structure */ + bus_dmamap_sync(sc->sc_dmat, dsdma, + acb->dsoffset, sizeof(struct osiop_ds), + BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE); + + acb->status = ACB_S_ACTIVE; + + /* handle timeout */ + timeout_set(&xs->stimeout, osiop_timeout, acb); + if ((xs->flags & SCSI_POLL) == 0) { + /* start expire timer */ + timeout_add(&xs->stimeout, (xs->timeout/1000) * hz); + } +#ifdef OSIOP_DEBUG + if (osiop_debug & DEBUG_DISC && + osiop_read_1(sc, OSIOP_SBCL) & OSIOP_BSY) { + printf("ACK! osiop was busy at start: " + "script %p dsa %p active %d\n", + sc->sc_script, acb->ds, sc->sc_active); +#ifdef DDB +#if 0 + Debugger(); +#endif +#endif + } +#endif + if (TAILQ_EMPTY(&sc->nexus_list)) { + if (osiop_read_1(sc, OSIOP_ISTAT) & OSIOP_ISTAT_CON) + printf("%s: osiop_select while connected?\n", + sc->sc_dev.dv_xname); + osiop_write_4(sc, OSIOP_TEMP, 0); + osiop_write_1(sc, OSIOP_SBCL, ti->sbcl); + osiop_write_4(sc, OSIOP_DSA, + dsdma->dm_segs[0].ds_addr + acb->dsoffset); + osiop_write_4(sc, OSIOP_DSP, + sc->sc_scrdma->dm_segs[0].ds_addr + Ent_scripts); + OSIOP_TRACE('s', 1, 0, 0); + } else { + if ((osiop_read_1(sc, OSIOP_ISTAT) & OSIOP_ISTAT_CON) == 0) { + osiop_write_1(sc, OSIOP_ISTAT, OSIOP_ISTAT_SIGP); + OSIOP_TRACE('s', 2, 0, 0); + } else { + OSIOP_TRACE('s', 3, + osiop_read_1(sc, OSIOP_ISTAT), 0); + } + } +#ifdef OSIOP_DEBUG + osiopstarts++; +#endif +} + +/* + * Process a DMA or SCSI interrupt from the 53C710 SIOP + */ + +int +osiop_checkintr(sc, istat, dstat, sstat0, status) + struct osiop_softc *sc; + u_int8_t istat; + u_int8_t dstat; + u_int8_t sstat0; + int *status; +{ + struct osiop_acb *acb = sc->sc_nexus; + struct osiop_ds *ds; + bus_dmamap_t dsdma = sc->sc_dsdma; + bus_addr_t scraddr = sc->sc_scrdma->dm_segs[0].ds_addr; + int target = 0; + int dfifo, dbc, intcode, sstat1; + + dfifo = osiop_read_1(sc, OSIOP_DFIFO); + dbc = osiop_read_4(sc, OSIOP_DBC) & 0x00ffffff; + sstat1 = osiop_read_1(sc, OSIOP_SSTAT1); + osiop_write_1(sc, OSIOP_CTEST8, + osiop_read_1(sc, OSIOP_CTEST8) | OSIOP_CTEST8_CLF); + while ((osiop_read_1(sc, OSIOP_CTEST1) & OSIOP_CTEST1_FMT) != + OSIOP_CTEST1_FMT) + ; + osiop_write_1(sc, OSIOP_CTEST8, + osiop_read_1(sc, OSIOP_CTEST8) & ~OSIOP_CTEST8_CLF); + intcode = osiop_read_4(sc, OSIOP_DSPS); +#ifdef OSIOP_DEBUG + osiopints++; + if (osiop_read_4(sc, OSIOP_DSP) != 0 && + (osiop_read_4(sc, OSIOP_DSP) < scraddr || + osiop_read_4(sc, OSIOP_DSP) >= scraddr + sizeof(osiop_script))) { + printf("%s: dsp not within script dsp %x scripts %lx:%lx", + sc->sc_dev.dv_xname, + osiop_read_4(sc, OSIOP_DSP), + scraddr, scraddr + sizeof(osiop_script)); + printf(" istat %x dstat %x sstat0 %x\n", istat, dstat, sstat0); +#ifdef DDB + Debugger(); +#endif + } +#endif + OSIOP_TRACE('i', dstat, istat, (istat & OSIOP_ISTAT_DIP) ? + intcode & 0xff : sstat0); + + if (acb != NULL) { /* XXX */ + ds = acb->ds; + bus_dmamap_sync(sc->sc_dmat, dsdma, + acb->dsoffset, sizeof(struct osiop_ds), + BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE); +#ifdef OSIOP_DEBUG + if (acb->status != ACB_S_ACTIVE) + printf("osiop_checkintr: acb not active (status %d)\n", + acb->status); +#endif + } + + + if (dstat & OSIOP_DSTAT_SIR && intcode == A_ok) { + /* Normal completion status, or check condition */ + struct osiop_tinfo *ti; +#ifdef OSIOP_DEBUG + if (osiop_read_4(sc, OSIOP_DSA) != + dsdma->dm_segs[0].ds_addr + acb->dsoffset) { + printf("osiop: invalid dsa: %x %lx\n", + osiop_read_4(sc, OSIOP_DSA), + dsdma->dm_segs[0].ds_addr + acb->dsoffset); + panic("*** osiop DSA invalid ***"); + } +#endif + target = acb->xs->sc_link->target; + ti = &sc->sc_tinfo[target]; + if (ti->state == NEG_WAITS) { + if (ds->msgbuf[1] == MSG_INVALID) + printf("%s: target %d ignored sync request\n", + sc->sc_dev.dv_xname, target); + else if (ds->msgbuf[1] == MSG_MESSAGE_REJECT) + printf("%s: target %d rejected sync request\n", + sc->sc_dev.dv_xname, target); + ti->period = 0; + ti->offset = 0; + osiop_update_xfer_mode(sc, target); + ti->state = NEG_DONE; + } +#ifdef OSIOP_DEBUG + if (osiop_read_1(sc, OSIOP_SBCL) & OSIOP_BSY) { +#if 0 + printf("ACK! osiop was busy at end: " + "script %p dsa %p\n", &osiop_script, ds); +#ifdef DDB + Debugger(); +#endif +#endif + } + if (ds->msgbuf[0] != MSG_CMDCOMPLETE) + printf("%s: message was not COMMAND COMPLETE: %02x\n", + sc->sc_dev.dv_xname, ds->msgbuf[0]); +#endif + if (!TAILQ_EMPTY(&sc->nexus_list)) + osiop_write_1(sc, OSIOP_DCNTL, + osiop_read_1(sc, OSIOP_DCNTL) | OSIOP_DCNTL_STD); + *status = ds->stat[0]; + acb->status = ACB_S_DONE; + return (1); + } + if (dstat & OSIOP_DSTAT_SIR && intcode == A_int_syncmsg) { + target = acb->xs->sc_link->target; + if (ds->msgbuf[1] == MSG_EXTENDED && + ds->msgbuf[2] == MSG_EXT_SDTR_LEN && + ds->msgbuf[3] == MSG_EXT_SDTR) { + struct osiop_tinfo *ti = &sc->sc_tinfo[target]; +#ifdef OSIOP_DEBUG + if (osiopsync_debug) + printf("sync msg in: " + "%02x %02x %02x %02x %02x %02x\n", + ds->msgbuf[0], ds->msgbuf[1], + ds->msgbuf[2], ds->msgbuf[3], + ds->msgbuf[4], ds->msgbuf[5]); +#endif + ti->period = ds->msgbuf[4]; + ti->offset = ds->msgbuf[5]; + osiop_update_xfer_mode(sc, target); + + bus_dmamap_sync(sc->sc_dmat, dsdma, + acb->dsoffset, sizeof(struct osiop_ds), + BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE); + osiop_write_1(sc, OSIOP_SXFER, ti->sxfer); + osiop_write_1(sc, OSIOP_SBCL, ti->sbcl); + if (ti->state == NEG_WAITS) { + ti->state = NEG_DONE; + osiop_write_4(sc, OSIOP_DSP, + scraddr + Ent_clear_ack); + return (0); + } + osiop_write_1(sc, OSIOP_DCNTL, + osiop_read_1(sc, OSIOP_DCNTL) | OSIOP_DCNTL_STD); + ti->state = NEG_DONE; + return (0); + } + /* XXX - not SDTR message */ + } + if (sstat0 & OSIOP_SSTAT0_M_A) { + /* Phase mismatch */ +#ifdef OSIOP_DEBUG + osiopphmm++; + if (acb == NULL) + printf("%s: Phase mismatch with no active command?\n", + sc->sc_dev.dv_xname); +#endif + if (acb->datalen > 0) { + int adjust = (dfifo - (dbc & 0x7f)) & 0x7f; + if (sstat1 & OSIOP_SSTAT1_ORF) + adjust++; + if (sstat1 & OSIOP_SSTAT1_OLF) + adjust++; + acb->curaddr = osiop_read_4(sc, OSIOP_DNAD) - adjust; + acb->curlen = dbc + adjust; +#ifdef OSIOP_DEBUG + if (osiop_debug & DEBUG_DISC) { + printf("Phase mismatch: curaddr %lx " + "curlen %lx dfifo %x dbc %x sstat1 %x " + "adjust %x sbcl %x starts %d acb %p\n", + acb->curaddr, acb->curlen, dfifo, + dbc, sstat1, adjust, + osiop_read_1(sc, OSIOP_SBCL), + osiopstarts, acb); + if (ds->data[1].count != 0) { + int i; + for (i = 0; ds->data[i].count != 0; i++) + printf("chain[%d] " + "addr %x len %x\n", i, + ds->data[i].addr, + ds->data[i].count); + } + bus_dmamap_sync(sc->sc_dmat, dsdma, + acb->dsoffset, sizeof(struct osiop_ds), + BUS_DMASYNC_PREREAD | + BUS_DMASYNC_PREWRITE); + } +#endif + } +#ifdef OSIOP_DEBUG + OSIOP_TRACE('m', osiop_read_1(sc, OSIOP_SBCL), + osiop_read_4(sc, OSIOP_DSP) >> 8, + osiop_read_4(sc, OSIOP_DSP)); + if (osiop_debug & DEBUG_PHASE) + printf("Phase mismatch: %x dsp +%lx dcmd %x\n", + osiop_read_1(sc, OSIOP_SBCL), + osiop_read_4(sc, OSIOP_DSP) - scraddr, + osiop_read_4(sc, OSIOP_DBC)); +#endif + if ((osiop_read_1(sc, OSIOP_SBCL) & OSIOP_REQ) == 0) { + printf("Phase mismatch: " + "REQ not asserted! %02x dsp %x\n", + osiop_read_1(sc, OSIOP_SBCL), + osiop_read_4(sc, OSIOP_DSP)); +#if defined(OSIOP_DEBUG) && defined(DDB) + /*Debugger(); XXX is*/ +#endif + } + switch (OSIOP_PHASE(osiop_read_1(sc, OSIOP_SBCL))) { + case DATA_OUT_PHASE: + case DATA_IN_PHASE: + case STATUS_PHASE: + case COMMAND_PHASE: + case MSG_IN_PHASE: + case MSG_OUT_PHASE: + osiop_write_4(sc, OSIOP_DSP, scraddr + Ent_switch); + break; + default: + printf("%s: invalid phase\n", sc->sc_dev.dv_xname); + goto bad_phase; + } + return (0); + } + if (sstat0 & OSIOP_SSTAT0_STO) { + /* Select timed out */ +#ifdef OSIOP_DEBUG + if (acb == NULL) + printf("%s: Select timeout with no active command?\n", + sc->sc_dev.dv_xname); + if (osiop_read_1(sc, OSIOP_SBCL) & OSIOP_BSY) { + printf("ACK! osiop was busy at timeout: " + "script %p dsa %lx\n", sc->sc_script, + dsdma->dm_segs[0].ds_addr + acb->dsoffset); + printf(" sbcl %x sdid %x " + "istat %x dstat %x sstat0 %x\n", + osiop_read_1(sc, OSIOP_SBCL), + osiop_read_1(sc, OSIOP_SDID), + istat, dstat, sstat0); + if ((osiop_read_1(sc, OSIOP_SBCL) & OSIOP_BSY) == 0) { + printf("Yikes, it's not busy now!\n"); +#if 0 + *status = SCSI_OSIOP_NOSTATUS; + if (!TAILQ_EMPTY(&sc->nexus_list)) + osiop_write_4(sc, OSIOP_DSP, + scraddr + Ent_wait_reselect); + return (1); +#endif + } +#if 0 + osiop_write_1(sc, OSIOP_DCNTL, + osiop_read_1(sc, OSIOP_DCNTL) | OSIOP_DCNTL_STD); +#endif +#ifdef DDB + Debugger(); +#endif + return (0); + } +#endif + acb->status = ACB_S_DONE; + *status = SCSI_OSIOP_NOSTATUS; + acb->xs->error = XS_SELTIMEOUT; + if (!TAILQ_EMPTY(&sc->nexus_list)) + osiop_write_4(sc, OSIOP_DSP, + scraddr + Ent_wait_reselect); + return (1); + } + if (acb != NULL) + target = acb->xs->sc_link->target; + else + target = sc->sc_id; + if (sstat0 & OSIOP_SSTAT0_UDC) { +#ifdef OSIOP_DEBUG + if (acb == NULL) + printf("%s: Unexpected disconnect " + "with no active command?\n", sc->sc_dev.dv_xname); + printf("%s: target %d disconnected unexpectedly\n", + sc->sc_dev.dv_xname, target); +#endif +#if 0 + osiop_abort(sc, "osiop_chkintr"); +#endif + *status = SCSI_CHECK; + if (!TAILQ_EMPTY(&sc->nexus_list)) + osiop_write_4(sc, OSIOP_DSP, + scraddr + Ent_wait_reselect); + return (acb != NULL); + } + if (dstat & OSIOP_DSTAT_SIR && + (intcode == A_int_disc || intcode == A_int_disc_wodp)) { + /* Disconnect */ + if (acb == NULL) { + printf("%s: Disconnect with no active command?\n", + sc->sc_dev.dv_xname); + return (0); + } +#ifdef OSIOP_DEBUG + if (osiop_debug & DEBUG_DISC) { + printf("%s: ID %02x disconnected TEMP %x (+%lx) " + "curaddr %lx curlen %lx buf %x len %x dfifo %x " + "dbc %x sstat1 %x starts %d acb %p\n", + sc->sc_dev.dv_xname, 1 << target, + osiop_read_4(sc, OSIOP_TEMP), + (osiop_read_4(sc, OSIOP_TEMP) != 0) ? + osiop_read_4(sc, OSIOP_TEMP) - scraddr : 0, + acb->curaddr, acb->curlen, + ds->data[0].addr, ds->data[0].count, + dfifo, dbc, sstat1, osiopstarts, acb); + bus_dmamap_sync(sc->sc_dmat, dsdma, + acb->dsoffset, sizeof(struct osiop_ds), + BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE); + } +#endif + /* + * XXXX need to update curaddr/curlen to reflect + * current data transferred. If device disconnected in + * the middle of a DMA block, they should already be set + * by the phase change interrupt. If the disconnect + * occurs on a DMA block boundary, we have to figure out + * which DMA block it was. + */ + if (acb->datalen > 0 && + osiop_read_4(sc, OSIOP_TEMP) != 0) { + long n = osiop_read_4(sc, OSIOP_TEMP) - scraddr; + + if (acb->curlen != 0 && + acb->curlen != ds->data[0].count) + printf("%s: curaddr/curlen already set? " + "n %lx iob %lx/%lx chain[0] %x/%x\n", + sc->sc_dev.dv_xname, n, + acb->curaddr, acb->curlen, + ds->data[0].addr, ds->data[0].count); + if (n < Ent_datain) + n = (n - Ent_dataout) / 16; + else + n = (n - Ent_datain) / 16; + if (n <= 0 && n > OSIOP_NSG) + printf("TEMP invalid %ld\n", n); + else { + acb->curaddr = ds->data[n].addr; + acb->curlen = ds->data[n].count; + } +#ifdef OSIOP_DEBUG + if (osiop_debug & DEBUG_DISC) { + printf("%s: TEMP offset %ld", + sc->sc_dev.dv_xname, n); + printf(" curaddr %lx curlen %lx\n", + acb->curaddr, acb->curlen); + } +#endif + } + /* + * If data transfer was interrupted by disconnect, curaddr + * and curlen should reflect the point of interruption. + * Adjust the DMA chain so that the data transfer begins + * at the appropriate place upon reselection. + * XXX This should only be done on save data pointer message? + */ + if (acb->curlen > 0) { + int i, j; + +#ifdef OSIOP_DEBUG + if (osiop_debug & DEBUG_DISC) + printf("%s: adjusting DMA chain\n", + sc->sc_dev.dv_xname); + if (intcode == A_int_disc_wodp) + printf("%s: ID %02x disconnected " + "without Save Data Pointers\n", + sc->sc_dev.dv_xname, 1 << target); +#endif + for (i = 0; i < OSIOP_NSG; i++) { + if (ds->data[i].count == 0) + break; + if (acb->curaddr >= ds->data[i].addr && + acb->curaddr < + (ds->data[i].addr + ds->data[i].count)) + break; + } + if (i >= OSIOP_NSG || ds->data[i].count == 0) { + printf("couldn't find saved data pointer: " + "curaddr %lx curlen %lx i %d\n", + acb->curaddr, acb->curlen, i); +#ifdef DDB + Debugger(); +#endif + } +#ifdef OSIOP_DEBUG + if (osiop_debug & DEBUG_DISC) + printf(" chain[0]: %x/%x -> %lx/%lx\n", + ds->data[0].addr, ds->data[0].count, + acb->curaddr, acb->curlen); +#endif + ds->data[0].addr = acb->curaddr; + ds->data[0].count = acb->curlen; + for (j = 1, i = i + 1; + i < OSIOP_NSG && ds->data[i].count > 0; + i++, j++) { +#ifdef OSIOP_DEBUG + if (osiop_debug & DEBUG_DISC) + printf(" chain[%d]: %x/%x -> %x/%x\n", j, + ds->data[j].addr, ds->data[j].count, + ds->data[i].addr, ds->data[i].count); +#endif + ds->data[j].addr = ds->data[i].addr; + ds->data[j].count = ds->data[i].count; + } + if (j < OSIOP_NSG) { + ds->data[j].addr = 0; + ds->data[j].count = 0; + } + bus_dmamap_sync(sc->sc_dmat, dsdma, + acb->dsoffset, sizeof(struct osiop_ds), + BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE); + } + sc->sc_tinfo[target].dconns++; + /* + * add nexus to waiting list + * clear nexus + * try to start another command for another target/lun + */ + acb->intstat = sc->sc_flags & OSIOP_INTSOFF; + TAILQ_INSERT_TAIL(&sc->nexus_list, acb, chain); + sc->sc_nexus = NULL; /* no current device */ + osiop_write_4(sc, OSIOP_DSP, scraddr + Ent_wait_reselect); + /* XXXX start another command ? */ + if (!TAILQ_EMPTY(&sc->ready_list)) + osiop_sched(sc); + return (0); + } + if (dstat & OSIOP_DSTAT_SIR && intcode == A_int_reconnect) { + int reselid = ffs(osiop_read_4(sc, OSIOP_SCRATCH) & 0xff) - 1; + int reselun = osiop_read_1(sc, OSIOP_SFBR) & 0x07; +#ifdef OSIOP_DEBUG + u_int8_t resmsg; +#endif + + /* Reconnect */ + /* XXXX save current SBCL */ + sc->sc_sstat1 = osiop_read_1(sc, OSIOP_SBCL); +#ifdef OSIOP_DEBUG + if (osiop_debug & DEBUG_DISC) + printf("%s: target ID %02x reselected dsps %x\n", + sc->sc_dev.dv_xname, reselid, intcode); + resmsg = osiop_read_1(sc, OSIOP_SFBR); + if (!MSG_ISIDENTIFY(resmsg)) + printf("%s: Reselect message in was not identify: " + "%02x\n", sc->sc_dev.dv_xname, resmsg); +#endif + if (sc->sc_nexus != NULL) { + struct scsi_link *periph = + sc->sc_nexus->xs->sc_link; +#ifdef OSIOP_DEBUG + if (osiop_debug & DEBUG_DISC) + printf("%s: reselect ID %02x w/active\n", + sc->sc_dev.dv_xname, reselid); +#endif + TAILQ_INSERT_HEAD(&sc->ready_list, + sc->sc_nexus, chain); + sc->sc_tinfo[periph->target].lubusy + &= ~(1 << periph->lun); + sc->sc_active--; + } + /* + * locate acb of reselecting device + * set sc->sc_nexus to acb + */ + TAILQ_FOREACH(acb, &sc->nexus_list, chain) { + struct scsi_link *periph = acb->xs->sc_link; + if (reselid != periph->target || + reselun != periph->lun) { + continue; + } + TAILQ_REMOVE(&sc->nexus_list, acb, chain); + sc->sc_nexus = acb; + sc->sc_flags |= acb->intstat; + acb->intstat = 0; + osiop_write_4(sc, OSIOP_DSA, + dsdma->dm_segs[0].ds_addr + acb->dsoffset); + osiop_write_1(sc, OSIOP_SXFER, + sc->sc_tinfo[reselid].sxfer); + osiop_write_1(sc, OSIOP_SBCL, + sc->sc_tinfo[reselid].sbcl); + break; + } + if (acb == NULL) { + printf("%s: target ID %02x reselect nexus_list %p\n", + sc->sc_dev.dv_xname, reselid, + TAILQ_FIRST(&sc->nexus_list)); + panic("unable to find reselecting device"); + } + + osiop_write_4(sc, OSIOP_TEMP, 0); + osiop_write_1(sc, OSIOP_DCNTL, + osiop_read_1(sc, OSIOP_DCNTL) | OSIOP_DCNTL_STD); + return (0); + } + if (dstat & OSIOP_DSTAT_SIR && intcode == A_int_connect) { +#ifdef OSIOP_DEBUG + u_int8_t ctest2 = osiop_read_1(sc, OSIOP_CTEST2); + + /* reselect was interrupted (by Sig_P or select) */ + if (osiop_debug & DEBUG_DISC || + (ctest2 & OSIOP_CTEST2_SIGP) == 0) + printf("%s: reselect interrupted (Sig_P?) " + "scntl1 %x ctest2 %x sfbr %x istat %x/%x\n", + sc->sc_dev.dv_xname, + osiop_read_1(sc, OSIOP_SCNTL1), ctest2, + osiop_read_1(sc, OSIOP_SFBR), istat, + osiop_read_1(sc, OSIOP_ISTAT)); +#endif + /* XXX assumes it was not select */ + if (sc->sc_nexus == NULL) { +#ifdef OSIOP_DEBUG + printf("%s: reselect interrupted, sc_nexus == NULL\n", + sc->sc_dev.dv_xname); +#if 0 + osiop_dump(sc); +#ifdef DDB + Debugger(); +#endif +#endif +#endif + osiop_write_1(sc, OSIOP_DCNTL, + osiop_read_1(sc, OSIOP_DCNTL) | OSIOP_DCNTL_STD); + return (0); + } + target = sc->sc_nexus->xs->sc_link->target; + osiop_write_4(sc, OSIOP_TEMP, 0); + osiop_write_4(sc, OSIOP_DSA, + dsdma->dm_segs[0].ds_addr + sc->sc_nexus->dsoffset); + osiop_write_1(sc, OSIOP_SXFER, sc->sc_tinfo[target].sxfer); + osiop_write_1(sc, OSIOP_SBCL, sc->sc_tinfo[target].sbcl); + osiop_write_4(sc, OSIOP_DSP, scraddr + Ent_scripts); + return (0); + } + if (dstat & OSIOP_DSTAT_SIR && intcode == A_int_msgin) { + /* Unrecognized message in byte */ + if (acb == NULL) + printf("%s: Bad message-in with no active command?\n", + sc->sc_dev.dv_xname); + printf("%s: Unrecognized message in data " + "sfbr %x msg %x sbcl %x\n", sc->sc_dev.dv_xname, + osiop_read_1(sc, OSIOP_SFBR), ds->msgbuf[1], + osiop_read_1(sc, OSIOP_SBCL)); + /* what should be done here? */ + osiop_write_4(sc, OSIOP_DSP, scraddr + Ent_switch); + bus_dmamap_sync(sc->sc_dmat, dsdma, + acb->dsoffset, sizeof(struct osiop_ds), + BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE); + return (0); + } + if (dstat & OSIOP_DSTAT_SIR && intcode == A_int_status) { + /* Status phase wasn't followed by message in phase? */ + printf("%s: Status phase not followed by message in phase? " + "sbcl %x sbdl %x\n", sc->sc_dev.dv_xname, + osiop_read_1(sc, OSIOP_SBCL), + osiop_read_1(sc, OSIOP_SBDL)); + if (osiop_read_1(sc, OSIOP_SBCL) == 0xa7) { + /* It is now, just continue the script? */ + osiop_write_1(sc, OSIOP_DCNTL, + osiop_read_1(sc, OSIOP_DCNTL) | OSIOP_DCNTL_STD); + return (0); + } + } + if (dstat & OSIOP_DSTAT_SIR && sstat0 == 0) { + printf("OSIOP interrupt: %x sts %x msg %x %x sbcl %x\n", + intcode, ds->stat[0], ds->msgbuf[0], ds->msgbuf[1], + osiop_read_1(sc, OSIOP_SBCL)); + osiop_reset(sc); + *status = SCSI_OSIOP_NOSTATUS; + return (0); /* osiop_reset has cleaned up */ + } + if (sstat0 & OSIOP_SSTAT0_SGE) + printf("%s: SCSI Gross Error\n", sc->sc_dev.dv_xname); + if (sstat0 & OSIOP_SSTAT0_PAR) + printf("%s: Parity Error\n", sc->sc_dev.dv_xname); + if (dstat & OSIOP_DSTAT_IID) + printf("%s: Invalid instruction detected\n", + sc->sc_dev.dv_xname); + bad_phase: + /* + * temporary panic for unhandled conditions + * displays various things about the 53C710 status and registers + * then panics. + * XXXX need to clean this up to print out the info, reset, and continue + */ + printf("osiop_chkintr: target %x ds %p\n", target, ds); + printf("scripts %lx ds %lx dsp %x dcmd %x\n", scraddr, + sc->sc_dsdma->dm_segs[0].ds_addr + acb->dsoffset, + osiop_read_4(sc, OSIOP_DSP), + osiop_read_4(sc, OSIOP_DBC)); + printf("osiop_chkintr: istat %x dstat %x sstat0 %x " + "dsps %x dsa %x sbcl %x sts %x msg %x %x sfbr %x\n", + istat, dstat, sstat0, intcode, + osiop_read_4(sc, OSIOP_DSA), + osiop_read_1(sc, OSIOP_SBCL), + ds->stat[0], ds->msgbuf[0], ds->msgbuf[1], + osiop_read_1(sc, OSIOP_SFBR)); +#ifdef OSIOP_DEBUG + if (osiop_debug & DEBUG_DMA) + panic("osiop_chkintr: **** temp ****"); +#endif +#ifdef DDB + Debugger(); +#endif + osiop_reset(sc); /* hard reset */ + *status = SCSI_OSIOP_NOSTATUS; + acb->status = ACB_S_DONE; + return (0); /* osiop_reset cleaned up */ +} + +void +osiop_select(sc) + struct osiop_softc *sc; +{ + struct osiop_acb *acb = sc->sc_nexus; + +#ifdef OSIOP_DEBUG + if (osiop_debug & DEBUG_CMD) + printf("%s: select ", sc->sc_dev.dv_xname); +#endif + + if (acb->xs->flags & SCSI_POLL || sc->sc_flags & OSIOP_NODMA) { + sc->sc_flags |= OSIOP_INTSOFF; + sc->sc_flags &= ~OSIOP_INTDEFER; + if ((osiop_read_1(sc, OSIOP_ISTAT) & OSIOP_ISTAT_CON) == 0) { + osiop_write_1(sc, OSIOP_SIEN, 0); + osiop_write_1(sc, OSIOP_DIEN, 0); + } +#if 0 + } else if ((sc->sc_flags & OSIOP_INTDEFER) == 0) { + sc->sc_flags &= ~OSIOP_INTSOFF; + if ((osiop_read_1(sc, OSIOP_ISTAT) & OSIOP_ISTAT_CON) == 0) { + osiop_write_1(sc, OSIOP_SIEN, sc->sc_sien); + osiop_write_1(sc, OSIOP_DIEN, sc->sc_dien); + } +#endif + } +#ifdef OSIOP_DEBUG + if (osiop_debug & DEBUG_CMD) + printf("osiop_select: target %x cmd %02x ds %p\n", + acb->xs->sc_link->target, + acb->xs->cmd->opcode, sc->sc_nexus->ds); +#endif + + osiop_start(sc); + + return; +} + +/* + * 53C710 interrupt handler + */ + +void +osiop_intr(sc) + struct osiop_softc *sc; +{ + int status, s; + u_int8_t istat, dstat, sstat0; + + s = splbio(); + + istat = sc->sc_istat; + if ((istat & (OSIOP_ISTAT_SIP | OSIOP_ISTAT_DIP)) == 0) { + splx(s); + return; + } + + /* Got a valid interrupt on this device; set by MD handler */ + dstat = sc->sc_dstat; + sstat0 = sc->sc_sstat0; + sc->sc_istat = 0; +#ifdef OSIOP_DEBUG + if (!sc->sc_active) { + /* XXX needs sync */ + printf("%s: spurious interrupt? " + "istat %x dstat %x sstat0 %x nexus %p status %x\n", + sc->sc_dev.dv_xname, istat, dstat, sstat0, sc->sc_nexus, + (sc->sc_nexus != NULL) ? sc->sc_nexus->ds->stat[0] : 0); + } +#endif + +#ifdef OSIOP_DEBUG + if (osiop_debug & (DEBUG_INT|DEBUG_CMD)) { + /* XXX needs sync */ + printf("%s: intr istat %x dstat %x sstat0 %x dsps %x " + "sbcl %x dsp %x dcmd %x sts %x msg %x\n", + sc->sc_dev.dv_xname, + istat, dstat, sstat0, + osiop_read_4(sc, OSIOP_DSPS), + osiop_read_1(sc, OSIOP_SBCL), + osiop_read_4(sc, OSIOP_DSP), + osiop_read_4(sc, OSIOP_DBC), + (sc->sc_nexus != NULL) ? sc->sc_nexus->ds->stat[0] : 0, + (sc->sc_nexus != NULL) ? sc->sc_nexus->ds->msgbuf[0] : 0); + } +#endif + if (sc->sc_flags & OSIOP_INTDEFER) { + sc->sc_flags &= ~(OSIOP_INTDEFER | OSIOP_INTSOFF); + osiop_write_1(sc, OSIOP_SIEN, sc->sc_sien); + osiop_write_1(sc, OSIOP_DIEN, sc->sc_dien); + } + if (osiop_checkintr(sc, istat, dstat, sstat0, &status)) { +#if 0 + if (status == SCSI_OSIOP_NOSTATUS) + printf("osiop_intr: no valid status \n"); +#endif + if ((sc->sc_flags & (OSIOP_INTSOFF | OSIOP_INTDEFER)) != + OSIOP_INTSOFF) { +#if 0 + if (osiop_read_1(sc, OSIOP_SBCL) & OSIOP_BSY) { + struct scsi_link *periph; + + periph = sc->sc_nexus->xs->sc_link; + printf("%s: SCSI bus busy at completion" + " targ %d sbcl %02x sfbr %x lcrc " + "%02x dsp +%x\n", sc->sc_dev.dv_xname, + periph->periphtarget, + osiop_read_1(sc, OSIOP_SBCL), + osiop_read_1(sc, OSIOP_SFBR), + osiop_read_1(sc, OSIOP_LCRC), + osiop_read_4(sc, OSIOP_DSP) - + sc->sc_scrdma->dm_segs[0].ds_addr); + } +#endif + osiop_scsidone(sc->sc_nexus, status); + } + } + splx(s); +} + +void +osiop_update_xfer_mode(sc, target) + struct osiop_softc *sc; + int target; +{ + struct osiop_tinfo *ti = &sc->sc_tinfo[target]; + + printf("%s: target %d using ", sc->sc_dev.dv_xname, target); + + ti->sxfer = 0; + ti->sbcl = 0; + if (ti->offset != 0) { + scsi_period_to_osiop(sc, target); + printf("Synchronous (%d offset %d period) "); + } else + printf("Asynchronous "); + + printf("data transfers\n"); +} + +/* + * This is based on the Progressive Peripherals 33Mhz Zeus driver and will + * not be correct for other 53c710 boards. + * + */ +void +scsi_period_to_osiop(sc, target) + struct osiop_softc *sc; + int target; +{ + int period, offset, sxfer, sbcl; +#ifdef DEBUG_SYNC + int i; +#endif + + period = sc->sc_tinfo[target].period; + offset = sc->sc_tinfo[target].offset; +#ifdef DEBUG_SYNC + sxfer = 0; + if (offset <= OSIOP_MAX_OFFSET) + sxfer = offset; + for (i = 0; i < sizeof(sync_tab) / sizeof(sync_tab[0]); i++) { + if (period <= sync_tab[i].p) { + sxfer |= sync_tab[i].r & 0x70; + sbcl = sync_tab[i].r & 0x03; + break; + } + } + printf("osiop sync old: osiop_sxfr %02x, osiop_sbcl %02x\n", + sxfer, sbcl); +#endif + for (sbcl = 1; sbcl < 4; sbcl++) { + sxfer = (period * 4 - 1) / sc->sc_tcp[sbcl] - 3; + if (sxfer >= 0 && sxfer <= 7) + break; + } + if (sbcl > 3) { + printf("osiop sync: unable to compute sync params " + "for period %d ns\n", period * 4); + /* + * XXX need to pick a value we can do and renegotiate + */ + sxfer = sbcl = 0; + } else { + sxfer = (sxfer << 4) | ((offset <= OSIOP_MAX_OFFSET) ? + offset : OSIOP_MAX_OFFSET); +#ifdef DEBUG_SYNC + printf("osiop sync: params for period %dns: sxfer %x sbcl %x", + period * 4, sxfer, sbcl); + printf(" actual period %dns\n", + sc->sc_tcp[sbcl] * ((sxfer >> 4) + 4)); +#endif + } + sc->sc_tinfo[target].sxfer = sxfer; + sc->sc_tinfo[target].sbcl = sbcl; +#ifdef DEBUG_SYNC + printf("osiop sync: osiop_sxfr %02x, osiop_sbcl %02x\n", sxfer, sbcl); +#endif +} + +void +osiop_timeout(arg) + void *arg; +{ + struct osiop_acb *acb = arg; + struct scsi_xfer *xs = acb->xs; + struct osiop_softc *sc = acb->sc; + int s; + + sc_print_addr(xs->sc_link); + printf("command timeout\n"); + + s = splbio(); + /* reset the scsi bus */ + osiop_resetbus(sc); + + /* deactivate timeout */ + timeout_del(&xs->stimeout); + acb->flags |= ACB_F_TIMEOUT; + osiop_reset(sc); + splx(s); + return; +} + +#ifdef OSIOP_DEBUG + +#if OSIOP_TRACE_SIZE +void +osiop_dump_trace() +{ + int i; + + printf("osiop trace: next index %d\n", osiop_trix); + i = osiop_trix; + do { + printf("%3d: '%c' %02x %02x %02x\n", i, + osiop_trbuf[i], osiop_trbuf[i + 1], + osiop_trbuf[i + 2], osiop_trbuf[i + 3]); + i = (i + 4) & (OSIOP_TRACE_SIZE - 1); + } while (i != osiop_trix); +} +#endif + +void +osiop_dump_acb(acb) + struct osiop_acb *acb; +{ + u_int8_t *b; + int i; + + printf("acb@%p ", acb); + if (acb->xs == NULL) { + printf("<unused>\n"); + return; + } + + b = (u_int8_t *)&acb->xs->cmd; + printf("(%d:%d) status %2x cmdlen %2ld cmd ", + acb->xs->sc_link->target, + acb->xs->sc_link->lun, acb->status, acb->cmdlen); + for (i = acb->cmdlen; i > 0; i--) + printf(" %02x", *b++); + printf("\n"); + printf(" xs: %p data %p:%04x ", acb->xs, acb->xs->data, + acb->xs->datalen); + printf("va %p:%lx ", acb->data, acb->datalen); + printf("cur %lx:%lx\n", acb->curaddr, acb->curlen); +} + +void +osiop_dump(sc) + struct osiop_softc *sc; +{ + struct osiop_acb *acb; + int i, s; + + s = splbio(); +#if OSIOP_TRACE_SIZE + osiop_dump_trace(); +#endif + printf("%s@%p istat %02x\n", + sc->sc_dev.dv_xname, sc, osiop_read_1(sc, OSIOP_ISTAT)); + if ((acb = TAILQ_FIRST(&sc->free_list)) != NULL) { + printf("Free list:\n"); + while (acb) { + osiop_dump_acb(acb); + acb = TAILQ_NEXT(acb, chain); + } + } + if ((acb = TAILQ_FIRST(&sc->ready_list)) != NULL) { + printf("Ready list:\n"); + while (acb) { + osiop_dump_acb(acb); + acb = TAILQ_NEXT(acb, chain); + } + } + if ((acb = TAILQ_FIRST(&sc->nexus_list)) != NULL) { + printf("Nexus list:\n"); + while (acb) { + osiop_dump_acb(acb); + acb = TAILQ_NEXT(acb, chain); + } + } + if (sc->sc_nexus) { + printf("Nexus:\n"); + osiop_dump_acb(sc->sc_nexus); + } + for (i = 0; i < OSIOP_NTGT; i++) { + if (sc->sc_tinfo[i].cmds > 2) { + printf("tgt %d: cmds %d disc %d lubusy %x\n", + i, sc->sc_tinfo[i].cmds, + sc->sc_tinfo[i].dconns, + sc->sc_tinfo[i].lubusy); + } + } + splx(s); +} +#endif diff --git a/sys/dev/ic/osiopreg.h b/sys/dev/ic/osiopreg.h new file mode 100644 index 00000000000..95690e1a977 --- /dev/null +++ b/sys/dev/ic/osiopreg.h @@ -0,0 +1,379 @@ +/* $OpenBSD: osiopreg.h,v 1.1 2003/01/08 02:11:38 krw Exp $ */ +/* $NetBSD: osiopreg.h,v 1.1 2001/04/30 04:47:51 tsutsui Exp $ */ + +/* + * Copyright (c) 1990 The Regents of the University of California. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Van Jacobson of Lawrence Berkeley Laboratory. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)siopreg.h 7.3 (Berkeley) 2/5/91 + */ + +/* + * NCR 53C710 SCSI interface hardware description. + * + * From the Mach scsi driver for the 53C710 and amiga siop driver + */ + +/* byte lane definitions */ +#if BYTE_ORDER == LITTLE_ENDIAN +#define BL0 0 +#define BL1 1 +#define BL2 2 +#define BL3 3 +#else +#define BL0 3 +#define BL1 2 +#define BL2 1 +#define BL3 0 +#endif + +#define OSIOP_SCNTL0 (0x00+BL0) /* rw: SCSI control reg 0 */ +#define OSIOP_SCNTL1 (0x00+BL1) /* rw: SCSI control reg 1 */ +#define OSIOP_SDID (0x00+BL2) /* rw: SCSI destination ID */ +#define OSIOP_SIEN (0x00+BL3) /* rw: SCSI interrupt enable */ + +#define OSIOP_SCID (0x04+BL0) /* rw: SCSI Chip ID reg */ +#define OSIOP_SXFER (0x04+BL1) /* rw: SCSI Transfer reg */ +#define OSIOP_SODL (0x04+BL2) /* rw: SCSI Output Data Latch */ +#define OSIOP_SOCL (0x04+BL3) /* rw: SCSI Output Control Latch */ + +#define OSIOP_SFBR (0x08+BL0) /* ro: SCSI First Byte Received */ +#define OSIOP_SIDL (0x08+BL1) /* ro: SCSI Input Data Latch */ +#define OSIOP_SBDL (0x08+BL2) /* ro: SCSI Bus Data Lines */ +#define OSIOP_SBCL (0x08+BL3) /* rw: SCSI Bus Control Lines */ + +#define OSIOP_DSTAT (0x0c+BL0) /* ro: DMA status */ +#define OSIOP_SSTAT0 (0x0c+BL1) /* ro: SCSI status reg 0 */ +#define OSIOP_SSTAT1 (0x0c+BL2) /* ro: SCSI status reg 1 */ +#define OSIOP_SSTAT2 (0x0c+BL3) /* ro: SCSI status reg 2 */ + +#define OSIOP_DSA 0x10 /* rw: Data Structure Address */ + +#define OSIOP_CTEST0 (0x14+BL0) /* ro: Chip test register 0 */ +#define OSIOP_CTEST1 (0x14+BL1) /* ro: Chip test register 1 */ +#define OSIOP_CTEST2 (0x14+BL2) /* ro: Chip test register 2 */ +#define OSIOP_CTEST3 (0x14+BL3) /* ro: Chip test register 3 */ + +#define OSIOP_CTEST4 (0x18+BL0) /* rw: Chip test register 4 */ +#define OSIOP_CTEST5 (0x18+BL1) /* rw: Chip test register 5 */ +#define OSIOP_CTEST6 (0x18+BL2) /* rw: Chip test register 6 */ +#define OSIOP_CTEST7 (0x18+BL3) /* rw: Chip test register 7 */ + +#define OSIOP_TEMP 0x1c /* rw: Temporary Stack reg */ + +#define OSIOP_DFIFO (0x20+BL0) /* rw: DMA FIFO */ +#define OSIOP_ISTAT (0x20+BL1) /* rw: Interrupt Status reg */ +#define OSIOP_CTEST8 (0x20+BL2) /* rw: Chip test register 8 */ +#define OSIOP_LCRC (0x20+BL3) /* rw: LCRC value */ + +#define OSIOP_DBC 0x24 /* rw: DMA Counter reg (longword) */ +#define OSIOP_DBC0 (0x24+BL0) /* rw: DMA Byte Counter reg 0 */ +#define OSIOP_DBC1 (0x24+BL1) /* rw: DMA Byte Counter reg 1 */ +#define OSIOP_DBC2 (0x24+BL2) /* rw: DMA Byte Counter reg 2 */ +#define OSIOP_DCMD (0x24+BL3) /* rw: DMA Command Register */ + +#define OSIOP_DNAD 0x28 /* rw: DMA Next Data Address */ + +#define OSIOP_DSP 0x2c /* rw: DMA SCRIPTS Pointer reg */ + +#define OSIOP_DSPS 0x30 /* rw: DMA SCRIPTS Pointer Save reg */ + +#define OSIOP_SCRATCH 0x34 /* rw: Scratch register */ + +#define OSIOP_DMODE (0x38+BL0) /* rw: DMA Mode reg */ +#define OSIOP_DIEN (0x38+BL1) /* rw: DMA Interrupt Enable */ +#define OSIOP_DWT (0x38+BL2) /* rw: DMA Watchdog Timer */ +#define OSIOP_DCNTL (0x38+BL3) /* rw: DMA Control reg */ + +#define OSIOP_ADDER 0x3c /* ro: Adder Sum Output */ + +#define OSIOP_NREGS 0x40 + + +/* + * Register defines + */ + +/* Scsi control register 0 (scntl0) */ + +#define OSIOP_SCNTL0_ARB 0xc0 /* Arbitration mode */ +#define OSIOP_ARB_SIMPLE 0x00 +#define OSIOP_ARB_FULL 0xc0 +#define OSIOP_SCNTL0_START 0x20 /* Start Sequence */ +#define OSIOP_SCNTL0_WATN 0x10 /* (Select) With ATN */ +#define OSIOP_SCNTL0_EPC 0x08 /* Enable Parity Checking */ +#define OSIOP_SCNTL0_EPG 0x04 /* Enable Parity Generation */ +#define OSIOP_SCNTL0_AAP 0x02 /* Assert ATN on Parity Error */ +#define OSIOP_SCNTL0_TRG 0x01 /* Target Mode */ + +/* Scsi control register 1 (scntl1) */ + +#define OSIOP_SCNTL1_EXC 0x80 /* Extra Clock Cycle of data setup */ +#define OSIOP_SCNTL1_ADB 0x40 /* Assert Data Bus */ +#define OSIOP_SCNTL1_ESR 0x20 /* Enable Selection/Reselection */ +#define OSIOP_SCNTL1_CON 0x10 /* Connected */ +#define OSIOP_SCNTL1_RST 0x08 /* Assert RST */ +#define OSIOP_SCNTL1_AESP 0x04 /* Assert even SCSI parity */ +#define OSIOP_SCNTL1_PAR 0x04 /* Force bad Parity */ +#define OSIOP_SCNTL1_RES0 0x02 /* Reserved */ +#define OSIOP_SCNTL1_RES1 0x01 /* Reserved */ + +/* Scsi interrupt enable register (sien) */ + +#define OSIOP_SIEN_M_A 0x80 /* Phase Mismatch or ATN active */ +#define OSIOP_SIEN_FCMP 0x40 /* Function Complete */ +#define OSIOP_SIEN_STO 0x20 /* (Re)Selection timeout */ +#define OSIOP_SIEN_SEL 0x10 /* (Re)Selected */ +#define OSIOP_SIEN_SGE 0x08 /* SCSI Gross Error */ +#define OSIOP_SIEN_UDC 0x04 /* Unexpected Disconnect */ +#define OSIOP_SIEN_RST 0x02 /* RST asserted */ +#define OSIOP_SIEN_PAR 0x01 /* Parity Error */ + +/* Scsi chip ID (scid) */ + +#define OSIOP_SCID_VALUE(i) (1 << (i)) + +/* Scsi transfer register (sxfer) */ + +#define OSIOP_SXFER_DHP 0x80 /* Disable Halt on Parity error/ + ATN asserted */ +#define OSIOP_SXFER_TP 0x70 /* Synch Transfer Period */ + /* see specs for formulas: + Period = TCP * (4 + XFERP ) + TCP = 1 + CLK + 1..2; + */ +#define OSIOP_SXFER_MO 0x0f /* Synch Max Offset */ +#define OSIOP_MAX_OFFSET 8 + +/* Scsi output data latch register (sodl) */ + +/* Scsi output control latch register (socl) */ + +#define OSIOP_REQ 0x80 /* SCSI signal <x> asserted */ +#define OSIOP_ACK 0x40 +#define OSIOP_BSY 0x20 +#define OSIOP_SEL 0x10 +#define OSIOP_ATN 0x08 +#define OSIOP_MSG 0x04 +#define OSIOP_CD 0x02 +#define OSIOP_IO 0x01 + +#define OSIOP_PHASE(x) ((x) & (OSIOP_MSG|OSIOP_CD|OSIOP_IO)) +#define DATA_OUT_PHASE 0x00 +#define DATA_IN_PHASE OSIOP_IO +#define COMMAND_PHASE OSIOP_CD +#define STATUS_PHASE (OSIOP_CD|OSIOP_IO) +#define MSG_OUT_PHASE (OSIOP_MSG|OSIOP_CD) +#define MSG_IN_PHASE (OSIOP_MSG|OSIOP_CD|OSIOP_IO) + +/* Scsi first byte received register (sfbr) */ + +/* Scsi input data latch register (sidl) */ + +/* Scsi bus data lines register (sbdl) */ + +/* Scsi bus control lines register (sbcl). Same as socl */ + +#define OSIOP_SBCL_SSCF1 0x02 /* wo */ +#define OSIOP_SBCL_SSCF0 0x01 /* wo */ + +/* DMA status register (dstat) */ + +#define OSIOP_DSTAT_DFE 0x80 /* DMA FIFO empty */ +#define OSIOP_DSTAT_RES 0x40 +#define OSIOP_DSTAT_BF 0x20 /* Bus fault */ +#define OSIOP_DSTAT_ABRT 0x10 /* Aborted */ +#define OSIOP_DSTAT_SSI 0x08 /* SCRIPT Single Step */ +#define OSIOP_DSTAT_SIR 0x04 /* SCRIPT Interrupt Instruction */ +#define OSIOP_DSTAT_WTD 0x02 /* Watchdog Timeout Detected */ +#define OSIOP_DSTAT_IID 0x01 /* Invalid Instruction Detected */ + +/* Scsi status register 0 (sstat0) */ + +#define OSIOP_SSTAT0_M_A 0x80 /* Phase Mismatch or ATN active */ +#define OSIOP_SSTAT0_FCMP 0x40 /* Function Complete */ +#define OSIOP_SSTAT0_STO 0x20 /* (Re)Selection timeout */ +#define OSIOP_SSTAT0_SEL 0x10 /* (Re)Selected */ +#define OSIOP_SSTAT0_SGE 0x08 /* SCSI Gross Error */ +#define OSIOP_SSTAT0_UDC 0x04 /* Unexpected Disconnect */ +#define OSIOP_SSTAT0_RST 0x02 /* RST asserted */ +#define OSIOP_SSTAT0_PAR 0x01 /* Parity Error */ + +/* Scsi status register 1 (sstat1) */ + +#define OSIOP_SSTAT1_ILF 0x80 /* Input latch (sidl) full */ +#define OSIOP_SSTAT1_ORF 0x40 /* output reg (sodr) full */ +#define OSIOP_SSTAT1_OLF 0x20 /* output latch (sodl) full */ +#define OSIOP_SSTAT1_AIP 0x10 /* Arbitration in progress */ +#define OSIOP_SSTAT1_LOA 0x08 /* Lost arbitration */ +#define OSIOP_SSTAT1_WOA 0x04 /* Won arbitration */ +#define OSIOP_SSTAT1_RST 0x02 /* SCSI RST current value */ +#define OSIOP_SSTAT1_SDP 0x01 /* SCSI SDP current value */ + +/* Scsi status register 2 (sstat2) */ + +#define OSIOP_SSTAT2_FF 0xf0 /* SCSI FIFO flags (bytecount) */ +#define OSIOP_SCSI_FIFO_DEEP 8 +#define OSIOP_SSTAT2_SDP 0x08 /* Latched (on REQ) SCSI SDP */ +#define OSIOP_SSTAT2_MSG 0x04 /* Latched SCSI phase */ +#define OSIOP_SSTAT2_CD 0x02 +#define OSIOP_SSTAT2_IO 0x01 + +/* Chip test register 0 (ctest0) */ + +#define OSIOP_CTEST0_RES0 0x80 +#define OSIOP_CTEST0_BTD 0x40 /* Byte-to-byte Timer Disable */ +#define OSIOP_CTEST0_GRP 0x20 /* Generate Receive Parity */ +#define OSIOP_CTEST0_EAN 0x10 /* Enable Active Negation */ +#define OSIOP_CTEST0_HSC 0x08 /* Halt SCSI clock */ +#define OSIOP_CTEST0_ERF 0x04 /* Extend REQ/ACK Filtering */ +#define OSIOP_CTEST0_RES1 0x02 +#define OSIOP_CTEST0_DDIR 0x01 /* Xfer direction (1-> from SCSI bus) */ + + +/* Chip test register 1 (ctest1) */ + +#define OSIOP_CTEST1_FMT 0xf0 /* Byte empty in DMA FIFO bottom + (high->byte3) */ +#define OSIOP_CTEST1_FFL 0x0f /* Byte full in DMA FIFO top, same */ + +/* Chip test register 2 (ctest2) */ + +#define OSIOP_CTEST2_RES 0x80 +#define OSIOP_CTEST2_SIGP 0x40 /* Signal process */ +#define OSIOP_CTEST2_SOFF 0x20 /* Synch Offset compare + (1-> zero Init, max Tgt */ +#define OSIOP_CTEST2_SFP 0x10 /* SCSI FIFO Parity */ +#define OSIOP_CTEST2_DFP 0x08 /* DMA FIFO Parity */ +#define OSIOP_CTEST2_TEOP 0x04 /* True EOP (a-la 5380) */ +#define OSIOP_CTEST2_DREQ 0x02 /* DREQ status */ +#define OSIOP_CTEST2_DACK 0x01 /* DACK status */ + +/* Chip test register 3 (ctest3) read-only, top of SCSI FIFO */ + +/* Chip test register 4 (ctest4) */ + +#define OSIOP_CTEST4_MUX 0x80 /* Host bus multiplex mode */ +#define OSIOP_CTEST4_ZMOD 0x40 /* High-impedance outputs */ +#define OSIOP_CTEST4_SZM 0x20 /* ditto, SCSI "outputs" */ +#define OSIOP_CTEST4_SLBE 0x10 /* SCSI loobpack enable */ +#define OSIOP_CTEST4_SFWR 0x08 /* SCSI FIFO write enable (from sodl) */ +#define OSIOP_CTEST4_FBL 0x07 /* DMA FIFO Byte Lane select + (from ctest6) 4->0, .. 7->3 */ + +/* Chip test register 5 (ctest5) */ + +#define OSIOP_CTEST5_ADCK 0x80 /* Clock Address Incrementor */ +#define OSIOP_CTEST5_BBCK 0x40 /* Clock Byte counter */ +#define OSIOP_CTEST5_ROFF 0x20 /* Reset SCSI offset */ +#define OSIOP_CTEST5_MASR 0x10 /* Master set/reset pulses + (of bits 3-0) */ +#define OSIOP_CTEST5_DDIR 0x08 /* (re)set internal DMA direction */ +#define OSIOP_CTEST5_EOP 0x04 /* (re)set internal EOP */ +#define OSIOP_CTEST5_DREQ 0x02 /* (re)set internal REQ */ +#define OSIOP_CTEST5_DACK 0x01 /* (re)set internal ACK */ + +/* Chip test register 6 (ctest6) DMA FIFO access */ + +/* Chip test register 7 (ctest7) */ + +#define OSIOP_CTEST7_CDIS 0x80 /* Cache burst disable */ +#define OSIOP_CTEST7_SC1 0x40 /* Snoop control 1 */ +#define OSIOP_CTEST7_SC0 0x20 /* Snoop contorl 0 */ +#define OSIOP_CTEST7_STD 0x10 /* Selection timeout disable */ +#define OSIOP_CTEST7_DFP 0x08 /* DMA FIFO parity bit */ +#define OSIOP_CTEST7_EVP 0x04 /* Even parity (to host bus) */ +#define OSIOP_CTEST7_TT1 0x02 /* Transfer type bit */ +#define OSIOP_CTEST7_DIFF 0x01 /* Differential mode */ + +/* DMA FIFO register (dfifo) */ + +#define OSIOP_DFIFO_FLF 0x80 /* Flush (spill) DMA FIFO */ +#define OSIOP_DFIFO_BO 0x7f /* FIFO byte offset counter */ + +/* Interrupt status register (istat) */ + +#define OSIOP_ISTAT_ABRT 0x80 /* Abort operation */ +#define OSIOP_ISTAT_RST 0x40 /* Software reset */ +#define OSIOP_ISTAT_SIGP 0x20 /* Signal process */ +#define OSIOP_ISTAT_RES 0x10 +#define OSIOP_ISTAT_CON 0x08 /* Connected */ +#define OSIOP_ISTAT_RES1 0x04 +#define OSIOP_ISTAT_SIP 0x02 /* SCSI Interrupt pending */ +#define OSIOP_ISTAT_DIP 0x01 /* DMA Interrupt pending */ + +/* Chip test register 8 (ctest8) */ + +#define OSIOP_CTEST8_V 0xf0 /* Chip revision level */ +#define OSIOP_CTEST8_FLF 0x08 /* Flush DMA FIFO */ +#define OSIOP_CTEST8_CLF 0x04 /* Clear DMA and SCSI FIFOs */ +#define OSIOP_CTEST8_FM 0x02 /* Fetch pin mode */ +#define OSIOP_CTEST8_SM 0x01 /* Snoop pins mode */ + +/* DMA Mode register (dmode) */ + +#define OSIOP_DMODE_BL_MASK 0xc0 /* DMA burst length */ +#define OSIOP_DMODE_BL8 0xc0 /* 8 bytes */ +#define OSIOP_DMODE_BL4 0x80 /* 4 bytes */ +#define OSIOP_DMODE_BL2 0x40 /* 2 bytes */ +#define OSIOP_DMODE_BL1 0x00 /* 1 byte */ +#define OSIOP_DMODE_FC 0x30 /* Function code */ +#define OSIOP_DMODE_PD 0x08 /* Program/data */ +#define OSIOP_DMODE_FAM 0x04 /* fixed address mode */ +#define OSIOP_DMODE_U0 0x02 /* User programmable transfer type */ +#define OSIOP_DMODE_MAN 0x01 /* SCRIPTS in Manual start mode */ + +/* DMA interrupt enable register (dien) */ + +#define OSIOP_DIEN_RES 0xc0 +#define OSIOP_DIEN_BF 0x20 /* On Bus Fault */ +#define OSIOP_DIEN_ABRT 0x10 /* On Abort */ +#define OSIOP_DIEN_SSI 0x08 /* On SCRIPTS sstep */ +#define OSIOP_DIEN_SIR 0x04 /* On SCRIPTS intr instruction */ +#define OSIOP_DIEN_WTD 0x02 /* On watchdog timeout */ +#define OSIOP_DIEN_IID 0x01 /* On illegal instruction detected */ + +/* DMA control register (dcntl) */ + +#define OSIOP_DCNTL_CF_MASK 0xc0 /* Clock frequency dividers: */ +#define OSIOP_DCNTL_CF_2 0x00 /* 0 --> 37.51..50.00 Mhz, div=2 */ +#define OSIOP_DCNTL_CF_1_5 0x40 /* 1 --> 25.01..37.50 Mhz, div=1.5 */ +#define OSIOP_DCNTL_CF_1 0x80 /* 2 --> 16.67..25.00 Mhz, div=1 */ +#define OSIOP_DCNTL_CF_3 0xc0 /* 3 --> 50.01..66.67 Mhz, div=3 */ +#define OSIOP_DCNTL_EA 0x20 /* Enable ACK */ +#define OSIOP_DCNTL_SSM 0x10 /* Single step mode */ +#define OSIOP_DCNTL_LLM 0x08 /* Enable SCSI Low-level mode */ +#define OSIOP_DCNTL_STD 0x04 /* Start DMA operation */ +#define OSIOP_DCNTL_FA 0x02 /* Fast arbitration */ +#define OSIOP_DCNTL_COM 0x01 /* 53C700 Compatibility */ diff --git a/sys/dev/ic/osiopvar.h b/sys/dev/ic/osiopvar.h new file mode 100644 index 00000000000..9516b003776 --- /dev/null +++ b/sys/dev/ic/osiopvar.h @@ -0,0 +1,253 @@ +/* $OpenBSD: osiopvar.h,v 1.1 2003/01/08 02:11:38 krw Exp $ */ +/* $NetBSD: osiopvar.h,v 1.3 2002/05/14 02:58:35 matt Exp $ */ + +/* + * Copyright (c) 2001 Izumi Tsutsui. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software 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. + */ + +/* + * Copyright (c) 1990 The Regents of the University of California. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Van Jacobson of Lawrence Berkeley Laboratory. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)siopvar.h 7.1 (Berkeley) 5/8/90 + */ + +#define osiop_read_1(sc, reg) \ + bus_space_read_1((sc)->sc_bst, (sc)->sc_reg, reg) +#define osiop_write_1(sc, reg, val) \ + bus_space_write_1((sc)->sc_bst, (sc)->sc_reg, reg, val) + +#define osiop_read_4(sc, reg) \ + bus_space_read_4((sc)->sc_bst, (sc)->sc_reg, reg) +#define osiop_write_4(sc, reg, val) \ + bus_space_write_4((sc)->sc_bst, (sc)->sc_reg, reg, val) + +/* + * The largest single request will be MAXPHYS bytes which will require + * at most MAXPHYS/NBPG+1 chain elements to describe, i.e. if none of + * the buffer pages are physically contiguous (MAXPHYS/NBPG) and the + * buffer is not page aligned (+1). + */ +/* XXX This should be (MAXPHYS / NBPG + 1), but hardcoded in script */ +#define OSIOP_NSG (16 + 1) +#if MAXPHYS > (PAGE_SIZE * (OSIOP_NSG - 1)) +#define OSIOP_MAX_XFER (PAGE_SIZE * (OSIOP_NSG - 1)) +#else +#define OSIOP_MAX_XFER MAXPHYS +#endif + +#define OSIOP_NTGT 8 +#define OSIOP_NACB 32 /* XXX (PAGE_SIZE / sizeof(osiop_ds)) is better? */ + +/* + * Data Structure for SCRIPTS program + */ +typedef struct buf_table { + u_int32_t count; + u_int32_t addr; +} buf_table_t; + +struct osiop_ds { + u_int32_t scsi_addr; /* SCSI ID & sync */ + u_int32_t pad1; + buf_table_t id; /* Identify message */ + buf_table_t cmd; /* SCSI command */ + buf_table_t status; /* Status */ + buf_table_t msg; /* Message */ + buf_table_t msgin; /* Message in */ + buf_table_t extmsg; /* Extended message in */ + buf_table_t synmsg; /* Sync transfer request */ + buf_table_t data[OSIOP_NSG]; /* DMA S/G buffers */ + + u_int8_t msgout[8]; + u_int8_t msgbuf[8]; + u_int8_t stat[8]; +} __attribute__((__packed__)); + +/* status can hold the SCSI_* status values, and 2 additionnal values: */ +#define SCSI_OSIOP_NOCHECK 0xfe /* don't check the scsi status */ +#define SCSI_OSIOP_NOSTATUS 0xff /* device didn't report status */ + +#define MSG_INVALID 0xff /* dummy value for message buffer */ + +#define OSIOP_DSOFF(x) offsetof(struct osiop_ds, x) +#define OSIOP_DSIDOFF OSIOP_DSOFF(msgout[0]) +#define OSIOP_DSMSGOFF OSIOP_DSOFF(msgbuf[0]) +#define OSIOP_DSMSGINOFF OSIOP_DSOFF(msgbuf[1]) +#define OSIOP_DSEXTMSGOFF OSIOP_DSOFF(msgbuf[2]) +#define OSIOP_DSSYNMSGOFF OSIOP_DSOFF(msgbuf[3]) +#define OSIOP_DSSTATOFF OSIOP_DSOFF(stat[0]) + +/* + * ACB. Holds additional information for each SCSI command Comments: + * Basicly, we refrain from fiddling with the scsi_xfer struct + * (except do the expected updating of return values). + * We'll generally update: xs->{flags,resid,error,status} and + * occasionally xs->retries. + */ +struct osiop_acb { + TAILQ_ENTRY(osiop_acb) chain; + struct scsi_xfer *xs; /* SCSI xfer ctrl block from upper layer */ + struct osiop_softc *sc; /* points back to our adapter */ + + bus_dmamap_t cmddma; /* DMA map for SCSI command */ + bus_dmamap_t datadma; /* DMA map for data transfer */ + + struct osiop_ds *ds; /* data structure for this acb */ + bus_size_t dsoffset; /* offset of data structure for this acb */ + + bus_size_t cmdlen; /* command length */ + bus_size_t datalen; /* transfer data length */ +#ifdef OSIOP_DEBUG + void *data; /* transfer data buffer ptr */ +#endif + + bus_addr_t curaddr; /* current transfer data buffer */ + bus_size_t curlen; /* current transfer data length */ + + int status; /* status of this acb */ +/* status defs */ +#define ACB_S_FREE 0 /* cmd slot is free */ +#define ACB_S_READY 1 /* cmd slot is waiting for processing */ +#define ACB_S_ACTIVE 2 /* cmd slot is being processed */ +#define ACB_S_DONE 3 /* cmd slot has been processed */ + + int flags; /* cmd slot flags */ +#define ACB_F_TIMEOUT 0x01 /* command timeout */ +#define ACB_F_AUTOSENSE 0x02 /* request sense due to SCSI_CHECK */ + + u_int8_t intstat; /* buffer to save sc_flags on disconnect */ +}; + +/* + * Some info about each (possible) target on the SCSI bus. This should + * probably have been a "per target+lunit" structure, but we'll leave it at + * this for now. Is there a way to reliably hook it up to sc->fordriver?? + */ +struct osiop_tinfo { + int cmds; /* number of commands processed */ + int dconns; /* number of disconnects */ + int touts; /* number of timeouts */ + int perrs; /* number of parity errors */ + int lubusy; /* What local units/subr. are busy? */ + int period; /* Period suggestion */ + int offset; /* Offset suggestion */ + int flags; /* misc flags per each target */ +#define TI_NOSYNC 0x01 /* disable sync xfer on this target */ +#define TI_NODISC 0x02 /* disable disconnect on this target */ + int state; /* negotiation state */ + u_int8_t sxfer; /* value for SXFER reg */ + u_int8_t sbcl; /* value for SBCL reg */ +}; + +struct osiop_softc { + struct device sc_dev; + + bus_space_tag_t sc_bst; /* bus space tag */ + bus_space_handle_t sc_reg; /* register I/O handle */ + + bus_dma_tag_t sc_dmat; /* bus dma tag */ + bus_dmamap_t sc_scrdma; /* script dma map */ + bus_dmamap_t sc_dsdma; /* script data dma map */ + + u_int32_t *sc_script; /* ptr to script memory */ + struct osiop_ds *sc_ds; /* ptr to data structure memory */ + + int sc_id; /* adapter SCSI id */ + int sc_active; /* number of active I/O's */ + + struct osiop_acb *sc_nexus; /* current command */ + struct osiop_acb *sc_acb; /* the real command blocks */ + + /* Lists of command blocks */ + TAILQ_HEAD(acb_list, osiop_acb) free_list, + ready_list, + nexus_list; + + struct scsi_link sc_link; + + struct osiop_tinfo sc_tinfo[OSIOP_NTGT]; + + int sc_clock_freq; + int sc_tcp[4]; + int sc_flags; +#define OSIOP_INTSOFF 0x80 /* Interrupts turned off */ +#define OSIOP_INTDEFER 0x40 /* MD interrupt has been deferred */ +#define OSIOP_NODMA 0x02 /* No DMA transfer */ +#define OSIOP_ALIVE 0x01 /* controller initialized */ + + int sc_cfflags; /* copy of config flags */ + + int sc_minsync; + + u_int8_t sc_dstat; + u_int8_t sc_sstat0; + u_int8_t sc_sstat1; + u_int8_t sc_istat; + u_int8_t sc_dcntl; + u_int8_t sc_ctest7; + u_int8_t sc_sien; + u_int8_t sc_dien; +}; + +/* negotiation states */ +#define NEG_INIT 0 /* Initial negotiate state */ +#define NEG_SYNC NEG_INIT /* Negotiate synch transfers */ +#define NEG_WAITS 1 /* Waiting for synch negoation response */ +#define NEG_DONE 2 /* Wide and/or sync negotation done */ + +void osiop_attach(struct osiop_softc *); +void osiop_intr(struct osiop_softc *); diff --git a/sys/dev/microcode/siop/Makefile b/sys/dev/microcode/siop/Makefile index 39dfe3d8df8..db8a569b205 100644 --- a/sys/dev/microcode/siop/Makefile +++ b/sys/dev/microcode/siop/Makefile @@ -1,7 +1,7 @@ -# $OpenBSD: Makefile,v 1.1 2001/02/15 04:07:59 krw Exp $ +# $OpenBSD: Makefile,v 1.2 2003/01/08 02:11:38 krw Exp $ # $NetBSD: Makefile,v 1.1 2000/04/21 17:57:01 bouyer Exp $ -all: siop.out +all: siop.out osiop.out PROG= ncr53cxxx MKSHARE=no @@ -9,9 +9,11 @@ MAN= .include <bsd.prog.mk> -regen: siop.out -headers: siop.out +regen: siop.out osiop.out +headers: siop.out osiop.out siop.out: siop.ss ${PROG} - ./${PROG} siop.ss -o siop.out + ./${PROG} siop.ss -p siop.out +osiop.out: osiop.ss ${PROG} + ./${PROG} osiop.ss -p osiop.out diff --git a/sys/dev/microcode/siop/osiop.out b/sys/dev/microcode/siop/osiop.out new file mode 100644 index 00000000000..c1deafa2f0a --- /dev/null +++ b/sys/dev/microcode/siop/osiop.out @@ -0,0 +1,181 @@ +/* $NetBSD: ncr53cxxx.c,v 1.10 2002/04/21 22:40:10 bouyer Exp $ */ +/* + * DO NOT EDIT - this file is automatically generated. + * created from osiop.ss on Sat Oct 19 20:16:28 2002 + */ +const u_int32_t osiop_script[] = { + 0x47000000, 0x00000128, /* 000 - 0 */ + 0x878b0000, 0x00000030, /* 008 - 8 */ + 0x868a0000, 0x00000178, /* 010 - 16 */ + 0x828a0000, 0x00000180, /* 018 - 24 */ + 0x808a0000, 0x00000190, /* 020 - 32 */ + 0x818a0000, 0x00000298, /* 028 - 40 */ + 0x838a0000, 0x000003a0, /* 030 - 48 */ + 0x98080000, 0x0000ff05, /* 038 - 56 */ + 0x1f000028, 0x00000028, /* 040 - 64 */ + 0x808c0001, 0x00000040, /* 048 - 72 */ + 0x808c0004, 0x00000088, /* 050 - 80 */ + 0x808c0002, 0x00000098, /* 058 - 88 */ + 0x808c0007, 0x00000010, /* 060 - 96 */ + 0x808c0003, 0x00000008, /* 068 - 104 */ + 0x98080000, 0x0000ff06, /* 070 - 112 */ + 0x60000040, 0x00000000, /* 078 - 120 */ + 0x60000008, 0x00000000, /* 080 - 128 */ + 0x80880000, 0xffffff78, /* 088 - 136 */ + 0x60000040, 0x00000000, /* 090 - 144 */ + 0x1f000030, 0x00000030, /* 098 - 152 */ + 0x808c0003, 0x00000008, /* 0a0 - 160 */ + 0x98080000, 0x0000ff07, /* 0a8 - 168 */ + 0x60000040, 0x00000000, /* 0b0 - 176 */ + 0x1f000038, 0x00000038, /* 0b8 - 184 */ + 0x98080000, 0x0000ff0b, /* 0c0 - 192 */ + 0x58000008, 0x00000000, /* 0c8 - 200 */ + 0x60000040, 0x00000000, /* 0d0 - 208 */ + 0x80880000, 0xffffff28, /* 0d8 - 216 */ + 0x60000040, 0x00000000, /* 0e0 - 224 */ + 0x48000000, 0x00000000, /* 0e8 - 232 */ + 0x98080000, 0x0000ff02, /* 0f0 - 240 */ + 0x60000040, 0x00000000, /* 0f8 - 248 */ + 0x87830000, 0xffffff00, /* 100 - 256 */ + 0x1f000030, 0x00000030, /* 108 - 264 */ + 0x98040004, 0x0000ff08, /* 110 - 272 */ + 0x60000040, 0x00000000, /* 118 - 280 */ + 0x48000000, 0x00000000, /* 120 - 288 */ + 0x98080000, 0x0000ff01, /* 128 - 296 */ + 0x54000000, 0x00000038, /* 130 - 304 */ + 0x72230000, 0x00000000, /* 138 - 312 */ + 0x6a340000, 0x00000000, /* 140 - 320 */ + 0x9f030000, 0x0000ff09, /* 148 - 328 */ + 0x1f000020, 0x00000020, /* 150 - 336 */ + 0x98080000, 0x0000ff03, /* 158 - 344 */ + 0x60000040, 0x00000000, /* 160 - 352 */ + 0x80880000, 0xfffffe98, /* 168 - 360 */ + 0x74011000, 0x00000000, /* 170 - 368 */ + 0x980c0000, 0x0000ff04, /* 178 - 376 */ + 0x74164000, 0x00000000, /* 180 - 384 */ + 0x80880000, 0xffffffa0, /* 188 - 392 */ + 0x1e000008, 0x00000008, /* 190 - 400 */ + 0x80880000, 0xfffffe68, /* 198 - 408 */ + 0x60000008, 0x00000000, /* 1a0 - 416 */ + 0x1a000010, 0x00000010, /* 1a8 - 424 */ + 0x80880000, 0xfffffe50, /* 1b0 - 432 */ + 0x18000040, 0x00000040, /* 1b8 - 440 */ + 0x88830000, 0xfffffe40, /* 1c0 - 448 */ + 0x18000048, 0x00000048, /* 1c8 - 456 */ + 0x88830000, 0xfffffe30, /* 1d0 - 464 */ + 0x18000050, 0x00000050, /* 1d8 - 472 */ + 0x88830000, 0xfffffe20, /* 1e0 - 480 */ + 0x18000058, 0x00000058, /* 1e8 - 488 */ + 0x88830000, 0xfffffe10, /* 1f0 - 496 */ + 0x18000060, 0x00000060, /* 1f8 - 504 */ + 0x88830000, 0xfffffe00, /* 200 - 512 */ + 0x18000068, 0x00000068, /* 208 - 520 */ + 0x88830000, 0xfffffdf0, /* 210 - 528 */ + 0x18000070, 0x00000070, /* 218 - 536 */ + 0x88830000, 0xfffffde0, /* 220 - 544 */ + 0x18000078, 0x00000078, /* 228 - 552 */ + 0x88830000, 0xfffffdd0, /* 230 - 560 */ + 0x18000080, 0x00000080, /* 238 - 568 */ + 0x88830000, 0xfffffdc0, /* 240 - 576 */ + 0x18000088, 0x00000088, /* 248 - 584 */ + 0x88830000, 0xfffffdb0, /* 250 - 592 */ + 0x18000090, 0x00000090, /* 258 - 600 */ + 0x88830000, 0xfffffda0, /* 260 - 608 */ + 0x18000098, 0x00000098, /* 268 - 616 */ + 0x88830000, 0xfffffd90, /* 270 - 624 */ + 0x180000a0, 0x000000a0, /* 278 - 632 */ + 0x88830000, 0xfffffd80, /* 280 - 640 */ + 0x180000a8, 0x000000a8, /* 288 - 648 */ + 0x88830000, 0xfffffd70, /* 290 - 656 */ + 0x180000b0, 0x000000b0, /* 298 - 664 */ + 0x88830000, 0xfffffd60, /* 2a0 - 672 */ + 0x180000b8, 0x000000b8, /* 2a8 - 680 */ + 0x88830000, 0xfffffd50, /* 2b0 - 688 */ + 0x180000c0, 0x000000c0, /* 2b8 - 696 */ + 0x88880000, 0xfffffd40, /* 2c0 - 704 */ + 0x19000040, 0x00000040, /* 2c8 - 712 */ + 0x89830000, 0xfffffd30, /* 2d0 - 720 */ + 0x19000048, 0x00000048, /* 2d8 - 728 */ + 0x89830000, 0xfffffd20, /* 2e0 - 736 */ + 0x19000050, 0x00000050, /* 2e8 - 744 */ + 0x89830000, 0xfffffd10, /* 2f0 - 752 */ + 0x19000058, 0x00000058, /* 2f8 - 760 */ + 0x89830000, 0xfffffd00, /* 300 - 768 */ + 0x19000060, 0x00000060, /* 308 - 776 */ + 0x89830000, 0xfffffcf0, /* 310 - 784 */ + 0x19000068, 0x00000068, /* 318 - 792 */ + 0x89830000, 0xfffffce0, /* 320 - 800 */ + 0x19000070, 0x00000070, /* 328 - 808 */ + 0x89830000, 0xfffffcd0, /* 330 - 816 */ + 0x19000078, 0x00000078, /* 338 - 824 */ + 0x89830000, 0xfffffcc0, /* 340 - 832 */ + 0x19000080, 0x00000080, /* 348 - 840 */ + 0x89830000, 0xfffffcb0, /* 350 - 848 */ + 0x19000088, 0x00000088, /* 358 - 856 */ + 0x89830000, 0xfffffca0, /* 360 - 864 */ + 0x19000090, 0x00000090, /* 368 - 872 */ + 0x89830000, 0xfffffc90, /* 370 - 880 */ + 0x19000098, 0x00000098, /* 378 - 888 */ + 0x89830000, 0xfffffc80, /* 380 - 896 */ + 0x190000a0, 0x000000a0, /* 388 - 904 */ + 0x89830000, 0xfffffc70, /* 390 - 912 */ + 0x190000a8, 0x000000a8, /* 398 - 920 */ + 0x89830000, 0xfffffc60, /* 3a0 - 928 */ + 0x190000b0, 0x000000b0, /* 3a8 - 936 */ + 0x89830000, 0xfffffc50, /* 3b0 - 944 */ + 0x190000b8, 0x000000b8, /* 3b8 - 952 */ + 0x89830000, 0xfffffc40, /* 3c0 - 960 */ + 0x190000c0, 0x000000c0, /* 3c8 - 968 */ + 0x88880000, 0xfffffc30, /* 3d0 - 976 */ + 0x1b000018, 0x00000018, /* 3d8 - 984 */ + 0x9f030000, 0x0000ff0a, /* 3e0 - 992 */ + 0x1f000020, 0x00000020, /* 3e8 - 1000 */ + 0x60000040, 0x00000000, /* 3f0 - 1008 */ + 0x48000000, 0x00000000, /* 3f8 - 1016 */ + 0x98080000, 0x0000ff00, /* 400 - 1024 */ + 0x80880000, 0xfffffd20, /* 408 - 1032 */ +}; + +#define A_ds_Device 0x00000000 +#define A_ds_MsgOut 0x00000008 +#define A_ds_Cmd 0x00000010 +#define A_ds_Status 0x00000018 +#define A_ds_Msg 0x00000020 +#define A_ds_MsgIn 0x00000028 +#define A_ds_ExtMsg 0x00000030 +#define A_ds_SyncMsg 0x00000038 +#define A_ds_Data1 0x00000040 +#define A_ds_Data2 0x00000048 +#define A_ds_Data3 0x00000050 +#define A_ds_Data4 0x00000058 +#define A_ds_Data5 0x00000060 +#define A_ds_Data6 0x00000068 +#define A_ds_Data7 0x00000070 +#define A_ds_Data8 0x00000078 +#define A_ds_Data9 0x00000080 +#define A_ds_Data10 0x00000088 +#define A_ds_Data11 0x00000090 +#define A_ds_Data12 0x00000098 +#define A_ds_Data13 0x000000a0 +#define A_ds_Data14 0x000000a8 +#define A_ds_Data15 0x000000b0 +#define A_ds_Data16 0x000000b8 +#define A_ds_Data17 0x000000c0 +#define A_ok 0x0000ff00 +#define A_int_disc 0x0000ff01 +#define A_int_disc_wodp 0x0000ff02 +#define A_int_reconnect 0x0000ff03 +#define A_int_connect 0x0000ff04 +#define A_int_phase 0x0000ff05 +#define A_int_msgin 0x0000ff06 +#define A_int_extmsg 0x0000ff07 +#define A_int_msgsdp 0x0000ff08 +#define A_int_identify 0x0000ff09 +#define A_int_status 0x0000ff0a +#define A_int_syncmsg 0x0000ff0b +#define Ent_scripts 0x00000000 +#define Ent_switch 0x00000008 +#define Ent_wait_reselect 0x00000130 +#define Ent_dataout 0x000001b8 +#define Ent_datain 0x000002c8 +#define Ent_clear_ack 0x00000078 diff --git a/sys/dev/microcode/siop/osiop.ss b/sys/dev/microcode/siop/osiop.ss new file mode 100644 index 00000000000..5c6511bcd5b --- /dev/null +++ b/sys/dev/microcode/siop/osiop.ss @@ -0,0 +1,256 @@ +; $OpenBSD: osiop.ss,v 1.1 2003/01/08 02:11:38 krw Exp $ +; $NetBSD: osiop.ss,v 1.1 2001/04/30 04:47:51 tsutsui Exp $ + +; +; Copyright (c) 1995 Michael L. Hitch +; 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 Michael L. Hitch. +; 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. +; + +; NCR 53c710 script +; +ARCH 710 +; +ABSOLUTE ds_Device = 0 +ABSOLUTE ds_MsgOut = ds_Device + 8 +ABSOLUTE ds_Cmd = ds_MsgOut + 8 +ABSOLUTE ds_Status = ds_Cmd + 8 +ABSOLUTE ds_Msg = ds_Status + 8 +ABSOLUTE ds_MsgIn = ds_Msg + 8 +ABSOLUTE ds_ExtMsg = ds_MsgIn + 8 +ABSOLUTE ds_SyncMsg = ds_ExtMsg + 8 +ABSOLUTE ds_Data1 = ds_SyncMsg + 8 +ABSOLUTE ds_Data2 = ds_Data1 + 8 +ABSOLUTE ds_Data3 = ds_Data2 + 8 +ABSOLUTE ds_Data4 = ds_Data3 + 8 +ABSOLUTE ds_Data5 = ds_Data4 + 8 +ABSOLUTE ds_Data6 = ds_Data5 + 8 +ABSOLUTE ds_Data7 = ds_Data6 + 8 +ABSOLUTE ds_Data8 = ds_Data7 + 8 +ABSOLUTE ds_Data9 = ds_Data8 + 8 +ABSOLUTE ds_Data10 = ds_Data9 + 8 +ABSOLUTE ds_Data11 = ds_Data10 + 8 +ABSOLUTE ds_Data12 = ds_Data11 + 8 +ABSOLUTE ds_Data13 = ds_Data12 + 8 +ABSOLUTE ds_Data14 = ds_Data13 + 8 +ABSOLUTE ds_Data15 = ds_Data14 + 8 +ABSOLUTE ds_Data16 = ds_Data15 + 8 +ABSOLUTE ds_Data17 = ds_Data16 + 8 + + +ABSOLUTE ok = 0xff00 +ABSOLUTE int_disc = 0xff01 +ABSOLUTE int_disc_wodp = 0xff02 +ABSOLUTE int_reconnect = 0xff03 +ABSOLUTE int_connect = 0xff04 +ABSOLUTE int_phase = 0xff05 +ABSOLUTE int_msgin = 0xff06 +ABSOLUTE int_extmsg = 0xff07 +ABSOLUTE int_msgsdp = 0xff08 +ABSOLUTE int_identify = 0xff09 +ABSOLUTE int_status = 0xff0a +ABSOLUTE int_syncmsg = 0xff0b + +ENTRY scripts +ENTRY switch +ENTRY wait_reselect +ENTRY dataout +ENTRY datain +ENTRY clear_ack + +PROC osiop_script: + +scripts: + + SELECT ATN FROM ds_Device, REL(reselect) +; +switch: + JUMP REL(msgin), WHEN MSG_IN + JUMP REL(msgout), IF MSG_OUT + JUMP REL(command_phase), IF CMD + JUMP REL(dataout), IF DATA_OUT + JUMP REL(datain), IF DATA_IN + JUMP REL(end), IF STATUS + + INT int_phase ; Unrecognized phase + +msgin: + MOVE FROM ds_MsgIn, WHEN MSG_IN + JUMP REL(ext_msg), IF 0x01 ; extended message + JUMP REL(disc), IF 0x04 ; disconnect message + JUMP REL(msg_sdp), IF 0x02 ; save data pointers + JUMP REL(msg_rej), IF 0x07 ; message reject + JUMP REL(msg_rdp), IF 0x03 ; restore data pointers + INT int_msgin ; unrecognized message + +msg_rej: +; Do we need to interrupt host here to let it handle the reject? +msg_rdp: +clear_ack: + CLEAR ACK + CLEAR ATN + JUMP REL(switch) + +ext_msg: + CLEAR ACK + MOVE FROM ds_ExtMsg, WHEN MSG_IN + JUMP REL(sync_msg), IF 0x03 + int int_extmsg ; extended message not SDTR + +sync_msg: + CLEAR ACK + MOVE FROM ds_SyncMsg, WHEN MSG_IN + int int_syncmsg ; Let host handle the message +; If we continue from the interrupt, the host has set up a response +; message to be sent. Set ATN, clear ACK, and continue. + SET ATN + CLEAR ACK + JUMP REL(switch) + +disc: + CLEAR ACK + WAIT DISCONNECT + + int int_disc_wodp ; signal disconnect w/o save DP + +msg_sdp: + CLEAR ACK ; acknowledge message + JUMP REL(switch), WHEN NOT MSG_IN + MOVE FROM ds_ExtMsg, WHEN MSG_IN + INT int_msgsdp, IF NOT 0x04 ; interrupt if not disconnect + CLEAR ACK + WAIT DISCONNECT + + INT int_disc ; signal disconnect + +reselect: +wait_reselect: + WAIT RESELECT REL(select_adr) + MOVE LCRC to SFBR ; Save reselect ID + MOVE SFBR to SCRATCH0 + + INT int_identify, WHEN NOT MSG_IN + MOVE FROM ds_Msg, WHEN MSG_IN + INT int_reconnect ; let host know about reconnect + CLEAR ACK ; acknowlege the message + JUMP REL(switch) + +select_adr: + MOVE SCNTL1 & 0x10 to SFBR ; get connected status + INT int_connect, IF 0x00 ; tell host if not connected + MOVE CTEST2 & 0x40 to SFBR ; clear Sig_P + JUMP REL(wait_reselect) ; and try reselect again + +msgout: + MOVE FROM ds_MsgOut, WHEN MSG_OUT + JUMP REL(switch) + +command_phase: + CLEAR ATN + MOVE FROM ds_Cmd, WHEN CMD + JUMP REL(switch) + +dataout: + MOVE FROM ds_Data1, WHEN DATA_OUT + CALL REL(switch), WHEN NOT DATA_OUT + MOVE FROM ds_Data2, WHEN DATA_OUT + CALL REL(switch), WHEN NOT DATA_OUT + MOVE FROM ds_Data3, WHEN DATA_OUT + CALL REL(switch), WHEN NOT DATA_OUT + MOVE FROM ds_Data4, WHEN DATA_OUT + CALL REL(switch), WHEN NOT DATA_OUT + MOVE FROM ds_Data5, WHEN DATA_OUT + CALL REL(switch), WHEN NOT DATA_OUT + MOVE FROM ds_Data6, WHEN DATA_OUT + CALL REL(switch), WHEN NOT DATA_OUT + MOVE FROM ds_Data7, WHEN DATA_OUT + CALL REL(switch), WHEN NOT DATA_OUT + MOVE FROM ds_Data8, WHEN DATA_OUT + CALL REL(switch), WHEN NOT DATA_OUT + MOVE FROM ds_Data9, WHEN DATA_OUT + CALL REL(switch), WHEN NOT DATA_OUT + MOVE FROM ds_Data10, WHEN DATA_OUT + CALL REL(switch), WHEN NOT DATA_OUT + MOVE FROM ds_Data11, WHEN DATA_OUT + CALL REL(switch), WHEN NOT DATA_OUT + MOVE FROM ds_Data12, WHEN DATA_OUT + CALL REL(switch), WHEN NOT DATA_OUT + MOVE FROM ds_Data13, WHEN DATA_OUT + CALL REL(switch), WHEN NOT DATA_OUT + MOVE FROM ds_Data14, WHEN DATA_OUT + CALL REL(switch), WHEN NOT DATA_OUT + MOVE FROM ds_Data15, WHEN DATA_OUT + CALL REL(switch), WHEN NOT DATA_OUT + MOVE FROM ds_Data16, WHEN DATA_OUT + CALL REL(switch), WHEN NOT DATA_OUT + MOVE FROM ds_Data17, WHEN DATA_OUT + CALL REL(switch) + +datain: + MOVE FROM ds_Data1, WHEN DATA_IN + CALL REL(switch), WHEN NOT DATA_IN + MOVE FROM ds_Data2, WHEN DATA_IN + CALL REL(switch), WHEN NOT DATA_IN + MOVE FROM ds_Data3, WHEN DATA_IN + CALL REL(switch), WHEN NOT DATA_IN + MOVE FROM ds_Data4, WHEN DATA_IN + CALL REL(switch), WHEN NOT DATA_IN + MOVE FROM ds_Data5, WHEN DATA_IN + CALL REL(switch), WHEN NOT DATA_IN + MOVE FROM ds_Data6, WHEN DATA_IN + CALL REL(switch), WHEN NOT DATA_IN + MOVE FROM ds_Data7, WHEN DATA_IN + CALL REL(switch), WHEN NOT DATA_IN + MOVE FROM ds_Data8, WHEN DATA_IN + CALL REL(switch), WHEN NOT DATA_IN + MOVE FROM ds_Data9, WHEN DATA_IN + CALL REL(switch), WHEN NOT DATA_IN + MOVE FROM ds_Data10, WHEN DATA_IN + CALL REL(switch), WHEN NOT DATA_IN + MOVE FROM ds_Data11, WHEN DATA_IN + CALL REL(switch), WHEN NOT DATA_IN + MOVE FROM ds_Data12, WHEN DATA_IN + CALL REL(switch), WHEN NOT DATA_IN + MOVE FROM ds_Data13, WHEN DATA_IN + CALL REL(switch), WHEN NOT DATA_IN + MOVE FROM ds_Data14, WHEN DATA_IN + CALL REL(switch), WHEN NOT DATA_IN + MOVE FROM ds_Data15, WHEN DATA_IN + CALL REL(switch), WHEN NOT DATA_IN + MOVE FROM ds_Data16, WHEN DATA_IN + CALL REL(switch), WHEN NOT DATA_IN + MOVE FROM ds_Data17, WHEN DATA_IN + CALL REL(switch) + +end: + MOVE FROM ds_Status, WHEN STATUS + int int_status, WHEN NOT MSG_IN ; status not followed by msg + MOVE FROM ds_Msg, WHEN MSG_IN + CLEAR ACK + WAIT DISCONNECT + INT ok ; signal completion + JUMP REL(wait_reselect) |