summaryrefslogtreecommitdiffstats
path: root/sys/i386/scsi/aic7xxx.c
diff options
context:
space:
mode:
authorgibbs <gibbs@FreeBSD.org>1996-04-20 21:29:27 +0000
committergibbs <gibbs@FreeBSD.org>1996-04-20 21:29:27 +0000
commit62413c04e3dce44b08aa9dbac2920a828e85f1bc (patch)
tree946c12e88dc37a2d3d8c23f235e196a0a71d3358 /sys/i386/scsi/aic7xxx.c
parent02afa38b7bc1bab7ebb708ff57cbbc396cb4823c (diff)
downloadFreeBSD-src-62413c04e3dce44b08aa9dbac2920a828e85f1bc.zip
FreeBSD-src-62413c04e3dce44b08aa9dbac2920a828e85f1bc.tar.gz
Implement SCB paging which allows up to 255 active commands on aic7770
(Rev E or greater), aic7850, aic7860, aic7870, and aic7880 controllers. SCB paging is enabled with the option "AHC_SCBPAGING_ENABLE". Full comments on the algorithm are at the top of i386/scsi/aic7xxx.c. options "AHC_TAGENABLE" and "AHC_QUEUE_FULL" have been removed. The default is 4 tags without SCB paging, 8 with. Clear the SCSIRSTI bit after throwing a bus reset. Some cards seem to get confused otherwise. Handle SCSIRSTI interrupts before checking to see if there is a valid SCB in use since this can happen. (Clears PR# i386/1123) Clean up the way we determine the number of SCBs on the card (courtesy of Dan Eischen). Guard against attempts to negotiate wide to a narrow controller. Fix some comments. Update my copyrights.
Diffstat (limited to 'sys/i386/scsi/aic7xxx.c')
-rw-r--r--sys/i386/scsi/aic7xxx.c1109
1 files changed, 724 insertions, 385 deletions
diff --git a/sys/i386/scsi/aic7xxx.c b/sys/i386/scsi/aic7xxx.c
index e2fce6e..67e1a8a 100644
--- a/sys/i386/scsi/aic7xxx.c
+++ b/sys/i386/scsi/aic7xxx.c
@@ -1,35 +1,116 @@
/*
* Generic driver for the aic7xxx based adaptec SCSI controllers
- * Copyright (c) 1994, 1995, 1996 Justin T. Gibbs.
- * All rights reserved.
- *
* Product specific probe and attach routines can be found in:
* i386/eisa/aic7770.c 27/284X and aic7770 motherboard controllers
* pci/aic7870.c 3940, 2940, aic7870 and aic7850 controllers
*
- * Portions of this driver are based on the FreeBSD 1742 Driver:
- *
- * Written by Julian Elischer (julian@tfs.com)
- * for TRW Financial Systems for use under the MACH(2.5) operating system.
- *
- * TRW Financial Systems, in accordance with their agreement with Carnegie
- * Mellon University, makes this software available to CMU to distribute
- * or use in any manner that they see fit as long as this message is kept with
- * the software. For this reason TFS also grants any other persons or
- * organisations permission to use or modify this software.
+ * Copyright (c) 1994, 1995, 1996 Justin T. Gibbs.
+ * All rights reserved.
*
- * TFS supplies this software to be publicly redistributed
- * on the understanding that TFS is not responsible for the correct
- * functioning of this software in any circumstances.
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice immediately at the beginning of the file, without modification,
+ * this list of conditions, and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
*
- * commenced: Sun Sep 27 18:14:01 PDT 1992
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
*
- * $Id: aic7xxx.c,v 1.59 1996/03/23 11:29:20 phk Exp $
+ * $Id: aic7xxx.c,v 1.60 1996/03/31 03:15:24 gibbs Exp $
*/
/*
* TODO:
* Implement Target Mode
*
+ * A few notes on how SCB paging works...
+ *
+ * SCB paging takes advantage of the fact that devices stay disconnected
+ * from the bus a relatively long time and that while they're disconnected,
+ * having the SCBs for that device down on the host adapter is of little use.
+ * Instead we copy the SCB back up into kernel memory and reuse the SCB slot
+ * on the card to schedule another transaction. This can be a real payoff
+ * when doing random I/O to tagged queueing devices since there are more
+ * transactions active at once for the device to sort for optimal seek
+ * reduction. The algorithm goes like this...
+ *
+ * At the sequencer level:
+ * 1) Disconnected SCBs are threaded onto a doubly linked list, headed by
+ * DISCONNECTED_SCBH using the SCB_NEXT and SCB_PREV fields. The most
+ * recently disconnected device is always at the head.
+ *
+ * 2) The SCB has an added field SCB_TAG that corresponds to the kernel
+ * SCB number (ie 0-254).
+ *
+ * 3) When a command is queued, the hardware index of the SCB it was downloaded
+ * into is placed into the QINFIFO for easy indexing by the sequencer.
+ *
+ * 4) The tag field is used as the tag for tagged-queueing, for determining
+ * the related kernel SCB, and is the value put into the QOUTFIFO
+ * so the kernel doesn't have to upload the SCB to determine the kernel SCB
+ * that completed on command completes.
+ *
+ * 5) When a reconnect occurs, the sequencer must scan the SCB array (even
+ * in the tag case) looking for the appropriate SCB and if it can't find
+ * it, it interrupts the kernel so it can page the SCB in.
+ *
+ * 6) If the sequencer is successful in finding the SCB, it removes it from
+ * the doubly linked list of disconnected SCBS.
+ *
+ * At the kernel level:
+ * 1) There are four queues that a kernel SCB may reside on:
+ * free_scbs - SCBs that are not in use and have a hardware slot assigned
+ * to them.
+ * page_scbs - SCBs that are not in use and need to have a hardware slot
+ * assigned to them (i.e. they will most likely cause a page
+ * out event).
+ * waiting_scbs - SCBs that are active, don't have an assigned hardware
+ * slot assigned to them and are waiting for either a
+ * disconnection or a command complete to free up a slot.
+ * assigned_scbs - SCBs were in the waiting_scbs queue, but were assigned
+ * a slot by ahc_free_scb.
+ *
+ * 2) When a new request comes in, an SCB is allocated from the free_scbs or
+ * page_scbs queue with preference to SCBs on the free_scbs queue.
+ *
+ * 3) If there are no free slots (we retrieved the SCB off of the page_scbs
+ * queue), the SCB is inserted onto the tail of the waiting_scbs list and
+ * we attempt to run this queue down.
+ *
+ * 4) ahc_run_waiing_queues() looks at both the assigned_scbs and waiting_scbs
+ * queues. In the case of the assigned_scbs, the commands are immediately
+ * downloaded and started. For waiting_scbs, we page in all that we can
+ * ensuring we don't create a resource deadlock (see comments in
+ * ahc_run_waing_queues()).
+ *
+ * 5) After we handle a bunch of command completes, we also try running the
+ * queues since many SCBs may have disconnected since the last command
+ * was started and we have at least one free slot on the card.
+ *
+ * 6) ahc_free_scb looks at the waiting_scbs queue for a transaction
+ * requiring a slot and moves it to the assigned_scbs queue if it
+ * finds one. Otherwise it puts the current SCB onto the free_scbs
+ * queue for later use.
+ *
+ * 7) The driver handles page-in requests from the sequencer in response to
+ * the NO_MATCH sequencer interrupt. For tagged commands, the approprite
+ * SCB is easily found since the tag is a direct index into our kernel SCB
+ * array. For non-tagged commands, we keep a separate array of 16 pointers
+ * that point to the single possible SCB that was paged out for that target.
*/
#include <sys/param.h>
@@ -62,13 +143,14 @@
u_long ahc_unit = 0;
-static int ahc_debug = AHC_SHOWABORTS|AHC_SHOWMISC;
+#ifdef AHC_DEBUG
+static int ahc_debug = AHC_SHOWSENSE;
+#endif
/**** bit definitions for SCSIDEF ****/
#define HSCSIID 0x07 /* our SCSI ID */
#define HWSCSIID 0x0f /* our SCSI ID if Wide Bus */
-static u_int32_t ahc_adapter_info __P((int unit));
static void ahcminphys __P((struct buf *bp));
static int32_t ahc_scsi_cmd __P((struct scsi_xfer *xs));
@@ -78,7 +160,7 @@ static struct scsi_adapter ahc_switch =
ahcminphys,
0,
0,
- ahc_adapter_info,
+ 0,
"ahc",
{ 0, 0 }
};
@@ -127,15 +209,17 @@ 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 inline void ahc_fetch_scb __P((struct ahc_data *ahc, struct scb *scb,
- int iosize));
+static inline void ahc_send_scb __P((struct ahc_data *ahc, struct scb *scb));
+static inline void ahc_fetch_scb __P((struct ahc_data *ahc, struct scb *scb));
+static inline void ahc_page_scb __P((struct ahc_data *ahc, struct scb *out_scb,
+ struct scb *in_scb));
+static inline void ahc_run_waiting_queues __P((struct ahc_data *ahc));
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));
#endif
static int ahc_reset_channel __P((struct ahc_data *ahc, char channel,
@@ -148,7 +232,6 @@ static void ahc_reset_current_bus __P((u_long iobase));
static void ahc_run_done_queue __P((struct ahc_data *ahc));
static void ahc_scsirate __P((struct ahc_data* ahc, u_char *scsirate,
int period, int offset, int target));
-static inline void ahc_send_scb __P((struct ahc_data *ahc, struct scb *scb));
static timeout_t
ahc_timeout;
static void ahc_busy_target __P((int target, char channel,
@@ -162,40 +245,23 @@ ahc_print_scb(scb)
struct scb *scb;
{
printf("scb:%p control:0x%x tcl:0x%x cmdlen:%d cmdpointer:0x%lx\n"
- ,scb
+ ,scb
,scb->control
- ,scb->target_channel_lun
- ,scb->cmdlen
- ,scb->cmdpointer );
+ ,scb->tcl
+ ,scb->cmdlen
+ ,scb->cmdpointer );
printf(" datlen:%d data:0x%lx segs:0x%x segp:0x%lx\n"
- ,scb->datalen
- ,scb->data
- ,scb->SG_segment_count
- ,scb->SG_list_pointer);
+ ,scb->datalen
+ ,scb->data
+ ,scb->SG_segment_count
+ ,scb->SG_list_pointer);
printf(" sg_addr:%lx sg_len:%ld\n"
,scb->ahc_dma[0].addr
,scb->ahc_dma[0].len);
}
-static void
-ahc_print_active_scb(ahc)
- struct ahc_data *ahc;
-{
- int cur_scb_offset;
- u_long iobase = ahc->baseport;
- PAUSE_SEQUENCER(ahc);
- cur_scb_offset = inb(SCBPTR + iobase);
- UNPAUSE_SEQUENCER(ahc);
- ahc_print_scb(ahc->scbarray[cur_scb_offset]);
-}
-
#endif
-#define PARERR 0x08
-#define ILLOPCODE 0x04
-#define ILLSADDR 0x02
-#define ILLHADDR 0x01
-
static struct {
u_char errno;
char *errmesg;
@@ -270,9 +336,12 @@ ahc_alloc(unit, iobase, type, flags)
return NULL;
}
bzero(ahc, sizeof(struct ahc_data));
- SLIST_INIT(&ahc->free_scb);
- ahc->unit = unit;
- ahc->baseport = iobase;
+ STAILQ_INIT(&ahc->free_scbs);
+ STAILQ_INIT(&ahc->page_scbs);
+ STAILQ_INIT(&ahc->waiting_scbs);
+ STAILQ_INIT(&ahc->assigned_scbs);
+ ahc->unit = unit;
+ ahc->baseport = iobase;
ahc->type = type;
ahc->flags = flags;
ahc->unpause = (inb(HCNTRL + iobase) & IRQMS) | INTEN;
@@ -340,7 +409,7 @@ ahc_scsirate(ahc, scsirate, period, offset, target )
printf("ahc%d: target %d requests "
"%sMHz transfers, but adapter "
"in Ultra mode can only sync at "
- "10MHz or above\n", ahc->unit,
+ "7.2MHz or above\n", ahc->unit,
target, ahc_syncrates[i].rate);
break; /* Use Async */
}
@@ -434,45 +503,191 @@ ahc_attach(ahc)
return 1;
}
-/* Send an SCB down to the card via PIO maintaining the SCB pointer */
+/*
+ * Send an SCB down to the card via PIO.
+ * We assume that the proper SCB is already selected in SCBPTR.
+ */
static inline void
ahc_send_scb(ahc, scb)
struct ahc_data *ahc;
struct scb *scb;
{
- u_long iobase = ahc->baseport;
- u_char cur_scb;
+ u_long iobase = ahc->baseport;
- 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, SCB_PIO_TRANSFER_SIZE);
- else
- outsl(SCBARRAY+iobase, scb,
- (SCB_PIO_TRANSFER_SIZE + 3) / 4);
- outb(SCBCNT + iobase, 0);
- outb(SCBPTR + iobase, cur_scb);
+ outb(SCBCNT + iobase, SCBAUTO);
+ if( ahc->type == AHC_284 )
+ /* Can only do 8bit PIO */
+ outsb(SCBARRAY+iobase, scb, SCB_PIO_TRANSFER_SIZE);
+ else
+ outsl(SCBARRAY+iobase, scb,
+ (SCB_PIO_TRANSFER_SIZE + 3) / 4);
+ outb(SCBCNT + iobase, 0);
}
+/*
+ * Retrieve an SCB from the card via PIO.
+ * We assume that the proper SCB is already selected in SCBPTR.
+ */
static inline void
-ahc_fetch_scb(ahc, scb, iosize)
+ahc_fetch_scb(ahc, scb)
struct ahc_data *ahc;
struct scb *scb;
- int iosize;
{
u_long iobase = ahc->baseport;
- outb(SCBCNT + iobase, 0x80); /* SCBAUTO */
+ outb(SCBCNT + iobase, 0x80); /* SCBAUTO */
- /* Can only do 8bit PIO for reads */
- insb(SCBARRAY+iobase, scb, iosize);
+ if( ahc->type == AHC_284 || (ahc->type & AHC_AIC7850))
+ /* Can only do 8bit PIO */
+ insb(SCBARRAY+iobase, scb, SCB_PIO_TRANSFER_SIZE);
+ else
+ insl(SCBARRAY+iobase, scb,
+ (SCB_PIO_TRANSFER_SIZE + 3) / 4);
outb(SCBCNT + iobase, 0);
}
/*
+ * Swap in_scbp for out_scbp down in the cards SCB array.
+ * We assume that the SCB for out_scbp is already selected in SCBPTR.
+ */
+static inline void
+ahc_page_scb(ahc, out_scbp, in_scbp)
+ struct ahc_data *ahc;
+ struct scb *out_scbp;
+ struct scb *in_scbp;
+{
+ /* Page-out */
+ ahc_fetch_scb(ahc, out_scbp);
+ out_scbp->flags |= SCB_PAGED_OUT;
+ if(!(out_scbp->control & TAG_ENB))
+ {
+ /* Stick in non-tagged array */
+ int index = (out_scbp->tcl >> 4)
+ | (out_scbp->tcl & SELBUSB);
+ ahc->pagedout_ntscbs[index] = out_scbp;
+ }
+
+ /* Page-in */
+ in_scbp->position = out_scbp->position;
+ out_scbp->position = SCB_LIST_NULL;
+ ahc_send_scb(ahc, in_scbp);
+ in_scbp->flags &= ~SCB_PAGED_OUT;
+}
+
+static inline void
+ahc_run_waiting_queues(ahc)
+ struct ahc_data *ahc;
+{
+ struct scb* scb;
+ u_char cur_scb;
+ u_long iobase = ahc->baseport;
+
+ if(!(ahc->assigned_scbs.stqh_first || ahc->waiting_scbs.stqh_first))
+ return;
+
+ PAUSE_SEQUENCER(ahc);
+ cur_scb = inb(SCBPTR + iobase);
+
+ /*
+ * First handle SCBs that are waiting but have been
+ * assigned a slot.
+ */
+ while((scb = ahc->assigned_scbs.stqh_first) != NULL) {
+ STAILQ_REMOVE_HEAD(&ahc->assigned_scbs, links);
+ outb(SCBPTR + iobase, scb->position);
+ ahc_send_scb(ahc, scb);
+ if (!(scb->xs->flags & SCSI_NOMASK)) {
+ timeout(ahc_timeout, (caddr_t)scb,
+ (scb->xs->timeout * hz) / 1000);
+ }
+ outb(QINFIFO + iobase, scb->position);
+ SC_DEBUG(scb->xs->sc_link, SDEV_DB3, ("cmd_sent\n"));
+ }
+ /* Now deal with SCBs that require paging */
+ if((scb = ahc->waiting_scbs.stqh_first) != NULL) {
+ u_char disc_scb = inb(DISCONNECTED_SCBH + iobase);
+ u_char active = inb(FLAGS+iobase) & (SELECTED|IDENTIFY_SEEN);
+ int count = 0;
+
+ do {
+ u_char next_scb;
+
+ /* Attempt to page this SCB in */
+ if(disc_scb == SCB_LIST_NULL)
+ break;
+
+ /*
+ * Advance disc_scb to the next on in the
+ * list.
+ */
+ outb(SCBPTR + iobase, disc_scb);
+ next_scb = inb(SCB_NEXT + iobase);
+
+ /*
+ * We have to be careful about when we allow
+ * an SCB to be paged out. There must always
+ * be at least one slot availible for a
+ * reconnecting target in case it references
+ * an SCB that has been paged out. Our
+ * heuristic is that either the disconnected
+ * list has at least two entries in it or
+ * there is one entry and the sequencer is
+ * activily working on an SCB which implies that
+ * it will either complete or disconnect before
+ * another reconnection can occur.
+ */
+ if((next_scb != SCB_LIST_NULL) || active)
+ {
+ u_char out_scbi;
+ struct scb* out_scbp;
+
+ STAILQ_REMOVE_HEAD(&ahc->waiting_scbs, links);
+
+ /*
+ * Find the in-core SCB for the one
+ * we're paging out.
+ */
+ out_scbi = inb(SCB_TAG + iobase);
+ out_scbp = ahc->scbarray[out_scbi];
+
+ /* Do the page out */
+ ahc_page_scb(ahc, out_scbp, scb);
+
+ /* Queue the command */
+ outb(QINFIFO + iobase, scb->position);
+ if (!(scb->xs->flags & SCSI_NOMASK)) {
+ timeout(ahc_timeout, (caddr_t)scb,
+ (scb->xs->timeout * hz) / 1000);
+ }
+ SC_DEBUG(scb->xs->sc_link, SDEV_DB3,
+ ("cmd_paged-in\n"));
+ count++;
+
+ /* Advance to the next disconnected SCB */
+ disc_scb = next_scb;
+ }
+ else
+ break;
+ } while((scb = ahc->waiting_scbs.stqh_first) != NULL);
+
+ if(count) {
+ /*
+ * Update the head of the disconnected list.
+ */
+ outb(DISCONNECTED_SCBH + iobase, disc_scb);
+ if(disc_scb != SCB_LIST_NULL) {
+ outb(SCBPTR + iobase, disc_scb);
+ outb(SCB_PREV + iobase, SCB_LIST_NULL);
+ }
+ }
+ }
+ /* Restore old position */
+ outb(SCBPTR + iobase, cur_scb);
+ UNPAUSE_SEQUENCER(ahc);
+}
+
+/*
* Add this SCB to the head of the "waiting for selection" list.
*/
static
@@ -487,7 +702,7 @@ void ahc_add_waiting_scb (iobase, scb)
next = inb(WAITING_SCBH + iobase);
outb(SCBPTR+iobase, scb->position);
- outb(SCB_NEXT_WAITING+iobase, next);
+ outb(SCB_NEXT+iobase, next);
outb(WAITING_SCBH + iobase, scb->position);
outb(SCBPTR + iobase, curscb);
@@ -530,6 +745,11 @@ ahc_intr(arg)
inb(SEQADDR0 + iobase));
}
if (intstat & SEQINT) {
+ /*
+ * This code isn't used by the SCB page-in code. It
+ * should probably be moved to cut out the extra
+ * inb.
+ */
u_short targ_mask;
u_char target = (inb(SCSIID + iobase) >> 4) & 0x0f;
u_char scratch_offset = target;
@@ -561,9 +781,9 @@ ahc_intr(arg)
else
printf("ahc%d:%c:%d: Warning - "
"unknown message recieved from "
- "target (0x%x). Rejecting\n",
+ "target (0x%x - 0x%x). Rejecting\n",
ahc->unit, channel, target,
- rejbyte);
+ rejbyte, inb(REJBYTE_EXT + iobase));
break;
}
case NO_IDENT:
@@ -573,7 +793,59 @@ ahc_intr(arg)
inb(SAVED_TCL + iobase));
break;
case NO_MATCH:
- {
+ if(ahc->flags & AHC_PAGESCBS) {
+ /* SCB Page-in request */
+ struct scb *outscb;
+ u_char arg_1 = inb(ARG_1 + iobase);
+ if(arg_1 == SCB_LIST_NULL) {
+ /* Non-tagged command */
+ int index = target | (scb->tcl&SELBUSB);
+ scb = ahc->pagedout_ntscbs[index];
+ }
+ else
+ scb = ahc->scbarray[arg_1];
+
+ /*
+ * Now to pick the SCB to page out.
+ * Either take a free SCB or the first
+ * one on the disconnected SCB list.
+ */
+ if(ahc->free_scbs.stqh_first) {
+ outscb = ahc->free_scbs.stqh_first;
+ STAILQ_REMOVE_HEAD(&ahc->free_scbs,
+ links);
+ scb->position = outscb->position;
+ outscb->position = SCB_LIST_NULL;
+ STAILQ_INSERT_HEAD(&ahc->page_scbs,
+ outscb, links);
+ outb(SCBPTR + iobase, scb->position);
+ ahc_send_scb(ahc, scb);
+ scb->flags &= ~SCB_PAGED_OUT;
+ }
+ else {
+ u_char tag;
+ u_char next;
+ u_char disc_scb =
+ inb(DISCONNECTED_SCBH + iobase);
+ if(disc_scb == SCB_LIST_NULL)
+ panic("Page-in request with no "
+ "candidates");
+ outb(SCBPTR + iobase, disc_scb);
+ tag = inb(SCB_TAG + iobase);
+ outscb = ahc->scbarray[tag];
+ next = inb(SCB_NEXT + iobase);
+ if(next != SCB_LIST_NULL) {
+ outb(SCBPTR + iobase, next);
+ outb(SCB_PREV + iobase,
+ SCB_LIST_NULL);
+ outb(SCBPTR + iobase, disc_scb);
+ }
+ outb(DISCONNECTED_SCBH + iobase, next);
+ ahc_page_scb(ahc, outscb, scb);
+ }
+ outb(RETURN_1 + iobase, SCB_PAGEDIN);
+ }
+ else {
printf("ahc%d:%c:%d: no active SCB for "
"reconnecting target - "
"issuing ABORT\n", ahc->unit, channel,
@@ -581,11 +853,11 @@ ahc_intr(arg)
printf("SAVED_TCL == 0x%x\n",
inb(SAVED_TCL + iobase));
ahc_unbusy_target(target, channel, iobase);
- outb(SCBARRAY + iobase, 0);
+ outb(SCB_CONTROL + iobase, 0);
outb(CLRSINT1 + iobase, CLRSELTIMEO);
- RESTART_SEQUENCER(ahc);
- break;
+ outb(RETURN_1 + iobase, 0);
}
+ break;
case SDTR_MSG:
{
short period;
@@ -712,16 +984,21 @@ ahc_intr(arg)
scratch &= 0x7f;
break;
case BUS_32_BIT:
+ case BUS_16_BIT:
+ if(ahc->type & AHC_WIDE) {
/* Negotiate 16_BITS */
bus_width = BUS_16_BIT;
- case BUS_16_BIT:
- if(bootverbose)
- printf("ahc%d: target "
- "%d using 16Bit "
- "transfers\n",
+ if(bootverbose)
+ printf("ahc%d: "
+ "target %d "
+ "using 16Bit "
+ "transfers\n",
ahc->unit,
target);
- scratch |= 0x80;
+ scratch |= 0x80;
+ }
+ else
+ bus_width = BUS_8_BIT;
break;
default:
break;
@@ -792,15 +1069,15 @@ ahc_intr(arg)
{
int scb_index;
- /* The sequencer will notify us when a command
- * has an error that would be of interest to
- * the kernel. This allows us to leave the sequencer
- * running in the common case of command completes
- * without error.
- */
+ /* The sequencer will notify us when a command
+ * has an error that would be of interest to
+ * the kernel. This allows us to leave the sequencer
+ * running in the common case of command completes
+ * without error.
+ */
- scb_index = inb(SCBPTR + iobase);
- scb = ahc->scbarray[scb_index];
+ scb_index = inb(SCB_TAG + iobase);
+ scb = ahc->scbarray[scb_index];
/*
* Set the default return value to 0 (don't
@@ -809,25 +1086,25 @@ ahc_intr(arg)
* duplication.
*/
outb(RETURN_1 + iobase, 0);
- if (!scb || !(scb->flags & SCB_ACTIVE)) {
- printf("ahc%d:%c:%d: ahc_intr - referenced scb "
- "not valid during seqint 0x%x scb(%d)\n",
- ahc->unit, channel, target, intstat,
- scb_index);
+ if (!(scb && (scb->flags & SCB_ACTIVE))) {
+ printf("ahc%d:%c:%d: ahc_intr - referenced scb "
+ "not valid during seqint 0x%x scb(%d)\n",
+ ahc->unit, channel, target, intstat,
+ scb_index);
goto clear;
}
xs = scb->xs;
- ahc_fetch_scb(ahc, scb, SCB_PIO_TRANSFER_SIZE);
+ scb->status = inb(SCB_TARGET_STATUS + iobase);
#ifdef AHC_DEBUG
if((ahc_debug & AHC_SHOWSCBS)
&& xs->sc_link->target == DEBUGTARG)
ahc_print_scb(scb);
#endif
- xs->status = scb->target_status;
- switch(scb->target_status){
+ xs->status = scb->status;
+ switch(scb->status){
case SCSI_OK:
printf("ahc%d: Interrupted for staus of"
" 0???\n", ahc->unit);
@@ -843,11 +1120,8 @@ ahc_intr(arg)
if((xs->error == XS_NOERROR) &&
!(scb->flags & SCB_SENSE)) {
- u_char control = scb->control;
- u_short active;
struct ahc_dma_seg *sg = scb->ahc_dma;
struct scsi_sense *sc = &(scb->sense_cmd);
- u_char tcl = scb->target_channel_lun;
#ifdef AHC_DEBUG
if(ahc_debug & AHC_SHOWSENSE)
{
@@ -855,9 +1129,6 @@ ahc_intr(arg)
printf("Sending Sense\n");
}
#endif
- bzero(scb, SCB_PIO_TRANSFER_SIZE);
- scb->control |= control & DISCENB;
- scb->flags |= SCB_SENSE;
sc->op_code = REQUEST_SENSE;
sc->byte2 = xs->sc_link->lun << 5;
sc->length = sizeof(struct scsi_sense_data);
@@ -866,18 +1137,17 @@ ahc_intr(arg)
sg->addr = KVTOPHYS(&xs->sense);
sg->len = sizeof(struct scsi_sense_data);
- scb->target_channel_lun = tcl;
+ scb->control &= DISCENB;
+ scb->status = 0;
scb->SG_segment_count = 1;
scb->SG_list_pointer = KVTOPHYS(sg);
- scb->cmdpointer = KVTOPHYS(sc);
- scb->cmdlen = sizeof(*sc);
-
scb->data = sg->addr;
scb->datalen = sg->len;
+ scb->cmdpointer = KVTOPHYS(sc);
+ scb->cmdlen = sizeof(*sc);
+ scb->flags |= SCB_SENSE;
ahc_send_scb(ahc, scb);
- outb(SCB_NEXT_WAITING+iobase,
- SCB_LIST_NULL);
/*
* Ensure that the target is "BUSY"
* so we don't get overlapping
@@ -916,12 +1186,13 @@ ahc_intr(arg)
*/
sc_print_addr(xs->sc_link);
printf("Queue Full\n");
- xs->error = XS_BUSY;
+ STAILQ_INSERT_TAIL(&ahc->assigned_scbs,
+ scb, links);
break;
default:
sc_print_addr(xs->sc_link);
printf("unexpected targ_status: %x\n",
- scb->target_status);
+ scb->status);
xs->error = XS_DRIVER_STUFFUP;
break;
}
@@ -930,7 +1201,7 @@ ahc_intr(arg)
case RESIDUAL:
{
int scb_index;
- scb_index = inb(SCBPTR + iobase);
+ scb_index = inb(SCB_TAG + iobase);
scb = ahc->scbarray[scb_index];
xs = scb->xs;
/*
@@ -977,8 +1248,8 @@ ahc_intr(arg)
}
case ABORT_TAG:
{
- int scb_index;
- scb_index = inb(SCBPTR + iobase);
+ int scb_index;
+ scb_index = inb(SCB_TAG + iobase);
scb = ahc->scbarray[scb_index];
xs = scb->xs;
/*
@@ -995,7 +1266,7 @@ ahc_intr(arg)
case AWAITING_MSG:
{
int scb_index;
- scb_index = inb(SCBPTR + iobase);
+ scb_index = inb(SCB_TAG + iobase);
scb = ahc->scbarray[scb_index];
/*
* This SCB had a zero length command, informing
@@ -1019,7 +1290,7 @@ ahc_intr(arg)
/*
* Take care of device reset messages
*/
- u_char scbindex = inb(SCBPTR + iobase);
+ u_char scbindex = inb(SCB_TAG + iobase);
scb = ahc->scbarray[scbindex];
if(scb->flags & SCB_DEVICE_RESET) {
u_char targ_scratch;
@@ -1083,35 +1354,34 @@ clear:
if (intstat & SCSIINT) {
- int scb_index = inb(SCBPTR + iobase);
+ int scb_index = inb(SCB_TAG + iobase);
status = inb(SSTAT1 + iobase);
scb = ahc->scbarray[scb_index];
xs = scb->xs;
- if (!scb || !(scb->flags & SCB_ACTIVE)) {
- printf("ahc%d: ahc_intr - referenced scb not "
- "valid during scsiint 0x%x scb(%d)\n",
- ahc->unit, status, scb_index);
- outb(CLRSINT1 + iobase, status);
- UNPAUSE_SEQUENCER(ahc);
- outb(CLRINT + iobase, CLRSCSIINT);
- scb = NULL;
- goto cmdcomplete;
- }
if (status & SCSIRSTI) {
char channel;
- PAUSE_SEQUENCER(ahc);
channel = inb(SBLKCTL + iobase);
channel = channel & SELBUSB ? 'B' : 'A';
printf("ahc%d: Someone reset channel %c\n",
- channel);
+ ahc->unit, channel);
ahc_reset_channel(ahc,
channel,
SCB_LIST_NULL,
XS_BUSY,
/* Initiate Reset */FALSE);
+ scb = NULL;
+ }
+ else if (!(scb && (scb->flags & SCB_ACTIVE))){
+ printf("ahc%d: ahc_intr - referenced scb not "
+ "valid during scsiint 0x%x scb(%d)\n",
+ ahc->unit, status, scb_index);
+ outb(CLRSINT1 + iobase, status);
+ UNPAUSE_SEQUENCER(ahc);
+ outb(CLRINT + iobase, CLRSCSIINT);
+ scb = NULL;
}
else if (status & SCSIPERR) {
/*
@@ -1185,7 +1455,7 @@ clear:
? 'B' : 'A',
iobase);
- outb(SCBARRAY + iobase, 0);
+ outb(SCB_CONTROL + iobase, 0);
outb(CLRSINT1 + iobase, CLRSELTIMEO);
@@ -1194,12 +1464,11 @@ clear:
/* Shift the waiting for selection queue forward */
waiting = inb(WAITING_SCBH + iobase);
outb(SCBPTR + iobase, waiting);
- waiting = inb(SCB_NEXT_WAITING + iobase);
+ waiting = inb(SCB_NEXT + iobase);
outb(WAITING_SCBH + iobase, waiting);
RESTART_SEQUENCER(ahc);
}
-
else if (!(status & BUSFREE)) {
sc_print_addr(xs->sc_link);
printf("Unknown SCSIINT. Status = 0x%x\n", status);
@@ -1214,7 +1483,6 @@ clear:
ahc_done(ahc, scb);
}
}
-cmdcomplete:
if (intstat & CMDCMPLT) {
int scb_index;
@@ -1236,6 +1504,7 @@ cmdcomplete:
ahc_done(ahc, scb);
} while (inb(QOUTCNT + iobase) & ahc->qcntmask);
+ ahc_run_waiting_queues(ahc);
}
}
@@ -1263,12 +1532,11 @@ ahc_done(ahc, scb)
xs->error = XS_NOERROR;
}
xs->flags |= ITSDONE;
-#ifdef AHC_TAGENABLE
if(xs->cmd->opcode == 0x12 && xs->error == XS_NOERROR)
{
struct scsi_inquiry_data *inq_data;
u_short mask = 0x01 << (xs->sc_link->target |
- (scb->target_channel_lun & 0x08));
+ (scb->tcl & 0x08));
/*
* Sneak a look at the results of the SCSI Inquiry
* command and see if we can do Tagged queing. This
@@ -1280,12 +1548,24 @@ ahc_done(ahc, scb)
printf("ahc%d: target %d Tagged Queuing Device\n",
ahc->unit, xs->sc_link->target);
ahc->tagenable |= mask;
-#ifdef QUEUE_FULL_SUPPORTED
- xs->sc_link->opennings += 2;
-#endif
+ if(ahc->maxhscbs >= 16 || (ahc->flags & AHC_PAGESCBS)) {
+ /* Default to 8 tags */
+ xs->sc_link->opennings += 16;
+ }
+ else
+ {
+ /*
+ * Default to 4 tags on whimpy
+ * cards that don't have much SCB
+ * space and can't page. This prevents
+ * a single device from hogging all
+ * slots. We should really have a better
+ * way of providing fairness.
+ */
+ xs->sc_link->opennings += 2;
+ }
}
}
-#endif
ahc_free_scb(ahc, scb, xs->flags);
scsi_done(xs);
}
@@ -1304,6 +1584,11 @@ ahc_init(ahc)
* Assume we have a board at this stage and it has been reset.
*/
+ /* Handle the SCBPAGING option */
+#ifndef AHC_SCBPAGING_ENABLE
+ ahc->flags &= ~AHC_PAGESCBS;
+#endif
+
/* Determine channel configuration and who we are on the scsi bus. */
switch ( (sblkctl = inb(SBLKCTL + iobase) & 0x0a) ) {
case 0:
@@ -1314,7 +1599,7 @@ ahc_init(ahc)
ahc->our_id);
else
printf("Single Channel, SCSI Id=%d, ", ahc->our_id);
- outb(FLAGS + iobase, SINGLE_BUS);
+ outb(FLAGS + iobase, SINGLE_BUS | (ahc->flags & AHC_PAGESCBS));
break;
case 2:
ahc->our_id = (inb(SCSICONF + 1 + iobase) & HWSCSIID);
@@ -1325,7 +1610,7 @@ ahc_init(ahc)
else
printf("Wide Channel, SCSI Id=%d, ", ahc->our_id);
ahc->type |= AHC_WIDE;
- outb(FLAGS + iobase, WIDE_BUS);
+ outb(FLAGS + iobase, WIDE_BUS | (ahc->flags & AHC_PAGESCBS));
break;
case 8:
ahc->our_id = (inb(SCSICONF + iobase) & HSCSIID);
@@ -1333,7 +1618,7 @@ ahc_init(ahc)
printf("Twin Channel, A SCSI Id=%d, B SCSI Id=%d, ",
ahc->our_id, ahc->our_id_b);
ahc->type |= AHC_TWIN;
- outb(FLAGS + iobase, TWIN_BUS);
+ outb(FLAGS + iobase, TWIN_BUS | (ahc->flags & AHC_PAGESCBS));
break;
default:
printf(" Unsupported adapter type. Ignoring\n");
@@ -1343,46 +1628,39 @@ ahc_init(ahc)
/* Determine the number of SCBs */
{
- int i;
- int j;
-
outb(SCBPTR + iobase, 0);
- outb(SCBARRAY + iobase, 0);
- for(i = 1; i < AHC_SCB_MAX; i<<=1) {
+ outb(SCB_CONTROL + iobase, 0);
+ for(i = 1; i < AHC_SCB_MAX; i++) {
outb(SCBPTR + iobase, i);
- outb(SCBARRAY + iobase, i);
- if(inb(SCBARRAY + iobase) == i){
- outb(SCBPTR + iobase, 0);
- if(inb(SCBARRAY + iobase) != i)
- continue;
- }
- break;
- }
- /*
- * Now go backward from i so we can detect odd
- * SCB configurations (like the aic7850 which
- * has 3).
- */
- j = i >> 1;
- outb(SCBPTR + iobase, 0);
- outb(SCBARRAY + iobase, 0);
- for(i--;i > j; i--) {
+ outb(SCB_CONTROL + iobase, i);
+ if(inb(SCB_CONTROL + iobase) != i)
+ break;
+ outb(SCBPTR + iobase, 0);
+ if(inb(SCB_CONTROL + iobase) != 0)
+ break;
+ /* Clear the control byte. */
outb(SCBPTR + iobase, i);
- outb(SCBARRAY + iobase, i);
- if(inb(SCBARRAY + iobase) == i){
- outb(SCBPTR + iobase, 0);
- if(inb(SCBARRAY + iobase) == i)
- continue;
- }
- break;
+ outb(SCB_CONTROL + iobase, 0);
+
+ ahc->qcntmask |= i; /* Update the count mask. */
}
- ahc->maxscbs = i+1;
- for(ahc->qcntmask = ahc->maxscbs; i > 0; i >>= 1 )
- ahc->qcntmask |= i;
+ /* Ensure we clear the 0 SCB's control byte. */
+ outb(SCBPTR + iobase, 0);
+ outb(SCB_CONTROL + iobase, 0);
+
+ ahc->qcntmask |= i;
+ ahc->maxhscbs = i;
}
- printf("%d SCBs\n", ahc->maxscbs);
+ if((ahc->maxhscbs < AHC_SCB_MAX) && (ahc->flags & AHC_PAGESCBS))
+ ahc->maxscbs = AHC_SCB_MAX;
+ else {
+ ahc->maxscbs = ahc->maxhscbs;
+ ahc->flags &= ~AHC_PAGESCBS;
+ }
+
+ printf("%d SCBs\n", ahc->maxhscbs);
#ifdef AHC_DEBUG
if(ahc_debug & AHC_SHOWMISC) {
@@ -1416,6 +1694,10 @@ ahc_init(ahc)
DELAY(1000);
outb(SCSISEQ + iobase, 0);
+ /* Ensure we don't get a RSTI interrupt from this */
+ outb(CLRSINT1 + iobase, CLRSCSIRSTI);
+ outb(CLRINT + iobase, CLRSCSIINT);
+
/* Select Channel A */
outb(SBLKCTL + iobase, 0);
}
@@ -1433,6 +1715,10 @@ ahc_init(ahc)
DELAY(1000);
outb(SCSISEQ + iobase, 0);
+ /* Ensure we don't get a RSTI interrupt from this */
+ outb(CLRSINT1 + iobase, CLRSCSIRSTI);
+ outb(CLRINT + iobase, CLRSCSIINT);
+
/*
* Look at the information that board initialization or
* the board bios has left us. In the lower four bits of each
@@ -1509,21 +1795,12 @@ ahc_init(ahc)
ahc->needwdtr, ahc->discenable);
#endif
/*
- * Clear the control byte for every SCB so that the sequencer
- * doesn't get confused and think that one of them is valid
- */
- for(i = 0; i < ahc->maxscbs; i++) {
- outb(SCBPTR + iobase, i);
- outb(SCBARRAY + iobase, 0);
- }
-
- /*
* Set the number of availible SCBs
*/
- outb(SCBCOUNT + iobase, ahc->maxscbs);
+ outb(SCBCOUNT + iobase, ahc->maxhscbs);
/*
- * 2s compliment of SCBCOUNT
+ * 2's compliment of maximum tag value
*/
i = ahc->maxscbs;
outb(COMP_SCBCOUNT + iobase, -i & 0xff);
@@ -1536,11 +1813,14 @@ ahc_init(ahc)
outb(QCNTMASK + iobase, ahc->qcntmask);
/* We don't have any busy targets right now */
- outb( ACTIVE_A + iobase, 0 );
- outb( ACTIVE_B + iobase, 0 );
+ outb(ACTIVE_A + iobase, 0);
+ outb(ACTIVE_B + iobase, 0);
/* We don't have any waiting selections */
- outb( WAITING_SCBH + iobase, SCB_LIST_NULL );
+ outb(WAITING_SCBH + iobase, SCB_LIST_NULL);
+
+ /* Our disconnection list is empty too */
+ outb(DISCONNECTED_SCBH + iobase, SCB_LIST_NULL);
/* Message out buffer starts empty */
outb(MSG_LEN + iobase, 0x00);
@@ -1564,7 +1844,7 @@ ahc_init(ahc)
/*
* Note that we are going and return (to probe)
*/
- ahc->flags = AHC_INIT;
+ ahc->flags |= AHC_INIT;
return (0);
}
@@ -1647,85 +1927,81 @@ ahc_scsi_cmd(xs)
scb->control |= NEEDSDTR;
ahc->sdtrpending |= mask;
}
- scb->target_channel_lun = ((xs->sc_link->target << 4) & 0xF0) |
+ scb->tcl = ((xs->sc_link->target << 4) & 0xF0) |
((u_long)xs->sc_link->fordriver & 0x08) |
(xs->sc_link->lun & 0x07);
- scb->cmdlen = xs->cmdlen;
+ scb->cmdlen = xs->cmdlen;
scb->cmdpointer = KVTOPHYS(xs->cmd);
xs->resid = 0;
xs->status = 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;
- seg = 0;
- {
- /*
- * Set up the scatter gather block
- */
- SC_DEBUG(xs->sc_link, SDEV_DB4,
- ("%ld @%p:- ", xs->datalen, xs->data));
- datalen = xs->datalen;
- thiskv = (int) xs->data;
- thisphys = KVTOPHYS(thiskv);
-
- while ((datalen) && (seg < AHC_NSEG)) {
- bytes_this_seg = 0;
-
- /* put in the base address */
- sg->addr = thisphys;
-
- SC_DEBUGN(xs->sc_link, SDEV_DB4, ("0x%lx",
- thisphys));
-
- /* do it at least once */
- nextphys = thisphys;
- while ((datalen) && (thisphys == nextphys)) {
- /*
- * This page is contiguous (physically)
- * with the the last, just extend the
- * length
- */
- /* how far to the end of the page */
- nextphys = (thisphys & (~(PAGESIZ - 1)))
- + PAGESIZ;
- bytes_this_page = nextphys - thisphys;
- /**** or the data ****/
- bytes_this_page = min(bytes_this_page
- ,datalen);
- bytes_this_seg += bytes_this_page;
- datalen -= bytes_this_page;
-
- /* get more ready for the next page */
- thiskv = (thiskv & (~(PAGESIZ - 1)))
- + PAGESIZ;
- if (datalen)
- thisphys = KVTOPHYS(thiskv);
- }
- /*
- * next page isn't contiguous, finish the seg
- */
- SC_DEBUGN(xs->sc_link, SDEV_DB4,
+ if (xs->datalen) { /* should use S/G only if not zero length */
+ scb->SG_list_pointer = KVTOPHYS(scb->ahc_dma);
+ sg = scb->ahc_dma;
+ seg = 0;
+ /*
+ * Set up the scatter gather block
+ */
+ SC_DEBUG(xs->sc_link, SDEV_DB4,
+ ("%ld @%p:- ", xs->datalen, xs->data));
+ datalen = xs->datalen;
+ thiskv = (int) xs->data;
+ thisphys = KVTOPHYS(thiskv);
+
+ while ((datalen) && (seg < AHC_NSEG)) {
+ bytes_this_seg = 0;
+
+ /* put in the base address */
+ sg->addr = thisphys;
+
+ SC_DEBUGN(xs->sc_link, SDEV_DB4, ("0x%lx", thisphys));
+
+ /* do it at least once */
+ nextphys = thisphys;
+ while ((datalen) && (thisphys == nextphys)) {
+ /*
+ * This page is contiguous (physically)
+ * with the the last, just extend the
+ * length
+ */
+ /* how far to the end of the page */
+ nextphys = (thisphys & (~(PAGESIZ - 1)))
+ + PAGESIZ;
+ bytes_this_page = nextphys - thisphys;
+ /**** or the data ****/
+ bytes_this_page = min(bytes_this_page ,datalen);
+ bytes_this_seg += bytes_this_page;
+ datalen -= bytes_this_page;
+
+ /* get more ready for the next page */
+ thiskv = (thiskv & (~(PAGESIZ - 1)))
+ + PAGESIZ;
+ if (datalen)
+ thisphys = KVTOPHYS(thiskv);
+ }
+ /*
+ * next page isn't contiguous, finish the seg
+ */
+ SC_DEBUGN(xs->sc_link, SDEV_DB4,
("(0x%x)", bytes_this_seg));
- sg->len = bytes_this_seg;
- sg++;
- seg++;
- }
- } /*end of iov/kv decision */
- scb->SG_segment_count = seg;
+ sg->len = bytes_this_seg;
+ sg++;
+ seg++;
+ }
+ scb->SG_segment_count = seg;
/* Copy the first SG into the data pointer area */
scb->data = scb->ahc_dma->addr;
scb->datalen = scb->ahc_dma->len;
- SC_DEBUGN(xs->sc_link, SDEV_DB4, ("\n"));
- if (datalen) {
+ SC_DEBUGN(xs->sc_link, SDEV_DB4, ("\n"));
+ if (datalen) {
/* there's still data, must have run out of segs! */
- printf("ahc_scsi_cmd%d: more than %d DMA segs\n",
- ahc->unit, AHC_NSEG);
- xs->error = XS_DRIVER_STUFFUP;
- ahc_free_scb(ahc, scb, flags);
- return (COMPLETE);
- }
- }
+ printf("ahc_scsi_cmd%d: more than %d DMA segs\n",
+ ahc->unit, AHC_NSEG);
+ xs->error = XS_DRIVER_STUFFUP;
+ ahc_free_scb(ahc, scb, flags);
+ return (COMPLETE);
+ }
+ }
else {
/*
* No data xfer, use non S/G values
@@ -1737,29 +2013,42 @@ ahc_scsi_cmd(xs)
}
#ifdef AHC_DEBUG
- if((ahc_debug & AHC_SHOWSCBS) && (xs->sc_link->target == DEBUGTARG))
+ if((ahc_debug & AHC_SHOWSCBS) && (xs->sc_link->target == DEBUGTARG))
ahc_print_scb(scb);
#endif
- if (!(flags & SCSI_NOMASK)) {
- s = splbio();
+ s = splbio();
+
+ if( scb->position != SCB_LIST_NULL )
+ {
+ /* We already have a valid slot */
+ u_long iobase = ahc->baseport;
+ u_char curscb;
+
PAUSE_SEQUENCER(ahc);
+ curscb = inb(SCBPTR + iobase);
+ outb(SCBPTR + iobase, scb->position);
ahc_send_scb(ahc, scb);
- outb(QINFIFO + ahc->baseport, scb->position);
+ outb(SCBPTR + iobase, curscb);
+ outb(QINFIFO + iobase, scb->position);
UNPAUSE_SEQUENCER(ahc);
- timeout(ahc_timeout, (caddr_t)scb, (xs->timeout * hz) / 1000);
- splx(s);
+ if (!(flags & SCSI_NOMASK)) {
+ timeout(ahc_timeout, (caddr_t)scb,
+ (xs->timeout * hz) / 1000);
+ }
SC_DEBUG(xs->sc_link, SDEV_DB3, ("cmd_sent\n"));
+ }
+ else {
+ STAILQ_INSERT_TAIL(&ahc->waiting_scbs, scb, links);
+ ahc_run_waiting_queues(ahc);
+ }
+ if (!(flags & SCSI_NOMASK)) {
+ splx(s);
return (SUCCESSFULLY_QUEUED);
}
/*
* If we can't use interrupts, poll for completion
*/
- s = splbio();
- PAUSE_SEQUENCER(ahc);
- ahc_send_scb(ahc, scb);
- outb(QINFIFO + ahc->baseport, scb->position);
- UNPAUSE_SEQUENCER(ahc);
- SC_DEBUG(xs->sc_link, SDEV_DB3, ("cmd_wait\n"));
+ SC_DEBUG(xs->sc_link, SDEV_DB3, ("cmd_poll\n"));
do {
if (ahc_poll(ahc, xs->timeout)) {
if (!(xs->flags & SCSI_SILENT))
@@ -1774,17 +2063,6 @@ ahc_scsi_cmd(xs)
/*
- * Return some information to the caller about
- * the adapter and it's capabilities.
- */
-static u_int32_t
-ahc_adapter_info(unit)
- int unit;
-{
- return (2); /* 2 outstanding requests at a time per device */
-}
-
-/*
* A scb (and hence an scb entry on the board is put onto the
* free list.
*/
@@ -1794,63 +2072,104 @@ ahc_free_scb(ahc, scb, flags)
int flags;
struct scb *scb;
{
- unsigned int opri;
+ struct scb *wscb;
+ unsigned int opri;
opri = splbio();
- scb->flags = SCB_FREE;
- SLIST_INSERT_HEAD(&ahc->free_scb, scb, next);
+ scb->flags = SCB_FREE;
+ if(scb->position == SCB_LIST_NULL) {
+ STAILQ_INSERT_HEAD(&ahc->page_scbs, scb, links);
+ if(!scb->links.stqe_next && !ahc->free_scbs.stqh_first)
+ /*
+ * If there were no SCBs availible, wake anybody waiting
+ * for one to come free.
+ */
+ wakeup((caddr_t)&ahc->free_scbs);
+ }
+ /*
+ * If there are any SCBS on the waiting queue,
+ * assign the slot of this "freed" SCB to the first
+ * one. We'll run the waiting queues after all command
+ * completes for a particular interrupt are completed
+ * or when we start another command.
+ */
+ else if((wscb = ahc->waiting_scbs.stqh_first) != NULL) {
+ wscb->position = scb->position;
+ STAILQ_REMOVE_HEAD(&ahc->waiting_scbs, links);
+ STAILQ_INSERT_TAIL(&ahc->assigned_scbs, wscb, links);
+
+ /*
+ * The "freed" SCB will need to be assigned a slot
+ * before being used, so put it in the page_scbs
+ * queue.
+ */
+ scb->position = SCB_LIST_NULL;
+ STAILQ_INSERT_HEAD(&ahc->page_scbs, scb, links);
+ if(!scb->links.stqe_next && !ahc->free_scbs.stqh_first)
+ /*
+ * If there were no SCBs availible, wake anybody waiting
+ * for one to come free.
+ */
+ wakeup((caddr_t)&ahc->free_scbs);
+ }
+ else {
+ STAILQ_INSERT_HEAD(&ahc->free_scbs, scb, links);
#ifdef AHC_DEBUG
- ahc->activescbs--;
+ ahc->activescbs--;
#endif
- /*
- * If there were none, wake abybody waiting for
- * one to come free, starting with queued entries
- */
- if (!scb->next.sle_next) {
- wakeup((caddr_t)&ahc->free_scb);
- }
+ if(!scb->links.stqe_next && !ahc->page_scbs.stqh_first)
+ /*
+ * If there were no SCBs availible, wake anybody waiting
+ * for one to come free.
+ */
+ wakeup((caddr_t)&ahc->free_scbs);
+ }
splx(opri);
}
/*
- * Get a free scb
- * If there are none, see if we can allocate a
- * new one. Otherwise either return an error or sleep
+ * Get a free scb, either one already assigned to a hardware slot
+ * on the adapter or one that will require an SCB to be paged out before
+ * use. If there are none, see if we can allocate a new SCB. Otherwise
+ * either return an error or sleep.
*/
static struct scb *
ahc_get_scb(ahc, flags)
struct ahc_data *ahc;
int flags;
{
- unsigned opri;
- struct scb *scbp;
+ unsigned opri;
+ struct scb *scbp;
opri = splbio();
- /*
- * If we can and have to, sleep waiting for one to come free
- * but only if we can't allocate a new one.
- */
- while (1) {
- if( scbp = ahc->free_scb.slh_first ) {
- SLIST_REMOVE_HEAD(&ahc->free_scb, next);
+ /*
+ * If we can and have to, sleep waiting for one to come free
+ * but only if we can't allocate a new one.
+ */
+ while (1) {
+ if((scbp = ahc->free_scbs.stqh_first)) {
+ STAILQ_REMOVE_HEAD(&ahc->free_scbs, links);
+ }
+ else if((scbp = ahc->page_scbs.stqh_first)) {
+ STAILQ_REMOVE_HEAD(&ahc->page_scbs, links);
}
else if (ahc->numscbs < ahc->maxscbs) {
scbp = (struct scb *) malloc(sizeof(struct scb),
M_TEMP, M_NOWAIT);
if (scbp) {
bzero(scbp, sizeof(struct scb));
- scbp->position = ahc->numscbs;
+ scbp->tag = ahc->numscbs;
+ if( ahc->numscbs < ahc->maxhscbs )
+ scbp->position = ahc->numscbs;
+ else
+ scbp->position = SCB_LIST_NULL;
ahc->numscbs++;
- scbp->flags = SCB_ACTIVE;
/*
* Place in the scbarray
- * Never is removed. Position
- * in ahc->scbarray is the scbarray
- * position on the board we will
- * load it into.
+ * Never is removed.
*/
- ahc->scbarray[scbp->position] = scbp;
+ ahc->scbarray[scbp->tag] = scbp;
}
else {
printf("ahc%d: Can't malloc SCB\n", ahc->unit);
@@ -1858,7 +2177,7 @@ ahc_get_scb(ahc, flags)
}
else {
if (!(flags & SCSI_NOSLEEP)) {
- tsleep((caddr_t)&ahc->free_scb, PRIBIO,
+ tsleep((caddr_t)&ahc->free_scbs, PRIBIO,
"ahcscb", 0);
continue;
}
@@ -1868,11 +2187,12 @@ ahc_get_scb(ahc, flags)
if (scbp) {
scbp->control = 0;
+ scbp->status = 0;
scbp->flags = SCB_ACTIVE;
#ifdef AHC_DEBUG
ahc->activescbs++;
if((ahc_debug & AHC_SHOWSCBCNT)
- && (ahc->activescbs == ahc->maxscbs))
+ && (ahc->activescbs == ahc->maxhscbs))
printf("ahc%d: Max SCBs active\n", ahc->unit);
#endif
}
@@ -1934,6 +2254,7 @@ ahc_timeout(arg)
int s, h, found;
u_char bus_state;
u_long iobase;
+ char channel;
s = splbio();
@@ -2037,9 +2358,9 @@ ahc_timeout(arg)
* Been down this road before.
* Do a full bus reset.
*/
- char channel = (scb->target_channel_lun & SELBUSB)
+ char channel = (scb->tcl & SELBUSB)
? 'B': 'A';
- found = ahc_reset_channel(ahc, channel, scb->position,
+ found = ahc_reset_channel(ahc, channel, scb->tag,
XS_TIMEOUT, /*Initiate Reset*/TRUE);
printf("ahc%d: Issued Channel %c Bus Reset #1. "
"%d SCBs aborted\n", ahc->unit, channel, found);
@@ -2061,59 +2382,74 @@ ahc_timeout(arg)
* of the bus and finally disconnect. If this fails,
* we'll get another timeout 2 seconds later which will
* cause a bus reset.
+ *
+ * XXX If the SCB is paged out, we simply reset the
+ * bus. We should probably queue a new command
+ * instead.
*/
- u_char active_scb, control;
- struct scb *active_scbp;
- active_scb = inb(SCBPTR + iobase);
- active_scbp = ahc->scbarray[active_scb];
- control = inb(SCB_CONTROL + iobase);
/* Test to see if scb is disconnected */
- outb(SCBPTR + iobase, scb->position);
- if(inb(SCB_CONTROL + iobase) & DISCONNECTED) {
- scb->flags |= SCB_DEVICE_RESET|SCB_ABORTED;
- scb->SG_segment_count = 0;
- scb->SG_list_pointer = 0;
- scb->data = 0;
- scb->datalen = 0;
- ahc_send_scb(ahc, scb);
- ahc_add_waiting_scb(iobase, scb);
- timeout(ahc_timeout, (caddr_t)scb, (2 * hz));
- sc_print_addr(scb->xs->sc_link);
- printf("BUS DEVICE RESET message queued.\n");
- outb(SCBPTR + iobase, active_scb);
- UNPAUSE_SEQUENCER(ahc);
- }
- /* Is the active SCB really active? */
- else if((active_scbp->flags & SCB_ACTIVE) && bus_state) {
- outb(MSG_LEN + iobase, 1);
- outb(MSG0 + iobase, MSG_BUS_DEVICE_RESET);
- outb(SCSISIGO + iobase, bus_state|ATNO);
- sc_print_addr(active_scbp->xs->sc_link);
- printf("asserted ATN - device reset in message buffer\n");
- active_scbp->flags |= SCB_DEVICE_RESET|SCB_ABORTED;
- if(active_scbp != scb) {
- untimeout(ahc_timeout, (caddr_t)active_scbp);
- /* Give scb a new lease on life */
- timeout(ahc_timeout, (caddr_t)scb,
- (scb->xs->timeout * hz) / 1000);
+ if( !(scb->flags & SCB_PAGED_OUT ) ){
+ u_char active_scb;
+ struct scb *active_scbp;
+
+ active_scb = inb(SCB_TAG + iobase);
+ active_scbp = ahc->scbarray[active_scb];
+ outb(SCBPTR + iobase, scb->position);
+
+ if(inb(SCB_CONTROL + iobase) & DISCONNECTED) {
+ scb->flags |= SCB_DEVICE_RESET|SCB_ABORTED;
+ scb->SG_segment_count = 0;
+ scb->SG_list_pointer = 0;
+ scb->data = 0;
+ scb->datalen = 0;
+ ahc_send_scb(ahc, scb);
+ ahc_add_waiting_scb(iobase, scb);
+ timeout(ahc_timeout, (caddr_t)scb, (2 * hz));
+ sc_print_addr(scb->xs->sc_link);
+ printf("BUS DEVICE RESET message queued.\n");
+ outb(SCBPTR + iobase, active_scb);
+ UNPAUSE_SEQUENCER(ahc);
+ goto done;
+ }
+ /* Is the active SCB really active? */
+ else if((active_scbp->flags & SCB_ACTIVE) && bus_state){
+ outb(MSG_LEN + iobase, 1);
+ outb(MSG0 + iobase, MSG_BUS_DEVICE_RESET);
+ outb(SCSISIGO + iobase, bus_state|ATNO);
+ sc_print_addr(active_scbp->xs->sc_link);
+ printf("asserted ATN - device reset in "
+ "message buffer\n");
+ active_scbp->flags |= SCB_DEVICE_RESET
+ | SCB_ABORTED;
+ if(active_scbp != scb) {
+ untimeout(ahc_timeout,
+ (caddr_t)active_scbp);
+ /* Give scb a new lease on life */
+ timeout(ahc_timeout, (caddr_t)scb,
+ (scb->xs->timeout * hz) / 1000);
+ }
+ timeout(ahc_timeout, (caddr_t)active_scbp,
+ (2 * hz));
+ outb(SCBPTR + iobase, active_scb);
+ UNPAUSE_SEQUENCER(ahc);
+ goto done;
}
- timeout(ahc_timeout, (caddr_t)active_scbp, (2 * hz));
- outb(SCBPTR + iobase, active_scb);
- UNPAUSE_SEQUENCER(ahc);
- }
- else {
- /* No active target? Try reseting the bus */
- char channel = (scb->target_channel_lun & SELBUSB)
- ? 'B': 'A';
- found = ahc_reset_channel(ahc, channel, scb->position,
- XS_TIMEOUT, /*Initiate Reset*/TRUE);
- printf("ahc%d: Issued Channel %c Bus Reset #2. "
- "%d SCBs aborted\n", ahc->unit, channel,
- found);
- ahc->in_timeout = FALSE;
}
+ /*
+ * No active target or a paged out SCB.
+ * Try reseting the bus
+ */
+ channel = (scb->tcl & SELBUSB) ? 'B': 'A';
+ found = ahc_reset_channel(ahc, channel, scb->tag,
+ XS_TIMEOUT,
+ /*Initiate Reset*/TRUE);
+ printf("ahc%d: Issued Channel %c Bus Reset #2. "
+ "%d SCBs aborted\n", ahc->unit, channel,
+ found);
+ ahc->in_timeout = FALSE;
}
+done:
splx(s);
}
@@ -2144,11 +2480,12 @@ ahc_reset_device(ahc, target, channel, timedout_scb, xs_error)
*/
{
int saved_queue[AHC_SCB_MAX];
- int queued = inb(QINCNT + iobase);
+ int queued = inb(QINCNT + iobase) & ahc->qcntmask;
for (i = 0; i < (queued - found); i++) {
saved_queue[i] = inb(QINFIFO + iobase);
- scbp = ahc->scbarray[saved_queue[i]];
+ outb(SCBPTR + iobase, saved_queue[i]);
+ scbp = ahc->scbarray[inb(SCB_TAG + iobase)];
if (ahc_match_scb (scbp, target, channel)){
/*
* We found an scb that needs to be aborted.
@@ -2157,8 +2494,7 @@ ahc_reset_device(ahc, target, channel, timedout_scb, xs_error)
scbp->xs->error |= xs_error;
if(scbp->position != timedout_scb)
untimeout(ahc_timeout, (caddr_t)scbp);
- outb(SCBPTR + iobase, scbp->position);
- outb(SCBARRAY + iobase, 0);
+ outb(SCB_CONTROL + iobase, 0);
i--;
found++;
}
@@ -2179,7 +2515,8 @@ ahc_reset_device(ahc, target, channel, timedout_scb, xs_error)
prev = SCB_LIST_NULL;
while (next != SCB_LIST_NULL) {
- scbp = ahc->scbarray[next];
+ outb(SCBPTR + iobase, next);
+ scbp = ahc->scbarray[inb(SCB_TAG + iobase)];
/*
* Select the SCB.
*/
@@ -2189,9 +2526,8 @@ ahc_reset_device(ahc, target, channel, timedout_scb, xs_error)
found++;
}
else {
- outb(SCBPTR + iobase, scbp->position);
prev = next;
- next = inb(SCB_NEXT_WAITING + iobase);
+ next = inb(SCB_NEXT + iobase);
}
}
}
@@ -2207,11 +2543,14 @@ ahc_reset_device(ahc, target, channel, timedout_scb, xs_error)
&& ahc_match_scb(scbp, target, channel)) {
/* Ensure the target is "free" */
ahc_unbusy_target(target, channel, iobase);
- outb(SCBPTR + iobase, scbp->position);
- outb(SCBARRAY + iobase, 0);
+ if( !(scbp->flags & SCB_PAGED_OUT) )
+ {
+ outb(SCBPTR + iobase, scbp->position);
+ outb(SCB_CONTROL + iobase, 0);
+ }
scbp->flags = SCB_ABORTED|SCB_QUEUED_FOR_DONE;
scbp->xs->error |= xs_error;
- if(scbp->position != timedout_scb)
+ if(scbp->tag != timedout_scb)
untimeout(ahc_timeout, (caddr_t)scbp);
found++;
}
@@ -2234,19 +2573,19 @@ ahc_abort_wscb (ahc, scbp, prev, iobase, timedout_scb, xs_error)
u_int32_t xs_error;
{
u_char curscbp, next;
- int target = ((scbp->target_channel_lun >> 4) & 0x0f);
- char channel = (scbp->target_channel_lun & SELBUSB) ? 'B' : 'A';
+ int target = ((scbp->tcl >> 4) & 0x0f);
+ char channel = (scbp->tcl & SELBUSB) ? 'B' : 'A';
/*
* Select the SCB we want to abort and
* pull the next pointer out of it.
*/
curscbp = inb(SCBPTR + iobase);
outb(SCBPTR + iobase, scbp->position);
- next = inb(SCB_NEXT_WAITING + iobase);
+ next = inb(SCB_NEXT + iobase);
/* Clear the necessary fields */
outb(SCB_CONTROL + iobase, 0);
- outb(SCB_NEXT_WAITING + iobase, SCB_LIST_NULL);
+ outb(SCB_NEXT + iobase, SCB_LIST_NULL);
ahc_unbusy_target(target, channel, iobase);
/* update the waiting list */
@@ -2259,7 +2598,7 @@ ahc_abort_wscb (ahc, scbp, prev, iobase, timedout_scb, xs_error)
* and update its next pointer.
*/
outb(SCBPTR + iobase, prev);
- outb(SCB_NEXT_WAITING + iobase, next);
+ outb(SCB_NEXT + iobase, next);
}
/*
* Point us back at the original scb position
@@ -2269,7 +2608,7 @@ ahc_abort_wscb (ahc, scbp, prev, iobase, timedout_scb, xs_error)
outb(SCBPTR + iobase, curscbp);
scbp->flags = SCB_ABORTED|SCB_QUEUED_FOR_DONE;
scbp->xs->error |= xs_error;
- if(scbp->position != timedout_scb)
+ if(scbp->tag != timedout_scb)
untimeout(ahc_timeout, (caddr_t)scbp);
return next;
}
@@ -2437,8 +2776,8 @@ ahc_match_scb (scb, target, channel)
int target;
char channel;
{
- int targ = (scb->target_channel_lun >> 4) & 0x0f;
- char chan = (scb->target_channel_lun & SELBUSB) ? 'B' : 'A';
+ int targ = (scb->tcl >> 4) & 0x0f;
+ char chan = (scb->tcl & SELBUSB) ? 'B' : 'A';
if (target == ALL_TARGETS)
return (chan == channel);
OpenPOWER on IntegriCloud