/*
* 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. 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
*/
/*
* Copyright (c) 1994 Christian E. Hopps
*
* 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
*/
/* These are for bounce buffers */
#include <amiga/amiga/cc.h>
#include <amiga/dev/zbusvar.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 */
int sbicicmd(struct sbic_softc *, int, int, void *, int, void *, int);
int sbicgo(struct sbic_softc *, struct scsipi_xfer *);
int sbicdmaok(struct sbic_softc *, struct scsipi_xfer *);
int sbicwait(sbic_regmap_t, char, int , int);
int sbiccheckdmap(void *, u_long, u_long);
int sbicselectbus(struct sbic_softc *, sbic_regmap_t, u_char, u_char, u_char);
int sbicxfstart(sbic_regmap_t, int, u_char, int);
int sbicxfout(sbic_regmap_t regs, int, void *, int);
int sbicfromscsiperiod(struct sbic_softc *, sbic_regmap_t, int);
int sbictoscsiperiod(struct sbic_softc *, sbic_regmap_t, int);
int sbicpoll(struct sbic_softc *);
int sbicnextstate(struct sbic_softc *, u_char, u_char);
int sbicmsgin(struct sbic_softc *);
int sbicxfin(sbic_regmap_t regs, int, void *);
int sbicabort(struct sbic_softc *, sbic_regmap_t, const char *);
void sbicxfdone(struct sbic_softc *, sbic_regmap_t, int);
void sbicerror(struct sbic_softc *, sbic_regmap_t, u_char);
void sbicstart(struct sbic_softc *);
void sbicreset(struct sbic_softc *);
void sbic_scsidone(struct sbic_acb *, int);
void sbic_sched(struct sbic_softc *);
void sbic_save_ptrs(struct sbic_softc *, sbic_regmap_t,int,int);
void sbic_load_ptrs(struct sbic_softc *, sbic_regmap_t,int,int);
#ifdef DEBUG
void sbicdumpstate(void);
void sbic_dump_acb(struct sbic_acb *);
#endif
/*
* 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.
*/
u_char sbic_inhibit_sync[8];
int sbic_enable_reselect = 1;
int sbic_clock_override = 0;
int sbic_no_dma = 0;
int sbic_parallel_operations = 1;
#ifdef DEBUG
sbic_regmap_t 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 data_pointer_debug = 0;
u_char debug_asr, debug_csr, routine;
void sbictimeout(struct sbic_softc *dev);
/*
* default minphys routine for sbic based controllers
*/
void
sbic_minphys(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(struct sbic_softc *dev, sbic_regmap_t regs, int target, int lun)
{
int count, asr, s;
struct sbic_acb* acb;
SBIC_TRACE(dev);
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);
SBIC_TRACE(dev);
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);
#ifdef DEBUG
if (data_pointer_debug)
printf("DMA recalc:kv(%p,%x)pa(%p,%lx)\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 (%p,%x)%x\n",
target, dev->sc_cur->dc_addr, dev->sc_cur->dc_count,
dev->sc_dmacmd);
#endif
SBIC_TRACE(dev);
}
/*
* 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_scsipi_cmd().
*/
void
sbic_scsipi_request(struct scsipi_channel *chan, scsipi_adapter_req_t req,
void *arg)
{
struct scsipi_xfer *xs;
struct scsipi_periph *periph;
struct sbic_acb *acb;
struct sbic_softc *dev = device_private(chan->chan_adapter->adapt_dev);
int flags, s, stat;
if (xs->error == XS_NOERROR) {
if (stat == SCSI_CHECK || stat == SCSI_BUSY)
xs->error = XS_BUSY;
}
/*
* 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_xs = NULL;
dev->sc_tinfo[periph->periph_target].lubusy &=
~(1<<periph->periph_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[periph->periph_target].lubusy
&= ~(1<<periph->periph_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",
device_xname(dev->sc_dev));
#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[periph->periph_target].cmds++;
scsipi_done(xs);
if (dosched)
sbic_sched(dev);
SBIC_TRACE(dev);
}
int
sbicdmaok(struct sbic_softc *dev, struct scsipi_xfer *xs)
{
if (sbic_no_dma || !xs->datalen || 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);
/*
* we have a bounce buffer?
*/
else if (dev->sc_tinfo[xs->xs_periph->periph_target].bounce)
return(1);
/*
* try to get one
*/
else if ((dev->sc_tinfo[xs->xs_periph->periph_target].bounce
= (char *)alloc_z2mem(MAXPHYS))) {
if (isztwomem(dev->sc_tinfo[xs->xs_periph->periph_target].bounce))
printf("alloc ZII target %d bounce pa 0x%x\n",
xs->xs_periph->periph_target,
(unsigned)kvtop(dev->sc_tinfo[xs->xs_periph->periph_target].bounce));
else if (dev->sc_tinfo[xs->xs_periph->periph_target].bounce)
printf("alloc CHIP target %d bounce pa %p\n",
xs->xs_periph->periph_target,
PREP_DMA_MEM(dev->sc_tinfo[xs->xs_periph->periph_target].bounce));
return(1);
}
return(0);
}
int
sbicwait(sbic_regmap_t regs, char until, int timeo, int line)
{
u_char val;
int csr;
SBIC_TRACE((struct sbic_softc *)0);
if (timeo == 0)
timeo = 1000000; /* 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);
}
SBIC_TRACE((struct sbic_softc *)0);
return(val);
}
/* 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",
device_xname(dev->sc_dev), 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);
printf("%s: sbicabort - sending ABORT command\n", device_xname(dev->sc_dev));
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.. */
/*
* 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
SBIC_TRACE(dev);
return 1;
}
SET_SBIC_cmd(regs, SBIC_CMD_SEL_ATN);
/*
* wait for select (merged from separate function may need
* cleanup)
*/
WAIT_CIP(regs);
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
SBIC_TRACE(dev);
return 1;
}
GET_SBIC_csr (regs, csr);
CSR_TRACE('s',csr,asr,target);
QPRINTF(("%02x ", csr));
if (csr == SBIC_CSR_RSLT_NI || csr == SBIC_CSR_RSLT_IFY) {
#ifdef DEBUG
if (reselect_debug)
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);
SBIC_TRACE(dev);
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);
/*
* 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(sbic_regmap_t regs, int len, void *bp)
{
int wait;
u_char orig_csr, csr, asr;
u_char *buf;
#ifdef DEBUG
u_char *obp;
#endif
/* 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(struct sbic_softc *dev, int target, int lun, void *cbuf, int clen,
void *buf, int len)
{
sbic_regmap_t regs;
u_char phase, csr, asr;
int wait, i;
struct sbic_acb *acb;
#define CSR_LOG_BUF_SIZE 0
#if CSR_LOG_BUF_SIZE
int bufptr;
int csrbuf[CSR_LOG_BUF_SIZE];
bufptr=0;
#endif
/* 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.
*/
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);
CSR_TRACE('I',csr,asr,target);
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);
CSR_TRACE('I',csr,asr,0xff);
#if CSR_LOG_BUF_SIZE
if (reselect_debug>1)
for (i=0; i<bufptr; i++)
printf("CSR:%02x", csrbuf[i]);
#endif
/*
* 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(struct sbic_softc *dev, sbic_regmap_t regs, int target)
{
u_char phase, asr, csr;
int s;
SBIC_TRACE(dev);
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 {
asr = SBIC_WAIT (regs, SBIC_ASR_INT, 0);
__USE(asr);
GET_SBIC_csr (regs, csr);
CSR_TRACE('f',csr,asr,target);
QPRINTF(("%02x:", csr));
} while ((csr != SBIC_CSR_DISC) && (csr != SBIC_CSR_DISC_1)
&& (csr != SBIC_CSR_S_XFERRED));
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"); */
SBIC_TRACE(dev);
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;
/*
* push the data cache (I think this won't work (EH))
*/
#if defined(M68040) || defined(M68060)
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
#ifdef __powerpc__
dma_cachectl(addr, count);
#endif
/*
* enintr() also enables interrupts for the sbic
*/
#ifdef DEBUG
if (data_pointer_debug > 1)
printf("sbicgo dmago:%d(%p:%lx)\n",
dev->target,dev->sc_cur->dc_addr,dev->sc_tcnt);
#if 0
/*
* Hmm - this isn't right: asr and csr haven't been set yet.
*/
debug_asr = asr;
debug_csr = csr;
#endif
#endif
/*
* Lets cycle a while then let the interrupt handler take over
*/
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));
CSR_TRACE('g',csr,asr,i<<4);
SBIC_TRACE(dev);
if (i == SBIC_STATE_DONE && dev->sc_stat[0] == 0xff) printf("sbicgo: done & stat = 0xff\n");
if (i == SBIC_STATE_DONE && dev->sc_stat[0] != 0xff) {
/* if (i == SBIC_STATE_DONE && dev->sc_stat[0]) { */
/* Did we really finish that fast? */
return 1;
}
return 0;
}
int
sbicintr(struct sbic_softc *dev)
{
sbic_regmap_t regs;
u_char asr, csr;
int i;
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);
CSR_TRACE('p',csr,asr,i<<4);
SBIC_TRACE(dev);
return(1);
}
/*
* Handle a single msgin
*/
int
sbicmsgin(struct sbic_softc *dev)
{
sbic_regmap_t regs;
int recvlen;
u_char asr, csr, *tmpaddr;
regs = dev->sc_sbic;
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
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);
CSR_TRACE('x',csr,asr,*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",
device_xname(dev->sc_dev), 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);
CSR_TRACE('X',csr,asr,dev->target);
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;
}
i = 0;
SBIC_TRACE(dev);
regs = dev->sc_sbic;
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
*/
#if defined(M68040) || defined(M68060)
if (dev->sc_flags & SBICF_DCFLUSH) {
#if 0
printf("sbic: 68040/68060 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(%p:%lx)\n",
dev->target,dev->sc_cur->dc_addr,dev->sc_tcnt);
dev->sc_dmatimo = 0;
#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 %p->%x\n",
device_xname(dev->sc_dev), acb->sc_usrbufpa,
(unsigned)kvtop(acb->sc_dmausrbuf));
#ifdef DEBUG
if (data_pointer_debug)
printf("sbicgo:copying %lx bytes from target %d bounce %x\n",
acb->sc_dmausrlen,
dev->target,
(unsigned)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]);
SBIC_TRACE(dev);
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->xs_control & XS_CTL_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 {
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;
}
/*
* 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(%p:%lx)\n",
dev->target,dev->sc_cur->dc_addr,
dev->sc_tcnt);
dev->sc_dmatimo = 1;
#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:
SBIC_TRACE(dev);
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);
SBIC_TRACE(dev);
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;