summaryrefslogtreecommitdiffstats
path: root/sys/i386/scsi/aic7xxx.c
diff options
context:
space:
mode:
Diffstat (limited to 'sys/i386/scsi/aic7xxx.c')
-rw-r--r--sys/i386/scsi/aic7xxx.c144
1 files changed, 80 insertions, 64 deletions
diff --git a/sys/i386/scsi/aic7xxx.c b/sys/i386/scsi/aic7xxx.c
index cdcbf09..dc6bba04 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.16 1995/03/07 08:59:28 gibbs Exp $
+ * $Id: aic7xxx.c,v 1.17 1995/03/17 23:58:27 gibbs Exp $
*/
/*
* TODO:
@@ -333,9 +333,6 @@ struct scsi_device ahc_dev =
#define PAUSE 0x04
#define INTEN 0x02
#define CHIPRST 0x01
-#define REQ_PAUSE IRQMS | PAUSE | INTEN
-#define UNPAUSE_274X IRQMS | INTEN
-#define UNPAUSE_284X INTEN
/*
* SCB Pointer (p. 3-49)
@@ -357,11 +354,12 @@ struct scsi_device ahc_dev =
#define MSG_WDTR 0x50
#define MSG_REJECT 0x60
#define BAD_STATUS 0x70
+#define RESIDUAL 0x80
#define BRKADRINT 0x08
#define SCSIINT 0x04
#define CMDCMPLT 0x02
#define SEQINT 0x01
-#define INT_PEND (SEQINT | SCSIINT | CMDCMPLT) /* For polling */
+#define INT_PEND (BRKADRINT | SEQINT | SCSIINT | CMDCMPLT)
/*
* Hard Error (p. 3-53)
@@ -380,7 +378,7 @@ struct scsi_device ahc_dev =
*/
#define CLRINT 0xc92ul
#define CLRBRKADRINT 0x08
-#define CLRINTSTAT 0x04 /* UNDOCUMENTED - must be unpaused */
+#define CLRSCSIINT 0x04
#define CLRCMDINT 0x02
#define CLRSEQINT 0x01
@@ -467,6 +465,7 @@ struct scsi_device ahc_dev =
#define HA_SCBCOUNT 0xc52ul
#define HA_FLAGS 0xc53ul
+#define SINGLE_BUS 0x00
#define TWIN_BUS 0x01
#define WIDE_BUS 0x02
#define SENSE 0x10
@@ -493,7 +492,7 @@ struct scsi_device ahc_dev =
* XXX Should add a timeout in here??
*/
#define PAUSE_SEQUENCER(ahc) \
- outb(HCNTRL + ahc->baseport, REQ_PAUSE); \
+ outb(HCNTRL + ahc->baseport, ahc->pause); \
\
while ((inb(HCNTRL + ahc->baseport) & PAUSE) == 0) \
;
@@ -755,22 +754,14 @@ ahcintr(unit)
struct scb *scb = NULL;
struct scsi_xfer *xs = NULL;
- /*
- * Check that we are in the "running" state, and should be
- * receiving interrupts. The reason for doing this is that
- * we have a choice of edge or level sensitive interrupts,
- * and if we have the wrong type set, we'll get spurrious
- * interrupts. Toggle to the other type if need be.
- */
- if(ahc->flags != AHC_RUNNING){
- printf("ahc%d: Switching interrupt type\n", unit);
- ahc->unpause ^= 0x8;
- outb(HCNTRL + iobase, inb(HCNTRL + iobase) ^ 0x8);
- return 1;
- }
-
intstat = inb(INTSTAT + iobase);
+ /*
+ * Is this interrupt for me? or for
+ * someone who is sharing my interrupt
+ */
+ if (!(intstat & INT_PEND))
+ return 0;
if (intstat & BRKADRINT) {
/* We upset the sequencer :-( */
@@ -779,11 +770,10 @@ ahcintr(unit)
int num_errors = sizeof(hard_error)/sizeof(hard_error[0]);
for(i = 0; error != 1 && i < num_errors; i++)
error >>= 1;
- panic("ahc%d: brkadrint, %s at seqaddr = 0x%lx\n",
+ panic("ahc%d: brkadrint, %s at seqaddr = 0x%x\n",
unit, hard_error[i].errmesg,
(inb(SEQADDR1 + iobase) << 8) |
inb(SEQADDR0 + iobase));
-
}
if (intstat & SEQINT) {
unsigned char transfer;
@@ -997,8 +987,8 @@ ahcintr(unit)
*/
#ifdef AHC_DEBUG
if(ahc_debug & AHC_SHOWMISC)
- printf("Ignored message "
- "reject!!\n");
+ printf("Message reject -- "
+ "ignored\n");
#endif
break;
}
@@ -1036,9 +1026,6 @@ ahcintr(unit)
ahc_print_scb(scb);
#endif
xs->status = scb->target_status;
- xs->resid = ((scb->residual_data_count[2] << 16) |
- (scb->residual_data_count[1] << 8) |
- scb->residual_data_count[0]);
switch(scb->target_status){
case SCSI_OK:
printf("ahc%d: Interrupted for staus of"
@@ -1111,12 +1098,11 @@ ahcintr(unit)
case SCSI_BUSY:
xs->error = XS_BUSY;
printf("ahc%d: Target Busy\n", unit);
- break;
+ break;
case SCSI_QUEUE_FULL:
/*
- * Stick this command into the "waiting"
- * slot to be retarted on the next command
- * complete
+ * The upper level SCSI code will eventually
+ * handle this properly.
*/
printf("ahc%d: Queue Full\n", unit);
xs->error = XS_BUSY;
@@ -1129,6 +1115,22 @@ ahcintr(unit)
}
break;
}
+ case RESIDUAL:
+ {
+ int scb_index;
+ scb_index = inb(SCBPTR + iobase);
+ scb = ahc->scbarray[scb_index];
+ /*
+ * Don't clobber valid resid info with
+ * a resid coming from a check sense
+ * operation.
+ */
+ if(!(scb->flags & SCB_SENSE))
+ scb->xs->resid = (inb(iobase+SCBARRAY+17) << 16) |
+ (inb(iobase+SCBARRAY+16) << 8) |
+ inb(iobase+SCBARRAY+15);
+ break;
+ }
default:
printf("ahc: seqint, "
"intstat == 0x%x, scsisigi = 0x%x\n",
@@ -1163,7 +1165,7 @@ clear:
unit, status, scb_index);
outb(CLRSINT1 + iobase, status);
UNPAUSE_SEQUENCER(ahc);
- outb(CLRINT + iobase, CLRINTSTAT);
+ outb(CLRINT + iobase, CLRSCSIINT);
scb = NULL;
goto cmdcomplete;
}
@@ -1195,13 +1197,14 @@ clear:
RESTART_SEQUENCER(ahc);
- outb(CLRINT + iobase, CLRINTSTAT);
+ outb(CLRINT + iobase, CLRSCSIINT);
}
if (status & SCSIPERR) {
- printf("ahc%d: parity error on channel A "
+ printf("ahc%d: parity error on channel %c "
"target %d, lun %d\n",
unit,
+ ((u_long)xs->sc_link->fordriver & 0x08)? 'B':'A',
xs->sc_link->target,
xs->sc_link->lun);
xs->error = XS_DRIVER_STUFFUP;
@@ -1209,7 +1212,7 @@ clear:
outb(CLRSINT1 + iobase, CLRSCSIPERR);
UNPAUSE_SEQUENCER(ahc);
- outb(CLRINT + iobase, CLRINTSTAT);
+ outb(CLRINT + iobase, CLRSCSIINT);
scb = NULL;
}
if (status & BUSFREE) {
@@ -1228,7 +1231,7 @@ clear:
unit, status);
outb(CLRSINT1 + iobase, status);
UNPAUSE_SEQUENCER(ahc);
- outb(CLRINT + iobase, CLRINTSTAT);
+ outb(CLRINT + iobase, CLRSCSIINT);
scb = NULL;
}
if(scb != NULL) {
@@ -1327,7 +1330,7 @@ ahc_init(unit)
struct ahc_data *ahc = ahcdata[unit];
u_long iobase = ahc->baseport;
u_char scsi_conf, sblkctl;
- int intdef, i;
+ int intdef, i, max_targ = 16, wait;
/*
* Assume we have a board at this stage
@@ -1341,21 +1344,39 @@ ahc_init(unit)
#endif /* AHC_DEBUG */
printf("ahc%d: reading board settings\n", unit);
+ /* Save the IRQ type before we do a chip reset */
+
+ ahc->unpause = (inb(HCNTRL + iobase) & IRQMS) | INTEN;
+ ahc->pause = (inb(HCNTRL + iobase) & IRQMS) | INTEN | PAUSE;
outb(HCNTRL + iobase, CHIPRST);
+ /*
+ * Ensure that the reset has finished
+ */
+ wait = 1000;
+ while (wait--) {
+ DELAY(1000);
+ if(!(inb(HCNTRL + iobase) & CHIPRST))
+ break;
+ }
+ if(wait == 0) {
+ printf("ahc%d: Failed chip reset - probe failed!\n", unit);
+ return(1);
+ }
switch( ahc->type ) {
case AHC_274:
printf("ahc%d: 274x ", unit);
- ahc->unpause = UNPAUSE_274X;
ahc->maxscbs = 0x4;
break;
case AHC_284:
printf("ahc%d: 284x ", unit);
- ahc->unpause = UNPAUSE_284X;
ahc->maxscbs = 0x4;
break;
+ case AHC_AIC7870:
case AHC_294:
- printf("ahc%d: 294x ", unit);
- ahc->unpause = UNPAUSE_274X;
+ if( ahc->type == AHC_AIC7870)
+ printf("ahc%d: aic7870 ", unit);
+ else
+ printf("ahc%d: 294x ", unit);
ahc->maxscbs = 0x10;
#define DFTHRESH 3
outb(DSPCISTATUS + iobase, DFTHRESH << 6);
@@ -1372,6 +1393,7 @@ ahc_init(unit)
case 0:
ahc->our_id = (inb(HA_SCSICONF + iobase) & HSCSIID);
printf("Single Channel, SCSI Id=%d, ", ahc->our_id);
+ outb(HA_FLAGS + iobase, SINGLE_BUS);
break;
case 2:
ahc->our_id = (inb(HA_SCSICONF + 1 + iobase) & HWSCSIID);
@@ -1399,13 +1421,13 @@ ahc_init(unit)
* Number of SCBs that will be used. Rev E aic7770s and
* aic7870s have 16. The rest have 4.
*/
- if(!(ahc->type & AHC_294))
+ if(!(ahc->type & AHC_AIC7870))
{
/*
* See if we have a Rev E or higher
* aic7770. If so, use 16 SCBs.
* Anything below a Rev E will have a
- * R/O autoflush diable configuration bit.
+ * R/O autoflush disable configuration bit.
*/
u_char sblkctl_orig;
sblkctl_orig = inb(SBLKCTL + iobase);
@@ -1428,7 +1450,7 @@ ahc_init(unit)
else
printf("aic7870, ");
printf("%d SCBs\n", ahc->maxscbs);
- if(!(ahc->type & AHC_294)){
+ if(!(ahc->type & AHC_AIC7870)){
/*
* The 294x cards are PCI, so we get their interrupt from the PCI
* BIOS.
@@ -1490,12 +1512,15 @@ ahc_init(unit)
*/
ahc->needsdtr_orig = 0;
ahc->needwdtr_orig = 0;
- for(i = 0; i < 16; i++){
+ if(!(ahc->type & AHC_WIDE))
+ max_targ = 8;
+
+ for(i = 0; i < max_targ; i++){
u_char target_settings = inb(HA_TARG_SCRATCH + i + iobase);
if(target_settings & 0x0f){
ahc->needsdtr_orig |= (0x01 << i);
- /* Default to a syncronous offset of 15 */
- target_settings |= 0x0f;
+ /* Default to a asyncronous transfers (0 offset) */
+ target_settings &= 0xf0;
}
if(target_settings & 0x80){
ahc->needwdtr_orig |= (0x01 << i);
@@ -1547,7 +1572,7 @@ ahc_init(unit)
printf("Done\n");
outb(SEQCTL + iobase, FASTMODE);
- if (!(ahc->type & AHC_294))
+ if (!(ahc->type & AHC_AIC7870))
outb(BCTL + iobase, ENABLE);
/* Reset the bus */
@@ -1601,16 +1626,6 @@ ahc_scsi_cmd(xs)
struct ahc_data *ahc = ahcdata[unit];
int s;
- /*
- * Set a flag that states, yes, we can receive interrupts
- * the reason for doing this is that we have a choice of
- * edge or level sensitive interrupts, and if we have the
- * wrong type, we'll get spurrious interrupts. We check
- * this flag in the interrupt handler and toggle to the
- * other type if need be.
- */
- ahc->flags = AHC_RUNNING;
-
SC_DEBUG(xs->sc_link, SDEV_DB2, ("ahc_scsi_cmd\n"));
/*
* get an scb to use. If the transfer
@@ -1642,23 +1657,24 @@ ahc_scsi_cmd(xs)
* Put all the arguments for the xfer in the scb
*/
+ if(ahc->tagenable & mask)
+ scb->control |= SCB_TE;
if((ahc->needwdtr & mask) && !(ahc->wdtrpending & mask))
{
scb->control |= SCB_NEEDWDTR;
ahc->wdtrpending |= mask;
}
- else if((ahc->needsdtr & mask) && !(ahc->sdtrpending & mask))
+ if((ahc->needsdtr & mask) && !(ahc->sdtrpending & mask))
{
scb->control |= SCB_NEEDSDTR;
ahc->sdtrpending |= mask;
}
- else if(ahc->tagenable & mask)
- scb->control |= SCB_TE;
scb->target_channel_lun = ((xs->sc_link->target << 4) & 0xF0) |
((u_long)xs->sc_link->fordriver & 0x08) |
(xs->sc_link->lun & 0x07);
scb->cmdlen = xs->cmdlen;
scb->cmdpointer = KVTOPHYS(xs->cmd);
+ xs->resid = 0;
if (xs->datalen) { /* should use S/G only if not zero length */
scb->SG_list_pointer = KVTOPHYS(scb->ahc_dma);
sg = scb->ahc_dma;
@@ -1919,14 +1935,14 @@ ahc_poll(int unit, int wait)
u_long stport = INTSTAT + iobase;
while (--wait) {
- DELAY(10000);
+ DELAY(1000);
if (inb(stport) & INT_PEND)
break;
} if (wait == 0) {
printf("ahc%d: board not responding\n", unit);
return (EIO);
}
- ahcintr(unit);
+ ahcintr(unit);
return (0);
}
OpenPOWER on IntegriCloud