patch from: Steve Woodford <steve@mctavish.demon.co.uk>

fixes the following problems:
   - Timeout on START/STOP unit command (ie. when spinning up the drive)
     Side effect of this fix is to reduce the busy-wait time in CMD phase.

   - Occasionally, the driver would lose an SBIC interrupt, especially when
     a tape drive was re-selecting on a busy SCSI bus.
This commit is contained in:
chuck 1996-04-23 16:32:54 +00:00
parent 7bc9a18250
commit edda199ab0

View File

@ -1,4 +1,4 @@
/* $NetBSD: sbic.c,v 1.1 1996/04/18 18:30:55 chuck Exp $ */
/* $NetBSD: sbic.c,v 1.2 1996/04/23 16:32:54 chuck Exp $ */
/*
* Changes Copyright (c) 1996 Steve Woodford
@ -805,7 +805,7 @@ sbicabort(dev, where)
dev->sc_dev.dv_xname, asr);
sbicreset(dev);
dev->sc_flags &= ~SBICF_SELECTED;
return -1;
return SBIC_STATE_ERROR;
}
printf("%s: sbicabort - sending DISC command\n", dev->sc_dev.dv_xname);
@ -825,7 +825,7 @@ sbicabort(dev, where)
dev->sc_flags &= ~SBICF_SELECTED;
}
return -1;
return SBIC_STATE_ERROR;
}
@ -1003,15 +1003,6 @@ sbicselectbus(dev)
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) ) {
@ -1093,7 +1084,7 @@ sbicselectbus(dev)
/*
* Enable (or not) reselection
* XXXSCW This is probably not necessary since we don't use use the
* Select-and-Xfer-with-ATN command to initialte a selection...
* Select-and-Xfer-with-ATN command to initiate a selection...
*/
if ( !sbic_enable_reselect && dev->nexus_list.tqh_first == NULL)
SET_SBIC_rselid (regs, 0);
@ -1171,11 +1162,43 @@ sbicselectbus(dev)
GET_SBIC_csr(regs, csr);
}
/*
* set sync or async
*/
if ( dev->sc_sync[target].state == SYNC_DONE ) {
#ifdef DEBUG
if ( sync_debug )
printf("select(%d): sync reg = 0x%02x\n", target,
SBIC_SYN(dev->sc_sync[target].offset,
dev->sc_sync[target].period));
#endif
SET_SBIC_syn(regs, SBIC_SYN(dev->sc_sync[target].offset,
dev->sc_sync[target].period));
} else {
#ifdef DEBUG
if ( sync_debug )
printf("select(%d): sync reg = 0x%02x\n", target,
SBIC_SYN(0,sbic_min_period));
#endif
SET_SBIC_syn(regs, SBIC_SYN(0, sbic_min_period));
}
return csr;
}
/*
* Information Transfer *to* a Scsi Target
* Information Transfer *to* a Scsi Target.
*
* Note: Don't expect there to be an interrupt immediately after all
* the data is transferred out. The WD spec sheet says that the Transfer-
* Info command for non-MSG_IN phases only completes when the target
* next asserts 'REQ'. That is, when the SCSI bus changes to a new state.
*
* This can have a nasty effect on commands which take a relatively long
* time to complete, for example a START/STOP unit command may remain in
* CMD phase until the disk has spun up. Only then will the target change
* to STATUS phase. This is really only a problem for immediate commands
* since we don't allow disconnection for them (yet).
*/
int
sbicxfout(regs, len, bp)
@ -1222,17 +1245,14 @@ sbicxfout(regs, len, bp)
wait = sbic_data_wait;
}
} while ( (asr & SBIC_ASR_INT) == 0 && wait-- > 0 );
SBIC_TC_GET(regs, len);
SBIC_TC_PUT(regs, 0);
} while ( len && (asr & SBIC_ASR_INT) == 0 && wait-- > 0 );
#ifdef DEBUG
QPRINTF(("sbicxfout done: %d bytes remaining (wait:%d)\n", len, wait));
#endif
/*
* this leaves with one csr to be read
* Normally, an interrupt will be pending when this routing returns.
*/
return(len);
}
@ -1301,6 +1321,10 @@ sbicxfin(regs, len, bp)
* 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.
*
* Note that although this routine looks like it can handle disconnect/
* reselect, the fact is that it can't. There is still some work to be
* done to clean this lot up.
*/
int
sbicicmd(dev, cbuf, clen, buf, len)
@ -1314,7 +1338,10 @@ sbicicmd(dev, cbuf, clen, buf, len)
struct sbic_acb *acb = dev->sc_nexus;
u_char csr,
asr;
int still_busy = 1;
int still_busy = SBIC_STATE_RUNNING;
#ifdef DEBUG
int counter = 0;
#endif
/*
* Make sure pointers are OK
@ -1351,106 +1378,113 @@ sbicicmd(dev, cbuf, clen, buf, len)
/*
* select the SCSI bus (it's an error if bus isn't free)
*/
if ( (dev->sc_flags & SBICF_SELECTED) == 0 ) {
if ( (dev->sc_flags & SBICF_SELECTED) == 0 &&
still_busy != SBIC_STATE_DISCONNECT ) {
if ( (csr = sbicselectbus(dev)) == 0 ) {
dev->sc_flags &= ~SBICF_ICMD;
return(-1);
}
} else
if ( (asr & (SBIC_ASR_BSY | SBIC_ASR_INT)) == SBIC_ASR_INT )
GET_SBIC_csr(regs, csr);
else
csr = 0;
QPRINTF((">ASR:0x%02x CSR:0x%02x <", asr, csr));
if ( csr ) {
switch ( csr ) {
QPRINTF((">ASR:0x%02x CSR:0x%02x< ", asr, csr));
case SBIC_CSR_S_XFERRED:
case SBIC_CSR_DISC:
case SBIC_CSR_DISC_1:
{
u_char phase;
switch ( csr ) {
dev->sc_flags &= ~SBICF_SELECTED;
GET_SBIC_cmd_phase (regs, phase);
case SBIC_CSR_S_XFERRED:
case SBIC_CSR_DISC:
case SBIC_CSR_DISC_1:
{
u_char phase;
if ( phase == 0x60 ) {
GET_SBIC_tlun (regs, dev->sc_stat[0]);
still_busy = 0; /* done */
} else {
dev->sc_flags &= ~SBICF_SELECTED;
GET_SBIC_cmd_phase (regs, phase);
if ( phase == 0x60 ) {
GET_SBIC_tlun (regs, dev->sc_stat[0]);
still_busy = SBIC_STATE_DONE; /* done */
} else {
#ifdef DEBUG
if ( reselect_debug > 1 )
printf("sbicicmd: handling disconnect\n");
if ( reselect_debug > 1 )
printf("sbicicmd: handling disconnect\n");
#endif
still_busy = SBIC_STATE_DISCONNECT;
still_busy = SBIC_STATE_DISCONNECT;
}
}
}
break;
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 ( sbicxfout(regs, clen, cbuf) )
still_busy = sbicabort(dev, "icmd sending cmd");
}
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 ( sbicxfout(regs, clen, cbuf) )
still_busy = sbicabort(dev, "icmd sending cmd");
}
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:
{
/*
* The sbic does the status/cmd-complete reading ok,
* so do this with its hi-level commands.
*/
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 (bsy=%d)\n", still_busy);
if ( sbic_debug )
printf("SBICICMD status phase (bsy=%d)\n", still_busy);
#endif
SET_SBIC_cmd_phase(regs, 0x46);
SET_SBIC_cmd(regs, SBIC_CMD_SEL_ATN_XFER);
}
break;
SET_SBIC_cmd_phase(regs, 0x46);
SET_SBIC_cmd(regs, SBIC_CMD_SEL_ATN_XFER);
}
break;
default:
{
still_busy = sbicnextstate(dev, csr, asr);
}
break;
}
/*
* 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 ) {
int i;
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);
default:
{
still_busy = sbicnextstate(dev, csr, asr);
}
break;
}
/*
* 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 ) {
int i;
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);
}
}
/*
@ -1460,13 +1494,13 @@ sbicicmd(dev, cbuf, clen, buf, len)
printf("sbicicmd: last command ignored\n");
}
else
if ( still_busy == 1 ) /* Bsy */
if ( still_busy >= SBIC_STATE_RUNNING ) /* Bsy */
SBIC_WAIT (regs, SBIC_ASR_INT, sbic_cmd_wait);
/*
* do it again
*/
} while ( still_busy > 0 && dev->sc_stat[0] == 0xff );
} while ( still_busy >= SBIC_STATE_RUNNING && dev->sc_stat[0] == 0xff );
/*
* Sometimes we need to do an extra read of the CSR
@ -1687,10 +1721,9 @@ sbicintr(dev)
if ( (asr & SBIC_ASR_INT) == 0 )
return(0);
do {
GET_SBIC_csr(regs, csr);
if ( asr & SBIC_ASR_INT )
GET_SBIC_csr(regs, csr);
do {
QPRINTF(("intr[0x%x]", csr));
@ -1698,7 +1731,15 @@ sbicintr(dev)
#if 0
WAIT_CIP(regs);
#endif
GET_SBIC_asr(regs, asr);
if ( i == SBIC_STATE_RUNNING ) {
GET_SBIC_asr(regs, asr);
if ( asr & SBIC_ASR_LCI )
printf("sbicgo: LCI asr:%02x csr:%02x\n", asr, csr);
if ( asr & SBIC_ASR_INT )
GET_SBIC_csr(regs, csr);
}
} while ( i == SBIC_STATE_RUNNING && asr & (SBIC_ASR_INT|SBIC_ASR_LCI) );
@ -1746,6 +1787,7 @@ sbicpoll(dev)
* tapes may take a loooong time..
*/
while ( asr & SBIC_ASR_BSY ) {
u_char z = 0;
if ( asr & SBIC_ASR_DBR ) {
printf("sbipoll: Waiting while sbic is jammed, CSR:%02x,ASR:%02x\n", csr,asr);
@ -1757,11 +1799,11 @@ sbicpoll(dev)
* DUNNO which direction
* Try old direction
*/
GET_SBIC_data(regs, i);
GET_SBIC_data(regs, z);
GET_SBIC_asr(regs, asr);
if ( asr & SBIC_ASR_DBR ) /* Wants us to write */
SET_SBIC_data(regs, i);
SET_SBIC_data(regs, z);
}
GET_SBIC_asr(regs, asr);
@ -1770,7 +1812,7 @@ sbicpoll(dev)
if ( asr & SBIC_ASR_LCI )
printf("sbicpoll: LCI asr:%02x csr:%02x\n", asr,csr);
else
if ( i == 1 ) /* BSY */
if ( i == SBIC_STATE_RUNNING ) /* BSY */
SBIC_WAIT(regs, SBIC_ASR_INT, sbic_cmd_wait);
} while ( i == SBIC_STATE_RUNNING );
@ -2038,6 +2080,12 @@ sbicmsgin(dev)
*/
SET_SBIC_syn(regs, SBIC_SYN(dev->sc_sync[dev->target].offset,
dev->sc_sync[dev->target].period));
#ifdef DEBUG
if ( sync_debug )
printf("msgin(%d): sync reg = 0x%02x\n", dev->target,
SBIC_SYN(dev->sc_sync[dev->target].offset,
dev->sc_sync[dev->target].period));
#endif
printf("%s: target %d now synchronous, period=%dns, offset=%d.\n",
dev->sc_dev.dv_xname, dev->target,
@ -2088,10 +2136,10 @@ sbicmsgin(dev)
/*
* sbicnextstate()
* return:
* 0 == done
* 1 == working
* 2 == disconnected
* -1 == error
* SBIC_STATE_DONE == done
* SBIC_STATE_RUNNING == working
* SBIC_STATE_DISCONNECT == disconnected
* SBIC_STATE_ERROR == error
*/
int
sbicnextstate(dev, csr, asr)
@ -2373,19 +2421,16 @@ sbicnextstate(dev, csr, asr)
GET_SBIC_asr(regs, asr);
if (asr & SBIC_ASR_INT)
break;
delay(1);
delay(10);
}
/*
* If we didn't get an interrupt, somethink's up
*/
if ( (asr & SBIC_ASR_INT) == 0 ) {
#ifdef DEBUG
if ( reselect_debug )
printf("RSLT_NI - no IFFY message? asr %x\n", asr);
#endif
printf("%s: Reselect without identify? asr %x\n",
dev->sc_dev.dv_xname, asr);
newlun = 0; /* XXXX */
} else {
/*
* We got an interrupt, verify that it's a change to
@ -2654,6 +2699,10 @@ sbictimeout(dev)
* We need to service a missed IRQ
*/
sbicintr(dev);
} else {
(void) sbicabort(dev, "timeout");
splx(s);
return;
}
}