Changes to support Performance enhancements in the sequencer.

Bring back the polling code (for dumps mostly), but protect it
with splbio() to make it safe for reprobes.
This commit is contained in:
Justin T. Gibbs 1996-01-29 03:17:39 +00:00
parent 4a1e5d799a
commit 4697fd511e
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=13693
2 changed files with 175 additions and 135 deletions

View File

@ -1,6 +1,6 @@
/*
* Generic driver for the aic7xxx based adaptec SCSI controllers
* Copyright (c) 1994, 1995 Justin T. Gibbs.
* Copyright (c) 1994, 1995, 1996 Justin T. Gibbs.
* All rights reserved.
*
* Product specific probe and attach routines can be found in:
@ -24,7 +24,7 @@
*
* commenced: Sun Sep 27 18:14:01 PDT 1992
*
* $Id: aic7xxx.c,v 1.53 1996/01/07 19:24:31 gibbs Exp $
* $Id: aic7xxx.c,v 1.54 1996/01/23 21:47:50 se Exp $
*/
/*
* TODO:
@ -135,11 +135,13 @@ static void ahc_add_waiting_scb __P((u_long iobase, struct scb *scb,
static void ahc_done __P((struct ahc_data *ahc, struct scb *scbp));
static void ahc_free_scb __P((struct ahc_data *ahc, struct scb *scb,
int flags));
static void ahc_getscb __P((u_long iobase, struct scb *scb));
static inline void ahc_getscb __P((struct ahc_data *ahc, struct scb *scb,
int iosize));
static struct scb *
ahc_get_scb __P((struct ahc_data *ahc, int flags));
static void ahc_loadseq __P((u_long iobase));
static int ahc_match_scb __P((struct scb *scb, int target, char channel));
static int ahc_poll __P((struct ahc_data *ahc, int wait));
#ifdef AHC_DEBUG
static void ahc_print_active_scb __P((struct ahc_data *ahc));
static void ahc_print_scb __P((struct scb *scb));
@ -153,7 +155,8 @@ static void ahc_reset_current_bus __P((u_long iobase));
static void ahc_scb_timeout __P((struct ahc_data *ahc, struct scb *scb));
static void ahc_scsirate __P((struct ahc_data* ahc, u_char *scsirate,
int period, int offset, int target));
static void ahc_send_scb __P((struct ahc_data *ahc, struct scb *scb));
static inline void ahc_send_scb __P((struct ahc_data *ahc, struct scb *scb,
int iosize));
static timeout_t
ahc_timeout;
static void ahc_busy_target __P((int target, char channel,
@ -434,26 +437,40 @@ ahc_attach(ahc)
return 1;
}
static void
ahc_send_scb( ahc, scb )
struct ahc_data *ahc;
struct scb *scb;
/* Send an SCB down to the card via PIO maintaining the SCB pointer */
static inline void
ahc_send_scb(ahc, scb, iosize)
struct ahc_data *ahc;
struct scb *scb;
int iosize; /* In bytes */
{
u_long iobase = ahc->baseport;
u_char cur_scb;
PAUSE_SEQUENCER(ahc);
outb(QINFIFO + iobase, scb->position);
UNPAUSE_SEQUENCER(ahc);
cur_scb = inb(SCBPTR + iobase);
outb(SCBPTR + iobase, scb->position);
outb(SCBCNT + iobase, SCBAUTO);
if( ahc->type == AHC_284 )
/* Can only do 8bit PIO */
outsb(SCBARRAY+iobase, scb, iosize);
else
outsl(SCBARRAY+iobase, scb, (iosize + 3) / 4);
outb(SCBCNT + iobase, 0);
outb(SCBPTR + iobase, cur_scb);
}
static
void ahc_getscb(iobase, scb)
u_long iobase;
struct scb *scb;
static inline void
ahc_getscb(ahc, scb, iosize)
struct ahc_data *ahc;
struct scb *scb;
int iosize;
{
u_long iobase = ahc->baseport;
outb(SCBCNT + iobase, 0x80); /* SCBAUTO */
insb(SCBARRAY + iobase, scb, SCB_PIO_TRANSFER_SIZE);
/* Can only do 8bit PIO for reads */
insb(SCBARRAY+iobase, scb, iosize);
outb(SCBCNT + iobase, 0);
}
@ -582,7 +599,7 @@ ahc_intr(arg)
printf("SAVED_TCL == 0x%x\n",
inb(SAVED_TCL + iobase));
ahc_unbusy_target(target, channel, iobase);
outb(SCBARRAY + iobase, NEEDDMA);
outb(SCBARRAY + iobase, 0);
outb(CLRSINT1 + iobase, CLRSELTIMEO);
RESTART_SEQUENCER(ahc);
break;
@ -820,7 +837,7 @@ ahc_intr(arg)
xs = scb->xs;
ahc_getscb(iobase, scb);
ahc_getscb(ahc, scb, SCB_PIO_TRANSFER_SIZE);
#ifdef AHC_DEBUG
if((ahc_debug & AHC_SHOWSCBS)
@ -875,10 +892,11 @@ ahc_intr(arg)
scb->data = sg->addr;
scb->datalen = sg->len;
outb(SCBCNT + iobase, 0x80);
outsb(SCBARRAY+iobase,scb,SCB_PIO_TRANSFER_SIZE);
outb(SCBCNT + iobase, 0);
outb(SCB_NEXT_WAITING+iobase,SCB_LIST_NULL);
ahc_send_scb(ahc, scb,
SCB_PIO_TRANSFER_SIZE);
outb(SCB_NEXT_WAITING+iobase,
SCB_LIST_NULL);
/*
* Ensure that the target is "BUSY"
* so we don't get overlapping
@ -1012,7 +1030,7 @@ ahc_intr(arg)
outb(MSG_LEN + iobase, 1);
}
else
panic("ahc_intr: AWAITING_MSG for an SCB that"
panic("ahc_intr: AWAITING_MSG for an SCB that "
"does not have a waiting message");
break;
}
@ -1063,6 +1081,68 @@ ahc_intr(arg)
case MSGIN_PHASEMIS:
break;
#endif
case PARITY_ERROR:
{
/*
* Determine the bus phase and
* queue an appropriate message
*/
char *phase;
u_char mesg_out = MSG_NOP;
u_char lastphase = inb(LASTPHASE + iobase);
int scb_index = inb(SCBPTR + iobase);
scb = ahc->scbarray[scb_index];
sc_print_addr(scb->xs->sc_link);
switch(lastphase) {
case P_DATAOUT:
phase = "Data-Out";
break;
case P_DATAIN:
phase = "Data-In";
mesg_out = MSG_INITIATOR_DET_ERROR;
break;
case P_COMMAND:
phase = "Command";
break;
case P_MESGOUT:
phase = "Message-Out";
break;
case P_STATUS:
phase = "Status";
mesg_out = MSG_INITIATOR_DET_ERROR;
break;
case P_MESGIN:
phase = "Message-In";
mesg_out = MSG_MSG_PARITY_ERROR;
break;
default:
phase = "unknown";
break;
}
printf("parity error during %s phase.\n", phase);
/*
* We've set the hardware to assert ATN if we
* get a parity error on "in" phases, so all we
* need to do is stuff the message buffer with
* the appropriate message. In phases have set
* mesg_out to something other than MSG_NOP.
*/
if(mesg_out != MSG_NOP) {
outb(MSG0 + iobase, mesg_out);
outb(MSG_LEN + iobase, 1);
}
else
/*
* Should we allow the target to make
* this decision for us?
*/
scb->xs->error = XS_DRIVER_STUFFUP;
break;
}
default:
printf("ahc: seqint, "
"intstat == 0x%x, scsisigi = 0x%x\n",
@ -1119,7 +1199,7 @@ clear:
? 'B' : 'A',
iobase);
outb(SCBARRAY + iobase, NEEDDMA);
outb(SCBARRAY + iobase, 0);
outb(CLRSINT1 + iobase, CLRSELTIMEO);
@ -1134,69 +1214,6 @@ clear:
RESTART_SEQUENCER(ahc);
}
else if (status & SCSIPERR) {
/*
* Determine the bus phase and
* queue an appropriate message
*/
char *phase;
u_char mesg_out = MSG_NOP;
u_char sigstate = inb(SIGSTATE + iobase);
sc_print_addr(xs->sc_link);
switch(sigstate) {
case P_DATAOUT:
phase = "Data-Out";
break;
case P_DATAIN:
phase = "Data-In";
mesg_out = MSG_INITIATOR_DET_ERROR;
break;
case P_COMMAND:
phase = "Command";
break;
case P_MESGOUT:
phase = "Message-Out";
break;
case P_STATUS:
phase = "Status";
mesg_out = MSG_INITIATOR_DET_ERROR;
break;
case P_MESGIN:
phase = "Message-In";
mesg_out = MSG_MSG_PARITY_ERROR;
break;
default:
phase = "unknown";
break;
}
printf("parity error during %s phase.\n", phase);
/*
* We've set the hardware to assert ATN if we
* get a parity error on "in" phases, so all we
* need to do is stuff the message buffer with
* the appropriate message. In phases have set
* mesg_out to something other than MSG_NOP.
*/
if(mesg_out != MSG_NOP) {
outb(MSG0 + iobase, mesg_out);
outb(MSG_LEN + iobase, 1);
}
else
/*
* Should we allow the target to make
* this decision for us?
*/
scb->xs->error = XS_DRIVER_STUFFUP;
outb(CLRSINT1 + iobase, CLRSCSIPERR);
UNPAUSE_SEQUENCER(ahc);
outb(CLRINT + iobase, CLRSCSIINT);
scb = NULL; /* Don't ahc_done the scb */
}
else if (!(status & BUSFREE)) {
sc_print_addr(xs->sc_link);
printf("Unknown SCSIINT. Status = 0x%x\n", status);
@ -1360,7 +1377,7 @@ ahc_init(ahc)
outb(SCSIID + iobase, ahc->our_id_b);
scsi_conf = inb(SCSICONF + 1 + iobase) & (ENSPCHK|STIMESEL);
outb(SXFRCTL1 + iobase, scsi_conf|ENSTIMER|ACTNEGEN|STPWEN);
outb(SIMODE1 + iobase, ENSELTIMO|ENSCSIPERR);
outb(SIMODE1 + iobase, ENSELTIMO);
if(ahc->type & AHC_ULTRA)
outb(SXFRCTL0 + iobase, DFON|SPIOEN|ULTRAEN);
else
@ -1377,7 +1394,7 @@ ahc_init(ahc)
outb(SCSIID + iobase, ahc->our_id);
scsi_conf = inb(SCSICONF + iobase) & (ENSPCHK|STIMESEL);
outb(SXFRCTL1 + iobase, scsi_conf|ENSTIMER|ACTNEGEN|STPWEN);
outb(SIMODE1 + iobase, ENSELTIMO|ENSCSIPERR);
outb(SIMODE1 + iobase, ENSELTIMO);
if(ahc->type & AHC_ULTRA)
outb(SXFRCTL0 + iobase, DFON|SPIOEN|ULTRAEN);
else
@ -1689,12 +1706,41 @@ ahc_scsi_cmd(xs)
if((ahc_debug & AHC_SHOWSCBS) && (xs->sc_link->target == DEBUGTARG))
ahc_print_scb(scb);
#endif
s = splbio();
ahc_send_scb(ahc, scb);
timeout(ahc_timeout, (caddr_t)scb, (xs->timeout * hz) / 1000);
splx(s);
SC_DEBUG(xs->sc_link, SDEV_DB3, ("cmd_sent\n"));
return (SUCCESSFULLY_QUEUED);
if (!(flags & SCSI_NOMASK)) {
s = splbio();
PAUSE_SEQUENCER(ahc);
ahc_send_scb(ahc, scb, SCB_PIO_TRANSFER_SIZE);
outb(QINFIFO + ahc->baseport, scb->position);
UNPAUSE_SEQUENCER(ahc);
timeout(ahc_timeout, (caddr_t)scb, (xs->timeout * hz) / 1000);
splx(s);
SC_DEBUG(xs->sc_link, SDEV_DB3, ("cmd_sent\n"));
return (SUCCESSFULLY_QUEUED);
}
/*
* If we can't use interrupts, poll for completion
*/
s = splbio();
PAUSE_SEQUENCER(ahc);
ahc_send_scb(ahc, scb, SCB_PIO_TRANSFER_SIZE);
outb(QINFIFO + ahc->baseport, scb->position);
UNPAUSE_SEQUENCER(ahc);
SC_DEBUG(xs->sc_link, SDEV_DB3, ("cmd_wait\n"));
do {
if (ahc_poll(ahc, xs->timeout)) {
if (!(xs->flags & SCSI_SILENT))
printf("cmd fail\n");
ahc_scb_timeout(ahc, scb);
splx(s);
return (HAD_ERROR);
}
} while (!(xs->flags & ITSDONE)); /* a non command complete intr */
if (xs->error) {
splx(s);
return (HAD_ERROR);
}
splx(s);
return (COMPLETE);
}
@ -1762,7 +1808,6 @@ ahc_get_scb(ahc, flags)
scbp = (struct scb *) malloc(sizeof(struct scb),
M_TEMP, M_NOWAIT);
if (scbp) {
physaddr scbaddr = KVTOPHYS(scbp);
u_long iobase = ahc->baseport;
u_char curscb;
bzero(scbp, sizeof(struct scb));
@ -1777,28 +1822,6 @@ ahc_get_scb(ahc, flags)
* load it into.
*/
ahc->scbarray[scbp->position] = scbp;
/*
* Initialize the host memory location
* of this SCB down on the board and
* flag that it should be DMA's before
* reference. Also set its psuedo
* next pointer (for use in the psuedo
* list of SCBs waiting for selection)
* to SCB_LIST_NULL.
*/
scbp->control = NEEDDMA;
scbp->host_scb = scbaddr;
scbp->next_waiting = SCB_LIST_NULL;
PAUSE_SEQUENCER(ahc);
curscb = inb(SCBPTR + iobase);
outb(SCBPTR + iobase, scbp->position);
outb(SCBCNT + iobase, 0x80);
outsb(SCBARRAY+iobase,scbp,SCB_HARDWARE_SIZE);
outb(SCBCNT + iobase, 0);
outb(SCBPTR + iobase, curscb);
UNPAUSE_SEQUENCER(ahc);
scbp->control = 0;
} else {
printf("ahc%d: Can't malloc SCB\n", ahc->unit);
}
@ -1850,6 +1873,30 @@ static void ahc_loadseq(iobase)
inb(SEQADDR1 + iobase != 0));
}
/*
* Function to poll for command completion when
* interrupts are disabled (crash dumps)
*/
static int
ahc_poll(ahc, wait)
struct ahc_data *ahc;
int wait; /* in msec */
{
u_long iobase = ahc->baseport;
u_long stport = INTSTAT + iobase;
while (--wait) {
DELAY(1000);
if (inb(stport) & INT_PEND)
break;
} if (wait == 0) {
printf("ahc%d: board not responding\n", ahc->unit);
return (EIO);
}
ahc_intr((void *)ahc);
return (0);
}
static void
ahc_scb_timeout(ahc, scb)
struct ahc_data *ahc;
@ -1932,8 +1979,8 @@ ahc_scb_timeout(ahc, scb)
UNPAUSE_SEQUENCER(ahc);
}
/* Is the active SCB really active? */
else if((active_scbp->flags & SCB_ACTIVE)
&& (control & NEEDDMA) == NEEDDMA) {
else if(active_scbp->flags & SCB_ACTIVE) {
/* XXX && (control & NEEDDMA) == NEEDDMA) { */
u_char msg_len = inb(MSG_LEN + iobase);
if(msg_len) {
/*
@ -2080,7 +2127,7 @@ ahc_reset_device(ahc, target, channel, timedout_scb, xs_error)
untimeout(ahc_timeout, (caddr_t)scbp);
ahc_done (ahc, scbp);
outb(SCBPTR + iobase, scbp->position);
outb(SCBARRAY + iobase, NEEDDMA);
outb(SCBARRAY + iobase, 0);
i--;
found++;
}
@ -2130,7 +2177,7 @@ ahc_reset_device(ahc, target, channel, timedout_scb, xs_error)
/* Ensure the target is "free" */
ahc_unbusy_target(target, channel, iobase);
outb(SCBPTR + iobase, scbp->position);
outb(SCBARRAY + iobase, NEEDDMA);
outb(SCBARRAY + iobase, 0);
scbp->flags |= SCB_ABORTED;
scbp->xs->error |= xs_error;
if(scbp->position != timedout_scb)
@ -2168,7 +2215,7 @@ ahc_abort_wscb (ahc, scbp, prev, iobase, timedout_scb, xs_error)
next = inb(SCB_NEXT_WAITING + iobase);
/* Clear the necessary fields */
outb(SCBARRAY + iobase, NEEDDMA);
outb(SCBARRAY + iobase, 0);
outb(SCB_NEXT_WAITING + iobase, SCB_LIST_NULL);
ahc_unbusy_target(target, channel, iobase);

View File

@ -3,7 +3,7 @@
* SCSI controllers. This is used to implement product specific
* probe and attach routines.
*
* Copyright (c) 1994, 1995 Justin T. Gibbs.
* Copyright (c) 1994, 1995, 1996 Justin T. Gibbs.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@ -20,7 +20,7 @@
* 4. Modifications may be freely made to this file if the above conditions
* are met.
*
* $Id: aic7xxx.h,v 1.18 1996/01/07 19:24:33 gibbs Exp $
* $Id: aic7xxx.h,v 1.19 1996/01/23 21:47:53 se Exp $
*/
#ifndef _AIC7XXX_H_
@ -108,19 +108,12 @@ struct scb {
*/
/*20*/ physaddr cmdpointer;
/*24*/ u_char cmdlen;
/*25*/ u_char RESERVED[2]; /* must be zero */
#define SCB_PIO_TRANSFER_SIZE 26 /*
* amount we need to upload/download
* via rep in/outsb to perform
* a request sense. The second
* RESERVED byte is initialized to
* 0 in get_scb().
#define SCB_PIO_TRANSFER_SIZE 25 /* amount we need to upload/download
* via PIO to initialize a transaction.
*/
/*27*/ u_char next_waiting; /* Used to thread SCBs awaiting
/*25*/ u_char next_waiting; /* Used to thread SCBs awaiting
* selection
*/
/*28*/ physaddr host_scb;
#define SCB_HARDWARE_SIZE 32
/*-----------------end of hardware supported fields----------------*/
struct scb *next; /* in free list */
struct scsi_xfer *xs; /* the scsi_xfer for this cmd */