summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authordg <dg@FreeBSD.org>1995-05-17 07:06:02 +0000
committerdg <dg@FreeBSD.org>1995-05-17 07:06:02 +0000
commitf879f636052f7f05419f0b359ca1e1d203d7962d (patch)
tree15d8079ad0390253228b99c72a1d1b94b3716938
parent037b95c21c474703d3d472125943753d6700da3c (diff)
downloadFreeBSD-src-f879f636052f7f05419f0b359ca1e1d203d7962d.zip
FreeBSD-src-f879f636052f7f05419f0b359ca1e1d203d7962d.tar.gz
Fixes to the aic7xxx sequencer code and device driver from Justin Gibbs:
1) If a target initiated a sync negotiation with us and happened to chose a value above 15, the old code inadvertantly truncated it with an "& 0x0f". If the periferal picked something really bad like 0x32, you'd end up with an offset of 2 which would hang the drive since it didn't expect to ever get something so low. We now do a MIN(maxoffset, given_offset). 2) In the case of Wide cards, we were turning on sync transfers after a sucessfull wide negotiation. Now we leave the offset alone in the per target scratch space (which implies asyncronous transfers since we initialize it that way) until a syncronous negotation occurs. 3) We were advertizing a max offset of 15 instead of 8 for wide devices. 4) If the upper level SCSI code sent down a "SCSI_RESET", it would hang the system because we would end up sending a null command to the sequencer. Now we handle SCSI_RESET correctly by having the sequencer interrupt us when it is about to fill the message buffer so that we can fill it in ourselves. The sequencer will also "simulate" a command complete for these "message only" SCBs so that the kernel driver can finish up properly. The cdplay utility will send a "SCSI_REST" to the cdplayer if you use the reset command. 5) The code that handles SCSIINTs was broken in that if more than one type of error was true at once, we'd do outbs without the card being paused. The else clause after the busfree case was also an accident waiting to happen. I've now turned this into an if, else if, else type of thing, since in most cases when we handle one type of error, it should be okay to ignore the rest (ie if we have a SELTO, who cares if there was a parity error on the transaction?), but the section should really be rewritten after 2.0.5. This fix was the least obtrusive way to patch the problem. 6) Only tag either SDTR or WDTR negotiation on an SCB. The real problem is that I don't account for the case when an SCB that is tagged to do a particular type of negotiation completes or SELTOs (selection timeout) without the negotiation taking place, so the accounting of sdtrpending and wdtrpending gets screwed up. In the wide case, if we tag it to do both wdtr and sdtr, it only performs wdtr (since wdtr must occur first and we spread out the negotiation over two commands) so we always have sdtrpending set for that target and we never do a real SDTR. I fill properly fix the accounting after 2.0.5 goes out the door, but this works (as confirmed by Dan) on wide targets. Other stuff that is also included: 1) Don't do a bzero when recycling SCBs. The only thing that must explicitly be set to zero is the scb control byte which is done in ahc_get_scb. We also need to set the SG_list_pointer and SG_list_count to 0 for commands that do not transfer data. 2) Mask the interrupt type printout for the aic7870 case. The bit we were using to determine interrupt type is only valid for the aic7770. Submitted by: Justin Gibbs
-rw-r--r--sys/dev/aic7xxx/aic7xxx.seq52
-rw-r--r--sys/i386/scsi/aic7xxx.c111
-rw-r--r--sys/i386/scsi/aic7xxx.h14
3 files changed, 117 insertions, 60 deletions
diff --git a/sys/dev/aic7xxx/aic7xxx.seq b/sys/dev/aic7xxx/aic7xxx.seq
index c5a1e9b..e8cc5bb 100644
--- a/sys/dev/aic7xxx/aic7xxx.seq
+++ b/sys/dev/aic7xxx/aic7xxx.seq
@@ -41,7 +41,7 @@
#
##-M#########################################################################
-VERSION AIC7XXX_SEQ_VER "$Id: aic7xxx.seq,v 1.14 1995/04/15 21:45:56 gibbs Exp $"
+VERSION AIC7XXX_SEQ_VER "$Id: aic7xxx.seq,v 1.15 1995/04/27 17:44:27 gibbs Exp $"
SCBMASK = 0x1f
@@ -155,6 +155,12 @@ MSG_REJECT = 0x61 # Reject message recieved
BAD_STATUS = 0x71 # Bad status from target
RESIDUAL = 0x81 # Residual byte count != 0
ABORT_TAG = 0x91 # Sent an ABORT_TAG message
+AWAITING_MSG = 0xa1 # Kernel requested to specify
+ # a message to this target
+ # (command was null), so tell
+ # it that it can fill the
+ # message buffer.
+
# The host adapter card (at least the BIOS) uses 20-2f for SCSI
# device information, 32-33 and 5a-5f as well. As it turns out, the
@@ -179,8 +185,8 @@ ABORT_TAG = 0x91 # Sent an ABORT_TAG message
# bank, so force a kernel panic if the target attempts a data in/out or
# command phase instead of corrupting something. FLAGS also contains
# configuration bits so that we can optimize for TWIN and WIDE controllers
-# as well as the MAX_SYNC bit which we set when we want to negotiate for
-# 10MHz irregardless of what the per target scratch space says.
+# as well as the MAX_OFFSET bit which we set when we want to negotiate for
+# maximum sync offset irregardless of what the per target scratch space says.
#
# Note that SG_NEXT occupies four bytes.
#
@@ -222,11 +228,14 @@ SCBCOUNT = 0x52 # the actual number of SCBs
FLAGS = 0x53 # Device configuration flags
TWIN_BUS = 0x01
WIDE_BUS = 0x02
-MAX_SYNC = 0x08
+MAX_OFFSET = 0x08
ACTIVE_MSG = 0x20
IDENTIFY_SEEN = 0x40
RESELECTED = 0x80
+MAX_OFFSET_8BIT = 0x0f
+MAX_OFFSET_WIDE = 0x08
+
ACTIVE_A = 0x54
ACTIVE_B = 0x55
SAVED_TCL = 0x56 # Temporary storage for the
@@ -369,6 +378,17 @@ start_selection:
# Messages are stored in scratch RAM starting with a flag byte (high bit
# set means active message), one length byte, and then the message itself.
#
+
+ test SCBARRAY+11,0xff jnz identify # 0 Length Command?
+
+# The kernel has sent us an SCB with no command attached. This implies
+# that the kernel wants to send a message of some sort to this target,
+# so we interrupt the driver, allow it to fill the message buffer, and
+# then go back into the arbitration loop
+ mvi INTSTAT,AWAITING_MSG
+ jmp poll_for_work
+
+identify:
mov SCBARRAY+1 call disconnect # disconnect ok?
and SINDEX,0x7,SCBARRAY+1 # lun
@@ -682,6 +702,7 @@ clear_a:
complete:
mov QOUTFIFO,SCBPTR
mvi INTSTAT,CMDCMPLT
+ test SCBARRAY+11,0xff jz start # Immediate message complete
jmp p_mesgin_done
# If we have a residual count, interrupt and tell the host. Other
@@ -851,6 +872,10 @@ p_mesgin_done:
p_busfree:
mvi CLRSINT1,0x40 # CLRATNO
clr SIGSTATE
+
+# if this is an immediate command, perform a psuedo command complete to
+# notify the driver.
+ test SCBARRAY+11,0xff jz status_ok
jmp start
# Instead of a generic bcopy routine that requires an argument, we unroll
@@ -1202,7 +1227,7 @@ ndx_dtr_2:
mk_dtr:
test SCBARRAY+0,0xc0 jz return # NEEDWDTR|NEEDSDTR
test SCBARRAY+0,NEEDWDTR jnz mk_wdtr_16bit
- or FLAGS, MAX_SYNC # Force an offset of 15
+ or FLAGS, MAX_OFFSET # Force an offset of 15 or 8 if WIDE
mk_sdtr:
mvi DINDIR,1 # extended message
@@ -1210,16 +1235,21 @@ mk_sdtr:
mvi DINDIR,1 # SDTR code
call sdtr_to_rate
mov DINDIR,RETURN_1 # REQ/ACK transfer period
- test FLAGS, MAX_SYNC jnz mk_sdtr_max_sync
- and DINDIR,0xf,SINDIR # Sync Offset
+ test FLAGS, MAX_OFFSET jnz mk_sdtr_max_offset
+ and DINDIR,0x0f,SINDIR # Sync Offset
mk_sdtr_done:
add MSG_LEN,-MSG_START+0,DINDEX ret # update message length
-mk_sdtr_max_sync:
-# We're initiating sync negotiation, so request the max offset we can (15)
- mvi DINDIR, 0x0f
- xor FLAGS, MAX_SYNC
+mk_sdtr_max_offset:
+# We're initiating sync negotiation, so request the max offset we can (15 or 8)
+ xor FLAGS, MAX_OFFSET
+ test SCSIRATE, 0x80 jnz wmax_offset # Talking to a WIDE device?
+ mvi DINDIR, MAX_OFFSET_8BIT
+ jmp mk_sdtr_done
+
+wmax_offset:
+ mvi DINDIR, MAX_OFFSET_WIDE
jmp mk_sdtr_done
mk_wdtr_16bit:
diff --git a/sys/i386/scsi/aic7xxx.c b/sys/i386/scsi/aic7xxx.c
index f896702..b4bbe96 100644
--- a/sys/i386/scsi/aic7xxx.c
+++ b/sys/i386/scsi/aic7xxx.c
@@ -24,7 +24,7 @@
*
* commenced: Sun Sep 27 18:14:01 PDT 1992
*
- * $Id: aic7xxx.c,v 1.25 1995/05/01 18:43:14 gibbs Exp $
+ * $Id: aic7xxx.c,v 1.26 1995/05/11 19:26:26 rgrimes Exp $
*/
/*
* TODO:
@@ -56,6 +56,8 @@
#include <sys/kernel.h>
#define KVTOPHYS(x) vtophys(x)
+#define MIN(a,b) ((a < b) ? a : b)
+
struct ahc_data *ahcdata[NAHC];
int ahc_init __P((int unit));
@@ -356,6 +358,7 @@ struct scsi_device ahc_dev =
#define BAD_STATUS 0x70
#define RESIDUAL 0x80
#define ABORT_TAG 0x90
+#define AWAITING_MSG 0xa0
#define BRKADRINT 0x08
#define SCSIINT 0x04
#define CMDCMPLT 0x02
@@ -443,6 +446,11 @@ struct scsi_device ahc_dev =
#define HA_REJBYTE 0xc31ul
/*
+ * Bit vector of targets that have disconnection disabled.
+ */
+#define HA_DISC_DSB 0xc32ul
+
+/*
* Length of pending message
*/
#define HA_MSG_LEN 0xc34ul
@@ -486,6 +494,7 @@ struct scsi_device ahc_dev =
#define HA_HOSTCONF 0xc5dul
#define MSG_ABORT 0x06
+#define MSG_BUS_DEVICE_RESET 0x0c
#define BUS_8_BIT 0x00
#define BUS_16_BIT 0x01
#define BUS_32_BIT 0x02
@@ -665,8 +674,8 @@ void ahc_scsirate(scsirate, period, offset, unit, target )
if ((ahc_syncrates[i].period - period) >= 0) {
*scsirate = (ahc_syncrates[i].sxfr) | (offset & 0x0f);
printf("ahc%d: target %d synchronous at %sMB/s, "
- "offset = 0x%x\n",
- unit, target, ahc_syncrates[i].rate, offset );
+ "offset = 0x%x\n", unit, target,
+ ahc_syncrates[i].rate, offset );
#ifdef AHC_DEBUG
#endif /* AHC_DEBUG */
return;
@@ -826,6 +835,7 @@ ahcintr(unit)
case MSG_SDTR:
{
u_char scsi_id, offset, rate, targ_scratch;
+ u_char maxoffset, mask;
/*
* Help the sequencer to translate the
* negotiated transfer rate. Transfer is
@@ -837,18 +847,23 @@ ahcintr(unit)
/* The bottom half of SCSIXFER */
offset = inb(ACCUM + iobase);
scsi_id = inb(SCSIID + iobase) >> 0x4;
- ahc_scsirate(&rate, transfer, offset, unit,
- scsi_id);
if(inb(SBLKCTL + iobase) & 0x08)
/* B channel */
scsi_id += 8;
+ mask = (0x01 << scsi_id);
targ_scratch = inb(HA_TARG_SCRATCH + iobase
+ scsi_id);
+ if(targ_scratch & 0x80)
+ maxoffset = 0x08;
+ else
+ maxoffset = 0x0f;
+ ahc_scsirate(&rate, transfer,
+ MIN(offset,maxoffset), unit, scsi_id);
/* Preserve the WideXfer flag */
rate |= targ_scratch & 0x80;
outb(HA_TARG_SCRATCH + iobase + scsi_id, rate);
outb(SCSIRATE + iobase, rate);
- if( (rate & 0x7f) == 0 )
+ if( (rate & 0x0f) == 0 )
{
/*
* The requested rate was so low
@@ -862,7 +877,7 @@ ahcintr(unit)
outb(HA_RETURN_1 + iobase, SEND_REJ);
}
/* See if we initiated Sync Negotiation */
- else if(ahc->sdtrpending & (0x01 << scsi_id))
+ else if(ahc->sdtrpending & mask)
{
/*
* Don't send an SDTR back to
@@ -883,8 +898,8 @@ ahcintr(unit)
/*
* Negate the flags
*/
- ahc->needsdtr &= ~(0x01 << scsi_id);
- ahc->sdtrpending &= ~(0x01 << scsi_id);
+ ahc->needsdtr &= ~mask;
+ ahc->sdtrpending &= ~mask;
break;
}
case MSG_WDTR:
@@ -918,8 +933,7 @@ ahcintr(unit)
"%d using 16Bit "
"transfers\n",
unit, scsi_id);
- scratch |= 0x88;
- scratch &= 0xf8;
+ scratch |= 0x80;
break;
}
}
@@ -941,8 +955,7 @@ ahcintr(unit)
"%d using 16Bit "
"transfers\n",
unit, scsi_id);
- scratch |= 0x88;
- scratch &= 0xf8;
+ scratch |= 0x80;
break;
}
outb(HA_RETURN_1 + iobase,
@@ -1201,6 +1214,28 @@ ahcintr(unit)
ahc_done(unit, scb);
break;
}
+ case AWAITING_MSG:
+ {
+ int scb_index;
+ scb_index = inb(SCBPTR + iobase);
+ scb = ahc->scbarray[scb_index];
+ /*
+ * This SCB had a zero length command, informing
+ * the sequencer that we wanted to send a special
+ * message to this target. We only do this for
+ * BUS_DEVICE_RESET messages currently.
+ */
+ if(scb->flags & SCB_DEVICE_RESET)
+ {
+ outb(HA_MSG_START + iobase,
+ MSG_BUS_DEVICE_RESET);
+ outb(HA_MSG_LEN + iobase, 1);
+ }
+ else
+ panic("ahcintr: AWAITING_MSG for an SCB that"
+ "does not have a waiting message");
+ break;
+ }
default:
printf("ahc: seqint, "
"intstat == 0x%x, scsisigi = 0x%x\n",
@@ -1278,7 +1313,7 @@ clear:
RESTART_SEQUENCER(ahc);
}
- if (status & SCSIPERR) {
+ else if (status & SCSIPERR) {
printf("ahc%d: parity error on channel %c "
"target %d, lun %d\n",
unit,
@@ -1293,18 +1328,7 @@ clear:
outb(CLRINT + iobase, CLRSCSIINT);
scb = NULL;
}
- if (status & BUSFREE) {
-#if 0
- /*
- * Has seen busfree since selection, i.e.
- * a "spurious" selection. Shouldn't happen.
- */
- printf("ahc: unexpected busfree\n");
- xs->error = XS_DRIVER_STUFFUP;
- outb(CLRSINT1 + iobase, BUSFREE); /* CLRBUSFREE */
-#endif
- }
- else {
+ else if (!(status & BUSFREE)) {
printf("ahc%d: Unknown SCSIINT. Status = 0x%x\n",
unit, status);
outb(CLRSINT1 + iobase, status);
@@ -1535,11 +1559,14 @@ ahc_init(unit)
printf("aic7870, ");
printf("%d SCBs\n", ahc->maxscbs);
- if(ahc->pause & IRQMS)
- printf("ahc%d: Using Level Sensitive Interrupts\n", unit);
- else
- printf("ahc%d: Using Edge Triggered Interrupts\n", unit);
-
+ if(!(ahc->type & AHC_AIC7870)) {
+ if(ahc->pause & IRQMS)
+ printf("ahc%d: Using Level Sensitive Interrupts\n",
+ unit);
+ else
+ printf("ahc%d: Using Edge Triggered Interrupts\n",
+ unit);
+ }
if(!(ahc->type & AHC_AIC7870)){
/*
* The 294x cards are PCI, so we get their interrupt from the PCI
@@ -1749,10 +1776,8 @@ ahc_scsi_cmd(xs)
}
SC_DEBUG(xs->sc_link, SDEV_DB3, ("start scb(%p)\n", scb));
scb->xs = xs;
- if (flags & SCSI_RESET) {
- /* XXX: Needs Implementation */
- printf("ahc0: SCSI_RESET called.\n");
- }
+ if (flags & SCSI_RESET)
+ scb->flags |= SCB_DEVICE_RESET|SCB_IMMED;
/*
* Put all the arguments for the xfer in the scb
*/
@@ -1764,7 +1789,7 @@ ahc_scsi_cmd(xs)
scb->control |= SCB_NEEDWDTR;
ahc->wdtrpending |= mask;
}
- if((ahc->needsdtr & mask) && !(ahc->sdtrpending & mask))
+ else if((ahc->needsdtr & mask) && !(ahc->sdtrpending & mask))
{
scb->control |= SCB_NEEDSDTR;
ahc->sdtrpending |= mask;
@@ -1842,10 +1867,13 @@ ahc_scsi_cmd(xs)
return (HAD_ERROR);
}
}
- /* else No data xfer, use non S/G values
- * the SG_segment_count and SG_list_pointer are pre-zeroed, so
- * we don't have to do anything
- */
+ else {
+ /*
+ * No data xfer, use non S/G values
+ */
+ scb->SG_segment_count = 0;
+ scb->SG_list_pointer = 0;
+ }
/*
* Usually return SUCCESSFULLY QUEUED
@@ -2001,7 +2029,7 @@ ahc_get_scb(unit, flags)
if (scbp) {
/* Get SCB from from free list */
ahc->free_scb = scbp->next;
- bzero(scbp, SCB_BZERO_SIZE);
+ scbp->control = 0;
scbp->flags = SCB_ACTIVE;
#ifdef AHC_DEBUG
ahc->activescbs++;
@@ -2197,7 +2225,6 @@ ahc_timeout(void *arg1)
*/
if (scb->flags & SCB_IMMED) {
scb->xs->retries = 0; /* I MEAN IT ! */
- scb->flags |= SCB_IMMED_FAIL;
ahc_done(unit, scb);
splx(s);
return;
diff --git a/sys/i386/scsi/aic7xxx.h b/sys/i386/scsi/aic7xxx.h
index 8108803..7fae490 100644
--- a/sys/i386/scsi/aic7xxx.h
+++ b/sys/i386/scsi/aic7xxx.h
@@ -20,7 +20,7 @@
* 4. Modifications may be freely made to this file if the above conditions
* are met.
*
- * $Id: aic7xxx.h,v 1.6 1995/04/23 22:04:58 gibbs Exp $
+ * $Id: aic7xxx.h,v 1.7 1995/04/27 17:47:17 gibbs Exp $
*/
#ifndef _AIC7XXX_H_
@@ -112,12 +112,12 @@ struct scb {
struct scb *next; /* in free list */
struct scsi_xfer *xs; /* the scsi_xfer for this cmd */
int flags;
-#define SCB_FREE 0x00
-#define SCB_ACTIVE 0x01
-#define SCB_ABORTED 0x02
-#define SCB_IMMED 0x04
-#define SCB_IMMED_FAIL 0x08
-#define SCB_SENSE 0x10
+#define SCB_FREE 0x00
+#define SCB_ACTIVE 0x01
+#define SCB_ABORTED 0x02
+#define SCB_DEVICE_RESET 0x04
+#define SCB_IMMED 0x08
+#define SCB_SENSE 0x10
int position; /* Position in scbarray */
struct ahc_dma_seg ahc_dma[AHC_NSEG] __attribute__ ((packed));
struct scsi_sense sense_cmd; /* SCSI command block */
OpenPOWER on IntegriCloud