NetBSD/sys/arch/amiga/dev/siop.c
thorpej 5b39541e48 New generic disk framework. Highlights:
- New metrics handling.  Metrics are now kept in the new
	  `struct disk'.  Busy time is now stored as a timeval, and
	  transfer count in bytes.

	- Storage for disklabels is now dynamically allocated, so that
	  the size of the disk structure is not machine-dependent.

	- Several new functions for attaching and detaching disks, and
	  handling metrics calculation.

Old-style instrumentation is still supported in drivers that did it before.
However, old-style instrumentation is being deprecated, and will go away
once the userland utilities are updated for the new framework.

For usage and architectural details, see the forthcoming disk(9) manual
page.
1996-01-07 22:01:38 +00:00

1608 lines
43 KiB
C

/* $NetBSD: siop.c,v 1.27 1996/01/07 22:01:58 thorpej Exp $ */
/*
* Copyright (c) 1994 Michael L. Hitch
* Copyright (c) 1990 The Regents of the University of California.
* All rights reserved.
*
* This code is derived from software contributed to Berkeley by
* Van Jacobson of Lawrence Berkeley Laboratory.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by the University of
* California, Berkeley and its contributors.
* 4. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* @(#)siop.c 7.5 (Berkeley) 5/4/91
*/
/*
* AMIGA 53C710 scsi adaptor driver
*/
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/device.h>
#include <sys/disklabel.h>
#include <sys/dkstat.h>
#include <sys/buf.h>
#include <scsi/scsi_all.h>
#include <scsi/scsiconf.h>
#include <machine/cpu.h>
#include <amiga/amiga/custom.h>
#include <amiga/amiga/isr.h>
#include <amiga/dev/siopreg.h>
#include <amiga/dev/siopvar.h>
extern u_int kvtop();
/*
* SCSI delays
* In u-seconds, primarily for state changes on the SPC.
*/
#define SCSI_CMD_WAIT 500000 /* wait per step of 'immediate' cmds */
#define SCSI_DATA_WAIT 500000 /* wait per data in/out step */
#define SCSI_INIT_WAIT 500000 /* wait per step (both) during init */
void siop_select __P((struct siop_softc *));
void siopabort __P((struct siop_softc *, siop_regmap_p, char *));
void sioperror __P((struct siop_softc *, siop_regmap_p, u_char));
void siopstart __P((struct siop_softc *));
void siopreset __P((struct siop_softc *));
void siopsetdelay __P((int));
void siop_scsidone __P((struct siop_acb *, int));
void siop_sched __P((struct siop_softc *));
int siop_poll __P((struct siop_softc *, struct siop_acb *));
int siopintr __P((struct siop_softc *));
/* 53C710 script */
const
#include <amiga/dev/siop_script.out>
/* default to not inhibit sync negotiation on any drive */
u_char siop_inhibit_sync[8] = { 0, 0, 0, 0, 0, 0, 0 }; /* initialize, so patchable */
u_char siop_allow_disc[8] = {3, 3, 3, 3, 3, 3, 3, 3};
int siop_no_dma = 0;
int siop_reset_delay = 250; /* delay after reset, in milleseconds */
int siop_cmd_wait = SCSI_CMD_WAIT;
int siop_data_wait = SCSI_DATA_WAIT;
int siop_init_wait = SCSI_INIT_WAIT;
#ifdef DEBUG
/*
* sync period transfer lookup - only valid for 66Mhz clock
*/
static struct {
unsigned char p; /* period from sync request message */
unsigned char r; /* siop_period << 4 | sbcl */
} sync_tab[] = {
{ 60/4, 0<<4 | 1},
{ 76/4, 1<<4 | 1},
{ 92/4, 2<<4 | 1},
{ 92/4, 0<<4 | 2},
{108/4, 3<<4 | 1},
{116/4, 1<<4 | 2},
{120/4, 4<<4 | 1},
{120/4, 0<<4 | 3},
{136/4, 5<<4 | 1},
{140/4, 2<<4 | 2},
{152/4, 6<<4 | 1},
{152/4, 1<<4 | 3},
{164/4, 3<<4 | 2},
{168/4, 7<<4 | 1},
{180/4, 2<<4 | 3},
{184/4, 4<<4 | 2},
{208/4, 5<<4 | 2},
{212/4, 3<<4 | 3},
{232/4, 6<<4 | 2},
{240/4, 4<<4 | 3},
{256/4, 7<<4 | 2},
{272/4, 5<<4 | 3},
{300/4, 6<<4 | 3},
{332/4, 7<<4 | 3}
};
#endif
#ifdef DEBUG
/*
* 0x01 - full debug
* 0x02 - DMA chaining
* 0x04 - siopintr
* 0x08 - phase mismatch
* 0x10 - <not used>
* 0x20 - panic on unhandled exceptions
* 0x100 - disconnect/reselect
*/
int siop_debug = 0;
int siopsync_debug = 0;
int siopdma_hits = 0;
int siopdma_misses = 0;
int siopchain_ints = 0;
int siopstarts = 0;
int siopints = 0;
int siopphmm = 0;
#define SIOP_TRACE_SIZE 128
#define SIOP_TRACE(a,b,c,d) \
siop_trbuf[siop_trix] = (a); \
siop_trbuf[siop_trix+1] = (b); \
siop_trbuf[siop_trix+2] = (c); \
siop_trbuf[siop_trix+3] = (d); \
siop_trix = (siop_trix + 4) & (SIOP_TRACE_SIZE - 1);
u_char siop_trbuf[SIOP_TRACE_SIZE];
int siop_trix;
void siop_dump __P((struct siop_softc *));
void siop_dump_trace __P((void));
#else
#define SIOP_TRACE(a,b,c,d)
#endif
/*
* default minphys routine for siop based controllers
*/
void
siop_minphys(bp)
struct buf *bp;
{
/*
* No max transfer at this level.
*/
minphys(bp);
}
/*
* used by specific siop controller
*
*/
int
siop_scsicmd(xs)
struct scsi_xfer *xs;
{
struct siop_acb *acb;
struct siop_softc *sc;
struct scsi_link *slp;
int flags, s, i;
slp = xs->sc_link;
sc = slp->adapter_softc;
flags = xs->flags;
/* XXXX ?? */
if (flags & SCSI_DATA_UIO)
panic("siop: scsi data uio requested");
/* XXXX ?? */
if (sc->sc_nexus && flags & SCSI_POLL)
/* panic("siop_scsicmd: busy");*/
printf("siop_scsicmd: busy\n");
s = splbio();
acb = sc->free_list.tqh_first;
if (acb) {
TAILQ_REMOVE(&sc->free_list, acb, chain);
}
splx(s);
if (acb == NULL) {
xs->error = XS_DRIVER_STUFFUP;
return(TRY_AGAIN_LATER);
}
acb->flags = ACB_ACTIVE;
acb->xs = xs;
bcopy(xs->cmd, &acb->cmd, xs->cmdlen);
acb->clen = xs->cmdlen;
acb->daddr = xs->data;
acb->dleft = xs->datalen;
s = splbio();
TAILQ_INSERT_TAIL(&sc->ready_list, acb, chain);
if (sc->sc_nexus == NULL)
siop_sched(sc);
splx(s);
if (flags & SCSI_POLL || siop_no_dma)
return(siop_poll(sc, acb));
return(SUCCESSFULLY_QUEUED);
}
int
siop_poll(sc, acb)
struct siop_softc *sc;
struct siop_acb *acb;
{
siop_regmap_p rp = sc->sc_siopp;
struct scsi_xfer *xs = acb->xs;
int i;
int status;
u_char istat;
u_char dstat;
u_char sstat0;
int s;
int to;
s = splbio();
to = xs->timeout / 1000;
if (sc->nexus_list.tqh_first)
printf("%s: siop_poll called with disconnected device\n",
sc->sc_dev.dv_xname);
for (;;) {
/* use cmd_wait values? */
i = 50000;
spl0();
while (((istat = rp->siop_istat) &
(SIOP_ISTAT_SIP | SIOP_ISTAT_DIP)) == 0) {
if (--i <= 0) {
#ifdef DEBUG
printf ("waiting: tgt %d cmd %02x sbcl %02x dsp %x (+%x) dcmd %x ds %x timeout %d\n",
xs->sc_link->target, acb->cmd.opcode,
rp->siop_sbcl, rp->siop_dsp,
rp->siop_dsp - sc->sc_scriptspa,
*((long *)&rp->siop_dcmd), &acb->ds, acb->xs->timeout);
#endif
i = 50000;
--to;
if (to <= 0) {
siopreset(sc);
return(COMPLETE);
}
}
delay(10);
}
sstat0 = rp->siop_sstat0;
dstat = rp->siop_dstat;
if (siop_checkintr(sc, istat, dstat, sstat0, &status)) {
if (acb != sc->sc_nexus)
printf("%s: siop_poll disconnected device completed\n",
sc->sc_dev.dv_xname);
else if ((sc->sc_flags & SIOP_INTDEFER) == 0) {
sc->sc_flags &= ~SIOP_INTSOFF;
rp->siop_sien = sc->sc_sien;
rp->siop_dien = sc->sc_dien;
}
siop_scsidone(sc->sc_nexus, status);
}
if (xs->flags & ITSDONE)
break;
}
splx(s);
return (COMPLETE);
}
/*
* start next command that's ready
*/
void
siop_sched(sc)
struct siop_softc *sc;
{
struct scsi_link *slp;
struct siop_acb *acb;
int stat, i;
#ifdef DEBUG
if (sc->sc_nexus) {
printf("%s: siop_sched- nexus %x/%d ready %x/%d\n",
sc->sc_dev.dv_xname, sc->sc_nexus,
sc->sc_nexus->xs->sc_link->target,
sc->ready_list.tqh_first,
sc->ready_list.tqh_first->xs->sc_link->target);
return;
}
#endif
for (acb = sc->ready_list.tqh_first; acb; acb = acb->chain.tqe_next) {
slp = acb->xs->sc_link;
i = slp->target;
if(!(sc->sc_tinfo[i].lubusy & (1 << slp->lun))) {
struct siop_tinfo *ti = &sc->sc_tinfo[i];
TAILQ_REMOVE(&sc->ready_list, acb, chain);
sc->sc_nexus = acb;
slp = acb->xs->sc_link;
ti = &sc->sc_tinfo[slp->target];
ti->lubusy |= (1 << slp->lun);
break;
}
}
if (acb == NULL) {
#ifdef DEBUGXXX
printf("%s: siop_sched didn't find ready command\n",
sc->sc_dev.dv_xname);
#endif
return;
}
if (acb->xs->flags & SCSI_RESET)
siopreset(sc);
#if 0
acb->cmd.bytes[0] |= slp->lun << 5; /* XXXX */
#endif
++sc->sc_active;
siop_select(sc);
}
void
siop_scsidone(acb, stat)
struct siop_acb *acb;
int stat;
{
struct scsi_xfer *xs = acb->xs;
struct scsi_link *slp = xs->sc_link;
struct siop_softc *sc = slp->adapter_softc;
int s, dosched = 0;
#ifdef DIAGNOSTIC
if (acb == NULL || xs == NULL)
panic("siop_scsidone");
#endif
/*
* XXX Support old-style instrumentation for now.
* IS THIS REALLY THE RIGHT PLACE FOR THIS? --thorpej
*/
if (slp->device_softc &&
((struct device *)(slp->device_softc))->dv_unit < dk_ndrive)
++dk_xfer[((struct device *)(slp->device_softc))->dv_unit];
/*
* is this right?
*/
xs->status = stat;
if (xs->error == XS_NOERROR && !(acb->flags & ACB_CHKSENSE)) {
if (stat == SCSI_CHECK) {
struct scsi_sense *ss = (void *)&acb->cmd;
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->daddr = (char *)&xs->sense;
acb->dleft = sizeof(struct scsi_sense_data);
acb->flags = ACB_ACTIVE | ACB_CHKSENSE;
TAILQ_INSERT_HEAD(&sc->ready_list, acb, chain);
--sc->sc_active;
sc->sc_tinfo[slp->target].lubusy &=
~(1 << slp->lun);
sc->sc_tinfo[slp->target].senses++;
if (sc->sc_nexus == acb) {
sc->sc_nexus = NULL;
siop_sched(sc);
}
SIOP_TRACE('d','s',0,0)
return;
}
}
if (xs->error == XS_NOERROR && (acb->flags & ACB_CHKSENSE)) {
xs->error = XS_SENSE;
} 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 == sc->sc_nexus) {
sc->sc_nexus = NULL;
sc->sc_tinfo[slp->target].lubusy &= ~(1<<slp->lun);
if (sc->ready_list.tqh_first)
dosched = 1; /* start next command */
--sc->sc_active;
SIOP_TRACE('d','a',stat,0)
} else if (sc->ready_list.tqh_last == &acb->chain.tqe_next) {
TAILQ_REMOVE(&sc->ready_list, acb, chain);
SIOP_TRACE('d','r',stat,0)
} else {
register struct siop_acb *acb2;
for (acb2 = sc->nexus_list.tqh_first; acb2;
acb2 = acb2->chain.tqe_next)
if (acb2 == acb) {
TAILQ_REMOVE(&sc->nexus_list, acb, chain);
sc->sc_tinfo[slp->target].lubusy
&= ~(1<<slp->lun);
--sc->sc_active;
break;
}
if (acb2)
;
else if (acb->chain.tqe_next) {
TAILQ_REMOVE(&sc->ready_list, acb, chain);
--sc->sc_active;
} else {
printf("%s: can't find matching acb\n",
sc->sc_dev.dv_xname);
#ifdef DDB
/* Debugger(); */
#endif
}
SIOP_TRACE('d','n',stat,0);
}
/* Put it on the free list. */
acb->flags = ACB_FREE;
TAILQ_INSERT_HEAD(&sc->free_list, acb, chain);
sc->sc_tinfo[slp->target].cmds++;
scsi_done(xs);
if (dosched && sc->sc_nexus == NULL)
siop_sched(sc);
}
void
siopabort(sc, rp, where)
register struct siop_softc *sc;
siop_regmap_p rp;
char *where;
{
int i;
printf ("%s: abort %s: dstat %02x, sstat0 %02x sbcl %02x\n",
sc->sc_dev.dv_xname,
where, rp->siop_dstat, rp->siop_sstat0, rp->siop_sbcl);
if (sc->sc_active > 0) {
#ifdef TODO
SET_SBIC_cmd (rp, SBIC_CMD_ABORT);
WAIT_CIP (rp);
GET_SBIC_asr (rp, asr);
if (asr & (SBIC_ASR_BSY|SBIC_ASR_LCI))
{
/* ok, get more drastic.. */
SET_SBIC_cmd (rp, SBIC_CMD_RESET);
delay(25);
SBIC_WAIT(rp, SBIC_ASR_INT, 0);
GET_SBIC_csr (rp, csr); /* clears interrupt also */
return;
}
do
{
SBIC_WAIT (rp, SBIC_ASR_INT, 0);
GET_SBIC_csr (rp, csr);
}
while ((csr != SBIC_CSR_DISC) && (csr != SBIC_CSR_DISC_1)
&& (csr != SBIC_CSR_CMD_INVALID));
#endif
/* lets just hope it worked.. */
#ifdef fix_this
for (i = 0; i < 2; ++i) {
if (sc->sc_iob[i].sc_xs && &sc->sc_iob[i] !=
sc->sc_cur) {
printf ("siopabort: cleanup!\n");
sc->sc_iob[i].sc_xs = NULL;
}
}
#endif /* fix_this */
/* sc->sc_active = 0; */
}
}
void
siopinitialize(sc)
struct siop_softc *sc;
{
int i;
u_int inhibit_sync;
extern u_long scsi_nosync;
extern int shift_nosync;
/*
* Need to check that scripts is on a long word boundary
* and that DS is on a long word boundary.
* Also should verify that dev doesn't span non-contiguous
* physical pages.
*/
sc->sc_scriptspa = kvtop(scripts);
sc->sc_tcp[1] = 1000 / sc->sc_clock_freq;
sc->sc_tcp[2] = 1500 / sc->sc_clock_freq;
sc->sc_tcp[3] = 2000 / sc->sc_clock_freq;
sc->sc_minsync = sc->sc_tcp[1]; /* in 4ns units */
if (sc->sc_minsync < 25)
sc->sc_minsync = 25;
if (sc->sc_clock_freq <= 25) {
sc->sc_dcntl = 0x80; /* SCLK/1 */
sc->sc_tcp[0] = sc->sc_tcp[1];
} else if (sc->sc_clock_freq <= 37) {
sc->sc_dcntl = 0x40; /* SCLK/1.5 */
sc->sc_tcp[0] = sc->sc_tcp[2];
} else if (sc->sc_clock_freq <= 50) {
sc->sc_dcntl = 0x00; /* SCLK/2 */
sc->sc_tcp[0] = sc->sc_tcp[3];
} else {
sc->sc_dcntl = 0xc0; /* SCLK/3 */
sc->sc_tcp[0] = 3000 / sc->sc_clock_freq;
}
if (scsi_nosync) {
inhibit_sync = (scsi_nosync >> shift_nosync) & 0xff;
shift_nosync += 8;
#ifdef DEBUG
if (inhibit_sync)
printf("%s: Inhibiting synchronous transfer %02x\n",
sc->sc_dev.dv_xname, inhibit_sync);
#endif
for (i = 0; i < 8; ++i)
if (inhibit_sync & (1 << i))
siop_inhibit_sync[i] = 1;
}
siopreset (sc);
}
void
siopreset(sc)
struct siop_softc *sc;
{
siop_regmap_p rp;
u_int i, s;
u_char dummy;
struct siop_acb *acb;
rp = sc->sc_siopp;
if (sc->sc_flags & SIOP_ALIVE)
siopabort(sc, rp, "reset");
printf("%s: ", sc->sc_dev.dv_xname); /* XXXX */
s = splbio();
/*
* Reset the chip
* XXX - is this really needed?
*/
rp->siop_istat |= SIOP_ISTAT_ABRT; /* abort current script */
rp->siop_istat |= SIOP_ISTAT_RST; /* reset chip */
rp->siop_istat &= ~SIOP_ISTAT_RST;
/*
* Reset SCSI bus (do we really want this?)
*/
rp->siop_sien = 0;
rp->siop_scntl1 |= SIOP_SCNTL1_RST;
delay(1);
rp->siop_scntl1 &= ~SIOP_SCNTL1_RST;
/*
* Set up various chip parameters
*/
rp->siop_scntl0 = SIOP_ARB_FULL | SIOP_SCNTL0_EPC | SIOP_SCNTL0_EPG;
rp->siop_scntl1 = SIOP_SCNTL1_ESR;
rp->siop_dcntl = sc->sc_dcntl;
rp->siop_dmode = 0x80; /* burst length = 4 */
rp->siop_sien = 0x00; /* don't enable interrupts yet */
rp->siop_dien = 0x00; /* don't enable interrupts yet */
rp->siop_scid = 1 << sc->sc_link.adapter_target;
rp->siop_dwt = 0x00;
rp->siop_ctest0 |= SIOP_CTEST0_BTD | SIOP_CTEST0_EAN;
rp->siop_ctest7 |= sc->sc_ctest7;
/* will need to re-negotiate sync xfers */
bzero(&sc->sc_sync, sizeof (sc->sc_sync));
i = rp->siop_istat;
if (i & SIOP_ISTAT_SIP)
dummy = rp->siop_sstat0;
if (i & SIOP_ISTAT_DIP)
dummy = rp->siop_dstat;
splx (s);
delay (siop_reset_delay * 1000);
printf("siop id %d reset V%d\n", sc->sc_link.adapter_target,
rp->siop_ctest8 >> 4);
if ((sc->sc_flags & SIOP_ALIVE) == 0) {
TAILQ_INIT(&sc->ready_list);
TAILQ_INIT(&sc->nexus_list);
TAILQ_INIT(&sc->free_list);
sc->sc_nexus = NULL;
acb = sc->sc_acb;
bzero(acb, sizeof(sc->sc_acb));
for (i = 0; i < sizeof(sc->sc_acb) / sizeof(*acb); i++) {
TAILQ_INSERT_TAIL(&sc->free_list, acb, chain);
acb++;
}
bzero(sc->sc_tinfo, sizeof(sc->sc_tinfo));
} else {
if (sc->sc_nexus != NULL) {
sc->sc_nexus->xs->error = XS_DRIVER_STUFFUP;
siop_scsidone(sc->sc_nexus, sc->sc_nexus->stat[0]);
}
while (acb = sc->nexus_list.tqh_first) {
acb->xs->error = XS_DRIVER_STUFFUP;
siop_scsidone(acb, acb->stat[0]);
}
}
sc->sc_flags |= SIOP_ALIVE;
sc->sc_flags &= ~(SIOP_INTDEFER|SIOP_INTSOFF);
/* enable SCSI and DMA interrupts */
sc->sc_sien = SIOP_SIEN_M_A | SIOP_SIEN_STO | /*SIOP_SIEN_SEL |*/ SIOP_SIEN_SGE |
SIOP_SIEN_UDC | SIOP_SIEN_RST | SIOP_SIEN_PAR;
sc->sc_dien = SIOP_DIEN_BF | SIOP_DIEN_ABRT | SIOP_DIEN_SIR |
/*SIOP_DIEN_WTD |*/ SIOP_DIEN_IID;
rp->siop_sien = sc->sc_sien;
rp->siop_dien = sc->sc_dien;
}
/*
* Setup Data Storage for 53C710 and start SCRIPTS processing
*/
void
siop_start (sc, target, lun, cbuf, clen, buf, len)
struct siop_softc *sc;
int target;
int lun;
u_char *cbuf;
int clen;
u_char *buf;
int len;
{
siop_regmap_p rp = sc->sc_siopp;
int i;
int nchain;
int count, tcount;
char *addr, *dmaend;
struct siop_acb *acb = sc->sc_nexus;
#ifdef DEBUG
if (siop_debug & 0x100 && rp->siop_sbcl & SIOP_BSY) {
printf ("ACK! siop was busy: rp %x script %x dsa %x active %d\n",
rp, &scripts, &acb->ds, sc->sc_active);
printf ("istat %02x sfbr %02x lcrc %02x sien %02x dien %02x\n",
rp->siop_istat, rp->siop_sfbr, rp->siop_lcrc,
rp->siop_sien, rp->siop_dien);
#ifdef DDB
/*Debugger();*/
#endif
}
#endif
acb->msgout[0] = MSG_IDENTIFY | lun;
if (siop_allow_disc[target] & 2 ||
(siop_allow_disc[target] && len == 0))
acb->msgout[0] = MSG_IDENTIFY_DR | lun;
acb->status = 0;
acb->stat[0] = -1;
acb->msg[0] = -1;
acb->ds.scsi_addr = (0x10000 << target) | (sc->sc_sync[target].sxfer << 8);
acb->ds.idlen = 1;
acb->ds.idbuf = (char *) kvtop(&acb->msgout[0]);
acb->ds.cmdlen = clen;
acb->ds.cmdbuf = (char *) kvtop(cbuf);
acb->ds.stslen = 1;
acb->ds.stsbuf = (char *) kvtop(&acb->stat[0]);
acb->ds.msglen = 1;
acb->ds.msgbuf = (char *) kvtop(&acb->msg[0]);
acb->msg[1] = -1;
acb->ds.msginlen = 1;
acb->ds.extmsglen = 1;
acb->ds.synmsglen = 3;
acb->ds.msginbuf = (char *) kvtop(&acb->msg[1]);
acb->ds.extmsgbuf = (char *) kvtop(&acb->msg[2]);
acb->ds.synmsgbuf = (char *) kvtop(&acb->msg[3]);
bzero(&acb->ds.chain, sizeof (acb->ds.chain));
if (sc->sc_sync[target].state == SYNC_START) {
if (siop_inhibit_sync[target]) {
sc->sc_sync[target].state = SYNC_DONE;
sc->sc_sync[target].sbcl = 0;
sc->sc_sync[target].sxfer = 0;
#ifdef DEBUG
if (siopsync_debug)
printf ("Forcing target %d asynchronous\n", target);
#endif
}
else {
acb->msg[2] = -1;
acb->msgout[1] = MSG_EXT_MESSAGE;
acb->msgout[2] = 3;
acb->msgout[3] = MSG_SYNC_REQ;
#ifdef MAXTOR_SYNC_KLUDGE
acb->msgout[4] = 50 / 4; /* ask for ridiculous period */
#else
acb->msgout[4] = sc->sc_minsync;
#endif
acb->msgout[5] = SIOP_MAX_OFFSET;
acb->ds.idlen = 6;
sc->sc_sync[target].state = SYNC_SENT;
#ifdef DEBUG
if (siopsync_debug)
printf ("Sending sync request to target %d\n", target);
#endif
}
}
/*
* Build physical DMA addresses for scatter/gather I/O
*/
acb->iob_buf = buf;
acb->iob_len = len;
acb->iob_curbuf = acb->iob_curlen = 0;
nchain = 0;
count = len;
addr = buf;
dmaend = NULL;
while (count > 0) {
acb->ds.chain[nchain].databuf = (char *) kvtop (addr);
if (count < (tcount = NBPG - ((int) addr & PGOFSET)))
tcount = count;
acb->ds.chain[nchain].datalen = tcount;
addr += tcount;
count -= tcount;
if (acb->ds.chain[nchain].databuf == dmaend) {
dmaend += acb->ds.chain[nchain].datalen;
acb->ds.chain[nchain].datalen = 0;
acb->ds.chain[--nchain].datalen += tcount;
#ifdef DEBUG
++siopdma_hits;
#endif
}
else {
dmaend = acb->ds.chain[nchain].databuf +
acb->ds.chain[nchain].datalen;
acb->ds.chain[nchain].datalen = tcount;
#ifdef DEBUG
if (nchain) /* Don't count miss on first one */
++siopdma_misses;
#endif
}
++nchain;
}
#ifdef DEBUG
if (nchain != 1 && len != 0 && siop_debug & 3) {
printf ("DMA chaining set: %d\n", nchain);
for (i = 0; i < nchain; ++i) {
printf (" [%d] %8x %4x\n", i, acb->ds.chain[i].databuf,
acb->ds.chain[i].datalen);
}
}
#endif
/* push data cache for all data the 53c710 needs to access */
dma_cachectl (sc, sizeof (struct siop_softc));
dma_cachectl (cbuf, clen);
if (buf != NULL && len != 0)
dma_cachectl (buf, len);
#ifdef DEBUG
if (siop_debug & 0x100 && rp->siop_sbcl & SIOP_BSY) {
printf ("ACK! siop was busy at start: rp %x script %x dsa %x active %d\n",
rp, &scripts, &acb->ds, sc->sc_active);
#ifdef DDB
/*Debugger();*/
#endif
}
#endif
if (sc->nexus_list.tqh_first == NULL) {
if (rp->siop_istat & SIOP_ISTAT_CON)
printf("%s: siop_select while connected?\n",
sc->sc_dev.dv_xname);
rp->siop_temp = 0;
rp->siop_sbcl = sc->sc_sync[target].sbcl;
rp->siop_dsa = kvtop(&acb->ds);
rp->siop_dsp = sc->sc_scriptspa;
SIOP_TRACE('s',1,0,0)
} else {
if ((rp->siop_istat & SIOP_ISTAT_CON) == 0) {
rp->siop_istat = SIOP_ISTAT_SIGP;
SIOP_TRACE('s',2,0,0);
}
else {
SIOP_TRACE('s',3,rp->siop_istat,0);
}
}
#ifdef DEBUG
++siopstarts;
#endif
}
/*
* Process a DMA or SCSI interrupt from the 53C710 SIOP
*/
int
siop_checkintr(sc, istat, dstat, sstat0, status)
struct siop_softc *sc;
u_char istat;
u_char dstat;
u_char sstat0;
int *status;
{
siop_regmap_p rp = sc->sc_siopp;
struct siop_acb *acb = sc->sc_nexus;
int target;
int dfifo, dbc, sstat1;
dfifo = rp->siop_dfifo;
dbc = rp->siop_dbc0;
sstat1 = rp->siop_sstat1;
rp->siop_ctest8 |= SIOP_CTEST8_CLF;
while ((rp->siop_ctest1 & SIOP_CTEST1_FMT) != SIOP_CTEST1_FMT)
;
rp->siop_ctest8 &= ~SIOP_CTEST8_CLF;
#ifdef DEBUG
++siopints;
#if 0
if (siop_debug & 0x100) {
DCIAS(&acb->stat[0]); /* XXX */
printf ("siopchkintr: istat %x dstat %x sstat0 %x dsps %x sbcl %x sts %x msg %x\n",
istat, dstat, sstat0, rp->siop_dsps, rp->siop_sbcl, acb->stat[0], acb->msg[0]);
printf ("sync msg in: %02x %02x %02x %02x %02x %02x\n",
acb->msg[0], acb->msg[1], acb->msg[2],
acb->msg[3], acb->msg[4], acb->msg[5]);
}
#endif
if (rp->siop_dsp && (rp->siop_dsp < sc->sc_scriptspa ||
rp->siop_dsp >= sc->sc_scriptspa + sizeof(scripts))) {
printf ("%s: dsp not within script dsp %x scripts %x:%x",
sc->sc_dev.dv_xname, rp->siop_dsp, sc->sc_scriptspa,
sc->sc_scriptspa + sizeof(scripts));
printf(" istat %x dstat %x sstat0 %x\n",
istat, dstat, sstat0);
Debugger();
}
#endif
SIOP_TRACE('i',dstat,istat,(istat&SIOP_ISTAT_DIP)?rp->siop_dsps&0xff:sstat0);
if (dstat & SIOP_DSTAT_SIR && rp->siop_dsps == 0xff00) {
/* Normal completion status, or check condition */
#ifdef DEBUG
if (rp->siop_dsa != kvtop(&acb->ds)) {
printf ("siop: invalid dsa: %x %x\n", rp->siop_dsa,
kvtop(&acb->ds));
panic("*** siop DSA invalid ***");
}
#endif
target = acb->xs->sc_link->target;
if (sc->sc_sync[target].state == SYNC_SENT) {
#ifdef DEBUG
if (siopsync_debug)
printf ("sync msg in: %02x %02x %02x %02x %02x %02x\n",
acb->msg[0], acb->msg[1], acb->msg[2],
acb->msg[3], acb->msg[4], acb->msg[5]);
#endif
if (acb->msg[1] == 0xff)
printf ("%s: target %d ignored sync request\n",
sc->sc_dev.dv_xname, target);
else if (acb->msg[1] == MSG_REJECT)
printf ("%s: target %d rejected sync request\n",
sc->sc_dev.dv_xname, target);
sc->sc_sync[target].state = SYNC_DONE;
sc->sc_sync[target].sxfer = 0;
sc->sc_sync[target].sbcl = 0;
if (acb->msg[2] == 3 &&
acb->msg[3] == MSG_SYNC_REQ &&
acb->msg[5] != 0) {
#ifdef MAXTOR_KLUDGE
/*
* Kludge for my Maxtor XT8580S
* It accepts whatever we request, even
* though it won't work. So we ask for
* a short period than we can handle. If
* the device says it can do it, use 208ns.
* If the device says it can do less than
* 100ns, then we limit it to 100ns.
*/
if (acb->msg[4] && acb->msg[4] < 100 / 4) {
#ifdef DEBUG
printf ("%d: target %d wanted %dns period\n",
sc->sc_dev.dv_xname, target,
acb->msg[4] * 4);
#endif
if (acb->msg[4] == 50 / 4)
acb->msg[4] = 208 / 4;
else
acb->msg[4] = 100 / 4;
}
#endif /* MAXTOR_KLUDGE */
printf ("%s: target %d now synchronous, period=%dns, offset=%d\n",
sc->sc_dev.dv_xname, target,
acb->msg[4] * 4, acb->msg[5]);
scsi_period_to_siop (sc, target);
}
}
dma_cachectl(&acb->stat[0], 1);
*status = acb->stat[0];
#ifdef DEBUG
if (rp->siop_sbcl & SIOP_BSY) {
/*printf ("ACK! siop was busy at end: rp %x script %x dsa %x\n",
rp, &scripts, &acb->ds);*/
#ifdef DDB
/*Debugger();*/
#endif
}
if (acb->msg[0] != 0x00)
printf("%s: message was not COMMAND COMPLETE: %x\n",
sc->sc_dev.dv_xname, acb->msg[0]);
#endif
if (sc->nexus_list.tqh_first)
rp->siop_dcntl |= SIOP_DCNTL_STD;
return 1;
}
if (sstat0 & SIOP_SSTAT0_M_A) { /* Phase mismatch */
#ifdef DEBUG
++siopphmm;
if (acb == NULL)
printf("%s: Phase mismatch with no active command?\n",
sc->sc_dev.dv_xname);
#endif
if (acb->iob_len) {
int adjust;
adjust = ((dfifo - (dbc & 0x7f)) & 0x7f);
if (sstat1 & SIOP_SSTAT1_ORF)
++adjust;
if (sstat1 & SIOP_SSTAT1_OLF)
++adjust;
acb->iob_curlen = *((long *)&rp->siop_dcmd) & 0xffffff;
acb->iob_curlen += adjust;
acb->iob_curbuf = *((long *)&rp->siop_dnad) - adjust;
#ifdef DEBUG
if (siop_debug & 0x100) {
int i;
printf ("Phase mismatch: curbuf %x curlen %x dfifo %x dbc %x sstat1 %x adjust %x sbcl %x starts %d acb %x\n",
acb->iob_curbuf, acb->iob_curlen, dfifo,
dbc, sstat1, adjust, rp->siop_sbcl, siopstarts, acb);
if (acb->ds.chain[1].datalen) {
for (i = 0; acb->ds.chain[i].datalen; ++i)
printf("chain[%d] addr %x len %x\n",
i, acb->ds.chain[i].databuf,
acb->ds.chain[i].datalen);
}
}
#endif
dma_cachectl (acb, sizeof(*acb));
}
#ifdef DEBUG
SIOP_TRACE('m',rp->siop_sbcl,(rp->siop_dsp>>8),rp->siop_dsp);
if (siop_debug & 9)
printf ("Phase mismatch: %x dsp +%x dcmd %x\n",
rp->siop_sbcl,
rp->siop_dsp - sc->sc_scriptspa,
*((long *)&rp->siop_dcmd));
#endif
if ((rp->siop_sbcl & SIOP_REQ) == 0) {
printf ("Phase mismatch: REQ not asserted! %02x dsp %x\n",
rp->siop_sbcl, rp->siop_dsp);
#ifdef DEBUG
Debugger();
#endif
}
switch (rp->siop_sbcl & 7) {
case 0: /* data out */
case 1: /* data in */
case 2: /* status */
case 3: /* command */
case 6: /* message in */
case 7: /* message out */
rp->siop_dsp = sc->sc_scriptspa + Ent_switch;
break;
default:
goto bad_phase;
}
return 0;
}
if (sstat0 & SIOP_SSTAT0_STO) { /* Select timed out */
#ifdef DEBUG
if (acb == NULL)
printf("%s: Select timeout with no active command?\n",
sc->sc_dev.dv_xname);
if (rp->siop_sbcl & SIOP_BSY) {
printf ("ACK! siop was busy at timeout: rp %x script %x dsa %x\n",
rp, &scripts, &acb->ds);
printf(" sbcl %x sdid %x istat %x dstat %x sstat0 %x\n",
rp->siop_sbcl, rp->siop_sdid, istat, dstat, sstat0);
if (!(rp->siop_sbcl & SIOP_BSY)) {
printf ("Yikes, it's not busy now!\n");
#if 0
*status = -1;
if (sc->nexus_list.tqh_first)
rp->siop_dsp = sc->sc_scriptspa + Ent_wait_reselect;
return 1;
#endif
}
/* rp->siop_dcntl |= SIOP_DCNTL_STD;*/
return (0);
#ifdef DDB
Debugger();
#endif
}
#endif
*status = -1;
acb->xs->error = XS_SELTIMEOUT;
if (sc->nexus_list.tqh_first)
rp->siop_dsp = sc->sc_scriptspa + Ent_wait_reselect;
return 1;
}
if (acb)
target = acb->xs->sc_link->target;
else
target = 7;
if (sstat0 & SIOP_SSTAT0_UDC) {
#ifdef DEBUG
if (acb == NULL)
printf("%s: Unexpected disconnect with no active command?\n",
sc->sc_dev.dv_xname);
printf ("%s: target %d disconnected unexpectedly\n",
sc->sc_dev.dv_xname, target);
#endif
#if 0
siopabort (sc, rp, "siopchkintr");
#endif
*status = STS_BUSY;
if (sc->nexus_list.tqh_first)
rp->siop_dsp = sc->sc_scriptspa + Ent_wait_reselect;
return 1;
}
if (dstat & SIOP_DSTAT_SIR && (rp->siop_dsps == 0xff01 ||
rp->siop_dsps == 0xff02)) {
#ifdef DEBUG
if (siop_debug & 0x100)
printf ("%s: ID %02x disconnected TEMP %x (+%x) curbuf %x curlen %x buf %x len %x dfifo %x dbc %x sstat1 %x starts %d acb %x\n",
sc->sc_dev.dv_xname, 1 << target, rp->siop_temp,
rp->siop_temp ? rp->siop_temp - sc->sc_scriptspa : 0,
acb->iob_curbuf, acb->iob_curlen,
acb->ds.chain[0].databuf, acb->ds.chain[0].datalen, dfifo, dbc, sstat1, siopstarts, acb);
#endif
if (acb == NULL) {
printf("%s: Disconnect with no active command?\n",
sc->sc_dev.dv_xname);
return (0);
}
/*
* XXXX need to update iob_curbuf/iob_curlen to reflect
* current data transferred. If device disconnected in
* the middle of a DMA block, they should already be set
* by the phase change interrupt. If the disconnect
* occurs on a DMA block boundary, we have to figure out
* which DMA block it was.
*/
if (acb->iob_len && rp->siop_temp) {
int n = rp->siop_temp - sc->sc_scriptspa;
if (acb->iob_curlen && acb->iob_curlen != acb->ds.chain[0].datalen)
printf("%s: iob_curbuf/len already set? n %x iob %x/%x chain[0] %x/%x\n",
sc->sc_dev.dv_xname, n, acb->iob_curbuf, acb->iob_curlen,
acb->ds.chain[0].databuf, acb->ds.chain[0].datalen);
if (n < Ent_datain)
n = (n - Ent_dataout) / 16;
else
n = (n - Ent_datain) / 16;
if (n <= 0 && n > DMAMAXIO)
printf("TEMP invalid %d\n", n);
else {
acb->iob_curbuf = (u_long)acb->ds.chain[n].databuf;
acb->iob_curlen = acb->ds.chain[n].datalen;
}
#ifdef DEBUG
if (siop_debug & 0x100) {
printf("%s: TEMP offset %d", sc->sc_dev.dv_xname, n);
printf(" curbuf %x curlen %x\n", acb->iob_curbuf,
acb->iob_curlen);
}
#endif
}
/*
* If data transfer was interrupted by disconnect, iob_curbuf
* and iob_curlen should reflect the point of interruption.
* Adjust the DMA chain so that the data transfer begins
* at the appropriate place upon reselection.
* XXX This should only be done on save data pointer message?
*/
if (acb->iob_curlen) {
int i, j;
#ifdef DEBUG
if (siop_debug & 0x100)
printf ("%s: adjusting DMA chain\n",
sc->sc_dev.dv_xname);
if (rp->siop_dsps == 0xff02)
printf ("%s: ID %02x disconnected without Save Data Pointers\n",
sc->sc_dev.dv_xname, 1 << target);
#endif
for (i = 0; i < DMAMAXIO; ++i) {
if (acb->ds.chain[i].datalen == 0)
break;
if (acb->iob_curbuf >= (long)acb->ds.chain[i].databuf &&
acb->iob_curbuf < (long)(acb->ds.chain[i].databuf +
acb->ds.chain[i].datalen))
break;
}
if (i >= DMAMAXIO || acb->ds.chain[i].datalen == 0)
printf("couldn't find saved data pointer\n");
#ifdef DEBUG
if (siop_debug & 0x100)
printf(" chain[0]: %x/%x -> %x/%x\n",
acb->ds.chain[0].databuf,
acb->ds.chain[0].datalen,
acb->iob_curbuf,
acb->iob_curlen);
#endif
acb->ds.chain[0].databuf = (char *)acb->iob_curbuf;
acb->ds.chain[0].datalen = acb->iob_curlen;
for (j = 1, ++i; i < DMAMAXIO && acb->ds.chain[i].datalen; ++i, ++j) {
#ifdef DEBUG
if (siop_debug & 0x100)
printf(" chain[%d]: %x/%x -> %x/%x\n", j,
acb->ds.chain[j].databuf,
acb->ds.chain[j].datalen,
acb->ds.chain[i].databuf,
acb->ds.chain[i].datalen);
#endif
acb->ds.chain[j].databuf = acb->ds.chain[i].databuf;
acb->ds.chain[j].datalen = acb->ds.chain[i].datalen;
}
if (j < DMAMAXIO)
acb->ds.chain[j].datalen = 0;
DCIAS(kvtop(&acb->ds.chain));
}
++sc->sc_tinfo[target].dconns;
/*
* add nexus to waiting list
* clear nexus
* try to start another command for another target/lun
*/
acb->status = sc->sc_flags & SIOP_INTSOFF;
TAILQ_INSERT_HEAD(&sc->nexus_list, acb, chain);
sc->sc_nexus = NULL; /* no current device */
/* start script to wait for reselect */
if (sc->sc_nexus == NULL)
rp->siop_dsp = sc->sc_scriptspa + Ent_wait_reselect;
/* XXXX start another command ? */
if (sc->ready_list.tqh_first)
siop_sched(sc);
return (0);
}
if (dstat & SIOP_DSTAT_SIR && rp->siop_dsps == 0xff03) {
int reselid = rp->siop_scratch & 0x7f;
int reselun = rp->siop_sfbr & 0x07;
sc->sc_sstat1 = rp->siop_sbcl; /* XXXX save current SBCL */
#ifdef DEBUG
if (siop_debug & 0x100)
printf ("%s: target ID %02x reselected dsps %x\n",
sc->sc_dev.dv_xname, reselid,
rp->siop_dsps);
if ((rp->siop_sfbr & 0x80) == 0)
printf("%s: Reselect message in was not identify: %x\n",
sc->sc_dev.dv_xname, rp->siop_sfbr);
#endif
if (sc->sc_nexus) {
#ifdef DEBUG
if (siop_debug & 0x100)
printf ("%s: reselect ID %02x w/active\n",
sc->sc_dev.dv_xname, reselid);
#endif
TAILQ_INSERT_HEAD(&sc->ready_list, sc->sc_nexus, chain);
sc->sc_tinfo[sc->sc_nexus->xs->sc_link->target].lubusy
&= ~(1 << sc->sc_nexus->xs->sc_link->lun);
--sc->sc_active;
}
/*
* locate acb of reselecting device
* set sc->sc_nexus to acb
*/
for (acb = sc->nexus_list.tqh_first; acb;
acb = acb->chain.tqe_next) {
if (reselid != (acb->ds.scsi_addr >> 16) ||
reselun != (acb->msgout[0] & 0x07))
continue;
TAILQ_REMOVE(&sc->nexus_list, acb, chain);
sc->sc_nexus = acb;
sc->sc_flags |= acb->status;
acb->status = 0;
DCIAS(kvtop(&acb->stat[0]));
rp->siop_dsa = kvtop(&acb->ds);
rp->siop_sxfer = sc->sc_sync[acb->xs->sc_link->target].sxfer;
rp->siop_sbcl = sc->sc_sync[acb->xs->sc_link->target].sbcl;
break;
}
if (acb == NULL) {
printf("%s: target ID %02x reselect nexus_list %x\n",
sc->sc_dev.dv_xname, reselid,
sc->nexus_list.tqh_first);
panic("unable to find reselecting device");
}
dma_cachectl (acb, sizeof(*acb));
rp->siop_temp = 0;
rp->siop_dcntl |= SIOP_DCNTL_STD;
return (0);
}
if (dstat & SIOP_DSTAT_SIR && rp->siop_dsps == 0xff04) {
u_short ctest2 = rp->siop_ctest2;
/* reselect was interrupted (by Sig_P or select) */
#ifdef DEBUG
if (siop_debug & 0x100 ||
(ctest2 & SIOP_CTEST2_SIGP) == 0)
printf ("%s: reselect interrupted (Sig_P?) scntl1 %x ctest2 %x sfbr %x istat %x/%x\n",
sc->sc_dev.dv_xname, rp->siop_scntl1,
ctest2, rp->siop_sfbr, istat, rp->siop_istat);
#endif
/* XXX assumes it was not select */
if (sc->sc_nexus == NULL) {
printf("%s: reselect interrupted, sc_nexus == NULL\n",
sc->sc_dev.dv_xname);
#if 0
siop_dump(sc);
#ifdef DDB
Debugger();
#endif
#endif
rp->siop_dcntl |= SIOP_DCNTL_STD;
return(0);
}
target = sc->sc_nexus->xs->sc_link->target;
rp->siop_temp = 0;
rp->siop_dsa = kvtop(&sc->sc_nexus->ds);
rp->siop_sxfer = sc->sc_sync[target].sxfer;
rp->siop_sbcl = sc->sc_sync[target].sbcl;
rp->siop_dsp = sc->sc_scriptspa;
return (0);
}
if (dstat & SIOP_DSTAT_SIR && rp->siop_dsps == 0xff06) {
if (acb == NULL)
printf("%s: Bad message-in with no active command?\n",
sc->sc_dev.dv_xname);
/* Unrecognized message in byte */
dma_cachectl (&acb->msg[1],1);
printf ("%s: Unrecognized message in data sfbr %x msg %x sbcl %x\n",
sc->sc_dev.dv_xname, rp->siop_sfbr, acb->msg[1], rp->siop_sbcl);
/* what should be done here? */
DCIAS(kvtop(&acb->msg[1]));
rp->siop_dsp = sc->sc_scriptspa + Ent_switch;
return (0);
}
if (dstat & SIOP_DSTAT_SIR && rp->siop_dsps == 0xff0a) {
/* Status phase wasn't followed by message in phase? */
printf ("%s: Status phase not followed by message in phase? sbcl %x sbdl %x\n",
sc->sc_dev.dv_xname, rp->siop_sbcl, rp->siop_sbdl);
if (rp->siop_sbcl == 0xa7) {
/* It is now, just continue the script? */
rp->siop_dcntl |= SIOP_DCNTL_STD;
return (0);
}
}
if (sstat0 == 0 && dstat & SIOP_DSTAT_SIR) {
dma_cachectl (&acb->stat[0], 1);
dma_cachectl (&acb->msg[0], 1);
printf ("SIOP interrupt: %x sts %x msg %x %x sbcl %x\n",
rp->siop_dsps, acb->stat[0], acb->msg[0], acb->msg[1],
rp->siop_sbcl);
siopreset (sc);
*status = -1;
return 0; /* siopreset has cleaned up */
}
if (sstat0 & SIOP_SSTAT0_SGE)
printf ("SIOP: SCSI Gross Error\n");
if (sstat0 & SIOP_SSTAT0_PAR)
printf ("SIOP: Parity Error\n");
if (dstat & SIOP_DSTAT_IID)
printf ("SIOP: Invalid instruction detected\n");
bad_phase:
/*
* temporary panic for unhandled conditions
* displays various things about the 53C710 status and registers
* then panics.
* XXXX need to clean this up to print out the info, reset, and continue
*/
printf ("siopchkintr: target %x ds %x\n", target, &acb->ds);
printf ("scripts %x ds %x rp %x dsp %x dcmd %x\n", sc->sc_scriptspa,
kvtop(&acb->ds), kvtop(rp), rp->siop_dsp,
*((long *)&rp->siop_dcmd));
printf ("siopchkintr: istat %x dstat %x sstat0 %x dsps %x dsa %x sbcl %x sts %x msg %x %x sfbr %x\n",
istat, dstat, sstat0, rp->siop_dsps, rp->siop_dsa,
rp->siop_sbcl, acb->stat[0], acb->msg[0], acb->msg[1], rp->siop_sfbr);
#ifdef DEBUG
if (siop_debug & 0x20)
panic("siopchkintr: **** temp ****");
#endif
#ifdef DDB
Debugger ();
#endif
siopreset (sc); /* hard reset */
*status = -1;
return 0; /* siopreset cleaned up */
}
void
siop_select(sc)
struct siop_softc *sc;
{
siop_regmap_p rp;
struct siop_acb *acb = sc->sc_nexus;
#ifdef DEBUG
if (siop_debug & 1)
printf ("%s: select ", sc->sc_dev.dv_xname);
#endif
rp = sc->sc_siopp;
if (acb->xs->flags & SCSI_POLL || siop_no_dma) {
sc->sc_flags |= SIOP_INTSOFF;
sc->sc_flags &= ~SIOP_INTDEFER;
if ((rp->siop_istat & 0x08) == 0) {
rp->siop_sien = 0;
rp->siop_dien = 0;
}
#if 0
} else if ((sc->sc_flags & SIOP_INTDEFER) == 0) {
sc->sc_flags &= ~SIOP_INTSOFF;
if ((rp->siop_istat & 0x08) == 0) {
rp->siop_sien = sc->sc_sien;
rp->siop_dien = sc->sc_dien;
}
#endif
}
#ifdef DEBUG
if (siop_debug & 1)
printf ("siop_select: target %x cmd %02x ds %x\n",
acb->xs->sc_link->target, acb->cmd.opcode,
&sc->sc_nexus->ds);
#endif
siop_start(sc, acb->xs->sc_link->target, acb->xs->sc_link->lun,
&acb->cmd, acb->clen, acb->daddr, acb->dleft);
return;
}
/*
* 53C710 interrupt handler
*/
int
siopintr (sc)
register struct siop_softc *sc;
{
siop_regmap_p rp;
register u_char istat, dstat, sstat0;
int status;
int s = splbio();
istat = sc->sc_istat;
if ((istat & (SIOP_ISTAT_SIP | SIOP_ISTAT_DIP)) == 0) {
splx(s);
return;
}
/* Got a valid interrupt on this device */
rp = sc->sc_siopp;
dstat = sc->sc_dstat;
sstat0 = sc->sc_sstat0;
if (dstat & SIOP_DSTAT_SIR)
sc->sc_intcode = rp->siop_dsps;
sc->sc_istat = 0;
#ifdef DEBUG
if (siop_debug & 1)
printf ("%s: intr istat %x dstat %x sstat0 %x\n",
sc->sc_dev.dv_xname, istat, dstat, sstat0);
if (!sc->sc_active) {
printf ("%s: spurious interrupt? istat %x dstat %x sstat0 %x status %x\n",
sc->sc_dev.dv_xname, istat, dstat, sstat0, sc->sc_nexus->stat[0]);
}
#endif
#ifdef DEBUG
if (siop_debug & 5) {
DCIAS(kvtop(&sc->sc_nexus->stat[0]));
printf ("%s: intr istat %x dstat %x sstat0 %x dsps %x sbcl %x sts %x msg %x\n",
sc->sc_dev.dv_xname, istat, dstat, sstat0,
rp->siop_dsps, rp->siop_sbcl,
sc->sc_nexus->stat[0], sc->sc_nexus->msg[0]);
}
#endif
if (sc->sc_flags & SIOP_INTDEFER) {
sc->sc_flags &= ~(SIOP_INTDEFER | SIOP_INTSOFF);
rp->siop_sien = sc->sc_sien;
rp->siop_dien = sc->sc_dien;
}
if (siop_checkintr (sc, istat, dstat, sstat0, &status)) {
#if 1
if (status == 0xff)
printf ("siopintr: status == 0xff\n");
#endif
if ((sc->sc_flags & (SIOP_INTSOFF | SIOP_INTDEFER)) != SIOP_INTSOFF) {
#if 0
if (rp->siop_sbcl & SIOP_BSY) {
printf ("%s: SCSI bus busy at completion",
sc->sc_dev.dv_xname);
printf(" targ %d sbcl %02x sfbr %x lcrc %02x dsp +%x\n",
sc->sc_nexus->xs->sc_link->target,
rp->siop_sbcl, rp->siop_sfbr, rp->siop_lcrc,
rp->siop_dsp - sc->sc_scriptspa);
}
#endif
siop_scsidone(sc->sc_nexus, sc->sc_nexus->stat[0]);
}
}
splx(s);
}
/*
* This is based on the Progressive Peripherals 33Mhz Zeus driver and will
* not be correct for other 53c710 boards.
*
*/
scsi_period_to_siop (sc, target)
struct siop_softc *sc;
{
int period, offset, i, sxfer, sbcl;
period = sc->sc_nexus->msg[4];
offset = sc->sc_nexus->msg[5];
#ifdef DEBUG
sxfer = 0;
if (offset <= SIOP_MAX_OFFSET)
sxfer = offset;
for (i = 0; i < sizeof (sync_tab) / 2; ++i) {
if (period <= sync_tab[i].p) {
sxfer |= sync_tab[i].r & 0x70;
sbcl = sync_tab[i].r & 0x03;
break;
}
}
printf ("siop sync old: siop_sxfr %02x, siop_sbcl %02x\n", sxfer, sbcl);
#endif
for (sbcl = 1; sbcl < 4; ++sbcl) {
sxfer = (period * 4 - 1) / sc->sc_tcp[sbcl] - 3;
if (sxfer >= 0 && sxfer <= 7)
break;
}
if (sbcl > 3) {
printf("siop sync: unable to compute sync params for period %dns\n",
period * 4);
/*
* XXX need to pick a value we can do and renegotiate
*/
sxfer = sbcl = 0;
} else {
sxfer = (sxfer << 4) | ((offset <= SIOP_MAX_OFFSET) ?
offset : SIOP_MAX_OFFSET);
printf("siop sync: params for period %dns: sxfer %x sbcl %x",
period * 4, sxfer, sbcl);
printf(" actual period %dns\n",
sc->sc_tcp[sbcl] * ((sxfer >> 4) + 4));
}
sc->sc_sync[target].sxfer = sxfer;
sc->sc_sync[target].sbcl = sbcl;
#ifdef DEBUG
printf ("siop sync: siop_sxfr %02x, siop_sbcl %02x\n", sxfer, sbcl);
#endif
}
#ifdef DEBUG
#if SIOP_TRACE_SIZE
void
siop_dump_trace()
{
int i;
printf("siop trace: next index %d\n", siop_trix);
i = siop_trix;
do {
printf("%3d: '%c' %02x %02x %02x\n", i, siop_trbuf[i],
siop_trbuf[i + 1], siop_trbuf[i + 2], siop_trbuf[i + 3]);
i = (i + 4) & (SIOP_TRACE_SIZE - 1);
} while (i != siop_trix);
}
#endif
void
siop_dump_acb(acb)
struct siop_acb *acb;
{
u_char *b = (u_char *) &acb->cmd;
int i;
printf("acb@%x ", acb);
if (acb->xs == NULL) {
printf("<unused>\n");
return;
}
printf("(%d:%d) flags %2x clen %2d cmd ", acb->xs->sc_link->target,
acb->xs->sc_link->lun, acb->flags, acb->clen);
for (i = acb->clen; i; --i)
printf(" %02x", *b++);
printf("\n");
printf(" xs: %08x data %8x:%04x ", acb->xs, acb->xs->data,
acb->xs->datalen);
printf("va %8x:%04x ", acb->iob_buf, acb->iob_len);
printf("cur %8x:%04x\n", acb->iob_curbuf, acb->iob_curlen);
}
void
siop_dump(sc)
struct siop_softc *sc;
{
struct siop_acb *acb;
siop_regmap_p rp = sc->sc_siopp;
int s;
int i;
s = splbio();
#if SIOP_TRACE_SIZE
siop_dump_trace();
#endif
printf("%s@%x regs %x istat %x\n",
sc->sc_dev.dv_xname, sc, rp, rp->siop_istat);
if (acb = sc->free_list.tqh_first) {
printf("Free list:\n");
while (acb) {
siop_dump_acb(acb);
acb = acb->chain.tqe_next;
}
}
if (acb = sc->ready_list.tqh_first) {
printf("Ready list:\n");
while (acb) {
siop_dump_acb(acb);
acb = acb->chain.tqe_next;
}
}
if (acb = sc->nexus_list.tqh_first) {
printf("Nexus list:\n");
while (acb) {
siop_dump_acb(acb);
acb = acb->chain.tqe_next;
}
}
if (sc->sc_nexus) {
printf("Nexus:\n");
siop_dump_acb(sc->sc_nexus);
}
for (i = 0; i < 8; ++i) {
if (sc->sc_tinfo[i].cmds > 2) {
printf("tgt %d: cmds %d disc %d senses %d lubusy %x\n",
i, sc->sc_tinfo[i].cmds,
sc->sc_tinfo[i].dconns,
sc->sc_tinfo[i].senses,
sc->sc_tinfo[i].lubusy);
}
}
splx(s);
}
#endif