Index: conf/GENERIC =================================================================== RCS file: /cvsroot/src/sys/arch/hp300/conf/GENERIC,v retrieving revision 1.93 diff -u -r1.93 GENERIC --- conf/GENERIC 2003/05/24 06:21:21 1.93 +++ conf/GENERIC 2003/06/30 16:55:26 @@ -218,11 +218,17 @@ mt* at hpibbus? slave ? punit ? # HP-IB 9-track tape ppi0 at hpibbus0 slave 5 punit 0 # HP-IB plotter -oscsi* at dio? scode ? # Old HP SCSI +#oscsi* at dio? scode ? # Old HP SCSI -sd* at oscsi? target ? lun ? # SCSI disks -st* at oscsi? target ? lun ? # SCSI tapes -ac* at oscsi? target ? lun ? # SCSI changers +#sd* at oscsi? target ? lun ? # SCSI disks +#st* at oscsi? target ? lun ? # SCSI tapes +#ac* at oscsi? target ? lun ? # SCSI changers + +spc* at dio? scode ? +scsibus* at spc? +sd* at scsibus? target ? lun ? +cd* at scsibus? target ? lun ? +st* at scsibus? target ? lun ? # # Pseudo-devices Index: conf/files.hp300 =================================================================== RCS file: /cvsroot/src/sys/arch/hp300/conf/files.hp300,v retrieving revision 1.66 diff -u -r1.66 files.hp300 --- conf/files.hp300 2003/06/28 08:22:02 1.66 +++ conf/files.hp300 2003/06/30 16:55:26 @@ -170,23 +170,31 @@ file arch/hp300/dev/ppi.c ppi needs-flag # Old HP SCSI layer -device oscsi { target = -1, lun = -1 } -attach oscsi at dio -file arch/hp300/dev/scsi.c oscsi +#device oscsi { target = -1, lun = -1 } +#attach oscsi at dio +#file arch/hp300/dev/scsi.c oscsi # Old HP SCSI devices -device sd: disk -attach sd at oscsi -file arch/hp300/dev/sd.c sd needs-flag -file arch/hp300/dev/sd_compat.c sd # XXX +#device sd: disk +#attach sd at oscsi +#file arch/hp300/dev/sd.c sd needs-flag +#file arch/hp300/dev/sd_compat.c sd # XXX -device st: tape -attach st at oscsi -file arch/hp300/dev/st.c st needs-flag +#device st: tape +#attach st at oscsi +#file arch/hp300/dev/st.c st needs-flag -device ac -attach ac at oscsi -file arch/hp300/dev/ac.c ac needs-flag +#device ac +#attach ac at oscsi +#file arch/hp300/dev/ac.c ac needs-flag + +# MI SCSI +include "dev/scsipi/files.scsipi" + +device spc: scsi +attach spc at dio +file arch/hp300/dev/spc.c spc needs-flag +file arch/hp300/dev/mb89352.c spc # Memory Disk for ramdisk file dev/md_root.c memory_disk_hooks Index: hp300/autoconf.c =================================================================== RCS file: /cvsroot/src/sys/arch/hp300/hp300/autoconf.c,v retrieving revision 1.62 diff -u -r1.62 autoconf.c --- hp300/autoconf.c 2003/04/01 20:41:38 1.62 +++ hp300/autoconf.c 2003/06/30 16:55:27 @@ -131,6 +131,10 @@ #include +#include +#include +#include + #include #include #include @@ -149,8 +153,6 @@ #include #include -#include - /* should go away with a cleanup */ extern int dcacnattach(bus_space_tag_t, bus_addr_t, int); @@ -435,7 +437,7 @@ if (memcmp(dev->dv_xname, "fhpib", 5) == 0 || memcmp(dev->dv_xname, "nhpib", 5) == 0 || - memcmp(dev->dv_xname, "oscsi", 5) == 0) { + memcmp(dev->dv_xname, "spc", 3) == 0) { struct dio_attach_args *da = aux; dd->dd_scode = da->da_scode; @@ -451,10 +453,10 @@ } if (memcmp(dev->dv_xname, "sd", 2) == 0) { - struct oscsi_attach_args *osa = aux; + struct scsipibus_attach_args *sa = aux; - dd->dd_slave = osa->osa_target; - dd->dd_punit = osa->osa_lun; + dd->dd_slave = sa->sa_periph->periph_target; + dd->dd_punit = sa->sa_periph->periph_lun; goto linkup; } @@ -473,7 +475,7 @@ return; } - if (memcmp(dev->dv_xname, "oscsi", 5) == 0) { + if (memcmp(dev->dv_xname, "spc", 3) == 0) { dev_data_insert(dd, &dev_data_list_scsi); return; } @@ -603,23 +605,11 @@ for (dd = dev_data_list.lh_first; dd != NULL; dd = dd->dd_list.le_next) { /* - * XXX We don't yet have the extra bus indirection - * XXX for SCSI, so we have to do a little bit of - * XXX extra work. + * "sd" -> "scsibus" -> "spc" + * "rd" -> "hpibbus" -> "fhpib" */ - if (memcmp(dd->dd_dev->dv_xname, "sd", 2) == 0) { - /* - * "sd" -> "oscsi" - */ - if (dd->dd_dev->dv_parent != cdd->dd_dev) - continue; - } else { - /* - * "rd" -> "hpibbus" -> "fhpib" - */ - if (dd->dd_dev->dv_parent->dv_parent != cdd->dd_dev) - continue; - } + if (dd->dd_dev->dv_parent->dv_parent != cdd->dd_dev) + continue; if (dd->dd_slave == slave && dd->dd_punit == punit) { @@ -686,31 +676,15 @@ * Get parent's info. */ switch (type) { - case 2: + case 2: /* rd */ + case 4: /* sd */ /* * "rd" -> "hpibbus" -> "fhpib" + * "sd" -> "scsibus" -> "spc" */ for (cdd = dev_data_list_hpib.lh_first, ctlr = 0; cdd != NULL; cdd = cdd->dd_clist.le_next, ctlr++) { if (cdd->dd_dev == root_device->dv_parent->dv_parent) { - /* - * Found it! - */ - bootdev = MAKEBOOTDEV(type, - ctlr, dd->dd_slave, dd->dd_punit, - DISKPART(rootdev)); - break; - } - } - break; - - case 4: - /* - * "sd" -> "oscsi" - */ - for (cdd = dev_data_list_scsi.lh_first, ctlr = 0; - cdd != NULL; cdd = cdd->dd_clist.le_next, ctlr++) { - if (cdd->dd_dev == root_device->dv_parent) { /* * Found it! */ --- /dev/null 2003-06-30 23:47:47.000000000 +0900 +++ dev/hp98265reg.h 2003-05-25 13:29:59.000000000 +0900 @@ -0,0 +1,74 @@ +/* $NetBSD$ */ + +/* + * Copyright (c) 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Van Jacobson of Lawrence Berkeley Laboratory. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)scsireg.h 8.1 (Berkeley) 6/10/93 + */ + +/* + * HP 98265A SCSI Interface Hardware Description. + */ + +#define SPC_OFFSET 32 +#define SPC_SIZE (32 * 2) /* XXX */ + +#define HPSCSI_ID 0x01 +#define ID_MASK 0x1f +#define SCSI_ID 0x07 +#define ID_WORD_DMA 0x20 + +#define HPSCSI_CSR 0x03 +#define CSR_IE 0x80 +#define CSR_IR 0x40 +#define SCSI_IPL(csr) ((((csr) >> 4) & 3) + 3) +#define CSR_DMA32 0x08 +#define CSR_DMAIN 0x04 +#define CSR_DE1 0x02 +#define CSR_DE0 0x01 + +#define HPSCSI_WRAP 0x05 +#define WRAP_REQ 0x80 +#define WRAP_ACK 0x40 +#define WRAP_BSY 0x08 +#define WRAP_MSG 0x04 +#define WRAP_CD 0x02 +#define WRAP_IO 0x01 + +#define HPSCSI_HCONF 0x07 +#define HCONF_TP 0x80 +#define SCSI_SYNC_XFER(hconf) (((hconf) >> 5) & 3) +#define HCONF_SD 0x10 +#define HCONF_PARITY 0x08 --- /dev/null 2003-06-30 23:47:47.000000000 +0900 +++ dev/mb89352.c 2003-06-30 02:52:42.000000000 +0900 @@ -0,0 +1,2160 @@ +/* $NetBSD$ */ +/* NecBSD: mb89352.c,v 1.4 1998/03/14 07:31:20 kmatsuda Exp */ + +/*- + * Copyright (c) 1996,97,98,99 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Charles M. Hannum, Masaru Oki and Kouichi Matsuda. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Charles M. Hannum. + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * Copyright (c) 1994 Jarle Greipsland + * 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. + */ +/* + * [NetBSD for NEC PC-98 series] + * Copyright (c) 1996, 1997, 1998 + * NetBSD/pc98 porting staff. All rights reserved. + * Copyright (c) 1996, 1997, 1998 + * Kouichi Matsuda. All rights reserved. + */ + +/* + * Acknowledgements: Many of the algorithms used in this driver are + * inspired by the work of Julian Elischer (julian@tfs.com) and + * Charles Hannum (mycroft@duality.gnu.ai.mit.edu). Thanks a million! + */ + +/* TODO list: + * 1) Get the DMA stuff working. + * 2) Get the iov/uio stuff working. Is this a good thing ??? + * 3) Get the synch stuff working. + * 4) Rewrite it to use malloc for the acb structs instead of static alloc.? + */ + +#include +__KERNEL_RCSID(0, "$NetBSD$"); + +#ifdef DDB +#define integrate +#else +#define integrate __inline static +#endif + +/* + * A few customizable items: + */ + +/* Synchronous data transfers? */ +#define SPC_USE_SYNCHRONOUS 0 +#define SPC_SYNC_REQ_ACK_OFS 8 + +/* Wide data transfers? */ +#define SPC_USE_WIDE 0 +#define SPC_MAX_WIDTH 0 + +/* Max attempts made to transmit a message */ +#define SPC_MSG_MAX_ATTEMPT 3 /* Not used now XXX */ + +/* + * Some spin loop parameters (essentially how long to wait some places) + * The problem(?) is that sometimes we expect either to be able to transmit a + * byte or to get a new one from the SCSI bus pretty soon. In order to avoid + * returning from the interrupt just to get yanked back for the next byte we + * may spin in the interrupt routine waiting for this byte to come. How long? + * This is really (SCSI) device and processor dependent. Tuneable, I guess. + */ +#define SPC_MSGIN_SPIN 1 /* Will spinwait upto ?ms for a new msg byte */ +#define SPC_MSGOUT_SPIN 1 + +/* + * Include debug functions? At the end of this file there are a bunch of + * functions that will print out various information regarding queued SCSI + * commands, driver state and chip contents. You can call them from the + * kernel debugger. If you set SPC_DEBUG to 0 they are not included (the + * kernel uses less memory) but you lose the debugging facilities. + */ +#if 0 +#define SPC_DEBUG 1 +#endif + +#define SPC_ABORT_TIMEOUT 2000 /* time to wait for abort */ + +/* threshold length for DMA transfer */ +#define SPC_MIN_DMA_LEN 32 + +/* End of customizable parameters */ + +/* + * MB89352 SCSI Protocol Controller (SPC) routines. + */ + +#include "opt_ddb.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include + +#include /* XXX */ +#include /* XXX */ + +#ifndef DDB +#define Debugger() panic("should call debugger here (mb89352.c)") +#endif /* ! DDB */ + +#if SPC_DEBUG +int spc_debug = 0x00; /* SPC_SHOWSTART|SPC_SHOWMISC|SPC_SHOWTRACE; */ +#endif + +void spc_done __P((struct spc_softc *, struct spc_acb *)); +void spc_dequeue __P((struct spc_softc *, struct spc_acb *)); +void spc_scsipi_request __P((struct scsipi_channel *, + scsipi_adapter_req_t, void *)); +int spc_poll __P((struct spc_softc *, struct scsipi_xfer *, int)); +integrate void spc_sched_msgout __P((struct spc_softc *, u_char)); +integrate void spc_setsync __P((struct spc_softc *, struct spc_tinfo *)); +void spc_select __P((struct spc_softc *, struct spc_acb *)); +void spc_timeout __P((void *)); +void spc_scsi_reset __P((struct spc_softc *)); +void spc_reset __P((struct spc_softc *)); +void spc_free_acb __P((struct spc_softc *, struct spc_acb *, int)); +struct spc_acb* spc_get_acb __P((struct spc_softc *)); +int spc_reselect __P((struct spc_softc *, int)); +void spc_msgin __P((struct spc_softc *)); +void spc_abort __P((struct spc_softc *, struct spc_acb *)); +void spc_msgout __P((struct spc_softc *)); +int spc_dataout_pio __P((struct spc_softc *, u_char *, int)); +int spc_datain_pio __P((struct spc_softc *, u_char *, int)); +#if SPC_DEBUG +void spc_print_acb __P((struct spc_acb *)); +void spc_dump_driver __P((struct spc_softc *)); +void spc_dump89352 __P((struct spc_softc *)); +void spc_show_scsi_cmd __P((struct spc_acb *)); +void spc_print_active_acb __P((void)); +#endif + +extern struct cfdriver spc_cd; + +/* + * INITIALIZATION ROUTINES (probe, attach ++) + */ + +/* + * Do the real search-for-device. + * Prerequisite: sc->sc_iobase should be set to the proper value + */ +int +spc_find(iot, ioh, bdid) + bus_space_tag_t iot; + bus_space_handle_t ioh; + int bdid; +{ + long timeout = SPC_ABORT_TIMEOUT; + + SPC_TRACE(("spc: probing for spc-chip\n")); + /* + * Disable interrupts then reset the FUJITSU chip. + */ + bus_space_write_1(iot, ioh, SCTL, SCTL_DISABLE | SCTL_CTRLRST); + bus_space_write_1(iot, ioh, SCMD, 0); + bus_space_write_1(iot, ioh, PCTL, 0); + bus_space_write_1(iot, ioh, TEMP, 0); + bus_space_write_1(iot, ioh, TCH, 0); + bus_space_write_1(iot, ioh, TCM, 0); + bus_space_write_1(iot, ioh, TCL, 0); + bus_space_write_1(iot, ioh, INTS, 0); + bus_space_write_1(iot, ioh, SCTL, + SCTL_DISABLE | SCTL_ABRT_ENAB | SCTL_PARITY_ENAB | SCTL_RESEL_ENAB); + bus_space_write_1(iot, ioh, BDID, bdid); + delay(400); + bus_space_write_1(iot, ioh, SCTL, + bus_space_read_1(iot, ioh, SCTL) & ~SCTL_DISABLE); + + /* The following detection is derived from spc.c + * (by Takahide Matsutsuka) in FreeBSD/pccard-test. + */ + while (bus_space_read_1(iot, ioh, PSNS) && timeout) { + timeout--; + DELAY(1); + } + if (timeout == 0) { + printf("spc: find failed\n"); + return 0; + } + + SPC_START(("SPC found")); + return 1; +} + +void +spc_attach(sc) + struct spc_softc *sc; +{ + + SPC_TRACE(("spc_attach ")); + sc->sc_state = SPC_INIT; + + sc->sc_freq = 20; /* XXXX Assume 20 MHz. */ + +#if SPC_USE_SYNCHRONOUS + /* + * These are the bounds of the sync period, based on the frequency of + * the chip's clock input and the size and offset of the sync period + * register. + * + * For a 20MHz clock, this gives us 25, or 100nS, or 10MB/s, as a + * maximum transfer rate, and 112.5, or 450nS, or 2.22MB/s, as a + * minimum transfer rate. + */ + sc->sc_minsync = (2 * 250) / sc->sc_freq; + sc->sc_maxsync = (9 * 250) / sc->sc_freq; +#endif + + spc_init(sc); /* Init chip and driver */ + + /* + * Fill in the adapter. + */ + sc->sc_adapter.adapt_dev = &sc->sc_dev; + sc->sc_adapter.adapt_nchannels = 1; + sc->sc_adapter.adapt_openings = 7; + sc->sc_adapter.adapt_max_periph = 1; + sc->sc_adapter.adapt_minphys = minphys; + sc->sc_adapter.adapt_request = spc_scsipi_request; + + sc->sc_channel.chan_adapter = &sc->sc_adapter; + sc->sc_channel.chan_bustype = &scsi_bustype; + sc->sc_channel.chan_channel = 0; + sc->sc_channel.chan_ntargets = 8; + sc->sc_channel.chan_nluns = 8; + sc->sc_channel.chan_id = sc->sc_initiator; + + /* + * ask the adapter what subunits are present + */ + config_found(&sc->sc_dev, &sc->sc_channel, scsiprint); +} + +/* + * Initialize MB89352 chip itself + * The following conditions should hold: + * spc_isa_probe should have succeeded, i.e. the iobase address in spc_softc + * must be valid. + */ +void +spc_reset(sc) + struct spc_softc *sc; +{ + bus_space_tag_t iot = sc->sc_iot; + bus_space_handle_t ioh = sc->sc_ioh; + + SPC_TRACE(("spc_reset ")); + /* + * Disable interrupts then reset the FUJITSU chip. + */ + bus_space_write_1(iot, ioh, SCTL, SCTL_DISABLE | SCTL_CTRLRST); + bus_space_write_1(iot, ioh, SCMD, 0); + bus_space_write_1(iot, ioh, TMOD, 0); + bus_space_write_1(iot, ioh, PCTL, 0); + bus_space_write_1(iot, ioh, TEMP, 0); + bus_space_write_1(iot, ioh, TCH, 0); + bus_space_write_1(iot, ioh, TCM, 0); + bus_space_write_1(iot, ioh, TCL, 0); + bus_space_write_1(iot, ioh, INTS, 0); + bus_space_write_1(iot, ioh, SCTL, + SCTL_DISABLE | SCTL_ABRT_ENAB | SCTL_PARITY_ENAB | SCTL_RESEL_ENAB); + bus_space_write_1(iot, ioh, BDID, sc->sc_initiator); + delay(400); + bus_space_write_1(iot, ioh, SCTL, + bus_space_read_1(iot, ioh, SCTL) & ~SCTL_DISABLE); +} + + +/* + * Pull the SCSI RST line for 500us. + */ +void +spc_scsi_reset(sc) + struct spc_softc *sc; +{ + bus_space_tag_t iot = sc->sc_iot; + bus_space_handle_t ioh = sc->sc_ioh; + + SPC_TRACE(("spc_scsi_reset ")); + bus_space_write_1(iot, ioh, SCMD, + bus_space_read_1(iot, ioh, SCMD) | SCMD_RST); + delay(500); + bus_space_write_1(iot, ioh, SCMD, + bus_space_read_1(iot, ioh, SCMD) & ~SCMD_RST); + delay(50); +} + +/* + * Initialize spc SCSI driver. + */ +void +spc_init(sc) + struct spc_softc *sc; +{ + struct spc_acb *acb; + int r; + + SPC_TRACE(("spc_init ")); + spc_reset(sc); + spc_scsi_reset(sc); + spc_reset(sc); + + if (sc->sc_state == SPC_INIT) { + /* First time through; initialize. */ + TAILQ_INIT(&sc->ready_list); + TAILQ_INIT(&sc->nexus_list); + TAILQ_INIT(&sc->free_list); + sc->sc_nexus = NULL; + acb = sc->sc_acb; + memset(acb, 0, sizeof(sc->sc_acb)); + for (r = 0; r < sizeof(sc->sc_acb) / sizeof(*acb); r++) { + TAILQ_INSERT_TAIL(&sc->free_list, acb, chain); + acb++; + } + memset(&sc->sc_tinfo, 0, sizeof(sc->sc_tinfo)); + } else { + /* Cancel any active commands. */ + sc->sc_state = SPC_CLEANING; + if ((acb = sc->sc_nexus) != NULL) { + acb->xs->error = XS_DRIVER_STUFFUP; + callout_stop(&acb->xs->xs_callout); + spc_done(sc, acb); + } + while ((acb = TAILQ_FIRST(&sc->nexus_list)) != NULL) { + acb->xs->error = XS_DRIVER_STUFFUP; + callout_stop(&acb->xs->xs_callout); + spc_done(sc, acb); + } + } + + sc->sc_prevphase = PH_INVALID; + for (r = 0; r < 8; r++) { + struct spc_tinfo *ti = &sc->sc_tinfo[r]; + + ti->flags = 0; +#if SPC_USE_SYNCHRONOUS + ti->flags |= DO_SYNC; + ti->period = sc->sc_minsync; + ti->offset = SPC_SYNC_REQ_ACK_OFS; +#else + ti->period = ti->offset = 0; +#endif +#if SPC_USE_WIDE + ti->flags |= DO_WIDE; + ti->width = SPC_MAX_WIDTH; +#else + ti->width = 0; +#endif + } + + sc->sc_state = SPC_IDLE; + bus_space_write_1(sc->sc_iot, sc->sc_ioh, SCTL, + bus_space_read_1(sc->sc_iot, sc->sc_ioh, SCTL) | SCTL_INTR_ENAB); +} + +void +spc_free_acb(sc, acb, flags) + struct spc_softc *sc; + struct spc_acb *acb; + int flags; +{ + int s; + + SPC_TRACE(("spc_free_acb ")); + s = splbio(); + + acb->flags = 0; + TAILQ_INSERT_HEAD(&sc->free_list, acb, chain); + splx(s); +} + +struct spc_acb * +spc_get_acb(sc) + struct spc_softc *sc; +{ + struct spc_acb *acb; + int s; + + SPC_TRACE(("spc_get_acb ")); + s = splbio(); + acb = TAILQ_FIRST(&sc->free_list); + if (acb != NULL) { + TAILQ_REMOVE(&sc->free_list, acb, chain); + acb->flags |= ACB_ALLOC; + } + splx(s); + return acb; +} + +/* + * DRIVER FUNCTIONS CALLABLE FROM HIGHER LEVEL DRIVERS + */ + +/* + * Expected sequence: + * 1) Command inserted into ready list + * 2) Command selected for execution + * 3) Command won arbitration and has selected target device + * 4) Send message out (identify message, eventually also sync.negotiations) + * 5) Send command + * 5a) Receive disconnect message, disconnect. + * 5b) Reselected by target + * 5c) Receive identify message from target. + * 6) Send or receive data + * 7) Receive status + * 8) Receive message (command complete etc.) + */ + +/* + * Start a SCSI-command + * This function is called by the higher level SCSI-driver to queue/run + * SCSI-commands. + */ +void +spc_scsipi_request(chan, req, arg) + struct scsipi_channel *chan; + scsipi_adapter_req_t req; + void *arg; +{ + struct scsipi_xfer *xs; + struct scsipi_periph *periph; + struct spc_softc *sc; + struct spc_acb *acb; + int s, flags; + + sc = (struct spc_softc *)chan->chan_adapter->adapt_dev; + + switch (req) { + case ADAPTER_REQ_RUN_XFER: + xs = arg; + periph = xs->xs_periph; + SPC_TRACE(("spc_scsipi_request ")); + SPC_CMDS(("[0x%x, %d]->%d ", (int)xs->cmd->opcode, xs->cmdlen, + periph->periph_target)); + + flags = xs->xs_control; + acb = spc_get_acb(sc); +#ifdef DIAGNOSTIC + if (acb == NULL) { + scsipi_printaddr(periph); + printf("unable to allocate acb\n"); + panic("spc_scsipi_request"); + } +#endif + + /* Initialize acb */ + acb->xs = xs; + acb->timeout = xs->timeout; + + if (xs->xs_control & XS_CTL_RESET) { + acb->flags |= ACB_RESET; + acb->scsipi_cmd_length = 0; + acb->data_length = 0; + } else { + memcpy(&acb->scsipi_cmd, xs->cmd, xs->cmdlen); + acb->scsipi_cmd_length = xs->cmdlen; + acb->data_addr = xs->data; + acb->data_length = xs->datalen; + } + acb->target_stat = 0; + + s = splbio(); + + TAILQ_INSERT_TAIL(&sc->ready_list, acb, chain); + /* + * Start scheduling unless a queue process is in progress. + */ + if (sc->sc_state == SPC_IDLE) + spc_sched(sc); + /* + * After successful sending, check if we should return just now. + * If so, return SUCCESSFULLY_QUEUED. + */ + + splx(s); + + if ((flags & XS_CTL_POLL) == 0) + return; + + /* Not allowed to use interrupts, use polling instead */ + s = splbio(); + if (spc_poll(sc, xs, acb->timeout)) { + spc_timeout(acb); + if (spc_poll(sc, xs, acb->timeout)) + spc_timeout(acb); + } + splx(s); + return; + case ADAPTER_REQ_GROW_RESOURCES: + /* XXX Not supported. */ + return; + case ADAPTER_REQ_SET_XFER_MODE: + { + /* + * We don't support Sync, Wide, or Tagged Command Queuing. + * Just callback now, to report this. + */ + struct scsipi_xfer_mode *xm = arg; + + xm->xm_mode = 0; + xm->xm_period = 0; + xm->xm_offset = 0; + scsipi_async_event(chan, ASYNC_EVENT_XFER_MODE, xm); + return; + } + } +} + +/* + * Used when interrupt driven I/O isn't allowed, e.g. during boot. + */ +int +spc_poll(sc, xs, count) + struct spc_softc *sc; + struct scsipi_xfer *xs; + int count; +{ + bus_space_tag_t iot = sc->sc_iot; + bus_space_handle_t ioh = sc->sc_ioh; + + SPC_TRACE(("spc_poll ")); + while (count) { + /* + * If we had interrupts enabled, would we + * have got an interrupt? + */ + if (bus_space_read_1(iot, ioh, INTS) != 0) + spc_intr(sc); + if ((xs->xs_status & XS_STS_DONE) != 0) + return 0; + delay(1000); + count--; + } + return 1; +} + +/* + * LOW LEVEL SCSI UTILITIES + */ + +integrate void +spc_sched_msgout(sc, m) + struct spc_softc *sc; + u_char m; +{ + bus_space_tag_t iot = sc->sc_iot; + bus_space_handle_t ioh = sc->sc_ioh; + + SPC_TRACE(("spc_sched_msgout ")); + if (sc->sc_msgpriq == 0) + bus_space_write_1(iot, ioh, SCMD, SCMD_SET_ATN); + sc->sc_msgpriq |= m; +} + +/* + * Set synchronous transfer offset and period. + */ +integrate void +spc_setsync(sc, ti) + struct spc_softc *sc; + struct spc_tinfo *ti; +{ +#if SPC_USE_SYNCHRONOUS + bus_space_tag_t iot = sc->sc_iot; + bus_space_handle_t ioh = sc->sc_ioh; + + SPC_TRACE(("spc_setsync ")); + if (ti->offset != 0) + bus_space_write_1(iot, ioh, TMOD, + ((ti->period * sc->sc_freq) / 250 - 2) << 4 | ti->offset); + else + bus_space_write_1(iot, ioh, TMOD, 0); +#endif +} + +/* + * Start a selection. This is used by spc_sched() to select an idle target. + */ +void +spc_select(sc, acb) + struct spc_softc *sc; + struct spc_acb *acb; +{ + struct scsipi_periph *periph = acb->xs->xs_periph; + int target = periph->periph_target; + struct spc_tinfo *ti = &sc->sc_tinfo[target]; + bus_space_tag_t iot = sc->sc_iot; + bus_space_handle_t ioh = sc->sc_ioh; + + SPC_TRACE(("spc_select ")); + spc_setsync(sc, ti); + +#if 0 + bus_space_write_1(iot, ioh, SCMD, SCMD_SET_ATN); +#endif +#ifdef x68k /* XXX? */ + do { + asm ("nop"); + } while (bus_space_read_1(iot, ioh, SSTS) & + (SSTS_ACTIVE|SSTS_TARGET|SSTS_BUSY)); +#endif + + bus_space_write_1(iot, ioh, PCTL, 0); + bus_space_write_1(iot, ioh, TEMP, + (1 << sc->sc_initiator) | (1 << target)); + /* + * Setup BSY timeout (selection timeout). + * 250ms according to the SCSI specification. + * T = (X * 256 + 15) * Tclf * 2 (Tclf = 200ns on x68k) + * To setup 256ms timeout, + * 128000ns/200ns = X * 256 + 15 + * 640 - 15 = X * 256 + * X = 625 / 256 + * X = 2 + 113 / 256 + * ==> tch = 2, tcm = 113 (correct?) + */ + /* Time to the information transfer phase start. */ + /* XXX These values should be calculated from sc_freq */ + bus_space_write_1(iot, ioh, TCH, 2); + bus_space_write_1(iot, ioh, TCM, 113); + bus_space_write_1(iot, ioh, TCL, 3); + bus_space_write_1(iot, ioh, SCMD, SCMD_SELECT); + + sc->sc_state = SPC_SELECTING; +} + +int +spc_reselect(sc, message) + struct spc_softc *sc; + int message; +{ + u_char selid, target, lun; + struct spc_acb *acb; + struct scsipi_periph *periph; + struct spc_tinfo *ti; + + SPC_TRACE(("spc_reselect ")); + /* + * The SCSI chip made a snapshot of the data bus while the reselection + * was being negotiated. This enables us to determine which target did + * the reselect. + */ + selid = sc->sc_selid & ~(1 << sc->sc_initiator); + if (selid & (selid - 1)) { + printf("%s: reselect with invalid selid %02x; " + "sending DEVICE RESET\n", sc->sc_dev.dv_xname, selid); + SPC_BREAK(); + goto reset; + } + + /* + * Search wait queue for disconnected cmd + * The list should be short, so I haven't bothered with + * any more sophisticated structures than a simple + * singly linked list. + */ + target = ffs(selid) - 1; + lun = message & 0x07; + TAILQ_FOREACH(acb, &sc->nexus_list, chain) { + periph = acb->xs->xs_periph; + if (periph->periph_target == target && + periph->periph_lun == lun) + break; + } + if (acb == NULL) { + printf("%s: reselect from target %d lun %d with no nexus; " + "sending ABORT\n", sc->sc_dev.dv_xname, target, lun); + SPC_BREAK(); + goto abort; + } + + /* Make this nexus active again. */ + TAILQ_REMOVE(&sc->nexus_list, acb, chain); + sc->sc_state = SPC_CONNECTED; + sc->sc_nexus = acb; + ti = &sc->sc_tinfo[target]; + ti->lubusy |= (1 << lun); + spc_setsync(sc, ti); + + if (acb->flags & ACB_RESET) + spc_sched_msgout(sc, SEND_DEV_RESET); + else if (acb->flags & ACB_ABORT) + spc_sched_msgout(sc, SEND_ABORT); + + /* Do an implicit RESTORE POINTERS. */ + sc->sc_dp = acb->data_addr; + sc->sc_dleft = acb->data_length; + sc->sc_cp = (u_char *)&acb->scsipi_cmd; + sc->sc_cleft = acb->scsipi_cmd_length; + + return (0); + +reset: + spc_sched_msgout(sc, SEND_DEV_RESET); + return (1); + +abort: + spc_sched_msgout(sc, SEND_ABORT); + return (1); +} + +/* + * Schedule a SCSI operation. This has now been pulled out of the interrupt + * handler so that we may call it from spc_scsi_cmd and spc_done. This may + * save us an unecessary interrupt just to get things going. Should only be + * called when state == SPC_IDLE and at bio pl. + */ +void +spc_sched(sc) + struct spc_softc *sc; +{ + struct spc_acb *acb; + struct scsipi_periph *periph; + struct spc_tinfo *ti; + + /* missing the hw, just return and wait for our hw */ + if (sc->sc_flags & SPC_INACTIVE) + return; + SPC_TRACE(("spc_sched ")); + /* + * Find first acb in ready queue that is for a target/lunit pair that + * is not busy. + */ + TAILQ_FOREACH(acb, &sc->ready_list, chain) { + periph = acb->xs->xs_periph; + ti = &sc->sc_tinfo[periph->periph_target]; + if ((ti->lubusy & (1 << periph->periph_lun)) == 0) { + SPC_MISC(("selecting %d:%d ", + periph->periph_target, periph->periph_lun)); + TAILQ_REMOVE(&sc->ready_list, acb, chain); + sc->sc_nexus = acb; + spc_select(sc, acb); + return; + } else + SPC_MISC(("%d:%d busy\n", + periph->periph_target, periph->periph_lun)); + } + SPC_MISC(("idle ")); + /* Nothing to start; just enable reselections and wait. */ +} + +/* + * POST PROCESSING OF SCSI_CMD (usually current) + */ +void +spc_done(sc, acb) + struct spc_softc *sc; + struct spc_acb *acb; +{ + struct scsipi_xfer *xs = acb->xs; + struct scsipi_periph *periph = xs->xs_periph; + struct spc_tinfo *ti = &sc->sc_tinfo[periph->periph_target]; + + SPC_TRACE(("spc_done ")); + + if (xs->error == XS_NOERROR) { + if (acb->flags & ACB_ABORT) { + xs->error = XS_DRIVER_STUFFUP; + } else { + switch (acb->target_stat) { + case SCSI_CHECK: + /* First, save the return values */ + xs->resid = acb->data_length; + /* FALLTHROUGH */ + case SCSI_BUSY: + xs->status = acb->target_stat; + xs->error = XS_BUSY; + break; + case SCSI_OK: + xs->resid = acb->data_length; + break; + default: + xs->error = XS_DRIVER_STUFFUP; +#if SPC_DEBUG + printf("%s: spc_done: bad stat 0x%x\n", + sc->sc_dev.dv_xname, acb->target_stat); +#endif + break; + } + } + } + +#if SPC_DEBUG + if ((spc_debug & SPC_SHOWMISC) != 0) { + if (xs->resid != 0) + printf("resid=%d ", xs->resid); + else + printf("error=%d\n", xs->error); + } +#endif + + /* + * Remove the ACB from whatever queue it happens to be on. + */ + if (acb->flags & ACB_NEXUS) + ti->lubusy &= ~(1 << periph->periph_lun); + if (acb == sc->sc_nexus) { + sc->sc_nexus = NULL; + sc->sc_state = SPC_IDLE; + spc_sched(sc); + } else + spc_dequeue(sc, acb); + + spc_free_acb(sc, acb, xs->xs_control); + ti->cmds++; + scsipi_done(xs); +} + +void +spc_dequeue(sc, acb) + struct spc_softc *sc; + struct spc_acb *acb; +{ + + SPC_TRACE(("spc_dequeue ")); + if (acb->flags & ACB_NEXUS) + TAILQ_REMOVE(&sc->nexus_list, acb, chain); + else + TAILQ_REMOVE(&sc->ready_list, acb, chain); +} + +/* + * INTERRUPT/PROTOCOL ENGINE + */ + +/* + * Precondition: + * The SCSI bus is already in the MSGI phase and there is a message byte + * on the bus, along with an asserted REQ signal. + */ +void +spc_msgin(sc) + struct spc_softc *sc; +{ + bus_space_tag_t iot = sc->sc_iot; + bus_space_handle_t ioh = sc->sc_ioh; + int n; + + SPC_TRACE(("spc_msgin ")); + + if (sc->sc_prevphase == PH_MSGIN) { + /* This is a continuation of the previous message. */ + n = sc->sc_imp - sc->sc_imess; + goto nextbyte; + } + + /* This is a new MESSAGE IN phase. Clean up our state. */ + sc->sc_flags &= ~SPC_DROP_MSGIN; + +nextmsg: + n = 0; + sc->sc_imp = &sc->sc_imess[n]; + +nextbyte: + /* + * Read a whole message, but don't ack the last byte. If we reject the + * message, we have to assert ATN during the message transfer phase + * itself. + */ + for (;;) { + /* If parity error, just dump everything on the floor. */ + if ((bus_space_read_1(iot, ioh, SERR) & + (SERR_SCSI_PAR|SERR_SPC_PAR)) != 0) { + sc->sc_flags |= SPC_DROP_MSGIN; + spc_sched_msgout(sc, SEND_PARITY_ERROR); + } + + if ((bus_space_read_1(iot, ioh, PSNS) & PSNS_ATN) != 0) + bus_space_write_1(iot, ioh, SCMD, SCMD_RST_ATN); + + while ((bus_space_read_1(iot, ioh, PSNS) & PSNS_REQ) == 0) { + /* XXX needs timeout */ + if ((bus_space_read_1(iot, ioh, PSNS) & PH_MASK) + != PH_MSGIN || + (bus_space_read_1(iot, ioh, SSTS) & SSTS_INITIATOR) + == 0) + goto out; + } + + bus_space_write_1(iot, ioh, PCTL, PH_MSGIN); + bus_space_write_1(iot, ioh, SCMD, SCMD_SET_ACK); + while ((bus_space_read_1(iot, ioh, PSNS) & PSNS_REQ) != 0) + continue; /* XXX needs timeout */ + + /* Gather incoming message bytes if needed. */ + if ((sc->sc_flags & SPC_DROP_MSGIN) == 0) { + if (n >= SPC_MAX_MSG_LEN) { + (void) bus_space_read_1(iot, ioh, TEMP); + sc->sc_flags |= SPC_DROP_MSGIN; + spc_sched_msgout(sc, SEND_REJECT); + } else { + *sc->sc_imp++ = + bus_space_read_1(iot, ioh, TEMP); + n++; + /* + * This testing is suboptimal, but most + * messages will be of the one byte variety, so + * it should not affect performance + * significantly. + */ + if (n == 1 && MSG_IS1BYTE(sc->sc_imess[0])) + break; + if (n == 2 && MSG_IS2BYTE(sc->sc_imess[0])) + break; + if (n >= 3 && MSG_ISEXTENDED(sc->sc_imess[0]) && + n == sc->sc_imess[1] + 2) + break; + } + } else + (void) bus_space_read_1(iot, ioh, TEMP); + + /* + * If we reach this spot we're either: + * a) in the middle of a multi-byte message, or + * b) dropping bytes. + */ + + /* Ack the last byte read. */ + bus_space_write_1(iot, ioh, SCMD, SCMD_RST_ACK); + } + + SPC_MISC(("n=%d imess=0x%02x ", n, sc->sc_imess[0])); + + /* We now have a complete message. Parse it. */ + switch (sc->sc_state) { + struct spc_acb *acb; + struct scsipi_periph *periph; + struct spc_tinfo *ti; + + case SPC_CONNECTED: + SPC_ASSERT(sc->sc_nexus != NULL); + acb = sc->sc_nexus; + ti = &sc->sc_tinfo[acb->xs->xs_periph->periph_target]; + + switch (sc->sc_imess[0]) { + case MSG_CMDCOMPLETE: + if (sc->sc_dleft < 0) { + periph = acb->xs->xs_periph; + printf("%s: %d extra bytes from %d:%d\n", + sc->sc_dev.dv_xname, -sc->sc_dleft, + periph->periph_target, periph->periph_lun); + sc->sc_dleft = 0; + } + acb->xs->resid = acb->data_length = sc->sc_dleft; + sc->sc_state = SPC_CMDCOMPLETE; + break; + + case MSG_PARITY_ERROR: + /* Resend the last message. */ + spc_sched_msgout(sc, sc->sc_lastmsg); + break; + + case MSG_MESSAGE_REJECT: + SPC_MISC(("message rejected %02x ", sc->sc_lastmsg)); + switch (sc->sc_lastmsg) { +#if SPC_USE_SYNCHRONOUS + SPC_USE_WIDE + case SEND_IDENTIFY: + ti->flags &= ~(DO_SYNC | DO_WIDE); + ti->period = ti->offset = 0; + spc_setsync(sc, ti); + ti->width = 0; + break; +#endif +#if SPC_USE_SYNCHRONOUS + case SEND_SDTR: + ti->flags &= ~DO_SYNC; + ti->period = ti->offset = 0; + spc_setsync(sc, ti); + break; +#endif +#if SPC_USE_WIDE + case SEND_WDTR: + ti->flags &= ~DO_WIDE; + ti->width = 0; + break; +#endif + case SEND_INIT_DET_ERR: + spc_sched_msgout(sc, SEND_ABORT); + break; + } + break; + + case MSG_NOOP: + break; + + case MSG_DISCONNECT: + ti->dconns++; + sc->sc_state = SPC_DISCONNECT; + break; + + case MSG_SAVEDATAPOINTER: + acb->data_addr = sc->sc_dp; + acb->data_length = sc->sc_dleft; + break; + + case MSG_RESTOREPOINTERS: + sc->sc_dp = acb->data_addr; + sc->sc_dleft = acb->data_length; + sc->sc_cp = (u_char *)&acb->scsipi_cmd; + sc->sc_cleft = acb->scsipi_cmd_length; + break; + + case MSG_EXTENDED: + switch (sc->sc_imess[2]) { +#if SPC_USE_SYNCHRONOUS + case MSG_EXT_SDTR: + if (sc->sc_imess[1] != 3) + goto reject; + ti->period = sc->sc_imess[3]; + ti->offset = sc->sc_imess[4]; + ti->flags &= ~DO_SYNC; + if (ti->offset == 0) { + } else if (ti->period < sc->sc_minsync || + ti->period > sc->sc_maxsync || + ti->offset > 8) { + ti->period = ti->offset = 0; + spc_sched_msgout(sc, SEND_SDTR); + } else { + scsipi_printaddr(acb->xs->xs_periph); + printf("sync, offset %d, " + "period %dnsec\n", + ti->offset, ti->period * 4); + } + spc_setsync(sc, ti); + break; +#endif + +#if SPC_USE_WIDE + case MSG_EXT_WDTR: + if (sc->sc_imess[1] != 2) + goto reject; + ti->width = sc->sc_imess[3]; + ti->flags &= ~DO_WIDE; + if (ti->width == 0) { + } else if (ti->width > SPC_MAX_WIDTH) { + ti->width = 0; + spc_sched_msgout(sc, SEND_WDTR); + } else { + scsipi_printaddr(acb->xs->xs_periph); + printf("wide, width %d\n", + 1 << (3 + ti->width)); + } + break; +#endif + + default: + printf("%s: unrecognized MESSAGE EXTENDED; " + "sending REJECT\n", sc->sc_dev.dv_xname); + SPC_BREAK(); + goto reject; + } + break; + + default: + printf("%s: unrecognized MESSAGE; sending REJECT\n", + sc->sc_dev.dv_xname); + SPC_BREAK(); + reject: + spc_sched_msgout(sc, SEND_REJECT); + break; + } + break; + + case SPC_RESELECTED: + if (!MSG_ISIDENTIFY(sc->sc_imess[0])) { + printf("%s: reselect without IDENTIFY; " + "sending DEVICE RESET\n", sc->sc_dev.dv_xname); + SPC_BREAK(); + goto reset; + } + + (void) spc_reselect(sc, sc->sc_imess[0]); + break; + + default: + printf("%s: unexpected MESSAGE IN; sending DEVICE RESET\n", + sc->sc_dev.dv_xname); + SPC_BREAK(); + reset: + spc_sched_msgout(sc, SEND_DEV_RESET); + break; + +#ifdef notdef + abort: + spc_sched_msgout(sc, SEND_ABORT); + break; +#endif + } + + /* Ack the last message byte. */ + bus_space_write_1(iot, ioh, SCMD, SCMD_RST_ACK); + + /* Go get the next message, if any. */ + goto nextmsg; + +out: + bus_space_write_1(iot, ioh, SCMD, SCMD_RST_ACK); + SPC_MISC(("n=%d imess=0x%02x ", n, sc->sc_imess[0])); + + while ((bus_space_read_1(iot, ioh, SSTS) & SSTS_ACTIVE) + == SSTS_INITIATOR) + continue; /* XXX needs timeout */ +} + +/* + * Send the highest priority, scheduled message. + */ +void +spc_msgout(sc) + struct spc_softc *sc; +{ + bus_space_tag_t iot = sc->sc_iot; + bus_space_handle_t ioh = sc->sc_ioh; +#if SPC_USE_SYNCHRONOUS + struct spc_tinfo *ti; +#endif + int n; + + SPC_TRACE(("spc_msgout ")); + + if (sc->sc_prevphase == PH_MSGOUT) { + if (sc->sc_omp == sc->sc_omess) { + /* + * This is a retransmission. + * + * We get here if the target stayed in MESSAGE OUT + * phase. Section 5.1.9.2 of the SCSI 2 spec indicates + * that all of the previously transmitted messages must + * be sent again, in the same order. Therefore, we + * requeue all the previously transmitted messages, and + * start again from the top. Our simple priority + * scheme keeps the messages in the right order. + */ + SPC_MISC(("retransmitting ")); + sc->sc_msgpriq |= sc->sc_msgoutq; + /* + * Set ATN. If we're just sending a trivial 1-byte + * message, we'll clear ATN later on anyway. + */ + bus_space_write_1(iot, ioh, SCMD, + SCMD_SET_ATN); /* XXX? */ + } else { + /* This is a continuation of the previous message. */ + n = sc->sc_omp - sc->sc_omess; + goto nextbyte; + } + } + + /* No messages transmitted so far. */ + sc->sc_msgoutq = 0; + sc->sc_lastmsg = 0; + +nextmsg: + /* Pick up highest priority message. */ + sc->sc_currmsg = sc->sc_msgpriq & -sc->sc_msgpriq; + sc->sc_msgpriq &= ~sc->sc_currmsg; + sc->sc_msgoutq |= sc->sc_currmsg; + + /* Build the outgoing message data. */ + switch (sc->sc_currmsg) { + case SEND_IDENTIFY: + SPC_ASSERT(sc->sc_nexus != NULL); + sc->sc_omess[0] = + MSG_IDENTIFY(sc->sc_nexus->xs->xs_periph->periph_lun, 1); + n = 1; + break; + +#if SPC_USE_SYNCHRONOUS + case SEND_SDTR: + SPC_ASSERT(sc->sc_nexus != NULL); + ti = &sc->sc_tinfo[sc->sc_nexus->xs->xs_periph->periph_target]; + sc->sc_omess[4] = MSG_EXTENDED; + sc->sc_omess[3] = MSG_EXT_SDTR_LEN; + sc->sc_omess[2] = MSG_EXT_SDTR; + sc->sc_omess[1] = ti->period >> 2; + sc->sc_omess[0] = ti->offset; + n = 5; + break; +#endif + +#if SPC_USE_WIDE + case SEND_WDTR: + SPC_ASSERT(sc->sc_nexus != NULL); + ti = &sc->sc_tinfo[sc->sc_nexus->xs->xs_periph->periph_target]; + sc->sc_omess[3] = MSG_EXTENDED; + sc->sc_omess[2] = MSG_EXT_WDTR_LEN; + sc->sc_omess[1] = MSG_EXT_WDTR; + sc->sc_omess[0] = ti->width; + n = 4; + break; +#endif + + case SEND_DEV_RESET: + sc->sc_flags |= SPC_ABORTING; + sc->sc_omess[0] = MSG_BUS_DEV_RESET; + n = 1; + break; + + case SEND_REJECT: + sc->sc_omess[0] = MSG_MESSAGE_REJECT; + n = 1; + break; + + case SEND_PARITY_ERROR: + sc->sc_omess[0] = MSG_PARITY_ERROR; + n = 1; + break; + + case SEND_INIT_DET_ERR: + sc->sc_omess[0] = MSG_INITIATOR_DET_ERR; + n = 1; + break; + + case SEND_ABORT: + sc->sc_flags |= SPC_ABORTING; + sc->sc_omess[0] = MSG_ABORT; + n = 1; + break; + + default: + printf("%s: unexpected MESSAGE OUT; sending NOOP\n", + sc->sc_dev.dv_xname); + SPC_BREAK(); + sc->sc_omess[0] = MSG_NOOP; + n = 1; + break; + } + sc->sc_omp = &sc->sc_omess[n]; + +nextbyte: + /* Send message bytes. */ + /* send TRANSFER command. */ + bus_space_write_1(iot, ioh, TCH, n >> 16); + bus_space_write_1(iot, ioh, TCM, n >> 8); + bus_space_write_1(iot, ioh, TCL, n); + bus_space_write_1(iot, ioh, PCTL, sc->sc_phase | PCTL_BFINT_ENAB); +#ifdef x68k + bus_space_write_1(iot, ioh, SCMD, SCMD_XFR); /* XXX */ +#else + bus_space_write_1(iot, ioh, SCMD, + SCMD_XFR | SCMD_PROG_XFR); +#endif + for (;;) { + if ((bus_space_read_1(iot, ioh, SSTS) & SSTS_BUSY) != 0) + break; + if (bus_space_read_1(iot, ioh, INTS) != 0) + goto out; + } + for (;;) { +#if 0 + for (;;) { + if ((bus_space_read_1(iot, ioh, PSNS) & PSNS_REQ) != 0) + break; + /* Wait for REQINIT. XXX Need timeout. */ + } +#endif + if (bus_space_read_1(iot, ioh, INTS) != 0) { + /* + * Target left MESSAGE OUT, possibly to reject + * our message. + * + * If this is the last message being sent, then we + * deassert ATN, since either the target is going to + * ignore this message, or it's going to ask for a + * retransmission via MESSAGE PARITY ERROR (in which + * case we reassert ATN anyway). + */ +#if 0 + if (sc->sc_msgpriq == 0) + bus_space_write_1(iot, ioh, SCMD, SCMD_RST_ATN); +#endif + goto out; + } + +#if 0 + /* Clear ATN before last byte if this is the last message. */ + if (n == 1 && sc->sc_msgpriq == 0) + bus_space_write_1(iot, ioh, SCMD, SCMD_RST_ATN); +#endif + + while ((bus_space_read_1(iot, ioh, SSTS) & SSTS_DREG_FULL) != 0) + ; + /* Send message byte. */ + bus_space_write_1(iot, ioh, DREG, *--sc->sc_omp); + --n; + /* Keep track of the last message we've sent any bytes of. */ + sc->sc_lastmsg = sc->sc_currmsg; +#if 0 + /* Wait for ACK to be negated. XXX Need timeout. */ + while ((bus_space_read_1(iot, ioh, PSNS) & ACKI) != 0) + ; +#endif + + if (n == 0) + break; + } + + /* We get here only if the entire message has been transmitted. */ + if (sc->sc_msgpriq != 0) { + /* There are more outgoing messages. */ + goto nextmsg; + } + + /* + * The last message has been transmitted. We need to remember the last + * message transmitted (in case the target switches to MESSAGE IN phase + * and sends a MESSAGE REJECT), and the list of messages transmitted + * this time around (in case the target stays in MESSAGE OUT phase to + * request a retransmit). + */ + +out: + /* Disable REQ/ACK protocol. */ + return; +} + +/* + * spc_dataout_pio: perform a data transfer using the FIFO datapath in the spc + * Precondition: The SCSI bus should be in the DOUT phase, with REQ asserted + * and ACK deasserted (i.e. waiting for a data byte) + * + * This new revision has been optimized (I tried) to make the common case fast, + * and the rarer cases (as a result) somewhat more comlex + */ +int +spc_dataout_pio(sc, p, n) + struct spc_softc *sc; + u_char *p; + int n; +{ + bus_space_tag_t iot = sc->sc_iot; + bus_space_handle_t ioh = sc->sc_ioh; + u_char intstat = 0; + int out = 0; +#define DOUTAMOUNT 8 /* Full FIFO */ + + SPC_TRACE(("spc_dataout_pio ")); + /* send TRANSFER command. */ + bus_space_write_1(iot, ioh, TCH, n >> 16); + bus_space_write_1(iot, ioh, TCM, n >> 8); + bus_space_write_1(iot, ioh, TCL, n); + bus_space_write_1(iot, ioh, PCTL, sc->sc_phase | PCTL_BFINT_ENAB); +#ifdef x68k + bus_space_write_1(iot, ioh, SCMD, SCMD_XFR); /* XXX */ +#else + bus_space_write_1(iot, ioh, SCMD, + SCMD_XFR | SCMD_PROG_XFR); /* XXX */ +#endif + for (;;) { + if ((bus_space_read_1(iot, ioh, SSTS) & SSTS_BUSY) != 0) + break; + if (bus_space_read_1(iot, ioh, INTS) != 0) + break; + } + + /* + * I have tried to make the main loop as tight as possible. This + * means that some of the code following the loop is a bit more + * complex than otherwise. + */ + while (n > 0) { + int xfer; + + for (;;) { + intstat = bus_space_read_1(iot, ioh, INTS); + /* Wait till buffer is empty. */ + if ((bus_space_read_1(iot, ioh, SSTS) & + SSTS_DREG_EMPTY) != 0) + break; + /* Break on interrupt. */ + if (intstat != 0) + goto phasechange; + } + + xfer = min(DOUTAMOUNT, n); + + SPC_MISC(("%d> ", xfer)); + + n -= xfer; + out += xfer; + + bus_space_write_multi_1(iot, ioh, DREG, p, xfer); + p += xfer; + } + + if (out == 0) { + for (;;) { + if (bus_space_read_1(iot, ioh, INTS) != 0) + break; + } + SPC_MISC(("extra data ")); + } else { + /* See the bytes off chip */ + for (;;) { + /* Wait till buffer is empty. */ + if ((bus_space_read_1(iot, ioh, SSTS) & + SSTS_DREG_EMPTY) != 0) + break; + intstat = bus_space_read_1(iot, ioh, INTS); + /* Break on interrupt. */ + if (intstat != 0) + goto phasechange; + } + } + +phasechange: + /* Stop the FIFO data path. */ + + if (intstat != 0) { + /* Some sort of phase change. */ + int amount; + + amount = (bus_space_read_1(iot, ioh, TCH) << 16) | + (bus_space_read_1(iot, ioh, TCM) << 8) | + bus_space_read_1(iot, ioh, TCL); + if (amount > 0) { + out -= amount; + SPC_MISC(("+%d ", amount)); + } + } + + return out; +} + +/* + * spc_datain_pio: perform data transfers using the FIFO datapath in the spc + * Precondition: The SCSI bus should be in the DIN phase, with REQ asserted + * and ACK deasserted (i.e. at least one byte is ready). + * + * For now, uses a pretty dumb algorithm, hangs around until all data has been + * transferred. This, is OK for fast targets, but not so smart for slow + * targets which don't disconnect or for huge transfers. + */ +int +spc_datain_pio(sc, p, n) + struct spc_softc *sc; + u_char *p; + int n; +{ + bus_space_tag_t iot = sc->sc_iot; + bus_space_handle_t ioh = sc->sc_ioh; + int in = 0; + u_int8_t intstat, sstat; +#define DINAMOUNT 8 /* Full FIFO */ + + SPC_TRACE(("spc_datain_pio ")); + /* send TRANSFER command. */ + bus_space_write_1(iot, ioh, TCH, n >> 16); + bus_space_write_1(iot, ioh, TCM, n >> 8); + bus_space_write_1(iot, ioh, TCL, n); + bus_space_write_1(iot, ioh, PCTL, sc->sc_phase | PCTL_BFINT_ENAB); +#ifdef x68k + bus_space_write_1(iot, ioh, SCMD, SCMD_XFR); /* XXX */ +#else + bus_space_write_1(iot, ioh, SCMD, + SCMD_XFR | SCMD_PROG_XFR); /* XXX */ +#endif + for (;;) { + if ((bus_space_read_1(iot, ioh, SSTS) & SSTS_BUSY) != 0) + break; + if (bus_space_read_1(iot, ioh, INTS) != 0) + goto phasechange; + } + + /* + * We leave this loop if one or more of the following is true: + * a) phase != PH_DATAIN && FIFOs are empty + * b) reset has occurred or busfree is detected. + */ + while (n > 0) { + int xfer; + + /* Wait for fifo half full or phase mismatch */ + for (;;) { + /* XXX needs timeout */ + intstat = bus_space_read_1(iot, ioh, INTS); + sstat = bus_space_read_1(iot, ioh, SSTS); + if (intstat != 0 || + (sstat & SSTS_DREG_EMPTY) == 0) + break; + } + + if (sstat & SSTS_DREG_FULL) { + xfer = DINAMOUNT; + n -= xfer; + in += xfer; + bus_space_read_multi_1(iot, ioh, DREG, p, xfer); + p += xfer; + } + while (n > 0 && + (bus_space_read_1(iot, ioh, SSTS) & SSTS_DREG_EMPTY) == 0) { + n--; + in++; + *p++ = bus_space_read_1(iot, ioh, DREG); + } + + if (intstat != 0) + goto phasechange; + } + + /* + * Some SCSI-devices are rude enough to transfer more data than what + * was requested, e.g. 2048 bytes from a CD-ROM instead of the + * requested 512. Test for progress, i.e. real transfers. If no real + * transfers have been performed (n is probably already zero) and the + * FIFO is not empty, waste some bytes.... + */ + if (in == 0) { + for (;;) { + /* XXX needs timeout */ + if (bus_space_read_1(iot, ioh, INTS) != 0) + break; + } + SPC_MISC(("extra data ")); + } + +phasechange: + /* Stop the FIFO data path. */ + + return in; +} + +/* + * Catch an interrupt from the adaptor + */ +/* + * This is the workhorse routine of the driver. + * Deficiencies (for now): + * 1) always uses programmed I/O + */ +int +spc_intr(arg) + void *arg; +{ + struct spc_softc *sc = arg; + bus_space_tag_t iot = sc->sc_iot; + bus_space_handle_t ioh = sc->sc_ioh; + u_char ints; + struct spc_acb *acb; + struct scsipi_periph *periph; + struct spc_tinfo *ti; + int n; + + /* + * Disable interrupt. + */ + bus_space_write_1(iot, ioh, SCTL, + bus_space_read_1(iot, ioh, SCTL) & ~SCTL_INTR_ENAB); + + SPC_TRACE(("spc_intr ")); + + ints = bus_space_read_1(iot, ioh, INTS); + if (ints == 0) + goto out; + + if (sc->sc_dma_done != NULL && + sc->sc_state == SPC_CONNECTED && + (sc->sc_flags & SPC_DOINGDMA) != 0 && + (sc->sc_phase == PH_DATAOUT || sc->sc_phase == PH_DATAIN)) { + (*sc->sc_dma_done)(sc); + } + +loop: + /* + * Loop until transfer completion. + */ + /* + * First check for abnormal conditions, such as reset. + */ +#ifdef x68k /* XXX? */ + while ((ints = bus_space_read_1(iot, ioh, INTS)) == 0) + delay(1); + SPC_MISC(("ints = 0x%x ", ints)); +#else + ints = bus_space_read_1(iot, ioh, INTS); + SPC_MISC(("ints = 0x%x ", ints)); +#endif + + if ((ints & INTS_RST) != 0) { + printf("%s: SCSI bus reset\n", sc->sc_dev.dv_xname); + goto reset; + } + + /* + * Check for less serious errors. + */ + if ((bus_space_read_1(iot, ioh, SERR) & (SERR_SCSI_PAR|SERR_SPC_PAR)) + != 0) { + printf("%s: SCSI bus parity error\n", sc->sc_dev.dv_xname); + if (sc->sc_prevphase == PH_MSGIN) { + sc->sc_flags |= SPC_DROP_MSGIN; + spc_sched_msgout(sc, SEND_PARITY_ERROR); + } else + spc_sched_msgout(sc, SEND_INIT_DET_ERR); + } + + /* + * If we're not already busy doing something test for the following + * conditions: + * 1) We have been reselected by something + * 2) We have selected something successfully + * 3) Our selection process has timed out + * 4) This is really a bus free interrupt just to get a new command + * going? + * 5) Spurious interrupt? + */ + switch (sc->sc_state) { + case SPC_IDLE: + case SPC_SELECTING: + SPC_MISC(("ints:0x%02x ", ints)); + + if ((ints & INTS_SEL) != 0) { + /* + * We don't currently support target mode. + */ + printf("%s: target mode selected; going to BUS FREE\n", + sc->sc_dev.dv_xname); + + goto sched; + } else if ((ints & INTS_RESEL) != 0) { + SPC_MISC(("reselected ")); + + /* + * If we're trying to select a target ourselves, + * push our command back into the ready list. + */ + if (sc->sc_state == SPC_SELECTING) { + SPC_MISC(("backoff selector ")); + SPC_ASSERT(sc->sc_nexus != NULL); + acb = sc->sc_nexus; + sc->sc_nexus = NULL; + TAILQ_INSERT_HEAD(&sc->ready_list, acb, chain); + } + + /* Save reselection ID. */ + sc->sc_selid = bus_space_read_1(iot, ioh, TEMP); + + sc->sc_state = SPC_RESELECTED; + } else if ((ints & INTS_CMD_DONE) != 0) { + SPC_MISC(("selected ")); + + /* + * We have selected a target. Things to do: + * a) Determine what message(s) to send. + * b) Verify that we're still selecting the target. + * c) Mark device as busy. + */ + if (sc->sc_state != SPC_SELECTING) { + printf("%s: selection out while idle; " + "resetting\n", sc->sc_dev.dv_xname); + SPC_BREAK(); + goto reset; + } + SPC_ASSERT(sc->sc_nexus != NULL); + acb = sc->sc_nexus; + periph = acb->xs->xs_periph; + ti = &sc->sc_tinfo[periph->periph_target]; + + sc->sc_msgpriq = SEND_IDENTIFY; + if (acb->flags & ACB_RESET) + sc->sc_msgpriq |= SEND_DEV_RESET; + else if (acb->flags & ACB_ABORT) + sc->sc_msgpriq |= SEND_ABORT; + else { +#if SPC_USE_SYNCHRONOUS + if ((ti->flags & DO_SYNC) != 0) + sc->sc_msgpriq |= SEND_SDTR; +#endif +#if SPC_USE_WIDE + if ((ti->flags & DO_WIDE) != 0) + sc->sc_msgpriq |= SEND_WDTR; +#endif + } + + acb->flags |= ACB_NEXUS; + ti->lubusy |= (1 << periph->periph_lun); + + /* Do an implicit RESTORE POINTERS. */ + sc->sc_dp = acb->data_addr; + sc->sc_dleft = acb->data_length; + sc->sc_cp = (u_char *)&acb->scsipi_cmd; + sc->sc_cleft = acb->scsipi_cmd_length; + + /* On our first connection, schedule a timeout. */ + if ((acb->xs->xs_control & XS_CTL_POLL) == 0) + callout_reset(&acb->xs->xs_callout, + mstohz(acb->timeout), spc_timeout, acb); + + sc->sc_state = SPC_CONNECTED; + } else if ((ints & INTS_TIMEOUT) != 0) { + SPC_MISC(("selection timeout ")); + + if (sc->sc_state != SPC_SELECTING) { + printf("%s: selection timeout while idle; " + "resetting\n", sc->sc_dev.dv_xname); + SPC_BREAK(); + goto reset; + } + SPC_ASSERT(sc->sc_nexus != NULL); + acb = sc->sc_nexus; + + delay(250); + + acb->xs->error = XS_SELTIMEOUT; + goto finish; + } else { + if (sc->sc_state != SPC_IDLE) { + printf("%s: BUS FREE while not idle; " + "state=%d\n", + sc->sc_dev.dv_xname, sc->sc_state); + SPC_BREAK(); + goto out; + } + + goto sched; + } + + /* + * Turn off selection stuff, and prepare to catch bus free + * interrupts, parity errors, and phase changes. + */ + + sc->sc_flags = 0; + sc->sc_prevphase = PH_INVALID; + goto dophase; + } + + if ((ints & INTS_DISCON) != 0) { + /* We've gone to BUS FREE phase. */ + /* disable disconnect interrupt */ + bus_space_write_1(iot, ioh, PCTL, + bus_space_read_1(iot, ioh, PCTL) & ~PCTL_BFINT_ENAB); + /* XXX reset interrput */ + bus_space_write_1(iot, ioh, INTS, ints); + + switch (sc->sc_state) { + case SPC_RESELECTED: + goto sched; + + case SPC_CONNECTED: + SPC_ASSERT(sc->sc_nexus != NULL); + acb = sc->sc_nexus; + +#if SPC_USE_SYNCHRONOUS + SPC_USE_WIDE + if (sc->sc_prevphase == PH_MSGOUT) { + /* + * If the target went to BUS FREE phase during + * or immediately after sending a SDTR or WDTR + * message, disable negotiation. + */ + periph = acb->xs->xs_periph; + ti = &sc->sc_tinfo[periph->periph_target]; + switch (sc->sc_lastmsg) { +#if SPC_USE_SYNCHRONOUS + case SEND_SDTR: + ti->flags &= ~DO_SYNC; + ti->period = ti->offset = 0; + break; +#endif +#if SPC_USE_WIDE + case SEND_WDTR: + ti->flags &= ~DO_WIDE; + ti->width = 0; + break; +#endif + } + } +#endif + + if ((sc->sc_flags & SPC_ABORTING) == 0) { + /* + * Section 5.1.1 of the SCSI 2 spec suggests + * issuing a REQUEST SENSE following an + * unexpected disconnect. Some devices go into + * a contingent allegiance condition when + * disconnecting, and this is necessary to + * clean up their state. + */ + printf("%s: unexpected disconnect; " + "sending REQUEST SENSE\n", + sc->sc_dev.dv_xname); + SPC_BREAK(); + acb->target_stat = SCSI_CHECK; + acb->xs->error = XS_NOERROR; + goto finish; + } + + acb->xs->error = XS_DRIVER_STUFFUP; + goto finish; + + case SPC_DISCONNECT: + SPC_ASSERT(sc->sc_nexus != NULL); + acb = sc->sc_nexus; + TAILQ_INSERT_HEAD(&sc->nexus_list, acb, chain); + sc->sc_nexus = NULL; + goto sched; + + case SPC_CMDCOMPLETE: + SPC_ASSERT(sc->sc_nexus != NULL); + acb = sc->sc_nexus; + goto finish; + } + } + else if ((ints & INTS_CMD_DONE) != 0 && + sc->sc_prevphase == PH_MSGIN && + sc->sc_state != SPC_CONNECTED) + goto out; + +dophase: +#if 0 + if ((bus_space_read_1(iot, ioh, PSNS) & PSNS_REQ) == 0) { + /* Wait for REQINIT. */ + goto out; + } +#else + bus_space_write_1(iot, ioh, INTS, ints); + ints = 0; + while ((bus_space_read_1(iot, ioh, PSNS) & PSNS_REQ) == 0) + delay(1); /* need timeout XXX */ +#endif + + /* + * State transition. + */ + sc->sc_phase = bus_space_read_1(iot, ioh, PSNS) & PH_MASK; +#if 0 + bus_space_write_1(iot, ioh, PCTL, sc->sc_phase); +#endif + + SPC_MISC(("phase=%d\n", sc->sc_phase)); + switch (sc->sc_phase) { + case PH_MSGOUT: + if (sc->sc_state != SPC_CONNECTED && + sc->sc_state != SPC_RESELECTED) + break; + spc_msgout(sc); + sc->sc_prevphase = PH_MSGOUT; + goto loop; + + case PH_MSGIN: + if (sc->sc_state != SPC_CONNECTED && + sc->sc_state != SPC_RESELECTED) + break; + spc_msgin(sc); + sc->sc_prevphase = PH_MSGIN; + goto loop; + + case PH_CMD: + if (sc->sc_state != SPC_CONNECTED) + break; +#if SPC_DEBUG + if ((spc_debug & SPC_SHOWMISC) != 0) { + SPC_ASSERT(sc->sc_nexus != NULL); + acb = sc->sc_nexus; + printf("cmd=0x%02x+%d ", + acb->scsipi_cmd.opcode, acb->scsipi_cmd_length - 1); + } +#endif + n = spc_dataout_pio(sc, sc->sc_cp, sc->sc_cleft); + sc->sc_cp += n; + sc->sc_cleft -= n; + sc->sc_prevphase = PH_CMD; + goto loop; + + case PH_DATAOUT: + if (sc->sc_state != SPC_CONNECTED) + break; + SPC_MISC(("dataout dleft=%d ", sc->sc_dleft)); + if (sc->sc_dma_start != NULL && + sc->sc_dleft > SPC_MIN_DMA_LEN) { + (*sc->sc_dma_start)(sc, sc->sc_dp, sc->sc_dleft, 0); + sc->sc_prevphase = PH_DATAOUT; + goto out; + } + n = spc_dataout_pio(sc, sc->sc_dp, sc->sc_dleft); + sc->sc_dp += n; + sc->sc_dleft -= n; + sc->sc_prevphase = PH_DATAOUT; + goto loop; + + case PH_DATAIN: + if (sc->sc_state != SPC_CONNECTED) + break; + SPC_MISC(("datain ")); + if (sc->sc_dma_start != NULL && + sc->sc_dleft > SPC_MIN_DMA_LEN) { + (*sc->sc_dma_start)(sc, sc->sc_dp, sc->sc_dleft, 1); + sc->sc_prevphase = PH_DATAIN; + goto out; + } + n = spc_datain_pio(sc, sc->sc_dp, sc->sc_dleft); + sc->sc_dp += n; + sc->sc_dleft -= n; + sc->sc_prevphase = PH_DATAIN; + goto loop; + + case PH_STAT: + if (sc->sc_state != SPC_CONNECTED) + break; + SPC_ASSERT(sc->sc_nexus != NULL); + acb = sc->sc_nexus; + + if ((bus_space_read_1(iot, ioh, PSNS) & PSNS_ATN) != 0) + bus_space_write_1(iot, ioh, SCMD, SCMD_RST_ATN); + while ((bus_space_read_1(iot, ioh, PSNS) & PSNS_REQ) == 0) + continue; /* XXX needs timeout */ + bus_space_write_1(iot, ioh, PCTL, PH_STAT); + bus_space_write_1(iot, ioh, SCMD, SCMD_SET_ACK); + while ((bus_space_read_1(iot, ioh, PSNS) & PSNS_REQ) != 0) + continue; /* XXX needs timeout */ + acb->target_stat = bus_space_read_1(iot, ioh, TEMP); + bus_space_write_1(iot, ioh, SCMD, SCMD_RST_ACK); + + SPC_MISC(("target_stat=0x%02x ", acb->target_stat)); + sc->sc_prevphase = PH_STAT; + goto loop; + } + + printf("%s: unexpected bus phase; resetting\n", sc->sc_dev.dv_xname); + SPC_BREAK(); +reset: + spc_init(sc); + return 1; + +finish: + callout_stop(&acb->xs->xs_callout); + bus_space_write_1(iot, ioh, INTS, ints); + ints = 0; + spc_done(sc, acb); + goto out; + +sched: + sc->sc_state = SPC_IDLE; + spc_sched(sc); + goto out; + +out: + if (ints) + bus_space_write_1(iot, ioh, INTS, ints); + bus_space_write_1(iot, ioh, SCTL, + bus_space_read_1(iot, ioh, SCTL) | SCTL_INTR_ENAB); + return 1; +} + +void +spc_abort(sc, acb) + struct spc_softc *sc; + struct spc_acb *acb; +{ + + /* 2 secs for the abort */ + acb->timeout = SPC_ABORT_TIMEOUT; + acb->flags |= ACB_ABORT; + + if (acb == sc->sc_nexus) { + /* + * If we're still selecting, the message will be scheduled + * after selection is complete. + */ + if (sc->sc_state == SPC_CONNECTED) + spc_sched_msgout(sc, SEND_ABORT); + } else { + spc_dequeue(sc, acb); + TAILQ_INSERT_HEAD(&sc->ready_list, acb, chain); + if (sc->sc_state == SPC_IDLE) + spc_sched(sc); + } +} + +void +spc_timeout(arg) + void *arg; +{ + struct spc_acb *acb = arg; + struct scsipi_xfer *xs = acb->xs; + struct scsipi_periph *periph = xs->xs_periph; + struct spc_softc *sc; + int s; + + sc = (void *)periph->periph_channel->chan_adapter->adapt_dev; + scsipi_printaddr(periph); + printf("timed out"); + + s = splbio(); + + if (acb->flags & ACB_ABORT) { + /* abort timed out */ + printf(" AGAIN\n"); + /* XXX Must reset! */ + } else { + /* abort the operation that has timed out */ + printf("\n"); + acb->xs->error = XS_TIMEOUT; + spc_abort(sc, acb); + } + + splx(s); +} + +#ifdef SPC_DEBUG +/* + * The following functions are mostly used for debugging purposes, either + * directly called from the driver or from the kernel debugger. + */ + +void +spc_show_scsi_cmd(acb) + struct spc_acb *acb; +{ + u_char *b = (u_char *)&acb->scsipi_cmd; + int i; + + scsipi_printaddr(acb->xs->xs_periph); + if ((acb->xs->xs_control & XS_CTL_RESET) == 0) { + for (i = 0; i < acb->scsipi_cmd_length; i++) { + if (i) + printf(","); + printf("%x", b[i]); + } + printf("\n"); + } else + printf("RESET\n"); +} + +void +spc_print_acb(acb) + struct spc_acb *acb; +{ + + printf("acb@%p xs=%p flags=%x", acb, acb->xs, acb->flags); + printf(" dp=%p dleft=%d target_stat=%x\n", + acb->data_addr, acb->data_length, acb->target_stat); + spc_show_scsi_cmd(acb); +} + +void +spc_print_active_acb() +{ + struct spc_acb *acb; + struct spc_softc *sc = spc_cd.cd_devs[0]; /* XXX */ + + printf("ready list:\n"); + TAILQ_FOREACH(acb, &sc->ready_list, chain) + spc_print_acb(acb); + printf("nexus:\n"); + if (sc->sc_nexus != NULL) + spc_print_acb(sc->sc_nexus); + printf("nexus list:\n"); + TAILQ_FOREACH(acb, &sc->nexus_list, chain) + spc_print_acb(acb); +} + +void +spc_dump89352(sc) + struct spc_softc *sc; +{ + bus_space_tag_t iot = sc->sc_iot; + bus_space_handle_t ioh = sc->sc_ioh; + + printf("mb89352: BDID=%x SCTL=%x SCMD=%x TMOD=%x\n", + bus_space_read_1(iot, ioh, BDID), + bus_space_read_1(iot, ioh, SCTL), + bus_space_read_1(iot, ioh, SCMD), + bus_space_read_1(iot, ioh, TMOD)); + printf(" INTS=%x PSNS=%x SSTS=%x SERR=%x PCTL=%x\n", + bus_space_read_1(iot, ioh, INTS), + bus_space_read_1(iot, ioh, PSNS), + bus_space_read_1(iot, ioh, SSTS), + bus_space_read_1(iot, ioh, SERR), + bus_space_read_1(iot, ioh, PCTL)); + printf(" MBC=%x DREG=%x TEMP=%x TCH=%x TCM=%x\n", + bus_space_read_1(iot, ioh, MBC), +#if 0 + bus_space_read_1(iot, ioh, DREG), +#else + 0, +#endif + bus_space_read_1(iot, ioh, TEMP), + bus_space_read_1(iot, ioh, TCH), + bus_space_read_1(iot, ioh, TCM)); + printf(" TCL=%x EXBF=%x\n", + bus_space_read_1(iot, ioh, TCL), + bus_space_read_1(iot, ioh, EXBF)); +} + +void +spc_dump_driver(sc) + struct spc_softc *sc; +{ + struct spc_tinfo *ti; + int i; + + printf("nexus=%p prevphase=%x\n", sc->sc_nexus, sc->sc_prevphase); + printf("state=%x msgin=%x msgpriq=%x msgoutq=%x lastmsg=%x " + "currmsg=%x\n", sc->sc_state, sc->sc_imess[0], + sc->sc_msgpriq, sc->sc_msgoutq, sc->sc_lastmsg, sc->sc_currmsg); + for (i = 0; i < 7; i++) { + ti = &sc->sc_tinfo[i]; + printf("tinfo%d: %d cmds %d disconnects %d timeouts", + i, ti->cmds, ti->dconns, ti->touts); + printf(" %d senses flags=%x\n", ti->senses, ti->flags); + } +} +#endif --- /dev/null 2003-06-30 23:47:47.000000000 +0900 +++ dev/mb89352reg.h 2003-05-20 00:05:43.000000000 +0900 @@ -0,0 +1,203 @@ +/* $NetBSD$ */ +/* NecBSD: mb89352reg.h,v 1.3 1998/03/14 07:04:34 kmatsuda Exp */ + +/*- + * Copyright (c) 1996,97,98,99 The NetBSD Foundation, Inc. + * All rights reserved. + * + * Copyright (c) 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Charles M. Hannum, Masaru Oki and Kouichi Matsuda. + * + * This code is derived from software contributed to Berkeley by + * Van Jacobson of Lawrence Berkeley Laboratory. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)scsireg.h 8.1 (Berkeley) 6/10/93 + */ +/* + * [NetBSD for NEC PC-98 series] + * Copyright (c) 1996, 1997, 1998 + * NetBSD/pc98 porting staff. All rights reserved. + * Copyright (c) 1996, 1997, 1998 + * Kouichi Matsuda. All rights reserved. + */ + +/* + * FUJITSU MB89352A SCSI Protocol Controler Hardware Description. + */ + +/* Definitions, most of them has turned out to be unneccesary, but here they + * are anyway. + */ + +#define BDID 0x01 /* Bus Device ID (R/W) */ +#define SCTL 0x03 /* SPC Control register (R/W) */ +#define SCMD 0x05 /* Command Register (R/W) */ +#define TMOD 0x07 /* Transmit Mode Register (synch models) */ +#define INTS 0x09 /* Interrupt sense (R); Interrupt Reset (W) */ +#define PSNS 0x0b /* Phase Sence (R); SPC Diagnostic Control (W) */ +#define SSTS 0x0d /* SPC status (R/O) */ +#define SERR 0x0f /* SPC error status (R/O) */ +#define PCTL 0x11 /* Phase Control (R/W) */ +#define MBC 0x13 /* Modified Byte Counter (R/O) */ +#define DREG 0x15 /* Data Register (R/W) */ +#define TEMP 0x17 /* Temporary Register (R/W) */ +#define TCH 0x19 /* Transfer Counter High (R/W) */ +#define TCM 0x1b /* Transfer Counter Middle (R/W) */ +#define TCL 0x1d /* Transfer Counter Low (R/W) */ +#define EXBF 0x1f /* External Buffer (synch models) */ + +/* What all the bits do */ + +/* SCSI_BDID */ +/* SCSI selection/reselection ID (both target *and* initiator) */ +#define SELID7 0x80 +#define SELID6 0x40 +#define SELID5 0x20 +#define SELID4 0x10 +#define SELID3 0x08 +#define SELID2 0x04 +#define SELID1 0x02 +#define SELID0 0x01 + +/* SCSI_SCTL */ +#define SCTL_DISABLE 0x80 +#define SCTL_CTRLRST 0x40 +#define SCTL_DIAG 0x20 +#define SCTL_ABRT_ENAB 0x10 +#define SCTL_PARITY_ENAB 0x08 +#define SCTL_SEL_ENAB 0x04 +#define SCTL_RESEL_ENAB 0x02 +#define SCTL_INTR_ENAB 0x01 + +/* SCSI_SCMD */ +#define SCMD_RST 0x10 +#define SCMD_ICPT_XFR 0x08 +#define SCMD_PROG_XFR 0x04 +#define SCMD_PAD 0x01 /* if initiator */ +#define SCMD_PERR_STOP 0x01 /* if target */ + /* command codes */ +#define SCMD_BUS_REL 0x00 +#define SCMD_SELECT 0x20 +#define SCMD_RST_ATN 0x40 +#define SCMD_SET_ATN 0x60 +#define SCMD_XFR 0x80 +#define SCMD_XFR_PAUSE 0xa0 +#define SCMD_RST_ACK 0xc0 +#define SCMD_SET_ACK 0xe0 + +/* SCSI_TMOD */ +#define TMOD_SYNC 0x80 + +/* SCSI_INTS */ +#define INTS_SEL 0x80 +#define INTS_RESEL 0x40 +#define INTS_DISCON 0x20 +#define INTS_CMD_DONE 0x10 +#define INTS_SRV_REQ 0x08 +#define INTS_TIMEOUT 0x04 +#define INTS_HARD_ERR 0x02 +#define INTS_RST 0x01 + +/* SCSI_PSNS */ +#define PSNS_REQ 0x80 +#define PSNS_ACK 0x40 +#define PSNS_ATN 0x20 +#define PSNS_SEL 0x10 +#define PSNS_BSY 0x08 + +/* PSNS */ +#define REQI 0x80 +#define ACKI 0x40 +#define ATNI 0x20 +#define SELI 0x10 +#define BSYI 0x08 +#define MSGI 0x04 +#define CDI 0x02 +#define IOI 0x01 + +/* Important! The 3 most significant bits of this register, in initiator mode, + * represents the "expected" SCSI bus phase and can be used to trigger phase + * mismatch and phase change interrupts. But more important: If there is a + * phase mismatch the chip will not transfer any data! This is actually a nice + * feature as it gives us a bit more control over what is happening when we are + * bursting data (in) through the FIFOs and the phase suddenly changes from + * DATA IN to STATUS or MESSAGE IN. The transfer will stop and wait for the + * proper phase to be set in this register instead of dumping the bits into the + * FIFOs. + */ +#if 0 +#define REQO 0x80 +#define ACKO 0x40 +#define ATNO 0x20 +#define SELO 0x10 +#define BSYO 0x08 +#endif +/* PCTL */ +#define MSGO 0x04 +#define CDO 0x02 +#define IOO 0x01 + +/* Information transfer phases */ +#define PH_DATAOUT (0) +#define PH_DATAIN (IOI) +#define PH_CMD (CDI) +#define PH_STAT (CDI | IOI) +#define PH_MSGOUT (MSGI | CDI) +#define PH_MSGIN (MSGI | CDI | IOI) + +#define PH_MASK (MSGI | CDI | IOI) + +#define PH_INVALID 0xff + +/* SCSI_SSTS */ +#define SSTS_INITIATOR 0x80 +#define SSTS_TARGET 0x40 +#define SSTS_BUSY 0x20 +#define SSTS_XFR 0x10 +#define SSTS_ACTIVE (SSTS_INITIATOR|SSTS_XFR) +#define SSTS_RST 0x08 +#define SSTS_TCZERO 0x04 +#define SSTS_DREG_FULL 0x02 +#define SSTS_DREG_EMPTY 0x01 + +/* SCSI_SERR */ +#define SERR_SCSI_PAR 0x80 +#define SERR_SPC_PAR 0x40 +#define SERR_TC_PAR 0x08 +#define SERR_PHASE_ERR 0x04 +#define SERR_SHORT_XFR 0x02 +#define SERR_OFFSET 0x01 + +/* SCSI_PCTL */ +#define PCTL_BFINT_ENAB 0x80 --- /dev/null 2003-06-30 23:47:47.000000000 +0900 +++ dev/mb89352var.h 2003-06-28 11:39:06.000000000 +0900 @@ -0,0 +1,211 @@ +/* $NetBSD$ */ +/* NecBSD: mb89352var.h,v 1.4 1998/03/14 07:31:22 kmatsuda Exp */ + +/*- + * Copyright (c) 1996,97,98,99 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Charles M. Hannum, Masaru Oki and Kouichi Matsuda. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Charles M. Hannum. + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * Copyright (c) 1994 Jarle Greipsland + * 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. + */ +/* + * [NetBSD for NEC PC-98 series] + * Copyright (c) 1996, 1997, 1998 + * NetBSD/pc98 porting staff. All rights reserved. + * Copyright (c) 1996, 1997, 1998 + * Kouich Matsuda. All rights reserved. + */ + +#ifndef _MB89352VAR_H_ +#define _MB89352VAR_H_ +/* + * ACB. Holds additional information for each SCSI command Comments: We + * need a separate scsi command block because we may need to overwrite it + * with a request sense command. Basicly, we refrain from fiddling with + * the scsi_xfer struct (except do the expected updating of return values). + * We'll generally update: xs->{flags,resid,error,sense,status} and + * occasionally xs->retries. + */ +struct spc_acb { + struct scsi_generic scsipi_cmd; + int scsipi_cmd_length; + u_char *data_addr; /* Saved data pointer */ + int data_length; /* Residue */ + + u_char target_stat; /* SCSI status byte */ + +#ifdef notdef + struct spc_dma_seg dma[SPC_NSEG]; /* Physical addresses+len */ +#endif + + TAILQ_ENTRY(spc_acb) chain; + struct scsipi_xfer *xs; /* SCSI xfer ctrl block from above */ + int flags; +#define ACB_ALLOC 0x01 +#define ACB_NEXUS 0x02 +#define ACB_SENSE 0x04 +#define ACB_ABORT 0x40 +#define ACB_RESET 0x80 + int timeout; +}; + +/* + * 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. + */ +struct spc_tinfo { + int cmds; /* #commands processed */ + int dconns; /* #disconnects */ + int touts; /* #timeouts */ + int perrs; /* #parity errors */ + int senses; /* #request sense commands sent */ + ushort lubusy; /* What local units/subr. are busy? */ + u_char flags; +#define DO_SYNC 0x01 /* (Re)Negotiate synchronous options */ +#define DO_WIDE 0x02 /* (Re)Negotiate wide options */ + u_char period; /* Period suggestion */ + u_char offset; /* Offset suggestion */ + u_char width; /* Width suggestion */ +}; + +struct spc_softc { + struct device sc_dev; + + bus_space_tag_t sc_iot; + bus_space_handle_t sc_ioh; + + struct scsipi_channel sc_channel; /* prototype for subdevs */ + struct scsipi_adapter sc_adapter; + + TAILQ_HEAD(, spc_acb) free_list, ready_list, nexus_list; + struct spc_acb *sc_nexus; /* current command */ + struct spc_acb sc_acb[8]; + struct spc_tinfo sc_tinfo[8]; + + /* Data about the current nexus (updated for every cmd switch) */ + u_char *sc_dp; /* Current data pointer */ + size_t sc_dleft; /* Data bytes left to transfer */ + u_char *sc_cp; /* Current command pointer */ + size_t sc_cleft; /* Command bytes left to transfer */ + + /* Adapter state */ + u_char sc_phase; /* Current bus phase */ + u_char sc_prevphase; /* Previous bus phase */ + u_char sc_state; /* State applicable to the adapter */ +#define SPC_INIT 0 +#define SPC_IDLE 1 +#define SPC_SELECTING 2 /* SCSI command is arbiting */ +#define SPC_RESELECTED 3 /* Has been reselected */ +#define SPC_CONNECTED 4 /* Actively using the SCSI bus */ +#define SPC_DISCONNECT 5 /* MSG_DISCONNECT received */ +#define SPC_CMDCOMPLETE 6 /* MSG_CMDCOMPLETE received */ +#define SPC_CLEANING 7 + u_char sc_flags; +#define SPC_DROP_MSGIN 0x01 /* Discard all msgs (parity err detected) */ +#define SPC_ABORTING 0x02 /* Bailing out */ +#define SPC_DOINGDMA 0x04 /* doing DMA */ +#define SPC_INACTIVE 0x80 /* The FIFO data path is active! */ + u_char sc_selid; /* Reselection ID */ + + /* Message stuff */ + u_char sc_msgpriq; /* Messages we want to send */ + u_char sc_msgoutq; /* Messages sent during last MESSAGE OUT */ + u_char sc_lastmsg; /* Message last transmitted */ + u_char sc_currmsg; /* Message currently ready to transmit */ +#define SEND_DEV_RESET 0x01 +#define SEND_PARITY_ERROR 0x02 +#define SEND_INIT_DET_ERR 0x04 +#define SEND_REJECT 0x08 +#define SEND_IDENTIFY 0x10 +#define SEND_ABORT 0x20 +#define SEND_SDTR 0x40 +#define SEND_WDTR 0x80 +#define SPC_MAX_MSG_LEN 8 + u_char sc_omess[SPC_MAX_MSG_LEN]; + u_char *sc_omp; /* Outgoing message pointer */ + u_char sc_imess[SPC_MAX_MSG_LEN]; + u_char *sc_imp; /* Incoming message pointer */ + + /* Hardware stuff */ + int sc_initiator; /* Our scsi id */ + int sc_freq; /* Clock frequency in MHz */ + int sc_minsync; /* Minimum sync period / 4 */ + int sc_maxsync; /* Maximum sync period / 4 */ + + /* DMA function set from MD code */ + void (*sc_dma_start)(struct spc_softc *, void *, size_t, int); + void (*sc_dma_done)(struct spc_softc *); +}; + +#if SPC_DEBUG +#define SPC_SHOWACBS 0x01 +#define SPC_SHOWINTS 0x02 +#define SPC_SHOWCMDS 0x04 +#define SPC_SHOWMISC 0x08 +#define SPC_SHOWTRACE 0x10 +#define SPC_SHOWSTART 0x20 +#define SPC_DOBREAK 0x40 +extern int spc_debug; /* SPC_SHOWSTART|SPC_SHOWMISC|SPC_SHOWTRACE; */ +#define SPC_PRINT(b, s) do {if ((spc_debug & (b)) != 0) printf s;} while (0) +#define SPC_BREAK() do {if ((spc_debug & SPC_DOBREAK) != 0) Debugger();} while (0) +#define SPC_ASSERT(x) do {if (x) {} else {printf("%s at line %d: assertion failed\n", sc->sc_dev.dv_xname, __LINE__); Debugger();}} while (0) +#else +#define SPC_PRINT(b, s) +#define SPC_BREAK() +#define SPC_ASSERT(x) +#endif + +#define SPC_ACBS(s) SPC_PRINT(SPC_SHOWACBS, s) +#define SPC_INTS(s) SPC_PRINT(SPC_SHOWINTS, s) +#define SPC_CMDS(s) SPC_PRINT(SPC_SHOWCMDS, s) +#define SPC_MISC(s) SPC_PRINT(SPC_SHOWMISC, s) +#define SPC_TRACE(s) SPC_PRINT(SPC_SHOWTRACE, s) +#define SPC_START(s) SPC_PRINT(SPC_SHOWSTART, s) + +void spc_attach __P((struct spc_softc *)); +int spc_intr __P((void *)); +int spc_find __P((bus_space_tag_t, bus_space_handle_t, int)); +void spc_init __P((struct spc_softc *)); +void spc_sched __P((struct spc_softc *)); +#endif /* _MB89352VAR_H_ */ --- /dev/null 2003-06-30 23:47:47.000000000 +0900 +++ dev/spc.c 2003-06-29 18:58:52.000000000 +0900 @@ -0,0 +1,303 @@ +/* $NetBSD$ */ + +/* + * Copyright (c) 2003 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. + */ + +#include "opt_ddb.h" + +#include /* RCS ID & Copyright macro defns */ + +__KERNEL_RCSID(0, "$NetBSD$"); + +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include + +#include /* XXX */ +#include /* XXX */ + +#include +#include +#include + +static int spc_dio_match __P((struct device *, struct cfdata *, void *)); +static void spc_dio_attach __P((struct device *, struct device *, void *)); +static void spc_dio_dmastart __P((struct spc_softc *, void *, size_t, int)); +static void spc_dio_dmadone __P((struct spc_softc *)); +static void spc_dio_dmago __P((void *)); +static void spc_dio_dmastop __P((void *)); + +struct spc_dio_softc { + struct spc_softc sc_spc; /* MI spc softc */ + + /* DIO specific goo. */ + bus_space_handle_t sc_iohsc; /* bus space handle for HPSCSI */ + struct dmaqueue sc_dq; /* DMA job queue */ + u_int sc_dflags; /* DMA flags */ +#define SCSI_DMA32 0x01 /* 32-bit DMA should be used */ +#define SCSI_HAVEDMA 0x02 /* controller has DMA channel */ +#define SCSI_DATAIN 0x04 /* DMA direction */ +}; + +CFATTACH_DECL(spc, sizeof(struct spc_dio_softc), + spc_dio_match, spc_dio_attach, NULL, NULL); + +static int +spc_dio_match(parent, cf, aux) + struct device *parent; + struct cfdata *cf; + void *aux; +{ + struct dio_attach_args *da = aux; + + switch (da->da_id) { + case DIO_DEVICE_ID_SCSI0: + case DIO_DEVICE_ID_SCSI1: + case DIO_DEVICE_ID_SCSI2: + case DIO_DEVICE_ID_SCSI3: + return 1; + } + + return 0; +} + +static void +spc_dio_attach(parent, self, aux) + struct device *parent, *self; + void *aux; +{ + struct spc_dio_softc *dsc = (struct spc_dio_softc *)self; + struct spc_softc *sc = &dsc->sc_spc; + struct dio_attach_args *da = aux; + bus_space_tag_t iot = da->da_bst; + bus_space_handle_t iohsc, iohspc; + u_int8_t id; + + if (bus_space_map(iot, (bus_addr_t)dio_scodetopa(da->da_scode), + da->da_size, 0, &iohsc)) { + printf(": can't map SCSI registers\n"); + return; + } + + if (bus_space_subregion(iot, iohsc, SPC_OFFSET, SPC_SIZE, &iohspc)) { + printf(": can't map SPC registers\n"); + return; + } + + printf(": HP 98265A SCSI"); + + bus_space_write_1(iot, iohsc, HPSCSI_ID, 0xff); + DELAY(100); + id = bus_space_read_1(iot, iohsc, HPSCSI_ID); + if ((id & ID_WORD_DMA) == 0) { + printf(", 32-bit DMA"); + dsc->sc_dflags |= SCSI_DMA32; + } + id &= ID_MASK; + printf(", SCSI ID %d\n", id); + + sc->sc_iot = iot; + sc->sc_ioh = iohspc; + sc->sc_initiator = id; + + sc->sc_dma_start = spc_dio_dmastart; + sc->sc_dma_done = spc_dio_dmadone; + + dsc->sc_iohsc = iohsc; + dsc->sc_dq.dq_softc = dsc; + dsc->sc_dq.dq_start = spc_dio_dmago; + dsc->sc_dq.dq_done = spc_dio_dmastop; + + bus_space_write_1(iot, iohsc, HPSCSI_CSR, 0x00); + bus_space_write_1(iot, iohsc, HPSCSI_HCONF, 0x00); + + dio_intr_establish(spc_intr, (void *)sc, da->da_ipl, IPL_BIO); + + spc_attach(sc); + + /* enable SPC interrupt gate(?) */ + bus_space_write_1(iot, iohsc, HPSCSI_CSR, CSR_IE); +} + +static +void spc_dio_dmastart(sc, addr, size, datain) + struct spc_softc *sc; + void *addr; + size_t size; + int datain; +{ + struct spc_dio_softc *dsc = (struct spc_dio_softc *)sc; + + dsc->sc_dq.dq_chan = DMA0 | DMA1; + dsc->sc_dflags |= SCSI_HAVEDMA; + if (datain) + dsc->sc_dflags |= SCSI_DATAIN; + else + dsc->sc_dflags &= ~SCSI_DATAIN; + + if (dmareq(&dsc->sc_dq) == 1) + /* DMA channel is available, so start DMA immediately */ + spc_dio_dmago((void *)dsc); + /* else dma start function will be called later from dmafree(). */ +} + +static +void spc_dio_dmago(arg) + void *arg; +{ + struct spc_dio_softc *dsc = (struct spc_dio_softc *)arg; + struct spc_softc *sc = &dsc->sc_spc; + bus_space_tag_t iot; + bus_space_handle_t iohsc, iohspc; + int len, chan; + u_int32_t dmaflags; + u_int8_t cmd; + + iot = sc->sc_iot; + iohspc = sc->sc_ioh; + iohsc = dsc->sc_iohsc; + + bus_space_write_1(iot, iohsc, HPSCSI_HCONF, 0); + + cmd = CSR_IE; + dmaflags = DMAGO_NOINT; + chan = dsc->sc_dq.dq_chan; + if ((dsc->sc_dflags & SCSI_DATAIN) != 0) { + cmd |= CSR_DMAIN; + dmaflags |= DMAGO_READ; + } + if ((dsc->sc_dflags & SCSI_DMA32) != 0 && + ((u_int)sc->sc_dp & 3) == 0 && + (sc->sc_dleft & 3) == 0) { + cmd |= CSR_DMA32; + dmaflags |= DMAGO_LWORD; + } else + dmaflags |= DMAGO_WORD; + + dmago(chan, sc->sc_dp, sc->sc_dleft, dmaflags); + + bus_space_write_1(iot, iohsc, HPSCSI_CSR, cmd); + cmd |= (chan == 0) ? CSR_DE0 : CSR_DE1; + bus_space_write_1(iot, iohsc, HPSCSI_CSR, cmd); + + cmd = SCMD_XFR; + len = sc->sc_dleft; + + if ((len & (DEV_BSIZE - 1)) != 0) /* XXX ??? */ { + cmd |= SCMD_PAD; +#if 0 + if ((dsc->sc_dflags & SCSI_DATAIN) != 0) + len += 2; /* XXX ??? */ +#endif + } + + bus_space_write_1(iot, iohspc, TCH, len >> 16); + bus_space_write_1(iot, iohspc, TCM, len >> 8); + bus_space_write_1(iot, iohspc, TCL, len); + bus_space_write_1(iot, iohspc, PCTL, sc->sc_phase | PCTL_BFINT_ENAB); +#if 0 + bus_space_write_1(iot, iohspc, TMOD, 0); +#endif + bus_space_write_1(iot, iohspc, SCMD, cmd); + + sc->sc_flags |= SPC_DOINGDMA; +} + +static +void spc_dio_dmadone(sc) + struct spc_softc *sc; +{ + struct spc_dio_softc *dsc = (struct spc_dio_softc *)sc; + bus_space_tag_t iot; + bus_space_handle_t ioh, iohsc; + int resid, trans; + u_int8_t cmd; + + iot = sc->sc_iot; + ioh = sc->sc_ioh; + iohsc = dsc->sc_iohsc; + + /* wait DMA complete */ + if ((bus_space_read_1(iot, ioh, SSTS) & SSTS_BUSY) != 0) { + int timeout = 1000; /* XXX how long? */ + while ((bus_space_read_1(iot, ioh, SSTS) & SSTS_BUSY) != 0) { + if (--timeout < 0) + printf("%s: DMA complete timeout\n", + sc->sc_dev.dv_xname); + DELAY(1); + } + } + + if ((dsc->sc_dflags & SCSI_HAVEDMA) != 0) { + dmafree(&dsc->sc_dq); + dsc->sc_dflags &= ~SCSI_HAVEDMA; + } + + cmd = bus_space_read_1(iot, iohsc, HPSCSI_CSR); + cmd &= ~(CSR_DE1|CSR_DE0); + bus_space_write_1(iot, iohsc, HPSCSI_CSR, cmd); + + resid = bus_space_read_1(iot, ioh, TCH) << 16 | + bus_space_read_1(iot, ioh, TCM) << 8 | + bus_space_read_1(iot, ioh, TCL); + trans = sc->sc_dleft - resid; + sc->sc_dp += trans; + sc->sc_dleft -= trans; + + sc->sc_flags &= ~SPC_DOINGDMA; +} + +static +void spc_dio_dmastop(arg) + void *arg; +{ + struct spc_dio_softc *dsc = (struct spc_dio_softc *)arg; + struct spc_softc *sc = &dsc->sc_spc; + u_int8_t cmd; + + /* XXX When is this function called? */ + cmd = bus_space_read_1(sc->sc_iot, dsc->sc_iohsc, HPSCSI_CSR); + cmd &= ~(CSR_DE1|CSR_DE0); + bus_space_write_1(sc->sc_iot, dsc->sc_iohsc, HPSCSI_CSR, cmd); + + dsc->sc_dflags &= ~SCSI_HAVEDMA; + sc->sc_flags &= ~SPC_DOINGDMA; +}