summaryrefslogtreecommitdiffstats
path: root/sys/i386/scsi
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 /sys/i386/scsi
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
Diffstat (limited to 'sys/i386/scsi')
-rw-r--r--sys/i386/scsi/aic7xxx.c111
-rw-r--r--sys/i386/scsi/aic7xxx.h14
2 files changed, 76 insertions, 49 deletions
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