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:
parent
7bc9a18250
commit
edda199ab0
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user