summaryrefslogtreecommitdiff
path: root/sys/arch/mvme68k/dev/sbic.c
diff options
context:
space:
mode:
authorTheo de Raadt <deraadt@cvs.openbsd.org>1995-10-18 10:44:53 +0000
committerTheo de Raadt <deraadt@cvs.openbsd.org>1995-10-18 10:44:53 +0000
commit49235ceee0c25492d4ca35194d7c72838a9ec284 (patch)
tree14f77677934d4f4cb9baa428691ace792fd0deed /sys/arch/mvme68k/dev/sbic.c
parentc9328c850e70436131e06a34f73c14cc230c18f9 (diff)
mvme68k port by me. some parts by dale rahn.
Diffstat (limited to 'sys/arch/mvme68k/dev/sbic.c')
-rw-r--r--sys/arch/mvme68k/dev/sbic.c2561
1 files changed, 2561 insertions, 0 deletions
diff --git a/sys/arch/mvme68k/dev/sbic.c b/sys/arch/mvme68k/dev/sbic.c
new file mode 100644
index 00000000000..b871cdd95a2
--- /dev/null
+++ b/sys/arch/mvme68k/dev/sbic.c
@@ -0,0 +1,2561 @@
+/* $NetBSD: sbic.c,v 1.14 1995/08/18 15:28:03 chopps Exp $ */
+
+/*
+ * Copyright (c) 1994 Christian E. Hopps
+ * Copyright (c) 1990 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Van Jacobson of Lawrence Berkeley Laboratory.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)scsi.c 7.5 (Berkeley) 5/4/91
+ */
+
+/*
+ * AMD 33C93 scsi adaptor driver
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/device.h>
+#include <sys/kernel.h> /* For hz */
+#include <sys/disklabel.h>
+#include <sys/dkstat.h>
+#include <sys/buf.h>
+#include <scsi/scsi_all.h>
+#include <scsi/scsiconf.h>
+#include <vm/vm.h>
+#include <vm/vm_kern.h>
+#include <vm/vm_page.h>
+#include <machine/pmap.h>
+#include <machine/autoconf.h>
+#include <mvme68k/dev/dmavar.h>
+#include <mvme68k/dev/sbicreg.h>
+#include <mvme68k/dev/sbicvar.h>
+
+#include <vm/pmap.h>
+
+/* Since I can't find this in any other header files */
+#define SCSI_PHASE(reg) (reg&0x07)
+
+/*
+ * SCSI delays
+ * In u-seconds, primarily for state changes on the SPC.
+ */
+#define SBIC_CMD_WAIT 50000 /* wait per step of 'immediate' cmds */
+#define SBIC_DATA_WAIT 50000 /* wait per data in/out step */
+#define SBIC_INIT_WAIT 50000 /* wait per step (both) during init */
+
+#define b_cylin b_resid
+#define SBIC_WAIT(regs, until, timeo) sbicwait(regs, until, timeo, __LINE__)
+
+extern u_int kvtop();
+
+int sbicicmd __P((struct sbic_softc *, int, int, void *, int, void *, int));
+int sbicgo __P((struct sbic_softc *, struct scsi_xfer *));
+int sbicdmaok __P((struct sbic_softc *, struct scsi_xfer *));
+int sbicwait __P((sbic_regmap_p, char, int , int));
+int sbiccheckdmap __P((void *, u_long, u_long));
+int sbicselectbus __P((struct sbic_softc *, sbic_regmap_p, u_char, u_char, u_char));
+int sbicxfstart __P((sbic_regmap_p, int, u_char, int));
+int sbicxfout __P((sbic_regmap_p regs, int, void *, int));
+int sbicfromscsiperiod __P((struct sbic_softc *, sbic_regmap_p, int));
+int sbictoscsiperiod __P((struct sbic_softc *, sbic_regmap_p, int));
+int sbicintr __P((struct sbic_softc *));
+int sbicpoll __P((struct sbic_softc *));
+int sbicnextstate __P((struct sbic_softc *, u_char, u_char));
+int sbicmsgin __P((struct sbic_softc *));
+int sbicxfin __P((sbic_regmap_p regs, int, void *));
+int sbicabort __P((struct sbic_softc *, sbic_regmap_p, char *));
+void sbicxfdone __P((struct sbic_softc *, sbic_regmap_p, int));
+void sbicerror __P((struct sbic_softc *, sbic_regmap_p, u_char));
+void sbicstart __P((struct sbic_softc *));
+void sbicreset __P((struct sbic_softc *));
+void sbic_scsidone __P((struct sbic_acb *, int));
+void sbic_sched __P((struct sbic_softc *));
+void sbic_save_ptrs __P((struct sbic_softc *, sbic_regmap_p,int,int));
+void sbic_load_ptrs __P((struct sbic_softc *, sbic_regmap_p,int,int));
+
+/*
+ * Synch xfer parameters, and timing conversions
+ */
+int sbic_min_period = SBIC_SYN_MIN_PERIOD; /* in cycles = f(ICLK,FSn) */
+int sbic_max_offset = SBIC_SYN_MAX_OFFSET; /* pure number */
+
+int sbic_cmd_wait = SBIC_CMD_WAIT;
+int sbic_data_wait = SBIC_DATA_WAIT;
+int sbic_init_wait = SBIC_INIT_WAIT;
+
+/*
+ * was broken before.. now if you want this you get it for all drives
+ * on sbic controllers.
+ */
+int sbic_inhibit_sync = 1;
+int sbic_enable_reselect = 1;
+int sbic_clock_override = 0;
+int sbic_no_dma = 0;
+int sbic_parallel_operations = 1;
+
+#ifdef DEBUG
+sbic_regmap_p debug_sbic_regs;
+int sbicdma_ops = 0; /* total DMA operations */
+int sbicdma_bounces = 0; /* number operations using bounce buffer */
+int sbicdma_hits = 0; /* number of DMA chains that were contiguous */
+int sbicdma_misses = 0; /* number of DMA chains that were not contiguous */
+int sbicdma_saves = 0;
+#define QPRINTF(a) if (sbic_debug > 1) printf a
+int sbic_debug = 0;
+int sync_debug = 0;
+int sbic_dma_debug = 0;
+int reselect_debug = 0;
+int report_sense = 0;
+int data_pointer_debug = 0;
+int sbic_timeout = 0;
+u_char debug_asr, debug_csr, timeout_active=0, routine;
+void sbictimeout __P((struct sbic_softc *dev));
+#else
+#define QPRINTF
+#endif
+
+/*
+ * default minphys routine for sbic based controllers
+ */
+void
+sbic_minphys(bp)
+ struct buf *bp;
+{
+
+ /*
+ * No max transfer at this level.
+ */
+ minphys(bp);
+}
+
+/*
+ * Save DMA pointers. Take into account partial transfer. Shut down DMA.
+ */
+void
+sbic_save_ptrs(dev, regs, target, lun)
+ struct sbic_softc *dev;
+ sbic_regmap_p regs;
+ int target, lun;
+{
+ int count, asr, csr, s;
+ unsigned long ptr;
+ char *vptr;
+ struct sbic_acb* acb;
+
+ extern vm_offset_t vm_first_phys;
+
+ if( !dev->sc_cur ) return;
+ if( !(dev->sc_flags & SBICF_INDMA) ) return; /* DMA not active */
+
+ s = splbio();
+
+ acb = dev->sc_nexus;
+ count = -1;
+ do {
+ GET_SBIC_asr(regs, asr);
+ if( asr & SBIC_ASR_DBR ) {
+ printf("sbic_save_ptrs: asr %02x canceled!\n", asr);
+ splx(s);
+ return;
+ }
+ } while( asr & (SBIC_ASR_BSY|SBIC_ASR_CIP) );
+
+ /* Save important state */
+ /* must be done before dmastop */
+ acb->sc_dmacmd = dev->sc_dmacmd;
+ SBIC_TC_GET(regs, count);
+
+ /* Shut down DMA ====CAREFUL==== */
+ dev->sc_dmastop(dev);
+ dev->sc_flags &= ~SBICF_INDMA;
+ SBIC_TC_PUT(regs, 0);
+
+#ifdef DEBUG
+ if(!count && sbic_debug) printf("%dcount0",target);
+ if(data_pointer_debug == -1)
+ printf("SBIC saving target %d data pointers from (%x,%x)%xASR:%02x",
+ target, dev->sc_cur->dc_addr, dev->sc_cur->dc_count,
+ acb->sc_dmacmd, asr);
+#endif
+
+ /* Fixup partial xfers */
+ acb->sc_kv.dc_addr += (dev->sc_tcnt - count);
+ acb->sc_kv.dc_count -= (dev->sc_tcnt - count);
+ acb->sc_pa.dc_addr += (dev->sc_tcnt - count);
+ acb->sc_pa.dc_count -= ((dev->sc_tcnt - count)>>1);
+
+ acb->sc_tcnt = dev->sc_tcnt = count;
+#ifdef DEBUG
+ if(data_pointer_debug)
+ printf(" at (%x,%x):%x\n",
+ dev->sc_cur->dc_addr, dev->sc_cur->dc_count,count);
+ sbicdma_saves++;
+#endif
+ splx(s);
+}
+
+
+/*
+ * DOES NOT RESTART DMA!!!
+ */
+void sbic_load_ptrs(dev, regs, target, lun)
+ struct sbic_softc *dev;
+ sbic_regmap_p regs;
+ int target, lun;
+{
+ int i, s, asr, count;
+ char* vaddr, * paddr;
+ struct sbic_acb *acb;
+
+ acb = dev->sc_nexus;
+ if( !acb->sc_kv.dc_count )
+ /* No data to xfer */
+ return;
+
+ s = splbio();
+
+ dev->sc_last = dev->sc_cur = &acb->sc_pa;
+ dev->sc_tcnt = acb->sc_tcnt;
+ dev->sc_dmacmd = acb->sc_dmacmd;
+
+#ifdef DEBUG
+ sbicdma_ops++;
+#endif
+ if( !dev->sc_tcnt ) {
+ /* sc_tcnt == 0 implies end of segment */
+
+ /* do kvm to pa mappings */
+ paddr = acb->sc_pa.dc_addr =
+ (char *) kvtop(acb->sc_kv.dc_addr);
+
+ vaddr = acb->sc_kv.dc_addr;
+ count = acb->sc_kv.dc_count;
+ for(count = (NBPG - ((int)vaddr & PGOFSET));
+ count < acb->sc_kv.dc_count
+ && (char*)kvtop(vaddr + count + 4) == paddr + count + 4;
+ count += NBPG);
+ /* If it's all contiguous... */
+ if(count > acb->sc_kv.dc_count ) {
+ count = acb->sc_kv.dc_count;
+#ifdef DEBUG
+ sbicdma_hits++;
+#endif
+ } else {
+#ifdef DEBUG
+ sbicdma_misses++;
+#endif
+ }
+ acb->sc_tcnt = count;
+ acb->sc_pa.dc_count = count >> 1;
+
+#ifdef DEBUG
+ if(data_pointer_debug)
+ printf("DMA recalc:kv(%x,%x)pa(%x,%x)\n",
+ acb->sc_kv.dc_addr,
+ acb->sc_kv.dc_count,
+ acb->sc_pa.dc_addr,
+ acb->sc_tcnt);
+#endif
+ }
+ splx(s);
+#ifdef DEBUG
+ if(data_pointer_debug)
+ printf("SBIC restoring target %d data pointers at (%x,%x)%x\n",
+ target, dev->sc_cur->dc_addr, dev->sc_cur->dc_count,
+ dev->sc_dmacmd);
+#endif
+}
+
+/*
+ * used by specific sbic controller
+ *
+ * it appears that the higher level code does nothing with LUN's
+ * so I will too. I could plug it in, however so could they
+ * in scsi_scsi_cmd().
+ */
+int
+sbic_scsicmd(xs)
+ struct scsi_xfer *xs;
+{
+ struct sbic_acb *acb;
+ struct sbic_softc *dev;
+ struct scsi_link *slp;
+ int flags, s, stat;
+
+ slp = xs->sc_link;
+ dev = slp->adapter_softc;
+ flags = xs->flags;
+
+ if (flags & SCSI_DATA_UIO)
+ panic("sbic: scsi data uio requested");
+
+ if (dev->sc_nexus && flags & SCSI_POLL)
+ panic("sbic_scsicmd: busy");
+
+ if (slp->target == slp->adapter_target)
+ return ESCAPE_NOT_SUPPORTED;
+
+ s = splbio();
+ acb = dev->free_list.tqh_first;
+ if (acb)
+ TAILQ_REMOVE(&dev->free_list, acb, chain);
+ splx(s);
+
+ if (acb == NULL) {
+#ifdef DEBUG
+ printf("sbic_scsicmd: unable to queue request for target %d\n",
+ slp->target);
+#ifdef DDB
+ Debugger();
+#endif
+#endif
+ xs->error = XS_DRIVER_STUFFUP;
+ return(TRY_AGAIN_LATER);
+ }
+
+ acb->flags = ACB_ACTIVE;
+ if (flags & SCSI_DATA_IN)
+ acb->flags |= ACB_DATAIN;
+ acb->xs = xs;
+ bcopy(xs->cmd, &acb->cmd, xs->cmdlen);
+ acb->clen = xs->cmdlen;
+ acb->sc_kv.dc_addr = xs->data;
+ acb->sc_kv.dc_count = xs->datalen;
+ acb->pa_addr = xs->data ? (char *)kvtop(xs->data) : 0; /* XXXX check */
+
+ if (flags & SCSI_POLL) {
+ s = splbio();
+ /*
+ * This has major side effects -- it locks up the machine
+ */
+
+ dev->sc_flags |= SBICF_ICMD;
+ do {
+ while(dev->sc_nexus)
+ sbicpoll(dev);
+ dev->sc_nexus = acb;
+ dev->sc_stat[0] = -1;
+ dev->sc_xs = xs;
+ dev->target = slp->target;
+ dev->lun = slp->lun;
+ stat = sbicicmd(dev, slp->target, slp->lun,
+ &acb->cmd, acb->clen,
+ acb->sc_kv.dc_addr, acb->sc_kv.dc_count);
+ } while (dev->sc_nexus != acb);
+ sbic_scsidone(acb, stat);
+
+ splx(s);
+ return(COMPLETE);
+ }
+
+ s = splbio();
+ TAILQ_INSERT_TAIL(&dev->ready_list, acb, chain);
+
+ if (dev->sc_nexus) {
+ splx(s);
+ return(SUCCESSFULLY_QUEUED);
+ }
+
+ /*
+ * nothing is active, try to start it now.
+ */
+ sbic_sched(dev);
+ splx(s);
+
+/* TODO: add sbic_poll to do SCSI_POLL operations */
+#if 0
+ if (flags & SCSI_POLL)
+ return(COMPLETE);
+#endif
+ return(SUCCESSFULLY_QUEUED);
+}
+
+/*
+ * attempt to start the next available command
+ */
+void
+sbic_sched(dev)
+ struct sbic_softc *dev;
+{
+ struct scsi_xfer *xs;
+ struct scsi_link *slp;
+ struct sbic_acb *acb;
+ int flags, /*phase,*/ stat, i;
+
+ if (dev->sc_nexus)
+ return; /* a command is current active */
+
+ for (acb = dev->ready_list.tqh_first; acb; acb = acb->chain.tqe_next) {
+ slp = acb->xs->sc_link;
+ i = slp->target;
+ if (!(dev->sc_tinfo[i].lubusy & (1 << slp->lun))) {
+ struct sbic_tinfo *ti = &dev->sc_tinfo[i];
+
+ TAILQ_REMOVE(&dev->ready_list, acb, chain);
+ dev->sc_nexus = acb;
+ slp = acb->xs->sc_link;
+ ti = &dev->sc_tinfo[slp->target];
+ ti->lubusy |= (1 << slp->lun);
+ acb->sc_pa.dc_addr = acb->pa_addr; /* XXXX check */
+ break;
+ }
+ }
+
+ if (acb == NULL)
+ return; /* did not find an available command */
+
+ dev->sc_xs = xs = acb->xs;
+ slp = xs->sc_link;
+ flags = xs->flags;
+
+ if (flags & SCSI_RESET)
+ sbicreset(dev);
+
+#ifdef DEBUG
+ if( data_pointer_debug > 1 )
+ printf("sbic_sched(%d,%d)\n",slp->target,slp->lun);
+#endif
+ dev->sc_stat[0] = -1;
+ dev->target = slp->target;
+ dev->lun = slp->lun;
+ if ( flags & SCSI_POLL || ( !sbic_parallel_operations
+ && (/*phase == STATUS_PHASE ||*/
+ sbicdmaok(dev, xs) == 0) ) )
+ stat = sbicicmd(dev, slp->target, slp->lun, &acb->cmd,
+ acb->clen, acb->sc_kv.dc_addr, acb->sc_kv.dc_count);
+ else if (sbicgo(dev, xs) == 0)
+ return;
+ else
+ stat = dev->sc_stat[0];
+
+ sbic_scsidone(acb, stat);
+}
+
+void
+sbic_scsidone(acb, stat)
+ struct sbic_acb *acb;
+ int stat;
+{
+ struct scsi_xfer *xs;
+ struct scsi_link *slp;
+ struct sbic_softc *dev;
+ int s, dosched = 0;
+
+ xs = acb->xs;
+ slp = xs->sc_link;
+ dev = slp->adapter_softc;
+#ifdef DIAGNOSTIC
+ if (acb == NULL || xs == NULL) {
+ printf("sbic_scsidone -- (%d,%d) no scsi_xfer\n",
+ dev->target, dev->lun);
+#ifdef DDB
+ Debugger();
+#endif
+ return;
+ }
+#endif
+#if 1
+ if (((struct device *)(slp->device_softc))->dv_unit < dk_ndrive)
+ ++dk_xfer[((struct device *)(slp->device_softc))->dv_unit];
+#endif
+ /*
+ * is this right?
+ */
+ xs->status = stat;
+
+#ifdef DEBUG
+ if( data_pointer_debug > 1 )
+ printf("scsidone: (%d,%d)->(%d,%d)%02x\n",
+ slp->target, slp->lun,
+ dev->target, dev->lun, stat);
+ if( xs->sc_link->target == dev->sc_link.adapter_target )
+ panic("target == hostid");
+#endif
+
+ if (xs->error == XS_NOERROR && !(acb->flags & ACB_CHKSENSE)) {
+ if (stat == SCSI_CHECK) {
+ /* Schedule a REQUEST SENSE */
+ struct scsi_sense *ss = (void *)&acb->cmd;
+#ifdef DEBUG
+ if (report_sense)
+ printf("sbic_scsidone: autosense %02x targ %d lun %d",
+ acb->cmd.opcode, slp->target, slp->lun);
+#endif
+ bzero(ss, sizeof(*ss));
+ ss->opcode = REQUEST_SENSE;
+ ss->byte2 = slp->lun << 5;
+ ss->length = sizeof(struct scsi_sense_data);
+ acb->clen = sizeof(*ss);
+ acb->sc_kv.dc_addr = (char *)&xs->sense;
+ acb->sc_kv.dc_count = sizeof(struct scsi_sense_data);
+ acb->pa_addr = (char *)kvtop(&xs->sense); /* XXX check */
+ acb->flags = ACB_ACTIVE | ACB_CHKSENSE | ACB_DATAIN;
+ TAILQ_INSERT_HEAD(&dev->ready_list, acb, chain);
+ dev->sc_tinfo[slp->target].lubusy &=
+ ~(1 << slp->lun);
+ dev->sc_tinfo[slp->target].senses++;
+ if (dev->sc_nexus == acb) {
+ dev->sc_nexus = NULL;
+ sbic_sched(dev);
+ }
+ return;
+ }
+ }
+ if (xs->error == XS_NOERROR && (acb->flags & ACB_CHKSENSE)) {
+ xs->error = XS_SENSE;
+#ifdef DEBUG
+ if (report_sense)
+ printf(" => %02x %02x\n", xs->sense.extended_flags,
+ xs->sense.extended_extra_bytes[3]);
+#endif
+ } else {
+ xs->resid = 0; /* XXXX */
+ }
+#if whataboutthisone
+ case SCSI_BUSY:
+ xs->error = XS_BUSY;
+ break;
+#endif
+ xs->flags |= ITSDONE;
+
+ /*
+ * Remove the ACB from whatever queue it's on. We have to do a bit of
+ * a hack to figure out which queue it's on. Note that it is *not*
+ * necessary to cdr down the ready queue, but we must cdr down the
+ * nexus queue and see if it's there, so we can mark the unit as no
+ * longer busy. This code is sickening, but it works.
+ */
+ if (acb == dev->sc_nexus) {
+ dev->sc_nexus = NULL;
+ dev->sc_tinfo[slp->target].lubusy &= ~(1<<slp->lun);
+ if (dev->ready_list.tqh_first)
+ dosched = 1; /* start next command */
+ } else if (dev->ready_list.tqh_last == &acb->chain.tqe_next) {
+ TAILQ_REMOVE(&dev->ready_list, acb, chain);
+ } else {
+ register struct sbic_acb *acb2;
+ for (acb2 = dev->nexus_list.tqh_first; acb2;
+ acb2 = acb2->chain.tqe_next)
+ if (acb2 == acb) {
+ TAILQ_REMOVE(&dev->nexus_list, acb, chain);
+ dev->sc_tinfo[slp->target].lubusy
+ &= ~(1<<slp->lun);
+ break;
+ }
+ if (acb2)
+ ;
+ else if (acb->chain.tqe_next) {
+ TAILQ_REMOVE(&dev->ready_list, acb, chain);
+ } else {
+ printf("%s: can't find matching acb\n",
+ dev->sc_dev.dv_xname);
+#ifdef DDB
+ Debugger();
+#endif
+ }
+ }
+ /* Put it on the free list. */
+ acb->flags = ACB_FREE;
+ TAILQ_INSERT_HEAD(&dev->free_list, acb, chain);
+
+ dev->sc_tinfo[slp->target].cmds++;
+
+ scsi_done(xs);
+
+ if (dosched)
+ sbic_sched(dev);
+}
+
+int
+sbicdmaok(dev, xs)
+ struct sbic_softc *dev;
+ struct scsi_xfer *xs;
+{
+ if (sbic_no_dma || xs->datalen & 0x1 || (u_int)xs->data & 0x3)
+ return(0);
+ /*
+ * controller supports dma to any addresses?
+ */
+ else if ((dev->sc_flags & SBICF_BADDMA) == 0)
+ return(1);
+ /*
+ * this address is ok for dma?
+ */
+ else if (sbiccheckdmap(xs->data, xs->datalen, dev->sc_dmamask) == 0)
+ return(1);
+#if 0
+ /*
+ * we have a bounce buffer?
+ */
+ else if (dev->sc_tinfo[xs->sc_link->target].bounce)
+ return(1);
+ /*
+ * try to get one
+ */
+ else if (dev->sc_tinfo[xs->sc_link->target].bounce
+ = (char *)alloc_z2mem(MAXPHYS)) {
+ if (isztwomem(dev->sc_tinfo[xs->sc_link->target].bounce))
+ printf("alloc ZII target %d bounce pa 0x%x\n",
+ xs->sc_link->target,
+ kvtop(dev->sc_tinfo[xs->sc_link->target].bounce));
+ else if (dev->sc_tinfo[xs->sc_link->target].bounce)
+ printf("alloc CHIP target %d bounce pa 0x%x\n",
+ xs->sc_link->target,
+ PREP_DMA_MEM(dev->sc_tinfo[xs->sc_link->target].bounce));
+ return(1);
+ }
+#endif
+
+ return(0);
+}
+
+
+int
+sbicwait(regs, until, timeo, line)
+ sbic_regmap_p regs;
+ char until;
+ int timeo;
+ int line;
+{
+ u_char val;
+ int csr;
+
+ if (timeo == 0)
+ timeo = 10000; /* some large value.. */
+
+ GET_SBIC_asr(regs,val);
+ while ((val & until) == 0) {
+ if (timeo-- == 0) {
+ GET_SBIC_csr(regs, csr);
+ printf("sbicwait TIMEO @%d with asr=x%x csr=x%x\n",
+ line, val, csr);
+#if defined(DDB) && defined(DEBUG)
+ Debugger();
+#endif
+ return(val); /* Maybe I should abort */
+ break;
+ }
+ DELAY(1);
+ GET_SBIC_asr(regs,val);
+ }
+ return(val);
+}
+
+int
+sbicabort(dev, regs, where)
+ struct sbic_softc *dev;
+ sbic_regmap_p regs;
+ char *where;
+{
+ u_char csr, asr;
+
+ GET_SBIC_csr(regs, csr);
+ GET_SBIC_asr(regs, asr);
+
+ printf ("%s: abort %s: csr = 0x%02x, asr = 0x%02x\n",
+ dev->sc_dev.dv_xname, where, csr, asr);
+
+
+#if 0
+ /* Clean up running command */
+ if (dev->sc_nexus != NULL) {
+ dev->sc_nexus->xs->error = XS_DRIVER_STUFFUP;
+ sbic_scsidone(dev->sc_nexus, dev->sc_stat[0]);
+ }
+ while (acb = dev->nexus_list.tqh_first) {
+ acb->xs->error = XS_DRIVER_STUFFUP;
+ sbic_scsidone(acb, -1 /*acb->stat[0]*/);
+ }
+#endif
+
+ /* Clean up chip itself */
+ if (dev->sc_flags & SBICF_SELECTED) {
+ while( asr & SBIC_ASR_DBR ) {
+ /* sbic is jammed w/data. need to clear it */
+ /* But we don't know what direction it needs to go */
+ GET_SBIC_data(regs, asr);
+ printf("%s: abort %s: clearing data buffer 0x%02x\n",
+ dev->sc_dev.dv_xname, where, asr);
+ GET_SBIC_asr(regs, asr);
+ if( asr & SBIC_ASR_DBR ) /* Not the read direction, then */
+ SET_SBIC_data(regs, asr);
+ GET_SBIC_asr(regs, asr);
+ }
+ WAIT_CIP(regs);
+ SET_SBIC_cmd(regs, SBIC_CMD_ABORT);
+ WAIT_CIP(regs);
+
+ GET_SBIC_asr(regs, asr);
+ if (asr & (SBIC_ASR_BSY|SBIC_ASR_LCI)) {
+ /* ok, get more drastic.. */
+
+ sbicreset(dev);
+ dev->sc_flags &= ~SBICF_SELECTED;
+ return -1;
+ }
+ SET_SBIC_cmd(regs, SBIC_CMD_DISC);
+
+ do {
+ SBIC_WAIT (regs, SBIC_ASR_INT, 0);
+ GET_SBIC_csr (regs, csr);
+ } while ((csr != SBIC_CSR_DISC) && (csr != SBIC_CSR_DISC_1)
+ && (csr != SBIC_CSR_CMD_INVALID));
+
+ /* lets just hope it worked.. */
+ dev->sc_flags &= ~SBICF_SELECTED;
+ }
+ return -1;
+}
+
+
+/*
+ * Initialize driver-private structures
+ */
+
+void
+sbicinit(dev)
+ struct sbic_softc *dev;
+{
+ sbic_regmap_p regs;
+ u_int my_id, i, s;
+ u_char csr;
+ struct sbic_acb *acb;
+
+ regs = dev->sc_sbicp;
+
+ if ((dev->sc_flags & SBICF_ALIVE) == 0) {
+ TAILQ_INIT(&dev->ready_list);
+ TAILQ_INIT(&dev->nexus_list);
+ TAILQ_INIT(&dev->free_list);
+ dev->sc_nexus = NULL;
+ dev->sc_xs = NULL;
+ acb = dev->sc_acb;
+ bzero(acb, sizeof(dev->sc_acb));
+ for (i = 0; i < sizeof(dev->sc_acb) / sizeof(*acb); i++) {
+ TAILQ_INSERT_TAIL(&dev->free_list, acb, chain);
+ acb++;
+ }
+ bzero(dev->sc_tinfo, sizeof(dev->sc_tinfo));
+ } else panic("sbic: reinitializing driver!");
+
+ dev->sc_flags |= SBICF_ALIVE;
+ dev->sc_flags &= ~SBICF_SELECTED;
+
+ sbicreset(dev);
+}
+
+void
+sbicreset(dev)
+ struct sbic_softc *dev;
+{
+ sbic_regmap_p regs;
+ u_int my_id, i, s;
+ u_char csr;
+ struct sbic_acb *acb;
+
+ regs = dev->sc_sbicp;
+#if 0
+ if (dev->sc_flags & SBICF_ALIVE) {
+ SET_SBIC_cmd(regs, SBIC_CMD_ABORT);
+ WAIT_CIP(regs);
+ }
+#else
+ SET_SBIC_cmd(regs, SBIC_CMD_ABORT);
+ WAIT_CIP(regs);
+#endif
+ s = splbio();
+ my_id = dev->sc_link.adapter_target & SBIC_ID_MASK;
+
+ /* Enable advanced mode */
+ my_id |= SBIC_ID_EAF /*| SBIC_ID_EHP*/ ;
+ SET_SBIC_myid(regs, my_id);
+
+ /*
+ * Disable interrupts (in dmainit) then reset the chip
+ */
+ SET_SBIC_cmd(regs, SBIC_CMD_RESET);
+ DELAY(25);
+ SBIC_WAIT(regs, SBIC_ASR_INT, 0);
+ GET_SBIC_csr(regs, csr); /* clears interrupt also */
+
+ if (dev->sc_clkfreq < 110)
+ my_id |= SBIC_ID_FS_8_10;
+ else if (dev->sc_clkfreq < 160)
+ my_id |= SBIC_ID_FS_12_15;
+ else if (dev->sc_clkfreq < 210)
+ my_id |= SBIC_ID_FS_16_20;
+
+ SET_SBIC_myid(regs, my_id);
+
+ /*
+ * Set up various chip parameters
+ */
+ SET_SBIC_control(regs, SBIC_CTL_EDI | SBIC_CTL_IDI /* | SBIC_CTL_HSP */
+ | SBIC_MACHINE_DMA_MODE);
+ /*
+ * don't allow (re)selection (SBIC_RID_ES)
+ * until we can handle target mode!!
+ */
+ SET_SBIC_rselid(regs, SBIC_RID_ER);
+ SET_SBIC_syn(regs, 0); /* asynch for now */
+
+ /*
+ * anything else was zeroed by reset
+ */
+ splx(s);
+
+#if 0
+ if ((dev->sc_flags & SBICF_ALIVE) == 0) {
+ TAILQ_INIT(&dev->ready_list);
+ TAILQ_INIT(&dev->nexus_list);
+ TAILQ_INIT(&dev->free_list);
+ dev->sc_nexus = NULL;
+ dev->sc_xs = NULL;
+ acb = dev->sc_acb;
+ bzero(acb, sizeof(dev->sc_acb));
+ for (i = 0; i < sizeof(dev->sc_acb) / sizeof(*acb); i++) {
+ TAILQ_INSERT_TAIL(&dev->free_list, acb, chain);
+ acb++;
+ }
+ bzero(dev->sc_tinfo, sizeof(dev->sc_tinfo));
+ } else {
+ if (dev->sc_nexus != NULL) {
+ dev->sc_nexus->xs->error = XS_DRIVER_STUFFUP;
+ sbic_scsidone(dev->sc_nexus, dev->sc_stat[0]);
+ }
+ while (acb = dev->nexus_list.tqh_first) {
+ acb->xs->error = XS_DRIVER_STUFFUP;
+ sbic_scsidone(acb, -1 /*acb->stat[0]*/);
+ }
+ }
+
+ dev->sc_flags |= SBICF_ALIVE;
+#endif
+ dev->sc_flags &= ~SBICF_SELECTED;
+}
+
+void
+sbicerror(dev, regs, csr)
+ struct sbic_softc *dev;
+ sbic_regmap_p regs;
+ u_char csr;
+{
+ struct scsi_xfer *xs;
+
+ xs = dev->sc_xs;
+
+#ifdef DIAGNOSTIC
+ if (xs == NULL)
+ panic("sbicerror");
+#endif
+ if (xs->flags & SCSI_SILENT)
+ return;
+
+ printf("%s: ", dev->sc_dev.dv_xname);
+ printf("csr == 0x%02x\n", csr); /* XXX */
+}
+
+/*
+ * select the bus, return when selected or error.
+ */
+int
+sbicselectbus(dev, regs, target, lun, our_addr)
+ struct sbic_softc *dev;
+ sbic_regmap_p regs;
+ u_char target, lun, our_addr;
+{
+ u_char asr, csr, id;
+
+ QPRINTF(("sbicselectbus %d\n", target));
+
+ /*
+ * if we're already selected, return (XXXX panic maybe?)
+ */
+ if (dev->sc_flags & SBICF_SELECTED)
+ return(1);
+
+ SET_SBIC_rselid (regs, 0);
+#if 0
+ GET_SBIC_asr(regs, asr);
+ if( asr & (SBIC_ASR_INT|SBIC_ASR_BSY) ) {
+ /* This means we got ourselves reselected upon */
+/* printf("sbicselectbus: weird asr %02x\n", asr);*/
+#ifdef DDB
+/* Debugger();*/
+#endif
+ SET_SBIC_rselid (regs, SBIC_RID_ER);
+ return 1;
+ }
+#endif
+ /*
+ * issue select
+ */
+ SBIC_TC_PUT(regs, 0);
+ SET_SBIC_selid(regs, target);
+ SET_SBIC_timeo(regs, SBIC_TIMEOUT(250,dev->sc_clkfreq));
+
+ /*
+ * set sync or async
+ */
+ if (dev->sc_sync[target].state == SYNC_DONE)
+ SET_SBIC_syn(regs, SBIC_SYN (dev->sc_sync[target].offset,
+ dev->sc_sync[target].period));
+ else
+ SET_SBIC_syn(regs, SBIC_SYN (0, sbic_min_period));
+
+ GET_SBIC_asr(regs, asr);
+ if( asr & (SBIC_ASR_INT|SBIC_ASR_BSY) ) {
+ /* This means we got ourselves reselected upon */
+/* printf("sbicselectbus: INT/BSY asr %02x\n", asr);*/
+#ifdef DDB
+/* Debugger();*/
+#endif
+ return 1;
+ }
+
+ SET_SBIC_cmd(regs, SBIC_CMD_SEL_ATN);
+
+ /*
+ * wait for select (merged from seperate function may need
+ * cleanup)
+ */
+ WAIT_CIP(regs);
+#if 0
+ GET_SBIC_asr(regs, asr);
+ if( asr & SBIC_ASR_LCI ) {
+ /* This means we got ourselves reselected upon */
+#ifdef DEBUG
+ if (reselect_debug)
+ printf("sbicselectbus: LCI asr %02x\n", asr);
+/* Debugger();*/
+#endif
+ return 1;
+ }
+#endif
+ do {
+ asr = SBIC_WAIT(regs, SBIC_ASR_INT | SBIC_ASR_LCI, 0);
+ if (asr & SBIC_ASR_LCI) {
+#ifdef DEBUG
+ if (reselect_debug)
+ printf("sbicselectbus: late LCI asr %02x\n", asr);
+#endif
+ return 1;
+ }
+ GET_SBIC_csr (regs, csr);
+ QPRINTF(("%02x ", csr));
+ if( csr == SBIC_CSR_RSLT_NI || csr == SBIC_CSR_RSLT_IFY) {
+#ifdef DEBUG
+ if( reselect_debug || 1 )
+ printf("sbicselectbus: reselected asr %02x\n", asr);
+#endif
+ /* We need to handle this now so we don't lock up later */
+ sbicnextstate(dev, csr, asr);
+ return 1;
+ }
+ if( csr == SBIC_CSR_SLT || csr == SBIC_CSR_SLT_ATN) {
+ panic("sbicselectbus: target issued select!");
+ return 1;
+ }
+ } while (csr != (SBIC_CSR_MIS_2|MESG_OUT_PHASE)
+ && csr != (SBIC_CSR_MIS_2|CMD_PHASE) && csr != SBIC_CSR_SEL_TIMEO);
+
+ /* Enable (or not) reselection */
+ if( (dev->sc_xs->flags & SCSI_POLL
+ || (dev->sc_flags & SBICF_ICMD)
+ || !sbic_enable_reselect)
+ && dev->nexus_list.tqh_first == NULL )
+ SET_SBIC_rselid (regs, 0);
+ else
+ SET_SBIC_rselid (regs, SBIC_RID_ER);
+
+ if (csr == (SBIC_CSR_MIS_2|CMD_PHASE)) {
+ dev->sc_flags |= SBICF_SELECTED; /* device ignored ATN */
+ GET_SBIC_selid(regs, id);
+ dev->target = id;
+ GET_SBIC_tlun(regs,dev->lun);
+ if( dev->lun & SBIC_TLUN_VALID )
+ dev->lun &= SBIC_TLUN_MASK;
+ else
+ dev->lun = lun;
+ } else if (csr == (SBIC_CSR_MIS_2|MESG_OUT_PHASE)) {
+ /*
+ * Send identify message
+ * (SCSI-2 requires an identify msg (?))
+ */
+ GET_SBIC_selid(regs, id);
+ dev->target = id;
+ GET_SBIC_tlun(regs,dev->lun);
+ if( dev->lun & SBIC_TLUN_VALID )
+ dev->lun &= SBIC_TLUN_MASK;
+ else
+ dev->lun = lun;
+ /*
+ * handle drives that don't want to be asked
+ * whether to go sync at all.
+ */
+ if (sbic_inhibit_sync && dev->sc_sync[id].state == SYNC_START) {
+#ifdef DEBUG
+ if (sync_debug)
+ printf("Forcing target %d asynchronous.\n", id);
+#endif
+ dev->sc_sync[id].offset = 0;
+ dev->sc_sync[id].period = sbic_min_period;
+ dev->sc_sync[id].state = SYNC_DONE;
+ }
+
+
+ if (dev->sc_sync[id].state != SYNC_START){
+ if( dev->sc_xs->flags & SCSI_POLL
+ || (dev->sc_flags & SBICF_ICMD)
+ || !sbic_enable_reselect )
+ SEND_BYTE (regs, MSG_IDENTIFY | lun);
+ else
+ SEND_BYTE (regs, MSG_IDENTIFY_DR | lun);
+ } else {
+ /*
+ * try to initiate a sync transfer.
+ * So compose the sync message we're going
+ * to send to the target
+ */
+
+#ifdef DEBUG
+ if (sync_debug)
+ printf("Sending sync request to target %d ... ",
+ id);
+#endif
+ /*
+ * setup scsi message sync message request
+ */
+ dev->sc_msg[0] = MSG_IDENTIFY | lun;
+ dev->sc_msg[1] = MSG_EXT_MESSAGE;
+ dev->sc_msg[2] = 3;
+ dev->sc_msg[3] = MSG_SYNC_REQ;
+ dev->sc_msg[4] = sbictoscsiperiod(dev, regs,
+ sbic_min_period);
+ dev->sc_msg[5] = sbic_max_offset;
+
+ if (sbicxfstart(regs, 6, MESG_OUT_PHASE, sbic_cmd_wait))
+ sbicxfout(regs, 6, dev->sc_msg, MESG_OUT_PHASE);
+
+ dev->sc_sync[id].state = SYNC_SENT;
+#ifdef DEBUG
+ if (sync_debug)
+ printf ("sent\n");
+#endif
+ }
+
+ SBIC_WAIT (regs, SBIC_ASR_INT, 0);
+ GET_SBIC_csr (regs, csr);
+ QPRINTF(("[%02x]", csr));
+#ifdef DEBUG
+ if (sync_debug && dev->sc_sync[id].state == SYNC_SENT)
+ printf("csr-result of last msgout: 0x%x\n", csr);
+#endif
+
+ if (csr != SBIC_CSR_SEL_TIMEO)
+ dev->sc_flags |= SBICF_SELECTED;
+ }
+ if (csr == SBIC_CSR_SEL_TIMEO)
+ dev->sc_xs->error = XS_SELTIMEOUT;
+
+ QPRINTF(("\n"));
+
+ return(csr == SBIC_CSR_SEL_TIMEO);
+}
+
+int
+sbicxfstart(regs, len, phase, wait)
+ sbic_regmap_p regs;
+ int len, wait;
+ u_char phase;
+{
+ u_char id;
+
+ switch (phase) {
+ case DATA_IN_PHASE:
+ case MESG_IN_PHASE:
+ GET_SBIC_selid (regs, id);
+ id |= SBIC_SID_FROM_SCSI;
+ SET_SBIC_selid (regs, id);
+ SBIC_TC_PUT (regs, (unsigned)len);
+ break;
+ case DATA_OUT_PHASE:
+ case MESG_OUT_PHASE:
+ case CMD_PHASE:
+ GET_SBIC_selid (regs, id);
+ id &= ~SBIC_SID_FROM_SCSI;
+ SET_SBIC_selid (regs, id);
+ SBIC_TC_PUT (regs, (unsigned)len);
+ break;
+ default:
+ SBIC_TC_PUT (regs, 0);
+ }
+ QPRINTF(("sbicxfstart %d, %d, %d\n", len, phase, wait));
+
+ return(1);
+}
+
+int
+sbicxfout(regs, len, bp, phase)
+ sbic_regmap_p regs;
+ int len;
+ void *bp;
+ int phase;
+{
+ u_char orig_csr, csr, asr, *buf;
+ int wait;
+
+ buf = bp;
+ wait = sbic_data_wait;
+
+ QPRINTF(("sbicxfout {%d} %02x %02x %02x %02x %02x "
+ "%02x %02x %02x %02x %02x\n", len, buf[0], buf[1], buf[2],
+ buf[3], buf[4], buf[5], buf[6], buf[7], buf[8], buf[9]));
+
+ GET_SBIC_csr (regs, orig_csr);
+
+ /*
+ * sigh.. WD-PROTO strikes again.. sending the command in one go
+ * causes the chip to lock up if talking to certain (misbehaving?)
+ * targets. Anyway, this procedure should work for all targets, but
+ * it's slightly slower due to the overhead
+ */
+ WAIT_CIP (regs);
+ SET_SBIC_cmd (regs, SBIC_CMD_XFER_INFO);
+ for (;len > 0; len--) {
+ GET_SBIC_asr (regs, asr);
+ while ((asr & SBIC_ASR_DBR) == 0) {
+ if ((asr & SBIC_ASR_INT) || --wait < 0) {
+#ifdef DEBUG
+ if (sbic_debug)
+ printf("sbicxfout fail: l%d i%x w%d\n",
+ len, asr, wait);
+#endif
+ return (len);
+ }
+/* DELAY(1);*/
+ GET_SBIC_asr (regs, asr);
+ }
+
+ SET_SBIC_data (regs, *buf);
+ buf++;
+ }
+ SBIC_TC_GET(regs, len);
+ QPRINTF(("sbicxfout done %d bytes\n", len));
+ /*
+ * this leaves with one csr to be read
+ */
+ return(0);
+}
+
+/* returns # bytes left to read */
+int
+sbicxfin(regs, len, bp)
+ sbic_regmap_p regs;
+ int len;
+ void *bp;
+{
+ int wait, read;
+ u_char *obp, *buf;
+ u_char orig_csr, csr, asr;
+
+ wait = sbic_data_wait;
+ obp = bp;
+ buf = bp;
+
+ GET_SBIC_csr (regs, orig_csr);
+
+ QPRINTF(("sbicxfin %d, csr=%02x\n", len, orig_csr));
+
+ WAIT_CIP (regs);
+ SET_SBIC_cmd (regs, SBIC_CMD_XFER_INFO);
+ for (;len > 0; len--) {
+ GET_SBIC_asr (regs, asr);
+ if((asr & SBIC_ASR_PE)) {
+#ifdef DEBUG
+ printf("sbicxfin parity error: l%d i%x w%d\n",
+ len, asr, wait);
+/* return ((unsigned long)buf - (unsigned long)bp); */
+#ifdef DDB
+ Debugger();
+#endif
+#endif
+ }
+ while ((asr & SBIC_ASR_DBR) == 0) {
+ if ((asr & SBIC_ASR_INT) || --wait < 0) {
+#ifdef DEBUG
+ if (sbic_debug) {
+ QPRINTF(("sbicxfin fail:{%d} %02x %02x %02x %02x %02x %02x "
+ "%02x %02x %02x %02x\n", len, obp[0], obp[1], obp[2],
+ obp[3], obp[4], obp[5], obp[6], obp[7], obp[8], obp[9]));
+ printf("sbicxfin fail: l%d i%x w%d\n",
+ len, asr, wait);
+}
+#endif
+ return len;
+ }
+
+ if( ! asr & SBIC_ASR_BSY ) {
+ GET_SBIC_csr(regs, csr);
+ QPRINTF(("[CSR%02xASR%02x]", csr, asr));
+ }
+
+/* DELAY(1);*/
+ GET_SBIC_asr (regs, asr);
+ }
+
+ GET_SBIC_data (regs, *buf);
+/* QPRINTF(("asr=%02x, csr=%02x, data=%02x\n", asr, csr, *buf));*/
+ buf++;
+ }
+
+ QPRINTF(("sbicxfin {%d} %02x %02x %02x %02x %02x %02x "
+ "%02x %02x %02x %02x\n", len, obp[0], obp[1], obp[2],
+ obp[3], obp[4], obp[5], obp[6], obp[7], obp[8], obp[9]));
+
+ /* this leaves with one csr to be read */
+ return len;
+}
+
+/*
+ * SCSI 'immediate' command: issue a command to some SCSI device
+ * and get back an 'immediate' response (i.e., do programmed xfer
+ * to get the response data). 'cbuf' is a buffer containing a scsi
+ * command of length clen bytes. 'buf' is a buffer of length 'len'
+ * bytes for data. The transfer direction is determined by the device
+ * (i.e., by the scsi bus data xfer phase). If 'len' is zero, the
+ * command must supply no data.
+ */
+int
+sbicicmd(dev, target, lun, cbuf, clen, buf, len)
+ struct sbic_softc *dev;
+ void *cbuf, *buf;
+ int clen, len;
+{
+ sbic_regmap_p regs;
+ u_char phase, csr, asr;
+ int wait, newtarget, cmd_sent, parity_err;
+ struct sbic_acb *acb;
+
+ int discon;
+ int i;
+
+#define CSR_LOG_BUF_SIZE 0
+#if CSR_LOG_BUF_SIZE
+ int bufptr;
+ int csrbuf[CSR_LOG_BUF_SIZE];
+ bufptr=0;
+#endif
+
+ regs = dev->sc_sbicp;
+ acb = dev->sc_nexus;
+
+ /* Make sure pointers are OK */
+ dev->sc_last = dev->sc_cur = &acb->sc_pa;
+ dev->sc_tcnt = acb->sc_tcnt = 0;
+ acb->sc_pa.dc_count = 0; /* No DMA */
+ acb->sc_kv.dc_addr = buf;
+ acb->sc_kv.dc_count = len;
+
+#ifdef DEBUG
+ routine = 3;
+ debug_sbic_regs = regs; /* store this to allow debug calls */
+ if( data_pointer_debug > 1 )
+ printf("sbicicmd(%d,%d):%d\n", target, lun,
+ acb->sc_kv.dc_count);
+#endif
+
+ /*
+ * set the sbic into non-DMA mode
+ */
+ SET_SBIC_control(regs, SBIC_CTL_EDI | SBIC_CTL_IDI /*| SBIC_CTL_HSP*/);
+
+ dev->sc_stat[0] = 0xff;
+ dev->sc_msg[0] = 0xff;
+ i = 1; /* pre-load */
+
+ /* We're stealing the SCSI bus */
+ dev->sc_flags |= SBICF_ICMD;
+
+ do {
+ /*
+ * select the SCSI bus (it's an error if bus isn't free)
+ */
+ if (!( dev->sc_flags & SBICF_SELECTED )
+ && sbicselectbus(dev, regs, target, lun, dev->sc_scsiaddr)) {
+ /*printf("sbicicmd trying to select busy bus!\n");*/
+ dev->sc_flags &= ~SBICF_ICMD;
+ return(-1);
+ }
+
+ /*
+ * Wait for a phase change (or error) then let the device sequence
+ * us through the various SCSI phases.
+ */
+
+ wait = sbic_cmd_wait;
+
+ GET_SBIC_asr (regs, asr);
+ GET_SBIC_csr (regs, csr);
+ QPRINTF((">ASR:%02xCSR:%02x<", asr, csr));
+
+#if CSR_LOG_BUF_SIZE
+ csrbuf[bufptr++] = csr;
+#endif
+
+
+ switch (csr) {
+ case SBIC_CSR_S_XFERRED:
+ case SBIC_CSR_DISC:
+ case SBIC_CSR_DISC_1:
+ dev->sc_flags &= ~SBICF_SELECTED;
+ GET_SBIC_cmd_phase (regs, phase);
+ if (phase == 0x60) {
+ GET_SBIC_tlun (regs, dev->sc_stat[0]);
+ i = 0; /* done */
+/* break; /* Bypass all the state gobldygook */
+ } else {
+#ifdef DEBUG
+ if(reselect_debug>1)
+ printf("sbicicmd: handling disconnect\n");
+#endif
+ i = SBIC_STATE_DISCONNECT;
+ }
+ break;
+
+ case SBIC_CSR_XFERRED|CMD_PHASE:
+ case SBIC_CSR_MIS|CMD_PHASE:
+ case SBIC_CSR_MIS_1|CMD_PHASE:
+ case SBIC_CSR_MIS_2|CMD_PHASE:
+ if (sbicxfstart(regs, clen, CMD_PHASE, sbic_cmd_wait))
+ if (sbicxfout(regs, clen,
+ cbuf, CMD_PHASE))
+ i = sbicabort(dev, regs,"icmd sending cmd");
+#if 0
+ GET_SBIC_csr(regs, csr); /* Lets us reload tcount */
+ WAIT_CIP(regs);
+ GET_SBIC_asr(regs, asr);
+ if( asr & (SBIC_ASR_BSY|SBIC_ASR_LCI|SBIC_ASR_CIP) )
+ printf("next: cmd sent asr %02x, csr %02x\n",
+ asr, csr);
+#endif
+ break;
+
+#if 0
+ case SBIC_CSR_XFERRED|DATA_OUT_PHASE:
+ case SBIC_CSR_XFERRED|DATA_IN_PHASE:
+ case SBIC_CSR_MIS|DATA_OUT_PHASE:
+ case SBIC_CSR_MIS|DATA_IN_PHASE:
+ case SBIC_CSR_MIS_1|DATA_OUT_PHASE:
+ case SBIC_CSR_MIS_1|DATA_IN_PHASE:
+ case SBIC_CSR_MIS_2|DATA_OUT_PHASE:
+ case SBIC_CSR_MIS_2|DATA_IN_PHASE:
+ if (acb->sc_kv.dc_count <= 0)
+ i = sbicabort(dev, regs, "icmd out of data");
+ else {
+ wait = sbic_data_wait;
+ if (sbicxfstart(regs,
+ acb->sc_kv.dc_count,
+ SBIC_PHASE(csr), wait))
+ if (csr & 0x01)
+ /* data in? */
+ i=sbicxfin(regs,
+ acb->sc_kv.dc_count,
+ acb->sc_kv.dc_addr);
+ else
+ i=sbicxfout(regs,
+ acb->sc_kv.dc_count,
+ acb->sc_kv.dc_addr,
+ SBIC_PHASE(csr));
+ acb->sc_kv.dc_addr +=
+ (acb->sc_kv.dc_count - i);
+ acb->sc_kv.dc_count = i;
+ i = 1;
+ }
+ break;
+
+#endif
+ case SBIC_CSR_XFERRED|STATUS_PHASE:
+ case SBIC_CSR_MIS|STATUS_PHASE:
+ case SBIC_CSR_MIS_1|STATUS_PHASE:
+ case SBIC_CSR_MIS_2|STATUS_PHASE:
+ /*
+ * the sbic does the status/cmd-complete reading ok,
+ * so do this with its hi-level commands.
+ */
+#ifdef DEBUG
+ if(sbic_debug)
+ printf("SBICICMD status phase\n");
+#endif
+ SBIC_TC_PUT(regs, 0);
+ SET_SBIC_cmd_phase(regs, 0x46);
+ SET_SBIC_cmd(regs, SBIC_CMD_SEL_ATN_XFER);
+ break;
+
+#if THIS_IS_A_RESERVED_STATE
+ case BUS_FREE_PHASE: /* This is not legal */
+ if( dev->sc_stat[0] != 0xff )
+ goto out;
+ break;
+#endif
+
+ default:
+ i = sbicnextstate(dev, csr, asr);
+ }
+
+ /*
+ * make sure the last command was taken,
+ * ie. we're not hunting after an ignored command..
+ */
+ GET_SBIC_asr(regs, asr);
+
+ /* tapes may take a loooong time.. */
+ while (asr & SBIC_ASR_BSY){
+ if(asr & SBIC_ASR_DBR) {
+ printf("sbicicmd: Waiting while sbic is jammed, CSR:%02x,ASR:%02x\n",
+ csr,asr);
+#ifdef DDB
+ Debugger();
+#endif
+ /* SBIC is jammed */
+ /* DUNNO which direction */
+ /* Try old direction */
+ GET_SBIC_data(regs,i);
+ GET_SBIC_asr(regs, asr);
+ if( asr & SBIC_ASR_DBR) /* Wants us to write */
+ SET_SBIC_data(regs,i);
+ }
+ GET_SBIC_asr(regs, asr);
+ }
+
+ /*
+ * wait for last command to complete
+ */
+ if (asr & SBIC_ASR_LCI) {
+ printf("sbicicmd: last command ignored\n");
+ }
+ else if( i == 1 ) /* Bsy */
+ SBIC_WAIT (regs, SBIC_ASR_INT, wait);
+
+ /*
+ * do it again
+ */
+ } while ( i > 0 && dev->sc_stat[0] == 0xff);
+
+ /* Sometimes we need to do an extra read of the CSR */
+ GET_SBIC_csr(regs, csr);
+
+#if CSR_LOG_BUF_SIZE
+ if(reselect_debug>1)
+ for(i=0; i<bufptr; i++)
+ printf("CSR:%02x", csrbuf[i]);
+#endif
+
+#ifdef DEBUG
+ if(data_pointer_debug > 1)
+ printf("sbicicmd done(%d,%d):%d =%d=\n",
+ dev->target, lun,
+ acb->sc_kv.dc_count,
+ dev->sc_stat[0]);
+#endif
+
+ QPRINTF(("=STS:%02x=", dev->sc_stat[0]));
+ dev->sc_flags &= ~SBICF_ICMD;
+
+ return(dev->sc_stat[0]);
+}
+
+/*
+ * Finish SCSI xfer command: After the completion interrupt from
+ * a read/write operation, sequence through the final phases in
+ * programmed i/o. This routine is a lot like sbicicmd except we
+ * skip (and don't allow) the select, cmd out and data in/out phases.
+ */
+void
+sbicxfdone(dev, regs, target)
+ struct sbic_softc *dev;
+ sbic_regmap_p regs;
+ int target;
+{
+ u_char phase, csr;
+ int s;
+
+ QPRINTF(("{"));
+ s = splbio();
+
+ /*
+ * have the sbic complete on its own
+ */
+ SBIC_TC_PUT(regs, 0);
+ SET_SBIC_cmd_phase(regs, 0x46);
+ SET_SBIC_cmd(regs, SBIC_CMD_SEL_ATN_XFER);
+
+ do {
+ SBIC_WAIT (regs, SBIC_ASR_INT, 0);
+ GET_SBIC_csr (regs, csr);
+ QPRINTF(("%02x:", csr));
+ } while ((csr != SBIC_CSR_DISC) && (csr != SBIC_CSR_DISC_1)
+ && (csr != SBIC_CSR_S_XFERRED));
+
+ dev->sc_flags &= ~SBICF_SELECTED;
+
+ GET_SBIC_cmd_phase (regs, phase);
+ QPRINTF(("}%02x", phase));
+ if (phase == 0x60)
+ GET_SBIC_tlun(regs, dev->sc_stat[0]);
+ else
+ sbicerror(dev, regs, csr);
+
+ QPRINTF(("=STS:%02x=\n", dev->sc_stat[0]));
+ splx(s);
+}
+
+ /*
+ * No DMA chains
+ */
+
+int
+sbicgo(dev, xs)
+ struct sbic_softc *dev;
+ struct scsi_xfer *xs;
+{
+ int i, dmaflags, count, wait, usedma;
+ u_char csr, asr, cmd, *addr;
+ sbic_regmap_p regs;
+ struct sbic_acb *acb;
+
+ dev->target = xs->sc_link->target;
+ dev->lun = xs->sc_link->lun;
+ acb = dev->sc_nexus;
+ regs = dev->sc_sbicp;
+
+ usedma = sbicdmaok(dev, xs);
+#ifdef DEBUG
+ routine = 1;
+ debug_sbic_regs = regs; /* store this to allow debug calls */
+ if( data_pointer_debug > 1 )
+ printf("sbicgo(%d,%d)\n", dev->target, dev->lun);
+#endif
+
+ /*
+ * set the sbic into DMA mode
+ */
+ if( usedma )
+ SET_SBIC_control(regs, SBIC_CTL_EDI | SBIC_CTL_IDI |
+ SBIC_MACHINE_DMA_MODE);
+ else
+ SET_SBIC_control(regs, SBIC_CTL_EDI | SBIC_CTL_IDI);
+
+
+ /*
+ * select the SCSI bus (it's an error if bus isn't free)
+ */
+ if (sbicselectbus(dev, regs, dev->target, dev->lun,
+ dev->sc_scsiaddr)) {
+/* printf("sbicgo: Trying to select busy bus!\n"); */
+ return(0); /* Not done: needs to be rescheduled */
+ }
+ dev->sc_stat[0] = 0xff;
+
+ /*
+ * Calculate DMA chains now
+ */
+
+ dmaflags = 0;
+ if (acb->flags & ACB_DATAIN)
+ dmaflags |= DMAGO_READ;
+
+
+ /*
+ * Deal w/bounce buffers.
+ */
+
+ addr = acb->sc_kv.dc_addr;
+ count = acb->sc_kv.dc_count;
+ if (count && (char *)kvtop(addr) != acb->sc_pa.dc_addr) { /* XXXX check */
+ printf("sbic: DMA buffer mapping changed %x->%x\n",
+ acb->sc_pa.dc_addr, kvtop(addr));
+#ifdef DDB
+ Debugger();
+#endif
+ }
+
+#ifdef DEBUG
+ ++sbicdma_ops; /* count total DMA operations */
+#endif
+ if (count && usedma && dev->sc_flags & SBICF_BADDMA &&
+ sbiccheckdmap(addr, count, dev->sc_dmamask)) {
+ /*
+ * need to bounce the dma.
+ */
+ if (dmaflags & DMAGO_READ) {
+ acb->flags |= ACB_BBUF;
+ acb->sc_dmausrbuf = addr;
+ acb->sc_dmausrlen = count;
+ acb->sc_usrbufpa = (u_char *)kvtop(addr);
+ if(!dev->sc_tinfo[dev->target].bounce) {
+ printf("sbicgo: HELP! no bounce allocated for %d\n",
+ dev->target);
+ printf("xfer: (%x->%x,%x)\n", acb->sc_dmausrbuf,
+ acb->sc_usrbufpa, acb->sc_dmausrlen);
+#if 0
+ dev->sc_tinfo[xs->sc_link->target].bounce
+ = (char *)alloc_z2mem(MAXPHYS);
+ if (isztwomem(dev->sc_tinfo[xs->sc_link->target].bounce))
+ printf("alloc ZII target %d bounce pa 0x%x\n",
+ xs->sc_link->target,
+ kvtop(dev->sc_tinfo[xs->sc_link->target].bounce));
+ else if (dev->sc_tinfo[xs->sc_link->target].bounce)
+ printf("alloc CHIP target %d bounce pa 0x%x\n",
+ xs->sc_link->target,
+ PREP_DMA_MEM(dev->sc_tinfo[xs->sc_link->target].bounce));
+#endif
+
+ printf("Allocating %d bounce at %x\n",
+ dev->target,
+ kvtop(dev->sc_tinfo[dev->target].bounce));
+ }
+ } else { /* write: copy to dma buffer */
+#ifdef DEBUG
+ if(data_pointer_debug)
+ printf("sbicgo: copying %x bytes to target %d bounce %x\n",
+ count, dev->target,
+ kvtop(dev->sc_tinfo[dev->target].bounce));
+#endif
+ bcopy (addr, dev->sc_tinfo[dev->target].bounce, count);
+ }
+ addr = dev->sc_tinfo[dev->target].bounce;/* and use dma buffer */
+ acb->sc_kv.dc_addr = addr;
+#ifdef DEBUG
+ ++sbicdma_bounces; /* count number of bounced */
+#endif
+ }
+
+ /*
+ * Allocate the DMA chain
+ */
+
+ /* Set start KVM addresses */
+#if 0
+ acb->sc_kv.dc_addr = addr;
+ acb->sc_kv.dc_count = count;
+#endif
+
+ /* Mark end of segment */
+ acb->sc_tcnt = dev->sc_tcnt = 0;
+ acb->sc_pa.dc_count = 0;
+
+ sbic_load_ptrs(dev, regs, dev->target, dev->lun);
+ /* Enable interrupts but don't do any DMA */
+ dev->sc_tcnt = dev->sc_dmago(dev, acb->sc_pa.dc_addr,
+ acb->sc_pa.dc_count,
+ dmaflags);
+ dev->sc_flags |= SBICF_INDMA;
+ if( !usedma )
+ dev->sc_dmacmd = 0; /* Don't use DMA */
+/* SBIC_TC_PUT(regs, dev->sc_tcnt); /* XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX */
+ sbic_save_ptrs(dev, regs, dev->target, dev->lun);
+
+ /*
+ * push the data cache ( I think this won't work (EH))
+ */
+#if defined(M68040)
+ if (mmutype == MMU_68040 && usedma && count) {
+ dma_cachectl(addr, count);
+ if (((u_int)addr & 0xF) || (((u_int)addr + count) & 0xF))
+ dev->sc_flags |= SBICF_DCFLUSH;
+ }
+#endif
+
+ /*
+ * dmago() also enables interrupts for the sbic
+ */
+#ifdef DEBUG
+ if( data_pointer_debug > 1 )
+ printf("sbicgo dmago:%d(%x:%x)\n",
+ dev->target,dev->sc_cur->dc_addr,dev->sc_tcnt);
+ if( sbic_timeout && !timeout_active ) {
+ timeout((void *)sbictimeout, (void*)dev, sbic_timeout * hz);
+ timeout_active = 1;
+ }
+ debug_asr = asr;
+ debug_csr = csr;
+#endif
+
+ /*
+ * Lets cycle a while then let the interrupt handler take over
+ */
+
+ GET_SBIC_asr(regs, asr);
+ do {
+ GET_SBIC_csr(regs, csr);
+#ifdef DEBUG
+ debug_csr = csr;
+ routine = 1;
+#endif
+ QPRINTF(("go[0x%x]", csr));
+
+ i = sbicnextstate(dev, csr, asr);
+
+ WAIT_CIP(regs);
+ GET_SBIC_asr(regs, asr);
+#ifdef DEBUG
+ debug_asr = asr;
+#endif
+ if(asr & SBIC_ASR_LCI) printf("sbicgo: LCI asr:%02x csr:%02x\n",
+ asr,csr);
+ } while( i == SBIC_STATE_RUNNING
+ && asr & (SBIC_ASR_INT|SBIC_ASR_LCI) );
+
+ if (i == SBIC_STATE_DONE && dev->sc_stat[0] != 0xff) {
+ /* Did we really finish that fast? */
+ return 1;
+ }
+ return 0;
+}
+
+
+int
+sbicintr(dev)
+ struct sbic_softc *dev;
+{
+ sbic_regmap_p regs;
+ struct dma_chain *df, *dl;
+ u_char asr, csr, *tmpaddr;
+ struct sbic_acb *acb;
+ int i, newtarget, newlun;
+ unsigned tcnt;
+
+ regs = dev->sc_sbicp;
+
+ /*
+ * pending interrupt?
+ */
+ GET_SBIC_asr (regs, asr);
+ if ((asr & SBIC_ASR_INT) == 0)
+ return(0);
+
+ do {
+ GET_SBIC_csr(regs, csr);
+#ifdef DEBUG
+ debug_csr = csr;
+ routine = 2;
+#endif
+ QPRINTF(("intr[0x%x]", csr));
+
+ i = sbicnextstate(dev, csr, asr);
+
+ WAIT_CIP(regs);
+ GET_SBIC_asr(regs, asr);
+#ifdef DEBUG
+ debug_asr = asr;
+#endif
+#if 0
+ if(asr & SBIC_ASR_LCI) printf("sbicintr: LCI asr:%02x csr:%02x\n",
+ asr,csr);
+#endif
+ } while(i == SBIC_STATE_RUNNING &&
+ asr & (SBIC_ASR_INT|SBIC_ASR_LCI));
+ return(1);
+}
+
+/*
+ * Run commands and wait for disconnect
+ */
+int
+sbicpoll(dev)
+ struct sbic_softc *dev;
+{
+ sbic_regmap_p regs;
+ u_char asr, csr;
+ struct sbic_pending* pendp;
+ int i;
+ unsigned tcnt;
+
+ regs = dev->sc_sbicp;
+
+ do {
+ GET_SBIC_asr (regs, asr);
+#ifdef DEBUG
+ debug_asr = asr;
+#endif
+ GET_SBIC_csr(regs, csr);
+#ifdef DEBUG
+ debug_csr = csr;
+ routine = 2;
+#endif
+ QPRINTF(("poll[0x%x]", csr));
+
+ i = sbicnextstate(dev, csr, asr);
+
+ WAIT_CIP(regs);
+ GET_SBIC_asr(regs, asr);
+ /* tapes may take a loooong time.. */
+ while (asr & SBIC_ASR_BSY){
+ if(asr & SBIC_ASR_DBR) {
+ printf("sbipoll: Waiting while sbic is jammed, CSR:%02x,ASR:%02x\n",
+ csr,asr);
+#ifdef DDB
+ Debugger();
+#endif
+ /* SBIC is jammed */
+ /* DUNNO which direction */
+ /* Try old direction */
+ GET_SBIC_data(regs,i);
+ GET_SBIC_asr(regs, asr);
+ if( asr & SBIC_ASR_DBR) /* Wants us to write */
+ SET_SBIC_data(regs,i);
+ }
+ GET_SBIC_asr(regs, asr);
+ }
+
+ if(asr & SBIC_ASR_LCI) printf("sbicpoll: LCI asr:%02x csr:%02x\n",
+ asr,csr);
+ else if( i == 1 ) /* BSY */
+ SBIC_WAIT(regs, SBIC_ASR_INT, sbic_cmd_wait);
+ } while(i == SBIC_STATE_RUNNING);
+ return(1);
+}
+
+/*
+ * Handle a single msgin
+ */
+
+int
+sbicmsgin(dev)
+ struct sbic_softc *dev;
+{
+ sbic_regmap_p regs;
+ int recvlen;
+ u_char asr, csr, *tmpaddr;
+
+ regs = dev->sc_sbicp;
+
+ dev->sc_msg[0] = 0xff;
+ dev->sc_msg[1] = 0xff;
+
+ GET_SBIC_asr(regs, asr);
+#ifdef DEBUG
+ if(reselect_debug>1)
+ printf("sbicmsgin asr=%02x\n", asr);
+#endif
+
+ sbic_save_ptrs(dev, regs, dev->target, dev->lun);
+
+ GET_SBIC_selid (regs, csr);
+ SET_SBIC_selid (regs, csr | SBIC_SID_FROM_SCSI);
+
+ SBIC_TC_PUT(regs, 0);
+ tmpaddr = dev->sc_msg;
+ recvlen = 1;
+ do {
+ while( recvlen-- ) {
+ GET_SBIC_asr(regs, asr);
+ GET_SBIC_csr(regs, csr);
+ QPRINTF(("sbicmsgin ready to go (csr,asr)=(%02x,%02x)\n",
+ csr, asr));
+
+ RECV_BYTE(regs, *tmpaddr);
+#if 1
+ /*
+ * get the command completion interrupt, or we
+ * can't send a new command (LCI)
+ */
+ SBIC_WAIT(regs, SBIC_ASR_INT, 0);
+ GET_SBIC_csr(regs, csr);
+#else
+ WAIT_CIP(regs);
+ do {
+ GET_SBIC_asr(regs, asr);
+ csr = 0xff;
+ GET_SBIC_csr(regs, csr);
+ if( csr == 0xff )
+ printf("sbicmsgin waiting: csr %02x asr %02x\n", csr, asr);
+ } while( csr == 0xff );
+#endif
+#ifdef DEBUG
+ if(reselect_debug>1)
+ printf("sbicmsgin: got %02x csr %02x asr %02x\n",
+ *tmpaddr, csr, asr);
+#endif
+#if do_parity_check
+ if( asr & SBIC_ASR_PE ) {
+ printf ("Parity error");
+ /* This code simply does not work. */
+ WAIT_CIP(regs);
+ SET_SBIC_cmd(regs, SBIC_CMD_SET_ATN);
+ WAIT_CIP(regs);
+ GET_SBIC_asr(regs, asr);
+ WAIT_CIP(regs);
+ SET_SBIC_cmd(regs, SBIC_CMD_CLR_ACK);
+ WAIT_CIP(regs);
+ if( !(asr & SBIC_ASR_LCI) )
+ /* Target wants to send garbled msg*/
+ continue;
+ printf("--fixing\n");
+ /* loop until a msgout phase occurs on target */
+ while(csr & 0x07 != MESG_OUT_PHASE) {
+ while( asr & SBIC_ASR_BSY &&
+ !(asr & SBIC_ASR_DBR|SBIC_ASR_INT) )
+ GET_SBIC_asr(regs, asr);
+ if( asr & SBIC_ASR_DBR )
+ panic("msgin: jammed again!\n");
+ GET_SBIC_csr(regs, csr);
+ if( csr & 0x07 != MESG_OUT_PHASE ) {
+ sbicnextstate(dev, csr, asr);
+ sbic_save_ptrs(dev, regs,
+ dev->target,
+ dev->lun);
+ }
+ }
+ /* Should be msg out by now */
+ SEND_BYTE(regs, MSG_PARITY_ERROR);
+ }
+ else
+#endif
+ tmpaddr++;
+
+ if(recvlen) {
+ /* Clear ACK */
+ WAIT_CIP(regs);
+ GET_SBIC_asr(regs, asr);
+ GET_SBIC_csr(regs, csr);
+ QPRINTF(("sbicmsgin pre byte CLR_ACK (csr,asr)=(%02x,%02x)\n",
+ csr, asr));
+ SET_SBIC_cmd(regs, SBIC_CMD_CLR_ACK);
+ SBIC_WAIT(regs, SBIC_ASR_INT, 0);
+ }
+
+ };
+
+ if(dev->sc_msg[0] == 0xff) {
+ printf("sbicmsgin: sbic swallowed our message\n");
+ break;
+ }
+#ifdef DEBUG
+ if (sync_debug)
+ printf("msgin done csr 0x%x asr 0x%x msg 0x%x\n",
+ csr, asr, dev->sc_msg[0]);
+#endif
+ /*
+ * test whether this is a reply to our sync
+ * request
+ */
+ if (MSG_ISIDENTIFY(dev->sc_msg[0])) {
+ QPRINTF(("IFFY"));
+#if 0
+ /* There is an implied load-ptrs here */
+ sbic_load_ptrs(dev, regs, dev->target, dev->lun);
+#endif
+ /* Got IFFY msg -- ack it */
+ } else if (dev->sc_msg[0] == MSG_REJECT
+ && dev->sc_sync[dev->target].state == SYNC_SENT) {
+ QPRINTF(("REJECT of SYN"));
+#ifdef DEBUG
+ if (sync_debug)
+ printf("target %d rejected sync, going async\n",
+ dev->target);
+#endif
+ dev->sc_sync[dev->target].period = sbic_min_period;
+ dev->sc_sync[dev->target].offset = 0;
+ dev->sc_sync[dev->target].state = SYNC_DONE;
+ SET_SBIC_syn(regs,
+ SBIC_SYN(dev->sc_sync[dev->target].offset,
+ dev->sc_sync[dev->target].period));
+ } else if ((dev->sc_msg[0] == MSG_REJECT)) {
+ QPRINTF(("REJECT"));
+ /*
+ * we'll never REJECt a REJECT message..
+ */
+ } else if ((dev->sc_msg[0] == MSG_SAVE_DATA_PTR)) {
+ QPRINTF(("MSG_SAVE_DATA_PTR"));
+ /*
+ * don't reject this either.
+ */
+ } else if ((dev->sc_msg[0] == MSG_DISCONNECT)) {
+ QPRINTF(("DISCONNECT"));
+#ifdef DEBUG
+ if( reselect_debug>1 && dev->sc_msg[0] == MSG_DISCONNECT )
+ printf("sbicmsgin: got disconnect msg %s\n",
+ (dev->sc_flags & SBICF_ICMD)?"rejecting":"");
+#endif
+ if( dev->sc_flags & SBICF_ICMD ) {
+ /* We're in immediate mode. Prevent disconnects. */
+ /* prepare to reject the message, NACK */
+ SET_SBIC_cmd(regs, SBIC_CMD_SET_ATN);
+ WAIT_CIP(regs);
+ }
+ } else if (dev->sc_msg[0] == MSG_CMD_COMPLETE ) {
+ QPRINTF(("CMD_COMPLETE"));
+ /* !! KLUDGE ALERT !! quite a few drives don't seem to
+ * really like the current way of sending the
+ * sync-handshake together with the ident-message, and
+ * they react by sending command-complete and
+ * disconnecting right after returning the valid sync
+ * handshake. So, all I can do is reselect the drive,
+ * and hope it won't disconnect again. I don't think
+ * this is valid behavior, but I can't help fixing a
+ * problem that apparently exists.
+ *
+ * Note: we should not get here on `normal' command
+ * completion, as that condition is handled by the
+ * high-level sel&xfer resume command used to walk
+ * thru status/cc-phase.
+ */
+
+#ifdef DEBUG
+ if (sync_debug)
+ printf ("GOT MSG %d! target %d acting weird.."
+ " waiting for disconnect...\n",
+ dev->sc_msg[0], dev->target);
+#endif
+ /* Check to see if sbic is handling this */
+ GET_SBIC_asr(regs, asr);
+ if(asr & SBIC_ASR_BSY)
+ return SBIC_STATE_RUNNING;
+
+ /* Let's try this: Assume it works and set status to 00 */
+ dev->sc_stat[0] = 0;
+ } else if (dev->sc_msg[0] == MSG_EXT_MESSAGE
+ && tmpaddr == &dev->sc_msg[1]) {
+ QPRINTF(("ExtMSG\n"));
+ /* Read in whole extended message */
+ SET_SBIC_cmd(regs, SBIC_CMD_CLR_ACK);
+ SBIC_WAIT(regs, SBIC_ASR_INT, 0);
+ GET_SBIC_asr(regs, asr);
+ GET_SBIC_csr(regs, csr);
+ QPRINTF(("CLR ACK asr %02x, csr %02x\n", asr, csr));
+ RECV_BYTE(regs, *tmpaddr);
+ /* Wait for command completion IRQ */
+ SBIC_WAIT(regs, SBIC_ASR_INT, 0);
+ recvlen = *tmpaddr++;
+ QPRINTF(("Recving ext msg, asr %02x csr %02x len %02x\n",
+ asr, csr, recvlen));
+ } else if (dev->sc_msg[0] == MSG_EXT_MESSAGE && dev->sc_msg[1] == 3
+ && dev->sc_msg[2] == MSG_SYNC_REQ) {
+ QPRINTF(("SYN"));
+ dev->sc_sync[dev->target].period =
+ sbicfromscsiperiod(dev,
+ regs, dev->sc_msg[3]);
+ dev->sc_sync[dev->target].offset = dev->sc_msg[4];
+ dev->sc_sync[dev->target].state = SYNC_DONE;
+ SET_SBIC_syn(regs,
+ SBIC_SYN(dev->sc_sync[dev->target].offset,
+ dev->sc_sync[dev->target].period));
+ printf("%s: target %d now synchronous,"
+ " period=%dns, offset=%d.\n",
+ dev->sc_dev.dv_xname, dev->target,
+ dev->sc_msg[3] * 4, dev->sc_msg[4]);
+ } else {
+#ifdef DEBUG
+ if (sbic_debug || sync_debug)
+ printf ("sbicmsgin: Rejecting message 0x%02x\n",
+ dev->sc_msg[0]);
+#endif
+ /* prepare to reject the message, NACK */
+ SET_SBIC_cmd(regs, SBIC_CMD_SET_ATN);
+ WAIT_CIP(regs);
+ }
+ /* Clear ACK */
+ WAIT_CIP(regs);
+ GET_SBIC_asr(regs, asr);
+ GET_SBIC_csr(regs, csr);
+ QPRINTF(("sbicmsgin pre CLR_ACK (csr,asr)=(%02x,%02x)%d\n",
+ csr, asr, recvlen));
+ SET_SBIC_cmd(regs, SBIC_CMD_CLR_ACK);
+ SBIC_WAIT(regs, SBIC_ASR_INT, 0);
+ }
+#if 0
+ while((csr == SBIC_CSR_MSGIN_W_ACK)
+ || (SBIC_PHASE(csr) == MESG_IN_PHASE));
+#else
+ while (recvlen>0);
+#endif
+
+ QPRINTF(("sbicmsgin finished: csr %02x, asr %02x\n",csr, asr));
+
+ /* Should still have one CSR to read */
+ return SBIC_STATE_RUNNING;
+}
+
+
+/*
+ * sbicnextstate()
+ * return:
+ * 0 == done
+ * 1 == working
+ * 2 == disconnected
+ * -1 == error
+ */
+int
+sbicnextstate(dev, csr, asr)
+ struct sbic_softc *dev;
+ u_char csr, asr;
+{
+ sbic_regmap_p regs;
+ struct dma_chain *df, *dl;
+ struct sbic_acb *acb;
+ int i, newtarget, newlun, wait;
+ unsigned tcnt;
+
+ regs = dev->sc_sbicp;
+ acb = dev->sc_nexus;
+
+ QPRINTF(("next[%02x,%02x]",asr,csr));
+
+ switch (csr) {
+ case SBIC_CSR_XFERRED|CMD_PHASE:
+ case SBIC_CSR_MIS|CMD_PHASE:
+ case SBIC_CSR_MIS_1|CMD_PHASE:
+ case SBIC_CSR_MIS_2|CMD_PHASE:
+ sbic_save_ptrs(dev, regs, dev->target, dev->lun);
+ if (sbicxfstart(regs, acb->clen, CMD_PHASE, sbic_cmd_wait))
+ if (sbicxfout(regs, acb->clen,
+ &acb->cmd, CMD_PHASE))
+ goto abort;
+ break;
+
+ case SBIC_CSR_XFERRED|STATUS_PHASE:
+ case SBIC_CSR_MIS|STATUS_PHASE:
+ case SBIC_CSR_MIS_1|STATUS_PHASE:
+ case SBIC_CSR_MIS_2|STATUS_PHASE:
+ /*
+ * this should be the normal i/o completion case.
+ * get the status & cmd complete msg then let the
+ * device driver look at what happened.
+ */
+ sbicxfdone(dev,regs,dev->target);
+ /*
+ * check for overlapping cache line, flush if so
+ */
+#ifdef M68040
+ if (dev->sc_flags & SBICF_DCFLUSH) {
+#if 0
+ printf("sbic: 68040 DMA cache flush needs fixing? %x:%x\n",
+ dev->sc_xs->data, dev->sc_xs->datalen);
+#endif
+ }
+#endif
+#ifdef DEBUG
+ if( data_pointer_debug > 1 )
+ printf("next dmastop: %d(%x:%x)\n",
+ dev->target,dev->sc_cur->dc_addr,dev->sc_tcnt);
+#endif
+ dev->sc_dmastop(dev); /* was dmafree */
+ if (acb->flags & ACB_BBUF) {
+ if ((u_char *)kvtop(acb->sc_dmausrbuf) != acb->sc_usrbufpa)
+ printf("%s: WARNING - buffer mapping changed %x->%x\n",
+ dev->sc_dev.dv_xname, acb->sc_usrbufpa,
+ kvtop(acb->sc_dmausrbuf));
+#ifdef DEBUG
+ if(data_pointer_debug)
+ printf("sbicgo:copying %x bytes from target %d bounce %x\n",
+ acb->sc_dmausrlen,
+ dev->target,
+ kvtop(dev->sc_tinfo[dev->target].bounce));
+#endif
+ bcopy(dev->sc_tinfo[dev->target].bounce,
+ acb->sc_dmausrbuf,
+ acb->sc_dmausrlen);
+ }
+ dev->sc_flags &= ~(SBICF_INDMA | SBICF_DCFLUSH);
+ sbic_scsidone(acb, dev->sc_stat[0]);
+ return SBIC_STATE_DONE;
+
+ case SBIC_CSR_XFERRED|DATA_OUT_PHASE:
+ case SBIC_CSR_XFERRED|DATA_IN_PHASE:
+ case SBIC_CSR_MIS|DATA_OUT_PHASE:
+ case SBIC_CSR_MIS|DATA_IN_PHASE:
+ case SBIC_CSR_MIS_1|DATA_OUT_PHASE:
+ case SBIC_CSR_MIS_1|DATA_IN_PHASE:
+ case SBIC_CSR_MIS_2|DATA_OUT_PHASE:
+ case SBIC_CSR_MIS_2|DATA_IN_PHASE:
+ if( dev->sc_xs->flags & SCSI_POLL || dev->sc_flags & SBICF_ICMD
+ || acb->sc_dmacmd == 0 ) {
+ /* Do PIO */
+ SET_SBIC_control(regs, SBIC_CTL_EDI | SBIC_CTL_IDI);
+ if (acb->sc_kv.dc_count <= 0) {
+ printf("sbicnextstate:xfer count %d asr%x csr%x\n",
+ acb->sc_kv.dc_count, asr, csr);
+ goto abort;
+ }
+ wait = sbic_data_wait;
+ if( sbicxfstart(regs,
+ acb->sc_kv.dc_count,
+ SBIC_PHASE(csr), wait))
+ if( SBIC_PHASE(csr) == DATA_IN_PHASE )
+ /* data in? */
+ i=sbicxfin(regs,
+ acb->sc_kv.dc_count,
+ acb->sc_kv.dc_addr);
+ else
+ i=sbicxfout(regs,
+ acb->sc_kv.dc_count,
+ acb->sc_kv.dc_addr,
+ SBIC_PHASE(csr));
+ acb->sc_kv.dc_addr +=
+ (acb->sc_kv.dc_count - i);
+ acb->sc_kv.dc_count = i;
+ } else {
+ /*
+ * do scatter-gather dma
+ * hacking the controller chip, ouch..
+ */
+ SET_SBIC_control(regs, SBIC_CTL_EDI | SBIC_CTL_IDI |
+ SBIC_MACHINE_DMA_MODE);
+ /*
+ * set next dma addr and dec count
+ */
+#if 0
+ SBIC_TC_GET(regs, tcnt);
+ dev->sc_cur->dc_count -= ((dev->sc_tcnt - tcnt) >> 1);
+ dev->sc_cur->dc_addr += (dev->sc_tcnt - tcnt);
+ dev->sc_tcnt = acb->sc_tcnt = tcnt;
+#else
+ sbic_save_ptrs(dev, regs, dev->target, dev->lun);
+ sbic_load_ptrs(dev, regs, dev->target, dev->lun);
+#endif
+#ifdef DEBUG
+ if( data_pointer_debug > 1 )
+ printf("next dmanext: %d(%x:%x)\n",
+ dev->target,dev->sc_cur->dc_addr,
+ dev->sc_tcnt);
+#endif
+ dev->sc_tcnt = dev->sc_dmanext(dev);
+ SBIC_TC_PUT(regs, (unsigned)dev->sc_tcnt);
+ SET_SBIC_cmd(regs, SBIC_CMD_XFER_INFO);
+ dev->sc_flags |= SBICF_INDMA;
+ }
+ break;
+
+ case SBIC_CSR_XFERRED|MESG_IN_PHASE:
+ case SBIC_CSR_MIS|MESG_IN_PHASE:
+ case SBIC_CSR_MIS_1|MESG_IN_PHASE:
+ case SBIC_CSR_MIS_2|MESG_IN_PHASE:
+ return sbicmsgin(dev);
+
+ case SBIC_CSR_MSGIN_W_ACK:
+ SET_SBIC_cmd(regs, SBIC_CMD_CLR_ACK); /* Dunno what I'm ACKing */
+ printf("Acking unknown msgin CSR:%02x",csr);
+ break;
+
+ case SBIC_CSR_XFERRED|MESG_OUT_PHASE:
+ case SBIC_CSR_MIS|MESG_OUT_PHASE:
+ case SBIC_CSR_MIS_1|MESG_OUT_PHASE:
+ case SBIC_CSR_MIS_2|MESG_OUT_PHASE:
+#ifdef DEBUG
+ if (sync_debug)
+ printf ("sending REJECT msg to last msg.\n");
+#endif
+
+ sbic_save_ptrs(dev, regs, dev->target, dev->lun);
+ /*
+ * should only get here on reject,
+ * since it's always US that
+ * initiate a sync transfer
+ */
+ SEND_BYTE(regs, MSG_REJECT);
+ WAIT_CIP(regs);
+ if( asr & (SBIC_ASR_BSY|SBIC_ASR_LCI|SBIC_ASR_CIP) )
+ printf("next: REJECT sent asr %02x\n", asr);
+ return SBIC_STATE_RUNNING;
+
+ case SBIC_CSR_DISC:
+ case SBIC_CSR_DISC_1:
+ dev->sc_flags &= ~(SBICF_INDMA|SBICF_SELECTED);
+
+ /* Try to schedule another target */
+#ifdef DEBUG
+ if(reselect_debug>1)
+ printf("sbicnext target %d disconnected\n", dev->target);
+#endif
+ TAILQ_INSERT_HEAD(&dev->nexus_list, acb, chain);
+ ++dev->sc_tinfo[dev->target].dconns;
+ dev->sc_nexus = NULL;
+ dev->sc_xs = NULL;
+
+ if( acb->xs->flags & SCSI_POLL
+ || (dev->sc_flags & SBICF_ICMD)
+ || !sbic_parallel_operations )
+ return SBIC_STATE_DISCONNECT;
+ sbic_sched(dev);
+ return SBIC_STATE_DISCONNECT;
+
+ case SBIC_CSR_RSLT_NI:
+ case SBIC_CSR_RSLT_IFY:
+ GET_SBIC_rselid(regs, newtarget);
+ /* check SBIC_RID_SIV? */
+ newtarget &= SBIC_RID_MASK;
+ if (csr == SBIC_CSR_RSLT_IFY) {
+ /* Read IFY msg to avoid lockup */
+ GET_SBIC_data(regs, newlun);
+ WAIT_CIP(regs);
+ newlun &= SBIC_TLUN_MASK;
+ } else {
+ /* Need to get IFY message */
+ for (newlun = 256; newlun; --newlun) {
+ GET_SBIC_asr(regs, asr);
+ if (asr & SBIC_ASR_INT)
+ break;
+ delay(1);
+ }
+ newlun = 0; /* XXXX */
+ if ((asr & SBIC_ASR_INT) == 0) {
+#ifdef DEBUG
+ if (reselect_debug)
+ printf("RSLT_NI - no IFFY message? asr %x\n", asr);
+#endif
+ } else {
+ GET_SBIC_csr(regs,csr);
+ if (csr == SBIC_CSR_MIS|MESG_IN_PHASE ||
+ csr == SBIC_CSR_MIS_1|MESG_IN_PHASE ||
+ csr == SBIC_CSR_MIS_2|MESG_IN_PHASE) {
+ sbicmsgin(dev);
+ newlun = dev->sc_msg[0] & 7;
+ } else {
+ printf("RSLT_NI - not MESG_IN_PHASE %x\n",
+ csr);
+ }
+ }
+ }
+#ifdef DEBUG
+ if(reselect_debug>1 || (reselect_debug && csr==SBIC_CSR_RSLT_NI))
+ printf("sbicnext: reselect %s from targ %d lun %d\n",
+ csr == SBIC_CSR_RSLT_NI ? "NI" : "IFY",
+ newtarget, newlun);
+#endif
+ if (dev->sc_nexus) {
+#ifdef DEBUG
+ if (reselect_debug > 1)
+ printf("%s: reselect %s with active command\n",
+ dev->sc_dev.dv_xname,
+ csr == SBIC_CSR_RSLT_NI ? "NI" : "IFY");
+#ifdef DDB
+/* Debugger();*/
+#endif
+#endif
+ TAILQ_INSERT_HEAD(&dev->ready_list, dev->sc_nexus, chain);
+ dev->sc_tinfo[dev->target].lubusy &= ~(1 << dev->lun);
+ }
+ /* Reload sync values for this target */
+ if (dev->sc_sync[newtarget].state == SYNC_DONE)
+ SET_SBIC_syn(regs, SBIC_SYN (dev->sc_sync[newtarget].offset,
+ dev->sc_sync[newtarget].period));
+ else
+ SET_SBIC_syn(regs, SBIC_SYN (0, sbic_min_period));
+ for (acb = dev->nexus_list.tqh_first; acb;
+ acb = acb->chain.tqe_next) {
+ if (acb->xs->sc_link->target != newtarget ||
+ acb->xs->sc_link->lun != newlun)
+ continue;
+ TAILQ_REMOVE(&dev->nexus_list, acb, chain);
+ dev->sc_nexus = acb;
+ dev->sc_xs = acb->xs;
+ dev->sc_flags |= SBICF_SELECTED;
+ dev->target = newtarget;
+ dev->lun = newlun;
+ break;
+ }
+ if (acb == NULL) {
+ printf("%s: reselect %s targ %d not in nexus_list %x\n",
+ dev->sc_dev.dv_xname,
+ csr == SBIC_CSR_RSLT_NI ? "NI" : "IFY", newtarget,
+ &dev->nexus_list.tqh_first);
+ panic("bad reselect in sbic");
+ }
+ if (csr == SBIC_CSR_RSLT_IFY)
+ SET_SBIC_cmd(regs, SBIC_CMD_CLR_ACK);
+ break;
+
+ default:
+ abort:
+ /*
+ * Something unexpected happened -- deal with it.
+ */
+ printf("sbicnextstate: aborting csr %02x asr %02x\n", csr, asr);
+#ifdef DDB
+ Debugger();
+#endif
+#ifdef DEBUG
+ if( data_pointer_debug > 1 )
+ printf("next dmastop: %d(%x:%x)\n",
+ dev->target,dev->sc_cur->dc_addr,dev->sc_tcnt);
+#endif
+ dev->sc_dmastop(dev);
+ sbicerror(dev, regs, csr);
+ sbicabort(dev, regs, "next");
+ if (dev->sc_flags & SBICF_INDMA) {
+ /*
+ * check for overlapping cache line, flush if so
+ */
+#ifdef M68040
+ if (dev->sc_flags & SBICF_DCFLUSH) {
+#if 0
+ printf("sibc: 68040 DMA cache flush needs fixing? %x:%x\n",
+ dev->sc_xs->data, dev->sc_xs->datalen);
+#endif
+ }
+#endif
+ dev->sc_flags &=
+ ~(SBICF_INDMA | SBICF_DCFLUSH);
+#ifdef DEBUG
+ if( data_pointer_debug > 1 )
+ printf("next dmastop: %d(%x:%x)\n",
+ dev->target,dev->sc_cur->dc_addr,dev->sc_tcnt);
+#endif
+ dev->sc_dmastop(dev);
+ sbic_scsidone(acb, -1);
+ }
+ return SBIC_STATE_ERROR;
+ }
+
+ return(SBIC_STATE_RUNNING);
+}
+
+
+/*
+ * Check if DMA can not be used with specified buffer
+ */
+
+int
+sbiccheckdmap(bp, len, mask)
+ void *bp;
+ u_long len, mask;
+{
+ u_char *buffer;
+ u_long phy_buf;
+ u_long phy_len;
+
+ buffer = bp;
+
+ if (len == 0)
+ return(0);
+
+ while (len) {
+ phy_buf = kvtop(buffer);
+ if (len < (phy_len = NBPG - ((int) buffer & PGOFSET)))
+ phy_len = len;
+ if (phy_buf & mask)
+ return(1);
+ buffer += phy_len;
+ len -= phy_len;
+ }
+ return(0);
+}
+
+int
+sbictoscsiperiod(dev, regs, a)
+ struct sbic_softc *dev;
+ sbic_regmap_p regs;
+ int a;
+{
+ unsigned int fs;
+
+ /*
+ * cycle = DIV / (2*CLK)
+ * DIV = FS+2
+ * best we can do is 200ns at 20Mhz, 2 cycles
+ */
+
+ GET_SBIC_myid(regs,fs);
+ fs = (fs >>6) + 2; /* DIV */
+ fs = (fs * 10000) / (dev->sc_clkfreq<<1); /* Cycle, in ns */
+ if (a < 2) a = 8; /* map to Cycles */
+ return ((fs*a)>>2); /* in 4 ns units */
+}
+
+int
+sbicfromscsiperiod(dev, regs, p)
+ struct sbic_softc *dev;
+ sbic_regmap_p regs;
+ int p;
+{
+ register unsigned int fs, ret;
+
+ /* Just the inverse of the above */
+
+ GET_SBIC_myid(regs,fs);
+ fs = (fs >>6) + 2; /* DIV */
+ fs = (fs * 10000) / (dev->sc_clkfreq<<1); /* Cycle, in ns */
+
+ ret = p << 2; /* in ns units */
+ ret = ret / fs; /* in Cycles */
+ if (ret < sbic_min_period)
+ return(sbic_min_period);
+
+ /* verify rounding */
+ if (sbictoscsiperiod(dev, regs, ret) < p)
+ ret++;
+ return (ret >= 8) ? 0 : ret;
+}
+
+#ifdef DEBUG
+
+void sbicdumpstate()
+{
+ u_char csr, asr;
+
+ GET_SBIC_asr(debug_sbic_regs,asr);
+ GET_SBIC_csr(debug_sbic_regs,csr);
+ printf("%s: asr:csr(%02x:%02x)->(%02x:%02x)\n",
+ (routine==1)?"sbicgo":
+ (routine==2)?"sbicintr":
+ (routine==3)?"sbicicmd":
+ (routine==4)?"sbicnext":"unknown",
+ debug_asr, debug_csr, asr, csr);
+
+}
+
+void sbictimeout(dev)
+ struct sbic_softc *dev;
+{
+ int s, asr;
+
+ /* Dump this every second while there's an active command */
+ if( dev->sc_nexus ) {
+ s = splbio();
+ GET_SBIC_asr(dev->sc_sbicp, asr);
+ if( asr & SBIC_ASR_INT ) {
+ /* We need to service a missed IRQ */
+ printf("Servicing a missed int:(%02x,%02x)->(%02x,??)\n",
+ debug_asr, debug_csr, asr);
+ sbicintr(dev);
+ }
+ splx(s);
+ sbicdumpstate();
+ timeout((void *)sbictimeout, (void*)dev, sbic_timeout * hz);
+ } else timeout_active = 0;
+}
+
+#endif