summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--sys/i386/scsi/aic7xxx.c144
-rw-r--r--sys/i386/scsi/aic7xxx.h24
2 files changed, 137 insertions, 31 deletions
diff --git a/sys/i386/scsi/aic7xxx.c b/sys/i386/scsi/aic7xxx.c
index 5ace4cc50b80..462d8336aff8 100644
--- a/sys/i386/scsi/aic7xxx.c
+++ b/sys/i386/scsi/aic7xxx.c
@@ -31,7 +31,7 @@
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
- * $Id: aic7xxx.c,v 1.63 1996/04/23 04:22:41 gibbs Exp $
+ * $Id: aic7xxx.c,v 1.64 1996/04/23 04:47:02 gibbs Exp $
*/
/*
* TODO:
@@ -81,8 +81,8 @@
* 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.
+ * assigned_scbs - SCBs that 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.
@@ -537,14 +537,10 @@ ahc_fetch_scb(ahc, scb)
outb(SCBCNT + iobase, 0x80); /* SCBAUTO */
- 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);
+ /* Can only do 8bit PIO for reads */
+ insb(SCBARRAY+iobase, scb, SCB_PIO_TRANSFER_SIZE);
- outb(SCBCNT + iobase, 0);
+ outb(SCBCNT + iobase, 0);
}
/*
@@ -597,11 +593,15 @@ ahc_run_waiting_queues(ahc)
STAILQ_REMOVE_HEAD(&ahc->assigned_scbs, links);
outb(SCBPTR + iobase, scb->position);
ahc_send_scb(ahc, scb);
+
+ /* Mark this as an active command */
+ scb->flags = SCB_ACTIVE;
+
+ outb(QINFIFO + iobase, scb->position);
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 */
@@ -654,6 +654,9 @@ ahc_run_waiting_queues(ahc)
/* Do the page out */
ahc_page_scb(ahc, out_scbp, scb);
+ /* Mark this as an active command */
+ scb->flags = SCB_ACTIVE;
+
/* Queue the command */
outb(QINFIFO + iobase, scb->position);
if (!(scb->xs->flags & SCSI_NOMASK)) {
@@ -799,7 +802,8 @@ ahc_intr(arg)
u_char arg_1 = inb(ARG_1 + iobase);
if(arg_1 == SCB_LIST_NULL) {
/* Non-tagged command */
- int index = target | (scb->tcl&SELBUSB);
+ int index = target |
+ (channel == 'B' ? SELBUSB : 0);
scb = ahc->pagedout_ntscbs[index];
}
else
@@ -807,7 +811,8 @@ ahc_intr(arg)
/*
* Now to pick the SCB to page out.
- * Either take a free SCB or the first
+ * Either take a free SCB, an assigned SCB,
+ * an SCB that just completed or the first
* one on the disconnected SCB list.
*/
if(ahc->free_scbs.stqh_first) {
@@ -822,10 +827,51 @@ ahc_intr(arg)
ahc_send_scb(ahc, scb);
scb->flags &= ~SCB_PAGED_OUT;
}
+ else if(ahc->assigned_scbs.stqh_first) {
+ outscb = ahc->assigned_scbs.stqh_first;
+ STAILQ_REMOVE_HEAD(&ahc->assigned_scbs,
+ links);
+ scb->position = outscb->position;
+ outscb->position = SCB_LIST_NULL;
+ STAILQ_INSERT_HEAD(&ahc->waiting_scbs,
+ outscb, links);
+ outscb->flags = SCB_WAITINGQ;
+ outb(SCBPTR + iobase, scb->position);
+ ahc_send_scb(ahc, scb);
+ scb->flags &= ~SCB_PAGED_OUT;
+ }
+ else if(intstat & CMDCMPLT) {
+ int scb_index;
+
+ printf("PIC\n");
+ outb(CLRINT + iobase, CLRCMDINT);
+ scb_index = inb(QOUTFIFO + iobase);
+ if(!(inb(QOUTCNT + iobase) & ahc->qcntmask))
+ intstat &= ~CMDCMPLT;
+
+ outscb = ahc->scbarray[scb_index];
+ if (!outscb || !(outscb->flags & SCB_ACTIVE)) {
+ printf("ahc%d: WARNING "
+ "no command for scb %d (cmdcmplt)\n",
+ ahc->unit, scb_index );
+ goto use_disconnected_scb;
+ }
+ else {
+ scb->position = outscb->position;
+ outscb->position = SCB_LIST_NULL;
+ outb(SCBPTR + iobase, scb->position);
+ ahc_send_scb(ahc, scb);
+ scb->flags &= ~SCB_PAGED_OUT;
+ untimeout(ahc_timeout, (caddr_t)outscb);
+ ahc_done(ahc, outscb);
+ }
+ }
else {
u_char tag;
u_char next;
- u_char disc_scb =
+ u_char disc_scb;
+use_disconnected_scb:
+ disc_scb =
inb(DISCONNECTED_SCBH + iobase);
if(disc_scb == SCB_LIST_NULL)
panic("Page-in request with no "
@@ -1186,6 +1232,7 @@ ahc_intr(arg)
*/
sc_print_addr(xs->sc_link);
printf("Queue Full\n");
+ scb->flags = SCB_ASSIGNEDQ;
STAILQ_INSERT_TAIL(&ahc->assigned_scbs,
scb, links);
break;
@@ -1279,6 +1326,7 @@ ahc_intr(arg)
outb(MSG0 + iobase,
MSG_BUS_DEVICE_RESET);
outb(MSG_LEN + iobase, 1);
+ printf("Bus Device Reset Message Sent\n");
}
else
panic("ahc_intr: AWAITING_MSG for an SCB that "
@@ -1498,12 +1546,12 @@ clear:
outb(CLRINT + iobase, CLRCMDINT);
continue;
}
-
outb(CLRINT + iobase, CLRCMDINT);
untimeout(ahc_timeout, (caddr_t)scb);
ahc_done(ahc, scb);
} while (inb(QOUTCNT + iobase) & ahc->qcntmask);
+
ahc_run_waiting_queues(ahc);
}
}
@@ -1527,11 +1575,14 @@ ahc_done(ahc, scb)
*/
if(scb->flags & SCB_SENSE)
xs->error = XS_SENSE;
+ if(scb->flags & SCB_SENTORDEREDTAG)
+ ahc->in_timeout = FALSE;
if ((xs->flags & SCSI_ERR_OK) && !(xs->error == XS_SENSE)) {
/* All went correctly OR errors expected */
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;
@@ -1566,6 +1617,7 @@ ahc_done(ahc, scb)
}
}
}
+#endif
ahc_free_scb(ahc, scb, xs->flags);
scsi_done(xs);
}
@@ -1786,6 +1838,7 @@ ahc_init(ahc)
ahc->sdtrpending = 0;
ahc->wdtrpending = 0;
ahc->tagenable = 0;
+ ahc->orderedtag = 0;
#ifdef AHC_DEBUG
/* How did we do? */
@@ -1913,8 +1966,14 @@ ahc_scsi_cmd(xs)
* Put all the arguments for the xfer in the scb
*/
- if(ahc->tagenable & mask)
+ if(ahc->tagenable & mask) {
scb->control |= TAG_ENB;
+ if(ahc->orderedtag & mask) {
+ printf("Ordered Tag sent\n");
+ scb->control |= 0x02;
+ ahc->orderedtag &= ~mask;
+ }
+ }
if(ahc->discenable & mask)
scb->control |= DISCENB;
if((ahc->needwdtr & mask) && !(ahc->wdtrpending & mask))
@@ -2031,6 +2090,7 @@ ahc_scsi_cmd(xs)
outb(SCBPTR + iobase, curscb);
outb(QINFIFO + iobase, scb->position);
UNPAUSE_SEQUENCER(ahc);
+ scb->flags = SCB_ACTIVE;
if (!(flags & SCSI_NOMASK)) {
timeout(ahc_timeout, (caddr_t)scb,
(xs->timeout * hz) / 1000);
@@ -2038,6 +2098,7 @@ ahc_scsi_cmd(xs)
SC_DEBUG(xs->sc_link, SDEV_DB3, ("cmd_sent\n"));
}
else {
+ scb->flags = SCB_WAITINGQ;
STAILQ_INSERT_TAIL(&ahc->waiting_scbs, scb, links);
ahc_run_waiting_queues(ahc);
}
@@ -2097,7 +2158,8 @@ ahc_free_scb(ahc, scb, flags)
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);
+ STAILQ_INSERT_HEAD(&ahc->assigned_scbs, wscb, links);
+ wscb->flags = SCB_ASSIGNEDQ;
/*
* The "freed" SCB will need to be assigned a slot
@@ -2188,7 +2250,7 @@ ahc_get_scb(ahc, flags)
if (scbp) {
scbp->control = 0;
scbp->status = 0;
- scbp->flags = SCB_ACTIVE;
+ scbp->flags = 0;
#ifdef AHC_DEBUG
ahc->activescbs++;
if((ahc_debug & AHC_SHOWSCBCNT)
@@ -2366,6 +2428,19 @@ ahc_timeout(arg)
"%d SCBs aborted\n", ahc->unit, channel, found);
ahc->in_timeout = FALSE;
}
+ else if(scb->control & TAG_ENB) {
+ /*
+ * We could be starving this command
+ * try sending an ordered tag command
+ * to the target we come from.
+ */
+ scb->flags |= SCB_ABORTED|SCB_SENTORDEREDTAG;
+ ahc->orderedtag |= 0xFF;
+ timeout(ahc_timeout, (caddr_t)scb, (5 * hz));
+ UNPAUSE_SEQUENCER(ahc);
+ printf("Ordered Tag queued\n");
+ goto done;
+ }
else {
/*
* Send a Bus Device Reset Message:
@@ -2393,12 +2468,39 @@ ahc_timeout(arg)
u_char active_scb;
struct scb *active_scbp;
- active_scb = inb(SCB_TAG + iobase);
- active_scbp = ahc->scbarray[active_scb];
+ active_scb = inb(SCBPTR + iobase);
+ active_scbp = ahc->scbarray[inb(SCB_TAG + iobase)];
outb(SCBPTR + iobase, scb->position);
if(inb(SCB_CONTROL + iobase) & DISCONNECTED) {
+ if(ahc->flags & AHC_PAGESCBS) {
+ /*
+ * Pull this SCB out of the
+ * disconnected list.
+ */
+ u_char prev = inb(SCB_PREV + iobase);
+ u_char next = inb(SCB_NEXT + iobase);
+ if(prev == SCB_LIST_NULL) {
+ /* At the head */
+ outb(DISCONNECTED_SCBH + iobase,
+ next );
+ }
+ else {
+ outb(SCBPTR + iobase, prev);
+ outb(SCB_NEXT + iobase, next);
+ if(next != SCB_LIST_NULL) {
+ outb(SCBPTR + iobase,
+ next);
+ outb(SCB_PREV + iobase,
+ prev);
+ }
+ outb(SCBPTR + iobase,
+ scb->position);
+ }
+ }
scb->flags |= SCB_DEVICE_RESET|SCB_ABORTED;
+ scb->control &= DISCENB;
+ scb->cmdlen = 0;
scb->SG_segment_count = 0;
scb->SG_list_pointer = 0;
scb->data = 0;
@@ -2438,7 +2540,7 @@ ahc_timeout(arg)
}
/*
* No active target or a paged out SCB.
- * Try reseting the bus
+ * Try reseting the bus.
*/
channel = (scb->tcl & SELBUSB) ? 'B': 'A';
found = ahc_reset_channel(ahc, channel, scb->tag,
diff --git a/sys/i386/scsi/aic7xxx.h b/sys/i386/scsi/aic7xxx.h
index 30e5c3a9db9b..312260721b53 100644
--- a/sys/i386/scsi/aic7xxx.h
+++ b/sys/i386/scsi/aic7xxx.h
@@ -30,7 +30,7 @@
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
- * $Id: aic7xxx.h,v 1.23 1996/03/31 03:15:31 gibbs Exp $
+ * $Id: aic7xxx.h,v 1.25 1996/04/20 21:29:27 gibbs Exp $
*/
#ifndef _AIC7XXX_H_
@@ -132,15 +132,18 @@ struct scb {
STAILQ_ENTRY(scb) links; /* for chaining */
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_DEVICE_RESET 0x04
-#define SCB_IMMED 0x08
-#define SCB_SENSE 0x10
-#define SCB_TIMEDOUT 0x20
-#define SCB_QUEUED_FOR_DONE 0x40
-#define SCB_PAGED_OUT 0x80
+#define SCB_FREE 0x000
+#define SCB_ACTIVE 0x001
+#define SCB_ABORTED 0x002
+#define SCB_DEVICE_RESET 0x004
+#define SCB_IMMED 0x008
+#define SCB_SENSE 0x010
+#define SCB_TIMEDOUT 0x020
+#define SCB_QUEUED_FOR_DONE 0x040
+#define SCB_PAGED_OUT 0x080
+#define SCB_WAITINGQ 0x100
+#define SCB_ASSIGNEDQ 0x200
+#define SCB_SENTORDEREDTAG 0x400
u_char position; /* Position in card's scbarray */
struct ahc_dma_seg ahc_dma[AHC_NSEG] __attribute__ ((packed));
struct scsi_sense sense_cmd; /* SCSI command block */
@@ -182,6 +185,7 @@ struct ahc_data {
u_short sdtrpending; /* Pending SDTR to these targets */
u_short wdtrpending; /* Pending WDTR to these targets */
u_short tagenable; /* Targets that can handle tagqueing */
+ u_short orderedtag; /* Targets to use ordered tag on */
u_short discenable; /* Targets allowed to disconnect */
u_char our_id; /* our scsi id */
u_char our_id_b; /* B channel scsi id */