diff options
author | gibbs <gibbs@FreeBSD.org> | 1995-02-22 01:43:25 +0000 |
---|---|---|
committer | gibbs <gibbs@FreeBSD.org> | 1995-02-22 01:43:25 +0000 |
commit | feab573385c4fb00912256d73a6fefa7a2781bfb (patch) | |
tree | fc9c3e7f583417b2e131187e78d0ddd8f440e05e /sys/i386/scsi | |
parent | 6a52ec2d2cad2be5b8476d21e7decf9e6b1c927e (diff) | |
download | FreeBSD-src-feab573385c4fb00912256d73a6fefa7a2781bfb.zip FreeBSD-src-feab573385c4fb00912256d73a6fefa7a2781bfb.tar.gz |
Add tagged queueing support. Right now, we only allow two tagged
commands per target. I could have followed the route of the ncr driver
and gone to great lengths to get the SCSI subsystem to support more, but
I think I'll use the time saved to help Julian and Peter make tagged
queuing a better handled generic feature. This also includes some comment
and enum clean up and a possible fix for the hanging PCI controllers.
Diffstat (limited to 'sys/i386/scsi')
-rw-r--r-- | sys/i386/scsi/aic7xxx.c | 229 | ||||
-rw-r--r-- | sys/i386/scsi/aic7xxx.h | 25 |
2 files changed, 169 insertions, 85 deletions
diff --git a/sys/i386/scsi/aic7xxx.c b/sys/i386/scsi/aic7xxx.c index 1817f48..444fb00 100644 --- a/sys/i386/scsi/aic7xxx.c +++ b/sys/i386/scsi/aic7xxx.c @@ -5,7 +5,7 @@ * * Product specific probe and attach routines can be found in: * i386/isa/aic7770.c 27/284X and aic7770 motherboard controllers - * i386/pci/aic7870.c 294x and aic7870 motherboard controllers + * /pci/aic7870.c 294x and aic7870 motherboard controllers * * Portions of this driver are based on the FreeBSD 1742 Driver: * @@ -24,13 +24,12 @@ * * commenced: Sun Sep 27 18:14:01 PDT 1992 * - * $Id: aic7xxx.c,v 1.13 1995/01/27 17:37:05 gibbs Exp $ + * $Id: aic7xxx.c,v 1.14 1995/02/03 17:15:11 gibbs Exp $ */ /* * TODO: * Add target reset capabilities * Implement Target Mode - * Implement Tagged Queuing * * This driver is very stable, and seems to offer performance * comprable to the 1742 FreeBSD driver. I have not experienced @@ -52,6 +51,8 @@ #define PAGESIZ 4096 +#define MAX_TAGS 4; + #include <sys/kernel.h> #define KVTOPHYS(x) vtophys(x) @@ -71,9 +72,12 @@ u_int32 ahc_adapter_info(); int ahc_unit = 0; -/* Different debugging levels - only one so-far */ +/* Different debugging levels */ #define AHC_SHOWMISC 0x0001 -int ahc_debug = AHC_SHOWMISC; +#define AHC_SHOWCMDS 0x0002 +#define AHC_SHOWSCBS 0x0004 +/*#define AHC_DEBUG*/ +int ahc_debug = AHC_SHOWCMDS; /**** bit definitions for SCSIDEF ****/ #define HSCSIID 0x07 /* our SCSI ID */ @@ -441,27 +445,27 @@ struct scsi_device ahc_dev = /* * Length of pending message */ -#define HA_MSG_LEN 0xc36ul +#define HA_MSG_LEN 0xc34ul /* * message body */ -#define HA_MSG_START 0xc37ul /* outgoing message body */ +#define HA_MSG_START 0xc35ul /* outgoing message body */ /* * These are offsets into the card's scratch ram. Some of the values are * specified in the AHA2742 technical reference manual and are initialized * by the BIOS at boot time. */ -#define HA_ARG_1 0xc4cul -#define HA_RETURN_1 0xc4cul +#define HA_ARG_1 0xc4aul +#define HA_RETURN_1 0xc4aul #define SEND_WDTR 0x80 #define SEND_SDTR 0x80 -#define HA_SIGSTATE 0xc4dul +#define HA_SIGSTATE 0xc4bul -#define HA_SCBCOUNT 0xc56ul -#define HA_FLAGS 0xc57ul +#define HA_SCBCOUNT 0xc52ul +#define HA_FLAGS 0xc53ul #define TWIN_BUS 0x01 #define WIDE_BUS 0x02 #define SENSE 0x10 @@ -469,8 +473,9 @@ struct scsi_device ahc_dev = #define IDENTIFY_SEEN 0x40 #define RESELECTING 0x80 -#define HA_ACTIVE0 0xc58ul -#define HA_ACTIVE1 0xc59ul +#define HA_ACTIVE0 0xc54ul +#define HA_ACTIVE1 0xc55ul +#define SAVED_TCL 0xc56ul #define HA_SCSICONF 0xc5aul #define INTDEF 0xc5cul @@ -506,18 +511,18 @@ struct scsi_device ahc_dev = \ UNPAUSE_SEQUENCER(ahc); -#ifdef AHCDEBUG +#ifdef AHC_DEBUG void ahc_print_scb(scb) struct scb *scb; { - printf("scb:%x control:%x tcl:%x cmdlen:%d cmdpointer:%x\n" + printf("scb:0x%x control:0x%x tcl:0x%x cmdlen:%d cmdpointer:0x%x\n" ,scb ,scb->control ,scb->target_channel_lun ,scb->cmdlen ,scb->cmdpointer ); - printf(" datlen:%d data:%x res:%x segs:%x segp:%x\n" + printf(" datlen:%d data:0x%x res:0x%x segs:0x%x segp:0x%x\n" ,scb->datalen[2] << 16 | scb->datalen[1] << 8 | scb->datalen[0] ,scb->data ,scb->RESERVED[1] << 8 | scb->RESERVED[0] @@ -542,13 +547,13 @@ ahc_print_active_scb(ahc) ahc_print_scb(ahc->scbarray[cur_scb_offset]); } +#endif + #define PARERR 0x08 #define ILLOPCODE 0x04 #define ILLSADDR 0x02 #define ILLHADDR 0x01 -#endif - static struct { u_char errno; char *errmesg; @@ -658,8 +663,8 @@ void ahc_scsirate(scsirate, period, offset, unit, target ) printf("ahc%d: target %d synchronous at %sMB/s, " "offset = 0x%x\n", unit, target, ahc_syncrates[i].rate, offset ); -#ifdef AHCDEBUG -#endif /* AHCDEBUG */ +#ifdef AHC_DEBUG +#endif /* AHC_DEBUG */ return; } } @@ -667,8 +672,8 @@ void ahc_scsirate(scsirate, period, offset, unit, target ) *scsirate = 0; printf("ahc%d: target %d using asyncronous transfers\n", unit, target ); -#ifdef AHCDEBUG -#endif /* AHCDEBUG */ +#ifdef AHC_DEBUG +#endif /* AHC_DEBUG */ } @@ -699,8 +704,7 @@ ahc_attach(unit) printf("ahc%d: Probing channel A\n", unit); scsi_attachdevs(&(ahc->sc_link)); - if(ahc->type == AHC_274T || ahc->type == AHC_284T - || ahc->type == AHC_294T) { + if(ahc->type & AHC_TWIN) { /* Configure the second scsi bus */ ahc->sc_link_b = ahc->sc_link; ahc->sc_link_b.fordriver = (void *)0x0008; @@ -796,8 +800,12 @@ ahcintr(unit) break; case NO_IDENT: panic("ahc%d: No IDENTIFY message from reconnecting " - "target %d\n", - unit, (inb(SELID + iobase) >> 4) & 0xf); + "target %d at seqaddr = 0x%lx " + "SAVED_TCL == 0x%x\n", + unit, (inb(SELID + iobase) >> 4) & 0xf, + (inb(SEQADDR1 + iobase) << 8) | + inb(SEQADDR0 + iobase), + inb(SAVED_TCL + iobase)); break; case NO_MATCH: { @@ -807,7 +815,9 @@ ahcintr(unit) int target = (tcl >> 4) & 0x0f; printf("ahc%d: no active SCB for reconnecting " "target %d, channel %c - issuing ABORT\n", - unit, target, tcl & 0x07); + unit, target, tcl & 0x08 ? 'B' : 'A'); + printf("SAVED_TCL == 0x%x\n", + inb(SAVED_TCL + iobase)); if( tcl & 0x88 ) { /* Second channel stores its info * in byte two of HA_ACTIVE @@ -1012,7 +1022,7 @@ ahcintr(unit) ahc_getscb(iobase, scb); -#ifdef AHCDEBUG +#ifdef AHC_DEBUG if(xs->sc_link->target == DEBUGTARG) ahc_print_scb(scb); #endif @@ -1026,7 +1036,7 @@ ahcintr(unit) " 0???\n", unit); break; case SCSI_CHECK: -#ifdef AHCDEBUG +#ifdef AHC_DEBUG printf("ahc%d: target %d, lun %d (%s%d) " "requests Check Status\n", unit ,xs->sc_link->target @@ -1040,9 +1050,10 @@ ahcintr(unit) u_char flags; struct ahc_dma_seg *sg = scb->ahc_dma; struct scsi_sense *sc = &(scb->sense_cmd); + u_char control = scb->control; u_char tcl = scb->target_channel_lun; int i, active; -#ifdef AHCDEBUG +#ifdef AHC_DEBUG printf("ahc%d: target %d, lun %d " "(%s%d) Sending Sense\n", unit ,xs->sc_link->target @@ -1052,7 +1063,8 @@ ahcintr(unit) #endif bzero(scb, SCB_DOWN_SIZE); scb->flags |= SCB_SENSE; - scb->control = SCB_NEEDDMA; + scb->control = SCB_NEEDDMA | + (control & SCB_TE); sc->op_code = REQUEST_SENSE; sc->byte2 = xs->sc_link->lun << 5; sc->length = sizeof(struct scsi_sense_data); @@ -1093,6 +1105,15 @@ ahcintr(unit) xs->error = XS_BUSY; printf("ahc%d: Target Busy\n", unit); break; + case SCSI_QUEUE_FULL: + /* + * Stick this command into the "waiting" + * slot to be retarted on the next command + * complete + */ + printf("ahc%d: Queue Full\n", unit); + xs->error = XS_BUSY; + break; default: printf("unexpected targ_status: %x\n", scb->target_status); @@ -1256,10 +1277,33 @@ ahc_done(unit, scb) xs->error = XS_SENSE; if ((xs->flags & SCSI_ERR_OK) && !(xs->error == XS_SENSE)) { /* All went correctly OR errors expected */ - xs->error = 0; + xs->error = XS_NOERROR; } xs->flags |= ITSDONE; - + if(xs->cmd->opcode == 0x12 && xs->error == XS_NOERROR) + { + struct ahc_data *ahc = ahcdata[unit]; + struct scsi_inquiry_data *inq_data; + u_short mask = 0x01 << (xs->sc_link->target | + (scb->target_channel_lun & 0x08)); + /* + * Sneak a look at the results of the SCSI Inquiry + * command and see if we can do Tagged queing. This + * should really be done by the higher level drivers. + */ + inq_data = (struct scsi_inquiry_data *)xs->data; + if(((inq_data->device & SID_TYPE) == 0) + && (inq_data->flags & SID_CmdQue) + && !(ahc->tagenable & mask)) + { + /* + * Disk type device and can tag + */ + printf("ahc%d: target %d Tagged Queuing Device\n", + unit, xs->sc_link->target); + ahc->tagenable |= mask; + } + } ahc_free_scb(unit, scb, xs->flags); scsi_done(xs); } @@ -1281,14 +1325,15 @@ ahc_init(unit) * Find out the configured interupt and the card type. */ -#ifdef AHCDEBUG +#ifdef AHC_DEBUG printf("ahc%d: scb %d bytes; SCB_SIZE %d bytes, ahc_dma %d bytes\n", unit, sizeof(struct scb), SCB_DOWN_SIZE, sizeof(struct ahc_dma_seg)); -#endif /* AHCDEBUG */ +#endif /* AHC_DEBUG */ printf("ahc%d: reading board settings\n", unit); outb(HCNTRL + iobase, CHIPRST); + DELAY(100); switch( ahc->type ) { case AHC_274: printf("ahc%d: 274x ", unit); @@ -1301,16 +1346,21 @@ ahc_init(unit) ahc->maxscbs = 0x4; break; case AHC_294: - printf("ahc%d: 294x ", unit); - ahc->unpause = UNPAUSE_274X; - ahc->maxscbs = 0x10; - #define DFTHRESH 3 - outb(DSPCISTATUS + iobase, DFTHRESH << 6); - /* XXX Hard coded SCSI ID for now */ - outb(HA_SCSICONF + iobase, 0x07 | (DFTHRESH << 6)); - /* In case we are a wide card */ - outb(HA_SCSICONF + 1 + iobase, 0x07); - break; + { + #define DFTHRESH 0xc0 + u_char threshold; + printf("ahc%d: 294x ", unit); + ahc->unpause = UNPAUSE_274X; + ahc->maxscbs = 0x10; + threshold = inb(DSPCISTATUS + iobase); + threshold |= DFTHRESH ; + outb(DSPCISTATUS + iobase, threshold); + /* XXX Hard coded SCSI ID for now */ + outb(HA_SCSICONF + iobase, 0x07 | DFTHRESH); + /* In case we are a wide card */ + outb(HA_SCSICONF + 1 + iobase, 0x07); + break; + } default: }; @@ -1325,7 +1375,7 @@ ahc_init(unit) case 0xc2: ahc->our_id = (inb(HA_SCSICONF + 1 + iobase) & HWSCSIID); printf("Wide Channel, SCSI Id=%d, ", ahc->our_id); - ahc->type += 2; + ahc->type |= AHC_WIDE; outb(HA_FLAGS + iobase, WIDE_BUS); break; case 8: @@ -1334,7 +1384,7 @@ ahc_init(unit) ahc->our_id_b = (inb(HA_SCSICONF + 1 + iobase) & HSCSIID); printf("Twin Channel, A SCSI Id=%d, B SCSI Id=%d, ", ahc->our_id, ahc->our_id_b); - ahc->type += 1; + ahc->type |= AHC_TWIN; outb(HA_FLAGS + iobase, TWIN_BUS); break; default: @@ -1342,13 +1392,40 @@ ahc_init(unit) return(-1); } - /* Number of SCBs that will be used. Supposedly some newer rev - * aic7770s have more than four so maybe we can detect this in - * the future. Aic7870s have 16 SCBs. + /* + * Number of SCBs that will be used. Rev E aic7770s and + * aic7870s have 16. The rest have 4. */ + if(ahc->type & AHC_274 || ahc->type & AHC_284) + { + /* + * 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. + */ + u_char sblkctl, sblkctl_orig; + sblkctl_orig = inb(SBLKCTL + iobase); + sblkctl = sblkctl_orig ^ AUTOFLUSHDIS; + outb(SBLKCTL + iobase, sblkctl); + sblkctl = inb(SBLKCTL + iobase); + if(sblkctl != sblkctl_orig) + { + printf("aic7770 >= Rev E, "); + ahc->maxscbs = 0x10; + /* + * Ensure autoflush is enabled + */ + sblkctl &= ~AUTOFLUSHDIS; + outb(SBLKCTL + iobase, sblkctl); + } + else + printf("aic7770 <= Rev C, "); + } + else + printf("aic7870, "); printf("%d SCBs\n", ahc->maxscbs); - - if(ahc->type < AHC_294){ + if(!(ahc->type & AHC_294)){ /* The 294x cards are PCI, so we get their interrupt from the PCI * BIOS. It doesn't look like the ISA mapped interrupt is reported * correctly this way either. @@ -1390,12 +1467,11 @@ ahc_init(unit) ahc_loadseq(iobase); printf("Done\n"); outb(SEQCTL + iobase, FASTMODE); - if (ahc->type < AHC_294) + if (!(ahc->type & AHC_294)) outb(BCTL + iobase, ENABLE); /* Set the SCSI Id, SXFRCTL1, and SIMODE1, for both channes */ - if( ahc->type == AHC_274T || ahc->type == AHC_284T - || ahc->type == AHC_294T) + if( ahc->type & AHC_TWIN) { /* * The device is gated to channel B after a chip reset, @@ -1443,10 +1519,19 @@ ahc_init(unit) } outb(HA_TARG_SCRATCH+i+iobase,target_settings); } + /* + * If we are not a WIDE device, forget WDTR. This + * makes the driver work on some cards that don't + * leave these fields cleared when the BIOS is not + * installed. + */ + if(!(ahc->type & AHC_WIDE)) + ahc->needwdtr_orig = 0; ahc->needsdtr = ahc->needsdtr_orig; ahc->needwdtr = ahc->needwdtr_orig; ahc->sdtrpending = 0; ahc->wdtrpending = 0; + ahc->tagenable = 0; /* * Set the number of availible SCBs */ @@ -1548,17 +1633,19 @@ ahc_scsi_cmd(xs) /* * Put all the arguments for the xfer in the scb */ - - if((ahc->needsdtr & mask) && !(ahc->sdtrpending & mask)) + + if((ahc->needwdtr & mask) && !(ahc->wdtrpending & mask)) { - scb->control |= SCB_NEEDSDTR; - ahc->sdtrpending |= mask; + scb->control |= SCB_NEEDWDTR; + ahc->wdtrpending |= mask; } - if((ahc->needwdtr & mask) && !(ahc->wdtrpending & mask)) + else if((ahc->needsdtr & mask) && !(ahc->sdtrpending & mask)) { - scb->control |= SCB_NEEDWDTR; - ahc->wdtrpending |= 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; @@ -1638,7 +1725,7 @@ ahc_scsi_cmd(xs) /* * Usually return SUCCESSFULLY QUEUED */ -#ifdef AHCDEBUG +#ifdef AHC_DEBUG if(xs->sc_link->target == DEBUGTARG) ahc_print_scb(scb); #endif @@ -1906,8 +1993,7 @@ ahc_abort_scb( unit, ahc, scb ) ahc->sdtrpending &= 0x00ff; outb(HA_ACTIVE1, 0); } - else if (ahc->type == AHC_274W || ahc->type == AHC_284W - || ahc->type == AHC_294W){ + else if (ahc->type & AHC_WIDE){ ahc->needsdtr = ahc->needsdtr_orig; ahc->needwdtr = ahc->needwdtr_orig; ahc->sdtrpending = 0; @@ -1955,15 +2041,18 @@ ahc_timeout(void *arg1) unit = scb->xs->sc_link->adapter_unit; ahc = ahcdata[unit]; - printf("ahc%d: target %d, lun %d (%s%d) timed out ", unit + printf("ahc%d: target %d, lun %d (%s%d) timed out\n", unit ,scb->xs->sc_link->target ,scb->xs->sc_link->lun ,scb->xs->sc_link->device->name ,scb->xs->sc_link->dev_unit); -#ifdef AHCDEBUG - if (ahc_debug & AHC_SHOWMISC) +#ifdef AHC_DEBUG + if (ahc_debug & AHC_SHOWCMDS) { + show_scsi_cmd(ecb->xs); + } + if (ahc_debug & AHC_SHOWSCBS) ahc_print_active_scb(unit); -#endif /*AHCDEBUG */ +#endif /*AHC_DEBUG */ /* * If it's immediate, don't try abort it diff --git a/sys/i386/scsi/aic7xxx.h b/sys/i386/scsi/aic7xxx.h index ce08cc4..7a9b00f 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.2 1995/01/16 16:33:47 gibbs Exp $ + * $Id: aic7xxx.h,v 1.3 1995/02/03 17:15:12 gibbs Exp $ */ #ifndef _AIC7XXX_H_ @@ -45,20 +45,15 @@ struct ahc_dma_seg { long len; }; -typedef enum { - AHC_274, /* Single Channel */ - AHC_274T, /* Twin Channel */ - AHC_274W, /* Wide Channel */ - AHC_284, /* VL Single Channel */ - AHC_284T, /* VL Twin Channel */ - AHC_284W, /* VL Wide Channel - Do these exist?? */ - AHC_294, /* PCI Single Channel */ - AHC_294T, /* PCI Twin Channel */ - AHC_294W /* PCI Wide Channel */ -}ahc_type; +typedef u_char ahc_type; +#define AHC_WIDE 0x02 /* Wide Channel */ +#define AHC_TWIN 0x08 /* Twin Channel */ +#define AHC_274 0x10 /* EISA Based Controller */ +#define AHC_284 0x20 /* VL/ISA Based Controller */ +#define AHC_294 0x40 /* PCI Based Controller */ /* - * The driver keeps up to four scb structures per card in memory. Only the + * The driver keeps up to MAX_SCB scb structures per card in memory. Only the * first 26 bytes of the structure are valid for the hardware, the rest used * for driver level bookeeping. The "__attribute ((packed))" tags ensure that * gcc does not attempt to pad the long ints in the structure to word @@ -72,11 +67,10 @@ struct scb { /*1*/ u_char control; #define SCB_NEEDWDTR 0x80 /* Initiate Wide Negotiation */ #define SCB_NEEDSDTR 0x40 /* Initiate Sync Negotiation */ +#define SCB_TE 0x20 /* Tag enable */ #define SCB_NEEDDMA 0x08 /* SCB needs to be DMA'd from * from host memory */ -#define SCB_TE 0x20 /* Tag enable */ -#define SCB_WAITING 0x06 #define SCB_DIS 0x04 #define SCB_TAG_TYPE 0x3 #define SIMPLE_QUEUE 0x0 @@ -138,6 +132,7 @@ struct ahc_data { u_short needwdtr; /* Current list of negotiated targets */ u_short sdtrpending; /* Pending SDTR to these targets */ u_short wdtrpending; /* Pending WDTR to these targets */ + u_short tagenable; /* Targets that can handle tagqueing */ int numscbs; u_char maxscbs; int unpause; |